证书的应用之一 —— TCP&SSL通信实例及协议分析(上)

本文介绍如何使用数字证书实现SSL安全传输,包括服务器端与客户端的TCP监听与连接、证书配置及数据交换流程,并讨论了常见错误及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://www.cnblogs.com/piyeyong/archive/2010/06/20/1761458.html

SSL(Security Socket Layer)是TLS(Transport Layer Security)的前身,是一种加解密协议,它提供了再网络上的安全传输,它介于网络通信协议的传输层与应用层之间。

为实现TCP层之上的ssl通信,需要用到数字证书。本文通过具体例子来说明如何使用数字证书来实现网络上的安全传输。需要用到.net提供的SslStream, TcpListener, TcpClient, X509Certificate2,X509Store,X509Certification2Collection等类。终于开始涉及到代码了。

一.服务器端

1.指定证书

常用的有两种方式:从文件获取和从certificate store中获取

a.从文件

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

注意X509Certificate2构造函数第三个参数,如果想把调用Export方法将cert对象到处,此处必须使用Exportable标记,否则在导出时会抛出异常。

pfx格式的证书包含有private key,因此需要密码的保护,构造函数的第二个参数就是密码。

选取的证书必须包含有private key,否则在SSL的server端使用时会抛出AuthenticationException。

怎么得到pfx文件:使用MMC->File->Add/Remove Sanp-in->Add->Certificates->Add->My user account/Computer account->Finish 查看存储在本机当前用户和所有用户的证书,选择用导出的证书,

右键->All Tasks...->Export...注意要勾选[Yes, export the private key],如果该Radio button被禁用,说明该证书的private key不能被导出,可能是在导入该证书时没有选择标记private key为可导出,如下图所示:

b.从certificate store

 

从store读取证书
复制代码
    
1 X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine); 2 store.Open(OpenFlags.ReadOnly); 3 X509Certificate2Collection collection = X509Certificate2UI.SelectFromCollection(store.Certificates, 4 " Select Certificate " , 5 " Please select a certificate from the following list " , 6 X509SelectionFlag.SingleSelection);
复制代码

注意:

Server端指定的cert必须含有privatekey,且Enhanced key usage必须含有Server Authentication (1.3.6.1.5.5.7.3.1)

没有private key

NotSupportedException: The server modeSSL must use a certificate with the associated private key.

证书purpose不对:

server端:AuthenticationException: A call to SSPIfailed, see inner exception.

client端:IOException: Unable to read data from the transport connection: Anexisting connection was forcibly closed by the remote host..

2.开始TCP监听

 

复制代码
TCP监听
    
1 TcpListener listener = new TcpListener(IPAddress.Any, 8080 ); 2 listener.Start(); 3 while ( true ) 4 { 5 Console.WriteLine( " Waiting for a client to connect... " ); 6 TcpClient client = listener.AcceptTcpClient(); 7 ...... 8 }
复制代码

 

3.指定服务器证书

 

复制代码
指定服务器证书
    
1 // A client has connected. Create the 2 // SslStream using the client's network stream. 3   SslStream sslStream = new SslStream(client.GetStream(), false ); 4 // Authenticate the server but don't require the client to authenticate. 5   try 6 { 7 sslStream.AuthenticateAsServer(cert, false , SslProtocols.Default, false ); 8 9 // Set timeouts for the read and write to 5 seconds. 10   sslStream.ReadTimeout = 5000 ; 11 sslStream.WriteTimeout = 5000 ; 12 ...... 13 }
复制代码

 

4.发送数据

 

   
1 byte [] message = Encoding.UTF8.GetBytes( " Hello from the server.<EOF> " ); 2 sslStream.Write(message); 3 sslStream.Flush();

 

5.接收数据

 

复制代码
接收数据
    
1 byte [] buffer = new byte [ 2048 ]; 2 StringBuilder messageData = new StringBuilder(); 3 int bytes = - 1 ; 4 do 5 { 6 // Read the client's message. 7   bytes = sslStream.Read(buffer, 0 , buffer.Length); 8 messageData.Append(Encoding.UTF8.GetString(buffer, 0 , bytes)); 9 // Check for EOF or an empty message. 10   if (messageData.ToString().IndexOf( " <EOF> " ) != - 1 ) 11 { 12 break ; 13 } 14 } while (bytes != 0 ); 15 16 return messageData.ToString();
复制代码

 

注意:Write后需要调用Flush将数据立刻发送,Read需要多次调用,确定读不到数据位置,因为TCP连接时Stream方式的,在网络中传输可能会分包到达,一次无法全部读取,还需要消息边界。

6.结束

 

   
1 sslStream.Close(); 2 client.Close();

 

二.客户端

1.与服务器端建立TCP连接

 

   
1 TcpClient client = new TcpClient(machineName, 8080 );

2.与服务端建立SSL握手

 

复制代码
客户端与服务端建立SSL握手
    
1 SslStream sslStream = new SslStream( 2 client.GetStream(), 3 false , 4 new RemoteCertificateValidationCallback(ValidateServerCertificate), 5 null 6 ); 7 try 8 { 9 // The server name must match the name on the server certificate. 10   X509Certificate2 cert = GetCert(); 11 X509Certificate2Collection collection = new X509Certificate2Collection(); 12 if (cert != null ) 13 { 14 collection.Add(cert); 15 } 16 sslStream.AuthenticateAsClient(serverName, collection, SslProtocols.Default, false ); 17 } 18 catch (AuthenticationException e) 19 { 20 Console.WriteLine( " Exception: {0} " , e.Message); 21 if (e.InnerException != null ) 22 { 23 Console.WriteLine( " Inner exception: {0} " , e.InnerException.Message); 24 } 25 Console.WriteLine( " Authentication failed - closing the connection. " ); 26 client.Close(); 27 return ; 28 }
复制代码

 

如果服务端在调用AuthenticateAsServer方法时指定不需要客户端的证书,则客户端在调用AuthenticateAsClient时可以不指定证书,

如果serverAuthenticateAsServer是指定client需要cert,而client在调用AuthenticateAsClient时没有指定cert或者cert没有private key时:

server端:AuthenticationExceptionThe remote certificate is invalid according to the validationprocedure.

client端:IOException: Unable to read data from the transportconnection: An established connection was aborted by the software in your hostmachine.

 

ClientAuthenticateAsClient方法指定的名字需要与server端使用cert的名字一致,否则在RemoteCertificateValidationCallback事件中SslPolicyErrors会是RemoteCertificateNameMismatch

3.发送接收数据,关闭连接,与服务器端方法相同


使用Wireshark Network Analyzer工具进行抓包分析,发现在建立TCP连接后,首先进行SSL握手,之后传输的数据都是被加密的,如下图所示:


对SSL的握手和加密传输的详细过程,将在下节分析。

下载Demo

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值