tcjwt.pp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. unit tcjwt;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, fpcunit, testregistry, fpjwt;
  6. type
  7. { TMyClaims }
  8. TMyClaims = Class(TClaims)
  9. private
  10. FAdmin: Boolean;
  11. FName: string;
  12. Published
  13. Property Name : string Read FName Write FName;
  14. Property admin : Boolean Read FAdmin Write FAdmin;
  15. end;
  16. { TMyJWT }
  17. TMyJWT = Class(TJWT)
  18. Function CreateClaims : TClaims; override;
  19. end;
  20. { TTestJWT }
  21. TTestJWT= class(TTestCase)
  22. private
  23. FJWT: TJWT;
  24. FKey : TJWTKey;
  25. FVerifyResult : TJWT;
  26. protected
  27. procedure SetUp; override;
  28. procedure TearDown; override;
  29. Property JWT : TJWT Read FJWT;
  30. Property Key : TJWTKey Read FKey;
  31. published
  32. procedure TestSignNone;
  33. procedure TestVerifyNone;
  34. procedure TestSignSHA256;
  35. procedure TestVerifySHA256;
  36. procedure TestSignSHA512;
  37. procedure TestVerifySHA512;
  38. procedure TestSignSHA384;
  39. procedure TestVerifySHA384;
  40. procedure TestVerifyES256;
  41. procedure TestVerifyES256Pem;
  42. end;
  43. implementation
  44. uses
  45. basenenc, sha256, fpjwasha256, sha512, fpjwasha512, fpjwasha384, fpjwaes256, fpecc, fppem;
  46. { TMyJWT }
  47. function TMyJWT.CreateClaims: TClaims;
  48. begin
  49. Result:=TMyClaims.Create;
  50. end;
  51. procedure TTestJWT.TestSignNone;
  52. Var
  53. P1,P2 : String;
  54. begin
  55. P1:=FJWT.JOSE.AsEncodedString;
  56. P2:=FJWT.Claims.AsEncodedString;
  57. AssertEquals('Signed with none',P1+'.'+P2+'.',FJWT.Sign(TJWTKey.Empty));
  58. end;
  59. procedure TTestJWT.TestVerifyNone;
  60. Var
  61. aJWT : String;
  62. begin
  63. aJWT:=FJWT.AsEncodedString;
  64. FVerifyResult:=TJWT.ValidateJWT(aJWT,TJWTKey.Empty,TMyJWT);
  65. AssertNotNull('Have result',FVerifyResult);
  66. AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
  67. end;
  68. procedure TTestJWT.TestSignSHA256;
  69. Var
  70. Sign,P1,P2 : UTF8String;
  71. aDigest : TSHA256Digest;
  72. B : TBytes;
  73. begin
  74. FJWT.JOSE.alg:='HS256';
  75. // Writeln('JOSE: ',FJWT.JOSE.AsString);
  76. // Writeln('Claims: ',FJWT.Claims.AsString);
  77. P1:=FJWT.JOSE.AsEncodedString;
  78. P2:=FJWT.Claims.AsEncodedString;
  79. B:=TEncoding.UTF8.GetAnsiBytes(P1+'.'+P2);
  80. if not TSHA256.HMAC(FKey.AsPointer,FKey.Length,PByte(B),Length(B),aDigest) then
  81. Fail('Could not HMAC');
  82. Sign:=Base64URL.Encode(@aDigest[0],Length(aDigest),False);
  83. // Writeln('Signed: ',P1+'.'+P2+'.'+Sign);
  84. AssertEquals('Signed with SHA256',P1+'.'+P2+'.'+Sign,FJWT.Sign(FKey));
  85. end;
  86. procedure TTestJWT.TestVerifySHA256;
  87. Const
  88. JWTText ='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.'+
  89. 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.'+
  90. 'SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
  91. begin
  92. FKey:=TJWTKey.Create('your-256-bit-secret');
  93. FVerifyResult:=TJWT.ValidateJWT(JWTText,FKey);
  94. AssertNotNull('Have result',FVerifyResult);
  95. AssertEquals('Have correct algorithm','HS256',FVerifyResult.JOSE.Alg);
  96. AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
  97. AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
  98. end;
  99. procedure TTestJWT.TestSignSHA512;
  100. Var
  101. Sign,P1,P2 : UTF8String;
  102. aDigest : TSHA512Digest;
  103. B : TBytes;
  104. begin
  105. FJWT.JOSE.alg:='HS512';
  106. // Writeln('JOSE: ',FJWT.JOSE.AsString);
  107. // Writeln('Claims: ',FJWT.Claims.AsString);
  108. P1:=FJWT.JOSE.AsEncodedString;
  109. P2:=FJWT.Claims.AsEncodedString;
  110. B:=TEncoding.UTF8.GetAnsiBytes(P1+'.'+P2);
  111. if not TSHA512.HMAC(FKey.AsPointer,FKey.Length,PByte(B),Length(B),aDigest) then
  112. Fail('Could not HMAC');
  113. Sign:=Base64URL.Encode(@aDigest[0],Length(aDigest),False);
  114. // Writeln('Signed: ',P1+'.'+P2+'.'+Sign);
  115. AssertEquals('Signed with SHA512',P1+'.'+P2+'.'+Sign,FJWT.Sign(FKey));
  116. end;
  117. procedure TTestJWT.TestVerifySHA512;
  118. Const
  119. JWTText = 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.'+
  120. 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.'+
  121. 'FEBOl5fjgnPe4gcc5ElXrHDl0jWsshiJ9rS0hlehItc-PKQEzwRKbhcz69V8kwRCUM2rDtuwaXK6DJfO1VOZdw';
  122. begin
  123. FKey:=TJWTKey.Create('mysecretkey');
  124. FVerifyResult:=TMyJWT.ValidateJWT(JWTText,FKey);
  125. AssertNotNull('Have result',FVerifyResult);
  126. AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
  127. AssertNotNull('Have result.claims',FVerifyResult.Claims);
  128. AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
  129. AssertEquals('Have correct algorithm','HS512',FVerifyResult.JOSE.Alg);
  130. AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
  131. AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
  132. AssertEquals('Have correct name','John Doe',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
  133. AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
  134. end;
  135. procedure TTestJWT.TestSignSHA384;
  136. Var
  137. Sign,P1,P2 : UTF8String;
  138. aDigest : TSHA384Digest;
  139. B : TBytes;
  140. begin
  141. FJWT.JOSE.alg:='HS384';
  142. // Writeln('JOSE: ',FJWT.JOSE.AsString);
  143. // Writeln('Claims: ',FJWT.Claims.AsString);
  144. P1:=FJWT.JOSE.AsEncodedString;
  145. P2:=FJWT.Claims.AsEncodedString;
  146. B:=TEncoding.UTF8.GetAnsiBytes(P1+'.'+P2);
  147. if not TSHA384.HMAC(FKey.AsPointer,FKey.Length,PByte(B),Length(B),aDigest) then
  148. Fail('Could not HMAC');
  149. Sign:=Base64URL.Encode(@aDigest[0],Length(aDigest),False);
  150. // Writeln('Signed: ',P1+'.'+P2+'.'+Sign);
  151. AssertEquals('Signed with SHA384',P1+'.'+P2+'.'+Sign,FJWT.Sign(FKey));
  152. end;
  153. procedure TTestJWT.TestVerifySHA384;
  154. Const
  155. JWTText =
  156. 'eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.'+
  157. 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.'+
  158. '8XBKpuFoIEyTxqiP7Rw32VkkxSPGrujBw2ZiKgcX5ZgjH3M8OmTWfYeRDAR6NRVB';
  159. begin
  160. FKey:=TJWTKey.Create('mysecretkey');
  161. FVerifyResult:=TMyJWT.ValidateJWT(JWTText,FKey);
  162. AssertNotNull('Have result',FVerifyResult);
  163. AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
  164. AssertNotNull('Have result.claims',FVerifyResult.Claims);
  165. AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
  166. AssertEquals('Have correct algorithm','HS384',FVerifyResult.JOSE.Alg);
  167. AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
  168. AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
  169. AssertEquals('Have correct name','John Doe',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
  170. AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
  171. end;
  172. procedure TTestJWT.TestVerifyES256;
  173. Const
  174. // from JWT.IO
  175. aJWT =
  176. 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.'+
  177. 'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.'+
  178. 'tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA';
  179. Var
  180. aPrivateKey2: TEccPrivateKey = ($7a,$f6,$73,$2f,$58,$1d,$00,$5a,$fc,$f2,$16,$f6,$38,$5f,$f6,
  181. $37,$10,$29,$24,$2c,$c6,$08,$40,$dd,$7d,$2a,$7a,$55,$03,$b7,
  182. $d2,$1c);
  183. begin
  184. FKey:=TJWTKey.Create(@aPrivateKey2,SizeOf(TEccPrivateKey));
  185. FVerifyResult:=TMyJWT.ValidateJWT(aJWT,FKey);
  186. AssertNotNull('Have result',FVerifyResult);
  187. AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
  188. AssertNotNull('Have result.claims',FVerifyResult.Claims);
  189. AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
  190. AssertEquals('Have correct algorithm','ES256',FVerifyResult.JOSE.Alg);
  191. AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
  192. AssertEquals('Have correct sub','1234567890',FVerifyResult.Claims.sub);
  193. AssertEquals('Have correct name','John Doe',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
  194. AssertEquals('Have correct admin',true,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
  195. end;
  196. procedure TTestJWT.TestVerifyES256Pem;
  197. Const
  198. aInput =
  199. 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.' +
  200. 'eyJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0OTAyMiwiaXNzIjoiRGVscGhpIEpPU0UgYW5kIEpXVCBMaWJyYXJ5In0.'+
  201. '4QDMKAvHwb6pA5fN0oQjlzuKmPIlNpmIQ8vPH7zy4fjZdtcPVJMtfiVhztwQldQL9A5yzBKI8q2puVygm-2Adw';
  202. // Private key in PEM format
  203. Const APrivateKeyPem =
  204. '-----BEGIN EC PRIVATE KEY-----'+ #10+
  205. 'MHcCAQEEIFzS3/5bCnrlpa4902/zkYzURF6E2D8pazgnJu4smhpQoAoGCCqGSM49'+ #10+
  206. 'AwEHoUQDQgAEqTjyg2z65i+zbyUZW8BQ+K87DNsICRaEH7Fy7Rm3MseXy9ItSCQU'+ #10+
  207. 'VeJbtO6kYUA00mx7bKoC1sx5sbtFExnYPQ=='+ #10+
  208. '-----END EC PRIVATE KEY-----';
  209. Var
  210. S : TStringStream;
  211. aPrivateKey : TEccPrivateKey;
  212. aPublicKey : TEccPublicKey;
  213. X,Y : AnsiString;
  214. begin
  215. S:=TStringStream.Create(aPrivateKeyPem);
  216. try
  217. PemLoadECDSA(S,aPrivateKey,aPublicKey,X,Y);
  218. finally
  219. S.Free;
  220. end;
  221. FKey:=TJWTKey.Create(@aPrivateKey,SizeOf(TEccPrivateKey));
  222. FVerifyResult:=TMyJWT.ValidateJWT(aInput,FKey);
  223. AssertNotNull('Have result',FVerifyResult);
  224. AssertEquals('Correct class',TMyJWT,FVerifyResult.ClassType);
  225. AssertNotNull('Have result.claims',FVerifyResult.Claims);
  226. AssertEquals('Correct claims class',TMyClaims,FVerifyResult.Claims.ClassType);
  227. AssertEquals('Have correct algorithm','ES256',FVerifyResult.JOSE.Alg);
  228. AssertEquals('Have correct typ','JWT',FVerifyResult.JOSE.typ);
  229. AssertEquals('Have correct sub','',FVerifyResult.Claims.sub);
  230. AssertEquals('Have correct name','',(TMyJWT(FVerifyResult).Claims as TMyClaims).Name);
  231. AssertEquals('Have correct admin',False,(TMyJWT(FVerifyResult).Claims as TMyClaims).Admin);
  232. end;
  233. procedure TTestJWT.SetUp;
  234. begin
  235. Inherited;
  236. FKey:=TJWTKey.Create('mysecretkey');
  237. FJWT:=TMyJWT.Create;
  238. FJWT.JOSE.alg:='none';
  239. FJWT.JOSE.typ:='JWT';
  240. FJWT.Claims.sub:='1234567890';
  241. FJWT.Claims.iat:=1516239022;
  242. (FJWT.Claims as TMyClaims).Name:='John Doe';
  243. end;
  244. procedure TTestJWT.TearDown;
  245. begin
  246. FreeAndNil(FJWT);
  247. FreeAndNil(FVerifyResult);
  248. Inherited;
  249. end;
  250. initialization
  251. RegisterTest(TTestJWT);
  252. end.