5

I am trying to validate a JWT in apex by using the Crypto.verify() method, however, it always returns false despite the fact that I know it is valid. I know it valid because a) I created it and b) I have used several other sites and pieces of code to check it.

This is the crux of the code that I am using;

Blob data = Blob.valueOf('eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FTTFOa1k1TVRCRFFURTFPRFkzUkRaRVJVUTJSa1JGTmpReU56STBNRUl4TVRnM04wSkdSZyJ9.eyJpc3MiOiJodHRwczovL3N0YWdpbmctbXktb2NmLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1ZDM4NjE0OWJkODJhNzBlZjIzNmZkNmYiLCJhdWQiOiJLbm5zU1Zjd3c3WDhGMVRBbmNxaHNjT3lSMk5Fb28wdyIsImlhdCI6MTU2NTc3MjQ3NywiZXhwIjoxNTY1ODA4NDc3LCJhdXRoX3RpbWUiOjE1NjU3NzI0NzcsImF0X2hhc2giOiJLNWdzMkd2OGZpdjkzeVVuWWt0ZkZRIiwibm9uY2UiOiJ6MUR4YnBHSUladk5QYlh5NH5UM2ZUdnZLUE9FdXlNWCJ9'); Blob signature = Blob.valueOf('bg0PGZwF5U4ruFXKz51qpMGEBpRhBb8FF_IIyJ2PlpBg7Xm2Rrc4MtjwgCW9GMQ3PYpep08Kv5N3YB1VALH2uPjNJKdLT9XEGNn-I6x4fujYmV1A5zNKWftIscH4xHW6L3RwdhsMFJ1Bs2vSG7uv9_scrCmVm_diC2S1F-KBUFxZMX-OCVLfHTjb3-VViCx_cjrrSGhZ7Kq5vmIicU_FDJUX7UiCJYHYqScz6fy12d7lHDdhYtXEDh7SjW8jvAZlWRmifq69uLMfhafm5EN7S5eVH-_9IaFdo2a7yYNrSrS64JH_j1Z9qsk9_gb7ksNakLzddZje3UGxNX-CPi_MDA'); Blob publicKey = EncodingUtil.base64Decode('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2PP+4VYzd2fSC7FgL75mBoPIFu6bvvNzXZQ4kBG1FqAkEcT3EraTGHlNq/iWXFpph+XJfhje0kkSOn6PKYzUd+29ZOJwtBgo8sPyi0tafpCQrRZ9y18qgW/vSr3gX6O/lobMianmCc+C8Qv72ptUVc3DdbSXxwJAJ2zkIzlJnVPKjRsWmLYhP3WpathoAxERRGjGmNkxScr6GDsMTaPV2s6mZusWyk2pgFbhWXDKD6UVGc2Wx7/IU3mNWufqRvrCD3qf7MKDRK3ZprfWghJt7x1epnPUqP2jdgq0LVeO8/cQGTwXFVHH2VJexkZ8qypuKAoNNTNLKboodVaZgigDtQIDAQAB'); System.debug(Crypto.verify('RSA-SHA256', data, signature, publicKey)); 

I can't see what I'm doing wrong. Right now my working assumption is that the public key is incorrect. I have extracted it from an X509 certificate so I think that it is in the correct format. For reference the JWKS for the keys that signed this data can be found here https://staging-my-ocf.eu.auth0.com/.well-known/jwks.json

So my questions are;

  • Have I derived the key correctly and is it in the X509 format expected by Crypto.verify()?
  • If that is all good then what is making verify() always return false?

For reference, I have verified the JWT using both JWT.io and JSON Web Token Verifier

2
  • If this is for testing, can you share private key? Commented Aug 15, 2019 at 11:38
  • I'm afraid I can't share the private key Commented Aug 15, 2019 at 12:52

2 Answers 2

6

JWT is encoded using base64 url-safe scheme so you must decode the signature appropriately when you convert it into bytes, straight base64 decoding won't do.

URL-safe base64 encoding is defined in JWS spec (RFC 7515):

 Base64 encoding using the URL- and filename-safe character set defined in Section 5 of RFC 4648 [RFC4648], with all trailing '=' characters omitted (as permitted by Section 3.2) and without the inclusion of any line breaks, whitespace, or other additional characters. 

RFC has a decoding example in C#. Translating to Apex:

String headerAndPayload = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FTTFOa1k1TVRCRFFURTFPRFkzUkRaRVJVUTJSa1JGTmpReU56STBNRUl4TVRnM04wSkdSZyJ9.eyJpc3MiOiJodHRwczovL3N0YWdpbmctbXktb2NmLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJhdXRoMHw1ZDM4NjE0OWJkODJhNzBlZjIzNmZkNmYiLCJhdWQiOiJLbm5zU1Zjd3c3WDhGMVRBbmNxaHNjT3lSMk5Fb28wdyIsImlhdCI6MTU2NTc3MjQ3NywiZXhwIjoxNTY1ODA4NDc3LCJhdXRoX3RpbWUiOjE1NjU3NzI0NzcsImF0X2hhc2giOiJLNWdzMkd2OGZpdjkzeVVuWWt0ZkZRIiwibm9uY2UiOiJ6MUR4YnBHSUladk5QYlh5NH5UM2ZUdnZLUE9FdXlNWCJ9'; String signature = 'bg0PGZwF5U4ruFXKz51qpMGEBpRhBb8FF_IIyJ2PlpBg7Xm2Rrc4MtjwgCW9GMQ3PYpep08Kv5N3YB1VALH2uPjNJKdLT9XEGNn-I6x4fujYmV1A5zNKWftIscH4xHW6L3RwdhsMFJ1Bs2vSG7uv9_scrCmVm_diC2S1F-KBUFxZMX-OCVLfHTjb3-VViCx_cjrrSGhZ7Kq5vmIicU_FDJUX7UiCJYHYqScz6fy12d7lHDdhYtXEDh7SjW8jvAZlWRmifq69uLMfhafm5EN7S5eVH-_9IaFdo2a7yYNrSrS64JH_j1Z9qsk9_gb7ksNakLzddZje3UGxNX-CPi_MDA'; Blob publicKey = EncodingUtil.base64Decode('MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2PP+4VYzd2fSC7FgL75mBoPIFu6bvvNzXZQ4kBG1FqAkEcT3EraTGHlNq/iWXFpph+XJfhje0kkSOn6PKYzUd+29ZOJwtBgo8sPyi0tafpCQrRZ9y18qgW/vSr3gX6O/lobMianmCc+C8Qv72ptUVc3DdbSXxwJAJ2zkIzlJnVPKjRsWmLYhP3WpathoAxERRGjGmNkxScr6GDsMTaPV2s6mZusWyk2pgFbhWXDKD6UVGc2Wx7/IU3mNWufqRvrCD3qf7MKDRK3ZprfWghJt7x1epnPUqP2jdgq0LVeO8/cQGTwXFVHH2VJexkZ8qypuKAoNNTNLKboodVaZgigDtQIDAQAB'); Blob bsignature = EncodingUtil.base64Decode(signature .replace('-', '+') .replace('_', '/') .rightPad(math.mod(signature.length() + (math.mod(4 - signature.length(), 4)), 4)) .replace(' ','=')); System.debug(Crypto.verify('RSA-SHA256', Blob.valueOf(headerAndPayload), bsignature, publicKey)); 
4
  • Thank you this is the step I managed to miss. It's working perfectly now. Commented Aug 15, 2019 at 16:31
  • I think "payload" is a misleading name for the string variable. It confused me when trying to verify my JWT. And I can see that it confused also Bryan Anderson. IMO It should be named "headerAndPayload" or "headerDotPayload" Commented Sep 14, 2022 at 10:05
  • why did you need to do .rightPad(math.mod(signature.length() + (math.mod(4 - signature.length(), 4)), 4)) ? Commented May 3, 2023 at 3:33
  • @compski See the paragraph about padding in RFC linked above Commented May 3, 2023 at 4:01
-1

Simon,

Can you follow this code block and double check your results. I just copied the data provided by JWT.io by default. So replace with your data where needed and let us know your results

String algorithmName = 'RSA-SHA256'; String privateKey = 'MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjskFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0goamMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM+bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5YlSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNpbU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X+jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9BBwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjxQYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5Ky18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrBjg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE='; String publicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQAB'; Blob privateKeyBlob = EncodingUtil.base64Decode(privateKey); Blob publicKeyBlob = EncodingUtil.base64Decode(publicKey); Blob input = Blob.valueOf('{"sub":"1234567890","name":"John Doe","admin":true,"iat":1516239022}'); Blob signature = Crypto.sign(algorithmName, input, privateKeyBlob); Boolean verified = Crypto.verify(algorithmName, input, signature, publicKeyBlob); System.debug(verified); 

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.