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

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

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

```c: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まで繰り返す。

```c: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()は繰り返す。
どこまで読み書きが進んだかはオブジェクト側で管理。

```c: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;
	}
```

```c: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まで繰り返す。

```c: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のチェックは以下を試したが、正常動作しなかった・・・

```c: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);
	～
```