简体   繁体   English

Windows/Linux dotnet 核心 SslStream 差异

[英]Windows/Linux dotnet core SslStream differences

I'm trying to get SslStream with local (file) certificates working with dotnet core 5. On Linux (Alpine Linux 3.14.0), everything functions as intended with servers authenticating the remote client.我正在尝试使用与 dotnet core 5 一起使用的本地(文件)证书获取 SslStream。在 Linux(Alpine Linux 3.14.0)上,一切都按预期运行,服务器对远程客户端进行身份验证。 On Windows (Windows 10 Enterprise, version 20H2), it seems that the authentication procedure is still trying to use the Windows certificate store to validate even though the certificate validation should be overridden by the SslStream constructor.在 Windows(Windows 10 企业版,版本 20H2)上,身份验证过程似乎仍在尝试使用 Windows 证书存储进行验证,即使证书验证应由 SslStream 构造函数覆盖。

Is this a bug in the Windows implementation of SslStream, or am I missing a required configuration to force it to only use the loaded certificate files?这是 SslStream 的 Windows 实现中的错误,还是我缺少强制它仅使用加载的证书文件所需的配置?

Test program below.测试程序如下。 The program will generate a CA and certificates for a client and server.该程序将为客户端和服务器生成 CA 和证书。 It will then create 2 threads to test a SslStream using those certificates.然后它将创建 2 个线程来使用这些证书测试SslStream Linux runs without any issues, but Windows will throw a System.ComponentModel.Win32Exception (0x80090304): The Local Security Authority cannot be contacted when it runs. Linux 运行没有任何问题,但 Windows 会抛出System.ComponentModel.Win32Exception (0x80090304): The Local Security Authority cannot be contacted运行时System.ComponentModel.Win32Exception (0x80090304): The Local Security Authority cannot be contacted

using System;

namespace sslstream
{
    class Program
    {
        static bool VerifyCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        {
            return true; //TODO: verify certificate chain and hostnames
        }

        static void RunClient()
        {
            var clientCert = new System.Security.Cryptography.X509Certificates.X509Certificate2("Client.pfx");
            var collection = new System.Security.Cryptography.X509Certificates.X509CertificateCollection(new System.Security.Cryptography.X509Certificates.X509Certificate[] { clientCert });
            using var client = new System.Net.Sockets.TcpClient();
            client.Connect(System.Net.IPAddress.Loopback, 12345);
            var clientStream = client.GetStream();
            using var sStream = new System.Net.Security.SslStream(clientStream, false, new System.Net.Security.RemoteCertificateValidationCallback(VerifyCertificate), null, System.Net.Security.EncryptionPolicy.RequireEncryption);
            sStream.AuthenticateAsClient("127.0.0.1", collection, System.Security.Authentication.SslProtocols.Tls12, false);
            sStream.Write(new byte[1] { 55 });
        }
        static void RunServer()
        {
            var serverCert = new System.Security.Cryptography.X509Certificates.X509Certificate2("127.0.0.1.pfx");
            var listener = new System.Net.Sockets.TcpListener(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 12345));
            listener.Start();
            using var client = listener.AcceptTcpClient();
            var clientStream = client.GetStream();
            using var sStream = new System.Net.Security.SslStream(clientStream, false, new System.Net.Security.RemoteCertificateValidationCallback(VerifyCertificate), null, System.Net.Security.EncryptionPolicy.RequireEncryption);
            sStream.AuthenticateAsServer(serverCert, true, System.Security.Authentication.SslProtocols.Tls12, false);
            var fiftyFive = sStream.ReadByte();
            if (fiftyFive != 55)
                throw new Exception($"Expected 55, got {fiftyFive}");
        }
        static void Main(string[] args)
        {
            if (!System.IO.File.Exists("CA.pfx"))
                MakeCertificates();
            CRNG.Dispose();
            var t1 = new System.Threading.Thread(RunServer);
            t1.Start();
            //TODO: wait for server to start before starting client
            System.Threading.Thread.Sleep(1000);
            var t2 = new System.Threading.Thread(RunClient);
            t2.Start();
            t1.Join();
            t2.Join();
        }
        static void MakeCertificates()
        {
            MakeCA();
            MakeCert("127.0.0.1");
            MakeCert("Client");
        }
        static void MakeCA()
        {
            var ecdsa = System.Security.Cryptography.ECDsa.Create(); // generate asymmetric key pair
            var req = new System.Security.Cryptography.X509Certificates.CertificateRequest($"cn=Certificate Authority", ecdsa, System.Security.Cryptography.HashAlgorithmName.SHA256);
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension(true, false, 0, true));
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension(req.PublicKey, false));
            var cert = req.CreateSelfSigned(System.DateTimeOffset.Now, System.DateTimeOffset.Now.AddYears(1000));
            System.IO.File.WriteAllBytes("CA.pfx", cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pfx));
            System.IO.File.WriteAllText("CA.crt",
                "-----BEGIN CERTIFICATE-----\r\n"
                + System.Convert.ToBase64String(cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Cert), System.Base64FormattingOptions.InsertLineBreaks)
                + "\r\n-----END CERTIFICATE-----");
        }
        static System.Security.Cryptography.RandomNumberGenerator CRNG = System.Security.Cryptography.RandomNumberGenerator.Create();
        static void MakeCert(string cn)
        {
            var ecdsa = System.Security.Cryptography.ECDsa.Create(); // generate asymmetric key pair
            var ca = new System.Security.Cryptography.X509Certificates.X509Certificate2("CA.pfx");
            var req = new System.Security.Cryptography.X509Certificates.CertificateRequest($"cn={cn}", ecdsa, System.Security.Cryptography.HashAlgorithmName.SHA256);
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509BasicConstraintsExtension(false, false, 0, false));
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509KeyUsageExtension(System.Security.Cryptography.X509Certificates.X509KeyUsageFlags.DigitalSignature | System.Security.Cryptography.X509Certificates.X509KeyUsageFlags.NonRepudiation, false));
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509SubjectKeyIdentifierExtension(req.PublicKey, false));
            req.CertificateExtensions.Add(new System.Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension(new System.Security.Cryptography.OidCollection { new System.Security.Cryptography.Oid("1.3.6.1.5.5.7.3.8") }, true));
            var serial = new byte[20];
            CRNG.GetBytes(serial);
            var cert = req.Create(ca, System.DateTime.Now, System.DateTime.Now.AddYears(500), serial);
            cert = System.Security.Cryptography.X509Certificates.ECDsaCertificateExtensions.CopyWithPrivateKey(cert, ecdsa);
            System.IO.File.WriteAllBytes($"{cn}.pfx", cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Pfx));
            System.IO.File.WriteAllText($"{cn}.crt",
                "-----BEGIN CERTIFICATE-----\r\n"
                + System.Convert.ToBase64String(cert.Export(System.Security.Cryptography.X509Certificates.X509ContentType.Cert), System.Base64FormattingOptions.InsertLineBreaks)
                + "\r\n-----END CERTIFICATE-----");
        }
    }
}

I'm not sure if it's just a bug on Windows, but I switched to RSA instead of ECDSA and now it works on both Linux and Windows.我不确定这是否只是 Windows 上的错误,但我切换到 RSA 而不是 ECDSA,现在它可以在 Linux 和 Windows 上运行。

Key generation is a lot slower (not sure about processing overhead from data transfer yet), so if anyone has a solution for using ECDSA on Windows I'd prefer that.密钥生成要慢得多(还不确定数据传输的处理开销),所以如果有人有在 Windows 上使用 ECDSA 的解决方案,我更喜欢那个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM