From e8e8a3cd2d1c67bdb3d6259c570ee1c611dc0a57 Mon Sep 17 00:00:00 2001 From: Francisco Zanini Date: Thu, 10 Aug 2023 10:18:11 -0300 Subject: [PATCH 1/5] New method to generate HMAC based on the key length --- Source/Common/JOSE.Hashing.HMAC.pas | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/Common/JOSE.Hashing.HMAC.pas b/Source/Common/JOSE.Hashing.HMAC.pas index ddbd4ba..4f76397 100644 --- a/Source/Common/JOSE.Hashing.HMAC.pas +++ b/Source/Common/JOSE.Hashing.HMAC.pas @@ -49,7 +49,8 @@ THMACAlgorithmHelper = record helper for THMACAlgorithm THMAC = class public - class function Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBytes; + class function Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBytes; overload; + class function Sign(const AInput, AKey: TBytes; AKeyLength: Integer): TBytes; overload; end; implementation @@ -93,6 +94,16 @@ class function THMAC.Sign(const AInput, AKey: TBytes; AAlg: THMACAlgorithm): TBy end; {$IFEND} +class function THMAC.Sign(const AInput, AKey: TBytes; AKeyLength: Integer): TBytes; +begin + case AKeyLength of + 256: Result := Sign(AInput, AKey, THMACAlgorithm.SHA256); + 384: Result := Sign(AInput, AKey, THMACAlgorithm.SHA384); + 512: Result := Sign(AInput, AKey, THMACAlgorithm.SHA512); + else + Result := Sign(AInput, AKey, THMACAlgorithm.SHA256); + end; +end; { THMACAlgorithmHelper } From 60934482c4d2d588df9ca60e08a19f20281d00b1 Mon Sep 17 00:00:00 2001 From: Francisco Zanini Date: Thu, 10 Aug 2023 10:22:17 -0300 Subject: [PATCH 2/5] Added new algorithms RSA1_5 for signing and A256CBC_HS512 for encryption --- Source/Common/JOSE.Types.Bytes.pas | 8 ++ Source/JOSE/JOSE.Core.JWA.Encryption.pas | 107 +++++++++++++++++++++++ Source/JOSE/JOSE.Core.JWA.Factory.pas | 2 + Source/JOSE/JOSE.Core.JWA.pas | 16 +++- 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/Source/Common/JOSE.Types.Bytes.pas b/Source/Common/JOSE.Types.Bytes.pas index 6c00ba1..ea9dcb5 100644 --- a/Source/Common/JOSE.Types.Bytes.pas +++ b/Source/Common/JOSE.Types.Bytes.pas @@ -54,6 +54,7 @@ TJOSEBytes = record class function Empty: TJOSEBytes; static; class function RandomBytes(ANumberOfBytes: Integer): TJOSEBytes; static; + class function Swap(const Value: TJOSEBytes): TJOSEBytes; static; function IsEmpty: Boolean; function Size: Integer; @@ -223,6 +224,13 @@ procedure TJOSEBytes.SetAsString(const Value: string); FPayload := TEncoding.UTF8.GetBytes(Value); end; +class function TJOSEBytes.Swap(const Value: TJOSEBytes): TJOSEBytes; +begin + SetLength(Result.FPayload, Value.Size); + for var i := 0 to Value.Size -1 do + Result.FPayload[Value.Size -1 -i] := Value.FPayload[i]; +end; + { TBytesUtils } class function TBytesUtils.MergeBytes(const ABytes1: TBytes; const ABytes2: TBytes): TBytes; diff --git a/Source/JOSE/JOSE.Core.JWA.Encryption.pas b/Source/JOSE/JOSE.Core.JWA.Encryption.pas index 38a776e..9d89b0c 100644 --- a/Source/JOSE/JOSE.Core.JWA.Encryption.pas +++ b/Source/JOSE/JOSE.Core.JWA.Encryption.pas @@ -32,10 +32,14 @@ interface uses System.SysUtils, + IdSSLOpenSSLHeaders, JOSE.Types.Bytes, JOSE.Core.JWA; type + + EEncryptionException = class(Exception); + TEncryptionParts = class private FAuthenticationTag: TBytes; @@ -56,8 +60,20 @@ TEncryptionParts = class function Decrypt(AEncryptionParts: TEncryptionParts; const AAdditionalData, AContentEncryptionKey: TBytes{; Headers headers}): TBytes; end; + TJOSEEncryptionAlgorithm = class(TJOSEAlgorithm, IJOSEEncryptionAlgorithm) + protected + constructor Create(AAlgorithmId: TJOSEAlgorithmId); + public + function Encrypt(const APlaintext, AAdditionalData, AContentEncryptionKey, IvOverride: TBytes): TEncryptionParts; + function Decrypt(AEncryptionParts: TEncryptionParts; const AAdditionalData, AContentEncryptionKey: TBytes): TBytes; + class function A256CBC_HS512: IJOSEEncryptionAlgorithm; + end; + implementation +uses + JOSE.Hashing.HMAC; + { TEncryptionParts } constructor TEncryptionParts.Create(const AIv, ACiphertext, AAuthenticationTag: TBytes); @@ -67,4 +83,95 @@ constructor TEncryptionParts.Create(const AIv, ACiphertext, AAuthenticationTag: FAuthenticationTag := AAuthenticationTag; end; +{ TJOSEEncryptionAlgorithm } + +constructor TJOSEEncryptionAlgorithm.Create(AAlgorithmId: TJOSEAlgorithmId); +begin + FAlgorithmIdentifier := AAlgorithmId; + FKeyCategory := TJOSEKeyCategory.Symmetric; + FKeyType := 'oct'; +end; + +function TJOSEEncryptionAlgorithm.Encrypt(const APlaintext, AAdditionalData, AContentEncryptionKey, IvOverride: TBytes): TEncryptionParts; +var + MacKey, EncKey: TBytes; + CipherText: TBytes; + CipherCtx: EVP_CIPHER_CTX; + IV: TBytes; +begin + + // Check the encryption key size + if Length(AContentEncryptionKey) * 8 < GetAlgorithmIdentifier.Length then + raise EEncryptionException.Create('[Encryption] The key size must be ' + GetAlgorithmIdentifier.Length.ToString + '-bit long'); + + // Extract MAC key - first half + SetLength(MacKey, Length(AContentEncryptionKey) div 2); + Move(AContentEncryptionKey[0], MacKey[0], Length(MacKey)); + + // Extract the ENC key - seconds half + SetLength(EncKey, Length(AContentEncryptionKey) div 2); + Move(AContentEncryptionKey[Length(AContentEncryptionKey) div 2], EncKey[0], Length(EncKey)); + + // Init the cipher + EVP_CIPHER_CTX_init(@CipherCtx); + try + + // The initialization vector + if Assigned(IvOverride) then + IV := IvOverride + else + IV := TJOSEBytes.RandomBytes(EVP_MAX_IV_LENGTH); + + // Initialize cipher + case GetAlgorithmIdentifier of + TJOSEAlgorithmId.A256CBC_HS512: EVP_EncryptInit_ex(@CipherCtx, EVP_aes_256_cbc, nil, @EncKey[0], @IV[0]); + else + raise EEncryptionException.Create('[Encryption] Invalid encryption algorithm'); + end; + + // Apply PKCS7 + var PaddingSize := CipherCtx.cipher.block_size - Length(APlaintext) mod CipherCtx.cipher.block_size; + var LPlainText := APlaintext; + SetLength(LPlainText, Length(LPlainText) + PaddingSize); + FillChar(LPlainText[Length(APlaintext)], PaddingSize, PaddingSize); + + // Set the cipher text length + SetLength(CipherText, Length(LPlainText) + CipherCtx.cipher.block_size); + + // Encrypt the plain text + var OutLen: Integer; + EVP_EncryptUpdate(@CipherCtx, @CipherText[0], @OutLen, @LPlainText[0], Length(LPlainText)); + EVP_EncryptFinal_ex(@CipherCtx, @CipherText[OutLen], @OutLen); + finally + EVP_CIPHER_CTX_cleanup(@cipherCtx); + end; + + // Additional data len + var AADSize: UInt64 := Length(AAdditionalData) * 8; + var AADLen: TBytes; + SetLength(AADLen, SizeOf(AADSize)); + Move(AADSize, AADLen[0], Length(AADLen)); + + // Make HMAC + var HMACData: TBytes := AAdditionalData + IV + CipherText + TJOSEBytes.Swap(AADLen); + var HMAC: TBytes := THMAC.Sign(HMACData, MacKey, GetAlgorithmIdentifier.Length); + var AuthenticationTag: TBytes; + SetLength(AuthenticationTag, Length(HMAC) div 2); + Move(HMAC[0], AuthenticationTag[0], Length(AuthenticationTag)); + + // Result + Result := TEncryptionParts.Create(IV, CipherText, AuthenticationTag); + +end; + +function TJOSEEncryptionAlgorithm.Decrypt(AEncryptionParts: TEncryptionParts; const AAdditionalData, AContentEncryptionKey: TBytes): TBytes; +begin + // To do +end; + +class function TJOSEEncryptionAlgorithm.A256CBC_HS512: IJOSEEncryptionAlgorithm; +begin + Result := TJOSEEncryptionAlgorithm.Create(TJOSEAlgorithmId.A256CBC_HS512); +end; + end. diff --git a/Source/JOSE/JOSE.Core.JWA.Factory.pas b/Source/JOSE/JOSE.Core.JWA.Factory.pas index 171d48d..2839865 100644 --- a/Source/JOSE/JOSE.Core.JWA.Factory.pas +++ b/Source/JOSE/JOSE.Core.JWA.Factory.pas @@ -146,6 +146,8 @@ constructor TJOSEAlgorithmRegistryFactory.Create; FEncryptionAlgorithmRegistry := TJOSEAlgorithmRegistry.Create('alg'); + FEncryptionAlgorithmRegistry.RegisterAlgorithm(TJOSEEncryptionAlgorithm.A256CBC_HS512); + FCompressionAlgorithmRegistry := TJOSEAlgorithmRegistry.Create('alg'); end; diff --git a/Source/JOSE/JOSE.Core.JWA.pas b/Source/JOSE/JOSE.Core.JWA.pas index ffb30fa..ea5fce4 100644 --- a/Source/JOSE/JOSE.Core.JWA.pas +++ b/Source/JOSE/JOSE.Core.JWA.pas @@ -48,7 +48,9 @@ interface HS256, HS384, HS512, RS256, RS384, RS512, ES256, ES256K, ES384, ES512, - PS256, PS384, PS512 + PS256, PS384, PS512, + RSA1_5, + A256CBC_HS512 ); TJOSEAlgorithmIdHelper = record helper for TJOSEAlgorithmId private @@ -128,6 +130,10 @@ function TJOSEAlgorithmIdHelper.GetAsString: string; TJOSEAlgorithmId.PS256: Result := 'PS256'; TJOSEAlgorithmId.PS384: Result := 'PS384'; TJOSEAlgorithmId.PS512: Result := 'PS512'; + + TJOSEAlgorithmId.A256CBC_HS512: Result := 'A256CBC-HS512'; + + TJOSEAlgorithmId.RSA1_5: Result := 'RSA1_5'; end; end; @@ -151,6 +157,8 @@ function TJOSEAlgorithmIdHelper.GetLength: Integer; TJOSEAlgorithmId.PS256: Result := 256; TJOSEAlgorithmId.PS384: Result := 384; TJOSEAlgorithmId.PS512: Result := 512; + + TJOSEAlgorithmId.A256CBC_HS512: Result := 512; end; end; @@ -189,6 +197,12 @@ procedure TJOSEAlgorithmIdHelper.SetAsString(const AValue: string); else if AValue = 'PS512' then Self := TJOSEAlgorithmId.PS512 + else if AValue = 'A256CBC-HS512' then + Self := TJOSEAlgorithmId.A256CBC_HS512 + + else if AValue = 'RSA1_5' then + Self := TJOSEAlgorithmId.RSA1_5 + else Self := TJOSEAlgorithmId.Unknown; end; From 2bdbb0a39c26be827561ce337b54e43b813fd5c0 Mon Sep 17 00:00:00 2001 From: Francisco Zanini Date: Thu, 10 Aug 2023 10:24:12 -0300 Subject: [PATCH 3/5] Added method to JWS to sign a raw payload This method will be used to sign the JWE compact serialization --- Source/JOSE/JOSE.Core.JWS.pas | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Source/JOSE/JOSE.Core.JWS.pas b/Source/JOSE/JOSE.Core.JWS.pas index b349581..0da14e6 100644 --- a/Source/JOSE/JOSE.Core.JWS.pas +++ b/Source/JOSE/JOSE.Core.JWS.pas @@ -78,6 +78,7 @@ TJWS = class(TJOSEParts) function Sign: TJOSEBytes; overload; function Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId): TJOSEBytes; overload; + function Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId; const APayload: TJOSEBytes): TJOSEBytes; overload; function VerifySignature: Boolean; overload; function VerifySignature(AKey: TJWK; const ACompactToken: TJOSEBytes): Boolean; overload; @@ -236,6 +237,16 @@ function TJWS.Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId): TJOSEBytes; begin SetKey(AKey); SetHeaderAlgorithm(AAlgId); + Payload := ToJSON(FToken.Claims.JSON); + + Result := Sign(); +end; + +function TJWS.Sign(AKey: TJWK; AAlgId: TJOSEAlgorithmId; const APayload: TJOSEBytes): TJOSEBytes; +begin + SetKey(AKey); + SetHeaderAlgorithm(AAlgId); + Payload := APayload; Result := Sign(); end; @@ -252,7 +263,7 @@ function TJWS.Sign: TJOSEBytes; LAlg.ValidateSigningKey(FKey); Header := TBase64.URLEncode(ToJSON(FToken.Header.JSON)); - Payload := TBase64.URLEncode(ToJSON(FToken.Claims.JSON)); + Payload := TBase64.URLEncode(Payload); Signature := LAlg.Sign(FKey, SigningInput); Result := Signature; From 9cfb8c3a9f03eeb26c06ef030d27b9e95cdb74a1 Mon Sep 17 00:00:00 2001 From: Francisco Zanini Date: Thu, 10 Aug 2023 10:27:50 -0300 Subject: [PATCH 4/5] Implementation of JWE encryption and serialization --- Source/Common/JOSE.Encryption.RSA.pas | 54 ++++++++++++++++++++++++ Source/JOSE/JOSE.Core.JWE.pas | 61 +++++++++++++++++++++++++-- 2 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 Source/Common/JOSE.Encryption.RSA.pas diff --git a/Source/Common/JOSE.Encryption.RSA.pas b/Source/Common/JOSE.Encryption.RSA.pas new file mode 100644 index 0000000..3996508 --- /dev/null +++ b/Source/Common/JOSE.Encryption.RSA.pas @@ -0,0 +1,54 @@ +unit JOSE.Encryption.RSA; + +interface + +uses + System.SysUtils, + IdSSLOpenSSLHeaders, + JOSE.Signing.Base; + +type + TRSAEncryption = class(TSigningBase) + private + class function LoadRSAPublicKeyFromCert(const ACertificate: TBytes): PRSA; + public + class function EncryptWithPublicCertificate(const ACertificate, AValue: TBytes): TBytes; + end; + +implementation + +class function TRSAEncryption.LoadRSAPublicKeyFromCert(const ACertificate: TBytes): PRSA; +var + LKey: PEVP_PKEY; +begin + LKey := LoadPublicKeyFromCert(ACertificate, NID_rsaEncryption); + try + Result := EVP_PKEY_get1_RSA(LKey); + if not Assigned(Result) then + raise ESignException.Create('[RSA] Error extracting RSA key from EVP_PKEY'); + finally + EVP_PKEY_free(LKey); + end; +end; + +class function TRSAEncryption.EncryptWithPublicCertificate(const ACertificate, AValue: TBytes): TBytes; +begin + + var RSA := LoadRSAPublicKeyFromCert(ACertificate); + try + + SetLength(Result, RSA_size(rsa)); + var EncryptedLen := RSA_public_encrypt(Length(AValue), PByte(AValue), @Result[0], RSA, RSA_PKCS1_PADDING); + + if encryptedLen = -1 then + raise ESignException.Create('[RSA] Error encrypting'); + + SetLength(Result, EncryptedLen); + + finally + RSA_free(RSA); + end; + +end; + +end. diff --git a/Source/JOSE/JOSE.Core.JWE.pas b/Source/JOSE/JOSE.Core.JWE.pas index b0220b9..e8318ac 100644 --- a/Source/JOSE/JOSE.Core.JWE.pas +++ b/Source/JOSE/JOSE.Core.JWE.pas @@ -29,7 +29,12 @@ interface JOSE.Types.Bytes, JOSE.Core.Base, JOSE.Core.Parts, - JOSE.Core.JWT; + JOSE.Core.JWA, + JOSE.Core.JWA.Factory, + JOSE.Core.JWK, + JOSE.Core.JWT, + JOSE.Encoding.Base64, + JOSE.Encryption.RSA; type /// @@ -41,12 +46,21 @@ interface TJWE = class(TJOSEParts) private const COMPACT_PARTS = 5; + private + function GetPart(Index: Integer): TJOSEBytes; + procedure SetPart(Index: Integer; const Value: TJOSEBytes); public constructor Create(AToken: TJWT); override; - //class function Encrypt(AKey: TJWK; AAlg: TJWAEnum): string; + function Encrypt(AKey: TJWK; AAlg, AEncryptionAlg: TJOSEAlgorithmId): string; //class function Decrypt(AKey: TJWK; AInput: TBytes): Boolean; - end; + function GetCompactToken: TJOSEBytes; override; + property Header: TJOSEBytes index 0 read GetPart write SetPart; + property EncryptedKey: TJOSEBytes index 1 read GetPart write SetPart; + property IV: TJOSEBytes index 2 read GetPart write SetPart; + property CipherText: TJOSEBytes index 3 read GetPart write SetPart; + property AuthenticationTag: TJOSEBytes index 4 read GetPart write SetPart; + end; implementation { TJWEParts } @@ -61,4 +75,45 @@ constructor TJWE.Create(AToken: TJWT); FParts.Add(TJOSEBytes.Empty); end; +function TJWE.GetPart(Index: Integer): TJOSEBytes; +begin + if (Index >= 0) and (Index < COMPACT_PARTS) then + Result := FParts[Index] + else + Result := nil; +end; + +procedure TJWE.SetPart(Index: Integer; const Value: TJOSEBytes); +begin + if (Index >= 0) and (Index < COMPACT_PARTS) then + FParts[Index] := Value; +end; + +function TJWE.Encrypt(AKey: TJWK; AAlg, AEncryptionAlg: TJOSEAlgorithmId): string; +begin + + // Set headers + SetHeaderAlgorithm(AAlg); + FToken.Header.SetHeaderParamOfType('enc', AEncryptionAlg.AsString); + + Header := TBase64.URLEncode(ToJSON(FToken.Header.JSON)); + + var CEK := TJOSEBytes.RandomBytes(AEncryptionAlg.Length div 8); + EncryptedKey := TBase64.URLEncode(TRSAEncryption.EncryptWithPublicCertificate(AKey.Key, CEK)); + + var Payload : TJOSEBytes := ToJSON(FToken.Claims.JSON); + var Parts := TJOSEAlgorithmRegistryFactory.Instance.EncryptionAlgorithmRegistry.GetAlgorithm(AEncryptionAlg.AsString).Encrypt(Payload, Header, CEK, nil); + IV := TBase64.URLEncode(Parts.Iv); + CipherText := TBase64.URLEncode(Parts.Ciphertext); + AuthenticationTag := TBase64.URLEncode(Parts.AuthenticationTag); + + Result := GetCompactToken; + +end; + +function TJWE.GetCompactToken: TJOSEBytes; +begin + Result := Header + PART_SEPARATOR + EncryptedKey + PART_SEPARATOR + IV + PART_SEPARATOR + CipherText + PART_SEPARATOR + AuthenticationTag; +end; + end. From 6f8c98c98583eda08074faddd522b49637277da8 Mon Sep 17 00:00:00 2001 From: Francisco Zanini Date: Thu, 10 Aug 2023 10:29:14 -0300 Subject: [PATCH 5/5] Added methods to Builder to serialize, encrypt and sign using JWE --- Source/JOSE/JOSE.Core.Builder.pas | 67 +++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Source/JOSE/JOSE.Core.Builder.pas b/Source/JOSE/JOSE.Core.Builder.pas index 55bf856..65b50be 100644 --- a/Source/JOSE/JOSE.Core.Builder.pas +++ b/Source/JOSE/JOSE.Core.Builder.pas @@ -49,6 +49,9 @@ TJOSE = class class function SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; overload; class function SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; overload; class function SerializeCompact(AKey: TJOSEBytes; AAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; overload; + class function SerializeCompact(APrivateKey, APublicKey: TJWK; AAlg, AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; overload; + class function SerializeCompact(APrivateKey, APublicKey: TJOSEBytes; AAlg, AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; overload; + class function SerializeCompact(APrivateKey, APublicKey: TJOSEBytes; AAlg, AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; overload; class function DeserializeCompact(AKey: TJWK; const ACompactToken: TJOSEBytes): TJWT; overload; class function DeserializeCompact(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes): TJWT; overload; @@ -58,6 +61,8 @@ TJOSE = class class function SHA256CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; class function SHA284CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; class function SHA512CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; + + class function EncryptCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AEncryptionAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; end; implementation @@ -173,6 +178,56 @@ class function TJOSE.SerializeCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AToken end; end; +class function TJOSE.SerializeCompact(APrivateKey, APublicKey: TJWK; AAlg, + AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; +var + LSigner: TJWS; + LToken: TJWT; + JWEData: TJOSEBytes; +begin + + JWEData := EncryptCompact(APublicKey, AEncryptionKeyAlg, AEncryptionContentAlg, AToken); + + LToken := TJWT.Create; + try + LSigner := TJWS.Create(AToken); + try + LSigner.SkipKeyValidation := ASkipValidation; + LSigner.Sign(APrivateKey, AAlg, JWEData); + Result := LSigner.CompactToken; + finally + LSigner.Free; + end; + finally + LToken.Free; + end; + +end; + +class function TJOSE.SerializeCompact(APrivateKey, APublicKey: TJOSEBytes; + AAlg, AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT; ASkipValidation: Boolean): TJOSEBytes; +var + PublicKey, PrivateKey: TJWK; +begin + PrivateKey := TJWK.Create(APrivateKey); + try + PublicKey := TJWK.Create(APublicKey); + try + Result := SerializeCompact(PrivateKey, PrivateKey, AAlg, AEncryptionKeyAlg, AEncryptionContentAlg, AToken, ASkipValidation); + finally + PublicKey.Free; + end; + finally + PrivateKey.Free; + end; +end; + +class function TJOSE.SerializeCompact(APrivateKey, APublicKey: TJOSEBytes; + AAlg, AEncryptionKeyAlg, AEncryptionContentAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; +begin + Result := SerializeCompact(APrivateKey, APublicKey, AAlg, AEncryptionKeyAlg, AEncryptionContentAlg, AToken, True); +end; + class function TJOSE.SHA256CompactToken(AKey: TJOSEBytes; AToken: TJWT): TJOSEBytes; begin Result := SerializeCompact(AKey, TJOSEAlgorithmId.HS256, AToken); @@ -209,4 +264,16 @@ class function TJOSE.Verify(AKey: TJOSEBytes; const ACompactToken: TJOSEBytes; end; end; +class function TJOSE.EncryptCompact(AKey: TJWK; AAlg: TJOSEAlgorithmId; AEncryptionAlg: TJOSEAlgorithmId; AToken: TJWT): TJOSEBytes; +begin + + var JWE := TJWE.Create(AToken); + try + Result := JWE.Encrypt(AKey, AAlg, AEncryptionAlg); + finally + JWE.Free; + end; + +end; + end.