SSL/TLS协议详解 - https为什么比http更安全

概述

  • 首先纠正一个错误,可能很多初学者都以为HTTPS跟HTTP一样,都属于应用层协议。但其实HTTPS并不是一个单独的协议。HTTPS是安全版本的HTTP,简单理解 HTTPS = HTTP + SSL/TLS,即HTTPS就是使用SSL/TLS协议对HTTP报文进行了加密处理。
    在这里插入图片描述
  • 我们发送HTTP请求时,不管直接从网页,还是使用抓包工具,可以很方便的拿到报文。但是发送HTTPS请求时,报文是经过加密处理的。因此说HTTPS比HTTP更加安全。
  • 接下来就介绍下SSL/TLS协议的握手过程,看看如何通过SSL/TLS协议对应用数据进行加密。

SSL/TLS简介

  • SSL (Secure Sockets Layer)安全套接层,是由Netscape公司于1990年开发,用于保障Word Wide Web(WWW)通讯的安全。主要任务是提供私密性,信息完整性和身份认证。1994年改版为 SSLv2,1995年改版为SSLv3
  • TLS(Transport Layer Security)安全传输层协议,用于在两个通信应用程序之间提供保密性和数据完整性。该标准协议是由IETF 于 1999年颁布,整体来说 TLS 非常类似 SSLv3,只是对 SSLv3 做了些增加和修改。

TLS握手过程

client server 生成客户端随机数 Client Hello 客户端随机数,客户端TLS版本,密码套件列表 生成服务端随机数 Server Hello 服务端随机数,确认TLS版本号,确认密码套件 Server Certificate 发送服务端证书 Server Hello Done 生成预备主密钥PreMaster 使用服务端证书中的公钥加密 预主密钥得到Encrypted PreMaster Client Key Exchange 发送Encrypted PreMaster 使用私钥解密Encrypted PreMaster 得到预备主密钥PreMaster 使用客户端随机数,服务端随机数 预备主密钥 计算出主密钥 使用客户端随机数,服务端随机数 预备主密钥 计算出主密钥 Change Cipher Spec 密钥协商完成 Finished Change Cipher Spec 密钥协商完成 Finished Application Data 应用数据加密通信 Application Data 应用数据加密通信 client server
  • 握手详解
    • [1] Client Hello
      • 客户端发送TLS版本,支持的加密套件列表给服务端,并生成客户端随机数发送给服务端。
      • Version : 表示客户端支持的SSL/TLS协议版本
      • Random : 客户端随机数
      • Session ID : 和会话恢复有关
      • Cipher Suites : 客户端支持的密码套件列表
        在这里插入图片描述
      • Compression Methods : 客户端支持的压缩方法
      • Extension : 扩展项
    • [2] Server Hello
      • 服务端确认TLS版本,选择使用的加密套件,生成服务端随机数发送给客户端。
      • Version : 服务端根据客户端传递的版本号,选择一个双方都支持的版本
      • Random : 服务端随机数
      • Session ID : 和会话恢复有关
      • Cipher Suite : 根据客户端传递过来的密码套件列表,选择一个双方都支持的密码套件
      • Compression Method : 压缩算法
      • Extension : 扩展项
    • [3] Server Certificate
      • 该消息是可选的。根据协商出来的密码套件,服务端选择是否发送证书消息。
    • [4] Server Key Exchange
      • 该消息是有条件才发送的。如果证书包含的信息不足以进行密钥交换,那么必须发送该消息。
    • [5] Server Hello Done
      • 表示服务端发送了足够的消息,接下来等待和客户端协商出预备主密钥。
    • [6] Client Key Exchange
      • 接收到服务端的Server Hello Done消息后,客户端发送此消息。该消息的主要作用就是协商出预备主密钥。这里只介绍使用RSA密码套件的流程。
      • 客户端生成一个48位的预备主密钥,然后用服务端证书中的公钥加密并发送给服务端。最终发送的消息就是Encrypted PreMaster。
    • [7] 计算主密钥和密钥块
      • 客户端根据预备主密钥,客户端随机数,服务端随机数计算出主密钥。服务端使用服务端证书中的私钥解密客户端发送过来的Encrypted PreMaster,得到预备主密钥,根据相同的方法计算出主密钥。主密钥的长度固定是48字节。
      • master_secret = PRF(pre_master_secret, “master secret”, ClientHello.random + ServerHello.random)
      • 计算出主密钥后,还需要根据主密钥计算出密钥块。密钥块主要有六个
      • Client MAC Key
      • Server MAC Key
      • Client Key
      • Server Key
      • Client IV
      • Server IV
      • MAC Key主要用于数据的完整性校验,Key用于加密数据。IV作为加密算法的初始化向量。
    • [8] Change Cipher Spec
      • 通知对方,可以用协商好的密钥进行通信了。
    • [9] Finished
      • 确认所有握手消息没有被篡改。
    • [10] Application Data
      • 使用密钥块加密通信

openssl实现tls通信

  • openssl提供了SSL/TLS协议库,下面通过代码实现下。
  • linux下执行这两个命令安装openssl。
    • sudo apt-get install openssl
    • sudo apt-get install libssl-dev

证书生成

  • 编码实现前,需要用到三个证书。CA证书,服务端证书,客户端证书。可以直接使用openssl命令行工具来生成。

  • CA证书

    • 生成RSA私钥
    • openssl genrsa -out ca.key 1024
    • 生成CA证书
    • openssl req -new -x509 -key ca.key -out ca.crt -days 365
  • 服务端证书

    • 生成服务端密钥
    • openssl genrsa -out server.key 1024
    • 生成证书请求文件
    • openssl req -new -key server.key -out server.csr
    • 使用CA证书签发服务端证书
    • openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt -days 365
  • 客户端证书

    • 生成客户端密钥
    • openssl genrsa -out client.key 1024
    • 生成证书请求文件
    • openssl req -new -key client.key -out client.csr
    • 使用CA证书签发客户端证书
    • openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt -days 365

代码

  • 服务端代码
    •   #include <stdio.h>
        #include <stdlib.h>
        #include <memory.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <unistd.h>
        #include <openssl/rsa.h>     
        #include <openssl/crypto.h>
        #include <openssl/x509.h>
        #include <openssl/pem.h>
        #include <openssl/ssl.h>
        #include <openssl/err.h>
        #include <openssl/rand.h>
      
        #define CERTF   "server.crt" /*服务端的证书(需经CA签名)*/
        #define KEYF   "server.key"  /*服务端的私钥(建议加密存储)*/
        #define CACERT "ca.crt" /*CA 的证书*/
        #define PORT   10088   /*准备绑定的端口*/
      
        int main (){
              
              
            SSL_load_error_strings();            /*为打印调试信息作准备*/
            OpenSSL_add_ssl_algorithms();        /*初始化*/
      
            SSL_CTX* ctx = SSL_CTX_new(TLSv1_server_method());
            if(ctx == NULL) {
              
              
                printf("SSL_CTX_new failed.\n");
                return -1;
            }
      
            SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);   /*验证与否*/
            SSL_CTX_load_verify_locations(ctx,CACERT,NULL); /*若验证,则放置CA证书*/
      
            if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            if (!SSL_CTX_check_private_key(ctx)) {
              
              
                printf("Private key does not match the certificate public key\n");
                return -1;
            }
      
            SSL_CTX_set_cipher_list(ctx,"RC4-MD5"); 
      
            /*开始正常的TCP socket过程.*/
            printf("Begin TCP socket...\n");
      
            int listenSock = socket(AF_INET, SOCK_STREAM, 0);  
            if(listenSock == -1){
              
              
                perror("socket");
                return -1;
            }
      
            struct sockaddr_in sa_serv;
            memset (&sa_serv, 0, sizeof(sa_serv));
            sa_serv.sin_family = AF_INET;
            sa_serv.sin_addr.s_addr = INADDR_ANY;
            sa_serv.sin_port = htons(PORT);         
      
            if(bind(listenSock, (struct sockaddr*) &sa_serv, sizeof (sa_serv)) == -1){
              
              
                perror("bind");
                return -1;
            }
      
            /*接受TCP链接*/
            if(listen (listenSock, 5) == -1){
              
              
                perror("listen");
                return -1;
            }                   
      
            struct sockaddr_in sa_cli;
            socklen_t client_len = sizeof(sa_cli);
            int connfd = accept (listenSock, (struct sockaddr*) &sa_cli, &client_len);
            if(connfd == -1){
              
              
                perror("accept");
                close (listenSock);
                return -1;
            }
      
            printf ("[%s:%d] connected...\n", inet_ntoa(sa_cli.sin_addr), sa_cli.sin_port);
      
            /*TCP连接已建立,进行服务端的SSL过程. */
            printf("Begin server side SSL\n");
      
            SSL* ssl = SSL_new (ctx);
            if(ssl == NULL){
              
              
                printf("SSL_new failed.\n");
                return -1;
            }
      
            SSL_set_fd (ssl, connfd);
            
            int sslSock = SSL_accept (ssl);
            if(sslSock == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf("SSL_accept finished\n");
      
            /*打印所有加密算法的信息(可选)*/
            printf ("SSL connection using %s\n", SSL_get_cipher(ssl));
      
            /*得到客户端的证书并打印些信息(可选) */
            X509* client_cert = SSL_get_peer_certificate (ssl);
            if (client_cert != NULL) {
              
              
                printf ("Client certificate:\n");
      
                char* subStr = X509_NAME_oneline(X509_get_subject_name (client_cert), 0, 0);
                if(subStr == NULL){
              
              
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
                }
      
                printf ("subject: %s\n", subStr);
                //Free (subStr);
      
                char* issStr = X509_NAME_oneline(X509_get_issuer_name  (client_cert), 0, 0);
                if(issStr == NULL){
              
              
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
                }
      
                printf ("issuer: %s\n", issStr);
                //Free (issStr);
      
                X509_free (client_cert);/*如不再需要,需将证书释放 */
            }else{
              
              
                printf ("Client does not have certificate\n");
            }
                
            char buf[4096] = {
              
              0};  
            /* 数据交换开始,用SSL_write,SSL_read代替write,read */
            int readSize = SSL_read(ssl, buf, sizeof(buf) - 1);  
            if(readSize == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf ("SSL_read buf[%d] = {%s}\n", readSize, buf);
      
            if(SSL_write (ssl, "Welcome to Connect to Server!", strlen("Welcome to Connect to Server!")) == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            } 
      
            shutdown (connfd, 2);
            SSL_free (ssl);
            SSL_CTX_free (ctx);
            return 0;
        }
      
  • 客户端代码
    •   #include <stdio.h>
        #include <stdlib.h>
        #include <memory.h>
        #include <errno.h>
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
        #include <unistd.h>
        #include <openssl/rsa.h>     
        #include <openssl/crypto.h>
        #include <openssl/x509.h>
        #include <openssl/pem.h>
        #include <openssl/ssl.h>
        #include <openssl/err.h>
        #include <openssl/rand.h>
      
        /*所有需要的参数信息都在此处以#define的形式提供*/
        #define CERTF  "client.crt"  /*客户端的证书(需经CA签名)*/
        #define KEYF  "client.key"   /*客户端的私钥(建议加密存储)*/
        #define CACERT "ca.crt"      /*CA 的证书*/
        #define PORT   10088          /*服务端的端口*/
        // #define SERVER_ADDR "182.92.205.179"  /*服务段的IP地址*/
        #define SERVER_ADDR "127.0.0.1"  /*服务段的IP地址*/
      
        int main () {
              
              
            /*初始化*/
            OpenSSL_add_ssl_algorithms(); 
      
            //载入所有SSL错误消息
            SSL_load_error_strings();     
      
            /*采用什么协议(SSLv2/SSLv3/TLSv1)在此指定*/
            /*申请SSL会话环境*/
            SSL_CTX *ctx = SSL_CTX_new(TLSv1_client_method());
            if(ctx == NULL){
              
              
                printf("SSL_CTX_new failed!\n");
                return -1;
            }                       
            
            /*验证与否,是否要验证对方*/
            SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
      
            /*若验证对方,则放置CA证书*/
            SSL_CTX_load_verify_locations(ctx,CACERT,NULL); 
      
            /*加载自己的证书*/
            if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
            
            /*加载自己的私钥,以用于签名*/
            if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            /*调用了以上两个函数后,检验一下自己的证书与私钥是否配对*/
            if (!SSL_CTX_check_private_key(ctx)) {
              
              
                printf("Private key does not match the certificate public key\n");
                return -1;
            } 
      
            /*以下是正常的TCP socket建立过程*/
            printf("Begin tcp socket...\n");
      
            int sock = socket(AF_INET, SOCK_STREAM, 0);
            if(sock == -1){
              
              
                perror("socket");
                return -1;
            }       
      
            struct sockaddr_in sa;
            memset (&sa, 0, sizeof(sa));
            sa.sin_family = AF_INET;
            sa.sin_addr.s_addr = inet_addr(SERVER_ADDR);   /* Server IP */
            sa.sin_port = htons(PORT);          /* Server Port number */
      
            if(connect(sock, (struct sockaddr*) &sa, sizeof(sa)) == -1){
              
              
                perror("connect");
                return -1;
            } 
      
            /* TCP 链接已建立.开始 SSL 握手过程 */
            printf("Begin SSL negotiation \n");
      
            /*申请一个SSL套接字*/
            SSL* ssl = SSL_new (ctx);
            if(ssl == NULL){
              
              
                printf("SSL_new failed.\n");
                return -1;
            }
      
            /*绑定读写套接字*/
            SSL_set_fd(ssl, sock);
      
            if(SSL_connect(ssl) == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            /*打印所有加密算法的信息(可选)*/
            printf ("SSL connection using %s\n", SSL_get_cipher(ssl));
      
            /*得到服务端的证书并打印些信息(可选) */
            X509* server_cert = SSL_get_peer_certificate (ssl); 
            if(server_cert == NULL){
              
              
                printf("SSL_get_peer_certificate failed.\n");
                return -1;
            }     
            
            printf ("Server certificate:\n");
      
            char* subStr = X509_NAME_oneline(X509_get_subject_name (server_cert),0,0);
            if(subStr == NULL){
              
              
                printf("X509_NAME_oneline subject failed.\n");
                return -1;
            }
      
            printf("subject: %s\n", subStr);
            // Free(subStr);
      
            char* issStr = X509_NAME_oneline (X509_get_issuer_name(server_cert),0,0);
            if(issStr == NULL){
              
              
                printf("X509_NAME_oneline issuer failed.\n");
                return -1;
            }
      
            printf ("issuer: %s\n", issStr);
            // Free (issStr);
      
            X509_free(server_cert);  /*如不再需要,需将证书释放 */
      
            /* 数据交换开始,用SSL_write,SSL_read代替write,read */
            printf("Begin SSL data exchange\n");
      
            if(SSL_write(ssl, "Hello, I am client!", strlen("Hello, I am client!")) == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            } 
      
            char buf[4096] = {
              
              0};
            int readSize = SSL_read(ssl, buf, sizeof(buf) - 1); 
            if(readSize == -1){
              
              
                ERR_print_errors_fp(stderr);
                return -1;
            }
      
            printf ("SSL_read buf[%d] = {%s}\n", readSize, buf);
      
            SSL_shutdown (ssl);  /* send SSL/TLS close_notify */
      
            shutdown (sock, 2);
            SSL_free (ssl);
            SSL_CTX_free (ctx);
      
            return 0;
        }
      
  • Makefile
    •   all: server client
        server: server.cpp
            g++ -o server server.cpp -lssl -lcrypto
        client: client.cpp
            g++ -o client client.cpp -lssl -lcrypto
      
        clean:
            rm server client
      

使用wireShark抓包分析

  • 接下来就使用wireShark抓包工具来分析下SSL/TLS协议的握手过程
    在这里插入图片描述

  • 通过上图可以看出,在SSL/TLS握手前后,还是要进行TCP三次握手和四次挥手。这个不再介绍TCP的握手过程,不清楚的可查看我这篇文章 : 抓包分析TCP协议

  • 接下来详细看下具体的SSL/TLS握手内容

  • Client Hello

    • TCP三次握手完成,客户端发送一个Client Hello消息,开始进行SSL/TLS握手
    • 客户端发送支持的TLS版本,支持的密码套件列表和一个客户端随机数。
      在这里插入图片描述
  • Server Hello

    • 服务端确认TLS版本,并从客户端发送来的加密套件列表中选择一个加密套件,再发送一个服务端随机数
      在这里插入图片描述
  • Server Certificate

    • 服务端发送自己的证书
      在这里插入图片描述
  • Server Hello Done

    • 服务端消息发送完毕,等待与客户端协商主密钥
      在这里插入图片描述
  • Client Key Exchange

    • 客户端将加密后的预备主密钥发送给服务端
      在这里插入图片描述
  • Change Cipher Spec

    • 通知对方,可以使用协商好的主密钥进行加密通信
      在这里插入图片描述
  • Finished

    • 即Encrypted Handshake Message消息。确认握手消息没有被篡改。
  • Application Data

    • 应用数据进行加密发送
      在这里插入图片描述

参考资料

  • 《HTTPS权威指南》
  • 《深入浅出HTTPS从原理到实战》

猜你喜欢

转载自blog.csdn.net/new9232/article/details/128554991