对于某些键名,RSACryptoServiceProvider(RSACryptoServiceProvider) 构造函数在 .NET 3.5 中崩溃

Kru*_*lur 3 .net c# 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 ) )
{
}
Run Code Online (Sandbox Code Playgroud)

这些是使用的 CspParameters:

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

this.oCspParameters.KeyContainerName = oProfile.KeyName;
Run Code Online (Sandbox Code Playgroud)

这就是关键名称:

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

最后是例外:

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)
Run Code Online (Sandbox Code Playgroud)

Joe*_*Joe 5

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

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

using ( RSACryptoServiceProvider rsa = new RSACryptoServiceProvider( this.oCspParameters ) ) 
{ 
} 
Run Code Online (Sandbox Code Playgroud)

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

cp.Flags = CspProviderFlags.NoPrompt | CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore;
Run Code Online (Sandbox Code Playgroud)

“重用”是否意味着它正在某处缓存这些东西,而在这样做时它崩溃了,现在被一个损坏的缓存版本卡住了?

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

也许那里有一个您无权访问的密钥?

密钥容器的文件名使用生成的唯一标识(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;
}
Run Code Online (Sandbox Code Playgroud)

我还没有尝试过,但是PersistKeyInCsp=false创建密钥时的设置可能会阻止 if 被持久化。

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

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