我正在尝试使用BouncyCastle创建证书。Crypto dll,然后用于将SslStream作为Windows服务进程中的服务器进行身份验证,该进程在本地系统帐户下运行。
然而,当我得到SslStream.AuthenticateAsServer(证书)调用时,它抛出一个Win32异常,错误消息为"提供给包的凭据未被识别"。
这里有几个关于这个错误信息的问题,但它们似乎没有一个描述或解决我的特定问题。希望有人能提供一些帮助,我包含了我用来创建和安装证书的代码:
// First create a certificate using the BouncyCastle classes
BigInteger serialNumber = BigInteger.ProbablePrime(120, new Random());
AsymmetricCipherKeyPair keyPair = GenerateKeyPair();
X509V1CertificateGenerator generator = new X509V1CertificateGenerator();
generator.SetSerialNumber(serialNumber);
generator.SetIssuerDN(new X509Name("CN=My Issuer"));
generator.SetNotBefore(DateTime.Today);
generator.SetNotAfter(DateTime.Today.AddYears(100));
generator.SetSubjectDN(new X509Name("CN=My Issuer"));
generator.SetPublicKey(keyPair.Public);
generator.SetSignatureAlgorithm("SHA1WITHRSA");
Org.BouncyCastle.X509.X509Certificate cert = generator.Generate(
keyPair.Private, SecureRandom.GetInstance("SHA1PRNG"));
// Ok, now we have a BouncyCastle certificate, we need to convert it to the
// System.Security.Cryptography class, by writing it out to disk and reloading
X509Certificate2 dotNetCert;
string tempStorePassword = "Password01"; // In real life I'd use a random password
FileInfo tempStoreFile = new FileInfo(Path.GetTempFileName());
try
{
Pkcs12Store newStore = new Pkcs12Store();
X509CertificateEntry entry = new X509CertificateEntry(cert);
newStore.SetCertificateEntry(Environment.MachineName, entry);
newStore.SetKeyEntry(
Environment.MachineName,
new AsymmetricKeyEntry(keyPair.Private),
new [] { entry });
using (FileStream s = tempStoreFile.Create())
{
newStore.Save(s,
tempStorePassword.ToCharArray(),
new SecureRandom(new CryptoApiRandomGenerator()));
}
// Reload the certificate from disk
dotNetCert = new X509Certificate2(tempStoreFile.FullName, tempStorePassword);
}
finally
{
tempStoreFile.Delete();
}
// Now install it into the required certificate stores
X509Store targetStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
targetStore.Open(OpenFlags.ReadWrite);
targetStore.Add(dotNetCert);
targetStore.Close();
Ok,现在我已经创建并安装了证书。然后,我通过提供生成的证书的指纹来配置我的Windows服务以使用此证书。然后我像这样使用证书:
// First load the certificate
X509Certificate2 certificate = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (X509Certificate2 certInStore in store.Certificates)
{
if (certInStore.Thumbprint == "...value not shown...")
{
certificate = certInStore;
break;
}
}
SslStream sslStream = new SslStream(new NetworkStream(socket, false), false);
// Now this line throws a Win32Exception
// "The credentials supplied to the package were not recognized"
sslStream.AuthenticateAsServer(certificate);
有人知道这里可能有什么问题吗?
如果我安装一个用'makecert'创建的证书,我不会得到这个问题,但这不适用于生产证书。
我还尝试创建一个单独的x509v1 CA证书,然后创建x509v3证书用于服务器身份验证,但我得到了相同的错误,因此为了简单起见,我在示例代码中删除了此错误。
这个特定的错误消息让我想起了什么。我猜要么是您没有将私钥与证书一起存储,要么是Windows服务没有访问私钥。要检查这一点,请打开Certificates MMC管理单元:
- 运行mmc(例如从开始菜单)
- 文件菜单>添加/删除管理单元
- 在左侧窗格中选择"证书",然后点击添加
- 选择"计算机帐户"(对于本地机器),然后单击下一步,点击完成
导航到证书并在右窗格中双击。在出现的General选项卡上,您应该在底部看到一个小密钥图标,以及"您有一个与此证书对应的私钥"的文本。如果不是,那就是问题所在。私钥未保存。
如果私钥存在,请单击Ok以关闭此对话框,然后右键单击右窗格中的证书并在弹出菜单中选择:All Tasks> Manage private Keys。在该对话框中,确保服务运行的Windows帐户具有对私钥的读访问权限。如果没有,那就是问题所在。
编辑:哎呀,你写的服务作为本地系统运行,所以它一定是一个丢失的私钥,如果它是这两个问题之一。不管怎样,我会在我的答案中留下键访问检查,对于任何其他点击这个而不是作为本地系统运行的人。
有时当应用程序尝试访问证书时没有足够的权限来访问证书时发生问题,这个问题可以通过以管理员身份运行应用程序来解决。
我也有同样的问题,尝试了许多帖子中的所有方法,并在谷歌上搜索。但看起来我找到了解决办法。当我将Identify从ApplicationPoolIdentity更改为LocalSystem时,一切都开始正常工作。
可能会对某人有帮助。
For me works on Windows Server 2012 R2 (.net 4.6.1) -"所有任务>管理私钥"并设置访问每个人(设置为IS_IUSRS是不够的)
在网上找到了这个解决方案,但我找不到来源给予信用。
由于我在AuthenticateAsClient()(用于客户端验证)中遇到了"提供给包的凭据未被识别"问题,我想记录我是如何解决它的。这是一种具有相同最终目标的不同方法。因为它可能对AuthenticateAsServer()有用,我想为什么不呢。
这里我将BC证书转换为。net证书。添加一个额外的步骤,将其转换为。net X509Certificate2来存储它的PrivateKey属性。
Org.BouncyCastle.X509.X509Certificate bcCert;
X509Certificate dotNetCert = DotNetUtilities.ToX509Certificate(bcCert);
X509Certificate2 dotNetCert2 = new X509Certificate2(dotNetCert);
将BouncyCastle私钥添加到。net私钥时出现问题。X509证书可以很好地转换,但私钥不能。我使用提供的DotNetUtilities将BC私钥转换为RSACryptoServiceProvider。不幸的是,看起来转换还没有完成。因此,我创建了另一个RSACryptoServiceProvider,然后对其进行初始化。然后将私钥导入到我创建的私钥中。
// Apparently, using DotNetUtilities to convert the private key is a little iffy. Have to do some init up front.
RSACryptoServiceProvider tempRcsp = (RSACryptoServiceProvider)DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)ackp.Private);
RSACryptoServiceProvider rcsp = new RSACryptoServiceProvider(new CspParameters(1, "Microsoft Strong Cryptographic Provider",
new Guid().ToString(),
new CryptoKeySecurity(), null));
rcsp.ImportCspBlob(tempRcsp.ExportCspBlob(true));
dotNetCert2.PrivateKey = rcsp;
之后,我能够将X509Certificate2对象直接保存到密钥存储库中。我不需要实际的文件,所以省略了这一步。
以前,每次遇到这个问题,我都不得不从本地计算机证书存储中删除证书,然后重新导入它。然后一切看起来都很愉快。我看不出这可能是一个全局权限问题或无效的证书,如果只是重新导入它解决了这个问题。
我如何最终修复它是使用winhttpcertcfg工具从Windows资源工具包授予权限给使用证书的特定用户。
语法为:
"C:Program Files (x86)Windows Resource KitsToolswinhttpcertcfg" -i cert.p12 -c LOCAL_MACHINEMy -a UserWhoUsesTheCert -p passwordforp12
当从.NET应用程序调用WCF REST服务时,我有类似的问题,我需要附加客户端证书;我所要做的就是提供访问证书存储[mmc控制台]到"NETWORKSERVICE"的证书,当然我的IIS池是默认池,这表明它使用NETWORKSERVICE用户帐户。
我所犯的错误是,我从另一个商店复制证书到本地机器->使用密码保护证书的人员存储。应在所需存储区中显式导入证书。
如果从IIS运行,请确保应用程序池将'Load User Profile'设置为true。这是我唯一的解决办法。
我不记得这个错误了,但是您正在创建的证书不是用于SSL/TLS的有效证书,包括:
- v1(非v3)证书;
- 错过扩展;
- 无效CN;
- …
有几个RFC讨论了这个问题,包括TLS(1.2)上的RFC5246。
最后,制作您的自己的证书并不比使用makecert
制作的证书更适合(但最后一个可以生成用于SSL/TLS服务器证书的最小集)。
I 强烈建议您从知名的证书颁发机构(CA)购买用于产品的SSL/TLS证书。这将使您获得大多数浏览器和工具认可的工作证书。
此错误的另一个原因是您在密码更改的帐户下运行应用程序/服务器,破坏了其访问想要在证书存储中使用的证书的能力。
如果你使用像letuceencrypt这样的NuGet包,它会自动将LetsEncrypt存储在你的商店中,这可能就不那么明显了。
从存储中删除证书并重新导入。