从azure网站访问证书时,密钥集不存在

时间:2016-11-19 18:04:00

标签: c# azure azure-web-sites x509certificate

当我使用以下代码从本地或天蓝网站访问文件系统中的证书时,我没有遇到任何问题:

X509Certificate2 certificate = new X509Certificate2(keyFilePath, "mysecret", X509KeyStorageFlags.MachineKeySet |
                   X509KeyStorageFlags.PersistKeySet |
                   X509KeyStorageFlags.Exportable);

但是,当我按照https://azure.microsoft.com/en-us/blog/using-certificates-in-azure-websites-applications/中的说明使用azure证书存储区时,一切都适用于前3到9个请求,所有后续调用都在以下行中失败

var rsa = certificate.PrivateKey as RSACryptoServiceProvider;

错误“System.Security.Cryptography.CryptographicException:Keyset不存在”,直到网站重新启动,然后将为另一个至少3个请求工作。

我很困惑为什么它适用于至少3个和最多9个请求,然后总是因错误而失败。我很感激任何建议。

3 个答案:

答案 0 :(得分:1)

从PFX加载时,您指定的是PersistKeySet,通常只应在计划将证书持久保存到证书库时设置。虽然代码的某些方面可能通过将RSACryptoServiceProvider对象的PersistKeyInCsp标记为false来尝试变得聪明并清理它自己的私钥,但是有可能。

我指出这一点的原因是“Keyset不存在”错误几乎总是意味着“证书存储被告知私钥存在,但有人已经删除它而没有通知证书存储”。最可能的罪魁祸首是某处设置PersistKeyInCspfalse(这意味着“删除Dispose / Finalize上的密钥文件”)。

如果您将PersistKeyInCsp设置为false但未手动处理该对象,则会因终结器而得到延迟清理,这可以解释为什么它是3-9次成功而不是确定性数字。

(我也不得不指出你应该使用cert.GetRSAPrivateKey()而不是cert.PrivateKey,因为a)它是类型安全的,b)它是来电者拥有的生命(你应该处置)它)而不是共享/模糊的生命。它使事情变得更加可预测,尽管它几乎从不返回RSACryptoServiceProvider,所以你不应该尝试强制转换它。)

答案 1 :(得分:0)

看起来像显式处置(使用using关键字)可以解决此问题:

using (X509Certificate2 certificate = new X509Certificate2(keyFilePath, "mysecret", X509KeyStorageFlags.MachineKeySet |
                   X509KeyStorageFlags.PersistKeySet |
                   X509KeyStorageFlags.Exportable))
{
// ... use cert
}

答案 2 :(得分:0)

一个单身人士为我工作:

  class static CertificateSingleton
  {
       private static X509Certificate2 certificate;

       public Get()
       {
            if (certificate == null) 
            {
                 byte[] certificateBytes = .....;
                 string password = .....;

                 certificate = new X509Certificate2(certificateBytes, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
            }

            return certificate;
       }
  }

因此,每次我想使用它时,我都使用CertificatesSingleton.Get(),这保证了我每次都使用相同的实例。