Validating a digitally signed JSon Web Token with a public key

Jun 18, 2015 at 5:38 PM
I have a digitally signed JSon Compact-serialized string produced by Microsoft's .NET
JSON Web Token Handler
https://msdn.microsoft.com/en-us/library/dn205065(v=vs.110).aspx

Is there a way with the C++ REST SDK to convert this to a web::json::value instance and also validate the digital signature?

Here is an example string:
eyJ0eXAiOiJKV1QiLCJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEifQ.eyJ1bmlxdWVfbmFtZSI6ImJvYlRoZUJ1aWxkZXIiLCJyb2xlIjoiQ3VzdG9tUm9sZU9mQXdlc29tZW5lc3MiLCJpc3MiOiJodHRwczovL3Rlc3QubmRpLm51YW5jZS5jb20vP2Zvbz1iYXIiLCJhdWQiOiJodHRwczovL3d3dy5udWFuY2UuY29tLyIsImV4cCI6MTQzNDczMTQ5NiwibmJmIjoxNDM0NjQ1MDk2fQ.DMe2Xvh6Zv2KrJC7iitnSadqBm-2vLmYP_KPxVmyMD_Pg_VJR37J4wKNHUKduYz3ENyMRFm42q1qI3tr8iDWJS2uATtEeSUTiirz-hnF_nopvBx3fQM3xvojAt5tLpZQq227pEu_BD2_i9VNnlg2yBygIn2yPKFOseJrI7509OV5kTFFqpmPXkTe9o2rAX1WGnEPhKtmuLxkmS46u-el94zkr1usqnQquSY7LSL6qpjO0BXSFuY7_xpxuigGn-6vGo9QWx2pVg6nIQZRY4Zkj071gPWeeugw9p-JzWSfdw9At1AgUbso6n2jkeHDEwpbEeoLg7X94T4jjZdfAmAymg

Here is the public key that can validate the token's integrity (this is a temp certificate for testing):
30 82 01 0a 02 82 01 01 00 e5 69 ab d7 0f 8a 3c b6 f5 22 ea 5d b3 94 c9 ec 5f 78 61 b8 40 d3 27 aa 68 5a 5a 3d db 30 ff 5d 70 41 d7 1c 5f 47 9c d6 52 55 3b e3 50 d7 03 fe c9 ff 9e 62 2b 8c 29 75 3e ff 24 08 8a 90 12 e8 46 8a 16 31 ce 4f c3 43 10 e0 1e 3d 3a 2e df 0e a7 73 dc e9 76 b7 d9 cc 43 02 d4 bf 4a 85 e1 48 7a e8 40 9e ca 0e 34 1b 10 39 ed 65 5c 6c d2 8f 81 d6 75 a9 ec 07 e5 da 1e d9 96 40 1c 5d 0c 38 8a ec dc df 10 49 83 21 e2 a5 18 16 81 3b b8 de 82 d9 a5 7a 8b 0b 93 a6 77 5a bf 64 46 93 e0 03 5a 5c b6 b2 6a 41 ef 1f 8a 11 bb f3 bf a6 0b 77 8a 3b a3 66 b4 4b 76 a2 ce fe bc 2e 8c 07 09 f2 9e 70 e6 16 34 02 9b a9 98 ab 00 82 05 75 d6 57 7c 35 fe e4 a6 ab a3 2e 3f 06 41 9b fa 16 21 e3 a9 74 3d b7 02 01 05 46 84 a3 23 3a 55 28 f8 0c 25 ac 0c 04 4c 71 16 7b 19 93 64 39 3b 60 28 fb 02 03 01 00 01

Thanks!
Jun 18, 2015 at 9:00 PM
Ok, so I am on the path to self enlightenment via reading the RFCs
http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-07
http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-07#ref-JWS

So each piece of the token (header, payload, signature) is part of the example string, delimited by a '.' character so we get three distinct packages

eyJ0eXAiOiJKV1QiLCJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEifQ
base64 decoded into
{"typ":"JWT","alg":"http://www.w3.org/2000/09/xmldsig#rsa-sha1"}

eyJ1bmlxdWVfbmFtZSI6ImJvYlRoZUJ1aWxkZXIiLCJyb2xlIjoiQ3VzdG9tUm9sZU9mQXdlc29tZW5lc3MiLCJpc3MiOiJodHRwczovL3Rlc3QubmRpLm51YW5jZS5jb20vP2Zvbz1iYXIiLCJhdWQiOiJodHRwczovL3d3dy5udWFuY2UuY29tLyIsImV4cCI6MTQzNDczMTQ5NiwibmJmIjoxNDM0NjQ1MDk2fQ
base64 decoded into
{"unique_name":"bobTheBuilder","role":"CustomRoleOfAwesomeness","iss":"https://test.ndi.nuance.com/?foo=bar","aud":"https://www.nuance.com/","exp":1434731496,"nbf":1434645096}

And the last piece is simply the digital signature of the first two pieces in PKCS1.5 format (according to the RFC that is). My next step will be to create the hash of the first two pieces and try to reproduce the signature with the private key (that I have on me).
Jun 19, 2015 at 12:57 AM
Hi stunney,

No we don't have any APIs that directly will take care of this. You can manually do this yourself, as you've started. We have some utilities for base64 encoding/decoding that might be helpful under the utility::conversions namespace, for example from_base64(...).

Steve
Jun 19, 2015 at 6:55 PM
Hello Steve and thank you! Found an annoying thing with from_base64. One must right-pad the string to make the length evenly divisible by 4.

std::string base64Decode(const std::wstring& encoded)
{
try
{
    std::wstring toPossiblyPad(encoded); //copy!
    toPossiblyPad += std::wstring(toPossiblyPad.length() % 4, L'=');        
    std::vector<unsigned char> decoded = utility::conversions::from_base64(toPossiblyPad.c_str());
    std::string strDecoded(decoded.begin(), decoded.end());
    return strDecoded;
}
catch (std::exception ex)
{
    //TODO!
}
catch (...)
{
            //TODO!
}
}
Jun 23, 2015 at 5:45 PM
Hi stunney,

Yes an improvement could be made to allow the decoding to tolerate input that didn't include '=' padding. We accept contributions to the library, if you wanted to make the improvement all the code is in Release\src\utilities\base64.cpp.

Steve
Aug 5, 2015 at 3:16 PM
Hello Steve,

Ok, so I've continued on my path and I now have code to generate JWTs properly, encode them, sign and verify with CNG using the windows certificate store. Excellent.

Now I am trying to ensure the validity of the token by placing the public certificate chain of the signing key into the JWT's header using the x5c field in the specification
https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41 (search x5c)

So the contents of this field are base64 encoded (not base64URL encoded).
I can encode the public certificate using pure base64 encoding. I then place the certificate into the header as such

{\"alg\":\"RS256\",\"typ\":\"JWT\",\"x5c\":\"MIIC7TCCAlagAwIBAgIQSJtiiU9cCIdASOTClOw3KjANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDExdzaGEyNTZDQS5uZGkubnVhbmNlLmNvbTAeFw0xNTA3MjcxNTMwMDlaFw0zOTEyMzEyMzU5NTlaMF0xWzBZBgNVBAMeUgBTAGkAZwBuAGUAZABCAHkAQwBBAF8AUwBIAEEAMgA1ADYALgBzAGgAYQAyADUANgBDAEEALgBuAGQAaQAuAG4AdQBhAG4AYwBlAC4AYwBvAG0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj7rtgy3p8q6WkT4y5j0G5OHHkOvX4cPVizUrA8tO7pfAWO+7iZ0eoM6aI8Z+uKR879zIcJs3czwg+cs0nemWh6dpyvr2GSUDmg5smJdw/sQFnsZQ048cEkG2aulRrL/z/iz2bIf6h48VPBahBHpNl577nHUcffFJuFea9JHa4m+nVZnZqIOcOazmu0ZigBEH/gl0yX/tcpAjgh+g6/8G+qUcIt77WC9VjT+lqeVGzp+QSR+518/4z8XRVutQlK/SO87rSLjbenWJ99fsiLKBSiA2OivgYN4ol7iHuOM2PT+9c5p50v5i4XNYKz5zuaXlSYc+duVFUD6PkSDQ1nKxnAgMBAAGjZTBjMAwGA1UdEwEB/wQCMAAwUwYDVR0BBEwwSoAQcnc96g42nSDzwsDLg3D+i6EkMCIxIDAeBgNVBAMTF3NoYTI1NkNBLm5kaS5udWFuY2UuY29tghBYaHOn4piIn00mSRBoZnFXMA0GCSqGSIb3DQEBCwUAA4GBAMOAdeqtl2j/MYVGoy81NDW85rwKBMI88vilqgcZ2FLek1pVXyzbB91WAjVWUIYar9eoz9dM4O4uiS/oAXa/jHQL3KskYydQOz1kCIlmiuemtCHWjdmHzX/ylLM1bV25gTuNZ8/2zpkYR3JbaQlSECzmQ5MmIFE91ZSkIwzM4VFR\"}

This is then base64URL encoded into
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6Ik1JSUM3VENDQWxhZ0F3SUJBZ0lRU0p0aWlVOWNDSWRBU09UQ2xPdzNLakFOQmdrcWhraUc5dzBCQVFzRkFEQWlNU0F3SGdZRFZRUURFeGR6YUdFeU5UWkRRUzV1WkdrdWJuVmhibU5sTG1OdmJUQWVGdzB4TlRBM01qY3hOVE13TURsYUZ3MHpPVEV5TXpFeU16VTVOVGxhTUYweFd6QlpCZ05WQkFNZVVnQlRBR2tBWndCdUFHVUFaQUJDQUhrQVF3QkJBRjhBVXdCSUFFRUFNZ0ExQURZQUxnQnpBR2dBWVFBeUFEVUFOZ0JEQUVFQUxnQnVBR1FBYVFBdUFHNEFkUUJoQUc0QVl3QmxBQzRBWXdCdkFHMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDajdydGd5M3A4cTZXa1Q0eTVqMEc1T0hIa092WDRjUFZpelVyQTh0TzdwZkFXTys3aVowZW9NNmFJOFordUtSODc5ekljSnMzY3p3ZytjczBuZW1XaDZkcHl2cjJHU1VEbWc1c21KZHcvc1FGbnNaUTA0OGNFa0cyYXVsUnJML3ovaXoyYklmNmg0OFZQQmFoQkhwTmw1NzduSFVjZmZGSnVGZWE5SkhhNG0rblZablpxSU9jT2F6bXUwWmlnQkVIL2dsMHlYL3RjcEFqZ2grZzYvOEcrcVVjSXQ3N1dDOVZqVCtscWVWR3pwK1FTUis1MTgvNHo4WFJWdXRRbEsvU084N3JTTGpiZW5XSjk5ZnNpTEtCU2lBMk9pdmdZTjRvbDdpSHVPTTJQVCs5YzVwNTB2NWk0WE5ZS3o1enVhWGxTWWMrZHVWRlVENlBrU0RRMW5LeG5BZ01CQUFHalpUQmpNQXdHQTFVZEV3RUIvd1FDTUFBd1V3WURWUjBCQkV3d1NvQVFjbmM5Nmc0Mm5TRHp3c0RMZzNEK2k2RWtNQ0l4SURBZUJnTlZCQU1URjNOb1lUSTFOa05CTG01a2FTNXVkV0Z1WTJVdVkyOXRnaEJZYUhPbjRwaUluMDBtU1JCb1puRlhNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0R0JBTU9BZGVxdGwyai9NWVZHb3k4MU5EVzg1cndLQk1JODh2aWxxZ2NaMkZMZWsxcFZYeXpiQjkxV0FqVldVSVlhcjllb3o5ZE00TzR1aVMvb0FYYS9qSFFMM0tza1l5ZFFPejFrQ0lsbWl1ZW10Q0hXamRtSHpYL3lsTE0xYlYyNWdUdU5aOC8yenBrWVIzSmJhUWxTRUN6bVE1TW1JRkU5MVpTa0l3ek00VkZSIn0

If I plug this encoded string into http://jwt.io/ 's online JWT debugger it gets decoded just fine but I get an exception from casablanca.

Is the string too long? This is only "one" RSA certificate and there will likely be 3+ certificates in an enterprise chain that I need to support.

Thoughts?
Aug 5, 2015 at 7:35 PM
And... I posted the C++ escaped wchar_t* code value (from my unit test). Here is the real value that I'm encoding:

{"alg":"RS256","typ":"JWT","x5c":"MIIC7TCCAlagAwIBAgIQSJtiiU9cCIdASOTClOw3KjANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDExdzaGEyNTZDQS5uZGkubnVhbmNlLmNvbTAeFw0xNTA3MjcxNTMwMDlaFw0zOTEyMzEyMzU5NTlaMF0xWzBZBgNVBAMeUgBTAGkAZwBuAGUAZABCAHkAQwBBAF8AUwBIAEEAMgA1ADYALgBzAGgAYQAyADUANgBDAEEALgBuAGQAaQAuAG4AdQBhAG4AYwBlAC4AYwBvAG0wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj7rtgy3p8q6WkT4y5j0G5OHHkOvX4cPVizUrA8tO7pfAWO+7iZ0eoM6aI8Z+uKR879zIcJs3czwg+cs0nemWh6dpyvr2GSUDmg5smJdw/sQFnsZQ048cEkG2aulRrL/z/iz2bIf6h48VPBahBHpNl577nHUcffFJuFea9JHa4m+nVZnZqIOcOazmu0ZigBEH/gl0yX/tcpAjgh+g6/8G+qUcIt77WC9VjT+lqeVGzp+QSR+518/4z8XRVutQlK/SO87rSLjbenWJ99fsiLKBSiA2OivgYN4ol7iHuOM2PT+9c5p50v5i4XNYKz5zuaXlSYc+duVFUD6PkSDQ1nKxnAgMBAAGjZTBjMAwGA1UdEwEB/wQCMAAwUwYDVR0BBEwwSoAQcnc96g42nSDzwsDLg3D+i6EkMCIxIDAeBgNVBAMTF3NoYTI1NkNBLm5kaS5udWFuY2UuY29tghBYaHOn4piIn00mSRBoZnFXMA0GCSqGSIb3DQEBCwUAA4GBAMOAdeqtl2j/MYVGoy81NDW85rwKBMI88vilqgcZ2FLek1pVXyzbB91WAjVWUIYar9eoz9dM4O4uiS/oAXa/jHQL3KskYydQOz1kCIlmiuemtCHWjdmHzX/ylLM1bV25gTuNZ8/2zpkYR3JbaQlSECzmQ5MmIFE91ZSkIwzM4VFR"}
Aug 6, 2015 at 3:52 PM
Turns out the code I found on the internetz is a little buggy

toPossiblyPad += std::wstring(toPossiblyPad.length() % 4, L'=');

should be

toPossiblyPad += std::wstring(4 - (toPossiblyPad.length() % 4), L'=');

That fixed my issue. It was not with Casablanca.
Marked as answer by roschuma on 8/12/2015 at 1:38 PM