C#仅用RSA公钥模与指数通过CertificateRequest生成CSR求助
无本地私钥(私钥存储在HSM)场景下生成CSR的实现方案
报错根因
- 通过
RSA.Create()导入仅包含模数、指数的公钥参数得到的RSA实例,仅存储公钥材料,Windows CNG层判定该实例无关联签名私钥,调用依赖私钥的签名方法时直接抛出Key does not exist异常。 - 无参
CreateSigningRequest()方法默认会从CertificateRequest绑定的密钥对象中提取私钥完成签名,纯公钥对象无对应私钥,且未传入自定义签名实现时,就会抛出要求使用带X509SignatureGenerator参数重载的异常。
实现思路
私钥全程存储在HSM不需要导出到本地,只需要自定义继承X509SignatureGenerator的签名实现类,将待签名的摘要数据转发给HSM完成签名运算,再将HSM返回的签名结果回传给证书请求对象即可,所有私钥运算全部在HSM内部完成。
具体实现代码
1. 构造纯公钥RSA对象
public static RSA BuildPublicKeyOnlyRsa(byte[] modulus, byte[] exponent) { RSA rsa = RSA.Create(); RSAParameters pubKeyParams = new RSAParameters { Modulus = modulus, Exponent = exponent }; rsa.ImportParameters(pubKeyParams); return rsa; }
调用该方法得到RSA实例后,直接提取PublicKey对象用于构造证书请求:
using var rsaPubInstance = BuildPublicKeyOnlyRsa(modulus, exponent); PublicKey certPublicKey = rsaPubInstance.GetPublicKey();
2. 自定义对接HSM的签名生成器
public class HsmRsaSignatureGenerator : X509SignatureGenerator { private readonly PublicKey _publicKey; private readonly HashAlgorithmName _hashAlgorithm; private readonly RSASignaturePadding _paddingMode; // 可根据对接的HSM SDK,新增HSM会话、私钥句柄等字段 // private readonly IntPtr _hsmKeyHandle; public HsmRsaSignatureGenerator(PublicKey publicKey, HashAlgorithmName hashAlgorithm, RSASignaturePadding paddingMode) { _publicKey = publicKey; _hashAlgorithm = hashAlgorithm; _paddingMode = paddingMode; // 初始化HSM连接、获取对应私钥句柄的逻辑可写在这里 } protected override PublicKey BuildPublicKey() => _publicKey; public override byte[] GetSignatureAlgorithmIdentifier(HashAlgorithmName hashAlgorithm) { // 返回对应签名算法的DER编码标识,以下为常用组合示例,可根据实际使用的算法补充 if (_paddingMode == RSASignaturePadding.Pkcs1) { if (hashAlgorithm == HashAlgorithmName.SHA256) // sha256WithRSAEncryption OID: 1.2.840.113549.1.1.11 return new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00 }; if (hashAlgorithm == HashAlgorithmName.SHA384) // sha384WithRSAEncryption OID: 1.2.840.113549.1.1.12 return new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, 0x05, 0x00 }; if (hashAlgorithm == HashAlgorithmName.SHA512) // sha512WithRSAEncryption OID: 1.2.840.113549.1.1.13 return new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, 0x05, 0x00 }; } // RSA-PSS等其他padding模式的算法标识可根据实际场景补充 throw new NotSupportedException("当前指定的签名算法组合不被支持"); } public override byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm) { // 替换为实际HSM签名调用逻辑: // 1. 将待签名数据data、指定的哈希算法、padding模式、HSM内存储的私钥句柄传入HSM SDK // 2. 直接返回HSM输出的签名二进制结果即可,本地不需要持有或运算私钥 // 伪代码示例: // return HsmSdk.RsaSign(_hsmKeyHandle, data, hashAlgorithm, _paddingMode); throw new NotImplementedException("请替换为实际的HSM签名接口调用逻辑"); } }
3. 生成CSR
var subjectName = new X500DistinguishedName("CN=你的证书通用名称"); var signatureGenerator = new HsmRsaSignatureGenerator(certPublicKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); var certRequest = new CertificateRequest(subjectName, certPublicKey, HashAlgorithmName.SHA256); // 必须传入自定义签名生成器调用重载方法,不能使用无参的CreateSigningRequest byte[] csrDerBytes = certRequest.CreateSigningRequest(signatureGenerator); // 如需PEM格式CSR,可做如下转换: string pemCsr = PemEncoding.Write("CERTIFICATE REQUEST", csrDerBytes);
注意:代码中配置的哈希算法、签名padding模式必须和HSM侧实际使用的签名参数完全一致,否则CA侧会验签失败,拒绝签发证书。
内容的提问来源于stack exchange,提问作者user1912383
相关产品推荐
相关产品推荐

