使用 BouncyCastle 生成的证书作为服务器进行身份验证时出现“无法识别提供给包的凭据”错误
Posted
技术标签:
【中文标题】使用 BouncyCastle 生成的证书作为服务器进行身份验证时出现“无法识别提供给包的凭据”错误【英文标题】:"The credentials supplied to the package were not recognized" error when authenticating as server with certificate generated using BouncyCastle 【发布时间】:2011-12-20 13:27:07 【问题描述】:我正在尝试使用 BouncyCastle.Crypto dll 创建证书,然后使用该 dll 将 SslStream 作为 Windows 服务进程中的服务器进行身份验证,该进程在本地系统帐户下运行。
但是,当我调用 SslStream.AuthenticateAsServer(certificate) 时,它会引发 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();
好的,现在我已经创建并安装了证书。然后,我通过为其提供生成的证书的指纹来配置我的 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 证书用于服务器身份验证,但我得到了同样的错误,所以为了简单起见,我在示例代码中删除了它。
【问题讨论】:
【参考方案1】:那个特定的错误消息敲响了警钟。我猜要么您没有将私钥与证书一起存储,要么 Windows 服务无权访问私钥。要检查这一点,请打开证书 MMC 管理单元:
-
运行 mmc(例如,从“开始”菜单)
文件菜单 > 添加/删除管理单元
在左侧窗格中选择“证书”,然后单击“添加”
选择“计算机帐户”(对于 LocalMachine),然后单击下一步,
然后完成
导航到证书并在右窗格中双击。在出现的常规选项卡上,您应该会在底部看到一个小钥匙图标,以及“您有一个与此证书对应的私钥”的文本。如果没有,那就是问题所在。私钥未保存。
如果存在私钥,单击确定关闭此对话框,然后右键单击右侧窗格中的证书并在弹出菜单中选择:所有任务 > 管理私人钥匙。在该对话框中,确保运行该服务的 Windows 帐户具有对私钥的读取权限。如果没有,那就是问题所在。
编辑:糟糕,您写道该服务作为本地系统运行,因此如果是这两个问题之一,它一定是缺少私钥。无论如何,对于遇到此问题且未作为本地系统运行的任何其他人,我都会在我的答案中保留密钥访问检查。
【讨论】:
嗯,我不认为是这些问题之一——我肯定有一个与证书对应的私钥(我可以看到你描述的图标和文字)。我尝试进入“管理私钥”对话框并授予每个人对证书的完全控制权,但我仍然遇到同样的错误。 检查这个答案,看看它是否有帮助:***.com/questions/2322560/… 我今天刚刚遇到此错误(至少相同的错误消息),因为证书在 MMC 管理单元中显示了私钥。我删除了它并从管理单元重新导入它,然后它工作了。尝试使用 MMC 管理单元删除证书并从 pfx 文件格式导入,看看是否可以避免错误。 +1 表示“所有任务 > 管理私钥”(并选择 IIS_IUSRS)。 前 4 个步骤可以通过按 Windows 键并输入“管理计算机证书”或足够的字符以显示该选项,然后按 Enter 来实现【参考方案2】:有时当应用程序尝试访问证书时会发生问题,但没有足够的权限访问证书,可以通过以管理员身份运行应用程序来解决此问题。
【讨论】:
希望我能投票两次,几个月前这让我很伤心,今天又让我很伤心,这个答案在两种情况下都有帮助。 这在我的情况下是完全正确的:在管理员模式下启动 Visual Studio =) 应该被授予正确答案 ;-) 或者确保你运行dotnet run
的终端是以管理员身份启动的。【参考方案3】:
我有同样的问题,尝试了很多帖子和谷歌研究的所有内容。 但看起来我找到了修复。 当我将 Identify 从 ApplicationPoolIdentity 更改为 LocalSystem 时,一切都开始正常工作了。
可能会对某人有所帮助。
【讨论】:
如果您使用的是 IIS,则适用。顺便说一句,在池的 Advanced Settings 中寻找 Identify 属性 (official docs) 非常感谢【参考方案4】:在网上找到了这个解决方案,但我找不到来源。
由于我遇到了 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 对象直接保存到密钥库中。我不需要实际的文件,所以我跳过了这一步。
【讨论】:
这个答案对我很有帮助。不知道为什么 DotNetUtilities 转换不能正常工作,但这解决了我的问题,但出现了同样的错误!谢谢! 我想你的意思是Guid.NewGuid().ToString(),否则所有的容器都会有相同的名字(Guid.Empty),导致key被覆盖的问题。如果您还在 CspParameters 对象上指定 Flags = CspProviderFlags.UseMachineKeyStore,至少它会这样做。问我怎么知道的!【参考方案5】:对我来说适用于 Windows Server 2012 R2 (.net 4.6.1) -“所有任务 > 管理私钥”并设置对所有人的访问权限(设置为 IS_IUSRS 是不够的)
【讨论】:
【参考方案6】:以前,每次遇到此问题时,我都必须从本地计算机证书存储中删除证书并重新导入。然后这一切似乎都很幸福。如果只是重新导入它可以解决问题,我看不出它怎么可能是全局权限问题或无效证书。
我最终修复它的方法是使用 Windows 资源工具包中的 winhttpcertcfg 工具向使用证书的特定用户授予权限。
语法是:
"C:\Program Files (x86)\Windows Resource Kits\Tools\winhttpcertcfg" -i cert.p12 -c LOCAL_MACHINE\My -a UserWhoUsesTheCert -p passwordforp12
【讨论】:
【参考方案7】:从需要附加客户端证书的 .NET 应用程序调用 WCF REST 服务时,我遇到了类似的问题;我所要做的就是向“NETWORKSERVICE”提供对证书存储[mmc控制台]中证书的访问权限,当然我的 IIS 池是默认池,这表明它使用的是 NETWORKService 用户帐户。
我犯的错误是,我将证书从另一家商店复制到本地 机器 -> 使用密码保护证书的人员存储。应在所需存储中显式导入证书。
【讨论】:
【参考方案8】:我不记得这个错误,但您创建的证书不适用于 SSL/TLS,包括:
v1(不是 v3)证书; 缺少扩展; CN 无效; ...有几个 RFC 讨论了这个问题,包括 TLS (1.2) 上的 RFC5246。
最终制作您的自己的证书并不比使用makecert
制作的证书更合适(但最后一个可以生成可用于SSL/TLS 服务器证书)。
我强烈建议您从知名的证书颁发机构 (CA) 购买用于生产的 SSL/TLS 证书。这将使您获得大多数浏览器和工具都认可的工作证书。
【讨论】:
我尝试将我的 v1 证书生成器更改为 v3 版本,但无济于事。我们无法购买证书 - 我们将在安装时为我们的服务生成证书。显然它没有可信的认证路径,但仍然允许我们在传输数据时对其进行加密。 我认为您无法提供更多关于“缺少扩展名”和“无效 CN”的意思的详细信息? 这属于“可以写一本书”(参见常见问题解答)类别;-) 您可以通过查看makecert
的内部结构来进行最小猜测(或来自网站的任何 SSL 证书)。周围有很多废话(可悲),但浏览器也接受它们。
TLS RFC 不关心如何验证证书。 PKI RFC 是。这里还有一个关于哪些扩展有用的很好的注释:mozilla.org/projects/security/pki/nss/tech-notes/tn3.html
对于 subjectAltName 或 CN 中应包含的内容,请检查:tools.ietf.org/html/rfc6125以上是关于使用 BouncyCastle 生成的证书作为服务器进行身份验证时出现“无法识别提供给包的凭据”错误的主要内容,如果未能解决你的问题,请参考以下文章
向证书请求添加属性,java + bouncycastle 1.48
在没有 BouncyCastle 的情况下用 Java 创建 X509 证书?