組み込み向け SSL の OCSP を試してみる

**wolfTips: IoTデバイスのセキュリティライブラリ wolfSSL を使いこなすためのヒント集**
　　　　　　　　　　　　　　**--- 鍵、証明書編 ---**

##概要
[wolfSSL](https://www.wolfssl.jp/)のOCSP機能について紹介します。


最初に CRL (Certificate Revocation Lists、証明書失効リスト）の代替となるOCSPについてその概要を紹介します。

CRLは、一時的または永続的に信頼すべきではない証明書のリストです。CRLの大きな欠点は、これらのリストが広く伝わるのに時間がかかることです。CRLは認証局（CA）から定期的に発行されるため、最大で1週間かかることがあります[(参照)](https://tools.ietf.org/html/rfc5280#section-3.3)。OCSPを使用すると、クライアントはOCSPレスポンダーを使用してサーバー証明書の有効性を検証し、証明書を信頼するかどうかをリアルタイムで知ることができます。

OCSPステープリングは、証明書のステータス情報を受信するようにサーバーに指示する、クライアントが送るTLS拡張です。ステープリングは、接続を確立するために必要な帯域幅とラウンドトリップを大幅に削減します。

TLSサーバーは、クライアントから証明書ステータス要求拡張機能を受信すると、OCSPレスポンダーに完全なDERエンコードされたOCSP応答を送信します。これにより、クライアントはOCSPレスポンダーに証明書の有効性を問い合わせる必要がなくなり、頻繁にアクセスするサーバーのクライアントから来るOCSPレスポンダーの帯域幅を節約することができます。

OCSPステープリングのバージョン1では、1つの証明書のステータス情報しか送信できないという厳しい制限があります。多くのTLSサーバは、クライアントが中間証明書チェーンを知らない場合に備えて、自身の証明書と一緒に中間証明書を送信することを選択します。OCSPステープリングのバージョン1は、クライアントがサーバの証明書のステータスを確認する手間を省くだけで、中間証明書を確認する手間は省けません。

OCSPステープリングのバージョン2では、「サーバー自身の証明書だけでなく、チェーン内の中間証明書のステータス情報を提供する」ことを可能にする新しい拡張機能が定義されています[(参照)](https://tools.ietf.org/html/rfc6961)。

[wolfSSL](https://www.wolfssl.jp/)はOCSP、OCSP Stapling version 1、version 2をサポートしています。

今回は、OCSPに関連したAPIと、使用例をサンプルのクライアントプログラムで紹介します。
使用するAPIは、OCSP 機能を有効にするためのAPI、及びOCSPレスポンダのURLアドレスを直接指定するためのAPIになります。

|指定単位|API名|
|---|:---:|
|コンテクスト|wolfSSL_CTX_SetOCSP_OverrideURL<br>wolfSSL_CTX_EnableOCSP
|セッション|wolfSSL_SetOCSP_OverrideURL<br>wolfSSL_EnableOCSP
表１．OCSPに関連するAPI

動作確認では、wolfSSL に同梱されるサンプルのクライアントを使って、azure.microsoft.com へアクセスし OCSP 機能を確認します。

##OCSPに関連するAPIの使用例

SSLセッションの指定単位で OSCP を使用する場合、TLS接続開始までの処理の流れは次のようになります。

1. SSLコンテクスト、SSLセッションの確保
2. OCSPを有効化
3. 証明書・秘密鍵のロード
4. TCPパケット接続に必要な設定
5. TLS 接続開始

SSLセッション単位で指定する場合でも、上記のステップと似通った流れになります。OCSPは相手方証明書の検証の一部として実施されるので、TLS接続開始前に、クライアントの場合はwolfSSL_connect API の呼び出し、サーバーの場合は wolfSSL_accept API の呼び出し前に OCSP機能の設定を行う必要があります。

下記はサンプルのクライアントプログラムからの抜粋になります。

```c
    /* ./example/client/client.c */
...
    /* SSLコンテクストの確保 */
    ctx = wolfSSL_CTX_new(method(NULL));
    if (ctx == NULL)
        err_sys("unable to get ctx");
...
#ifdef HAVE_OCSP
    if (useOcsp) {
    #ifdef HAVE_IO_TIMEOUT
        wolfIO_SetTimeout(DEFAULT_TIMEOUT_SEC);
    #endif

        if (ocspUrl != NULL) {
            /* OCSPレスポンダのURLを直接指定する場合 */
            wolfSSL_CTX_SetOCSP_OverrideURL(ctx, ocspUrl);
            /* OCSP 機能を有効にします */
            wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_NO_NONCE
                                                    | WOLFSSL_OCSP_URL_OVERRIDE);
        }
        else {
            /* URL は相手方の証明書のTLS拡張で指定されるものを使用します。*/
            /* OCSP 機能を有効にします */
            wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_CHECKALL);
        }

    #ifdef WOLFSSL_NONBLOCK_OCSP
        wolfSSL_CTX_SetOCSP_Cb(ctx, OCSPIOCb, OCSPRespFreeCb, NULL);
    #endif
    }
#endif
...
    /* SSLセッションオブジェクト確保 */
    ssl = wolfSSL_new(ctx);
    if (ssl == NULL) {
        wolfSSL_CTX_free(ctx); ctx = NULL;
        err_sys("unable to get SSL object");
    }
...
   /* TLS接続開始　*/
   ret = wolfSSL_connect(ssl);
   if (ret != WOLFSSL_SUCCESS) {
       err = wolfSSL_get_error(ssl, 0);
...
   /* 終了処理 */
   ret = wolfSSL_shutdown(ssl);
   ...
   /* SSL　セッションオブジェクトの解放 */
    wolfSSL_free(ssl); ssl = NULL;
   /* TCPソケットのクローズ処理 */
    CloseSocket(sockfd);
   /* コンテクストオブジェクトの解放*/
   wolfSSL_CTX_free(ctx); ctx = NULL
```

##OCSP機能を有効化し外部サーバーへアクセス
サンプルのクライアントプログラムで外部サーバーへアクセスしてみる
以下の説明では、Linux, MacOS, WidnowsのWSL, CygwinやminGWなどのコマンド環境を想定しています。

1 [wolfSSLのクイックスタート(コマンド編)](https://qiita.com/kojo1/items/3140b107fd7f0ccdf061)を参考に wolfSSL のソースコードをダウンロードします。

2 ビルドとインストール
コンフィグレーションに "--enable-ocsp"オプションを指定し、OCSP機能をwolfSSL に組み込みます。また、アクセスするサーバーを指定するために SNI (Server Name Indication) 機能を有効にします。

```
$cd /path/to/wolfssl

$./configure --enable-ocsp --enable-sni
...
$make check
...
$sudo make install
...
```

3　サンプルのクライアントプログラムの実行

サンプルのクライアントを実行します。"-S" オプションで、アクセスするサーバーを指定します。"-o" オプションで OCSP 機能を使用します。"-A" で指定する証明書は[ブラウザを使ってCA証明書を持ってくる](https://qiita.com/kj1/items/9fb7e0f6e2eebef935d5)を参考に予め ./certs フォルダーに配置しておきます。

```
$ ./examples/client/client -h azure.microsoft.com -p 443 -g -S azure.microsoft.com -A ./certs/azure_microsoft_com.cer -o
```

4　OCSP通信を WireShark で確認する

ステップ 3 の通信を Wireshark で覗いてみます。このときパケットのフィルターに "ocsp || (ssl && ip.addr == 相手方のアドレス)" を指定すると見やすくなります。

サーバーからは２つの証明書が提示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683403/3bd145d4-6ced-6621-34a5-8281200c9e28.png)

各々の証明書は、OCSPレスポンダに関する情報を機関情報アクセスという項目に持っています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683403/eda2ad7c-b3ca-288f-f632-c4448489cfaf.png)

クライアントは、各々のOCSPレスポンダにアクセスします。各OCSPレスポンダは下記の通りです。

|OCSPレスポンダ|URL|含まれる証明書|
|---|:---:|---|
|117.18.237.29|ocsp.digicert.com|Microsoft IT TLS CA 4|
|104.18.24.243|ocsp.msocsp.com|azure.microsoft.com|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683403/a3689a90-4715-c3c4-cb62-e15abaf4964a.png)

各OCSPレスポンダからの応答で証明書が有効（good)であることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683403/4499b44e-381c-f488-f286-a95a0335a1eb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/683403/cb52d7c6-079b-b00f-be9e-c43b9c1e10a7.png)

##まとめ
今回は wolfSSL のOCSP機能をサンプルのクライアントプログラムを使って紹介しました。

詳細なドキュメント、製品情報については、wolfSSLの下記を参照してください。wolfSSLは、ソフトウェアの内容は同じですが無償のオープンソース版と有償商用版のデュアルライセンスとなっています。製品への組み込みの際は商用ライセンス契約が必要です。

ドキュメント：https://www.wolfssl.jp/docs/
英語サイト：https://www.wolfssl.com/
日本語サイト：https://www.wolfssl.jp/
Twitter: https://twitter.com/wolfSSL_Japan/