OpenSSL入门-SSL-TLS编程实战
入门
用SSL_library_init
、OpenSSL_add_ssl_algorithms
或SSLeay_add_ssl_algorithms
初始化SSL算法库,调用SSL系列函数前必须先调用一个:
1 2 3
| int SSL_library_init(); #define OpenSSL_add_ssl_algorithms() SSL_library_init() #define SSLeay_add_ssl_algorithms() SSL_library_init()
|
用SSL_CTX_new
和SSL_CTX_free
分别初始化和释放SSL上下文环境变量。
1 2 3 4 5 6
| SSL_CTX* SSL_CTX_new( SSL_METHOD* meth ); void SSL_CTX_free( SSL_CTX* ctx );
|
其中参数meth可以是:
1 2 3 4 5 6 7 8
| SSLv2_server_method(); SSLv2_client_method(); SSLv3_server_method(); SSLv3_client_method(); SSLv23_server_method(); SSLv23_client_method(); TLSv1_server_method(); TLSv1_client_method();
|
用SSL_CTX_use_certificate_file
和SSL_CTX_use_certificate
分别以文件形式和结构体方式设置SSL证书:
1 2 3 4 5 6 7 8 9
| int SSL_CTX_use_certificate_file( SSL_CTX* ctx, const char* file, int type ); int SSL_CTX_use_certificate( SSL_CTX* ctx, X509* x );
|
type取值可以是SSL_FILETYPE_PEM或SSL_FILETYPE_ASN1。
用SSL_CTX_use_PrivateKey_file
和SSL_CTX_use_PrivateKey
分别以文件形式和结构体方式设置SSL私钥。
1 2 3 4 5 6 7 8 9
| int SSL_CTX_use_PrivateKey_file( SSL_CTX* ctx, const char* file, int type ); int SSL_CTX_use_PrivateKey( SSL_CTX* ctx, EVP_PKEY* pkey );
|
用SSL_CTX_check_private_key
检查SSL私钥和证书是否匹配。
1 2 3
| int SSL_CTX_check_private_key( const SSL_CTX* ctx );
|
用SSL_new
和SSL_free
分别创建和释放SSL套接字结构体:
1 2 3 4 5 6
| SSL* SSL_new( SSL_CTX* ctx ); void SSL_free( SSL* ssl );
|
用SSL_set_fd
、SSL_set_rfd
和SSL_set_wfd
分别设置读写、只读和只写套接字:
1 2 3 4 5 6 7 8 9 10 11 12
| int SSL_set_fd( SSL* s, int fd ); int SSL_set_rfd( SSL* s, int fd ); int SSL_set_wfd( SSL* s, int fd );
|
用SSL_connect
启动TLS/SSL握手,用SSL_accept
接受SSL连接,后者失败可用SSL_get_error
找出原因。
1 2 3 4 5 6
| int SSL_conenct( SSL* ssl ); int SSL_accept( SSL* ssl );
|
用SSL_get_peer_certificate
获取对方X509证书:
1 2 3
| X509* SSL_get_peer_certificate( const SSL* ssl );
|
用SSL_write
项TLS/SSL连接写数据,用SSL_read
从TLS/SSL连接中读取数据:
1 2 3 4 5 6 7 8 9 10
| int SSL_write( SSL* ssl, const void* buf, int num ); int SSL_read( SSL* ssl, void* buf, int num );
|
环境搭建
原理同PKI,假设目前工作目录在Windows的OpenSSL下,配置文件为/bin/openssl.cfg。然后准备好index.txt和serial文件,之后在CA主机上创建根CA证书:
1 2 3
| openssl genrsa -des3 -out root.key 1024 openssl req -new -key root.key -out root.csr openssl req -new -x509 -key root.key -out root.crt
|
在服务端生成证书请求文件:
1 2
| openssl genrsa -des3 -out server.key 1024 openssl req -new -key server.key -out server.csr
|
在CA主机上签发服务端证书:
1
| openssl ca -in server.csr -out server.crt -keyfile root.key -cert root.crt -days 365 -config ./openssl.cfg
|
在客户端生成证书请求文件:
1 2
| openssl genrsa -des3 -out client.key 1024 openssl req -new -key client.key -out client.csr
|
在CA上签发客户端证书:
1
| openssl ca -in client.csr -out client.crt -keyfile root.key -cert root.crt -days 365 -config ./openssl.cfg
|
实战
服务端实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <errno.h> #include <sys/types.h> #include <winsock2.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" extern "C" { #include <openssl/applink.c> };
#define CERTF "server.crt" #define KEYF "server.key" #define CACERT "root.crt" #define PORT 1111 #define CHK_NULL(x) if ((x)==NULL) exit (1) #define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); } #define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); } int main() { int err; int listen_sd; int sd; struct sockaddr_in sa_serv; struct sockaddr_in sa_cli; int client_len; SSL_CTX* ctx; SSL* ssl; X509* client_cert; char* str; char buf[4096]; const SSL_METHOD* meth; WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup()fail:%d\n", GetLastError()); return -1; } SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); meth = TLSv1_server_method(); ctx = SSL_CTX_new(meth); CHK_NULL(ctx); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 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); exit(3); } if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(4); } if (!SSL_CTX_check_private_key(ctx)) { printf("Private key does not match the certificate public key\n"); exit(5); } SSL_CTX_set_cipher_list(ctx, "RC4-MD5"); printf("I am ssl-server\n"); listen_sd = socket(AF_INET, SOCK_STREAM, 0); CHK_ERR(listen_sd, "socket"); 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); err = bind(listen_sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv)); CHK_ERR(err, "bind"); err = listen(listen_sd, 5); CHK_ERR(err, "listen"); client_len = sizeof(sa_cli); sd = accept(listen_sd, (struct sockaddr*)&sa_cli, &client_len); CHK_ERR(sd, "accept"); closesocket(listen_sd); printf("Connection from %lx, port %x\n", sa_cli.sin_addr.s_addr, sa_cli.sin_port); printf("Begin server side SSL\n"); ssl = SSL_new(ctx); CHK_NULL(ssl); SSL_set_fd(ssl, sd); err = SSL_accept(ssl); printf("SSL_accept finished\n"); CHK_SSL(err); printf("SSL connection using %s\n", SSL_get_cipher(ssl)); client_cert = SSL_get_peer_certificate(ssl); if (client_cert != NULL) { printf("Client certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0); CHK_NULL(str); printf("\t subject: %s\n", str); OPENSSL_free(str); str = X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0); CHK_NULL(str); printf("\t issuer: %s\n", str); OPENSSL_free(str); X509_free(client_cert); } else printf("Client does not have certificate.\n"); err = SSL_read(ssl, buf, sizeof(buf) - 1); CHK_SSL(err); buf[err] = '\0'; printf("Got %d chars:'%s'\n", err, buf); err = SSL_write(ssl, "I hear you.", strlen("I hear you.")); CHK_SSL(err); shutdown(sd, 2); SSL_free(ssl); SSL_CTX_free(ctx); return 0; }
|
客户端实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <errno.h> #include <sys/types.h> #include <winsock2.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" extern "C" { #include <openssl/applink.c> };
#define CERTF "client.crt" #define KEYF "client.key" #define CACERT "root.crt" #define PORT 1111 #define SERVER_ADDR "127.0.0.1" #define CHK_NULL(x) if ((x)==NULL) exit (-1) #define CHK_ERR(err,s) if ((err)==-1) { perror(s); exit(-2); } #define CHK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(-3); } int main() { int err; int sd; struct sockaddr_in sa; SSL_CTX* ctx; SSL* ssl; X509* server_cert; char* str; char buf[4096]; const SSL_METHOD* meth; int seed_int[100]; WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { printf("WSAStartup()fail:%d\n", GetLastError()); return -1; } OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); meth = TLSv1_client_method(); ctx = SSL_CTX_new(meth); CHK_NULL(ctx); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 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); exit(-2); } if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(-3); } if (!SSL_CTX_check_private_key(ctx)) { printf("Private key does not match the certificate public key\n"); exit(-4); } srand((unsigned)time(NULL)); for (int i = 0; i < 100; i++) seed_int[i] = rand(); RAND_seed(seed_int, sizeof(seed_int)); printf("I am ssl-client\n"); sd = socket(AF_INET, SOCK_STREAM, 0); CHK_ERR(sd, "socket"); memset(&sa, '\0', sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = inet_addr(SERVER_ADDR); sa.sin_port = htons(PORT); err = connect(sd, (struct sockaddr*)&sa, sizeof(sa)); CHK_ERR(err, "connect"); printf("Begin SSL negotiation \n"); ssl = SSL_new(ctx); CHK_NULL(ssl); SSL_set_fd(ssl, sd); err = SSL_connect(ssl); CHK_SSL(err); printf("SSL connection using %s\n", SSL_get_cipher(ssl)); server_cert = SSL_get_peer_certificate(ssl); CHK_NULL(server_cert); printf("Server certificate:\n"); str = X509_NAME_oneline(X509_get_subject_name(server_cert), 0, 0); CHK_NULL(str); printf("\t subject: %s\n", str); OPENSSL_free(str); str = X509_NAME_oneline(X509_get_issuer_name(server_cert), 0, 0); CHK_NULL(str); printf("\t issuer: %s\n", str); OPENSSL_free(str); X509_free(server_cert); printf("Begin SSL data exchange\n"); err = SSL_write(ssl, "Hello World!", strlen("Hello World!")); CHK_SSL(err); err = SSL_read(ssl, buf, sizeof(buf) - 1); CHK_SSL(err); buf[err] = '\0'; printf("Got %d chars:'%s'\n", err, buf); SSL_shutdown(ssl); shutdown(sd, 2); SSL_free(ssl); SSL_CTX_free(ctx); return 0; }
|