Browse Source

fcl-web: TJWTSignerRSA: prefix hash with ASN1 digest info

mattias 3 years ago
parent
commit
e339f8b660
2 changed files with 45 additions and 18 deletions
  1. 25 1
      packages/fcl-hash/src/fprsa.pas
  2. 20 17
      packages/fcl-web/src/jwt/fpjwarsa.pp

+ 25 - 1
packages/fcl-hash/src/fprsa.pas

@@ -14,6 +14,17 @@ uses
 const
   RSAPublicKeyOID = '1.2.840.113549.1.1.1';
 
+  RSADigestInfoSHA256 = 1;
+  RSADigestInfoSHA384 = 2;
+  RSADigestInfoSHA512 = 3;
+  RSADigestInfoSHA224 = 4;
+  RSADigestInfoSHA512_224 = 5;
+  RSADigestInfoSHA512_256 = 6;
+  RSADigestInfoSHA3_224 = 7;
+  RSADigestInfoSHA3_256 = 8;
+  RSADigestInfoSHA3_384 = 9;
+  RSADigestInfoSHA3_512 = 10;
+
 type
   TRSA = record
     M: PBigInt;             // Modulus
@@ -81,6 +92,8 @@ function RSADecryptVerify(var RSA: TRSA; const Input: PByte; Output: PByte; Len:
 function RS256VerifyFromPublicKeyHexa(const PublicKeyHexa, SignatureBaseHash, Signature: String): Boolean;
 function TestRS256Verify: Boolean;
 
+function EncodeDigestInfoSHA(SHAType, len: byte): TBytes;
+
 implementation
 
 const
@@ -330,7 +343,7 @@ begin
     Exit;
   Size := RSA.ModulusLen;
   Padding := Size-Len-3;
-  if Len > Size-11 then
+  if Len > Size-8-3 then
     Exit;
   {$IFDEF CRYPTO_DEBUG}
   writeln('RSAEncryptSign - Len = ' + IntToStr(Len) + ' Size = ' + IntToStr(Size) + ' Padding = ' + IntToStr(Padding)); //To Do
@@ -642,5 +655,16 @@ begin
   Result := RsaVerify(Modulus, Exponent, Hash, Signature);
 end;
 
+function EncodeDigestInfoSHA(SHAType, len: byte): TBytes;
+begin
+  Result:= [
+    ASN1_SEQ, 17 + len,
+      ASN1_SEQ, 13,
+        ASN1_OBJID, 9, 2*40 + 16, $86, $48, 1, 101, 3, 4, 2, SHAType,
+        ASN1_NULL, 0,
+      ASN1_OCTSTR, len
+    ];
+end;
+
 end.
 

+ 20 - 17
packages/fcl-web/src/jwt/fpjwarsa.pp

@@ -14,9 +14,9 @@ Type
   TJWTSignerRSA = Class(TJWTSigner)
   Public
     Class function AlgorithmName : String; override;
-    function ComputeHash(const Value: TBytes): TBytes; virtual; abstract;
-    Function CreateSignature(aJWT : TJWT; aKey : TJWTKey) : String; override;
-    Function Verify(const aJWT : String; aKey : TJWTKey) : Boolean; override; overload;
+    function ComputeASNHash(const Value: TBytes): TBytes; virtual; abstract;
+    Function CreateSignature(aJWT : TJWT; aPrivateKey : TJWTKey) : String; override;
+    Function Verify(const aJWT : String; aPublicKey : TJWTKey) : Boolean; override; overload;
   end;
   TJWTSignerRSAClass = class of TJWTSignerRSA;
 
@@ -25,7 +25,7 @@ Type
   TJWTSignerRS256 = class(TJWTSignerRSA)
   public
     class function AlgorithmName : String; override;
-    function ComputeHash(const Value: TBytes): TBytes; override;
+    function ComputeASNHash(const Value: TBytes): TBytes; override;
   end;
 
   { TJWTSignerRS384 }
@@ -33,7 +33,7 @@ Type
   TJWTSignerRS384 = class(TJWTSignerRSA)
   public
     class function AlgorithmName : String; override;
-    function ComputeHash(const Value: TBytes): TBytes; override;
+    function ComputeASNHash(const Value: TBytes): TBytes; override;
   end;
 
   { TJWTSignerRS512 }
@@ -41,7 +41,7 @@ Type
   TJWTSignerRS512 = class(TJWTSignerRSA)
   public
     class function AlgorithmName : String; override;
-    function ComputeHash(const Value: TBytes): TBytes; override;
+    function ComputeASNHash(const Value: TBytes): TBytes; override;
   end;
 
 implementation
@@ -53,10 +53,11 @@ begin
   Result:='RS512';
 end;
 
-function TJWTSignerRS512.ComputeHash(const Value: TBytes): TBytes;
+function TJWTSignerRS512.ComputeASNHash(const Value: TBytes): TBytes;
 begin
   Result:=nil;
   TSHA512.DigestBytes(Value,Result);
+  Result:=Concat(EncodeDigestInfoSHA(RSADigestInfoSHA512,length(Result)),Result);
 end;
 
 { TJWTSignerRS384 }
@@ -66,10 +67,11 @@ begin
   Result:='RS384';
 end;
 
-function TJWTSignerRS384.ComputeHash(const Value: TBytes): TBytes;
+function TJWTSignerRS384.ComputeASNHash(const Value: TBytes): TBytes;
 begin
   Result:=nil;
   TSHA384.DigestBytes(Value,Result);
+  Result:=Concat(EncodeDigestInfoSHA(RSADigestInfoSHA384,length(Result)),Result);
 end;
 
 { TJWTSignerRS256 }
@@ -79,10 +81,11 @@ begin
   Result:='RS256';
 end;
 
-function TJWTSignerRS256.ComputeHash(const Value: TBytes): TBytes;
+function TJWTSignerRS256.ComputeASNHash(const Value: TBytes): TBytes;
 begin
   Result:=nil;
   TSHA256.DigestBytes(Value,Result);
+  Result:=Concat(EncodeDigestInfoSHA(RSADigestInfoSHA256,length(Result)),Result);
 end;
 
 { TJWTSignerRSA }
@@ -93,9 +96,9 @@ begin
   Result:='RSA';
 end;
 
-function TJWTSignerRSA.CreateSignature(aJWT: TJWT; aKey: TJWTKey): String;
+function TJWTSignerRSA.CreateSignature(aJWT: TJWT; aPrivateKey: TJWTKey): String;
 var
-  aSignInput, Hash, aSignature: TBytes;
+  aSignInput, ASNHash, aSignature: TBytes;
   RSA: TRSA;
 begin
   Result:='';
@@ -103,13 +106,13 @@ begin
   aSignInput:=GetSignInput(aJWT);
   if length(aSignInput)=0 then
     raise Exception.Create('20220430010854: missing SignInput');
-  Hash:=ComputeHash(aSignInput);
+  ASNHash:=ComputeASNHash(aSignInput);
 
   RSACreate(RSA);
   try
-    RSAInitFromPrivateKeyDER(RSA,aKey.AsBytes);
+    RSAInitFromPrivateKeyDER(RSA,aPrivateKey.AsBytes);
     SetLength(aSignature{%H-},RSA.ModulusLen);
-    if RSAEncryptSign(RSA,@Hash[0],length(Hash),@aSignature[0],true)<RSA.ModulusLen then
+    if RSAEncryptSign(RSA,@ASNHash[0],length(ASNHash),@aSignature[0],true)<RSA.ModulusLen then
       raise Exception.Create('20220429223334');
     Result:=Base64URL.Encode(@aSignature[0],Length(aSignature),False);
   finally
@@ -117,7 +120,7 @@ begin
   end;
 end;
 
-function TJWTSignerRSA.Verify(const aJWT: String; aKey: TJWTKey): Boolean;
+function TJWTSignerRSA.Verify(const aJWT: String; aPublicKey: TJWTKey): Boolean;
 var
   aHeader, theClaims, aSignature, aInput: String;
   InputBytes, EncryptedHash, DecryptedHash, ActualHash: TBytes;
@@ -135,7 +138,7 @@ begin
   // decrypt hash
   RSACreate(RSA);
   try
-    RSAInitFromPublicKeyDER(RSA,aKey.AsBytes);
+    RSAInitFromPublicKeyDER(RSA,aPublicKey.AsBytes);
     SetLength(DecryptedHash{%H-},length(EncryptedHash));
     HashLen:=RSADecryptVerify(RSA,@EncryptedHash[0],@DecryptedHash[0],length(DecryptedHash),true);
     if HashLen<=0 then exit;
@@ -148,7 +151,7 @@ begin
   aInput:=aHeader+'.'+theClaims;
   SetLength(InputBytes{%H-},length(aInput));
   Move(aInput[1],InputBytes[0],length(aInput));
-  ActualHash:=ComputeHash(InputBytes);
+  ActualHash:=ComputeASNHash(InputBytes);
 
   // check decrypted hash and actual hash fit
   Result:=(length(DecryptedHash)=length(ActualHash))