如果您查看下面的代码,除了创建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)
答案 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 ++中的密钥文件,我们可以在其中一个文件的顶部找到密钥的名称。
删除违规文件允许开发人员再次成功运行应用程序 - 下次调用构造函数时文件已重新生成。
我仍然不确定发生了什么,但看起来原始密钥文件已经以某种方式损坏了。