使用 OpenSSL 验证签名

问题描述 投票:0回答:1

基于较旧的post,我正在使用 Azure Key Vault 使用在 Azure Key Vault 中创建的 EC 密钥来签署某些内容。在 C# 代码片段中,一切正常,两个签名(IEEE P1363 和 RFC 3279 Der Sequence)都可以成功验证。

如果我用 OpenSSL 验证签名,验证失败。 我已使用此命令从Azure下载了公钥,只是为了确保我拥有正确的PEM格式的公钥(我已将其保存为pubkey.pem)。出于测试目的,我将 C# 脚本中的签名结果保存在名为 sig.txt 的文件中。我确保 C# 和 OpenSSL 中的摘要是相同的。我确保公钥格式正确。

这是 C# 中的一个小片段,我在其中硬编码了签名和公钥。

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.");
}

现在是我在 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

现在,我尝试通过运行来查看公钥是否正确:

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

它返回关键详细信息,它们看起来没问题。

然后我厌倦了通过运行这些命令来查看签名是否有效:

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

签名有问题,还是我没有正确验证签名?

azure openssl signature ecdsa
1个回答
0
投票

对于我造成的任何混乱,我们深表歉意。 我的目标是使用将从外部系统收到的数据、公钥和签名作为三个 base64 字符串,在 C# 和 OpenSSL 中验证它们。

我的问题是签名是原始格式,OpenSSL 总是验证失败。

所以我做的第一件事就是运行这个 C# 脚本

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;
}

现在,使用从外部系统获取的签名和上面 C# 脚本转换的签名这两种签名,我尝试使用 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

结论:我的问题是当我尝试将原始签名转换为 RFC3279 时。我缺少前导零,添加这些前导零后,签名也在 OpenSSL 中得到了验证

© www.soinside.com 2019 - 2024. All rights reserved.