3

如果您查看下面的代码,它除了创建一个新的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)
4

2 回答 2

5

据我所知,这段代码没有创建任何持久对象

如果密钥容器不存在,以下代码将创建它:

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在创建密钥时设置会阻止 if 被持久化。

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

最后,您似乎建议您不想保留密钥容器,为什么要使用机器存储?

于 2012-07-04T13:40:46.620 回答
1

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

在我们的场景中,我们使用 RSA 来加密密码。有问题的代码已经在应用程序中工作了多年。突然,RSACryptoServiceProvider构造函数开始在一位开发人员的机器上遇到错误。该消息是模棱两可且令人恐惧的“密钥在指定状态下无效。

从上面乔的回答中,我们发现将密钥存储在如下位置:

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

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

删除有问题的文件允许开发人员再次成功运行应用程序- 下次我们调用构造函数时重新生成文件。

我仍然不确定发生了什么,但似乎原始密钥文件已损坏。

于 2015-08-14T18:30:33.440 回答