Search code examples
c#jwtelliptic-curveecdsa

ES 512 jwt token verification


I am trying to verify ES512 jwt token using public key which is in string format:

I am trying to imitate a sample written for ES256 which is something like this:

    // The code for ES256
    public static void VerifyES512Jwt(string token,string publicKey)
    {
        byte[] publicKeyBytes = Convert.FromBase64String(publicKey);

        string[] parts = token.Split('.');

        string header = parts[0];
        string payload = parts[1];
        string signature = parts[2];

        var keyType = new byte[] { 0x45, 0x43, 0x53, 0x31 };
        var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
        var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 64)).ToArray(); 
        CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);
        
        // the purpose is to get ECDsaCng and verify the payload data
         ECDsaCng eCDsaCng = new ECDsaCng(cngKey);
        
        bool result = eCDsaCng.VerifyData(payload, signatureBytes); 
    }

I am trying to use this code for ES512 and got stuck on getting the key

        var keyType = new byte[] { 0x45, 0x43, 0x53, 0x31 };
        var keyLength = new byte[] { 0x20, 0x00, 0x00, 0x00 };
        var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 64)).ToArray(); 

when used with the above it gives error in getting key:

CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);

The public key and token I am using are as follows: public key:

MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47 6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM Al8G7CqwoJOsW7Kddns=

Token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjUwOTk2ODgsImV4cCI6MTU5ODE4OTg4NSwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozNTg4Iiwic3ViIjoiaHR0cDovL2xvY2FsaG9zdDo1MDM3NiIsImZpcnN0bmFtZSI6IkFydmluZCIsImxhc3RuYW1lIjoiS3VtYXIiLCJFbWFpbCI6ImFydmluZC5rdW1hckBzdHJlYW1hbWcuY29tIiwiSWQiOiIxMDEifQ.AVwAJeY44lKrnywnDs7CdUOu3gli2cGafSJ6iP3zT7lkZpd2QnL0k54aVmPVxAGuN5dDnzbYmMTdRl5u2QE92ccOAHrcf5yA2gsvhhAGuDAAeh6Io4VU7v5TOTvwWGRb-ubgdjUvagA_HSJOyeXvFR16_M_MzGfDnXfg02sj4y9VFjDr


Solution

  • ES512 uses ECDSA with P-521 and SHA-512.

    A public key for P-521 (secp521r1, chap. 2.6.1) has a size of 2 x 66 = 132 bytes in uncompressed form (front byte 0x04). MS specifies the public key for P-521 with the value 0x35534345.

    The key must therefore be generated as follows:

    var keyType = new byte[] { 0x45, 0x43, 0x53, 0x35 };
    var keyLength = new byte[] { 0x42, 0x00, 0x00, 0x00 };
    var key = keyType.Concat(keyLength).Concat(publicKeyBytes.Skip(publicKeyBytes.Length - 132)).ToArray();
    

    The data to be signed are header and payload (both Base64url encoded) including the separator (.). The signature is also Base64url encoded. The verification must therefore be performed as follows:

    byte[] headerPayloadBytes = Encoding.UTF8.GetBytes(header + "." + payload);
    byte[] signatureBytes = Base64UrlDecode(signature);
    bool verified = eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes, HashAlgorithmName.SHA512);
    

    Base64UrlDecode accomplishes the Base64url decoding, see e.g. here for implementation details.