一、openssl库概述
OpenSSL库是一个开放源代码库,提供了用于SSL / TLS协议的加密,解密和验证功能,以及用于密码学的功能。它可以用于安全的网络通信,数字签名和证书管理。OpenSSL是一个跨平台的库,适用于多种操作系统,包括Windows,Linux,Unix等等。 OpenSSL库提供了两种主要的API,C语言API和命令行工具API。C语言API包含了700多个函数,其中有很多函数都是用于不同种类加密,解密和签名算法等等方面。命令行工具API可以让用户通过命令行直接操作SSL功能,包括TLS握手、创建和管理密钥、证书和CRLs等等。 下面介绍一些常用openssl功能及其用法。
二、加密解密
OpenSSL库提供了多种加密算法,包括对称加密算法和非对称加密算法。下面以AES加密算法为例,介绍OpenSSL的加密解密功能。
//AES加密
int aes_encrypt(unsigned char *in_data, int in_data_len, unsigned char *out_data, unsigned char *key, unsigned char *iv) {
EVP_CIPHER_CTX *ctx;
int out_len, len;
int ret = 0;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) return ERR_EVP_CIPHER_INIT;
/* Initialise the encryption operation. IMPORTANT - ensure you use a key and IV size appropriate for your cipher */
if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) return ERR_EVP_CIPHER_INIT;
/* Provide the message to be encrypted, and obtain the encrypted output. EVP_EncryptUpdate can be called multiple times if necessary */
if(1 != EVP_EncryptUpdate(ctx, out_data, &out_len, in_data, in_data_len)) ret=ERR_EVP_FAILED;
else {
/* Finalise the encryption. Further data cannot be encrypted */
if(1 != EVP_EncryptFinal_ex(ctx, out_data + out_len, &len)) ret=ERR_EVP_FAILED;
else out_len += len;
}
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ret;
}
//AES解密
int aes_decrypt(unsigned char *in_data, int in_data_len, unsigned char *out_data, unsigned char *key, unsigned char *iv) {
EVP_CIPHER_CTX *ctx;
int out_len, len;
int ret = 0;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) return ERR_EVP_CIPHER_INIT;
/* Initialise the decryption operation. IMPORTANT - ensure you use a key and IV size appropriate for your cipher */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) return ERR_EVP_CIPHER_INIT;
/* Provide the message to be decrypted, and obtain the plaintext output. EVP_DecryptUpdate can be called multiple times if necessary */
if(1 != EVP_DecryptUpdate(ctx, out_data, &out_len, in_data, in_data_len)) ret = ERR_EVP_FAILED;
else {
/* Finalise the decryption. Further data cannot be decrypted */
if(1 != EVP_DecryptFinal_ex(ctx, out_data + out_len, &len)) ret=ERR_EVP_FAILED;
else out_len += len;
}
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
return ret;
}
三、密码学
OpenSSL库提供了多种密码学算法,包括哈希算法、消息认证码算法、数字签名算法等等。下面以SHA256算法为例,介绍OpenSSL的密码学功能。
//SHA256哈希算法
int sha256_hash(unsigned char *data, unsigned int data_len, unsigned char *hash) {
EVP_MD_CTX *mdctx;
/* Create and initialise the context */
if(!(mdctx = EVP_MD_CTX_create())) return ERR_EVP_MD_INIT;
/* Initialise the message digest operation with the SHA-256 algorithm */
if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL)) return ERR_EVP_MD_INIT;
/* Provide the message to be hashed, and obtain the hashed output */
if(1 != EVP_DigestUpdate(mdctx, data, data_len)) return ERR_EVP_MD_FAILED;
if(1 != EVP_DigestFinal_ex(mdctx, hash, NULL)) return ERR_EVP_MD_FAILED;
/* Clean up */
EVP_MD_CTX_destroy(mdctx);
return 0;
}
四、证书管理
OpenSSL库提供了多种证书管理的功能,包括证书创建、查看和验证等等。下面以证书查看功能为例,介绍OpenSSL的证书管理功能。
//读取x509格式证书信息
int read_x509_certificate(const char *cert_file, X509 **x509) {
FILE *fp = NULL;
int ret = 0;
if ((fp = fopen(cert_file, "r")) == NULL) return ERR_X509_FAILED;
/* Create a new X509 object */
if ((*x509 = X509_new()) == NULL) return ERR_X509_FAILED;
/* Read the certificate from the file into the X509 object */
if(!PEM_read_X509(fp, x509, 0, NULL)) ret = ERR_X509_FAILED;
fclose(fp);
return ret;
}
//获取证书信息
int get_subject_name(X509 *x509, char *subject_name, size_t name_len) {
X509_NAME *name;
BIO *outbio = BIO_new(BIO_s_mem());
if (!outbio) return ERR_BIO_FAILED;
/* Get the subject name from the X509 object */
name = X509_get_subject_name(x509);
X509_NAME_print_ex(outbio, name, 0, XN_FLAG_RFC2253);
/* Copy the name to the output buffer */
memset(subject_name, 0, name_len);
if (BIO_read(outbio, subject_name, name_len-1) <= 0) {
BIO_free(outbio);
return ERR_BIO_FAILED;
}
BIO_free(outbio);
return 0;
}
五、TLS/SSL通信
OpenSSL库最重要的功能之一就是处理SSL/TLS通信,下面以SSL/TLS服务器的创建和客户端的连接为例,介绍OpenSSL的TLS/SSL通信功能。
//TLS/SSL服务器
int server_start(int listen_fd, SSL_CTX *ctx) {
int conn_fd;
socklen_t client_len;
struct sockaddr_in client_addr;
SSL *ssl;
while (1) {
client_len = sizeof(client_addr);
/* Wait for a client to connect */
if ((conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) continue;
/* Create a new SSL object for the connection */
if ((ssl = SSL_new(ctx)) == NULL) {
close(conn_fd);
continue;
}
/* Attach the socket descriptor to the SSL object */
if (!SSL_set_fd(ssl, conn_fd)) {
SSL_free(ssl);
close(conn_fd);
continue;
}
/* Perform the SSL/TLS handshake */
if (SSL_accept(ssl) <= 0) {
SSL_free(ssl);
close(conn_fd);
continue;
}
/* Handle the client connection */
handle_client(ssl);
/* Clean up the SSL object */
SSL_free(ssl);
close(conn_fd);
}
return 0;
}
//TLS/SSL客户端
int client_connect(const char *server_name, const char *server_port, SSL_CTX *ctx) {
int sockfd;
struct addrinfo hints, *servinfo, *p;
char ipstr[INET6_ADDRSTRLEN];
SSL *ssl;
/* Look up the server address */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(server_name, server_port, &hints, &servinfo) != 0) return ERR_SOCKET_FAILED;
/* Try to connect to the server */
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) continue;
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
continue;
}
break;
}
if (p == NULL) return ERR_SOCKET_FAILED;
/* Create a new SSL object for the connection */
if ((ssl = SSL_new(ctx)) == NULL) {
close(sockfd);
return ERR_SSL_FAILED;
}
/* Attach the socket descriptor to the SSL object */
if (!SSL_set_fd(ssl, sockfd)) {
SSL_free(ssl);
close(sockfd);
return ERR_SSL_FAILED;
}
/* Perform the SSL/TLS handshake */
if (SSL_connect(ssl) <= 0) {
SSL_free(ssl);
close(sockfd);
return ERR_SSL_FAILED;
}
/* Handle the server connection */
handle_server(ssl);
/* Clean up the SSL object */
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockfd);
freeaddrinfo(servinfo);
return 0;
}