Help us understand the problem. What is going on with this article?

OpenSSL APIを利用したSSL/TLS通信

More than 3 years have passed since last update.

ノンブロッキングのソケットでのOpenSSL APIを使用した、SSL/TLS通信。
SSL/TLSのハンドシェイクはサーバ認証+クライアント認証。

TCPコネクション確率後のサーバ側SSL/TLS接続処理。(※クライアント認証)
ノンブロッキングの場合、SSL_accept()をSSL_ERROR_NONEまで繰り返す。

server_accept.c
    SSL_CTX *ctx;
    SSL *ssl;
    /* SSL/TLS汎用でSSL_CTXオブジェクトを生成 */
    if (!(ctx = SSL_CTX_new(SSLv23_server_method())))
    {
        // エラー処理
    }
    /* SSLv2はセキュリティ的にNGなので除く*/
    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
    // 証明書の登録
    if (1 != SSL_CTX_use_certificate_file(ctx, certificate, SSL_FILETYPE_PEM))
    {
        // エラー処理
    }

    // 秘密鍵の登録
    if (1 != SSL_CTX_use_PrivateKey_file(ctx, privatekey, SSL_FILETYPE_PEM))
    {
        // エラー処理
    }
    // CA証明書の登録とクライアント証明書の要求
    if (1 != SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL))
    {
        // エラー処理
    }
    // 証明書検証機能の有効化
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    // 証明書チェーンの深さ
    SSL_CTX_set_verify_depth(ctx,9);
    /* SSLオブジェクトを生成 */
    if (!(ssl = SSL_new(ctx)))
    {
        // エラー処理
    }
    /* SSLオブジェクトとファイルディスクリプタを接続 */
    if (!SSL_set_fd(ssl, fd))
    {
        // エラー処理
    }
    while (1)
    {
        /* SSL通信の開始 */
        sslret  = SSL_accept(ssl);
        ssl_eno = SSL_get_error(ssl, sslret);
        switch (ssl_eno)
        {
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_SYSCALL:
                continue;
            default:
                // エラー処理
        }
        break;
    }

TCPコネクション確率後のクライアント側SSL/TLS接続処理。
ノンブロッキングの場合、SSL_connect()をSSL_ERROR_NONEまで繰り返す。

client_connect.c
    SSL_CTX *ctx;
    SSL *ssl;
    /* SSL/TLS汎用でSSL_CTXオブジェクトを生成 */
    if (!(ctx = SSL_CTX_new(SSLv23_client_method())))
    {
        // エラー処理
    }

    /* SSLv2はセキュリティ的にNGなので除く*/
    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);

    // 証明書の登録
    if (1 != SSL_CTX_use_certificate_file(ctx, certificate, SSL_FILETYPE_PEM))
    {
        // エラー処理
    }
    // 秘密鍵の登録
    if (1 != SSL_CTX_use_PrivateKey_file(ctx, privatekey, SSL_FILETYPE_PEM))
    {
        // エラー処理
    }

    // CA証明書の登録
    if (1 != SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL))
    {
        // エラー処理
    }
    // 証明書検証機能の有効化
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
    // 証明書チェーンの深さ
    SSL_CTX_set_verify_depth(ctx,9);
    /* SSLオブジェクトを生成 */
    if (!(ssl = SSL_new(ctx)))
    {
        // エラー処理
    }
    /* SSLオブジェクトとファイルディスクリプタを接続 */
    if (!SSL_set_fd(ssl, fd))
    {
        // エラー処理
    }
    while (1)
    {
        /* SSL通信の開始 */
        sslret  = SSL_connect(ssl);
        ssl_eno = SSL_get_error(ssl, sslret);
        switch (ssl_eno)
        {
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_SYSCALL:
                continue;
            default:
                // エラー処理
        }
        break;
    }

データ送受信は、SSL_write()/SSL_read()をSSL_ERROR_NONEまで繰り返す。
recv()/send()のイメージ。
SSL_write()/SSL_read()は、TCPの送受信両方が発生する可能性がある。
同じ引数でSSL_write()/SSL_read()は繰り返す。
どこまで読み書きが進んだかはオブジェクト側で管理。

send_data.c
    // epoll_waitでイベントまち

    while (1)
    {
        /* SSLデータ送信 */
        sslret  = SSL_write(ssl, data, size);
        ssl_eno = SSL_get_error(ssl, sslret);
        switch (ssl_eno)
        {
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_SYSCALL:
                continue;
            default:
                // エラー処理
        }
        break;
    }
recv_data.c
    // epoll_waitでイベントまち

    while (1)
    {
        /* SSLデータ受信 */
        sslret  = SSL_read(ssl, data, size);
        ssl_eno = SSL_get_error(ssl, sslret);
        switch (ssl_eno)
        {
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_SYSCALL:
                continue;
            default:
                // エラー処理
        }
        break;
    }

SSL/TLS切断処理は、SSL_shutdown()をSSL_ERROR_NONEまで繰り返す。

close.c
    while (1)
    {
        /* SSL通信の終了 */
        sslret  = SSL_shutdown(ssl);
        ssl_eno = SSL_get_error(ssl, sslret);
        switch (ssl_eno)
        {
            case SSL_ERROR_NONE:
                break;
            case SSL_ERROR_WANT_READ:
            case SSL_ERROR_WANT_WRITE:
            case SSL_ERROR_SYSCALL:
                continue;
            default:
                // エラー処理
        }
        break;
    }
    SSL_free(ssl); 
    SSL_CTX_free(ctx);

CRLのチェックは以下を試したが、正常動作しなかった・・・

client_connect.c
    
    // CRL検証の有効化
    X509_STORE *store = SSL_CTX_get_cert_store(ssl->ctx);
    X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
    X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
    //X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
    X509_STORE_set1_param(store, param);
    X509_VERIFY_PARAM_free(param);

    // 証明書検証機能の有効化
    SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, verify_callback);
    
yoshida-jk
現在のお仕事は、ネットワーク上のパケットを解析&監視するサーバパッケージの開発。 休日は3人の子供とゲームに熱中しては嫁にしかられる。 最近は、スプラトゥーンにはまってます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした