使用带有 SHA256 证书的 ECDSA 的 C# 签名验证
C# signature verification using ECDSA with SHA256 certificate
我正在尝试使用 C# 和内置的加密库来验证使用 EC 密钥 + SHA256 创建的签名。这就是我正在做的。
我已经使用 openssl 创建了私钥和相应的证书:
$ openssl ecparam -genkey -name prime256v1 -out ca.key
$ openssl req -x509 -new -SHA256 -nodes -key ca.key -days 36500 -out ca.crt
这是我正在使用的密钥(别担心,它们并不重要):
$ cat ca.key
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHd3OvRV1nEnoDxGzzemX1x8l2rHasWH3L/LflUGg5vloAoGCCqGSM49
AwEHoUQDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/wNcL5TdCfWt0XfnUfg0x+RsIf
1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END EC PRIVATE KEY-----
$ cat ca.crt
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----
然后我有一个包含字符串 "Hello" 的简单数据文件。然后我使用 openssl 对该文件进行签名,如下所示:
$ openssl dgst -sha256 -sign ca.key data.txt > sig
$ base64 sig
MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQ
vw676bGqOTEU/25qcRQ=
然后我可以通过首先从证书中提取 public 密钥来验证签名,然后使用它来验证:
$ openssl x509 -pubkey -noout -in ca.crt > ca.pub
$ cat ca.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/w
NcL5TdCfWt0XfnUfg0x+RsIf1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END PUBLIC KEY-----
$ openssl dgst -verify ca.pub -sha256 -signature sig data.txt
Verified OK
然后我尝试使用 C# (.NET Core 3.1) 来验证签名。代码如下:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace security_sandbox
{
class Program
{
static void Main(string[] args)
{
var certData = Encoding.ASCII.GetBytes(
@"-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----");
var cert = new X509Certificate2(certData);
var ecdsa = cert.GetECDsaPublicKey();
var data = Encoding.ASCII.GetBytes("Hello");
var signature = Convert.FromBase64String("MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQvw676bGqOTEU/25qcRQ=");
var success = ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
if (success)
{
Console.WriteLine("Verified");
} else
{
Console.WriteLine("Failed");
}
}
}
}
不幸的是,它总是验证失败。哪里错了?
.NET 中缺少 PEM/OpenSSL-compatible 操作工具被证明是极其令人沮丧的。我最终使用 Bouncy Castle 加载证书或 public 密钥,然后使用它来验证我的 ASN 签名。这是一个完整的工作代码示例,演示了如何使用证书和 PEM public 密钥以及使用 ASN 编码的签名来执行签名验证。
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace security_sandbox
{
class Program
{
static void Main(string[] args)
{
var certificateString = @"-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----";
var pemreader = new PemReader(new StringReader(certificateString));
var cert = (X509Certificate)pemreader.ReadObject();
// Alternatively, load the public key directly
var pubkeyString =
@"-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/w
NcL5TdCfWt0XfnUfg0x+RsIf1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END PUBLIC KEY-----";
pemreader = new PemReader(new StringReader(pubkeyString));
var pubkey = (AsymmetricKeyParameter)pemreader.ReadObject();
var data = "Hello";
var signature = Convert.FromBase64String("MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQvw676bGqOTEU/25qcRQ=");
// Verify using the public key
var signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(false, pubkey);
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
var success = signer.VerifySignature(signature);
if (success) {
Console.WriteLine("Signature verified successfully using public key");
} else {
Console.WriteLine("Failed to verify signature using public key");
}
// Verify using the certificate - the certificate's public key is extracted using the GetPublicKey method.
signer.Init(false, cert.GetPublicKey());
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
success = signer.VerifySignature(signature);
if (success) {
Console.WriteLine("Signature verified successfully using certificate");
} else {
Console.WriteLine("Failed to verify signature using certificate");
}
}
}
}
我正在尝试使用 C# 和内置的加密库来验证使用 EC 密钥 + SHA256 创建的签名。这就是我正在做的。
我已经使用 openssl 创建了私钥和相应的证书:
$ openssl ecparam -genkey -name prime256v1 -out ca.key
$ openssl req -x509 -new -SHA256 -nodes -key ca.key -days 36500 -out ca.crt
这是我正在使用的密钥(别担心,它们并不重要):
$ cat ca.key
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIHd3OvRV1nEnoDxGzzemX1x8l2rHasWH3L/LflUGg5vloAoGCCqGSM49
AwEHoUQDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/wNcL5TdCfWt0XfnUfg0x+RsIf
1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END EC PRIVATE KEY-----
$ cat ca.crt
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----
然后我有一个包含字符串 "Hello" 的简单数据文件。然后我使用 openssl 对该文件进行签名,如下所示:
$ openssl dgst -sha256 -sign ca.key data.txt > sig
$ base64 sig
MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQ
vw676bGqOTEU/25qcRQ=
然后我可以通过首先从证书中提取 public 密钥来验证签名,然后使用它来验证:
$ openssl x509 -pubkey -noout -in ca.crt > ca.pub
$ cat ca.pub
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/w
NcL5TdCfWt0XfnUfg0x+RsIf1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END PUBLIC KEY-----
$ openssl dgst -verify ca.pub -sha256 -signature sig data.txt
Verified OK
然后我尝试使用 C# (.NET Core 3.1) 来验证签名。代码如下:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace security_sandbox
{
class Program
{
static void Main(string[] args)
{
var certData = Encoding.ASCII.GetBytes(
@"-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----");
var cert = new X509Certificate2(certData);
var ecdsa = cert.GetECDsaPublicKey();
var data = Encoding.ASCII.GetBytes("Hello");
var signature = Convert.FromBase64String("MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQvw676bGqOTEU/25qcRQ=");
var success = ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
if (success)
{
Console.WriteLine("Verified");
} else
{
Console.WriteLine("Failed");
}
}
}
}
不幸的是,它总是验证失败。哪里错了?
.NET 中缺少 PEM/OpenSSL-compatible 操作工具被证明是极其令人沮丧的。我最终使用 Bouncy Castle 加载证书或 public 密钥,然后使用它来验证我的 ASN 签名。这是一个完整的工作代码示例,演示了如何使用证书和 PEM public 密钥以及使用 ASN 编码的签名来执行签名验证。
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
namespace security_sandbox
{
class Program
{
static void Main(string[] args)
{
var certificateString = @"-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIUKt0WdaKI2eRXBO2nVk+OF6AZqHMwCgYIKoZIzj0EAwIw
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMDA1MDcxMzM2MTNaGA8yMTIwMDQx
MzEzMzYxM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABO39ccEC+Zv1HDeMy/s7JculdYN/8DXC+U3Qn1rdF351H4NMfkbC
H9bnqwZ4a5oR9HDfaNMX14OQd4VERZV1bhGjUzBRMB0GA1UdDgQWBBRGuUmsyB2h
JCXMRTVMRTcdoWZQaDAfBgNVHSMEGDAWgBRGuUmsyB2hJCXMRTVMRTcdoWZQaDAP
BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGG8tQZlh7aJaI34Y7jq
44SmSc/ule9MgjIX+Gg+i5vwAiEA9Jb/304KO4t9OMqFMQeWZXIHdzhDFBwx7FWz
78+UsnY=
-----END CERTIFICATE-----";
var pemreader = new PemReader(new StringReader(certificateString));
var cert = (X509Certificate)pemreader.ReadObject();
// Alternatively, load the public key directly
var pubkeyString =
@"-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7f1xwQL5m/UcN4zL+zsly6V1g3/w
NcL5TdCfWt0XfnUfg0x+RsIf1uerBnhrmhH0cN9o0xfXg5B3hURFlXVuEQ==
-----END PUBLIC KEY-----";
pemreader = new PemReader(new StringReader(pubkeyString));
var pubkey = (AsymmetricKeyParameter)pemreader.ReadObject();
var data = "Hello";
var signature = Convert.FromBase64String("MEUCIQD5593C/NBhHA1DILT72gjhGj/lKjom9vYP+JbuypBrxQIgNAjYT1LihEpPbUhe1n9ccUHQvw676bGqOTEU/25qcRQ=");
// Verify using the public key
var signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(false, pubkey);
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
var success = signer.VerifySignature(signature);
if (success) {
Console.WriteLine("Signature verified successfully using public key");
} else {
Console.WriteLine("Failed to verify signature using public key");
}
// Verify using the certificate - the certificate's public key is extracted using the GetPublicKey method.
signer.Init(false, cert.GetPublicKey());
signer.BlockUpdate(Encoding.ASCII.GetBytes(data), 0, data.Length);
success = signer.VerifySignature(signature);
if (success) {
Console.WriteLine("Signature verified successfully using certificate");
} else {
Console.WriteLine("Failed to verify signature using certificate");
}
}
}
}