《openssl库全面详解》

发布时间:2023-05-19

一、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;
}