选中内容(绿色)时除了会搜索文章名,还会搜索文章内容
点击结果中的文章名进入文章界面后可以按Ctrl+F在页面内搜索
  • 版权:CC BY-SA 4.0
  • 创建:2019-10-13
  • 更新:2020-02-28
SSL/TLS 基本概念 证书生成 代码实现


SSL/TLS 基本概念

  • 身份认证
  • 数据加密

公钥加密,私钥解密

对称加密和非对称加密:对称加密使用一个密钥,非对称使用公钥加密私钥解密

先确认身份,然后生成随即码作为对称加密的密钥,传输随机码时的数据内容经过了非对称加密的,不会泄露,而且每次新的连接对称密钥都是生成的,就算被破解了下次就失效了

SSL和TLS的关系与区别

简单一句话概括就是TLS是基于SSL发布的新版本,更新更安全
参考一下文章:

SSL/TLS 单向认证步骤

身份认证:客户端需要验证服务器的身份,但是服务器不需要验证客户端身份,典型的例子:浏览器浏览网站
数据加密:双向加密

  • 客户端发起请求,给出客户端支持的协议版本信息
  • 服务端返回服务器的公钥和证书(一个crt文件),是明文发送
  • 客户端收到来自服务器的公钥和证书并验证安全性,从证书中获取CA信息,如果CA在本地白名单并且签发的证书有效,就通过
  • 客户端向服务器发送支持的加密套件
  • 服务器选择加密套件,返回给客户端(明文)
  • 客户端生成随机码作为后面传输对称加密的密钥,向服务器发送,并且使用服务器的公钥进行加密传输
  • 服务端接收到数据,使用私钥进行解密得到真正的数据,其中包含了对称加密密钥。由于私钥只有服务器有,就算数据被拦截只要加密算法够好就不会被破解
  • 然后客户端和服务器的双向通信都使用这个随机生成的对称加密密钥进行对称加密传输

SSL/TLS双向认证步骤

身份认证:服务器和客户端需要互相认证,典型例子:网银U盾,只有拥有银行给的包含了一个公钥和私钥的客户端(U盾)才能认证通过从而通信
数据加密:双向加密

  • 客户端发起请求,给出客户端支持的协议版本信息
  • 服务端返回服务器的公钥,是明文发送
  • 客户端收到来自服务器的公钥和证书并验证安全性,从证书中获取CA信息,如果CA在本地白名单并且签发的证书有效,就通过
  • 客户端向服务器发送公钥和证书
  • 服务器接收到客户端的公钥并同样验证有效性,通过后保存证书和密钥
  • 客户端发送支持的加密套件给服务器
  • 将选择好的加密方案 通过客户端发过来的公钥加密后发送给客户端
  • 客户端生成随机码作为后面传输对称加密的密钥,向服务器发送,并且使用服务器的公钥进行加密传输
  • 服务端接收到数据,使用私钥进行解密得到真正的数据,其中包含了对称加密密钥。由于私钥只有服务器有,就算数据被拦截只要加密算法够好就不会被破解
  • 然后客户端和服务器的双向通信都使用这个随机生成的对称加密密钥进行对称加密传输

SSL/TLS认证证书生成

使用以下脚本生成:

  1. 首先自己做CA,需要生成CA的秘钥(ca.key)和证书(ca.crt)
  2. 服务器的秘钥(server.key)和被CA签过的证书(server.crt)。(server.csr是中间文件,用来给CA生成server.crt时使用,如果CA不是自己,则需要将这个发送给服务器和客户端都信任的CA,由它帮我们生成server.crt
  3. 客户端的秘钥(client.key)和被CA签过的证书(client.crt)。(同理,client.csr是中间文件)

运行脚本会要求填写一些信息,总共会填写三次,第一次是CA的信息,第二次是服务器的信息,第三次是客户端的信息

脚本最后注释掉的部分可以用来验证证书是否可用

  1. #!/bin/bash
  2. echo ""
  3. echo "========generate CA cert and key files========"
  4. echo ""
  5. openssl genrsa -out ca.key 2048
  6. openssl req -new -x509 -days 365 -key ca.key -out ca.crt
  7. echo ""
  8. echo "========generate Server cert and key files========"
  9. echo ""
  10. openssl genrsa -out server.key 2048
  11. openssl req -new -key server.key -out server.csr
  12. echo ""
  13. echo "========generate Client cert and key files========"
  14. echo ""
  15. openssl genrsa -out client.key 2048
  16. openssl req -new -key client.key -out client.csr
  17. echo ""
  18. echo "========sign server cert with CA========"
  19. echo ""
  20. openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
  21. #openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
  22. echo ""
  23. echo "========sign client cert with CA========"
  24. echo ""
  25. openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
  26. #openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
  27. # openssl s_server -accept 10001 -key server.key -cert server.crt
  28. # openssl s_client -connect localhost:10001 -CAfile ca.crt
  29. # openssl s_server -accept 10001 -key server.key -cert server.crt -Verify 5
  30. # openssl s_client -connect localhost:10001 -cert client.crt -key client.key -CAfile ca.crt

单向认证时,客户端需要验证服务器是否是可信任的,客户端应该持有CA证书ca.crt,服务器应该持有服务器证书server.crt和服务器秘钥server.key
双向认证时,客户端需要验证服务器是否是可信任的,而且服务器也要验证客户端是否是可信任的,客户端应该持有CA证书ca.crt和客户端证书client.crt以及客户端秘钥client.,服务器应该持有服务器证书server.crt和服务器秘钥server.key

socket + SSL/TLS 实现

服务器

python
  1. import socket, ssl, time, threading
  2. HOST = ''
  3. PORT = 3456
  4. BUFSIZE = 1024
  5. ADDR = (HOST,PORT)
  6. def receiveThread(connstream):
  7. count = 0
  8. while True:
  9. data = connstream.recv(BUFSIZE)
  10. if not data:
  11. print("client disconnect")
  12. break;
  13. count+=1
  14. print(count," receive from data:",data.decode("utf-8"))
  15. connstream.send(data)
  16. print("ack complete")
  17. connstream.shutdown(socket.SHUT_RDWR)
  18. connstream.close()
  19. # main
  20. context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
  21. context.load_cert_chain(certfile="server.crt", keyfile="server.key")
  22. bindsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  23. bindsocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  24. bindsocket.bind(ADDR)
  25. bindsocket.listen(5)
  26. while True:
  27. newsocket,addr = bindsocket.accept()
  28. try:
  29. connstream = context.wrap_socket(newsocket, server_side=True)
  30. except Exception as e:
  31. print("SSL connect fail,cause:")
  32. print(e)
  33. continue
  34. print("hello client,ip:")
  35. print(addr)
  36. t = threading.Thread(target=receiveThread,args=(connstream,))
  37. t.setDaemon(True)
  38. t.start()

客户端

python
  1. import socket, ssl, pprint,time
  2. HOST = '127.0.0.1'
  3. PORT = 3456
  4. BUFSIZE = 1024
  5. ADDR = (HOST,PORT)
  6. #socket create success
  7. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  8. # require a certificate from the server
  9. # single authentication (单向验证)
  10. # ssl_sock = ssl.wrap_socket(s,ca_certs="ca.crt",cert_reqs=ssl.CERT_REQUIRED)
  11. # mutual authentication(双向验证)
  12. ssl_sock = ssl.wrap_socket(s,ca_certs="ca.crt", certfile="client.crt", keyfile="client.key", cert_reqs=ssl.CERT_REQUIRED)
  13. #socket connect success
  14. ssl_sock.connect(ADDR)
  15. # note that closing the SSLSocket will also close the underlying socket
  16. pprint.pprint(ssl_sock.getpeercert())
  17. count = 0
  18. while True:
  19. data = "hello this message from client"
  20. if not data:
  21. break
  22. count+=1
  23. print(count," send data:",data)
  24. ssl_sock.send(data.encode("utf-8"))
  25. data=ssl_sock.recv(BUFSIZE)
  26. if not data:
  27. break
  28. print("received data:",data.decode("utf-8"))
  29. time.sleep(3)
  30. ssl_sock.close()

C ( linux )

先安装opennssl和libssl
编译使用

  1. gcc -o ssl_client.o ssl_client.c -lssl -lcrypto
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <sys/un.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <openssl/ssl.h>
  10. #include <openssl/err.h>
  11. #define SSL_CLIENT_RSA_CERT "client.crt"
  12. #define SSL_CLIENT_RSA_KEY "client.key"
  13. #define SSL_CLIENT_RSA_CA_CERT "ca.crt"
  14. #define SSL_SERVER_ADDR "127.0.0.1"
  15. #define SSL_SERVER_PORT 3456
  16. #define OFF 0
  17. #define ON 1
  18. int main(void)
  19. {
  20. int verify_peer = ON;
  21. int verify_mutual = ON;
  22. SSL_METHOD *client_meth;
  23. SSL_CTX *ssl_client_ctx;
  24. int clientsocketfd;
  25. // struct sockaddr_un serveraddr;
  26. struct sockaddr_in servaddr;
  27. int handshakestatus;
  28. SSL *clientssl;
  29. char buffer[1024] = "Client Hello World";
  30. int ret;
  31. SSL_library_init();
  32. OpenSSL_add_all_algorithms();
  33. SSL_load_error_strings();
  34. client_meth = (SSL_METHOD*)SSLv23_client_method();
  35. ssl_client_ctx = SSL_CTX_new(client_meth);
  36. if(!ssl_client_ctx)
  37. {
  38. printf("ctx create fail\n");
  39. ERR_print_errors_fp(stderr);
  40. return -1;
  41. }
  42. if(verify_peer)
  43. {
  44. if(verify_mutual)
  45. {
  46. if(SSL_CTX_use_certificate_file(ssl_client_ctx, SSL_CLIENT_RSA_CERT, SSL_FILETYPE_PEM) <= 0)
  47. {
  48. ERR_print_errors_fp(stderr);
  49. return -1;
  50. }
  51. if(SSL_CTX_use_PrivateKey_file(ssl_client_ctx, SSL_CLIENT_RSA_KEY, SSL_FILETYPE_PEM) <= 0)
  52. {
  53. ERR_print_errors_fp(stderr);
  54. return -1;
  55. }
  56. if(SSL_CTX_check_private_key(ssl_client_ctx) != 1)
  57. {
  58. printf("Private and certificate is not matching\n");
  59. return -1;
  60. }
  61. }
  62. // See function man pages for instructions on generating CERT files
  63. if(!SSL_CTX_load_verify_locations(ssl_client_ctx, SSL_CLIENT_RSA_CA_CERT, NULL))
  64. {
  65. ERR_print_errors_fp(stderr);
  66. return -1;
  67. }
  68. SSL_CTX_set_verify(ssl_client_ctx, SSL_VERIFY_PEER, NULL);
  69. SSL_CTX_set_verify_depth(ssl_client_ctx, 1);
  70. }
  71. printf("new socket\n");
  72. if((clientsocketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  73. {
  74. printf("Error on socket creation\n");
  75. return -1;
  76. }
  77. // memset(&serveraddr, 0, sizeof(struct sockaddr_un));
  78. // serveraddr.sun_family = AF_INET;
  79. // serveraddr.sun_path[0] = 0;
  80. // strncpy(&(serveraddr.sun_path[1]), SSL_SERVER_ADDR, strlen(SSL_SERVER_ADDR) + 1);
  81. memset(&servaddr, 0, sizeof(servaddr));
  82. servaddr.sin_family = AF_INET;
  83. servaddr.sin_port = htons(SSL_SERVER_PORT);
  84. if( inet_pton(AF_INET, SSL_SERVER_ADDR, &servaddr.sin_addr) <= 0)
  85. {
  86. printf("inet_pton error for %s\n",SSL_SERVER_ADDR);
  87. return -1;
  88. }
  89. printf("connect now\n");
  90. if(connect(clientsocketfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_un)) < 0)
  91. {
  92. printf("connect error:%s,%d",strerror(errno),errno);
  93. return -1;
  94. }
  95. printf("connect socket success\n");
  96. clientssl = SSL_new(ssl_client_ctx);
  97. if(!clientssl)
  98. {
  99. printf("Error SSL_new\n");
  100. return -1;
  101. }
  102. SSL_set_fd(clientssl, clientsocketfd);
  103. ERR_print_errors_fp(stderr);
  104. printf("ssl new success\n");
  105. if((ret = SSL_connect(clientssl)) != 1)
  106. {
  107. printf("0000\n");
  108. printf("Handshake Error %d\n", SSL_get_error(clientssl, ret));
  109. ERR_print_errors_fp(stderr);
  110. return -1;
  111. }
  112. printf("ssl connect success\n");
  113. if(verify_peer)
  114. {
  115. X509 *ssl_client_cert = NULL;
  116. char* line;
  117. ssl_client_cert = SSL_get_peer_certificate(clientssl);
  118. if(ssl_client_cert)
  119. {
  120. long verifyresult;
  121. printf("Server certificates:\n");
  122. line = X509_NAME_oneline(X509_get_subject_name(ssl_client_cert), 0, 0);
  123. printf("Subject: %s\n", line);
  124. free(line); /* free the malloc'ed string */
  125. line = X509_NAME_oneline(X509_get_issuer_name(ssl_client_cert), 0, 0);
  126. printf("Issuer: %s\n", line);
  127. free(line); /* free the malloc'ed string */
  128. verifyresult = SSL_get_verify_result(clientssl);
  129. if(verifyresult == X509_V_OK)
  130. printf("Certificate Verify Success\n");
  131. else
  132. printf("Certificate Verify Failed\n");
  133. X509_free(ssl_client_cert);
  134. }
  135. else
  136. printf("There is no client certificate\n");
  137. }
  138. printf("Send to SSL server:%s\n", buffer);
  139. SSL_write(clientssl, buffer, strlen(buffer) + 1);
  140. SSL_read(clientssl, buffer, sizeof(buffer));
  141. printf("Received from SSL server:%s\n", buffer);
  142. SSL_shutdown(clientssl);
  143. close(clientsocketfd);
  144. SSL_free(clientssl);
  145. SSL_CTX_free(ssl_client_ctx);
  146. }

C (mbedtls)

下载mbedtls
然后使用ssl_client1.c(需要修改服务器地址端口和ca证书)和ssl_client2.c(需要修改服务器地址、端口、ca证书、客户端证书、客户端秘钥)进行验证

其它

测试HTTPS延迟

  1. curl -w "TCP handshake: %{time_connect}, SSL handshake: %{time_appconnect}\n" -so /dev/null https://www.alipay.com

上面命令中的w参数表示指定输出格式,time_connect变量表示TCP握手的耗时,time_appconnect变量表示SSL握手的耗时(更多变量请查看文档实例),s参数和o参数用来关闭标准输出。

参考

文章有误?有想法想讨论?查看或者发起勘误/讨论 主题
(发起评论需要先登录 github)

/wallpaper/wallhaven-rdyewm.jpg