Linux开发——http加密(https)

简介

HTTPS (全称:Hypertext Transfer Protocol over Secure Socket Layer) 是HTTP的安全版

HTTPS默认使用TCP端口443,也可以指定其他的TCP端口。

URI格式      https//:URL

加密简介

对称加密:

指的是加密方和解密方使用的是同一个密钥

优点:加密解密的速度很快

缺点:如果两个从未通信过的用户要进行通信的时候,该如何把解密的密钥传输给对方呢(密钥仍然要在网络上传输,所以密钥还是可能会被“中间人”截获),这是对称加密最大的缺点;

常见的对称加密算法有:

    DES:使用56位的密钥,2000年的时候被人破解了,所以现在基本不再使用

    AES:高级加密标准,可以使用128,129,256三种长度密钥

其他的还有blowfish,Twofish和RC6,IDEA(商业算法),CAST5等

非对称加密

非对称加密方式解决了对称加密的缺陷,它的加密和解密密钥是不同的,比如对一组数字加密,我们可以用公钥对其加密,然后我们想要将其还原,就必须用私钥进行解密,公钥和私钥是配对使用的,常见的非对称加密算法有:

公钥加密 私钥解密

 RSA:既可以用来加密解密,又可以用来实现用户认证

 DSA:只能用来加密解密,所以使用范围没有RSA广

非对称加密长度通常有512,1024,2048,4096位,最常用的就是2048位,长度固然可以增加安全性但是需要花费很长时间来进行加密/解密,和对称加密相比,加密/解密的时间差不多是对称加密的1000倍,所以我们通常用其作为用户认证,用对称加密来实现数据的加密/解密

单项加密

单向加密就是用来计算一段数据的特征码的,为了防止用户通过“暴力破解”的方式解密,所以单向加密一般具有“雪崩效应”就是说:只要被加密内容有一点点的不同,加密所得结果就会有很大的变化。单项加密还有一个特点就是无论被加密的内容多长/短,加密的结果(就是提取特征码)是定长的,用途:用于验证数据的完整性,常用的单项加密算法

MD5:这种加密算法固定长度为128位

SHA1:这种加密算法固定长度是160位

 

ssl简介

SSL协议的功能

1)保证传输数据的保密性

2)保证传输数据的完整性  rc4

3)实现通信双方的互相身份认证-----非对称加密

SSL协议在协议栈的位置

SSL 协议是一个分层的协议,共有两层组成。

高层协议包括 :

        SSL 握手协议(SSL HandshakeProtocol) 、

        改变加密约定协议(Change Cipher Spec Protocol) 、

        报警协议(AlertProtocol)

处于 SSL 协议的底层的是:  SSL记录层协议(SSL Record Protocol)

https加密详解

随机数+Key值+公钥---->私钥

解释如下,先说BOB和ALICE通信阶段

黑框A:表示要传输的数据
黑框B:就是单项加密对这段数据提取的特征码,这段特征码同时运用了非对称加密,具体过程是用BOB的私钥加密,传输给ALICE,只要到达后ALICE能解密,表明对方确实是BOB。这一过程同时起到了用户认证和数据完整性的校验。黑框B又称为数字签名
红框A:这一阶段会生成一段很长的随机数(密钥)然后配合对称加密算法对黑框A和黑框B加密,但是我们如何把加密的密钥传输给ALICE呢?这就要用到红框B了
红框B:这一阶段是用ALICE的公钥加密这串随机数(对称加密阶段的密钥),ALICE接受到数据后如果能用自己私钥解密,那就证明接受者确实ALICE


加密过程:
第一步:用单向加密算法提取数据(黑框A)的特征值
第二步:用自己的私钥加密这段特征值形成黑框B
第三步:用对称加密算法,对黑框A和黑框B来加密,得到红框A
第四步:用ALICE的公钥来加密第三步所用的密钥,得到红框B


解密过程:
第一步:ALICE用自己的私钥解密红框B得到对称加密的密钥
第二步:用这个密钥解密红框A内容
第三步:用BOB的公钥解密黑框B,如果能成功,说明发送方确实是BOB,这就完成了身份验证(解密后会得到一串数据的特征值)
第四步:用同样的单项加密算法来对这段数据提取特征值,如果和第三步的特征值一样,说明这段数据是完整的,这就完成了数据完整性的校验

openssl安装命令

apt-get install openssl      

apt-get install libssl-dev    //开发人员需要再安装这个开发库

私钥公钥,中间服务认证,服务器认证,客户端认证等钥匙制作流程

详见文档:https://download.csdn.net/download/qq_25490573/11944848

ssl加密流程

每个数据包都有不同的对称加密秘钥,随机生成

如何获得对方公钥

证书

      公钥

      数据    --->单项加密

                 ---->对称加密

                 ---->非对称的加密(使用对方的公钥)

每次进行数据传输的时候,每包数据都有一个随机生成的密钥

制作ca---建立一个server的公钥,建立一个client的公钥

server   私钥,公钥

client     私钥,公钥

自建根证书

生成服务器证书,并用自建根证书签名

openssl工具库用法

ssl全流程握手

实战例子

server端

#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>




#define DMSG(msg_fmt, msg_args...) \
    printf("%s(%04u): " msg_fmt "\n", __FILE__, __LINE__, ##msg_args)

#define SSL_FILL_EMSG(msg_buf) ERR_error_string_n(ERR_get_error(), msg_buf, sizeof(msg_buf))
#define SSL_FILL_IO_EMSG(ssl_fd, msg_buf) \
    ERR_error_string_n(SSL_get_error(ssl_fd), msg_buf, sizeof(msg_buf))




int shutdown_process = 0;
char *server_key_passphrase = NULL;
char ssl_ebuf[128];




void signal_handle(
    int signal_value)
{
    switch(signal_value)
    {
        case SIGINT:
        case SIGQUIT:
        case SIGTERM:
            shutdown_process = 1;
            break;
    }

    return;
}

int ssl_init(
    SSL_CTX **ssl_ctx_buf)
{
    const SSL_METHOD *ssl_method;
    SSL_CTX *ssl_ctx;


    // SSL 函式庫初始化.
    SSL_library_init();
    // 載入 SSL 錯誤訊息.
    SSL_load_error_strings();
    // 載入 SSL 演算法.
    OpenSSL_add_all_algorithms();

    // 指定使用 TLSv1 協定.
#if OPENSSL_VERSION_NUMBER >= 0x10002000
    ssl_method = TLSv1_2_server_method();
#else
    ssl_method = TLSv1_server_method();
#endif
    if(ssl_method == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_01;
    }

    // 建立 CTX 物件.
    ssl_ctx = SSL_CTX_new(ssl_method);
    if(ssl_ctx == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_01;
    }

    // 此函式非必要, 設定使用的加密演算法的優先權.
    // 使用方法 https://linux.die.net/man/3/ssl_ctx_set_cipher_list
    if(SSL_CTX_set_cipher_list(ssl_ctx, "DEFAULT") != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_02;
    }

#if OPENSSL_VERSION_NUMBER >= 0x10002000
    // ECDH 相關, OpenSSL 1.0.2 以上才支援.
    if(SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_set_ecdh_auto() fail [%s]", ssl_ebuf);
        goto FREE_02;
    }
#endif

    *ssl_ctx_buf = ssl_ctx;

    return 0;
FREE_02:
    SSL_CTX_free(ssl_ctx);
FREE_01:
    return -1;
}

int ssl_set_key_passphrase(
    char *buf,
    int size,
    int rwflag,
    void *userdata)
{
    return snprintf(buf, size, "%s", server_key_passphrase);
}

int ssl_load_pem(
    SSL_CTX *ssl_ctx,
    char *server_passphrase,
    char *server_key_path,
    char *server_cert_path,
    char *server_chain_path)
{
    // 設定伺服器的私鑰的密碼.
    // 如果沒有使用 SSL_CTX_set_default_passwd_cb() 設定則會要求手動輸入.
    if(server_passphrase != NULL)
        SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_set_key_passphrase);

    // 載入伺服器的私鑰 (PEM 格式).
    if(SSL_CTX_use_PrivateKey_file(ssl_ctx, server_key_path, SSL_FILETYPE_PEM) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_use_PrivateKey_file(%s) fail [%s]", server_key_path, ssl_ebuf);
        return -1;
    }

    // 載入伺服器的憑證 (PEM 格式).
    if(SSL_CTX_use_certificate_file(ssl_ctx, server_cert_path, SSL_FILETYPE_PEM) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_use_certificate_file(%s) fail [%s]", server_cert_path, ssl_ebuf);
        return -1;
    }

    // 檢查伺服器的私鑰和憑證是否批配.
    if(SSL_CTX_check_private_key(ssl_ctx) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_check_private_key() fail [%s]", ssl_ebuf);
        return -1;
    }

    // 載入伺服器的憑證串鍊 (PEM 格式), 伺服器端要提供憑證串鍊讓客戶端做驗證.
    // 憑證串鍊的內容就是把伺服器憑證和中繼憑證合併成單一憑證,
    // 憑證串鍊內如必須依照 伺服器憑證 -> 中繼憑證-1 -> .. -> 中繼憑證-N 的順序記錄,
    //
    // 以 www.google.com 為例 :
    // www.google.com 的憑證簽發順序是 (上到下),
    // Equifax Secure Certificate Authority,
    //   GeoTrust Global CA,
    //     Google Internet Authority G2,
    //       www.google.com
    // 憑證串鍊內如必須紀錄除了根憑證 (Equifax Secure Certificate Authority) 之外的憑證,
    // 憑證串鍊檔案內容會像 :
    // -----BEGIN CERTIFICATE-----
    // www.google.com 的憑證內容.
    // -----END CERTIFICATE-----
    // -----BEGIN CERTIFICATE-----
    // Google Internet Authority G2 的憑證內容.
    // -----END CERTIFICATE-----
    // -----BEGIN CERTIFICATE-----
    // GeoTrust Global CA 的憑證內容.
    // -----END CERTIFICATE-----
    if(SSL_CTX_use_certificate_chain_file(ssl_ctx, server_chain_path) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_use_certificate_chain_file(%s) fail [%s]",
             server_chain_path, ssl_ebuf);
        return -1;
    }

    return 0;
}

int ssl_socket_init(
    struct sockaddr_in *local_addr,
    socklen_t addr_len,    
    int *sock_fd_buf)
{
    int sock_fd;


    // 建立 socket.
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock_fd == -1)
    {
        DMSG("call socket() fail [%s]", strerror(errno));
        return -1;
    }

    // 綁定服務位址.
    if(bind(sock_fd, (struct sockaddr *) local_addr, addr_len) == -1)
    {
        DMSG("call bind() fail [%s]", strerror(errno));
        return -1;
    }

    // 監聽客戶端連線.
    if(listen(sock_fd, 4) == -1)
    {
        DMSG("call listen() fail [%s]", strerror(errno));
        return -1;
    }

    *sock_fd_buf = sock_fd;

    return 0;
}

int ssl_socket_accept(
    int sock_fd,
    SSL_CTX *ssl_ctx,
    socklen_t addr_len,
    int *sock_rfd_buf,
    struct sockaddr_in *remote_addr_buf,
    SSL **ssl_session_buf)
{
    fd_set fdset_fd;
    socklen_t alen = addr_len;
    struct sockaddr_in remote_addr;
    int sock_rfd;
    SSL *ssl_session;


    FD_ZERO(&fdset_fd);
    FD_SET(sock_fd, &fdset_fd);

    // 等待客戶端連線.
    if(select(sock_fd + 1, &fdset_fd, NULL, NULL, NULL) == -1)
    {
        DMSG("call select() fail [%s]", strerror(errno));
        goto FREE_01;
    }

    if(FD_ISSET(sock_fd, &fdset_fd) == 0)
    {
        DMSG("call FD_ISSET() return nothing");
        goto FREE_01;
    }

    // 接收客戶端連線.
    sock_rfd = accept(sock_fd, (struct sockaddr *) &remote_addr, &alen);
    if(sock_rfd == -1)
    {
        DMSG("call accept() fail [%s]", strerror(errno));
        goto FREE_01;
    }

    // 基於 ctx 產生新的 SSL.
    ssl_session = SSL_new(ssl_ctx);
    if(ssl_session == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_new() fail [%s]", ssl_ebuf);
        goto FREE_02;
    }

    // 指定 SSL 連線使用的 socket.
    if(SSL_set_fd(ssl_session, sock_rfd) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_set_fd() fail [%s]", ssl_ebuf);
        goto FREE_03;
    }

    // 建立 SSL 連線.
    if(SSL_accept(ssl_session) == -1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_accept() fail [%s]", ssl_ebuf);
        goto FREE_03;
    }

    *sock_rfd_buf = sock_rfd;
    memcpy(remote_addr_buf, &remote_addr, addr_len);
    *ssl_session_buf = ssl_session;

    return 0;
FREE_03:
    SSL_free(ssl_session);
FREE_02:
    close(sock_rfd);
FREE_01:
    return -1;
}

int ssl_show_info(
    struct sockaddr_in *remote_addr,
    SSL *ssl_session)
{
    X509 *cert_data;
    char data_buf[256];


    DMSG("");

    // 顯示客戶端的位址.
    DMSG("client :");
    DMSG("%s:%u", inet_ntop(AF_INET, &remote_addr->sin_addr, data_buf, sizeof(data_buf)),
         ntohs(remote_addr->sin_port));

    // 顯示連線的 SSL 版本.
    DMSG("version :");
    DMSG("%s", SSL_get_cipher_version(ssl_session));

    // 顯示使用的加密方式.
    DMSG("cipher :");
    DMSG("%s", SSL_get_cipher_name(ssl_session));

    // 取出對方的憑證.
    cert_data = SSL_get_peer_certificate(ssl_session);
    DMSG("client certificate :");
    if(cert_data != NULL)
    {
        // 取出憑證的主旨.
        X509_NAME_oneline(X509_get_subject_name(cert_data), data_buf, sizeof(data_buf));
        DMSG("subject : %s", data_buf);
        // 取出憑證的簽發者.
        X509_NAME_oneline(X509_get_issuer_name(cert_data), data_buf, sizeof(data_buf));
        DMSG("issuer  : %s", data_buf);
    }
    else
    {
        DMSG("no certificate");
    }

    return 0;
}

int ssl_socket_send(
    SSL *ssl_session,
    void *data_con,
    unsigned int data_len)
{
    int slen;


    slen = SSL_write(ssl_session, data_con, data_len);
    if(slen <= 0)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_write() fail [%s]", ssl_ebuf);
        return -1;
    }

    return slen;
}

int ssl_socket_recv(
    SSL *ssl_session,
    void *data_buf,
    unsigned int buf_size)
{
    int rlen;


    rlen = SSL_read(ssl_session, data_buf, buf_size);
    if(rlen <= 0)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_read() fail [%s]", ssl_ebuf);
        return -1;
    }

    return rlen;
}

int main(
    int argc,
    char **argv)
{
    int fret = -1;
    char opt_ch, *server_key_path = NULL, *server_cert_path = NULL, *server_chain_path = NULL;
    char data_buf[256];
    int sock_fd, sock_rfd;
    SSL_CTX *ssl_ctx;
    SSL *ssl_session;
    struct in_addr naddr;
    struct sockaddr_in local_addr, remote_addr;


    while((opt_ch = getopt(argc , argv, "p:k:c:l:"))!= -1)
        switch(opt_ch)
        {
            case 'p':
                server_key_passphrase = optarg;
                break;
            case 'k':
                server_key_path = optarg;
                break;
            case 'c':
                server_cert_path = optarg;
                break;
            case 'l':
                server_chain_path = optarg;
                break;
            default:
                goto FREE_HELP;
        }

    if(server_key_passphrase == NULL)
        goto FREE_HELP;
    if(server_key_path == NULL)
        goto FREE_HELP;
    if(server_cert_path == NULL)
        goto FREE_HELP;
    if(server_chain_path == NULL)
        goto FREE_HELP;

    signal(SIGINT, signal_handle);
    signal(SIGQUIT, signal_handle);
    signal(SIGTERM, signal_handle);

    // 初始化 SSL 資料.
    if(ssl_init(&ssl_ctx) < 0)
    {
        DMSG("call ssl_init() fail");
        goto FREE_01;
    }

    // 載入伺服器的憑證串鍊, 私鑰, 憑證.
    if(ssl_load_pem(ssl_ctx, server_key_passphrase, server_key_path, server_cert_path,
                    server_chain_path) < 0)
    {
        DMSG("call ssl_load_pem() fail");
        goto FREE_02;
    }

    // 設定伺服器服務的位址.
    if(inet_pton(AF_INET, "127.0.0.1", &naddr) != 1)
    {
        DMSG("call inet_pton() fail [%s]", strerror(errno));
        goto FREE_02;
    }
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = naddr.s_addr;
    local_addr.sin_port = htons(443);

    // socket 初始化並監聽連線.
    if(ssl_socket_init(&local_addr, sizeof(local_addr), &sock_fd) < 0)
    {
        DMSG("call ssl_socket_init() fail");
        goto FREE_02;
    }

    DMSG("listen connect");
    while(shutdown_process == 0)
    {
        // 接收連線.
        fret = ssl_socket_accept(sock_fd, ssl_ctx, sizeof(remote_addr),
                                 &sock_rfd, &remote_addr, &ssl_session);
        if(fret < 0)
        {
            DMSG("call ssl_socket_accept() fail");
            goto FREE_02;
        }

        // 顯示 SSL 的連線和憑證等資訊.
        ssl_show_info(&remote_addr, ssl_session);

        // 接收資料.
        if(ssl_socket_recv(ssl_session, data_buf, sizeof(data_buf)) < 0)
        {
            DMSG("call ssl_socket_recv() fail");
            shutdown_process = 1;
            goto FREE_SESSION;
        }
        DMSG("recv : %s", data_buf);

        // 傳送資料.
        snprintf(data_buf, sizeof(data_buf), "hellow, i am server");
        DMSG("send : %s", data_buf);
        if(ssl_socket_send(ssl_session, data_buf, strlen(data_buf)) < 0)
        {
            DMSG("call ssl_socket_send() fail");
            shutdown_process = 1;
            goto FREE_SESSION;
        }

FREE_SESSION:
        DMSG("close connect");
        SSL_shutdown(ssl_session);
        SSL_free(ssl_session);
        close(sock_rfd);
    }

FREE_02:
    SSL_CTX_free(ssl_ctx);
FREE_01:
    return 0;
FREE_HELP:
    printf("\ntls_one_way_server <-p> <-k> <-c> <-l>\n");
    printf("  -p : server private key pass phrase\n");
    printf("       ex : -p john123\n");
    printf("  -k : server private key path\n");
    printf("       ex : -k ../pem/server/server.key.pem\n");
    printf("  -c : server certificate path\n");
    printf("       ex : -c ../pem/server/server.cert.pem\n");
    printf("  -l : server certificate chain path\n");
    printf("       ex : -l ../pem/server/server_chain.cert.pem\n\n");
    return 0;
}

client端

// ©.
// https://github.com/sizet/ssl_example

#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>




#define DMSG(msg_fmt, msg_args...) \
    printf("%s(%04u): " msg_fmt "\n", __FILE__, __LINE__, ##msg_args)

#define SSL_FILL_EMSG(msg_buf) ERR_error_string_n(ERR_get_error(), msg_buf, sizeof(msg_buf))
#define SSL_FILL_IO_EMSG(ssl_session, msg_buf) \
    ERR_error_string_n(SSL_get_error(ssl_session), msg_buf, sizeof(msg_buf))




char ssl_ebuf[128];




void signal_handle(
    int signal_value)
{
    switch(signal_value)
    {
        case SIGINT:
        case SIGQUIT:
        case SIGTERM:
            exit(0);
            break;
    }

    return;
}

int ssl_init(
    SSL_CTX **ssl_ctx_buf)
{
    const SSL_METHOD *ssl_method;
    SSL_CTX *ssl_ctx;


    // SSL 函式庫初始化.
    SSL_library_init();
    // 載入 SSL 錯誤訊息.
    SSL_load_error_strings();
    // 載入 SSL 演算法.
    OpenSSL_add_all_algorithms();

    // 指定使用 TLSv1 協定.
#if OPENSSL_VERSION_NUMBER >= 0x10002000
    ssl_method = TLSv1_2_client_method();
#else
    ssl_method = TLSv1_client_method();
#endif
    if(ssl_method == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_01;
    }

    // 建立 CTX 物件.
    ssl_ctx = SSL_CTX_new(ssl_method);
    if(ssl_ctx == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_01;
    }

    // 此函式非必要, 設定使用的加密演算法的優先權.
    // 使用方法 https://linux.die.net/man/3/ssl_ctx_set_cipher_list
    if(SSL_CTX_set_cipher_list(ssl_ctx, "DEFAULT") != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_new(TLSv1_server_method()) fail [%s]", ssl_ebuf);
        goto FREE_02;
    }

#if OPENSSL_VERSION_NUMBER >= 0x10002000
    // ECDH 相關, OpenSSL 1.0.2 以上才支援.
    if(SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_CTX_set_ecdh_auto() fail [%s]", ssl_ebuf);
        goto FREE_02;
    }
#endif

    *ssl_ctx_buf = ssl_ctx;

    return 0;
FREE_02:
    SSL_CTX_free(ssl_ctx);
FREE_01:
    return -1;
}

// 驗證伺服器的憑證的函式.
// 憑證的檢查順序是 根憑證 -> 中繼憑證 -> ... -> 伺服器憑證, 每個憑證都會呼叫驗證函式.
// preverify_ok :
//   OpenSSL 在呼叫此函式之前會先對憑證做基本檢查, 這邊會紀錄檢查結果.
//   0 = 憑證錯誤.
//   1 = 憑證正確.
int ssl_verify_cert(
    int preverify_ok,
    X509_STORE_CTX *x509_ctx)
{
    X509 *cert_data;
    int err_code, cert_depth;
    char data_buf[256];
    const char *err_msg;


    // 哪一層的憑證.
    cert_depth = X509_STORE_CTX_get_error_depth(x509_ctx);
    // 錯誤的原因.
    err_code = X509_STORE_CTX_get_error(x509_ctx);

    DMSG("verify server certificate, depth = %d :", cert_depth);

    cert_data = X509_STORE_CTX_get_current_cert(x509_ctx);
    // 取出憑證的主旨.
    X509_NAME_oneline(X509_get_subject_name(cert_data), data_buf, sizeof(data_buf));
    DMSG("subject : %s", data_buf);
    // 取出憑證的簽發者.
    X509_NAME_oneline(X509_get_issuer_name(cert_data), data_buf, sizeof(data_buf));
    DMSG("issuer  : %s", data_buf);

    if(preverify_ok == 0)
    {
        // 憑證錯誤.

        // 以 www.google.com 為例,
        // 在 SSL_CTX_load_verify_locations() 不指定或使用錯誤的根憑證, 會顯示,
        // verify server certificate, depth = 2 :
        // subject : /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
        // issuer  : /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
        // invalid, unable to get local issuer certificate.

        // 錯誤的文字說明.
        err_msg = X509_verify_cert_error_string(err_code);
        DMSG("invalid, %s", err_msg);

        // 設定錯誤原因.
        X509_STORE_CTX_set_error(x509_ctx, err_code);
    }
    else
    {
        // 憑證正確.

        // 以 www.google.com 為例,
        // 在 SSL_CTX_load_verify_locations() 使用正確的根憑證, 會顯示,
        // verify server certificate, depth = 3 :
        // subject : /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
        // issuer  : /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
        // verify server certificate, depth = 2 :
        // subject : /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
        // issuer  : /C=US/O=Equifax/OU=Equifax Secure Certificate Authority
        // verify server certificate, depth = 1 :
        // subject : /C=US/O=Google Inc/CN=Google Internet Authority G2
        // issuer  : /C=US/O=GeoTrust Inc./CN=GeoTrust Global CA
        // verify server certificate, depth = 0 :
        // subject : /C=US/ST=California/L=Mountain View/O=Google Inc/CN=www.google.com
        // issuer  : /C=US/O=Google Inc/CN=Google Internet Authority G2
        //
        // 可以對憑證作其他額外的檢查,
        // 如果要拒絕此憑證要使用 X509_STORE_CTX_set_error() 設定原因, 並回傳 0 (參考下面說明),
        // 可以設定的值參考 https://linux.die.net/man/3/x509_store_ctx_set_error
    }

    // 回傳值會影響 SSL_connect/SSL_accept 的結果,
    // 0 = 驗證結果錯誤, OpenSSL 會中斷憑證串鍊的驗證並使 SSL_connect/SSL_accept 回傳錯誤.
    // 1 = 驗證結果正確, OpenSSL 會繼續憑證串鍊的驗證.
    return preverify_ok;
}

int ssl_load_pem(
    SSL_CTX *ssl_ctx,
    char *server_root_ca_path)
{
    if(server_root_ca_path != NULL)
    {
        // 客戶端是否驗證伺服器的憑證.
        // 參數 2 表示驗證模式,
        //   SSL_VERIFY_NONE = 不驗證, 預設值.
        //   SSL_VERIFY_PEER = 要驗證.
        // 參數 3 表示額外的驗證函式,
        //   NULL = 使用 OpenSSL 內建的驗證函式.
        //   函式 = 指定的額外驗證函式.
        // 使用 SSL_connect/SSL_accept 取得對方憑證後會呼叫指定的額外驗證函式處理.
        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, ssl_verify_cert);

        // 在 SSL_CTX_set_verify() 使用 SSL_VERIFY_PEER 的話,
        // 需要指定伺服器使用的憑證的根憑證 (PEM 格式).
        //
        // 以 www.google.com 為例 :
        // 在終端機輸入 echo "Q" | openssl s_client -connect www.google.com:443
        // 顯示的憑證串鍊 :
        // CONNECTED(00000003)
        // depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
        // verify return:1
        // depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
        // verify return:1
        // depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
        // verify return:1
        // depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com
        // verify return:1
        // 其中 :
        // depth=0, CN = www.google.com,
        //   這邊是 www.google.com 的憑證.
        // depth=1, CN = Google Internet Authority G2,
        //   這邊是 www.google.com 的憑證的簽發者, 屬於中繼憑證.
        // depth=2, CN = GeoTrust Global CA,
        //   這邊是 Google Internet Authority G2 的憑證的簽發者, 屬於中繼憑證.
        // depth=3, CN = Equifax Secure Certificate Authority,
        //   這邊是 GeoTrust Global CA 的憑證的簽發者, 而在他之上沒其他簽發者, 屬於根憑證.
        // www.google.com 的根憑證就是 Equifax Secure Certificate Authority,
        // Equifax Secure Certificate Authority 的憑證可以在網路上下載.
        //
        // 在建立 SSL 連線時, 伺服器必須傳送完整的憑證串鍊給客戶端做驗證,
        // 憑證串鍊一般會紀錄在 Server Hellow 中的 SSL Record Layer 的 Handshake (Certificate),
        // 憑證串鍊會依照 伺服器憑證 -> 中繼憑證-1 -> .. -> 中繼憑證-N 的順序記錄,
        // 以 www.google.com 為例, www.google.com 會傳送的憑證串鍊會是 :
        // [www.google.com 憑證], [Google Internet Authority G2 憑證], [GeoTrust Global CA 憑證],
        // 總共 3 個憑證, 一般情況伺服器傳來的憑證串鍊不會含有根憑證,
        // 客戶端必須自己擁有根憑證來做驗證.
        if(SSL_CTX_load_verify_locations(ssl_ctx, server_root_ca_path, NULL) != 1)
        {
            SSL_FILL_EMSG(ssl_ebuf);
            DMSG("call SSL_CTX_load_verify_locations(%s) fail [%s]",
                 server_root_ca_path, ssl_ebuf);
            return -1;
        }
    }

    return 0;
}

int ssl_socket_init(
    int *sock_fd_buf)
{
    int sock_fd;


    // 建立 socket.
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock_fd == -1)
    {
        DMSG("call socket() fail [%s]", strerror(errno));
        return -1;
    }

    *sock_fd_buf = sock_fd;

    return 0;
}

int ssl_socket_connect(
    int sock_fd,
    SSL_CTX *ssl_ctx,
    struct sockaddr_in *remote_addr,
    socklen_t addr_len,
    SSL **ssl_session_buf)
{
    int cret;
    SSL *ssl_session;


    // 連線.
    if(connect(sock_fd, (struct sockaddr *) remote_addr, addr_len) == -1)
    {
        DMSG("call connect() fail [%s]", strerror(errno));
        goto FREE_01;
    }

    // 基於 CTX 產生新的 SSL 連線.
    ssl_session = SSL_new(ssl_ctx);
    if(ssl_session == NULL)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_new() fail [%s]", ssl_ebuf);
        goto FREE_01;
    }

    // 指定 SSL 連線使用的 socket.
    if(SSL_set_fd(ssl_session, sock_fd) != 1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_set_fd() fail [%s]", ssl_ebuf);
        goto FREE_02;
    }

    // 建立 SSL 連線.
    if(SSL_connect(ssl_session) == -1)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_connect() fail [%s]", ssl_ebuf);
        cret = SSL_get_verify_result(ssl_session);
        if(cret != X509_V_OK)
        {
            DMSG("invalid certificate, %s", X509_verify_cert_error_string(cret));
        }
        goto FREE_02;
    }

    *ssl_session_buf = ssl_session;

    return 0;
FREE_02:
    SSL_free(ssl_session);
FREE_01:
    return -1;
}

int ssl_show_info(
    struct sockaddr_in *remote_addr,
    SSL *ssl_session)
{
    X509 *cert_data;
    char data_buf[256];


    DMSG("");

    // 顯示伺服器的位址.
    DMSG("server :");
    DMSG("%s:%u", inet_ntop(AF_INET, &remote_addr->sin_addr, data_buf, sizeof(data_buf)),
         ntohs(remote_addr->sin_port));

    // 顯示連線的 SSL 版本.
    DMSG("version :");
    DMSG("%s", SSL_get_cipher_version(ssl_session));

    // 顯示使用的加密方式.
    DMSG("cipher :");
    DMSG("%s", SSL_get_cipher_name(ssl_session));

    // 取出對方的憑證.
    cert_data = SSL_get_peer_certificate(ssl_session);
    DMSG("server certificate :");
    if(cert_data != NULL)
    {
        // 取出憑證的主旨.
        X509_NAME_oneline(X509_get_subject_name(cert_data), data_buf, sizeof(data_buf));
        DMSG("subject : %s", data_buf);
        // 取出憑證的簽發者.
        X509_NAME_oneline(X509_get_issuer_name(cert_data), data_buf, sizeof(data_buf));
        DMSG("issuer  : %s", data_buf);
    }
    else
    {
        DMSG("no certificate");
    }

    return 0;
}

int ssl_socket_send(
    SSL *ssl_session,
    void *data_con,
    unsigned int data_len)
{
    int slen;


    slen = SSL_write(ssl_session, data_con, data_len);
    if(slen <= 0)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_write() fail [%s]", ssl_ebuf);
        return -1;
    }

    return slen;
}

int ssl_socket_recv(
    SSL *ssl_session,
    void *data_buf,
    unsigned int buf_size)
{
    int rlen;


    rlen = SSL_read(ssl_session, data_buf, buf_size);
    if(rlen <= 0)
    {
        SSL_FILL_EMSG(ssl_ebuf);
        DMSG("call SSL_read() fail [%s]", ssl_ebuf);
        return -1;
    }

    return rlen;
}

int main(
    int argc,
    char **argv)
{
    int sock_fd;
    char opt_ch, *server_root_ca_path = NULL, data_buf[256];
    SSL_CTX *ssl_ctx;
    SSL *ssl_session;
    struct in_addr naddr;
    struct sockaddr_in remote_addr;


    while((opt_ch = getopt(argc , argv, "r:"))!= -1)
        switch(opt_ch)
        {
            case 'r':
                server_root_ca_path = optarg;
                break;
            default:
                goto FREE_HELP;
        }

    if(server_root_ca_path == NULL)
        goto FREE_HELP;

    signal(SIGINT, signal_handle);
    signal(SIGQUIT, signal_handle);
    signal(SIGTERM, signal_handle);

    // 初始化 SSL 資料.
    if(ssl_init(&ssl_ctx) < 0)
    {
        DMSG("call ssl_init() fail");
        goto FREE_01;
    }

    // 載入伺服器的根憑證, 需要驗證伺服器的憑證才需要.
    if(ssl_load_pem(ssl_ctx, server_root_ca_path) < 0)
    {
        DMSG("call ssl_load_pem() fail");
        goto FREE_02;
    }

    // socket 初始化.
    if(ssl_socket_init(&sock_fd) < 0)
    {
        DMSG("call ssl_socket_init() fail");
        goto FREE_02;
    }

    // 設定伺服器的位址.
    if(inet_pton(AF_INET, "127.0.0.1", &naddr) != 1)
    {
        DMSG("call inet_pton() fail [%s]", strerror(errno));
        goto FREE_02;
    }
    memset(&remote_addr, 0, sizeof(remote_addr));
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_addr.s_addr = naddr.s_addr;
    remote_addr.sin_port = htons(443);

    // 連線.
    if(ssl_socket_connect(sock_fd, ssl_ctx, &remote_addr, sizeof(remote_addr), &ssl_session) < 0)
    {
        DMSG("call ssl_socket_connect() fail");
        goto FREE_03;
    }

    // 顯示 SSL 的連線和憑證等資訊.
    ssl_show_info(&remote_addr, ssl_session);

    // 傳送資料.
    snprintf(data_buf, sizeof(data_buf), "hellow, i am client");
    DMSG("send : %s", data_buf);
    if(ssl_socket_send(ssl_session, data_buf, strlen(data_buf)) < 0)
    {
        DMSG("call ssl_socket_send() fail");
        goto FREE_03;
    }

    // 接收資料.
    if(ssl_socket_recv(ssl_session, data_buf, sizeof(data_buf)) < 0)
    {
        DMSG("call ssl_socket_recv() fail");
        goto FREE_03;
    }
    DMSG("recv : %s", data_buf);

    // 關閉 SSL 連線.
    SSL_shutdown(ssl_session);

    SSL_free(ssl_session);
FREE_03:
    close(sock_fd);
FREE_02:
    SSL_CTX_free(ssl_ctx);
FREE_01:
    return 0;
FREE_HELP:
    printf("\ntls_one_way_client <-r>\n");
    printf("  -r : server root CA certificate path\n");
    printf("       ex : -r ../pem/server/root_ca.cert.pem\n");
    return 0;
}

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源大于15MB分2次上传。 清晰度一般。加到11章 第12,13章没有。 第1章 嵌入式系统基础. 1.1 嵌入式系统简介 1.1.1 嵌入式系统定义 1.1.2 嵌入式系统与PC 1.1.3 嵌入式系统的特点 1.2 嵌入式系统的发展 1.2.1 嵌入式系统现状 1.2.2 嵌入式系统发展趋势 1.3 嵌入式操作系统与实时操作系统 1.3.1 Linux 1.3.2 uC/OS 1.3.3 Windows CE 1.3.4 VxWorks 1.3.5 Palm OS 1.3.6 QNX 1.4 嵌入式系统选型 第2章 基于ARM9处理器的硬件开发平台 2.1 ARM处理器简介 2.1.1 ARM公司简介 2.1.2 ARM微处理器核 .2.2 ARM9微处理器简介 2.2.1 与ARM7处理器的比较 2.2.2 三星S3C2410X处理器详解 2.3 FS2410开发平台 第3章 创建嵌入式系统开发环境 3.1 嵌入式Linux开发环境 3.2 Cygwin 3.3 虚拟机 3.4 交叉编译的预备知识 3.4.1 Make命令和Makefile文件 3.4.2 binutils工具包 3.4.3 gcc编译器 3.4.4 Glibc库 3.4.5 GDB 3.5 交叉编译 3.5.1 创建编译环境 3.5.2 编译binutils 3.5.3 编译bootstrap_gcc 3.5.4 编译Glibc 3.5.5 编译完整的gcc 3.5.6 编译GDB 3.5.7 成果 3.5.8 其他交叉编译方法 3.6 通过二进制软件包创建交叉编译环境 3.7 开发套件 第4章 调试嵌入式系统程序 4.1 嵌入式系统调试方法 4.1.1 实时在线仿真 4.1.2 模拟调试 4.1.3 软件调试 4.1.4 BDM/JTAG调试 4.2 ARM仿真器 4.2.1 techorICE ARM仿真器 4.2.2 ARM仿真器工作原理 4.2.3 ARM仿真器的系统功能层次 4.2.4 使用仿真器和ADS Debugger调试ARM开发板 4.3 JTAG接口 4.3.1 JTAG引脚定义 4.3.2 通过JTAG烧写Flash 4.3.3 烧写Flash技术内幕 第5章 Bootloader 5.1 嵌入式系统的引导代码 5.1.1 初识Bootloader 5.1.2 Bootloader的启动流程 5.2 Bootloader之vivi 5.2.1 vivi简介 5.2.2 vivi的配置与编译 5.2.3 vivi代码导读 5.3 Bootloader之U-Boot 5.3.1 U-Boot代码结构分析 5.3.2 编译U-Boot代码 5.3.3 U-Boot代码导读 5.3.4 U-Boot命令 5.4 FS2410的Bootloader 第6章 Linux系统在ARM平台的移植 6.1 移植的概念 6.2 Linux内核结构 6.3 Linux-2.4内核向ARM平台的移植 6.3.1 根目录 6.3.2 arch目录 6.3.3 arch/arm/boot目录 6.3.4 arch/arm/def-configs目录 6.3.5 arch/arm/kernel目录 6.3.6 arch/arm/mm目录 6.3.7 arch/arm/mach-s3c2410目录 6.4 Linux-2.6内核向ARM平台的移植 6.4.1 定义平台和编译器 6.4.2 arch/arm/mach-s3c2410/devs.c 6.4.3 arch/arm/mach-s3c2410/mach-fs2410.c 6.4.4 串口输出 6.5 编译Linux内核 6.5.1 代码成熟等级选项 6.5.2 通用的一些选项 6.5.3 和模块相关的选项 6.5.4 和块相关的选项 6.5.5 和系统类型相关的选项 6.5.6 和总线相关的选项 6.5.7 和内核特性相关的选项 6.5.8 和系统启动相关的选项 6.5.9 和浮点运算相关的选项 6.5.10 用户空间使用的二进制文件格式的选项 6.5.11 和电源管理相关的选项 6.5.12 和网络协议相关的选项 6.5.13 和设备驱动程序相关的选项 6.5.14 和文件系统相关的选项 6.5.15 和程序性能分析相关的选项 6.5.16 和内核调试相关的选项 6.5.17 和安全相关的选项 6.5.18 和加密算法相关的选项 6.5.19 库选项 6.5.20 保存内核配置 第7章 Linux设备驱动程序开发 7.1 设备驱动概述 7.1.1 设备驱动和文件系统的关系 7.1.2 设备类型分类 7.1.3 内核空间和用户空间.. 7.2 设备驱动基础 7.2.1 设备驱动中关键数据结构 7.2.2 字符设备驱动开发 第8章 网络设备驱动程序开发 8.1 网络设备驱动程序简介 8.1.1 device数据结构 8.1.2 sk_buff数据结构 8.1.3 内核的驱动程序接口 8.2 以太网控制器CS8900A 8.2.1 特性 8.2.2 工作原理 8.2.3 电路连接 8.2.4 引脚 8.2.5 操作模式 8.3 网络设备驱动程序实例 8.3.1 初始化函数 8.3.2 打开函数 8.3.3 关闭函数 8.3.4 发送函数 8.3.5 接收函数 8.3.6 中断处理函数 第9章 USB驱动程序开发 9.1 USB驱动程序简介 9.1.1 USB背景知识 9.1.2 Linux内核对USB规范的支持 9.1.3 OHCI简介 9.2 Linux下USB系统文件结点 9.3 USB主机驱动结构 9.3.1 USB数据传输时序 9.3.2 USB设备连接/断开时序 9.4 主要数据结构及接口函数 9.4.1 数据传输管道 9.4.2 统一的USB数据传输块 9.4.3 USBD数据描述 9.4.4 USBD与HCD驱动程序接口 9.4.5 USBD层的设备管理 9.4.6 设备类驱动与USBD接口 9.5 USBD文件系统接口 9.5.1 设备驱动程序访问 9.5.2 设备拓扑访问 9.5.3 设备信息访问 9.6 设备类驱动与文件系统接口 9.7 USB HUB驱动程序 9.7.1 HUB驱动初始化 9.7.2 HUB Probe相关函数 9.8 OHCI HCD实现 9.8.1 OHCI驱动初始化 9.8.2 与USBD连接 9.8.3 OHCI根HUB 9.9 扫描仪设备驱动程序 9.9.1 USBD接口 9.9.2 文件系统接口 9.10 USB主机驱动在S3C2410X平台的实现 9.10.1 USB主机控制器简介 9.10.2 驱动程序的移植 第10章 图形用户接口 10.1 嵌入式系统中的GUI简介 10.1.1 MicroWindows 10.1.2 MiniGUI 10.1.3 Qt/Embedded 10.2 MiniGUI编程 10.2.1 MiniGUI移植 10.2.2 MiniGUI编程 10.3 初识Qt/Embedded 10.3.1 Qt介绍 10.3.2 系统要求 10.3.3 Qt的架构 10.4 Qt/Embedded嵌入式图形开发基础 10.4.1 建立Qt/Embedded 开发环境 10.4.2 认识Qt/Embedded开发环境 10.4.3 窗体 10.4.4 对话框 10.4.5 外形与感觉 10.4.6 国际化 10.5 Qt/Embedded实战演练 10.5.1 安装Qt/Embedded工具开发包 10.5.2 交叉编译Qt/Embedded库 10.5.3 Hello,World 10.5.4 发布Qt/Embedded程序到目标板 10.5.5 添加一个Qt/Embedded应用到QPE 第11章 Java虚拟机的移植 11.1 Java虚拟机概述 11.1.1 Java虚拟机的概念 11.1.2 J2ME 11.1.3 KVM 11.2 Java虚拟机的移植 11.2.1 获得源码 11.2.2 编译环境的建立 11.2.3 JDK的安装 11.2.4 KVM的移植及编译 11.2.5 KVM的测试 11.3 其他可选的虚拟机 11.4 性能优化

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值