mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-17 12:39:25 +02:00
ES256 signing algorithm
This commit is contained in:
parent
6ae8840ece
commit
5afaeaa3ac
@ -334,6 +334,8 @@ begin
|
||||
T.Dependencies.AddUnit('fpjwt');
|
||||
T:=P.Targets.AddUnit('fpjwasha384.pp');
|
||||
T.Dependencies.AddUnit('fpjwt');
|
||||
T:=P.Targets.AddUnit('fpjwaes256.pp');
|
||||
T.Dependencies.AddUnit('fpjwt');
|
||||
T:=P.Targets.AddUnit('fphttpwebclient.pp');
|
||||
T.Dependencies.AddUnit('fpwebclient');
|
||||
T:=P.Targets.AddUnit('restbase.pp');
|
||||
|
95
packages/fcl-web/src/jwt/fpjwaes256.pp
Normal file
95
packages/fcl-web/src/jwt/fpjwaes256.pp
Normal file
@ -0,0 +1,95 @@
|
||||
unit fpjwaes256;
|
||||
|
||||
{$mode ObjFPC}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, fpjwt, Ecc;
|
||||
|
||||
Type
|
||||
{ TJWTSignerES256 }
|
||||
|
||||
TJWTSignerES256 = Class(TJWTSigner)
|
||||
Public
|
||||
Class function AlgorithmName : String; override;
|
||||
Function CreateSignature(aJWT : TJWT; aKey : TJWTKey) : String; override;
|
||||
Function Verify(const aJWT : String; aKey : TJWTKey) : Boolean; override; overload;
|
||||
Class Function Verify(const aJWT : String; aPrivateKey : TECCPrivateKey) : Boolean; overload;
|
||||
Class Function Verify(const aJWT : String; aPublicKey : TECCPublicKey) : Boolean; overload;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses hashutils, basenenc, ecdsa;
|
||||
|
||||
{ TJWTSignerES256 }
|
||||
|
||||
class function TJWTSignerES256.AlgorithmName: String;
|
||||
begin
|
||||
Result:='ES256';
|
||||
end;
|
||||
|
||||
function TJWTSignerES256.CreateSignature(aJWT: TJWT; aKey: TJWTKey): String;
|
||||
Var
|
||||
B : TBytes;
|
||||
aPrivateKey : TEccPrivateKey;
|
||||
aSignature : TEccSignature;
|
||||
|
||||
begin
|
||||
Result:='';
|
||||
aPrivateKey:=Default(TECCPrivateKey);
|
||||
Move(aKey.AsPointer,aPrivateKey,Sizeof(aPrivateKey));
|
||||
B:=GetSignInput(aJWT);
|
||||
if TECDSA.SignSHA256(B,aPrivateKey,aSignature) then
|
||||
Result:=Base64URL.Encode(@aSignature[0],Length(aSignature),False);
|
||||
end;
|
||||
|
||||
function TJWTSignerES256.Verify(const aJWT: String; aKey: TJWTKey): Boolean;
|
||||
|
||||
var
|
||||
aPrivateKey : TECCPrivateKey;
|
||||
|
||||
begin
|
||||
aPrivateKey:=Default(TECCPrivateKey);
|
||||
Move(aKey.AsPointer^,aPrivateKey,Sizeof(aPrivateKey));
|
||||
Result:=Verify(aJWT,aPrivateKey);
|
||||
end;
|
||||
|
||||
Class function TJWTSignerES256.Verify(const aJWT: String; aPrivateKey: TECCPrivateKey): Boolean;
|
||||
|
||||
Var
|
||||
J,C,S : AnsiString;
|
||||
aSignature : TEccSignature;
|
||||
B : TBytes;
|
||||
|
||||
begin
|
||||
Result:=GetParts(aJWT,J,C,S);
|
||||
if Not Result then
|
||||
exit;
|
||||
B:=TEncoding.UTF8.GetAnsiBytes(J+'.'+C);
|
||||
BytesToVar(Base64url.Decode(S),aSignature,Sizeof(aSignature));
|
||||
Result:=TECDSA.verifySHA256(B,aPrivateKey,aSignature);
|
||||
end;
|
||||
|
||||
class function TJWTSignerES256.Verify(const aJWT: String; aPublicKey: TECCPublicKey): Boolean;
|
||||
|
||||
Var
|
||||
J,C,S : AnsiString;
|
||||
aSignature : TEccSignature;
|
||||
B : TBytes;
|
||||
|
||||
begin
|
||||
Result:=GetParts(aJWT,J,C,S);
|
||||
if Not Result then
|
||||
exit;
|
||||
B:=TEncoding.UTF8.GetAnsiBytes(J+'.'+C);
|
||||
Base64url.Decode(S,@aSignature);
|
||||
Result:=TECDSA.verifySHA256(B,aPublicKey,aSignature);
|
||||
end;
|
||||
|
||||
initialization
|
||||
TJWTSignerES256.Register;
|
||||
end.
|
||||
|
@ -35,6 +35,7 @@ Type
|
||||
procedure SetLength(AValue: Integer);
|
||||
public
|
||||
Bytes : TBytes;
|
||||
Class Function Create(aData : PByte; aSize : Word) : TJWTKey; static;
|
||||
Class Function Create(aBytes : TBytes) : TJWTKey; static;
|
||||
Class Function Create(aString : UTF8String) : TJWTKey; static;
|
||||
Class Function Empty : TJWTKey; static;
|
||||
@ -244,6 +245,18 @@ begin
|
||||
System.SetLength(Bytes,aValue)
|
||||
end;
|
||||
|
||||
class function TJWTKey.Create(aData: PByte; aSize : Word): TJWTKey;
|
||||
|
||||
Var
|
||||
B : TBytes;
|
||||
|
||||
begin
|
||||
B:=[];
|
||||
System.SetLength(B,aSize);
|
||||
Move(aData^,B[0],aSize);
|
||||
Result:=Create(B);
|
||||
end;
|
||||
|
||||
class function TJWTKey.Create(aBytes: TBytes): TJWTKey;
|
||||
begin
|
||||
Result.AsBytes:=aBytes;
|
||||
|
@ -47,11 +47,14 @@ type
|
||||
procedure TestVerifySHA512;
|
||||
procedure TestSignSHA384;
|
||||
procedure TestVerifySHA384;
|
||||
procedure TestVerifyES256;
|
||||
procedure TestVerifyES256Pem;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses basenenc, sha256, fpjwasha256, sha512, fpjwasha512, fpjwasha384;
|
||||
uses
|
||||
basenenc, sha256, fpjwasha256, sha512, fpjwasha512, fpjwasha384, fpjwaes256, ecc, pem;
|
||||
|
||||
{ TMyJWT }
|
||||
|
||||
@ -178,7 +181,7 @@ begin
|
||||
if not TSHA384.HMAC(FKey.AsPointer,FKey.Length,PByte(B),Length(B),aDigest) then
|
||||
Fail('Could not HMAC');
|
||||
Sign:=Base64URL.Encode(@aDigest[0],Length(aDigest),False);
|
||||
Writeln('Signed: ',P1+'.'+P2+'.'+Sign);
|
||||
// Writeln('Signed: ',P1+'.'+P2+'.'+Sign);
|
||||
AssertEquals('Signed with SHA384',P1+'.'+P2+'.'+Sign,FJWT.Sign(FKey));
|
||||
end;
|
||||
|
||||
@ -204,7 +207,81 @@ begin
|
||||
AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
|
||||
end;
|
||||
|
||||
procedure TTestJWT.TestVerifyES256;
|
||||
Const
|
||||
// from JWT.IO
|
||||
aJWT =
|
||||
'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.'+
|
||||
'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.'+
|
||||
'tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA';
|
||||
|
||||
Var
|
||||
aPrivateKey2: TEccPrivateKey = ($7a,$f6,$73,$2f,$58,$1d,$00,$5a,$fc,$f2,$16,$f6,$38,$5f,$f6,
|
||||
$37,$10,$29,$24,$2c,$c6,$08,$40,$dd,$7d,$2a,$7a,$55,$03,$b7,
|
||||
$d2,$1c);
|
||||
|
||||
|
||||
begin
|
||||
FKey:=TJWTKey.Create(@aPrivateKey2,SizeOf(TEccPrivateKey));
|
||||
FVerifyResult:=TMyJWT.ValidateJWT(aJWT,FKey);
|
||||
AssertNotNull('Have result',FVerifyResult);
|
||||
AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
|
||||
AssertNotNull('Have result.claims',FVerifyResult.Claims);
|
||||
AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
|
||||
AssertEquals('Have correct algorithm','ES256',FVerifyResult.JOSE.Alg);
|
||||
AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
|
||||
AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
|
||||
AssertEquals('Have correct name','John Doe',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
|
||||
AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
|
||||
|
||||
end;
|
||||
|
||||
procedure TTestJWT.TestVerifyES256Pem;
|
||||
|
||||
Const
|
||||
aInput =
|
||||
'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.' +
|
||||
'eyJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0OTAyMiwiaXNzIjoiRGVscGhpIEpPU0UgYW5kIEpXVCBMaWJyYXJ5In0.'+
|
||||
'4QDMKAvHwb6pA5fN0oQjlzuKmPIlNpmIQ8vPH7zy4fjZdtcPVJMtfiVhztwQldQL9A5yzBKI8q2puVygm-2Adw';
|
||||
|
||||
// Private key in PEM format
|
||||
Const APrivateKeyPem =
|
||||
'-----BEGIN EC PRIVATE KEY-----'+ #10+
|
||||
'MHcCAQEEIFzS3/5bCnrlpa4902/zkYzURF6E2D8pazgnJu4smhpQoAoGCCqGSM49'+ #10+
|
||||
'AwEHoUQDQgAEqTjyg2z65i+zbyUZW8BQ+K87DNsICRaEH7Fy7Rm3MseXy9ItSCQU'+ #10+
|
||||
'VeJbtO6kYUA00mx7bKoC1sx5sbtFExnYPQ=='+ #10+
|
||||
'-----END EC PRIVATE KEY-----';
|
||||
|
||||
|
||||
Var
|
||||
S : TStringStream;
|
||||
aPrivateKey : TEccPrivateKey;
|
||||
aPublicKey : TEccPublicKey;
|
||||
X,Y : AnsiString;
|
||||
|
||||
begin
|
||||
S:=TStringStream.Create(aPrivateKeyPem);
|
||||
try
|
||||
PemLoadECDSA(S,aPrivateKey,aPublicKey,X,Y);
|
||||
finally
|
||||
S.Free;
|
||||
end;
|
||||
FKey:=TJWTKey.Create(@aPrivateKey,SizeOf(TEccPrivateKey));
|
||||
FVerifyResult:=TMyJWT.ValidateJWT(aInput,FKey);
|
||||
AssertNotNull('Have result',FVerifyResult);
|
||||
AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
|
||||
AssertNotNull('Have result.claims',FVerifyResult.Claims);
|
||||
AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
|
||||
AssertEquals('Have correct algorithm','ES256',FVerifyResult.JOSE.Alg);
|
||||
AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
|
||||
AssertEquals('Have correct sub','',FVerifyResult.Claims.sub);
|
||||
AssertEquals('Have correct name','',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
|
||||
AssertEquals('Have correct admin',False,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
|
||||
end;
|
||||
|
||||
procedure TTestJWT.SetUp;
|
||||
|
||||
|
||||
begin
|
||||
Inherited;
|
||||
FKey:=TJWTKey.Create('mysecretkey');
|
||||
|
@ -19,13 +19,13 @@
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<local>
|
||||
<CommandLineParams Value="--suite=TTestJWT"/>
|
||||
<CommandLineParams Value="--suite=TTestJWT.TestVerifyES256"/>
|
||||
</local>
|
||||
<FormatVersion Value="2"/>
|
||||
<Modes Count="1">
|
||||
<Mode0 Name="default">
|
||||
<local>
|
||||
<CommandLineParams Value="--suite=TTestJWT"/>
|
||||
<CommandLineParams Value="--suite=TTestJWT.TestVerifyES256"/>
|
||||
</local>
|
||||
</Mode0>
|
||||
</Modes>
|
||||
@ -35,7 +35,7 @@
|
||||
<PackageName Value="FCL"/>
|
||||
</Item1>
|
||||
</RequiredPackages>
|
||||
<Units Count="7">
|
||||
<Units Count="8">
|
||||
<Unit0>
|
||||
<Filename Value="testfpweb.lpr"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
@ -64,6 +64,10 @@
|
||||
<Filename Value="../src/jwt/fpjwasha384.pp"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit6>
|
||||
<Unit7>
|
||||
<Filename Value="../src/jwt/fpjwaes256.pp"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit7>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
|
@ -3,7 +3,8 @@ program testfpweb;
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
uses
|
||||
Classes, consoletestrunner, tchttproute, tcjwt, jsonparser, fpjwasha256, fpjwasha512, fpjwasha384;
|
||||
Classes, consoletestrunner, tchttproute, tcjwt, jsonparser,
|
||||
fpjwasha256, fpjwasha512, fpjwasha384, fpjwaes256;
|
||||
|
||||
type
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user