某些密钥名称在.NET 3.5中崩溃的RSACryptoServiceProvider(RSACryptoServiceProvider)构造函数

时间:2012-07-04 13:21:23

标签: c# .net cryptography rsacryptoserviceprovider

如果您查看下面的代码,除了创建RSACryptoServiceProvider的新实例之外什么都不做。 密钥容器名称是从根据各种参数创建名称的属性初始化的。我在此演示代码中添加了硬编码值。

代码在Windows 2008 R2服务器安装上运行,并且已经工作了几个月,并为密钥容器名称返回了一个常量值。

前几天代码停止了工作,我们正面临下面的例外。 使用已用了几个月的密钥容器名称不再有效。服务器已重新启动,IIS已重新启动 - 没有成功。只有在更改了密钥名称后,它才会重新开始工作。

有人可以解释为什么会发生这种情况以及如何解决这个问题?据我所知,此代码不会创建任何持久对象。重启后为什么还会失败?从MSDN(http://msdn.microsoft.com/de-de/library/ca5htw4f.aspx)我读到构造函数“构造函数创建或重用使用KeyContainerName字段指定的密钥容器”。 “重用”是否意味着,它正在缓存某些地方的东西,而这样做它崩溃了,现在卡在一个腐败的缓存版本? 另请注意,相同的密钥名称目前仍在许多其他计算机上使用 - 在任何地方都没有问题。

这是崩溃的行:

using ( RSACryptoServiceProvider rsa = new RSACryptoServiceProvider( this.oCspParameters ) )
{
}

这些是使用的CspParameters:

private readonly CspParameters oCspParameters = new CspParameters
{
  Flags = CspProviderFlags.UseMachineKeyStore,
};

this.oCspParameters.KeyContainerName = oProfile.KeyName;

这就是关键名称:

public string KeyName
{
    get
    {
        return string.Format( "API-{0}-v{1}", "TestClient", "1.0.0.0" );
    }
}

最后例外:

CryptographicException: An internal error occurred.
Service Operation: ISessionService.Identify #f173250b-d7ac-45d5-98ed-7fffcf37d95a
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)
at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)

2 个答案:

答案 0 :(得分:4)

  

据我所知,此代码不会创建任何持久对象

以下代码创建密钥容器(如果它尚不存在):

using ( RSACryptoServiceProvider rsa = new RSACryptoServiceProvider( this.oCspParameters ) ) 
{ 
} 

如果要强制使用现有密钥,则应指定:

cp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
  

“重用”是否意味着,它正在缓存某些地方的东西,而这样做它崩溃了,现在陷入了腐败的缓存版本?

它将重用存储在Microsoft\Crypto\RSA\MachineKeys\文件夹Environment.SpecialFolder.CommonApplicationData子文件夹中的密钥(如果存在且您有权访问它)。否则它会尝试创建它。

也许有一个密钥,你没有权限访问?

密钥容器的文件名使用生成的唯一ID(CspKeyContainerInfo.UniqueKeyContainerName),但您可以使用文本编辑器检查文件内容,密钥容器名称位于文件的前几个字符中。

找到有问题的文件后,您可以检查其权限,也可以将其删除,以便重新创建。

来自评论:

  

如何强迫它不要这样做或如何删除容器?

使用以下代码完成后,您可以删除现有密钥容器(前提是您具有必要的权限):

CspParameters cp = new CspParameters();
cp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey | 
                   CspProviderFlags.UseMachineKeyStore;
cp.KeyContainerName = containerName;

using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp))
{
    rsa.PersistKeyInCsp = false;
}

我还没有尝试过,但是在创建密钥时设置PersistKeyInCsp=false可能会阻止它被保留。

但是,以上都没有解释为什么您无法再访问以前成功使用的密钥。最明显的原因是权限 - 如果您尝试访问您无权访问的密钥容器,我知道您获得CryptographicException,但我不知道您是否希望更明确错误消息比“发生内部错误”。我要做的第一件事就是检查密钥容器文件上的ACL。也许您有两个运行在不同身份下的应用程序实例创建密钥容器 - 身份1创建的密钥容器无法通过身份2访问。

最后,因为您似乎建议您不要坚持使用密钥容器,为什么要使用机器商店?

答案 1 :(得分:1)

我最近发现了这个问题,同时帮助另一个开发人员在他的本地机器上解决了这个问题。

在我们的场景中,我们使用RSA加密密码。有问题的代码多年来一直在应用程序中工作。突然,RSACryptoServiceProvider构造函数开始在一台开发人员的机器上遇到错误。消息是模糊且可怕的" 密钥无法在指定状态下使用。"

根据Joe的上述回答,我们发现将密钥存储在以下位置:

C:\Users\<user name>\AppData\Roaming\Microsoft\Crypto\RSA\

通过打开notepad ++中的密钥文件,我们可以在其中一个文件的顶部找到密钥的名称。

删除违规文件允许开发人员再次成功运行应用程序 - 下次调用构造函数时文件已重新生成。

我仍然不确定发生了什么,但看起来原始密钥文件已经以某种方式损坏了。