//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)。
非常感谢任何见解。
答案 0 :(得分:116)
原来,IIS应用程序池配置(应用程序池>高级设置)中有一个设置,用于加载应用程序池标识用户的用户配置文件。设置为false时,无法访问密钥容器。
所以只需将Load User Profile
选项设为True
答案 1 :(得分:54)
很可能,当您从Visual Studio / Cassini运行时,它正在访问您的用户证书存储区,即使您是从字节加载它。你可以试一试,看看它是否解决了你的问题:
var certificate = new X509Certificate(
cert.CertificatePKCS12, "SomePassword", X509KeyStorageFlags.MachineKeySet);
这将导致IIS(作为ASP.NET用户运行,可能无法访问用户存储)使用计算机存储。
This page更详细地解释了构造函数,this page解释了X509KeyStorageFlags
枚举。
修改强>
根据{{3}}中的second link,看起来好像是一个好主意(如果以前的解决方案不起作用),要合并一些FlagsAttribute
枚举值,如下所示:
var certificate = new X509Certificate(
cert.CertificatePKCS12, "SomePassword",
X509KeyStorageFlags.MachineKeySet
| X509KeyStorageFlags.PersistKeySet
| X509KeyStorageFlags.Exportable);
此外,如果您具有访问权限,则可能需要尝试更改“应用程序池”设置以使用LocalService(然后重新启动AppPool)。如果这是问题,这可能会将您的权限提升到适当的级别。
最后,您可以使用cyphr将CertificatePKCS12
内容写出到pfx文件,看看是否可以使用MMC下的证书控制台手动导入它(您可以在导入成功后删除;只是为了测试)。可能是您的数据被窃取,或者密码不正确。
答案 2 :(得分:29)
使用此代码:
certificate = new X509Certificate2(System.IO.File.ReadAllBytes(p12File)
, p12FilePassword
, X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
答案 3 :(得分:6)
我在Windows 2012 Server R2上遇到了问题,我的应用无法为磁盘上的PFX加载证书。事情可以很好地运行我的应用程序作为管理员,并且异常说Access拒绝所以它必须是权限问题。我尝试了一些上述建议,但我仍然遇到了问题。我发现将以下标志指定为cert构造函数的第三个参数对我来说很有用:
X509KeyStorageFlags.UserKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable
答案 4 :(得分:1)
为了能够真正解决您的问题,而不仅仅是猜测,它可以是什么,需要能够重现您的问题。如果您无法提供具有相同问题的测试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本身)。
答案 5 :(得分:1)
更改加载用户配置文件的替代方法是使应用程序池使用网络服务标识。
另见What exactly happens when I set LoadUserProfile of IIS pool?
答案 6 :(得分:0)
您需要将.cer证书导入本地计算机密钥库。无需导入.p12证书 - 而是使用Apple发给您帐户的第二个证书。我认为它必须是一对有效的证书(一个在文件系统中,第二个在密钥库中)。你当然必须在dll中设置所有3个标志。
答案 7 :(得分:0)
在运行IIS 10的应用程序上,通过使用以下代码对应用程序池使用LocalSystem身份,我设法解决了拒绝访问错误:
new X509Certificate2(certificateBinaryData, "password"
, X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
启用加载用户配置文件对我不起作用,尽管有很多建议可以这样做,但它们并不表示将“加载用户配置文件”设置为True仅适用于用户帐户,而不适用:
- ApplicationPoolIdentity
- NetworkService
答案 8 :(得分:0)
以下代码将为您提供帮助,您可以使用充气城堡库生成算法:
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));