mirror of
https://github.com/pintov/1c-jwt.git
synced 2024-11-24 08:12:36 +02:00
Initial commit
Initial commit
This commit is contained in:
commit
0cb2431e9a
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Vasily Pintov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
||||
1C JWT
|
||||
=====
|
||||
|
||||
This is a pure 1C implementation of `RFC 7519 <https://tools.ietf.org/html/rfc7519>`.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
|
||||
This implementation works only on 1C:Enterprise platform version 8.3.10.2168 or above.
|
||||
The platform you may download here: <https://1c-dn.com/user/updates/1c_enterprise_platform_training_version/>
|
||||
Supported algorithm HS256 only.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Download modules Cryptography.bsl and JWT.bsl.
|
||||
Put modules into the 1C application.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```
|
||||
SecretKey = "secret";
|
||||
Payload = New Structure;
|
||||
Payload.Insert("sub", "1234567890");
|
||||
Payload.Insert("name", "John Doe");
|
||||
Payload.Insert("admin", True);
|
||||
|
||||
Token = JWT.Encode(SecretKey, Payload);
|
||||
|
||||
DecodedPayload = JWT.Decode(Token, SecretKey);
|
||||
|
||||
```
|
||||
|
||||
Credits and License
|
||||
-------------------
|
||||
|
||||
Author: Vasily Pintov <vasily@pintov.ru>
|
||||
|
||||
License: MIT
|
125
src/Cryptography.bsl
Normal file
125
src/Cryptography.bsl
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
// Computes a Hash-based Message Authentication Code (HMAC)
|
||||
//
|
||||
// Parameters:
|
||||
// Key - BinaryData - the key to use in the hash algorithm
|
||||
// Message - BinaryData - the input to compute the hash code for
|
||||
// HashFunc - HashFunction - the name of the hash algorithm to use for hashing
|
||||
//
|
||||
// Returns:
|
||||
// BinaryData - The computed hash code
|
||||
//
|
||||
Function HMAC(Val SecretKey, Val Message, Val HashFunc) Export
|
||||
|
||||
If HashFunc = HashFunction.MD5 Then
|
||||
BlSz = 16;
|
||||
ElsIf HashFunc = HashFunction.SHA1 Then
|
||||
BlSz = 20;
|
||||
ElsIf HashFunc = HashFunction.SHA256 Then
|
||||
BlSz = 64;
|
||||
Else
|
||||
Raise "HMAC: unsupported hash function: " + HashFunc;
|
||||
EndIf;
|
||||
|
||||
EmptyBin = GetBinaryDataFromString("");
|
||||
SecretKey = BinLeft(SecretKey, BlSz);
|
||||
Ê0 = BinRightPad(SecretKey, BlSz, "0x00");
|
||||
|
||||
ipad = BinRightPad(EmptyBin, BlSz, "0x36");
|
||||
k_ipad = BinBitwiseXOR(Ê0, ipad);
|
||||
|
||||
opad = BinRightPad(EmptyBin, BlSz, "0x5C");
|
||||
k_opad = BinBitwiseXOR(Ê0, opad);
|
||||
|
||||
k_ipad_Message = BinConcat(k_ipad, Message);
|
||||
k_opad_Hash = BinConcat(k_opad, Hash(k_ipad_Message, HashFunc));
|
||||
res = Hash(k_opad_Hash, HashFunc);
|
||||
|
||||
Return res;
|
||||
|
||||
EndFunction
|
||||
|
||||
Function BinLeft(Val BinaryData, Val CountOfBytes)
|
||||
|
||||
DataReader = New DataReader(BinaryData);
|
||||
|
||||
MemoryStream = New MemoryStream();
|
||||
DataWriter = New DataWriter(MemoryStream);
|
||||
|
||||
Buffer = DataReader.ReadIntoBinaryDataBuffer(CountOfBytes);
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer);
|
||||
|
||||
Return MemoryStream.CloseAndGetBinaryData();
|
||||
|
||||
EndFunction
|
||||
|
||||
Function BinRightPad(Val BinaryData, Val Length, Val HexString)
|
||||
|
||||
PadByte = NumberFromHexString(HexString);
|
||||
|
||||
DataReader = New DataReader(BinaryData);
|
||||
|
||||
MemoryStream = New MemoryStream();
|
||||
DataWriter = New DataWriter(MemoryStream);
|
||||
|
||||
Buffer = DataReader.ReadIntoBinaryDataBuffer();
|
||||
If Buffer.Size > 0 Then
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer);
|
||||
EndIf;
|
||||
|
||||
For n = Buffer.Size + 1 To Length Do
|
||||
DataWriter.WriteByte(PadByte);
|
||||
EndDo;
|
||||
|
||||
Return MemoryStream.CloseAndGetBinaryData();
|
||||
|
||||
EndFunction
|
||||
|
||||
Function BinBitwiseXOR(Val BinaryData1, Val BinaryData2)
|
||||
|
||||
MemoryStream = New MemoryStream();
|
||||
DataWriter = New DataWriter(MemoryStream);
|
||||
|
||||
DataReader1 = New DataReader(BinaryData1);
|
||||
DataReader2 = New DataReader(BinaryData2);
|
||||
|
||||
Buffer1 = DataReader1.ReadIntoBinaryDataBuffer();
|
||||
Buffer2 = DataReader2.ReadIntoBinaryDataBuffer();
|
||||
|
||||
If Buffer1.Size > Buffer2.Size Then
|
||||
Buffer1.WriteBitwiseXor(0, Buffer2, Buffer2.Size);
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer1);
|
||||
Else
|
||||
Buffer2.WriteBitwiseXor(0, Buffer1, Buffer1.Size);
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer2);
|
||||
EndIf;
|
||||
|
||||
res = MemoryStream.CloseAndGetBinaryData();
|
||||
Return res;
|
||||
|
||||
EndFunction
|
||||
|
||||
Function Hash(Val Value, Val HashFunc)
|
||||
DataHashing = New DataHashing(HashFunc);
|
||||
DataHashing.Append(Value);
|
||||
Return DataHashing.HashSum;
|
||||
EndFunction
|
||||
|
||||
Function BinConcat(Val BinaryData1, Val BinaryData2)
|
||||
|
||||
MemoryStream = New MemoryStream();
|
||||
DataWriter = New DataWriter(MemoryStream);
|
||||
|
||||
DataReader1 = New DataReader(BinaryData1);
|
||||
DataReader2 = New DataReader(BinaryData2);
|
||||
|
||||
Buffer1 = DataReader1.ReadIntoBinaryDataBuffer();
|
||||
Buffer2 = DataReader2.ReadIntoBinaryDataBuffer();
|
||||
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer1);
|
||||
DataWriter.WriteBinaryDataBuffer(Buffer2);
|
||||
|
||||
res = MemoryStream.CloseAndGetBinaryData();
|
||||
Return res;
|
||||
|
||||
EndFunction
|
146
src/JWT.bsl
Normal file
146
src/JWT.bsl
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
Function Encode(Val SecretKey, Val Payload = Undefined, Val ExtraHeaders = Undefined) Export
|
||||
|
||||
If Payload = Undefined Then
|
||||
Payload = New Structure;
|
||||
EndIf;
|
||||
|
||||
header = New Structure;
|
||||
header.Insert("typ", "JWT");
|
||||
header.Insert("alg", "HS256");
|
||||
If ExtraHeaders <> Undefined Then
|
||||
For Each eh In ExtraHeaders Do
|
||||
header.Insert(eh.Key, eh.Value);
|
||||
EndDo;
|
||||
EndIf;
|
||||
|
||||
headerBytes = GetBinaryDataFromString(ComposeJSON(header));
|
||||
payloadBytes = GetBinaryDataFromString(ComposeJSON(Payload));
|
||||
|
||||
segments = New Array;
|
||||
segments.Add(Base64UrlEncode(headerBytes));
|
||||
segments.Add(Base64UrlEncode(payloadBytes));
|
||||
|
||||
stringToSign = StrConcat(segments, ".");
|
||||
|
||||
signature = Cryptography.HMAC(
|
||||
GetBinaryDataFromString(SecretKey),
|
||||
GetBinaryDataFromString(stringToSign),
|
||||
HashFunction.SHA256);
|
||||
|
||||
segments.Add(Base64UrlEncode(signature));
|
||||
|
||||
res = StrConcat(segments, ".");
|
||||
|
||||
Return res;
|
||||
|
||||
EndFunction
|
||||
|
||||
Function Decode(Val Token, Val SecretKey, Val Verify = True) Export
|
||||
|
||||
parts = StrSplit(Token, ".");
|
||||
If parts.Count() <> 3 Then
|
||||
Raise "JWT.Decode: Token must consist from 3 delimited by dot parts";
|
||||
EndIf;
|
||||
|
||||
header = parts[0];
|
||||
payload = parts[1];
|
||||
crypto = Base64UrlDecode(parts[2]);
|
||||
|
||||
headerJson = GetStringFromBinaryData(Base64UrlDecode(header));
|
||||
payloadJson = GetStringFromBinaryData(Base64UrlDecode(payload));
|
||||
|
||||
headerData = ParseJSON(headerJson);
|
||||
payloadData = ParseJSON(payloadJson);
|
||||
|
||||
If Verify Then
|
||||
If headerData.Property("alg") Then
|
||||
If headerData.alg <> "HS256" Then
|
||||
Raise "JWT.Decode: unsopported algorithm: " + headerData.alg;
|
||||
EndIf;
|
||||
Else
|
||||
Raise "JWT.Decode: header doesn't contain field 'alg'";
|
||||
EndIf;
|
||||
|
||||
signature = Cryptography.HMAC(
|
||||
GetBinaryDataFromString(SecretKey),
|
||||
GetBinaryDataFromString(header + "." + payload),
|
||||
HashFunction.SHA256);
|
||||
|
||||
If Base64String(crypto) <> Base64String(signature) Then
|
||||
Raise "JWT.Decode: Invalid signature";
|
||||
EndIf;
|
||||
|
||||
EndIf;
|
||||
|
||||
Return payloadData;
|
||||
|
||||
EndFunction
|
||||
|
||||
Function Base64UrlEncode(Val input)
|
||||
|
||||
output = Base64String(input);
|
||||
output = StrSplit(output, "=")[0]; // Remove any trailing '='s
|
||||
output = StrReplace(output, Chars.CR + Chars.LF, "");
|
||||
output = StrReplace(output, "+", "-"); // 62nd char of encoding
|
||||
output = StrReplace(output, "/", "_"); // 63rd char of encoding
|
||||
Return output;
|
||||
|
||||
EndFunction
|
||||
|
||||
Function Base64UrlDecode(Val input)
|
||||
|
||||
res = input;
|
||||
res = StrReplace(input, "-", "+"); // 62nd char of encoding
|
||||
res = StrReplace(res, "_", "/"); // 63rd char of encoding
|
||||
m = StrLen(res) % 4;
|
||||
If m = 1 Then
|
||||
Raise "JWT.Base64UrlDecode: Illegal base64url string: " + input;
|
||||
ElsIf m = 2 Then
|
||||
res = res + "=="; // Two pad chars
|
||||
ElsIf m = 3 Then
|
||||
res = res + "="; // One pad char
|
||||
EndIf;
|
||||
return Base64Value(res);
|
||||
|
||||
EndFunction
|
||||
|
||||
Function ComposeJSON(Obj)
|
||||
|
||||
If Not ValueIsFilled(Obj) Then
|
||||
Return Undefined;
|
||||
EndIf;
|
||||
|
||||
JSONWriter = New JSONWriter;
|
||||
Settings = New JSONWriterSettings(JSONLineBreak.None);
|
||||
JSONWriter.SetString(Settings);
|
||||
WriteJSON(JSONWriter, Obj);
|
||||
Return JSONWriter.Close();
|
||||
|
||||
EndFunction
|
||||
|
||||
Function ParseJSON(Json) Export
|
||||
|
||||
If ValueIsNotFilled(Json) Then
|
||||
Return Undefined;
|
||||
EndIf;
|
||||
|
||||
JSONReader = New JSONReader;
|
||||
JSONReader.SetString(Json);
|
||||
Return ReadJSON(JSONReader, False);
|
||||
|
||||
EndFunction
|
||||
|
||||
Procedure Test() Export
|
||||
|
||||
SecretKey = "secret";
|
||||
Payload = New Structure;
|
||||
Payload.Insert("sub", "1234567890");
|
||||
Payload.Insert("name", "John Doe");
|
||||
Payload.Insert("admin", True);
|
||||
|
||||
Token = Encode(SecretKey, Payload);
|
||||
|
||||
DecodedPayload = Decode(Token, SecretKey);
|
||||
|
||||
EndProcedure
|
Loading…
Reference in New Issue
Block a user