【问题标题】:How can I use SslStream/AuthenticateAsServer with a self signed cert/key?如何将 SslStream/AuthenticateAsServer 与自签名证书/密钥一起使用?
【发布时间】:2021-02-19 14:02:44
【问题描述】:

我想(使用 C# 和 dotnet 4.8)编写一个使用 TLS 来确保安全的 TCP 服务。为此,我想将 SslStream 与 TcpListener 一起使用,但我不断收到奇怪的错误。

我的实验程序如下。简而言之:

  • 程序根据https://stackoverflow.com/a/52535184/3568 的代码创建一个自签名 PFX
  • 它启动一个线程来运行服务,而主线程运行客户端。
  • 服务线程侦听连接。
  • 客户端连接。
  • 该服务发送一条线路以确认底层 TCP 连接正常工作。
  • 客户端收到线路。
  • 服务器和客户端都将控制权移交给 SslStream,服务器使用之前创建的 PFX。
    • 这是引发异常的地方。
  • 协商 TLS 后,服务器会发送另一条线路,这次是在 TLS 通道内。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;

namespace TestTlsService
{
    class Program
    {
        static string tempCert;

        static void Main(string[] args)
        {
            /* Create a cert. */
            var cr = new CertificateRequest("cn=this.is.invalid", ECDsa.Create(), HashAlgorithmName.SHA256);
            using (var cert = cr.CreateSelfSigned(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow.AddYears(+1)))
            {
                var exp = cert.Export(X509ContentType.Pfx);
                tempCert = Path.Combine(Path.GetTempPath(), "MySelfSignedCert.pfx");
                File.WriteAllBytes(tempCert, exp);
            }

            /* Launch a service thread. */
            Thread svc = new Thread(ServiceMain);
            svc.Start();
            Thread.Sleep(100);

            /* Connect as a client. */
            using (var tcp = new TcpClient())
            {
                tcp.Connect("localhost", 1984);
                var stream = tcp.GetStream();

                /* Read the line "GoTLS" from the server. */
                var insecureLine = ReadLine(stream);

                /* Hand over control to TLS. */
                using (var tls = new SslStream(stream, false, CheckCert))
                {
                    tls.AuthenticateAsClient("this.is.invalid");

                    /* Read a different line, this time securely. */
                    string line = ReadLine(tls);
                }
            }
        }

        static void ServiceMain()
        {
            /* Open a listener and start listening. */
            var listen = new TcpListener(IPAddress.Loopback, 1984);
            listen.Start();
            using (var tcp = listen.AcceptTcpClient())
            {
                /* Send "GoTLS" to the client insecurely. */
                var stream = tcp.GetStream();
                stream.Write(Encoding.ASCII.GetBytes("GoTLS\r\n"), 0, 7);

                /* Hand over control to TLS, using the self-signed cert from earlier. */
                using (var tls = new SslStream(stream))
                {
                    var cert = new X509Certificate2(tempCert);
                    tls.AuthenticateAsServer(cert);

                    /* Send a new message inside the secure channel. */
                    tls.Write(Encoding.ASCII.GetBytes("Hello\r\n"), 0, 7);
                }
            }
        }

        /* Simple function that just reads a line. */
        static string ReadLine(Stream stream)
        {
            byte[] line = new byte[100];
            int bytesIn = stream.Read(line, 0, 100);
            string lineAscii = Encoding.ASCII.GetString(line, 0, bytesIn);
            return lineAscii;
        }

        /* Accept all certificates. DO NOT COPY THIS INTO YOUR OWN CODE! */
        private static bool CheckCert(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            /* The full version will check the cert, but for now we'll just... */
            return true;
        }
    }
}

此程序在 AuthenticateAsServer 行中引发 AuthenticationException,并显示消息“对 SSPI 的调用失败,请参阅内部异常”。而内部异常有“客户端和服务器无法通信,因为它们没有共同的算法”。我发现 SslStream 库自己找不到通用协议的可能性不大。

我的怀疑是我的问题的根源是我使用的是自签名证书,因为我可以找到的所有 AuthenticateAsServer 示例都使用预生成的证书文件,唉,我没有.

如何在服务器端使用 SslStream 与自签名证书协商 TLS?


预先提出预期的问题...

我知道客户端可以正常工作,因为我已经用邮件服务器替换了“localhost”(并跳过了 read-a-line),并且客户端线程可以正常读取远程邮件服务器发送的安全欢迎行在协商 TLS 之后。

ServicePointManager.SecurityProtocol 设置为Tls12 没有帮助。

如果我删除 pre-SslStream“GoTLS”交换,我会得到同样的错误。

我已经搜索了错误消息。我能找到的所有答案都建议将 SecurityProtocol 设置为 Tls12,我已经尝试过了。

我尝试了其他格式的证书而不是 PFX,但我收到了不同的错误,抱怨我丢失了私钥。

【问题讨论】:

    标签: c# .net server tls1.2


    【解决方案1】:

    我不知道具体原因,但它不喜欢您颁发的 ECDSA 证书。如果您改为使用标准 RSA:

    var cr = new CertificateRequest(new X500DistinguishedName("cn=this.is.invalid"), RSA.Create(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    

    并保留其他所有内容 - 然后它将按预期工作。理论上,TLS 支持 ECC(椭圆曲线)密钥,但我不是这方面的专家,也不知道为什么 SslStream.AuthenticateAsServer 不喜欢那个证书。希望您不需要专门的 ECC,那么上面使用 RSA 的解决方法就可以了。

    【讨论】:

      猜你喜欢
      • 2012-12-22
      • 2013-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-23
      • 2011-04-08
      • 2014-05-28
      • 1970-01-01
      相关资源
      最近更新 更多