Chrome 使用证书临时密钥作为服务器进行身份验证时出现Win32异常

6ss1mwsb  于 2023-04-27  发布在  Go
关注(0)|答案(1)|浏览(129)

我试图建立一个HTTPS连接到网络服务器通过SSL流使用谷歌浏览器(最新版本),我生成我的证书使用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,因为谷歌Chrome需要SAN,从来没有使用CNN回退。
soat7uwm

soat7uwm1#

回答

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

参见github issue here

还有:

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

return certificate基本相同。

人们进行pkcs 12导出和重新导入的原因是NOT指定EphemeralKeySet*(你也不应该AssertPersistKeySet,除非你真的想这么做)*。而且你可能也不需要Exportable。

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

会更有效

***我遇到的问题最好用一个问题来描述,这个问题由于年龄太大而被锁定在github问题链接here上。
***感谢微软的巴顿Jeremy Barton在这方面对我的帮助,并在这个问题上指出了一些光明。

解决方案

要解决此问题,请执行以下操作:

您可以使用Azure Key Vault切换到基于Linux的Azure App Service来管理您的证书。Azure Key Vault可以安全地存储证书和私钥,并自动处理续订。

必须使用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的帮助

相关问题