在C#(.NET 4.7.2)中无CRT参数生成RSA签名的方案咨询
问题
我正在开发一款基于.NET Framework 4.7.2(Visual Studio 2019,无法升级)的C#应用,需要使用RSA为指定字节序列生成签名。目前仅拥有RSA密钥的Modulus、公钥Exponent和PrivateExponent(D),缺少CRT优化相关参数(P、Q、DP、DQ、InverseQ)。尝试使用System.Security.Cryptography的SignData方法及BouncyCastle库均因缺少CRT参数报错。
相关代码片段:
RSAParameters rsaKeyInfo = new RSAParameters(); for (i = 0; i<Constants.KEY_ID_TABLE_LENGTH_3072; i++) { if((SigKeyTbl_3072.keyIdTbl_3072[i].KeyId == keyId) && (SigKeyTbl_3072.keyIdTbl_3072[i].KeyType == keyType)) { Console.WriteLine("KeyId found."); rsaKeyInfo.Modulus = SigKeyTbl_3072.keyIdTbl_3072[i].PublicModulus.ToArray(); rsaKeyInfo.Exponent = SigKeyTbl_3072.keyIdTbl_3072[i].PublicExponent; rsaKeyInfo.D = SigKeyTbl_3072.keyIdTbl_3072[i].PrivatExponent.ToArray(); return (ErrorCode: 0, rsaKeyInfo); }
签名代码:
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(rsaKey); FscOut = rsa.SignData(fscIn, HashAlgorithmName.SHA512, RSASignaturePadding.Pss); }
报错信息:
Message: System.ArgumentNullException : Value cannot be null. (Parameter 'p') Stack Trace: RsaPrivateCrtKeyParameters.ValidateValue(BigInteger x, String name, String desc) RsaPrivateCrtKeyParameters.ctor(BigInteger modulus, BigInteger publicExponent, BigInteger privateExponent, BigInteger p, BigInteger q, BigInteger dP, BigInteger dQ, BigInteger qInv)
现咨询:是否可通过System.Security.Cryptography或BouncyCastle在无CRT参数的情况下生成RSA签名?是否有支持该场景的特定配置或类?若无法实现,恢复缺失参数的难度如何?
回答
1. 使用System.Security.Cryptography实现签名
你当前用的RSACryptoServiceProvider是.NET Framework中的旧版RSA实现,它强制要求CRT参数来优化签名性能。但在.NET Framework 4.7.2中,可改用RSA基类的默认实现(Windows 10及以上系统通常返回RSACng实例),它支持仅通过Modulus、Exponent和D参数导入私钥并完成签名:
修改签名代码如下:
using (RSA rsa = RSA.Create()) { rsa.ImportParameters(rsaKey); FscOut = rsa.SignData(fscIn, HashAlgorithmName.SHA512, RSASignaturePadding.Pss); }
这种方式不需要CRT参数,RSACng会自动处理基于纯私钥(D)的签名逻辑,仅性能略低于CRT优化的签名,但3072位密钥的性能差异在多数场景下可接受。
2. 使用BouncyCastle实现签名
BouncyCastle报错是因为误用了需要CRT参数的RsaPrivateCrtKeyParameters类。你需要改用RsaPrivateKeyParameters类,它仅需Modulus、PublicExponent和PrivateExponent即可创建私钥:
示例代码(需引用BouncyCastle库):
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using System.Numerics; // 将字节数组转换为BigInteger BigInteger modulus = new BigInteger(1, rsaKey.Modulus); BigInteger publicExponent = new BigInteger(1, rsaKey.Exponent); BigInteger privateExponent = new BigInteger(1, rsaKey.D); // 创建纯私钥参数 RsaPrivateKeyParameters privateKey = new RsaPrivateKeyParameters(modulus, privateExponent, publicExponent); // 获取SHA512-PSS签名器 ISigner signer = SignerUtilities.GetSigner("SHA512withRSA/PSS"); signer.Init(true, privateKey); signer.BlockUpdate(fscIn, 0, fscIn.Length); // 生成签名 byte[] signature = signer.GenerateSignature();
这种方式完全不需要CRT参数,直接基于原始RSA私钥逻辑完成签名。
3. 恢复缺失CRT参数的难度
恢复CRT参数的核心是对RSA模数(Modulus)进行因数分解,即找到两个大质数P和Q,使得Modulus = P * Q。对于3072位的RSA模数,这在当前通用计算能力下几乎不可能完成——这也是RSA算法的安全性基础。除非模数存在设计缺陷(比如因子过小、重复因子等),否则没有可行方法在合理时间内分解出P和Q,也就无法恢复其他CRT参数。
内容的提问来源于stack exchange,提问作者user26408975

