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


    转载自: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.从文件

    从文件读取证书

    注意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读取证书

    注意:

    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 =newbyte[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

  • 相关阅读:
    XIX Open Cup, Grand Prix of China【杂题】
    开发自己的composer包
    IOS微信禁用分享跳转页面返回BUG修复
    Pytest Fixture(一)
    Python使用requests和requests_toolbelt上传文件
    Pytest之参数化
    Pytest Fixture(三)
    Pytest 多进程并发执行
    Pytest Fixture(二)
    20212923 202120222 《网络攻防实践》第二次作业
  • 原文地址:https://www.cnblogs.com/langqi250/p/2704384.html
Copyright © 2020-2023  润新知