Search code examples
c#digital-signatureazure-keyvault

Azure Key Vault Signature Format


I have this snippet on my local machine (dotnet 8.0) which is working fine.

using System.Security.Cryptography;

using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256))
{
    
    string input = "some base64 encoded data";
    byte[] data_bytes = Convert.FromBase64String(input);

    byte[] hash = SHA256.Create().ComputeHash(data_bytes);

    byte[] publicKeyBytes = ecdsa.ExportSubjectPublicKeyInfo();
    Console.WriteLine($"Public Key:\n{Convert.ToBase64String(publicKeyBytes)}\n");

    // Sign the hash using the Rfc3279DerSequence format
    byte[] signature_der = ecdsa.SignHash(hash, DSASignatureFormat.Rfc3279DerSequence);
    bool isValid_der = ecdsa.VerifyHash(hash, signature_der, DSASignatureFormat.Rfc3279DerSequence);
    Console.WriteLine($"Signature valid: {isValid_der}\n");
    string signature_b64_der = Convert.ToBase64String(signature_der);
    Console.WriteLine($"DER Signature:\n{signature_b64_der}\n");

    // Sign the hash using the IeeeP1363FixedFieldConcatenation format
    byte[] signature_ieee = ecdsa.SignHash(hash, DSASignatureFormat.IeeeP1363FixedFieldConcatenation);
    bool isValid_ieee = ecdsa.VerifyHash(hash, signature_ieee, DSASignatureFormat.IeeeP1363FixedFieldConcatenation);
    Console.WriteLine($"Signature valid: {isValid_ieee}\n");
    string signature_b64_ieee = Convert.ToBase64String(signature_ieee);
    Console.WriteLine($"IEEE Signature:\n{signature_b64_ieee}\n");
}

I want to implement the same logic, but this time I want to use Azure Key vault REST APIs to sign and verify same kind of data. I aimed for this approach so I can rotate keys regularly.

The signature return by Azure Key Vault seems to be returned as IEEE P1363 format only and I did not find a way to return the RFC 3279 DER Sequence format.

My question: Is there a way to obtain from Azure Key Vault signature in a different format, and of course to be able to verify that signature?

I've tried to use the CryptographyClient.SignAsync, but again it returns only the IEEE P1363 format, and I want the other format too (if possible).


Solution

  • I generated EC key type with P-256 Elliptic curve:

    enter image description here

    • Convert from IEEE P1363 to DER.
    • From Azure Key Vault retrieve the IEEE P1363 signature.
    • Use ConvertIeeeP1363ToDer function to convert it to DER.
    • Use ECDsa.VerifyHash to verify the DER-formatted signature.

    Make use of below code:

    public class AzureKeyVaultECDSASignature
    {
        private static string keyVaultUrl = "https://rrruuukkk.vault.azure.net/";
        private static string keyName = "myECDSAKey";
    
        public static async Task Main(string[] args)
        {
            var client = new KeyClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
    
            var key = await client.GetKeyAsync(keyName);
            var keyId = key.Value.Id;
            var cryptoClient = new CryptographyClient(keyId, new DefaultAzureCredential());
    
            string message = "Hello, Azure Key Vault!";
            byte[] messageHash = SHA256.HashData(Encoding.UTF8.GetBytes(message));
    
            byte[] ieeeSignature = await SignMessageAsync(cryptoClient, messageHash);
            Console.WriteLine("IEEE P1363 Signature (Base64): " + Convert.ToBase64String(ieeeSignature));
    
            byte[] derSignature = ConvertIeeeP1363ToDer(ieeeSignature);
            Console.WriteLine("DER Signature (Base64): " + Convert.ToBase64String(derSignature));
    
            bool isValid = await VerifySignatureAsync(messageHash, derSignature);
            Console.WriteLine($"Signature valid in DER format: {isValid}");
        }
    
        private static async Task<byte[]> SignMessageAsync(CryptographyClient cryptoClient, byte[] messageHash)
        { 
            var signResult = await cryptoClient.SignAsync(SignatureAlgorithm.ES256, messageHash);
            return signResult.Signature;
        }
    
        private static async Task<bool> VerifySignatureAsync(byte[] hash, byte[] derSignature)
        {
            var client = new KeyClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
            var key = await client.GetKeyAsync(keyName);
            var jsonWebKey = key.Value.Key;
    
            if (jsonWebKey.X != null && jsonWebKey.Y != null)
            {
                using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256))
                {
                    ecdsa.ImportParameters(new ECParameters
                    {
                        Curve = ECCurve.NamedCurves.nistP256,
                        Q = new ECPoint
                        {
                            X = jsonWebKey.X,
                            Y = jsonWebKey.Y
                        }
                    });
    
                    // Verify the signature
                    return ecdsa.VerifyHash(hash, derSignature, DSASignatureFormat.Rfc3279DerSequence);
                }
            }
    
            throw new InvalidOperationException("Invalid public key in the key vault.");
        }
    
        // Method to convert IEEE P1363 signature to DER sequence format
        private static byte[] ConvertIeeeP1363ToDer(byte[] ieeeSignature)
        {
            if (ieeeSignature.Length % 2 != 0)
            {
                throw new ArgumentException("Invalid IEEE P1363 signature length");
            }
    
            int halfLength = ieeeSignature.Length / 2;
            byte[] r = new byte[halfLength];
            byte[] s = new byte[halfLength];
    
            Array.Copy(ieeeSignature, 0, r, 0, halfLength);
            Array.Copy(ieeeSignature, halfLength, s, 0, halfLength);
    
            var writer = new AsnWriter(AsnEncodingRules.DER);
            writer.PushSequence();
            writer.WriteInteger(r);
            writer.WriteInteger(s);
            writer.PopSequence();
    
            return writer.Encode();
        }
    }
    

    enter image description here