如何做一个用于测试SSL版本的客户端
最近由于工作的需要,需要升级建链过程中SSL的版本,因此有了这篇博文。
科普:
版本排序,从小到大:SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2
SSL_CTX_new:creates a new SSL_CTX object as framework to establish TLS/SSL enabled connections.
#include <openssl/ssl.h> SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);
其中,要想知道 SSL_METHOD有多少个可以用的方法,参见:http://openssl.cs.utah.edu/docs/ssl/SSL_CTX_new.html
SSL_CTX_set_options()
or SSL_set_options()
:该方法是用来禁止使用什么协议来建链的。其中有以下选项可以填写:
SSL_OP_NO_SSLv2, SSL_OP_NO_SSLv3, SSL_OP_NO_TLSv1, SSL_OP_NO_TLSv1_1 and SSL_OP_NO_TLSv1_2
通过字面意思,我们可以知道其用法。
Demo
1 #include <sys/socket.h> 2 #include <resolv.h> 3 #include <netdb.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <string.h> 7 8 #include <openssl/bio.h> 9 #include <openssl/ssl.h> 10 #include <openssl/err.h> 11 #include <openssl/pem.h> 12 #include <openssl/x509.h> 13 #include <openssl/x509_vfy.h> 14 15 int create_socket(char[], BIO *); 16 17 int main() { 18 19 char dest_url[] = "https://www.baidu.com"; 20 BIO *certbio = NULL; 21 BIO *outbio = NULL; 22 X509 *cert = NULL; 23 X509_NAME *certname = NULL; 24 const SSL_METHOD *method; 25 SSL_CTX *ctx; 26 SSL *ssl; 27 int server = 0; 28 int ret, i; 29 30 /* ---------------------------------------------------------- * 31 * 初始化OpenSSL * 32 * ---------------------------------------------------------- */ 33 OpenSSL_add_all_algorithms(); 34 ERR_load_BIO_strings(); 35 ERR_load_crypto_strings(); 36 SSL_load_error_strings(); 37 38 /* ---------------------------------------------------------- * 39 * 创建一个BIO的输入和输出. * 40 * ---------------------------------------------------------- */ 41 certbio = BIO_new(BIO_s_file()); 42 outbio = BIO_new_fp(stdout, BIO_NOCLOSE); 43 44 /* ---------------------------------------------------------- * 45 * 初始化SSL库和注册算法 * 46 * ---------------------------------------------------------- */ 47 if (SSL_library_init() < 0) 48 BIO_printf(outbio, "Could not initialize the OpenSSL library !\n"); 49 50 /* ---------------------------------------------------------- * 51 * 发送SSL2 SSL3 TLS 1.0 TLS 2.0 TLS 3.0 与服务器建链 * 52 * ---------------------------------------------------------- */ 53 method = SSLv23_client_method(); 54 55 /* ---------------------------------------------------------- * 56 * 创建一个新的SSL上下文 * 57 * ---------------------------------------------------------- */ 58 if ((ctx = SSL_CTX_new(method)) == NULL) 59 BIO_printf(outbio, "Unable to create a new SSL context structure.\n"); 60 61 /* ---------------------------------------------------------- * 62 * 禁止使用SSLv3建链 * 63 * ---------------------------------------------------------- */ 64 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); 65 66 /* ---------------------------------------------------------- * 67 * 创建一个新的SSL的状态 * 68 * ---------------------------------------------------------- */ 69 ssl = SSL_new(ctx); 70 71 /* ---------------------------------------------------------- * 72 * 创建一个优先的SSL TCP 链接 * 73 * ---------------------------------------------------------- */ 74 server = create_socket(dest_url, outbio); 75 if (server != 0) 76 BIO_printf(outbio, "Successfully made the TCP connection to: %s.\n", 77 dest_url); 78 79 /* ---------------------------------------------------------- * 80 * 关联socket的会话 * 81 * ---------------------------------------------------------- */ 82 SSL_set_fd(ssl, server); 83 84 /* ---------------------------------------------------------- * 85 * 建立SSL 链接,返回1成功 * 86 * ---------------------------------------------------------- */ 87 if (SSL_connect(ssl) != 1) 88 BIO_printf(outbio, "Error: Could not build a SSL session to: %s.\n", 89 dest_url); 90 else 91 BIO_printf(outbio, "Successfully enabled SSL/TLS session to: %s.\n", 92 dest_url); 93 94 /* ---------------------------------------------------------- * 95 * 得到服务端的证书到X509结构体 * 96 * ---------------------------------------------------------- */ 97 cert = SSL_get_peer_certificate(ssl); 98 if (cert == NULL) 99 BIO_printf(outbio, "Error: Could not get a certificate from: %s.\n", 100 dest_url); 101 else 102 BIO_printf(outbio, "Retrieved the server's certificate from: %s.\n", 103 dest_url); 104 105 /* ---------------------------------------------------------- * 106 * 提取各种证书信息 * 107 * -----------------------------------------------------------*/ 108 certname = X509_NAME_new(); 109 certname = X509_get_subject_name(cert); 110 111 /* ---------------------------------------------------------- * 112 * 这里显示证书的主题 * 113 * -----------------------------------------------------------*/ 114 BIO_printf(outbio, "Displaying the certificate subject data:\n"); 115 X509_NAME_print_ex(outbio, certname, 0, 0); 116 BIO_printf(outbio, "\n"); 117 118 /* ---------------------------------------------------------- * 119 * 释放不再使用的结构体 * 120 * -----------------------------------------------------------*/ 121 SSL_free(ssl); 122 close(server); 123 X509_free(cert); 124 SSL_CTX_free(ctx); 125 BIO_printf(outbio, "Finished SSL/TLS connection with server: %s.\n", 126 dest_url); 127 return (0); 128 } 129 130 /* ---------------------------------------------------------- * 131 * create_socket() 和服务端建立TCP的socket链接 * 132 * ---------------------------------------------------------- */ 133 int create_socket(char url_str[], BIO *out) { 134 int sockfd; 135 char hostname[256] = ""; 136 char portnum[6] = "443"; 137 char proto[6] = ""; 138 char *tmp_ptr = NULL; 139 int port; 140 struct hostent *host; 141 struct sockaddr_in dest_addr; 142 143 /* ---------------------------------------------------------- * 144 * 移除url_str的末尾 / * 145 * ---------------------------------------------------------- */ 146 if (url_str[strlen(url_str)] == '/') 147 url_str[strlen(url_str)] = '\0'; 148 149 /* ---------------------------------------------------------- * 150 * the first : ends the protocol string, i.e. http * 151 * ---------------------------------------------------------- */ 152 strncpy(proto, url_str, (strchr(url_str, ':') - url_str)); 153 154 /* ---------------------------------------------------------- * 155 * the hostname starts after the "://" part * 156 * ---------------------------------------------------------- */ 157 strncpy(hostname, strstr(url_str, "://") + 3, sizeof(hostname)); 158 159 /* ---------------------------------------------------------- * 160 * if the hostname contains a colon :, we got a port number * 161 * ---------------------------------------------------------- */ 162 if (strchr(hostname, ':')) { 163 tmp_ptr = strchr(hostname, ':'); 164 /* the last : starts the port number, if avail, i.e. 8443 */ 165 strncpy(portnum, tmp_ptr + 1, sizeof(portnum)); 166 *tmp_ptr = '\0'; 167 } 168 169 port = atoi(portnum); 170 171 if ((host = gethostbyname(hostname)) == NULL) { 172 BIO_printf(out, "Error: Cannot resolve hostname %s.\n", hostname); 173 abort(); 174 } 175 176 /* ---------------------------------------------------------- * 177 * create the basic TCP socket * 178 * ---------------------------------------------------------- */ 179 sockfd = socket(AF_INET, SOCK_STREAM, 0); 180 181 dest_addr.sin_family = AF_INET; 182 dest_addr.sin_port = htons(port); 183 dest_addr.sin_addr.s_addr = *(long*) (host->h_addr); 184 185 /* ---------------------------------------------------------- * 186 * Zeroing the rest of the struct * 187 * ---------------------------------------------------------- */ 188 memset(&(dest_addr.sin_zero), '\0', 8); 189 190 tmp_ptr = inet_ntoa(dest_addr.sin_addr); 191 192 /* ---------------------------------------------------------- * 193 * Try to make the host connect here * 194 * ---------------------------------------------------------- */ 195 if (connect(sockfd, (struct sockaddr *) &dest_addr, sizeof(struct sockaddr)) 196 == -1) { 197 BIO_printf(out, "Error: Cannot connect to host %s [%s] on port %d.\n", 198 hostname, tmp_ptr, port); 199 } 200 201 return sockfd; 202 }