Win32Exception当使用证书临时密钥验证作为服务器时



我试图通过使用google chrome(最新版本)的SSL流建立到web服务器的HTTPS连接,我使用CreateSelfSignedCertificate(string commonName)生成我的证书,当我调用https://localhost/时,它总是引发System.Security.Authentication.AuthenticationException : A call to SSPI failed, see inner exception -> Win32Exception: An unknown error occurred while processing the certificate

我正在使用一个临时密钥,我不想存储证书。

这是我的代码:

...
ServerCertificate = CreateSelfSignedCertificate("localhost");
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
SslStream s  = new SslStream(_stream, false, ValidateServerCertificate);
s.AuthenticateAsServer(ServerCertificate, false, SslProtocols.Tls12, false);
_stream = s;
...
...
public bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
}
public static X509Certificate2 CreateSelfSignedCertificate(string commonName)
{
X500DistinguishedName subjectName = new X500DistinguishedName($"CN={commonName}");
using (RSA rsa = RSA.Create(2048))
{
CertificateRequest certificateRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
X509Certificate2 certificate = certificateRequest.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1));
byte[] pfxData = certificate.Export(X509ContentType.Pkcs12);
return new X509Certificate2(pfxData, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.EphemeralKeySet);
}
}
更新:

  • 必须使用SAN,因为google chrome需要SAN,并且从不使用CNN回退。

ANSWER

使用临时密钥进行身份验证在Windows上是不可能的,因为提供TLS/SSL的底层操作系统组件不能使用临时密钥。

查看github问题在这里

也:

byte[] pfxData = certificate.Export(X509ContentType.Pkcs12, (string)null);
return new X509Certificate2(pfxData, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.EphemeralKeySet);

return certificate基本相同。

人们做pkcs12导出和重新导入的原因是而不是指定EphemeralKeySet(你也不应该断言PersistKeySet,除非你真的想这么做)。而且你可能也不需要Exportable。

byte[] pfxData = certificate.Export(X509ContentType.Pkcs12, (string)null);
return new X509Certificate2(pfxData);

会更好。

我遇到的问题最好用github issue上的一个由于年老而被锁定的问题来描述。

感谢微软的Barton Jeremy Barton在这方面的帮助,并指出了一些关于这个主题的亮点。


解决方案

解决这个问题:

切换到使用Azure密钥库管理证书的基于linux的Azure应用服务。Azure密钥库可以安全地存储证书和私钥,并自动处理续订。

您必须使用X509KeyStorageFlags.MachineKeySet将证书持久化到特定的CSP

这是我到目前为止的代码,这基本上存储了自签名证书,一旦它已经创建,所以你的服务器能够AuthenticateAsServer()而不会抛出Win32异常。

按需创建自签名证书的函数(根据需要自由调整):

public void CreateSelfSignedCertificate()
{
string commonName = "My Authority CA";
using (RSA rsa = RSA.Create(2048))
{
// Create a subject name
X500DistinguishedName subjectName = new X500DistinguishedName($"CN={commonName}");
// Create a self-signed certificate
CertificateRequest certificateRequest = new CertificateRequest(subjectName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Add a "Key Usage" extension to the certificate during its creation.
// The "Key Usage" extension defines the purposes for which the public key of the certificate can be used.
// X509KeyUsageFlags.DataEncipherment: The public key can be used to encrypt data, typically by encrypting a session key that is then used to encrypt the actual data.
// X509KeyUsageFlags.KeyEncipherment: The public key can be used to encrypt other keys, for example, in the TLS protocol during key exchange.
// X509KeyUsageFlags.DigitalSignature: The public key can be used to verify digital signatures.
// The second parameter of X509KeyUsageExtension specifies whether the extension is critical or not.
// If it is critical (true), applications that do not understand this extension must reject the certificate.
// If it is non-critical (false), applications that do not understand this extension can still accept the certificate.
certificateRequest.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
// oid 1.3.6.1.5.5.7.3.1 = "Server Authentication"
// oid 1.3.6.1.5.5.7.3.2 = "Client Authentication"
// oid 1.3.6.1.5.5.7.3.3 = "Code Signing"
// ...
certificateRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
// Add SAN extension (fallback)
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
sanBuilder.AddIpAddress(IPAddress.Parse("127.0.0.1"));
// Add all Machine IPv4 ou IPv6 configuration to SAN extension
foreach (var ipAddress in Dns.GetHostAddresses(Dns.GetHostName()))
if (ipAddress.AddressFamily == AddressFamily.InterNetwork || ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
sanBuilder.AddIpAddress(ipAddress);
certificateRequest.CertificateExtensions.Add(sanBuilder.Build());
// Set the certificate date and duration
X509Certificate2 certificate = certificateRequest.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1));
// Export the certificat
pfxData = certificate.Export(X509ContentType.Pkcs12);
}
}

代码将证书存储到特定的CSP,只有在它不存在的情况下才进行身份验证(这里我们将其存储到LocalMachine受信任的证书中,因为Google共享这些证书并可以访问它)

...
ServerCertificate = new X509Certificate2(pfxData, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
bool certificateExists = false;
foreach (X509Certificate2 existingCert in store.Certificates)
{
if (existingCert.Subject == ServerCertificate.Subject && existingCert.HasPrivateKey == ServerCertificate.HasPrivateKey && existingCert.GetCertHashString() == ServerCertificate.GetCertHashString())
{
certificateExists = true;
break;
}
}
if (!certificateExists)
store.Add(new X509Certificate2(_certificateContent));
store.Close();
...

我希望它对你有帮助

感谢@Charlieface的帮助

相关内容

  • 没有找到相关文章

最新更新