wolfTips: OpenSSL 互換APIを使いこなす

OpenSSL資産をムダにせず活用できます!

wolfSSLは、インプリメンテーション依存による脆弱性などを最小限にするためにクリーンルームによる独自開発のSSL/TLSライブラリです。一方で OpenSSL 互換の API も提供し既存の OpenSSL ユーザにも簡単にアプリケーションが開発できるように設計されています。ほかにも多くのオープンソースアプリケーションはOpenSSL 互換 API を使用し動作しています。

今回は、この wolfSSL のOpenSSL互換APIの使い方を紹介します。

1.wolfSSLライブラリのビルド

デフォルトで wolfSSL ライブラリのビルドシステムは、OpenSSL 互換 API を無効にしています。OpenSSL互換APIを有効にするには、configure で”–enable-opensslextra”オプションを指定します。

$./configure --enable-opensslextra
“–enable-opensslextra” 以外に、”–enable-opensslall”というオプションが指定可能です。”–enable-opensslall”オプションは、wolfSSL でサポートしている OpenSSL 互換API を使用頻度の低いものも含めて有効化します。

 2.アプリケーションのビルド

OpenSSL APIを使用するアプリケーションをwolfSSL OpenSSL互換APIを用いてビルドするには gcc の場合以下のオプションを指定して行います。

  • ヘッダーファイルパスに wolfSSL ヘッダーファイルのインストール先を指定します。
    gcc の場合:
    -I /path/to/wolfssl/wolfssl -I /path/to/wolfssl
  • wolfssl/openssl/ssl.hをincludeオプションを使用し、直接インクルードします
    -include wolfssl/options.h
  • wolfSSLライブラリをリンクします。
    gcc の場合:
    -lwolfsslを指定します。

次のプログラムはTLSサーバに接続し、”Hello”を送信するだけの簡単なOpenSSL API 使用のサンプルです。ソースコードを変更することなく wolfSSL ライブラリと共に動作させることができます。

/* simplified echoclient wolfSSL and OpenSSL */
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "echoclient.h"

void hello_test()
{
    const char* msg = "Hello SSL!\n";
    SSL_CTX*    ctx = 0;
    SSL*        ssl = 0;
    int ret = 0, err = 0;
    int sendSz;
    char buffer[MAX_ERROR_SZ];
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in servaddr;

    /* server information */
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(11111);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    /* init library */
    SSL_library_init();
    /* create a new CTX */
    ctx = SSL_CTX_new(SSLv23_client_method());
    /* set default locations for trusted CA certificates */
    if (SSL_CTX_load_verify_locations(ctx, caCertFile, 0) != SSL_SUCCESS)
        printf("can't load ca-ecc file.\n");    
    /* create a new SSL structure for a connection */
    ssl = SSL_new(ctx);
    /* connect socket */
    connect(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr));
    /* connect the SSL object with a file descriptor */
    SSL_set_fd(ssl, sockfd);
    /* initiate the TLS/SSL handshake with an TLS/SSL server */
    err = 0; /* Reset error */
    ret = SSL_connect(ssl);
    if (ret != SSL_SUCCESS) {
        err = SSL_get_error(ssl, 0);
        printf("SSL_connect error %d, %s\n", err,
        ERR_error_string(err, buffer));
    }
    /* send message to server */
    sendSz = (int)strlen(msg);
    do {
        err = 0; /* reset error */
        ret = SSL_write(ssl, msg, sendSz);
        if (ret <= 0) {
            err = SSL_get_error(ssl, 0);
        }
    } while (err == _WANT_WRITE);
    /* clean up */
    SSL_shutdown(ssl);
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    /* close descriptor */
    close(sockfd);
}
/* entry */
int main()
{
    hello_test();
    return 0;
}
コンパイル例 (gccの場合):
$gcc -o hello_wolfssl hello.c -I/path/to/wolfssl -I/path/to/wolfssl/wolfssl -include wolfssl/options -lwolfssl

上記のサンプルプログラムのようにOpenSSL互換APIを利用することで、簡単に既存のOpenSSLソースコードを再利用することができます。

3. サポート済み互換APIの確認方法

OpenSSLのAPIは膨大なため、wolfSSL では使用頻度の高いAPIのサブセットを提供しています。必要なOpenSSL APIがサポートされているかは、以下の3ステップを行い判断することができます。

  • /wolfssl/opensslディレクトリ内のヘッダーファイルを確認する。
    /wolfssl/opensslディレクトリ内のヘッダーには OpenSSL 互換レイヤのAPI定義を含む記述があります。 このフォルダ内のssl.hに定義があるかを確認します。
  • NO_WOLFSSL_STUBを定義しアプリケーションのリンクを行ってみる
    APIによっては関数の定義のみで、内部の実装が行われていないものも存在します。そのようなAPIを検出するために次のステップを行います。
  1. configure に NO_WOLFSSL_STUB マクロ定義を指定します。
    $./configure –enable-opensslextra CFLAGS=”-DNO_WOLFSSL_STUB”
    SSL_CTX_get_mode() を例にとって試してみます。
    /wolfssl/openssl/ssl.h を確認します。

    #define SSL_CTX_set_mode                wolfSSL_CTX_set_mode
    #define SSL_CTX_get_mode                wolfSSL_CTX_get_mode
    #define SSL_CTX_set_default_read_ahead  wolfSSL_CTX_set_default_read_ahead
    

    SSL_CTX_get_modeは、wolfSSL_CTX_get_mode に対応付けられています。

  2. 次に、src/ssl.c内にあるwolfSSL_CTX_get_modeの実装を確認してみます。
    #ifndef NO_WOLFSSL_STUB
    long wolfSSL_CTX_get_mode(WOLFSSL_CTX* ctx)
    {
        /* TODO: */
        (void)ctx;
        WOLFSSL_STUB("SSL_CTX_get_mode");
        return 0;
    }
    #endif
    

    未実装で、#ifndef #endif で括られていることが分かります。

  3. NO_WOLFSSL_STUB を指定し、wolfSSL ライブラリをビルドします。
    $./configure –enable-opensslextra CFLAGS=”-DNO_WOLFSSL_STUB”
    $make

    先ほどのアプリケーションにSSL_CTX_get_mode() 呼び出しを追加し、コンパイルしてみます。

    $gcc -o hello_wolfssl hello.c -I/path/to/wolfssl -I/path/to/wolfssl/wolfssl -include wolfssl/options -lwolfssl
    /tmp/cco4upmi.o: 関数 `hello_test' 内:
    echoclient.c:(.text+0xf6): `wolfSSL_CTX_get_mode' に対する定義されていない参照です
    collect2: error: ld returned 1 exit status

    エラーになり、実装されていないことが分かります。

4. 注意事項

サポート済みAPIの有無以外にOpenSSL互換APIを使用する上で注意しておく点があります。

  • 構造体に互換性がない

構造体の互換性を保証しませんので、構造体メンバーを直接参照は避けてください。

  • エラーチェック・エラーコードに互換性がない

API内部の実装が、まったくの同一ではないため、エラーチェックの段階で異なる戻り値を返すことがあります。下記に一例を挙げます。
例:SSL_CTX_load_verify_locations()

エラーの条件wolfSSLOpenSSL
指定した CA ファイルが存在しない-4(WOLFSSL_BAD_FILE)0(失敗)
CA ファイルの有効期限が無効(有効期限前)-150(ASN_BEFORE_DATE_E)1(成功)
成功1(WOLFSSL_SUCCESS)1(成功)

サポート済みAPIの確認方法、及び注意点で言及しましたように気を付けるポイントがいくつかありますが、今回紹介したように wolfSSL ではOpenSSL からのアプリケーションの移行に配慮しています。また、サポート済みAPIの有無、その動作についてや新たなAPIの追加サービスのご希望などありましたらお気軽にsupport@wolfssl.com までお知らせください。