1

UPDATED I'm trying to verify a JWT access token programmatically using the x5c / x509 public key value below. I can get this working by plugging the token and x5c values into external web sites but not programmatically using JavaScript / jsrsasign. Any suggestions would be greatly appreciated.

Here is the the OIDC provider's public JSON Web Key Set.

    {
        "keys": [
            {
                "kty": "RSA",
                "kid": "server",
                "use": "sig",
                "alg": "RS256",
                "n": "gLZO9w1OT_SWO-KbqiU0k3HevHggiY70XbDqgE1YaqhD-MwFUWNudExzF3oB28NYWYg5v6CJY0F-pUNtgukDM6ARDlh0n4xIvBRlnUnCTCx7pYOjpfXbTv49tlXmh4-ddh8EeQBLrF92u5UYs0tnZd8843mvYWohUNH1X1hM08-hpk7xCiy4XdwbeSlH757D2d5E0J0dGtZ744-dB2ZRCw2Vms_mk4Yyny4ifx2j2gIhikbb7WGmsTR2sWrtuhgZ_EBNUvrD0O54xbhQNTTFQ1pi9UZxo_gYc5Gp5fLcSOK6SDBKXbDS5hhy1vFyoa0xdgFv-xpem7YzmkKqzfjC9w",
                "e": "AQAB",
                "x5c": [
                    "MIIDMDCCAhigAwIBAgIEFIopYzANBgkqhkiG9w0BAQsFADBaMQkwBwYDVQQGEwAxCTAHBgNVBAgTADEJMAcGA1UEBxMAMQkwBwYDVQQKEwAxCTAHBgNVBAsTADEhMB8GA1UEAxMYZmNpc2Rldi5pY2UuaWJtY2xvdWQuY29tMB4XDTE4MTAwMTE4MTYyOFoXDTI4MDkyODE4MTYyOFowWjEJMAcGA1UEBhMAMQkwBwYDVQQIEwAxCTAHBgNVBAcTADEJMAcGA1UEChMAMQkwBwYDVQQLEwAxITAfBgNVBAMTGGZjaXNkZXYuaWNlLmlibWNsb3VkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIC2TvcNTk/0ljvim6olNJNx3rx4IImO9F2w6oBNWGqoQ/jMBVFjbnRMcxd6AdvDWFmIOb+giWNBfqVDbYLpAzOgEQ5YdJ+MSLwUZZ1Jwkwse6WDo6X1207+PbZV5oePnXYfBHkAS6xfdruVGLNLZ2XfPON5r2FqIVDR9V9YTNPPoaZO8QosuF3cG3kpR++ew9neRNCdHRrWe+OPnQdmUQsNlZrP5pOGMp8uIn8do9oCIYpG2+1hprE0drFq7boYGfxATVL6w9DueMW4UDU0xUNaYvVGcaP4GHORqeXy3EjiukgwSl2w0uYYctbxcqGtMXYBb/saXpu2M5pCqs34wvcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdtN9znA9a6luPAQurcQn8kJBlllslRWsNPMhPWpMtYaMLx6JhmDICGbaYZBGUboedwnUaEk6tE2b+EVlUE/tnaKVJms2cmCCFExQQrTHmRfFI/Vi/esVqAnz1E2dB61LMnQ2AeebXAZ/C7hRt1uXVboXr5Zokppr4FRS9QsjSK4dhcXxhfglTKJOPZ4dkSexhe6hybpL8XdGhoyf2SyNXCy5iYX0zQ5BmJaLimOcJyasZ/A7/YgsVbQyAe6Ubno6/sIUuOZ+J+snZsBSLViqcftGVPUkIWamv/yNQcEJrDWa4C+sr+9Yb7uFjuj4gDY0jvGkGmu53g0K8Vks+IfAdQ=="
                ],
                "x5t#S256": "nTAGJuFFrm-vNBdkLVNmuePwTmlXr0T87IppgJPRT9k"
            }
        ]
    }

Here is the code I'm using to verify the access token with the x5c. I'm under the impression I should be using the x5c value, but if there is another way that is fine with me. Just need to verify the token with the above values under keys.

// break line every 64 characters.

x5cValue = x5cValue.replace(/(.{64})/g, "$1\n");

// base64 decode

var x5cValueAtob = atob(x5cValue);

// Add Begin / END certificate

x5cValue = "-----BEGIN CERTIFICATE-----\n" + x5cValueAtob + "\n-----END CERTIFICATE-----";

var decoded = KJUR.jws.JWS.verify(accessTokenJson, rawContent, ["RS256"]);

Should I be adding the BEGIN / END PUBLIC KEY strings to the x5c value before / after base64 decode? yes, thanks to Adam

Do I need to process the x5c value before verify?

Returned response - decoded: false

Thank you in advance.

Mike T
  • 93
  • 1
  • 2
  • 13
  • Looks like yes: https://kjur.github.io/jsjws/api/symbols/KJUR.jws.JWS.html#.verify – Adam Jenkins Mar 18 '19 at 17:10
  • I tried adding the following before base64 decoding and got the same error. x5cValue = "-----BEGIN CERTIFICATE-----" + x5cValue + "-----END CERTIFICATE-----"; – Mike T Mar 18 '19 at 18:27
  • Actually Adam you got me thinking, i added the BEGIN / END text and line breaks every 64 characters. So now I'm sending like this to verify and the error has changed to - false. I think I'm gettiing closer :) – Mike T Mar 18 '19 at 18:43
  • I have no idea, but I think you still need to decode the x5cValue in between the begin and end certificates so it's `-----BEGIN CERTIFICATE-----${atob(x5cValue)}-----END CERTIFICATE-----`. I'm curious about one thing, though, do you need to verify the token, or just decode it to get at it's contents? – Adam Jenkins Mar 18 '19 at 18:59
  • Thanks Adam, tried your suggestion. Updated the code above to show. Still getting false returned. – Mike T Mar 18 '19 at 19:16
  • Have you seen this? https://github.com/kjur/jsrsasign/wiki/Tutorial-for-JWS-verification It looks like you need to use the KEYUTIL class in that package. – Adam Jenkins Mar 18 '19 at 19:30
  • Thanks Adam, I tried using var pubKey = KEYUTIL.getKey(sRSAPUBKEY_X509CERT_PEM) but no matter what I passed to it the error returned is - undefined. Does not appear to work with x5c value, tried base64 decoded, tried adding BEGIN / END. If I remove the input value and run it, I get - Cannot read property 'curve' of undefined. So looks like the KEYUTIL is working – Mike T Mar 19 '19 at 12:05
  • Could I ask you which provider issued the JWT? I'm working on a project where we want to use the x5c header parameter, but I'm not sure our provider supports it. – Grubl3r Sep 24 '20 at 06:46

2 Answers2

5

I spent a day scratching my head and finally got it working like

public static PublicKey getPublicKey(String x5c) throws CertificateException, IOException {
    System.out.println(" x5c ="+x5c);
    String stripped = x5c.replaceAll("-----BEGIN (.*)-----", "");
    stripped = stripped.replaceAll("-----END (.*)----", "");
    stripped = stripped.replaceAll("\r\n", "");
    stripped = stripped.replaceAll("\n", "");
    stripped.trim();
    System.out.println(" stripped ="+stripped);
    byte[] keyBytes = Base64.decode(stripped);
    CertificateFactory fact = CertificateFactory.getInstance("X.509");
    X509Certificate cer = (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(keyBytes));
    return cer.getPublicKey();

}
dmdev
  • 51
  • 1
  • 2
2

This answer may not help you to answer all your questions, but I have to write it to improve the solution security.

I'm under the impression I should be using the x5c value,
Do I need to process the x5c value before verify?

From the security point of view - do not use the x5c certificate to validate the signature directly. In that case anybody could just provide its own certificate and spoof any identity.

The purpose if the x5t / x5t#S256 header is to identify the signer - check you trust the certificate provided by x5c or x5t#S256 (or its issuer) under the specified iss, only then you should validate the signature. The x5t headers enables your service to validate token from multiple IdP (identity providers / token issuers) or enable renewing the signer's certificate without loosing trust of the serice providers.

If you trust only a single identity provider using a single certificate, you may just directly use the provider's certificate without doing anything with the provided header.

For the solution - seems Adam is right in the comments, I as well suggest you use the KEYUTIL to load/parse the certificate

gusto2
  • 11,210
  • 2
  • 17
  • 36
  • Just to extend here, I would say "do not use the x5c to validate directly, _unless you also verify the trust of the x5c_". verifying that you trust the x5c might be: checking a TrustStore for the cert of the signer of that cert. Eg, like verification of any certificate. – Cheeso Oct 13 '22 at 16:41