2

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

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Arvind Krmar
  • 2,154
  • 2
  • 13
  • 18

2 Answers2

1

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.

Community
  • 1
  • 1
Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Thanks a lot. Generating key worked well, however, eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes); did not work, Token otherwise is verified when i used var validationParameters = new TokenValidationParameters() { ValidateAudience = false, ValidateIssuer = false, IssuerSigningKey = new ECDsaSecurityKey(pubKey) }; SecurityToken stoken; var claims = securityTokenHandler.ValidateToken(token, validationParameters, out stoken); – Arvind Krmar Jun 25 '20 at 13:40
  • @ArvindKrmar - I can't reproduce your problem. Runs fine locally on my machine and also here on [dotnetfiddle](https://dotnetfiddle.net/lQosw1). Both codes seem functionally more or less identical to me. My code (or rather the code you posted in your question) validates more directly, while the code posted in your answer encapsulates the validation process in some JWT classes. – Topaco Jun 25 '20 at 15:27
  • Thanks a lot anyway. your suggestions have solved my problem and it was a great help. I'll again try to execute the code above – Arvind Krmar Jun 26 '20 at 16:55
  • 1
    The code you suggested work a bit differently, I added this line eCDsaCng.HashAlgorithm = CngAlgorithm.Sha512; bool verified = eCDsaCng.VerifyData(headerPayloadBytes, signatureBytes, HashAlgorithmName.SHA512); for me eCDsaCng.VerifyData(); does not have the third parameter. HashAlgorithmName.SHA512 When i add the algorithm in eCDsaCng, it works This change might be because I am running it on Dot Net V 4.5 which is different from yours perhaps. – Arvind Krmar Jun 29 '20 at 11:56
  • Yes, that's right. In .NET Framework 3.5 to 4.6, the [overload](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdsacng.verifydata?view=netframework-4.8#overloads) of `VerifyData` used by you must be applied. The [overload](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecdsa.verifydata?view=netframework-4.8#overloads) used by me exists _additionally_ since .NET Framework 4.6.1 (and .NET Core 1). There was no .NET version specified in the question, so I assumed the current version (.NET Framework 4.8 or .NET Core 3.1). – Topaco Jun 29 '20 at 14:46
  • my mistake, I should have mentioned the version. – Arvind Krmar Jun 30 '20 at 13:07
1

I generated the key as suggested by @Topaco and used it with JwtSecurityTokenHandler of identity model and it worked. the function which i was trying to use in question did not work for some reasons: The working code is as follows:

public static bool ValidateEcdsa512JwtToken(string token, string publicKey)
{
    ECDsa pubKey = LoadPublicKey(publicKey);

    var securityTokenHandler = new JwtSecurityTokenHandler();
    var validationParameters = new TokenValidationParameters()
    {
        ValidateAudience = false,
        ValidateIssuer = false,
        IssuerSigningKey = new ECDsaSecurityKey(pubKey)
    };

    SecurityToken stoken;
    var claims = securityTokenHandler.ValidateToken(token, validationParameters, out stoken);
    var verified=claims.Identity.IsAuthenticated;

    return verified;
}


private static ECDsa LoadPublicKey(string publicKey)
{
     byte[] publicKeyBytes = Convert.FromBase64String(publicKey);
     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();

     CngKey cngKey = CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob);
     ECDsaCng eCDsaCng = new ECDsaCng(cngKey);
     eCDsaCng.HashAlgorithm = CngAlgorithm.ECDsaP521;
     return eCDsaCng;
}
Arvind Krmar
  • 2,154
  • 2
  • 13
  • 18