#1 2024-05-13 06:35:11

Dweomer
Member
Registered: 2024-05-13
Posts: 6

JWT ES256 Code Example

I am new to mORMot and JWT. I am currently only importing the mORMot library as I need to sign a JWT as ES256 in Delphi. I have been provided a simple private key (eg. text file starting with '-----BEGIN EC PRIVATE KEY-----' ) and able to use it from Postman using a js library sign() command.

I have no yet found a simple example using ES256 especially around loading a certificate object for use by the TJwtEs256.Create() function. Every example is using HS256. Any help is greatly appreciated. I would hope this is quite simple. My current code is below.

        fCertificate := TEccCertificate.Create();
       
        if( fCertificate.FromFile(OAuthConfiguration.ClientAssertionSignatureKeyFile) ) then
        begin
          fSignedToken := TJwtEs256.Create(fCertificate,[jrcIssuer,jrcSubject, jrcAudience, jrcExpirationTime],[OAuthConfiguration.ClientAssertionPayloadIssuer,OAuthConfiguration.ClientAssertionPayloadSubject,OAuthConfiguration.ClientAssertionPayloadAudience,expiryTimeStr]);
          fSignedTokenAsString := fSignedToken.Compute([], OAuthConfiguration.ClientAssertionPayloadIssuer);

          result := fSignedTokenAsString;
        end

Offline

#2 2024-05-13 06:49:33

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

1) You are making a confusion in the aAudience: array of RawUtf8 parameter of the constructor.
This is only for the 'aud' value.
The JWT issuer, subject and expiration are to be set as parameters to the TJwtAbstract.Compute() method.

2) About the certificate, TEccCertificate class has its own mormot.cryt.ecc format, which is not X.509 certificates.
If you are using a X.509 certificate, you need to use either TJwtCrypt with 'ES256' and both its public key and private key, or TJwtEs256Osl from mormot.crypt.openssl.

Offline

#3 2024-05-14 04:50:12

Dweomer
Member
Registered: 2024-05-13
Posts: 6

Re: JWT ES256 Code Example

Thanks for your help. I now need to figure out why my public key is not valid for the Create(). It works fine when creating a new key (key:=''):

        publicKey := StringFromFile('public.key'); //-----BEGIN PUBLIC KEY-----
        privateKey := StringFromFile('private.key'); //-----BEGIN EC PRIVATE KEY-----

        jwtCrytp := TJwtCrypt.Create( caaES256, publicKey, [jrcAudience],[OAuthConfiguration.ClientAssertionPayloadAudience], 60 );
        jwtCrytp.LoadPrivateKey(privateKey);
        fSignedTokenAsString := jwtCrytp.Compute([],OAuthConfiguration.ClientAssertionPayloadIssuer, OAuthConfiguration.ClientAssertionPayloadSubject, OAuthConfiguration.ClientAssertionPayloadAudience);

        result := fSignedTokenAsString;

I have stepped through the code but it has not helped to give a hint.
My public.key file looks like this and I expect is correct as my private key works in Postman and is a P-256 ES256  key. I assume P-256 means the same as secp256r1 or prime256v1:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoIQ8m1iBHYoxrdLT1A6MH9naG+hk
/ccw/Ij0p9Mk7JmNdzCUeEjzlU5/E683I9PZaz2/5RFj1HfKPTgDkxQFkA==
-----END PUBLIC KEY-----

Is it because I have the wrong format? Or need to include my private key in the file?
The full public key info is:

Public key in PEM format is:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoIQ8m1iBHYoxrdLT1A6MH9naG+hk
/ccw/Ij0p9Mk7JmNdzCUeEjzlU5/E683I9PZaz2/5RFj1HfKPTgDkxQFkA==
-----END PUBLIC KEY-----

Public key in JWK format is:
{"crv":"P-256","x":"oIQ8m1iBHYoxrdLT1A6MH9naG-hk_ccw_Ij0p9Mk7Jk","y":"jXcwlHhI85VOfxOvNyPT2Ws9v-URY9R3yj04A5MUBZA","kty":"EC","kid":"jIaUOEPIVw3jh6TWN0MxccCnN2IcbFib1d2F3qbjZHo"}

Public key in GitHub format is:
{\"keys\": [{\"crv\": \"P-256\", \"x\": \"oIQ8m1iBHYoxrdLT1A6MH9naG-hk_ccw_Ij0p9Mk7Jk\", \"y\": \"jXcwlHhI85VOfxOvNyPT2Ws9v-URY9R3yj04A5MUBZA\", \"kty\": \"EC\", \"kid\": \"jIaUOEPIVw3jh6TWN0MxccCnN2IcbFib1d2F3qbjZHo\"}]}

Offline

#4 2024-05-14 10:05:42

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

How did you generate the keys?
This is not a X.509 certificate but a raw P-256 public key:
https://lapo.it/asn1js/#MFkwEwYHKoZIzj0 … PTgDkxQFkA

I assume your error is "Create: impossible to load this ckaEcc256 key" ?

My guess is that you did not include OpenSSL in your project. It should fix the key loading.
But the plain mormot.crypt.ecc should work, and PemDerRawToEcc() should be able to decode the public key.
I will investigate and see how to fix it.

Edit:
Please try with the latest trunk.
mormot.crypt.ecc.pas should now be able to read the public key from your expected format:
https://github.com/synopse/mORMot2/commit/d55a0528

Offline

#5 2024-05-16 07:25:37

Dweomer
Member
Registered: 2024-05-13
Posts: 6

Re: JWT ES256 Code Example

Thank you sooo much for committing a fix! I will need to buy you a coffee. smile

I did not get a notification that you edited your post. I tested with trunk just now and it can read my public key now. It is however failing when loading the private key with  jwtCrytp.LoadPrivateKey(privateKey). The private key I'm using is only for a test environment. I can email it to you if you can also quickly check/fix why it is not loading. The headers contain EC ie. '-----BEGIN EC PRIVATE KEY-----'.

While waiting for your response, I got OpenSSL v3 working and tried using TJwtEs256Osl. I was able to create the JWT and even internally verify() it. But despite converting to string using Utf8ToString(), when copy/pasting the string to the JWT.io website with the public key, the signature failed validation there. Whereas the token created using a js library in Postman did pass this external test. I would vastly prefer using the native encryption of mormot (even if slower) if we can get it working instead.

Last edited by Dweomer (2024-05-16 07:32:47)

Offline

#6 2024-05-16 15:58:04

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

Yes, please email me the private key.

How did you generate the keys?

Offline

#7 2024-05-16 22:56:47

Dweomer
Member
Registered: 2024-05-13
Posts: 6

Re: JWT ES256 Code Example

Emailed you.

And they were provided to me by the internal group responsible for the JWT API used to authenticate with all APIs at the large organization. I have asked them how it was generated.

Offline

#8 2024-05-17 08:26:16

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

We actually supports PKCS#8 as generated by default by OpenSSL, and used by most libraries, e.g. like mbed-tls:
https://mbed-tls.readthedocs.io/en/late … r-and-pem/

What you sent me seems to be not the PKCS#12 format (.pfx) either..
https://www.rfc-editor.org/rfc/rfc7292#section-4.2.1

Here is what we got:

PKIMessage SEQUENCE (4 elem)
  header PKIHeader [?] INTEGER 1
  body PKIBody [?] OCTET STRING (32 byte) FC902.......
  protection [0] (1 elem)
    PKIProtection [?] OBJECT IDENTIFIER 1.2.840.10045.3.1.7 prime256v1 (ANSI X9.62 named elliptic curve)
  extraCerts [1] (1 elem)
    SEQUENCE [?] BIT STRING (520 bit) 000001001010000010....

Of course, the PKIMessage and PKI* names are wrong because the online decoder thing it is a PKIMessage (RFC4210).

But I think I found the format.
https://www.rfc-editor.org/rfc/rfc5915

Offline

#9 2024-05-17 10:23:25

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

Please try https://github.com/synopse/mORMot2/commit/dcedffeb
It should decode the private key.

And I have also let TJwtCrypt.Create accept a private key PEM/DER as alternative to a public key:
https://github.com/synopse/mORMot2/commit/4dd41add
So you could directly generate it with a private key (no need to supply the public key).

I included a private key in the regression tests
https://github.com/synopse/mORMot2/commit/10399d77
But I put 00000000 chars where the actual EC private key is stored, so there is nothing here which could be leaked.

Offline

#10 2024-05-20 07:46:53

Dweomer
Member
Registered: 2024-05-13
Posts: 6

Re: JWT ES256 Code Example

I have tested and although everything appears to work internally, the JWT.IO website sees the signature as invalid.

The signature created by a common JS library and validated by both the JWT API and JWT.IO is 9 characters shorter than the one generated by Mormot:
5mzvfO7OHFFWhhv2fzlqqARznylbhBaGrXC54T0z_GjJCBMo_6-TBkWL6dXndYZm6-ek6e-BNJVksB8LqTRgoA (86 characters)
MEUCIQDpe-pimZzIgZKT9QYYO5hbKUyv5w6DalS3p4dh5dP6sAIgb88dDuuIH6mVRkW3Q3RAP_QbmWNlLpVAfA_vHzJyrUs (95 characters)

I am now going to attempt using OpenSSL again. And also create a new key myself to test with Postman vs the JWT.IO website.

I am told the private key should be generated as follows:
openssl ecparam -name prime256v1 -genkey -noout -out <filename>.pem

Offline

#11 2024-05-20 08:36:59

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

You are right.

The problem comes from the fact that X.509 certificates are using DER encoding for their ECC signature, whereas JWT uses the plain digital signature without DER encoding.
I did only verify the X.509 certs layout with external libraries, not the JWT.

It sounds like if JWT is using DER encoding for its RSA signature (e.g. RSASSA-PKCS1-v1_5), but no DER encoding for ECC signature.
A pretty half-backed standard to my understanding. At least X.509 is much more consistent about this.

I will investigate and try to fix it.
My guess is that our JWT + OpenSSL output is also wrong, because by default OpenSSL outputs an ECC digital signature with a DER encoding.

Offline

#12 2024-05-21 15:49:21

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,686
Website

Re: JWT ES256 Code Example

Please try with today's commits.

They should properly encode and decode the JWT signature as expected by the "standard".
For both TJwtCrypt (native mormot) and TJwt*Osl classes (native OpenSSL).
https://github.com/synopse/mORMot2/commit/143a83c0ac059
https://github.com/synopse/mORMot2/commit/29a6d36ab3

Offline

#13 2024-05-22 01:48:58

Dweomer
Member
Registered: 2024-05-13
Posts: 6

Re: JWT ES256 Code Example

I just tested with my provided key and the one I created. They both validated correctly on JWT.io.

Thank you so much! You are a life saver. And I do love mORMot. I will use other APIs as well.

Offline

Board footer

Powered by FluxBB