基于较旧的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
签名有问题,还是我没有正确验证签名?
对于我造成的任何混乱,我们深表歉意。 我的目标是使用将从外部系统收到的数据、公钥和签名作为三个 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 中得到了验证