API Connect v10 での相互TLS認証検証① - トラストストア認証

# はじめに

API Connectでは、相互TLS認証（mutual tls , 2way tlsなどとも言います）のための機能が提供されています。
通常のHTTPSのTLS認証では、クライアントがサーバーとTLSネゴシエーションをする際に、サーバーから証明書が送られ、クライアントがサーバーを検証することでなりすましを防ぐことができます。さらに、相互TLS認証では、クライアントから証明書をサーバーに送り、サーバーが検証するプロセスが追加されます。
この記事では、API Connectで相互TLS認証を実装する方法を紹介します（検証環境は、v10.0.4となります）。

API Connectでは、デフォルトでGUI用のサーバー証明書や、コンポーネント間の通信で相互TLS認証が使われているため、そのための各TLSプロファイルが登録されています（[Knowledge Center:Deployment overview for endpoints and certificates](https://www.ibm.com/docs/en/api-connect/10.0.1.x?topic=pyd-deployment-overview-endpoints-certificates)）。


# 事前準備

## クライアント証明書の作成

相互TLS認証では、クライアントの秘密鍵と、Public証明書が必要となります。今回は、OpenSSLツールを使用して自己署名の証明書を作成します。
詳細は、[Knowledge Center: Generating a self-signed certificate using OpenSSL](https://www.ibm.com/docs/en/api-connect/10.0.1.x?topic=overview-generating-self-signed-certificate-using-openssl)をご参照ください。

```
$ openssl req -newkey rsa:2048 -nodes -keyout client_key.pem -x509 -days 365 -out client_certificate.pem
Generating a RSA private key
...............+++++
.............................................+++++
writing new private key to 'client_key.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:jp
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:client.com
Email Address []:

$ openssl pkcs12 -inkey client_key.pem -in client_certificate.pem -export -out client_certificate.p12
Enter Export Password:
Verifying - Enter Export Password:
```

上記の結果、秘密鍵（client_key.pem）と、PUblic証明書（client_certificate.pem）と、この2つを組み合わせてパスワードで保護したPKCS12形式ファイル（client_certificate.p12）が作成されました。


# サーバー設定

## ゲートウェイ・サービスのTLSプロファイル確認

Cloud Managerにログインし、トポロジーメニューからゲートウェイ・サービスに紐づいているTLSプロファイルを確認します。
インストール時に設定していない場合は、製品提供のデフォルトのプロファイルが設定されます。
以下の例では、API呼び出しエンドポイントのTLSサーバー・プロファイルに「デフォルトのTLSサーバー・プロファイル」が設定されていることがわかります。このプロファイルでは、相互認証はなしとなっていますので、クライアント証明書の認証は実施されません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/4c45619b-1285-b933-ac71-0466c16c896a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/118b3b15-a814-0ecd-6a36-4f69e4796941.png)

## 相互TLS認証用のトラストストアの登録

リソース-TLSメニューからトラストストアの作成をクリックします。
トラストストアは、クライアントから送付された証明書の検証で使用されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/412eac1b-1534-0227-ab46-45ed85d332ed.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/5188e50d-3c18-54c5-db27-1f8b0c2354d3.png)

事前準備で作成したクライアント証明書をこちらに登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/3715dd45-0987-6416-94d7-0b1678b505d7.png)


## 相互TLS認証用のTLSサーバー・プロファイルの登録

リソース-TLSメニューからTLSサーバー・プロファイルを新規登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/c640f30d-853a-771a-a8ec-78150439cf6d.png)

以下のようにタイトルに任意の名前を設定し、相互認証として「必須」を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/a4d81dee-6bb5-f4a4-4391-f41d07e834ef.png)

トラストストアで先ほど作成した「server-trust-store」を選択して保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/7324f115-4cba-b2d4-8cc1-716ef6f1bc26.png)

以下のように新たにTLSサーバー・プロファイルが作成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/8b00febc-700d-d1ca-cd66-18c29b39cb77.png)

## ゲートウェイ・サービスのTLSプロファイル変更

トポロジーメニューからゲートウェイ・サービスに紐づいているTLSプロファイルを先ほど作成した「server-profile」に変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/4c45619b-1285-b933-ac71-0466c16c896a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403443/3277d338-be51-df28-afeb-a0d5dbaa9039.png)

以上で設定は完了です。

# 動作確認

このゲートウェイ・サービスから公開されているAPIを呼び出してみます。
クライアント証明書が認証できないため、以下のようにTLSハンドシェイクエラーとなります。

```
$ curl -ki https://ademo-***-appdomain.cloud/test-org/sandbox/v1/backend
curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAG
E (0x80090326) - This error usually occurs when a fatal SSL/TLS alert is receive
d (e.g. handshake failed). More detail may be available in the Windows System ev
ent log.
```

次に、証明書を付与してAPI呼び出しを実施します。
今度は、クライアント証明書が認証され、APIの結果が返っています。

```
$ curl -ki https://ademo-***-appdomain.cloud/test-org/sandbox/v1/backend   -E client_certificate.p12:admin
HTTP/1.1 200 OK
Connection: Keep-Alive
Transfer-Encoding: chunked
User-Agent: curl/7.79.1
Accept: */*
X-Client-IP: 172.30.118.51
X-Global-Transaction-ID: 1deba6ad6227061d005b6960
content-type: application/json
Date: Tue, 08 Mar 2022 07:30:37 GMT

{"mtls for backend":"ok"}
```

以上となります。