1:添加正确的 NuGet 包
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
2: 生成 SM2 密钥对
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
public static (string publicKeyHex, string privateKeyHex) GenerateSm2KeyPair()
{
// 1. 获取国密标准曲线
var curve = GMNamedCurves.GetByName("sm2p256v1");
// 2. 创建曲线域参数
var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N);
// 3. 初始化密钥生成器
var generator = new ECKeyPairGenerator();
generator.Init(new ECKeyGenerationParameters(domainParams, new SecureRandom()));
// 4. 生成密钥对
AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair();
// 5. 提取并编码公私钥(通常转为十六进制字符串存储)
var pubKey = (ECPublicKeyParameters)keyPair.Public;
var privKey = (ECPrivateKeyParameters)keyPair.Private;
// 公钥使用非压缩格式 (04 | X | Y)
string publicKeyHex = Hex.ToHexString(pubKey.Q.GetEncoded(false));
string privateKeyHex = Hex.ToHexString(privKey.D.ToByteArrayUnsigned());
return (publicKeyHex, privateKeyHex);
}
3: 使用私钥进行 SM2 签名
public static byte[] SignData(byte[] data, string privateKeyHex)
{
// 1. 获取曲线和域参数
var curve = GMNamedCurves.GetByName("sm2p256v1");
var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N);
// 2. 从十六进制字符串还原私钥
var privateKey = new ECPrivateKeyParameters(
new BigInteger(1, Hex.Decode(privateKeyHex)), domainParams);
// 3. 创建签名器实例,算法指定为 SM3withSM2
var signer = SignerUtilities.GetSigner("SM3withSM2");
// 4. 初始化签名器(true表示签名模式),并绑定随机数
signer.Init(true, new ParametersWithRandom(privateKey, new SecureRandom()));
// 5. 传入待签名数据并生成签名
signer.BlockUpdate(data, 0, data.Length);
byte[] signature = signer.GenerateSignature();
return signature;
}
4: 使用公钥验证签名
public static bool VerifySignature(byte[] data, byte[] signature, string publicKeyHex)
{
// 1. 获取曲线和域参数
var curve = GMNamedCurves.GetByName("sm2p256v1");
var domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N);
// 2. 从十六进制字符串还原公钥
var q = curve.Curve.DecodePoint(Hex.Decode(publicKeyHex));
var publicKey = new ECPublicKeyParameters(q, domainParams);
// 3. 创建签名器实例
var signer = SignerUtilities.GetSigner("SM3withSM2");
// 4. 初始化签名器为验证模式
signer.Init(false, publicKey);
// 5. 传入数据和签名进行验证
signer.BlockUpdate(data, 0, data.Length);
return signer.VerifySignature(signature);
}
说明:
- Z值计算:SM2 验签时,需要计算一个 Z 值,它包含了用户身份、曲线参数等信息。Bouncy Castle 的 SM2Signer 在调用 Init 后会自动处理这部分,不要手写 Z 值计算逻辑,否则很容易因为 ID 长度、编码格式不一致导致验签失败。
- 公钥格式:以上示例中的公钥字符串采用了非压缩格式(以 04 开头,后跟 X 和 Y 坐标),这是最常见的一种格式。如果你的密钥是其他格式(如压缩格式),需要使用对应的解码方式。
- 版本影响:根据 Bouncy Castle 的历史版本迭代,较早的 1.8.x 版本可能不支持 SM2 相关的命名空间或 API。使用 2.0.0+ 版本是避免这类问题的有效方式