ノンブロッキングのソケットでの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);
~