如何将 PFX 证书文件应用于 SslStream 套接字侦听器?

Posted

技术标签:

【中文标题】如何将 PFX 证书文件应用于 SslStream 套接字侦听器?【英文标题】:How to apply PFX certificate file to SslStream socket listener? 【发布时间】:2015-04-27 11:52:25 【问题描述】:

我有一个多线程套接字侦听器。它监听一个端口。它从 HTTP 网站接收数据并根据他们的请求发送响应。效果很好。

现在我想对 HTTPS 网站做同样的事情。所以我将其更改为能够通过 SslStream 而不是套接字进行读写。

网站所有者给了我一个 pfx 文件和一个密码。我将它放入本地证书存储的受信任证书列表中。

已成功从商店中检索到认证文件。但是在调用 AuthenticateAsServer 方法后,我的处理程序套接字似乎是空的。所以我在 ReceiveCallBack 方法中看不到任何数据。

如何将 pfx 证书应用到我的 SslStream 套接字?

这是我的代码:

using System;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.IO;

namespace SslStreamTestApp

    public class SslStreamSocketListener
    
        private static readonly ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
        private readonly string pfxSerialNumber = "12345BLABLABLA";

        X509Certificate _cert = null;

        private void GetCertificate()
        
            X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly);

            X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySerialNumber, pfxSerialNumber, true);

            if (certificateCollection.Count == 1)
            
                _cert = certificateCollection[0];
            
        

        private void StartListening()
        
            GetCertificate();

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 9002);

            if (localEndPoint != null)
            
                Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                if (listener != null)
                
                    listener.Bind(localEndPoint);
                    listener.Listen(5);

                    Console.WriteLine("Socket listener is running...");

                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                
            
        

        private void AcceptCallback(IAsyncResult ar)
        
            _manualResetEvent.Set();

            Socket listener = (Socket)ar.AsyncState;

            Socket handler = listener.EndAccept(ar);

            SslStream stream = new SslStream(new NetworkStream(handler, true));
            stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Ssl3, true);

            StateObject state = new StateObject();
            state.workStream = stream;

            stream.BeginRead(state.buffer, 0, StateObject.BufferSize, ReceiveCallback, state);

            listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
        

        private void ReceiveCallback(IAsyncResult result)
        
            StateObject state = (StateObject)result.AsyncState;
            SslStream stream = state.workStream;

            // Check how many bytes received
            int byteCount = stream.EndRead(result);

            Decoder decoder = Encoding.UTF8.GetDecoder();

            char[] chars = new char[decoder.GetCharCount(state.buffer, 0, byteCount)];

            decoder.GetChars(state.buffer, 0, byteCount, chars, 0);

            state.sb.Append(chars);

            string check = state.sb.ToString();

            if (state.sb.ToString().IndexOf("<EOF>") == -1 && byteCount != 0)
            
                // We didn't receive all data. Continue receiving...
                stream.BeginRead(state.buffer, 0, state.buffer.Length, new AsyncCallback(ReceiveCallback), stream);
            
            else
            
                string[] lines = state.sb.ToString().Split('\n');

                // send test message to client
                byte[] test = Encoding.UTF8.GetBytes("\"infoMessage\":\"test\"");
                stream.BeginWrite(test, 0, test.Length, WriteAsyncCallback, stream);
            
        

        private void WriteAsyncCallback(IAsyncResult ar)
        
            SslStream sslStream = (SslStream)ar.AsyncState;

            Console.WriteLine("Sending message to client...");

            try
            
                sslStream.EndWrite(ar);
            
            catch (IOException)
            
                Console.WriteLine("Unable to complete send.");
            
            finally
            
                sslStream.Close();
            
        
    

    public class StateObject
    
        public SslStream workStream = null;
        public const int BufferSize = 1024;
        public byte[] buffer = new byte[BufferSize];
        public StringBuilder sb = new StringBuilder();
    

编辑: 我想我对 SSL 逻辑有一些问题。 PFX 文件属于 HTTPS 网站。但是请求来自 HTTPS 到我的套接字侦听器。然后我的套接字侦听器发送响应。在这种情况下,我是服务器还是客户端?我将使用 AuthenticateAsServer 还是 AuthenticateAsClient?

如果我调用 AuthenticateAsServer,它不会抛出错误。它毫无问题地通过了身份验证。但是我无法通过我的 StateObject 的 SslStream 将处理程序套接字中的数据传送到 ReceiveCallBack 方法。

【问题讨论】:

您是否有任何理由开始在处理程序本身而不是SslStream stream 上接收?无论如何,发送者最有可能发送加密数据。 我不想对我的代码进行大的改动,因为它运行良好。所以,我正在寻找是否有一种将套接字用于 HTTPS 的方法。 我不是低层传输方面的专家,但我想这是不可能的。如果客户端使用 SSL,则不能指望在接收时读取“原始”套接字并获取纯文本。您需要一些东西来解析/解码 SSL 流(这就是 SslStream 的用途)。您可以调整现有代码以使用Stream/NetworkStream。那么SslStream 应该是相当容易的。 我认为这应该是一个答案而不是评论。也许你是对的。 @Caramiriel 我将其更改为 SslStream 但它仍然无法读取数据。检查我编辑的问题。 【参考方案1】:

您可以使用以下命令直接加载 pfx 文件:

byte[] pfxData = File.ReadAllBytes("myfile.pfx");

cert = new X509Certificate2(pfxData, "password", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

如果您从证书存储加载它,那么它的重要性 密钥可以为应用程序导出。

信息:SSL 3.0 版不再安全。 也许这就是为什么你无法建立联系。 Chrome 和 Firefox 禁用了支持。

提示:编写一个简单的 SSL 客户端来发送一些“Hello World”字节。

我使用 pfx 格式的自签名证书测试了您的代码 通过 c# 客户端和 chrome 浏览器。 两个客户端都可以连接和发送/接收数据。

我唯一改变的是将 SSLProtocol 设置为 TLS

stream.AuthenticateAsServer(_cert, false, System.Security.Authentication.SslProtocols.Tls, true);

信息:HTTPS 是通过 tcp/tls 连接的 HTTP 协议。您有一个没有 http 协议的原始 tcp/tls 连接。

提示:不要忘记打开防火墙端口 9002 !

【讨论】:

我怎样才能让它为应用程序导出?它存储在证书存储的受信任发布者中。我通过证书商店打开了它,我也将它添加到了解决方案中,并尝试直接从那里打开它,但我无法实现。我尝试了 Tls 而不是 SSL3,但没有帮助。 您可以使用以下方法测试私钥: if(!cert.HasPrivateKey) throw new Exception("private key not loaded.");我会在几个小时内测试你的代码。

以上是关于如何将 PFX 证书文件应用于 SslStream 套接字侦听器?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 .crt 证书文件转换为 .pfx

SslStream.AuthenticateAsServer 异常 - 服务器模式 SSL 必须使用具有关联私钥的证书

将CERT / PEM证书转换为PFX证书

如何转换 PFX 证书文件以在 linux 服务器上与 Apache 一起使用?

从 .pfx 文件中提取证书和私钥文件

如何使用 c# 从 pfx 文件中检索证书?