X509Certificate 构造函数异常

Posted

技术标签:

【中文标题】X509Certificate 构造函数异常【英文标题】:X509Certificate Constructor Exception 【发布时间】:2012-04-14 15:43:46 【问题描述】:
//cert is an EF Entity and 
//    cert.CertificatePKCS12 is a byte[] with the certificate.

var certificate = new X509Certificate(cert.CertificatePKCS12, "SomePassword");

从我们的数据库加载证书时,在我们的暂存服务器(Windows 2008 R2/IIS7.5)上,我们会遇到以下异常:

System.Security.Cryptography.CryptographicException: An internal error occurred.

   at System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 hr)
   at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
   at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)

注意:此问题不会在本地发生(Windows 7/Casini)。

非常感谢任何见解。

【问题讨论】:

查看***.com/questions/1345262/…和***.com/questions/6097380/… 我想问题的根源在于byte[] data 其中cert.CertificatePKCS12。在没有数据的情况下,只能猜测“发生内部错误”异常的原因。所以我的建议是您创建可在您的环境中使用的测试证书来重现问题,将其保存在文件中并提供链接和密码(如“SomePassword”)以解码证书。在检查数据之后,人们将有更多机会找到原因并提出解决问题的方法。 感谢@Oleg 的回复-如果字节数组不好,在Win7 和Win2k8 上不会出错吗?当字节数组写入文件时,它会正确导入。 @lukiffer:我的意思不是错误,而是结合了证书、密钥等的一些属性。所以必须分析问题。为了能够重现结果或对其进行分析,需要将 PFX 文件用作字节数组cert.CertificatePKCS12 @oleg - 哪些属性会导致它在一个操作系统而不是另一个操作系统上失败?出于明显的安全原因,我们无法自行发布证书。 【参考方案1】:

很可能,当您从 Visual Studio/Cassini 运行时,它正在访问您的 用户 证书存储,即使您是从字节加载它。你能试试这个,看看它是否能解决你的问题:

var certificate = new X509Certificate(
    cert.CertificatePKCS12, "SomePassword", X509KeyStorageFlags.MachineKeySet);

这将导致 IIS(以 ASP.NET 用户身份运行,可能无权访问用户存储)使用机器存储。

This page 更详细地解释了构造函数,this page 解释了X509KeyStorageFlags 枚举。

编辑: 基于来自cyphr 的second link,看起来像这样组合一些FlagsAttribute 枚举值可能是个好主意(如果以前的解决方案不起作用):

var certificate = new X509Certificate(
    cert.CertificatePKCS12, "SomePassword",
    X509KeyStorageFlags.MachineKeySet
    | X509KeyStorageFlags.PersistKeySet
    | X509KeyStorageFlags.Exportable);

此外,如果您有访问权限,您可能想尝试更改您的应用程序池设置以使用 LocalService(然后重新启动 AppPool)。如果这是问题,这可能会将您的权限提升到适当的级别。

最后你可以使用File.WriteAllBytesCertificatePKCS12的内容写到一个pfx文件中,看看是否可以使用MMC下的证书控制台手动导入(导入成功后可以删除;这只是为了测试)。可能是您的数据被篡改,或者密码不正确。

【讨论】:

感谢您的回复,尽管在仅添加 X509KeyStorageFlags.MachineKeySet 标志以及添加所有三个 MachineKeySetPersistKeySetExportable 标志后仍然抛出相同的异常。 你能把你的 AppPool 的身份改成 LocalService 吗? 另外,使用File.WriteAllBytesCertificatePKCS12的内容写到一个pfx文件中,看看能不能用MMC下的证书控制台手动导入(导入成功后可以删除;这个是只是为了测试)。可能是您的数据被篡改,或者密码不正确。 好的。我更新了答案以包括那些以防万一。我正在努力赚取+50。 :) 请注意,如果您计划从您的 ASP.NET 项目中调用 SignTool.exe,您仍然需要为 Application Pool 启用“LoadUserProfile”。否则SignTool.exe 将失败并出现An internal error occurred 错误...【参考方案2】:

要真正解决您的问题,而不仅仅是猜测,它会是什么,需要能够重现您的问题。如果您无法提供具有相同问题的测试 PFX 文件,您必须自己检查问题。第一个重要问题是:PKCS12 的私钥部分或证书本身的公共部分中出现“内部错误”异常的根源是什么?

所以我建议您尝试使用相同的证书重复相同的实验,导出没有私钥(如 .CER 文件):

var certificate = new X509Certificate(cert.CertificateCER);

var certificate = new X509Certificate.CreateFromCertFile("My.cer");

这有助于验证您的问题的根源是私钥还是证书的某些属性。

如果您对 CER 文件有疑问,您可以安全地发布该文件的链接,因为它只有公共信息。或者,您至少可以执行

CertUtil.exe -dump -v "My.cer"

CertUtil.exe -dump -v -privatekey -p SomePassword "My.pfx"

(您也可以使用其他一些选项)并发布输出的某些部分(例如,没有 PRIVATEKEYBLOB 本身的私钥属性)。

【讨论】:

【参考方案3】:

原来在 IIS 应用程序池配置(应用程序池 > 高级设置)中有一个设置来加载应用程序池身份用户的用户配置文件。设置为 false 时,无法访问密钥容器。

所以只需将Load User Profile 选项设置为True

【讨论】:

请注意,这是针对 IIS7 的;在 IIS6 中,我认为没有加载用户配置文件的选项。 我正在尝试在 Azure 上执行此操作,但我不知道是否可以更改“加载用户配置文件”选项。任何建议...我对证书世界还是很陌生... @DanB for Azure 添加应用程序设置:WEBSITE_LOAD_USER_PROFILE=1 将加载用户配置文件设置为 True 仅适用于用户帐户,但不适用于 ApplicationPoolIdentity 和 NetworkService。【参考方案4】:

使用此代码:

certificate = new X509Certificate2(System.IO.File.ReadAllBytes(p12File)
                                   , p12FilePassword
                                   , X509KeyStorageFlags.MachineKeySet |
                                     X509KeyStorageFlags.PersistKeySet | 
                                     X509KeyStorageFlags.Exportable);

【讨论】:

只需将最后一位与 3 or 语句相加,我的东西就可以工作了。仍然相关的好代码。 在尝试将证书加载为 IIS 上的嵌入式资源时为我工作。 这是我在使用 spire.pdf 时遇到的问题【参考方案5】:

您需要将 .cer 证书导入本地计算机密钥库。无需导入您的 .p12 证书 - 而是使用 Apple 颁发给您帐户的第二个证书。我认为它必须是一对有效的证书(一个在文件系统中,第二个在密钥库中)。当然,您必须在 dll 中设置所有 3 个标志。

【讨论】:

【参考方案6】:

更改加载用户配置文件的另一种方法是让应用程序池使用网络服务身份。

另见What exactly happens when I set LoadUserProfile of IIS pool?

【讨论】:

【参考方案7】:

我在 Windows 2012 Server R2 上遇到问题,我的应用程序无法为磁盘上的 PFX 加载证书。以管理员身份运行我的应用程序可以正常工作,并且异常表示访问被拒绝,因此它必须是权限问题。我尝试了上面的一些建议,但我仍然遇到了问题。我发现将以下标志指定为 cert 构造函数的第三个参数对我有用:

 X509KeyStorageFlags.UserKeySet | 
 X509KeyStorageFlags.PersistKeySet | 
 X509KeyStorageFlags.Exportable

【讨论】:

【参考方案8】:

在运行 IIS 10 的应用程序上,我使用以下代码通过应用程序池的 LocalSystem 标识修复了访问被拒绝错误:

new X509Certificate2(certificateBinaryData, "password"
                               , X509KeyStorageFlags.MachineKeySet |
                                 X509KeyStorageFlags.PersistKeySet);

启用加载用户配置文件对我不起作用,虽然有很多建议这样做,但他们并没有表明将“加载用户配置文件”设置为 True 仅适用于用户帐户而不是:

    ApplicationPoolIdentity 网络服务

【讨论】:

【参考方案9】:

下面的代码会对你有所帮助,你可以使用充气城堡库生成算法:

private static ECDsa GetEllipticCurveAlgorithm(string privateKey)

    var keyParams = (ECPrivateKeyParameters)PrivateKeyFactory
        .CreateKey(Convert.FromBase64String(privateKey));

    var normalizedECPoint = keyParams.Parameters.G.Multiply(keyParams.D).Normalize();

    return ECDsa.Create(new ECParameters
    
        Curve = ECCurve.CreateFromValue(keyParams.PublicKeyParamSet.Id),
        D = keyParams.D.ToByteArrayUnsigned(),
        Q =
    
        X = normalizedECPoint.XCoord.GetEncoded(),
        Y = normalizedECPoint.YCoord.GetEncoded()
    
    );

并通过以下方式生成令牌:

var signatureAlgorithm = GetEllipticCurveAlgorithm(privateKey);

        ECDsaSecurityKey eCDsaSecurityKey = new ECDsaSecurityKey(signatureAlgorithm)
        
            KeyId = settings.Apple.KeyId
        ;

        var handler = new JwtSecurityTokenHandler();   
        var token = handler.CreateJwtSecurityToken(
            issuer: iss,
            audience: AUD,
            subject: new ClaimsIdentity(new List<Claim>  new Claim("sub", sub) ),
            expires: DateTime.UtcNow.AddMinutes(5), 
            issuedAt: DateTime.UtcNow,
            notBefore: DateTime.UtcNow,
            signingCredentials: new SigningCredentials(eCDsaSecurityKey, SecurityAlgorithms.EcdsaSha256));

【讨论】:

以上是关于X509Certificate 构造函数异常的主要内容,如果未能解决你的问题,请参考以下文章

如何从 PKCS#12 字节数组构造 X509Certificate2 抛出 CryptographicException(“系统找不到指定的文件。”)?

带有 cer 文件的 X509Certificate2 ctor

如何从客户端请求中获取 X509Certificate

X509Chain.Build(...) System.InvalidCastException 从 X509Certificate 到 X509Certificate2

X509Certificate2 使 IIS 崩溃

X509Certificate - 密钥集不存在