如何在 C# 中使用 SHA256 签名签署和验证 ECDSA
How to sign and verify an ECDSA with SHA256 signature in C#
我有一个包含 EC 私钥的文件:
-----BEGIN EC PRIVATE KEY-----
<data>
-----END EC PRIVATE KEY-----
我有一个证书,其 public 密钥对应于私钥:
pem 格式:
-----BEGIN CERTIFICATE-----
<data>
-----END CERTIFICATE-----
txt格式:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
80:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:15
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN = MyIssuer
Validity
Not Before: Jan 27 19:33:43 2020 GMT
Not After : Jun 10 19:33:43 2021 GMT
Subject: CN = MyIssuer MyCert
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:6a:9c:40:7b:71:06:3a:7f:3a:1e:4c:5c:60:9e:
d0:c4:a0:c0:c7:39:ec:6d:f1:5b:27:5a:5f:3b:f8:
77:29:7e:38:8e:e6:77:cf:d2:9c:77:c2:43:f4:92:
73:a8:10:d2:e9:f2:bb:d7:4d:97:76:07:d0:1f:16:
7b:01:d3:35:26
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Authority Key Identifier:
keyid:E0:1C:E6:36:88:8B:3D:77:A6:9D:80:8B:7B:9B:1D:1E:FF:24:74:B3
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Key Identifier:
49:57:2F:01:37:1B:E2:B6:9C:1A:C7:A9:03:9A:D1:61:7E:9B:4A:84
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
Signature Algorithm: ecdsa-with-SHA256
30:44:02:20:2c:1a:5c:ee:4a:58:59:f1:5a:4a:93:ed:e0:24:
70:9d:15:11:5e:11:df:30:5a:0f:54:a0:9f:95:c4:eb:f9:6d:
02:20:17:fe:e4:8c:ef:ef:34:56:fb:5d:79:40:f8:fc:f4:2f:
97:90:4b:ac:cb:b6:64:c0:58:3e:2d:fe:7b:4f:ea:19
-----BEGIN CERTIFICATE-----
MIIBrTCCAVSgAwIBAgIVAIAAAAAAAAAAAAAAAAAAAAAAAAAVMAoGCCqGSM49BAMC
MBIxEDAOBgNVBAMMB0lzc3VlckMwHhcNMjAwMTI3MTkzMzQzWhcNMjEwNjEwMTkz
MzQzWjAaMRgwFgYDVQQDDA9Jc3N1ZXJDIERldmljZTMwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAARqnEB7cQY6fzoeTFxgntDEoMDHOext8VsnWl87+HcpfjiO5nfP
0px3wkP0knOoENLp8rvXTZd2B9AfFnsB0zUmo38wfTAMBgNVHRMBAf8EAjAAMB8G
A1UdIwQYMBaAFOAc5jaIiz13pp2Ai3ubHR7/JHSzMB0GA1UdJQQWMBQGCCsGAQUF
BwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUSVcvATcb4racGsepA5rRYX6bSoQwDgYD
VR0PAQH/BAQDAgTwMAoGCCqGSM49BAMCA0cAMEQCICwaXO5KWFnxWkqT7eAkcJ0V
EV4R3zBaD1Sgn5XE6/ltAiAX/uSM7+80VvtdeUD4/PQvl5BLrMu2ZMBYPi3+e0/q
GQ==
-----END CERTIFICATE-----
我正在努力完成两件事:
- 使用带有 ecdsa-with-SHA256 签名算法的私钥对字节数组进行签名
- 使用证书中的 public 密钥验证签名是否正确
我试过使用 Bouncy Castle 库。这是我到目前为止所拥有的。我的断言失败了。
[TestMethod]
public void TestSignAndVerify()
{
var data = new byte[] {6, 5, 4, 3, 2};
var keyPair = GetKeyPair();
var signature = SignData(data, keyPair.Private);
var valid = VerifySignature(signature, keyPair.Public);
Assert.IsTrue(valid);
}
// get key pair from two local files
private AsymmetricCipherKeyPair GetKeyPair()
{
AsymmetricKeyParameter privateKey, publicKey;
var privateKeyString = File.ReadAllText("C:\privatekey.pem");
using (var textReader = new StringReader(privateKeyString))
{
// Only a private key
var pseudoKeyPair = (AsymmetricCipherKeyPair)new PemReader(textReader).ReadObject();
privateKey = pseudoKeyPair.Private;
}
var certificateString = File.ReadAllText("C:\publicCert.pem");
using (var textReader = new StringReader(certificateString))
{
// Only a private key
Org.BouncyCastle.X509.X509Certificate bcCertificate = (X509Certificate)new PemReader(textReader).ReadObject();
publicKey = bcCertificate.GetPublicKey();
}
return new AsymmetricCipherKeyPair(publicKey, privateKey);
}
public byte[] SignData(byte[] data, AsymmetricKeyParameter privateKey)
{
var signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(true, privateKey);
signer.BlockUpdate(data, 0, data.Length);
return signer.GenerateSignature();
}
public bool VerifySignature(byte[] signature, AsymmetricKeyParameter publicKey)
{
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, publicKey);
verifier.BlockUpdate(signature, 0, signature.Length);
return verifier.VerifySignature(signature);
}
,需要对数据使用verifier.blockUpdate
,然后使用VerifySignature
中的签名字节。
无法自行验证签名。该算法在进入验证状态之前需要数据作为输入来计算哈希值。当然,在没有数据的情况下验证签名只能提供有限的价值:您只能证明使用了私钥。
使用 ECDSA 也没有真正的选项来验证没有哈希的签名的任何部分
RSA / PKCS#1 理论上可以让您验证签名的内容至少是由私钥创建的,即使内容是未知的。由于您可以检索完整的哈希值,因此无需对每个可能的输入值都经过签名验证阶段就可以猜测数据输入。大多数 API 当然不适合这种签名的使用,因为这是特定于某些方案的。
我有一个包含 EC 私钥的文件:
-----BEGIN EC PRIVATE KEY-----
<data>
-----END EC PRIVATE KEY-----
我有一个证书,其 public 密钥对应于私钥:
pem 格式:
-----BEGIN CERTIFICATE-----
<data>
-----END CERTIFICATE-----
txt格式:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
80:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:15
Signature Algorithm: ecdsa-with-SHA256
Issuer: CN = MyIssuer
Validity
Not Before: Jan 27 19:33:43 2020 GMT
Not After : Jun 10 19:33:43 2021 GMT
Subject: CN = MyIssuer MyCert
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:6a:9c:40:7b:71:06:3a:7f:3a:1e:4c:5c:60:9e:
d0:c4:a0:c0:c7:39:ec:6d:f1:5b:27:5a:5f:3b:f8:
77:29:7e:38:8e:e6:77:cf:d2:9c:77:c2:43:f4:92:
73:a8:10:d2:e9:f2:bb:d7:4d:97:76:07:d0:1f:16:
7b:01:d3:35:26
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Authority Key Identifier:
keyid:E0:1C:E6:36:88:8B:3D:77:A6:9D:80:8B:7B:9B:1D:1E:FF:24:74:B3
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Subject Key Identifier:
49:57:2F:01:37:1B:E2:B6:9C:1A:C7:A9:03:9A:D1:61:7E:9B:4A:84
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
Signature Algorithm: ecdsa-with-SHA256
30:44:02:20:2c:1a:5c:ee:4a:58:59:f1:5a:4a:93:ed:e0:24:
70:9d:15:11:5e:11:df:30:5a:0f:54:a0:9f:95:c4:eb:f9:6d:
02:20:17:fe:e4:8c:ef:ef:34:56:fb:5d:79:40:f8:fc:f4:2f:
97:90:4b:ac:cb:b6:64:c0:58:3e:2d:fe:7b:4f:ea:19
-----BEGIN CERTIFICATE-----
MIIBrTCCAVSgAwIBAgIVAIAAAAAAAAAAAAAAAAAAAAAAAAAVMAoGCCqGSM49BAMC
MBIxEDAOBgNVBAMMB0lzc3VlckMwHhcNMjAwMTI3MTkzMzQzWhcNMjEwNjEwMTkz
MzQzWjAaMRgwFgYDVQQDDA9Jc3N1ZXJDIERldmljZTMwWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAARqnEB7cQY6fzoeTFxgntDEoMDHOext8VsnWl87+HcpfjiO5nfP
0px3wkP0knOoENLp8rvXTZd2B9AfFnsB0zUmo38wfTAMBgNVHRMBAf8EAjAAMB8G
A1UdIwQYMBaAFOAc5jaIiz13pp2Ai3ubHR7/JHSzMB0GA1UdJQQWMBQGCCsGAQUF
BwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUSVcvATcb4racGsepA5rRYX6bSoQwDgYD
VR0PAQH/BAQDAgTwMAoGCCqGSM49BAMCA0cAMEQCICwaXO5KWFnxWkqT7eAkcJ0V
EV4R3zBaD1Sgn5XE6/ltAiAX/uSM7+80VvtdeUD4/PQvl5BLrMu2ZMBYPi3+e0/q
GQ==
-----END CERTIFICATE-----
我正在努力完成两件事:
- 使用带有 ecdsa-with-SHA256 签名算法的私钥对字节数组进行签名
- 使用证书中的 public 密钥验证签名是否正确
我试过使用 Bouncy Castle 库。这是我到目前为止所拥有的。我的断言失败了。
[TestMethod]
public void TestSignAndVerify()
{
var data = new byte[] {6, 5, 4, 3, 2};
var keyPair = GetKeyPair();
var signature = SignData(data, keyPair.Private);
var valid = VerifySignature(signature, keyPair.Public);
Assert.IsTrue(valid);
}
// get key pair from two local files
private AsymmetricCipherKeyPair GetKeyPair()
{
AsymmetricKeyParameter privateKey, publicKey;
var privateKeyString = File.ReadAllText("C:\privatekey.pem");
using (var textReader = new StringReader(privateKeyString))
{
// Only a private key
var pseudoKeyPair = (AsymmetricCipherKeyPair)new PemReader(textReader).ReadObject();
privateKey = pseudoKeyPair.Private;
}
var certificateString = File.ReadAllText("C:\publicCert.pem");
using (var textReader = new StringReader(certificateString))
{
// Only a private key
Org.BouncyCastle.X509.X509Certificate bcCertificate = (X509Certificate)new PemReader(textReader).ReadObject();
publicKey = bcCertificate.GetPublicKey();
}
return new AsymmetricCipherKeyPair(publicKey, privateKey);
}
public byte[] SignData(byte[] data, AsymmetricKeyParameter privateKey)
{
var signer = SignerUtilities.GetSigner("SHA-256withECDSA");
signer.Init(true, privateKey);
signer.BlockUpdate(data, 0, data.Length);
return signer.GenerateSignature();
}
public bool VerifySignature(byte[] signature, AsymmetricKeyParameter publicKey)
{
var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
verifier.Init(false, publicKey);
verifier.BlockUpdate(signature, 0, signature.Length);
return verifier.VerifySignature(signature);
}
verifier.blockUpdate
,然后使用VerifySignature
中的签名字节。
无法自行验证签名。该算法在进入验证状态之前需要数据作为输入来计算哈希值。当然,在没有数据的情况下验证签名只能提供有限的价值:您只能证明使用了私钥。
使用 ECDSA 也没有真正的选项来验证没有哈希的签名的任何部分
RSA / PKCS#1 理论上可以让您验证签名的内容至少是由私钥创建的,即使内容是未知的。由于您可以检索完整的哈希值,因此无需对每个可能的输入值都经过签名验证阶段就可以猜测数据输入。大多数 API 当然不适合这种签名的使用,因为这是特定于某些方案的。