这个似乎是一个简单的问题,但它确实让我摸不着头脑。麻烦的是我们的代码在.NET framework 3.5上运行时运行良好,但现在我们已经切换到.NET 4.0,我们遇到了这个错误。这是相关的代码:
SignedCms signed = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(
SubjectIdentifierType.IssuerAndSerialNumber,
signingCertificate);
signed.ComputeSignature(signer);
再一次,在.NET 3.5上,这很好用。但是现在我们的项目针对的是.NET 4.0,它在使用完全相同的证书时会抛出CryptographicException。
[CryptographicException: Provider's public key is invalid.]
at System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer, Boolean silent)
at System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer, Boolean silent)
at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
at System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer)
...
任何可能导致此问题的想法?
更新
在深入挖掘之后,我发现问题只发生在我自己反序列化签名证书时。如果我从机器商店加载它,一切正常。所以,很明显,我的反序列化代码有问题。此代码未更改。唯一的区别是它现在以.NET 4为目标。这是代码:
var result = new X509Certificate2(certificate);
byte[] decryptedKey;
// Long ugly code to decrypt private key omitted...
var rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore
});
try
{
rsa.ImportCspBlob(decryptedKey);
result.PrivateKey = rsa;
return result;
}
catch
{
rsa.Dispose();
throw;
}
答案 0 :(得分:7)
事实证明,问题是RSACryptoServiceProvider中的一个微妙的,突破性的变化。在.NET 4.0之前,此类的新实例将具有随机生成的密钥容器名称(格式为" CLR {GUID}")。在.NET 4.0中,它将密钥容器名称保留为null。 (或者它可能将它设置为null,因为这是我通过CspParameters传递的内容。)
在任何情况下,这意味着每个后续RSACryptoService提供程序都会覆盖任何先前提供程序的密钥对。当我的代码最终绕过签名时,与证书关联的密钥对已被不同证书的密钥对覆盖。 (因此"公钥无效"错误。)
解决方案是自己简单地生成一个随机名称。对我的反序列化代码的更改如下:
var rsa = new RSACryptoServiceProvider(new CspParameters
{
Flags = CspProviderFlags.UseMachineKeyStore,
KeyContainerName = String.Format("MyPrefix {{{0}}}", Guid.NewGuid())
});
try {
rsa.PersistKeyInCsp = false; // ensure key is deleted when provider is disposed
rsa.ImportCspBlob(decryptedKey);
result.PrivateKey = rsa;
return result;
}