OpenSSL简介
对于OpenSSL而言,所有的XX_method都是通过类似这样的接口来完成对象的实例化、抽象回调函数的初始化等一些列的操作。如下为源码分析:
IMPLEMENT_dtls1_meth_func(DTLS_ANY_VERSION,
DTLS_server_method,
dtls1_accept,
ssl_undefined_function,
dtls1_get_server_method, DTLSv1_2_enc_data)
如上宏完成DTLS_server_method函数对应的对象实例化,并将一系列的回调函数以函数指针的方式注册到该对象中。
# define IMPLEMENT_dtls1_meth_func(version, func_name, s_accept, s_connect, \
s_get_meth, enc_data) \
const SSL_METHOD *func_name(void) \
{ \
static const SSL_METHOD func_name##_data= { \
version, \
dtls1_new, \
dtls1_clear, \
dtls1_free, \
s_accept, \ //该回调完成握手操作
s_connect, \ //该回调发起主动握手操作(ClientHello请求)
ssl3_read, \
ssl3_peek, \
ssl3_write, \
dtls1_shutdown, \
ssl3_renegotiate, \
ssl3_renegotiate_check, \
dtls1_get_message, \
dtls1_read_bytes, \
dtls1_write_app_data_bytes, \
dtls1_dispatch_alert, \
dtls1_ctrl, \
ssl3_ctx_ctrl, \
ssl3_get_cipher_by_char, \
ssl3_put_cipher_by_char, \
ssl3_pending, \
ssl3_num_ciphers, \
dtls1_get_cipher, \
s_get_meth, \
dtls1_default_timeout, \
&enc_data, \
ssl_undefined_void_function, \
ssl3_callback_ctrl, \
ssl3_ctx_callback_ctrl, \
}; \
ssl_log(SSL_LOG_VEB, "%s enter ...\n", __FUNCTION__); \
return &func_name##_data; \
}
OpenSSL初始化
#include <openssl.h>
static void openssl_log_format(int level, const char *file, int line, const char *msg)
{
int openssl_level = 0;
switch (level) {
case SSL_LOG_ERR:
openssl_level = OPENSSL_LOG_ERR;
break;
case SSL_LOG_WAR:
openssl_level = OPENSSL_LOG_WAR;
break;
case SSL_LOG_NOT:
openssl_level = OPENSSL_LOG_NOT;
break;
case SSL_LOG_DEB:
openssl_level = OPENSSL_LOG_DEB;
break;
case SSL_LOG_VEB:
openssl_level = OPENSSL_LOG_VEB;
break;
default:
openssl_level = OPENSSL_LOG_DEB;
break;
}
openssl_log(openssl_level, "[%s:%04d] %s", file, line, msg);
}
void openssl_log_init(void)
{
ssl_set_logger_cb(openssl_log_format);
}
void openssl_log_vsprintf(int level, const char *file, int line, const char *format, ...)
{
char *log_buffer = NULL;
char *file_name = NULL;
char *level_str = NULL;
struct timeval tv_now;
time_t tt;
struct tm *t = NULL;
va_list ap;
va_start(ap, format);
int ret = vasprintf(&log_buffer, format, ap);
va_end(ap);
if (file) {
file_name = strrchr(file, '/');
}
switch (level) {
case OPENSSL_LOG_ERR:
level_str = "\033[31;1m ERROR\33[0m";
break;
case OPENSSL_LOG_WAR:
level_str = "\033[32;31;1mWARRING\33[0m";
break;
case OPENSSL_LOG_NOT:
level_str = "\033[33;1m NOTICE\33[0m";
break;
case OPENSSL_LOG_DEB:
level_str = "\033[32;1m DEBUG\33[0m";
break;
case OPENSSL_LOG_VEB:
level_str = "\033[32mVERBOSE\33[0m";
break;
default:
level_str = "\033[32;1m DEBUG\33[0m";
break;
}
tt = time(NULL);
t = localtime(&tt);
gettimeofday(&tv_now, NULL);
fprintf(stderr, "[%4d-%02d-%02d %02d:%02d:%02d:%03ld] %s [%05ld] -- %s:%d %s\n",
t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv_now.tv_usec,
level_str, syscall(SYS_gettid), file_name ? ++file_name : file, line, log_buffer);
if (log_buffer) {
free(log_buffer);
}
}
static void openssl_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
{
const char *str_write_p, *str_version, *str_content_type =
"", *str_details1 = "", *str_details2 = "";
char msg_buffer[65535] = {0};
int msg_len = 0;
str_write_p = write_p ? ">>>" : "<<<";
switch (version) {
case SSL2_VERSION:
str_version = "SSL 2.0";
break;
case SSL3_VERSION:
str_version = "SSL 3.0 ";
break;
case TLS1_VERSION:
str_version = "TLS 1.0 ";
break;
case TLS1_1_VERSION:
str_version = "TLS 1.1 ";
break;
case TLS1_2_VERSION:
str_version = "TLS 1.2 ";
break;
case DTLS1_VERSION:
str_version = "DTLS 1.0 ";
break;
case DTLS1_BAD_VER:
str_version = "DTLS 1.0 (bad) ";
break;
default:
str_version = "???";
}
if (version == SSL2_VERSION) {
str_details1 = "???";
if (len > 0) {
switch (((const unsigned char *)buf)[0]) {
case 0:
str_details1 = ", ERROR:";
str_details2 = " ???";
if (len >= 3) {
unsigned err =
(((const unsigned char *)buf)[1] << 8) +
((const unsigned char *)buf)[2];
switch (err) {
case 0x0001:
str_details2 = " NO-CIPHER-ERROR";
break;
case 0x0002:
str_details2 = " NO-CERTIFICATE-ERROR";
break;
case 0x0004:
str_details2 = " BAD-CERTIFICATE-ERROR";
break;
case 0x0006:
str_details2 = " UNSUPPORTED-CERTIFICATE-TYPE-ERROR";
break;
}
}
break;
case 1:
str_details1 = ", CLIENT-HELLO";
break;
case 2:
str_details1 = ", CLIENT-MASTER-KEY";
break;
case 3:
str_details1 = ", CLIENT-FINISHED";
break;
case 4:
str_details1 = ", SERVER-HELLO";
break;
case 5:
str_details1 = ", SERVER-VERIFY";
break;
case 6:
str_details1 = ", SERVER-FINISHED";
break;
case 7:
str_details1 = ", REQUEST-CERTIFICATE";
break;
case 8:
str_details1 = ", CLIENT-CERTIFICATE";
break;
}
}
}
if (version == SSL3_VERSION ||
version == TLS1_VERSION ||
version == TLS1_1_VERSION ||
version == TLS1_2_VERSION ||
version == DTLS1_VERSION ||
version == DTLS1_2_VERSION ||
version == DTLS1_BAD_VER) {
switch (content_type) {
case 20:
str_content_type = "ChangeCipherSpec";
break;
case 21:
str_content_type = "Alert";
break;
case 22:
str_content_type = "Handshake";
break;
}
if (content_type == 21) { /* Alert */
str_details1 = ", ???";
if (len == 2) {
switch (((const unsigned char *)buf)[0]) {
case 1:
str_details1 = ", warning";
break;
case 2:
str_details1 = ", fatal";
break;
}
str_details2 = " ???";
switch (((const unsigned char *)buf)[1]) {
case 0:
str_details2 = " close_notify";
break;
case 10:
str_details2 = " unexpected_message";
break;
case 20:
str_details2 = " bad_record_mac";
break;
case 21:
str_details2 = " decryption_failed";
break;
case 22:
str_details2 = " record_overflow";
break;
case 30:
str_details2 = " decompression_failure";
break;
case 40:
str_details2 = " handshake_failure";
break;
case 42:
str_details2 = " bad_certificate";
break;
case 43:
str_details2 = " unsupported_certificate";
break;
case 44:
str_details2 = " certificate_revoked";
break;
case 45:
str_details2 = " certificate_expired";
break;
case 46:
str_details2 = " certificate_unknown";
break;
case 47:
str_details2 = " illegal_parameter";
break;
case 48:
str_details2 = " unknown_ca";
break;
case 49:
str_details2 = " access_denied";
break;
case 50:
str_details2 = " decode_error";
break;
case 51:
str_details2 = " decrypt_error";
break;
case 60:
str_details2 = " export_restriction";
break;
case 70:
str_details2 = " protocol_version";
break;
case 71:
str_details2 = " insufficient_security";
break;
case 80:
str_details2 = " internal_error";
break;
case 90:
str_details2 = " user_canceled";
break;
case 100:
str_details2 = " no_renegotiation";
break;
case 110:
str_details2 = " unsupported_extension";
break;
case 111:
str_details2 = " certificate_unobtainable";
break;
case 112:
str_details2 = " unrecognized_name";
break;
case 113:
str_details2 = " bad_certificate_status_response";
break;
case 114:
str_details2 = " bad_certificate_hash_value";
break;
case 115:
str_details2 = " unknown_psk_identity";
break;
}
}
}
if (content_type == 22) { /* Handshake */
str_details1 = "???";
if (len > 0) {
switch (((const unsigned char *)buf)[0]) {
case 0:
str_details1 = ", HelloRequest";
break;
case 1:
str_details1 = ", ClientHello";
break;
case 2:
str_details1 = ", ServerHello";
break;
case 3:
str_details1 = ", HelloVerifyRequest";
break;
case 11:
str_details1 = ", Certificate";
break;
case 12:
str_details1 = ", ServerKeyExchange";
break;
case 13:
str_details1 = ", CertificateRequest";
break;
case 14:
str_details1 = ", ServerHelloDone";
break;
case 15:
str_details1 = ", CertificateVerify";
break;
case 16:
str_details1 = ", ClientKeyExchange";
break;
case 20:
str_details1 = ", Finished";
break;
}
}
}
#ifndef OPENSSL_NO_HEARTBEATS
if (content_type == 24) { /* Heartbeat */
str_details1 = ", Heartbeat";
if (len > 0) {
switch (((const unsigned char *)buf)[0]) {
case 1:
str_details1 = ", HeartbeatRequest";
break;
case 2:
str_details1 = ", HeartbeatResponse";
break;
}
}
}
#endif
}
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1,
"%s %s%s [length %04lx]%s%s\n", str_write_p, str_version,
str_content_type, (unsigned long)len, str_details1, str_details2);
if (len > 0) {
size_t num, i;
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", " ");
num = len;
for (i = 0; i < num; i++) {
if (i % 32 == 0 && i > 0) {
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", "\n ");
}
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, " %02x", ((const unsigned char *)buf)[i]);
}
if (i < len) {
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", " ...");
}
msg_len += snprintf(msg_buffer + msg_len, sizeof(msg_buffer) - msg_len - 1, "%s", "\n");
}
openssl_log(OPENSSL_LOG_DEB, "\n%s", msg_buffer);
}
int openssl_init(void)
{
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
}
#define SERVER_CA "ServerCAcert.pem"
#define SERVER_KEY "ServerPrivkey.pem"
#define CLIENT_CA "ClientCAcert.pem"
#define CLIENT_KEY "ClientPrivkey.pem"
int openssl_load_cert_file(SSL_CTX *ctx, int csopt)
{
if (csopt) {
if (SSL_CTX_use_certificate_file(ctx, OPENSSL_SERVER_CA_PATH "/" SERVER_CA, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(ctx, OPENSSL_SERVER_CA_PATH "/" SERVER_KEY, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
return -1;
}
} else {
if (SSL_CTX_use_certificate_file(ctx, OPENSSL_CLIENT_CA_PATH "/" CLIENT_CA, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
return -1;
}
if (SSL_CTX_use_PrivateKey_file(ctx, OPENSSL_CLIENT_CA_PATH "/" CLIENT_KEY, SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stdout);
return -1;
}
}
if (!SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stdout);
return -1;
}
return 0;
}
static void openssl_info_callback(const SSL *s, int where, int ret)
{
const char *str;
int w;
w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT) {
str = "SSL_connect";
} else if (w & SSL_ST_ACCEPT) {
str = "SSL_accept";
} else {
str = "undefined";
}
if (where & SSL_CB_LOOP) {
openssl_log(SSL_LOG_DEB, "%s: %s\n", str, SSL_state_string_long(s));
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
openssl_log(SSL_LOG_DEB, "SSL3 alert %s:%s:%s\n", str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
} else if (where & SSL_CB_EXIT) {
if (ret == 0) {
openssl_log(SSL_LOG_ERR, "%s:failed in %s\n", str, SSL_state_string_long(s));
} else if (ret < 0) {
openssl_log(SSL_LOG_ERR, "%s:error in %s\n", str, SSL_state_string_long(s));
}
}
}
SSL_CTX *openssl_ctx_new(const SSL_METHOD *method)
{
SSL_CTX *ctx = NULL;
ctx = SSL_CTX_new(method);
if (NULL == ctx) {
ERR_print_errors_fp(stdout);
return NULL;
}
SSL_CTX_set_info_callback(ctx, openssl_info_callback);
return ctx;
}
SSL *openssl_ssl_new(SSL_CTX *ctx)
{
SSL *ssl = NULL;
ssl = SSL_new(ctx);
if (NULL == ssl) {
return NULL;
}
SSL_set_msg_callback(ssl, openssl_msg_cb);
SSL_set_msg_callback_arg(ssl, NULL);
return ssl;
}
int openssl_set_fd(SSL *ssl, int sockfd)
{
return SSL_set_fd(ssl, sockfd);
}
int openssl_accept(SSL *ssl)
{
return SSL_accept(ssl);
}
Socket初始化
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <inet_sock.h>
#include <openssl.h>
int init_nonblock(int fd)
{
int flags = -1;
if(flags = fcntl(fd, F_GETFL, 0) < 0) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
flags |= O_NONBLOCK;
if(fcntl(fd, F_SETFL, flags) < 0) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
return 0;
}
int init_sock(inet_sock_t sock, int type)
{
return socket(sock, type, 0);
}
int init_sockaddr(struct sockaddr *sockaddr, inet_sock_t sock, int sockfd, int utopt, int csopt)
{
struct sockaddr_in addr;
int on = 1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = sock;
if (csopt) {
addr.sin_port = htons(7838);
addr.sin_addr.s_addr = INADDR_ANY;
} else {
addr.sin_port = htons(7838);
if (!inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr.s_addr)) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
}
if (sockaddr) {
memcpy(sockaddr, &addr, sizeof(addr));
}
if (csopt) {
if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
openssl_log(OPENSSL_LOG_NOT, "Bind sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
if (utopt) {
if (listen(sockfd, 100)) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
} else {
if (!csopt) {
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr))) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
openssl_log(OPENSSL_LOG_NOT, "Connect sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
}
return 0;
}
int init_udp_sockaddr(struct sockaddr *sockaddr, inet_sock_t sock, int sockfd, int csopt)
{
struct sockaddr_in addr;
int on = 1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = sock;
if (csopt) {
addr.sin_port = htons(7838);
addr.sin_addr.s_addr = INADDR_ANY;
} else {
addr.sin_port = htons(7838);
if (!inet_aton("127.0.0.1", (struct in_addr *)&addr.sin_addr.s_addr)) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
}
if (sockaddr) {
memcpy(sockaddr, &addr, sizeof(addr));
}
if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
if (csopt) {
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
return -1;
}
openssl_log(OPENSSL_LOG_NOT, "Bind sockaddr: %s:%d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
}
return 0;
}
int init_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
return accept(sockfd, addr, addrlen);
}-
OpenSSL DTLS Client端
#include <openssl.h>
#include <inet_sock.h>
int main(int argc, char **argv)
{
SSL_CTX * ctx = NULL;
int sockfd = -1;
socklen_t socklen = 0;
struct sockaddr_in their_addr;
struct sockaddr peer;
SSL *ssl = NULL;
int retval = -1;
char readbuf[65535] = {0};
BIO *sbio = NULL;
int try = 0;
openssl_log_init();
openssl_init();
ctx = openssl_ctx_new(DTLS_method());
if (NULL == ctx) {
goto error;
}
if (openssl_load_cert_file(ctx, 0)) {
goto error;
}
sockfd = init_sock(SOCK_AF_INET, SOCK_DGRAM);
if (sockfd < 0) {
goto error;
}
if (init_sockaddr((struct sockaddr *)&their_addr, SOCK_AF_INET, sockfd, 0, 0)) {
goto error;
}
ssl = openssl_ssl_new(ctx);
if (NULL == ssl) {
goto error;
}
sbio = BIO_new_dgram(sockfd, BIO_NOCLOSE);
if (NULL == sbio) {
goto error;
}
BIO_ctrl_set_connected(sbio, 1, &peer);
SSL_set_bio(ssl, sbio, sbio);
SSL_set_connect_state(ssl);
while (try++ < 10) {
snprintf(readbuf, sizeof(readbuf) - 1, "%s", "This is a DTLS client!\n");
retval = SSL_write(ssl, readbuf, strlen(readbuf));
if (retval > 0) {
openssl_log(OPENSSL_LOG_DEB, "Write '%d' bytes to '%s:%d'\n", retval, inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port));
sleep(1);
} else {
openssl_log(OPENSSL_LOG_ERR, "%s\n", strerror(errno));
sleep(1);
}
}
return 0;
error:
if (sockfd > 0) {
close(sockfd);
}
if (ctx) {
SSL_CTX_free(ctx);
}
}
OpenSSL DTLS Server端
#include <poll.h>
#include <errno.h>
#include <string.h>
#include <openssl.h>
#include <inet_sock.h>
int main(int argc, char **argv)
{
SSL_CTX * ctx = NULL;
int sockfd = -1;
socklen_t socklen = 0;
struct sockaddr_in my_addr, their_addr;
SSL *ssl = NULL;
int retval = -1;
char readbuf[65535] = {0};
BIO * sbio = NULL;
int read_from_sslcon = -1;
struct pollfd fds = {0};
struct timeval timeout;
openssl_log_init();
openssl_init();
ctx = openssl_ctx_new(DTLS_server_method());
if (NULL == ctx) {
goto error;
}
if (openssl_load_cert_file(ctx, 1)) {
goto error;
}
sockfd = init_sock(SOCK_AF_INET, SOCK_DGRAM);
if (sockfd < 0) {
goto error;
}
if (init_sockaddr((struct sockaddr *)&my_addr, SOCK_AF_INET, sockfd, 0, 1)) {
goto error;
}
ssl = openssl_ssl_new(ctx);
if (NULL == ssl) {
goto error;
}
sbio = BIO_new_dgram(sockfd, BIO_NOCLOSE);
if (NULL == sbio) {
goto error;
}
timeout.tv_sec = 0;
timeout.tv_usec = 250000;
BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
timeout.tv_sec = 0;
timeout.tv_usec = 250000;
BIO_ctrl(sbio, BIO_CTRL_DGRAM_SET_SEND_TIMEOUT, 0, &timeout);
SSL_set_bio(ssl, sbio, sbio);
SSL_set_accept_state(ssl);
while (1) {
memset(&fds, 0, sizeof(fds));
fds.fd = sockfd;
fds.events = POLLIN;
retval = poll(&fds, 1, 10000);
if (retval && (fds.revents & POLLIN)) {
read_from_sslcon = SSL_read(ssl, readbuf, sizeof(readbuf) - 1);
if (read_from_sslcon > 0) {
if (!SSL_is_init_finished(ssl)) {
openssl_log(OPENSSL_LOG_WAR, "SSL init failure ...\n");
continue;
}
BIO_dgram_get_peer(sbio, &my_addr);
openssl_log(OPENSSL_LOG_DEB, "Read %d bytes from '%s:%d', %s\n", read_from_sslcon,
inet_ntoa(my_addr.sin_addr), ntohs(my_addr.sin_port), readbuf);
}
}
}
return 0;
error:
if (sockfd > 0) {
close(sockfd);
}
if (ctx) {
SSL_CTX_free(ctx);
}
}