1
votes

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.

2
I tried adding the following before base64 decoding and got the same error. x5cValue = "-----BEGIN CERTIFICATE-----" + x5cValue + "-----END CERTIFICATE-----";Mike T
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
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
Thanks Adam, tried your suggestion. Updated the code above to show. Still getting false returned.Mike T

2 Answers

2
votes

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

0
votes

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();

}