为什么SignedCms.ComputeSignature()方法抛出“Provider的公钥无效”异常?

时间:2011-03-11 18:24:48

标签: c# .net

这个似乎是一个简单的问题,但它确实让我摸不着头脑。麻烦的是我们的代码在.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;
}

1 个答案:

答案 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;
}