SSL/TLS

SSL/TLS 基本概念

SSL和TLS的关系与区别

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

SSL/TLS 单向认证步骤

SSL/TLS双向认证步骤

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的信息,第二次是服务器的信息,第三次是客户端的信息

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

#!/bin/bash

echo ""
echo "========generate CA cert and key files========"
echo ""
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

echo ""
echo "========generate Server cert and key files========"
echo ""
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr

echo ""
echo "========generate Client cert and key files========"
echo ""
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr

echo ""
echo "========sign server cert with CA========"
echo ""
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
#openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

echo ""
echo "========sign client cert with CA========"
echo ""
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
#openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

# openssl s_server -accept 10001 -key server.key -cert server.crt
# openssl s_client -connect localhost:10001 -CAfile ca.crt

# openssl s_server -accept 10001 -key server.key -cert server.crt -Verify 5
# 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
import socket, ssl, time, threading
  
HOST = ''  
PORT = 3456  
BUFSIZE = 1024  
ADDR = (HOST,PORT)  

  
def receiveThread(connstream):
    count = 0
    while True:
        data = connstream.recv(BUFSIZE)  
        if not data:
            print("client disconnect")
            break;
        count+=1
        print(count,"  receive from data:",data.decode("utf-8"))
        connstream.send(data)
        print("ack complete")
    connstream.shutdown(socket.SHUT_RDWR)
    connstream.close()

# main

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)  
context.load_cert_chain(certfile="server.crt", keyfile="server.key")  

bindsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
bindsocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
bindsocket.bind(ADDR)  
bindsocket.listen(5)  

while True:
    newsocket,addr = bindsocket.accept()
    try:
        connstream = context.wrap_socket(newsocket, server_side=True)
    except Exception as e:
        print("SSL connect fail,cause:")
        print(e)
        continue
    print("hello client,ip:")
    print(addr)
    t = threading.Thread(target=receiveThread,args=(connstream,))
    t.setDaemon(True)
    t.start()

客户端

python
import socket, ssl, pprint,time  
  
HOST = '127.0.0.1'  
PORT = 3456  
BUFSIZE = 1024  
ADDR = (HOST,PORT)  
  
#socket create success  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
# require a certificate from the server

#   single authentication (单向验证)
# ssl_sock = ssl.wrap_socket(s,ca_certs="ca.crt",cert_reqs=ssl.CERT_REQUIRED)
#   mutual authentication(双向验证)
ssl_sock = ssl.wrap_socket(s,ca_certs="ca.crt", certfile="client.crt", keyfile="client.key", cert_reqs=ssl.CERT_REQUIRED)
#socket connect success  
ssl_sock.connect(ADDR)  
  
# note that closing the SSLSocket will also close the underlying socket  
pprint.pprint(ssl_sock.getpeercert())  
count = 0
while True:
    data = "hello this message from client"
    if not data:
        break
    count+=1
    print(count,"  send data:",data)
    ssl_sock.send(data.encode("utf-8"))
    data=ssl_sock.recv(BUFSIZE)
    if not data:
        break
    print("received data:",data.decode("utf-8"))
    time.sleep(3)
ssl_sock.close()

C ( linux )

先安装opennssl和libssl
编译使用

gcc -o ssl_client.o ssl_client.c -lssl -lcrypto
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#define SSL_CLIENT_RSA_CERT "client.crt"
#define SSL_CLIENT_RSA_KEY  "client.key"
#define SSL_CLIENT_RSA_CA_CERT  "ca.crt"

#define SSL_SERVER_ADDR     "127.0.0.1"
#define SSL_SERVER_PORT 3456

#define OFF 0
#define ON  1

int main(void)
{
    int verify_peer = ON;
    int verify_mutual = ON;
    SSL_METHOD *client_meth;
    SSL_CTX *ssl_client_ctx;
    int clientsocketfd;
    // struct sockaddr_un serveraddr;
    struct sockaddr_in    servaddr;
    int handshakestatus;
    SSL *clientssl;
    char buffer[1024] = "Client Hello World";
    int ret;
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    client_meth = (SSL_METHOD*)SSLv23_client_method();
    ssl_client_ctx = SSL_CTX_new(client_meth);
    if(!ssl_client_ctx)
    {
        printf("ctx create fail\n");
        ERR_print_errors_fp(stderr);
        return -1;
    }

    if(verify_peer)
    {   
        if(verify_mutual)
        {
            if(SSL_CTX_use_certificate_file(ssl_client_ctx, SSL_CLIENT_RSA_CERT, SSL_FILETYPE_PEM) <= 0)    
            {
                ERR_print_errors_fp(stderr);
                return -1;      
            }

        
            if(SSL_CTX_use_PrivateKey_file(ssl_client_ctx, SSL_CLIENT_RSA_KEY, SSL_FILETYPE_PEM) <= 0)  
            {
                ERR_print_errors_fp(stderr);
                return -1;      
            }
        
            if(SSL_CTX_check_private_key(ssl_client_ctx) != 1)
            {
                printf("Private and certificate is not matching\n");
                return -1;
            }
        }

        // See function man pages for instructions on generating CERT files
        if(!SSL_CTX_load_verify_locations(ssl_client_ctx, SSL_CLIENT_RSA_CA_CERT, NULL))
        {
            ERR_print_errors_fp(stderr);
            return -1;      
        }
        SSL_CTX_set_verify(ssl_client_ctx, SSL_VERIFY_PEER, NULL);
        SSL_CTX_set_verify_depth(ssl_client_ctx, 1);
    }
    printf("new socket\n");
    if((clientsocketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Error on socket creation\n");
        return -1;
    }
    // memset(&serveraddr, 0, sizeof(struct sockaddr_un));
    // serveraddr.sun_family = AF_INET;
    // serveraddr.sun_path[0] = 0;
    // strncpy(&(serveraddr.sun_path[1]), SSL_SERVER_ADDR, strlen(SSL_SERVER_ADDR) + 1);
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SSL_SERVER_PORT);
    if( inet_pton(AF_INET, SSL_SERVER_ADDR, &servaddr.sin_addr) <= 0)
    {
        printf("inet_pton error for %s\n",SSL_SERVER_ADDR);
        return -1;
    }

    printf("connect now\n");
    if(connect(clientsocketfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_un)) < 0)
    {
        printf("connect error:%s,%d",strerror(errno),errno);
        return -1;
    }
        
    printf("connect socket success\n");
    clientssl = SSL_new(ssl_client_ctx);
    if(!clientssl)
    {
        printf("Error SSL_new\n");
        return -1;
    }
    SSL_set_fd(clientssl, clientsocketfd);
    ERR_print_errors_fp(stderr);
    printf("ssl new success\n");
    if((ret = SSL_connect(clientssl)) != 1)
    {
        printf("0000\n");
        printf("Handshake Error %d\n", SSL_get_error(clientssl, ret));
        ERR_print_errors_fp(stderr);
        return -1;
    }
    printf("ssl connect success\n");
    if(verify_peer)
    {
        X509 *ssl_client_cert = NULL;
        char* line;

        ssl_client_cert = SSL_get_peer_certificate(clientssl);
            
        if(ssl_client_cert)
        {
            long verifyresult;
            printf("Server certificates:\n");
            line = X509_NAME_oneline(X509_get_subject_name(ssl_client_cert), 0, 0);
            printf("Subject: %s\n", line);
            free(line); /* free the malloc'ed string */
            line = X509_NAME_oneline(X509_get_issuer_name(ssl_client_cert), 0, 0);
            printf("Issuer: %s\n", line);
            free(line);      /* free the malloc'ed string */
            verifyresult = SSL_get_verify_result(clientssl);
            if(verifyresult == X509_V_OK)
                printf("Certificate Verify Success\n"); 
            else
                printf("Certificate Verify Failed\n");
            X509_free(ssl_client_cert);             
        }
        else
            printf("There is no client certificate\n");
    }
    printf("Send to SSL server:%s\n", buffer);
    SSL_write(clientssl, buffer, strlen(buffer) + 1);
    SSL_read(clientssl, buffer, sizeof(buffer));
    printf("Received from SSL server:%s\n", buffer);
    SSL_shutdown(clientssl);
    close(clientsocketfd);
    SSL_free(clientssl);
    SSL_CTX_free(ssl_client_ctx);
}

C (mbedtls)

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

其它

测试HTTPS延迟

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参数用来关闭标准输出。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350