azureopensslsignatureecdsa

Verifying a signature with OpenSSL


Based on an older post, I am using Azure Key Vault to sign something with an EC Key created in Azure Key Vault. In the C# snippet everything works well and both signatures (IEEE P1363 and RFC 3279 Der Sequence) can be verified successfully.

If I were to verify the signature with OpenSSL, the verification fails. I have downloaded the Public Key from Azure using this command, just to make sure I have the proper Public Key in PEM format (I've saved it as pubkey.pem). For testing purposes I have saved the signature resulted in my C# script in a file called sig.txt. I made sure the digest is the same in both C# and OpenSSL. I made sure the Public key is correctly formatted.

Here is a small snippet in C# where I hard-codded the signature and the public key.

using System.Security.Cryptography;

string b64 = "SGVsbG9Xb3JsZA==";
byte[] b64_bytes = Convert.FromBase64String(b64);
byte[] digest = SHA256.Create().ComputeHash(b64_bytes);
string b64_hex = BitConverter.ToString(digest).Replace("-", ""); // compare the result

string signature = "base64_string";
string publicKey = "base64_string";

byte[] signatureBytes = Convert.FromBase64String(signature);
byte[] publicKeyBytes = Convert.FromBase64String(publicKey);

using (ECDsa ecdsa = ECDsa.Create())
{
    ecdsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
    bool isValid = ecdsa.VerifyHash(digest, signatureBytes, DSASignatureFormat.Rfc3279DerSequence);
    Console.WriteLine(isValid ? "Signature is valid." : "Signature is invalid.");
}

Now my commands in OpenSSL

openssl base64 -d -in b64.txt -out b64.der
openssl dgst -sha256 b64.der # compare the result
openssl dgst -sha256 -verify pubkey.pem -signature sig.txt b64.der
Error verifying data

Now, I've tried to see if the Public Key is correct by running:

openssl ec -pubin -in pubkey.pem -text -noout

And it returns the key details and they seem okay.

Then I've tired to see if the signature is valid by running these commands:

openssl base64 -d -in sig.txt -out sig.der
openssl asn1parse -in sig.der -inform DER
openssl dgst -sha256 -verify pubkey.pem -signature sig.der b64.der
Error verifying data

Is there a problem with the signature, or I am not verifying the signature correctly?


Solution

  • Sorry for any confusion that I've created. Thanks @Rukmini for leading me in the right direction. My goal was to use the data, the public key and the signature that I will receive from a external system as three base64 strings, to verify them in both C# and in OpenSSL. In this scenario, the external system is Azure Key Vault; I have an EC Key stored there, and I am using the Azure Key Vault REST APIs for sign and verify operations.

    My issue was that the signature was in raw format, and OpenSSL always failed the verification.

    So the first thing I do is run this C# script

    using System.Formats.Asn1;
    using System.Security.Cryptography;
    
    // Inputs
    // Public key and Signature from another system.
    string publicKey_FromOtherSystem = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcLNwTTk9eixQnaMPBmETJpdip0FBHcRrO1Rm2j6geNmWcl1v1pnoipc7ah09sWayJrlssqGTMX2CHiaU6X5kXQ==";
    string signature_FromOtherSystem = "fKsprDKyHNqIP3lCtJBBp+Kt+oEOPgYUpPDtA4/O0J+1A1xAku9PsVe/wMI3DgjUR0LMLkWeY950hLJ/L0dVwQ==";
    string input = "SGVsbG8gV29ybGQ="; 
    
    byte[] input_base64_bytes = Convert.FromBase64String(input);
    byte[] digest = SHA256.Create().ComputeHash(input_base64_bytes);
    string input_hex = BitConverter.ToString(digest).Replace("-", "");
    Console.WriteLine("Digest: " + input_hex); // Check this value with the one you get in openssl
    
    byte[] publicKeyBytes = Convert.FromBase64String(publicKey_FromOtherSystem);
    byte[] signatureBytes = Convert.FromBase64String(signature_FromOtherSystem);
    
    // convert the raw signature from the other system to be ASN1.DER Formatted
    byte[] rfc3279DerSignature = ConvertIeeeP1363ToRfc3279Der(signatureBytes);
    string rfc3279DerSignature_base64 = Convert.ToBase64String(rfc3279DerSignature); // This signature must be validated also with openssl
    Console.WriteLine("Signature in RFC3279 DER format: " + rfc3279DerSignature_base64);
    
    using (ECDsa ecdsa = ECDsa.Create())
    {
        ecdsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
        bool isValid = false;
        isValid = ecdsa.VerifyHash(digest, signatureBytes, DSASignatureFormat.Rfc3279DerSequence);
        Console.WriteLine($"IEEE Signature verification returned: {isValid}"); // This should FAIL (using the Raw Signature)
        isValid = ecdsa.VerifyHash(digest, rfc3279DerSignature, DSASignatureFormat.Rfc3279DerSequence);
        Console.WriteLine($"RFC Signature verification returned: {isValid}"); // This should be valid // This will be true even without the Leading Zeros, but it will fail in OpenSSL
    }
    
    static byte[] ConvertIeeeP1363ToRfc3279Der(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(AddLeadingZeroIfNeeded(r));
        writer.WriteInteger(AddLeadingZeroIfNeeded(s));
        writer.PopSequence();
    
        return writer.Encode();
    }
    
    static byte[] AddLeadingZeroIfNeeded(byte[] value)
    {
        if (value[0] >= 0x80)
        {
            byte[] extendedValue = new byte[value.Length + 1];
            Array.Copy(value, 0, extendedValue, 1, value.Length);
            return extendedValue;
        }
        return value;
    }
    

    Now, using both signatures, the one that I got from the external system and the one converted by the C# script above, I've tried to validate the signature using OpenSSL

    $ data="SGVsbG8gV29ybGQ="
    $ pubkey="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcLNwTTk9eixQnaMPBmETJpdip0FBHcRrO1Rm2j6geNmWcl1v1pnoipc7ah09sWayJrlssqGTMX
    2CHiaU6X5kXQ=="
    $ sigRaw="fKsprDKyHNqIP3lCtJBBp+Kt+oEOPgYUpPDtA4/O0J+1A1xAku9PsVe/wMI3DgjUR0LMLkWeY950hLJ/L0dVwQ=="
    $ sigRfc="MEUCIHyrKawyshzaiD95QrSQQafirfqBDj4GFKTw7QOPztCfAiEAtQNcQJLvT7FXv8DCNw4I1EdCzC5FnmPedISyfy9HVcE="
    
    $ echo $data | base64 -d > data.bin
    $ echo $pubkey | base64 -d > pubKey.pem
    $ echo $sigRfc | base64 -d > sigrfc.bin
    $ echo $sigRaw | base64 -d > sigraw.bin
    
    $ openssl dgst -sha256 data.bin
    SHA2-256(data.bin)= a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e
    
    $ openssl dgst -sha256 -verify pubkey.pem -signature sigraw.bin data.bin
    Error verifying data
    
    $ openssl dgst -sha256 -verify pubkey.pem -signature sigrfc.bin data.bin
    Verified OK
    

    Conclusion: My issue was when I tried to convert the raw signature to RFC3279. I was missing the leading zeros and after adding those leading zeros, the signature was validated also in OpenSSL