Giter Club home page Giter Club logo

delphi-jose-jwt's Introduction

Delphi JOSE and JWT Library


Delphi JWT Library

What is Delphi JOSE and JWT Library

Top language GitHub license GitHub issues GitHub PR GitHub release GitHub commit activity GitHub last commit GitHub contributors

Delphi implementation of JWT (JSON Web Token) and the JOSE (JSON Object Signing and Encryption) specification suite. This library supports the JWS (JWE support is planned) compact serializations with several JOSE algorithms.

Image of Delphi-JOSE Demo

πŸ“š Articles about using Delphi-JOSE

⚠️ Important: OpenSSL requirements

HMAC using SHA algorithm

Prior to Delphi 10 Seattle the the HMAC-SHA algorithm uses OpenSSL through the Indy library, so in order to generate the token you should have the OpenSSL DLLs in your server system.

In Delphi 10 Seattle or newer Delphi versions the HMAC algorithm is also in the System.Hash unit so OpenSSL is not needed.

HMAC using RSA or ECDSA algorithm

The HMAC-RSA(ECDSA) algorithm uses necessarily OpenSSL so if you plan to use these algorithms to sign your token you have to download and deploy OpenSSL (on the server).

Client-side considerations

Please keep in mind that the client doesn't have to generate or verify the token (using SHA or RSA) so on the client-side there's no need for the OpenSSL DLLs.

OpenSSL download

If you need the OpenSSL library on the server, you can download the package directly to the Indy's GitHub project page (keep in mind to always update to the latest version and to match you application's bitness)

❓ What is JOSE

JOSE is a standard that provides a general approach to the signing and encryption of any content. JOSE consists of several RFC:

⚑ General Features

Token serialization

  • One method call to serialize a token

Token deserialization

  • One method call to validate and deserialize a compact token

Token & Claims validation (Consumer)

Algorithms Supported
exp βœ”οΈ
iat βœ”οΈ
nbf βœ”οΈ
aud βœ”οΈ
iss βœ”οΈ
jti βœ”οΈ
typ βœ”οΈ

Easy to use classes for compact token productiom

  • Easy to use TJOSEProducer and TJOSEProducerBuilder (alias TJOSEProcess) classes to build a new compact token with many options

Easy to use classes for custom validation

  • Easy to use TJOSEConsumer and TJOSEConsumerBuilder classes to validate token with a fine granularity
  • Easy to write custom validators!

Signing algorithms

Algorithms Supported
None βœ”οΈ don't use it! πŸ’€
HS256 βœ”οΈ
HS384 βœ”οΈ
HS512 βœ”οΈ
RS256 βœ”οΈ updated! πŸ”₯
RS384 βœ”οΈ updated! πŸ”₯
RS512 βœ”οΈ updated! πŸ”₯
ES256 βœ”οΈ new! 🌟
ES384 βœ”οΈ new! 🌟
ES512 βœ”οΈ new! 🌟
ES256K βœ”οΈ new! 🌟

Security notes

Projects using Delphi JOSE and JWT

πŸ”§ Todo

Features
  • JWE support
  • Support of other crypto libraries (TMS Cryptography Pack, etc...)
Code
  • More unit tests
  • More examples

πŸͺ Prerequisite

This library has been tested with Delphi 12 Athens, Delphi 11 Alexandria, Delphi 10.4 Sydney, Delphi 10.3 Rio, Delphi 10.2 Tokyo but with some work it should compile with DXE6 and higher but I have not tried or tested this, if you succeed in this task I will be happy to create a branch of your work!

Libraries/Units dependencies

This library has no dependencies on external libraries/units.

Delphi units used:

  • System.JSON (DXE6+) (available on earlier Delphi versions as Data.DBXJSON)
  • System.Rtti (D2010+)
  • System.Generics.Collections (D2009+)
  • System.NetEncoding (DXE7+)
  • Indy units: IdHMAC, IdHMACSHA1, IdSSLOpenSSL, IdHash

Indy notes

πŸ’Ύ Installation

Manual installation

Simply add the source path "Source/Common" and Source/JOSE" to your Delphi project path and.. you are good to go!

Boss package manager

Using the boss install command:

$ boss install github.com/paolo-rossi/delphi-jose-jwt

πŸ“œ Quick Code Examples

Creating a token

To create a token, simply create an instance of the TJWT class and set the properties (claims).

Using TJOSE utility class

The easiest way to build a JWT token (compact representation) is to use the IJOSEProducer interface:

uses
  JOSE.Producer;

var
  LResult: string;
begin
  LResult := TJOSEProcess.New
    .SetIssuer('Delphi JOSE Library')
    .SetIssuedAt(Now)
    .SetExpiration(Now + 1)
    .SetAlgorithm(LAlg)
    .SetKey(TJOSEAlgorithmId.HS256)
    .Build
    .GetCompactToken
  ;

  memoCompact.Lines.Add(LResult);
end;

Using TJOSE utility class

Another way to serialize, deserialize, verify a token is to use the TJOSEutility class:

uses
  JOSE.Core.JWT,
  JOSE.Core.Builder;

var
  LToken: TJWT;
  LCompactToken: string;
begin
  LToken := TJWT.Create;
  try
    // Token claims
    LToken.Claims.Issuer := 'WiRL REST Library';
    LToken.Claims.Subject := 'Paolo Rossi';
    LToken.Claims.Expiration := Now + 1;

    // Signing and Compact format creation
    LCompactToken := TJOSE.SHA256CompactToken('my_very_long_and_safe_secret_key', LToken);
    mmoCompact.Lines.Add(LCompactToken);
  finally
    LToken.Free;
  end;

Using TJWT, TJWS and TJWK classes

Using the TJWT, TJWS and TJWK classes you have more control over the creation of the final compact token.

var
  LToken: TJWT;
  LSigner: TJWS;
  LKey: TJWK;
  LAlg: TJOSEAlgorithmId;
begin
  LToken := TJWT.Create;
  try
    // Set your claims
    LToken.Claims.Subject := 'Paolo Rossi';
    LToken.Claims.Issuer := 'Delphi JOSE Library';
    LToken.Claims.IssuedAt := Now;
    LToken.Claims.Expiration := Now + 1;

    // Choose the signing algorithm
    case cbbAlgorithm.ItemIndex of
      0: LAlg := TJOSEAlgorithmId.HS256;
      1: LAlg := TJOSEAlgorithmId.HS384;
      2: LAlg := TJOSEAlgorithmId.HS512;
    else LAlg := TJOSEAlgorithmId.HS256;
    end;

    // Create your key from any text or TBytes
    LKey := TJWK.Create(edtSecret.Text);

    try
      // Create the signer
      LSigner := TJWS.Create(LToken);
      try
        // With this option you can have keys < algorithm length
        LSigner.SkipKeyValidation := True;

        // Sign the token!
        LSigner.Sign(LKey, LAlg);

        memoCompact.Lines.Add('Header: ' + LSigner.Header);
        memoCompact.Lines.Add('Payload: ' + LSigner.Payload);
        memoCompact.Lines.Add('Signature: ' + LSigner.Signature);
        memoCompact.Lines.Add('Compact Token: ' + LSigner.CompactToken);
      finally
        LSigner.Free;
      end;
    finally
      LKey.Free;
    end;  
  finally
    LToken.Free;
  end;

Unpack and verify a token's signature

Unpacking and verifying tokens is simple.

Using TJOSE utility class

You have to pass the key and the token compact format to the TJOSE.Verify class function

var
  LKey: TJWK;
  LToken: TJWT;
begin
  // Create the key from a text or TBytes
  LKey := TJWK.Create('my_very_long_and_safe_secret_key');

  // Unpack and verify the token!
  LToken := TJOSE.Verify(LKey, FCompactToken);

  if Assigned(LToken) then
  begin
    try
      if LToken.Verified then
        mmoJSON.Lines.Add('Token signature is verified')
      else
        mmoJSON.Lines.Add('Token signature is not verified')
    finally
      LToken.Free;
    end;
  end;

end;

Unpacking and token validation

Using the new class TJOSEConsumer it's very easy to validate the token's claims. The TJOSEConsumer object is built with the TJOSEConsumerBuilder utility class using the fluent interface.

var
  LConsumer: IJOSEConsumer;
begin
  LConsumer := TJOSEConsumerBuilder.NewConsumer
    .SetClaimsClass(TJWTClaims)

    // JWS-related validation
    .SetVerificationKey(edtConsumerSecret.Text)
    .SetSkipVerificationKeyValidation
    .SetDisableRequireSignature

    // string-based claims validation
    .SetExpectedSubject('paolo-rossi')
    .SetExpectedAudience(True, ['Paolo'])

    // Time-related claims validation
    .SetRequireIssuedAt
    .SetRequireExpirationTime
    .SetEvaluationTime(IncSecond(FNow, 26))
    .SetAllowedClockSkew(20, TJOSETimeUnit.Seconds)
    .SetMaxFutureValidity(20, TJOSETimeUnit.Minutes)

    // Build the consumer object
    .Build();

  try
    // Process the token with your rules!
    LConsumer.Process(Compact);
  except
    // (optionally) log the errors
    on E: Exception do
      memoLog.Lines.Add(E.Message);
  end;

Paolo Rossi

delphi-jose-jwt's People

Contributors

adrianosantostreina avatar bernd5 avatar ccy avatar dbcto avatar fabioxgn avatar fulgan avatar hunsche avatar markrsill avatar paolo-rossi avatar saxos-simone avatar stefanon-infotronics avatar viniciussanchez avatar wlandgraf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

delphi-jose-jwt's Issues

Multiple Token Audience

Hi,
there could be problem with the update of a token's audience.
After getting a token (with its audience value already set) I tried to update it with a new audience.
I used a string generated from stringlist, using comma separator.
The re-genrated token's payload contains both old and new audience.
Token seems legit, and online tools like jwt.io show only the last occurence of audience, but the library reads (and uses) only the first one.

unreliable Check fΓΌr Booleans in JSON

General

Problem

I lately noticed a very unhappy behavior in the JSON Boolean classes of Delphi. If you check for an true JSON boolean value like this:

Result := aValue is TJSONTrue;

You maybe get False as result, although it is a TJSONValue with a Boolean true inside. If you created it like this:

aValue := TJSONBool.Create(True);
// or with a default function, that creates a TJSONBool internal
var aObj: TJSONObject := TJSONObject.Create;
try
  // ...
  aObj.AddPair('myBoolean',True);
  // ...
finally
  FreeAndNil(aObj);
end;

the result is False. The reason for this is the hierarchy of inheritance. The classes TJSONTrue and TJSONFalse are derived from TJSONBool.

Solution

To have a reliable check for boolean in JSON you should use TJSONBool. For example like this:

if aValue is TJSONBool then
  Result := TJSONBool(aValue).AsBoolean
else
  Result := False; // or something other suitable value / action

Place in JWT

In JWT there is such unreliable check in the code in JOSE.Types.JSON.pas line 90-93.
I only randomly noticed it in JWT at search through a codebase including JWT. I didn't encountered a specific bug in JWT, that is triggered by this.

json.pas file is missing

While compiling the issue is system.json.pas file is missing how to get the correct system.json.pas file

Hasura JWT

Greetings. How to generate JWT that can contain the data according to this example?

{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "123456",
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": [
"user"
]
},
"iat": 1579192910,
"exp": 1579196510,
"sub": "132456",
"aud": "https://endpoint.com.br",
"name": "Empresa"
}

In python I can but in Delphi with Jose I still can not.

I'm grateful right now

Shortcut to TJOSE class functions inside TJWT

Today to generate a token, you have to make 2 uses, as the documentation says. And I have to do it like this:

function GetToken: string;
var
  LToken: TJWT;
begin
  LToken := TJWT.Create;
  try
    LToken.Claims.NotBefore := Now;
    LToken.Claims.SetClaimOfType('A', 'B');
    Result := TJOSE.SHA256CompactToken('key', LToken);
  finally
    LToken.Free;
  end;
end;

What do you think about creating a shortcut inside the TJWT class that calls TJOSE, looking like this:

function GetToken: string;
var
  LToken: TJWT;
begin
  LToken := TJWT.Create;
  try
    LToken.Claims.NotBefore := Now;
    LToken.Claims.SetClaimOfType('A', 'B');
    Result := LToken.SHA256CompactToken('key');
  finally
    LToken.Free;
  end;
end;

I could do this with all the functions of the TJOSE class. With that I would need to make just one uses to generate the token and it would be simpler and more practical.

iOS can not compile JOSE.OpenSSL.Headers

Hi
I'm using FB4D and trying to get it to compile on iOS
I get: [DCC Error] JOSE.OpenSSL.Headers.pas(168): E2003 Undeclared identifier: 'GetCryptLibHandle'
Because its IFDEFed out in the {$IFNDEF STATICLOAD_OPENSSL} of IdSSLOpenSSLHeaders.pas

There seems to be an issues between static and dynamic loaded functions?

I can move the 'GetCryptLibHandle' function out out of the IFDEF block to get a compile but that doesn't seem the right answer.

I am using the latest Indy from Github.

How do I resolve this, please

Regards
Alan

Minor optimization(s)

[FixInsight Optimization] JOSE.Core.JWT.pas(306): O801 CONST missing for unmodified string parameter 'Value'
[FixInsight Optimization] JOSE.Core.JWT.pas(355): O801 CONST missing for unmodified string parameter 'Value'
[FixInsight Optimization] JOSE.Core.JWT.pas(363): O801 CONST missing for unmodified string parameter 'Value'
[FixInsight Optimization] JOSE.Core.JWT.pas(379): O801 CONST missing for unmodified string parameter 'Value'
[FixInsight Optimization] JOSE.Core.JWT.pas(409): O801 CONST missing for unmodified string parameter 'Value'

Erro JWS

Boa noite, ao fazer as atualizaçáes nos fontes do Horse, me deparo com o seguinte erro:
image
Alguma luz?

UnixTime

Hi,

in pas JOSE.Types.JSON

Line 116:
Is it necessary to change
Result := UnixToDateTime(LJSONValue)
to
Result := UnixToDateTime(LJSONValue,False)

Line 234:
SetJSONValue(AName, DateTimeToUnix(AValue.AsType), AJSON)
to
SetJSONValue(AName, DateTimeToUnix(AValue.AsType,False), AJSON)

Thanks

Memory leak on BCrypt.Core.pas

Memory leak identified when executing the function: "function TBCryptImpl.MakeSalt: TBytes;"

function TBCryptImpl.MakeSalt: TBytes;
var
LByteArray: TBytes;
LRandomTemp: string;
I: Longint;
begin
SetLength(LRandomTemp, 17);
SetLength(LByteArray, 16);
Randomize;
LRandomTemp := MTRandomBytes(BCRYPT_SALT_LEN);
I := 0;
while I <= Length(LRandomTemp) do
begin
LByteArray[i] := Ord(LRandomTemp[i + 1]);
Inc(i);
end;
SetLength(LByteArray, 16);
Result := LByteArray;
end;

to correct I changed this line:

SetLength(LByteArray, 16); to SetLength(LByteArray, 17);

function TBCryptImpl.MakeSalt: TBytes;
var
LByteArray: TBytes;
LRandomTemp: string;
I: Longint;
begin
SetLength(LRandomTemp, 17);
SetLength(LByteArray, 17);
Randomize;
LRandomTemp := MTRandomBytes(BCRYPT_SALT_LEN);
I := 0;
while I <= Length(LRandomTemp) do
begin
LByteArray[i] := Ord(LRandomTemp[i + 1]);
Inc(i);
end;
SetLength(LByteArray, 16);
Result := LByteArray;
end;

JOSE.OpenSSL.Headers is not compatible with platforms like iOSDevice or Android.

I believe the reason JOSE.OpenSSL.Headers exist is because there are a few OpenSSL functions that are not imported by Indy, is that correct? Still, why there are many functions imported there that are already in Indy? Like PEM_read_bio_PUBKEY, SHA* functions, among others?

Is there a reason for this, or can this be refactored to use Indy OpenSSL functions directly? I can do that, but I just wanted to know if I'm missing something and if there a different reason for those functions to be imported in such unit. Thanks.

Fatal bug

JOSE.Types.Bytes

This does not seem right

16-09-_2015_08-46-46

This project is in dire need of unit tests.

Example for using RS256 algorithm with pem file

Hi,

How to sign JWT with an external pem file? (Google Cloud IOT)
I studied TfrmSimple.BuildTokenComplete, probably I have to use it but I don't know how to assign an external pem file, created by openssl.exe.

TSigningBase.LoadPublicKeyFromCert fails for a certifcate loaded from Google Certificate Server

I realized that the token verification of the Firebase JWToken within my FB4D library is broken since introducing TRSA.VerifyWithCertificate.

The Google server https://www.googleapis.com/robot/v1/metadata/x509/[email protected] delivers a certificate based on NID_rsaEncryption.

That is why the FB4D needs the following change:

class function TSigningBase.LoadPublicKeyFromCert(const ACertificate: TBytes): PEVP_PKEY;
...
  LAlg := OBJ_obj2nid(LCer.cert_info.key.algor.algorithm);
  if not((LAlg = JoseSSL.NID_rsaEncryption) or (LAlg = JoseSSL.NID_X9_62_id_ecPublicKey)) then
    raise ESignException.Create('[OpenSSL] Unsupported algorithm type in X509 public key (RSA expected)');`

And in unit JOSE.OpenSSL.Headers:

// Numeric ASN1 Object Identifiers
NID_sha256 = 672;
NID_sha384 = 673;
NID_sha512 = 674;
NID_X9_62_id_ecPublicKey = 408; // EC Key
NID_rsaEncryption = 6;

Perhaps there are other algorithm types that could be supported. Is there a reason for this check and the ESignException?

Delphi-Jose-JWT on XE7

Is there a way to make Delphi-Jose-JWT work on Delphi XE7? I was able to install it, however, it keep returning an error when I try to creat a JWT. The error message doesn't say what is wrong, there is no error number to identify it, when it happen when it reaches the function below, and tries to pass through signature:

function TJWS.Sign: TJOSEBytes;
var
LAlgId: TJOSEAlgorithmId;
LAlg: IJOSESigningAlgorithm;
begin
LAlgId.AsString := FToken.Header.Algorithm;
LAlg := GetAlgorithm(LAlgId);

if not FSkipKeyValidation then
LAlg.ValidateSigningKey(FKey);

Header := TBase64.URLEncode(ToJSON(FToken.Header.JSON));
Payload := TBase64.URLEncode(ToJSON(FToken.Claims.JSON));
Signature := LAlg.Sign(FKey, SigningInput);

Result := Signature;
end;

Unable to verify signature with RSA/PEM

I'm trying to validate an Amazon Cognito JWT token using the folowing JWK:

{
	"alg":"RS256",
	"e": "AQAB",
	"kid": "fgjhlkhjlkhexample=",
	"kty": "RSA",
	"n": "sgjhlk6jp98ugp98up34hpexample",
	"use": "sig"
}

Using their examples (https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html), I was able to convert the JWT to PEM format and verify the signature on https://jwt.io/.

However, when I try to validate the same token using your library I've got the following error: 'Key is not RSA key in PEM format'

This is the code I've used:

function TSampleJWTConsole.VerifySampleToken: string;
var
  LKey: TJWK;
  LToken: TJWT;
  FCompactToken: string;
  FPemKey: string;
begin

  FCompactToken := 'xxxx.yyyy.zzzz';
  FPemKey:=
'-----BEGIN PUBLIC KEY-----' +
'MIIBIjANBg ......bQIDAQAB' +
'-----END PUBLIC KEY-----';


  LKey := TJWK.Create(FPemKey);
  LToken := TJOSE.Verify(LKey, FCompactToken);

  if Assigned(LToken) then
  begin
    try
      if LToken.Verified then
        WriteLn('Token signature is verified')
      else
        WriteLn('Token signature is not verified')
    finally
      LToken.Free;
    end;
  end;
end;

Do you have any ideas?

Floating Point Error Comparing Times

When validating a JWT;
If the Issued At is the exact same time as the Not Before and the clock skew seconds is set to 0
JoseConsumer will incorrectly error with 'The Expiration Time (exp=%d) claim value cannot be before the NotBefore (nbf=%d) claim value'

This is due to floating point rounding errors

Make TJWS.CheckCompactToken more strict

Hi. I noticed your code in TJWS.CheckCompactToken is rather simple, where you check if the given string contains 3 non-empty parts separated by a dot. While this might be sufficient for most cases, it could be more strict in a way that you try to parse the Header and Claims (Payload) section to a JSON value. For example:

class function TJWS.CheckCompactToken(const Value: TJOSEBytes; const AStrict: Boolean): Boolean;
var
  LRes: TStringDynArray;
  LIndex: Integer;
begin
  Result := True;

  if Value.IsEmpty then
    Exit(False);

  LRes := SplitString(Value, PART_SEPARATOR);
  if not (Length(LRes) = COMPACT_PARTS) then
    Exit(False);

  for LIndex := 0 to Length(LRes) - 1 do
  begin
    if LRes[LIndex].IsEmpty then
      Exit(False);
  end;
  
  if TJSONObject.ParseJSONValue(LRes[0]) = nil then
    Exit(False);
  if TJSONObject.ParseJSONValue(LRes[1]) = nil then
    Exit(False);
end;

Thanks for creating this library and writing such clean and readable code!

Azure AD JWT token authorization

Hello Paolo.

I have been trying to get the Azure AD JWT token authorization working for the last few days. Unfortunately without success so far. The public key is always rejected by the lib with the error "Key is not RSA key in PEM format".

I get the appropriate public key via the public access to the keys from Microsoft (property x5c):

For example:

-----BEGIN PUBLIC KEY-----
MIIDBTCCAe2gAwIBAgIQGQ6YG6NleJxJGDRAwAd/ZTANBgkqhkiG9w0BAQsFADAt
MSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4X
DTIyMTAwMjE4MDY0OVoXDTI3MTAwMjE4MDY0OVowLTErMCkGA1UEAxMiYWNjb3Vu
dHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALSS+lq9iVLMS8jXsz0IdSes5+sEqAwIYEWEg5GjLhB8u+VY
pIgfMINuVrkfeoHTKaKJHZUb4e0p0b7Y0DfW+ZuMyQjKUkXCeQ7l5eJnHewoN2ad
QufiZjKvCe5uzkvR6VEGwNcobQh6j+1wOFJ0CNvCfk5xogGt74jy5atOutwquoUM
O42KOcjY3SXFefhUvsTVe1B0eMwDEa7jFB8bXtSGSc2yZsYyqBIycA07XHeg5CN8
q5JmLfBnUJrtGAR0yUmYs/jNdAmNy27y83/rWwTSkP4H5xhihezL0QpjwP2BfwD8
p6yBu6eLzw0V4aRt/wiLd9ezcrxqCMIr9ALfN5ECAwEAAaMhMB8wHQYDVR0OBBYE
FJcSH+6Eaqucndn9DDu7Pym7OA8rMA0GCSqGSIb3DQEBCwUAA4IBAQADKkY0PIys
lgWGmRDKpp/5PqzzM9+TNDhXzk6pw8aESWoLPJo90RgTJVf8uIj3YSic89m4ftZd
mGFXwHcFC91aFe3PiDgCiteDkeH8KrrpZSve1pcM4SNjxwwmIKlJdrbcaJfWRsSo
GFjzbFgOecISiVaJ9ZWpb89/+BeAz1Zpmu8DSyY22dG/K6ZDx5qNFg8pehdOUYY2
4oMamd4J2u2lUgkCKGBZMQgBZFwk+q7H86B/byGuTDEizLjGPTY/sMms1FAX55xB
ydxrADAer/pKrOF1v7Dq9C1Z9QVcm5D9G4DcenyWUdMyK43NXbVQLPxLOng51KO9
icp2j4U7pwHP
-----END PUBLIC KEY-----

I have already tested different variants. Structure, header etc.
If I take the example from the demo application, it works without any problems.

The key is valid via an online check tool.

Here is my Delphi code example:

function Validate(AJwt : String; APublicKey : String) : Boolean;
var
  LToken  : TJWT;
  LSigner : TJWS;
  LKey    : TJWK;
begin
  LKey := TJWK.Create;
  try
    LKey.Key := APublicKey;

    LToken  := TJWT.Create;
    LSigner := TJWS.Create(LToken);
    try
      LSigner.SetKey(LKey);
      LSigner.CompactToken := AJwt;

      Result := LSigner.VerifySignature;
    finally
      LToken.Free;
      LSigner.Free;
    end;
  finally
    LKey.Free;
  end;
end;

Do you have any idea what I'm doing wrong?
Any hints are welcome. =:)

Best regards
Dennis

TJSONUtils.SetJSONRttiValue Array Support

It appears that the TJSONUtils.SetJSONRttiValue method (of the unit: JOSE.Types.JSON) used by methods like TJOSEProducerBuilder.SetHeaderParam (of the unit: JOSE.Producer) does not support dynamic arrays (tkDynArray) and static arrays (tkArray), is it possible to add support for arrays?

Error on choosing ES256 signing algorithm

Thank you for providing this work. It's very useful to me.

I'm using JOSE & JWT to generate the secret for Sign In with Apple from the private key file supplied by Apple. Apple requires the ES256 signing algorithm. Running Delphi 12.1 on Windows 10 and compiling for Win64, choosing TJOSEAlgorithmId.ES256 for the signing algorithm caused a protection fault when I called LSigner.Sign(LKey, LAlg). I found the fault was not generated when I changed a couple of lines in class function TECDSA.Sig2OctetSequence from this:

    JoseSSL.BN_bn2bin(ASignature.r, Pointer(Integer(Result) + (LSigLength div 2) - LRLength));
    JoseSSL.BN_bn2bin(ASignature.s, Pointer(Integer(Result) + LSigLength - LSLength));

to this:

{$IFDEF CPU64BITS}
    JoseSSL.BN_bn2bin(ASignature.r, Pointer(int64(Result) + (LSigLength div 2) - LRLength));
    JoseSSL.BN_bn2bin(ASignature.s, Pointer(int64(Result) + LSigLength - LSLength));
{$ELSE}
    JoseSSL.BN_bn2bin(ASignature.r, Pointer(Integer(Result) + (LSigLength div 2) - LRLength));
    JoseSSL.BN_bn2bin(ASignature.s, Pointer(Integer(Result) + LSigLength - LSLength));
{$ENDIF CPU64BITS}

I belive I'm a happy camper now.

Thanks again.

David

Jose.Inc

Jose.Inc has bee Deleted from folders Common and Jose. So we should find Jose.inc in Source Folder but there isn't any jose.inc

{$I ..\JOSE.inc}

Add Custom Validators to TJOSEConsumer/TJOSEConsumerBuilder

Currently TJOSEConsumer and TJOSEConsumerBuilder does not support adding additional claims.

One approach would be to derive new classes with the additional claims and validators. However, the members needed are marked as private.

The other approach would be to allow access to SetValidators in the TJOSEConsumer class. The builder would build the base consumer and then the additional validators could be added before the ProcessContext procedure is called.

Discussed in #81

10.0Seattle Packages?

Please forgive this stupid question, totally my fault for not knowing more about Delphi. I believe I'm running Delphi 10 Seattle with update 1. Does that mean I should use delphi-jose-jwt-master\Packages\10.1Berlin?

I couldn't find a 10.0Seattle package which is what I was expecting.

Here's my about dialog to avoid any confusion:
image

constructor TJOSEContext.Create shouldn't squash exception and should re-raise

The constructor TJOSEContext.Create squashes the exception (EJOSEException.Create('Malformed Compact Serialization')) that is raised when an invalid token string is passed in. This is almost certainly incorrect as it leaves the caller thinking the construction seemingly succeeded, but being left with what is in reality a partially constructed (invalid) TJOSEContext.

The consequence is a probable access violation (as is the case in our code ) when for example a custom validator runs, as both GetJOSEObject() and GetJWT() returns nil (due to the failed/partial construction mentioned above). Additionally there's no way of getting the actual invalid token bytes from the TJOSEContext either which makes raising a further error somewhat problematic.

The fix is to re-raise the exception in the try/except in TJOSEContext.Create. This results in the correct code being made aware of the failure and given the opportunity to act accordingly.

MacOS64: TRSA.LoadOpenSSL fails with "[RSA] Unable to get proc address for "PEM_read_bio_RSA_PUBKEY"

I tried out today the new MacOS 64 bit compiler, which is delivered with De 10.3.2.

Under Mojave version 10.14.5 I ran into an exception in TRSA.LoadOpenSSL '[RSA] Unable to get proc address for' PEM_read_bio_RSA_PUBKEY ''.

In the Mac32 target platform, the same code is still running properly as before. I also get the same version information from the OpenSSL Cryptolib on MacOS64 as on MacOS32 (query with _SSLeay_Version (SSLEAY_VERSION)): "LibreSSL 2.2.7".

Note: For MACOS you do not need to include the OpenSSL dynlib files into the project deployment because they are already part of the OSX.

Thank you for any tip to solve this issue.

RSA signing verification bug when compiling for Windows

In the JOSE.Signing.RSA.pas file, when verifying an RSA signature, the code ultimately calls TRSA._PEM_read_bio_RSA_PUBKEY. This function is never assigned in Windows. The code that gets the proc address is IFDEF'd for Linux64 in the TRAS.LoadOpenSSL procedure.

By removing the IFDEF and adding WinApi.Windows to the uses clause, that part works.

Secret base64 encoded

Hi Paolo,

How can I verify a token where the secret is base64 encoded?

For example, when I generate a token using your demo and test it at jwt.io with key "secret", it returns "Signature Verified". But if I mark the checkbox "secret base64 encoded", it returns "Invalid Signature"
The token generated by Auth0 work only with "secret base64 encoded" and I'd like to decode it.

Unable to Verify with RSA

Pulling the latest version I find I am now unable to Verify a JWT with a public key. I reviewed the change log since it worked previously I see JOSE.Signing.RSA was revised to compile for Linux. A change made in that revision when reverted corrects Token verification via Public Key. I don't compile for Linux so don't have a suggestion, but this code needs reviewed to work correctly in Windows. This change in TRSA.Verify corrects it.

  //LByte := IntToStr(ASignature[0]);
  Result := EVP_DigestVerifyFinal( LCtx, PAnsiChar(@ASignature[0]){@LByte}, Length(ASignature) ) = 1;

Read the payload of a token

My token is verified, how do I then get the value of the payload of the token??

var
lKey: TJWK;
lToken: TJWT;
begin
lKey := TJWK.Create('secret');
// Unpack and verify the token
lToken := TJOSE.Verify(lKey, AToken);

if Assigned(lToken) then
begin
try
if lToken.Verified then
begin
/*In here I would like to get the string value of the payload/
//Result := TJSONUtils.ToJSON(lToken.Claims.JSON);
end;
finally
lToken.Free;
end;
end;

SetJSONValue unable to handle Int64

Thanks, great library and much needed for our Delphi community!

I found that TJSONUtils.SetJSONValue is unable to set Int64 values. I'm working on an API integration that requires the higher resolution Unix date time value. When assigning the value they become negative due to the overflow. A revision in JOSE.Types.JSON.pas to detect tkInt64 would work to correct this.

Randy

Brainpool support possible?

Hello,

I had a look at JOSE because I need to verify a JWT, but BP256R1 is not supported yet. Is there any chance, that this could be available soon?

At the moment I am testing with an adapted copy of the ECDSA unit, but as I do not know enough about this topic, I am not sure, whether this is the correct way and whether it will work.

Thank you in advance for any answer!

Sebastian JΓ€nicke

RSA Verify Bug

There is a bug in the TRSA .Verify function in the JOSE.Signing.RSA unit. Verification would always fail.

The following two lines:

LByte := IntToStr(ASignature[0]);
Result := EVP_DigestVerifyFinal( LCtx, @lbyte, length(ASignature) ) = 1;

should be replaced with:
Result := EVP_DigestVerifyFinal( LCtx, @ASignture[0], length(ASignature) ) = 1;

TJWK class claims to implement RFC7517 (partially) but it really doesn't implement it at all

The unit header says:

/// <summary>
///   JSON Web Key (JWK) RFC implementation (partial)
/// </summary>
/// <seealso href="https://tools.ietf.org/html/rfc7517">
///   JWK RFC Document
/// </seealso>
unit JOSE.Core.JWK;

RFC7517 only marginally in a side note unter section 4.6 mentions PEM format at all. The whole TJWK class is basically just a simple wrapper around a TBytes which is assumed by other code (e.-g. function TJWS.VerifySignature: Boolean;) to be an RSA key in PEM format.

To put it bluntly, there is nothing (not even partially) JWK specific at TJWK at all, besides the name.

This is how an JWK would look

 {"kty":"RSA",
       "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx
            HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs
            D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH
            SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV
            MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8
            NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ",
       "e":"AQAB",
       "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I
            jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0
            BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn
            439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT
            CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh
            BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ"
      }
      

Feature Request: Support of OpenSSL 3.0 (and up)

It looks like the current implementation does not account for changes in OpenSSL 3.0 (and up).
The most current OpenSSL 1.1.1 had its End-Of-Life on 2023-09-11 [1] [2]

A general overview of the amount of changes for OpenSSL 3.0 exists as a migration guide [3]

[1] https://mta.openssl.org/pipermail/openssl-announce/2023-March/000254.html
[2] https://web.archive.org/web/20240703082539/https://www.openssl.org/blog/blog/2023/09/11/eol-111/
[3] https://docs.openssl.org/3.0/man7/migration_guide/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.