[OCI]Autonomous DatabaseのTLS接続+JDBC Thin driverのwallet無し接続を試す

# はじめに
2021年9月のOracle Autonomous Database 共有Exadata Infrastructure (ADB-S)の新機能としてTLS and Mutual TLS Connections という機能が提供されました。
ADB-Sが用意したWalletをダウンロードし、Oracle Client側へクライアントの秘密鍵やクライアント証明書を設定することでへ接続していた方式がMutual TLS Connections (mTLS)です。
ADB-SではこれまでmTLS接続のみ許可されていたのですが、TLS接続を利用したJDBC Thin driver接続ではクライアント側にWalletを用意しなくても良くなりました。

そのため、ADB-Sでの接続構成は、
- mTLSのみ(デフォルト)
- mTLS接続およびTLS接続
のどちらかを選択できます。

ADB-Sに関する新機能は以下を確認してください。
[What’s New for Oracle Autonomous Database on Shared Exadata Infrastructure](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsn/index.html)

## 構成
今回の検証構成は以下となります。
![Pasted image 20211030122023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45466/115a235a-081f-ef9a-51a0-2afd8ff17a2c.png)


Virtual Machine では Oracle Cloud Developer Image を利用しました。あらかじめ各種ツールが入っているのでちょっとした検証では便利に利用できます。
Oracle Linux 7.9、JDK8、Oracle Instant Client 19.3 を用意してATPへの接続を確認します。

ADB-SのTLS接続のネットワーク・アクセスの前提条件については以下のマニュアル記述をご確認ください。
[Network Access Prerequisites for TLS Connections](https://docs.oracle.com/en/cloud/paas/autonomous-database/adbsa/support-tls-mtls-authentication.html#GUID-4C2093B7-4DF0-4CF4-BB2D-33E1FC5D5184)

ADB-SとしてはOracle Autonomous Transaction Processing(ATP)を利用し、Private Endpointを使用したネットワーク・アクセス構成とします。
ATPのインスタンスを作成する際に Private Endpointを選択すると以下のようにNetwork Security Groupを要求されます。
![Pasted image 20211030130129.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45466/e1ea52fe-b9be-c57e-81a6-0856dd3b8d35.png)
Network Security Group 「NSG for ATP」の内容は以下の通りです。
1. ソース・タイプ: NSG、ソース: NSG for AP、プロトコル: TCP、宛先ポート範囲: 1522
2. ソース・タイプ: NSG、ソース: NSG for AP、プロトコル: TCP、宛先ポート範囲: 1521
![Pasted image 20211030130952.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45466/95da22ba-e816-2446-ab6d-c4a487e67afe.png)
ちなみに、Network Security Group「NSG for AP」はVirtual Machine に設定しています。

もしもATPインスタンス作成時に「相互TLS(mTLS)認証が必要」のチェックをいれたままインスタンスを作成しても、コンソールより変更が可能です。
![Pasted image 20211030131912.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45466/096707f9-b626-c05c-94c7-b50adaddd862.png)

## まずは接続確認
まずは以下のBlog情報を確認します。
[Connecting to Your Autonomous Database Has Never Been Easier](https://blogs.oracle.com/datawarehousing/post/connecting-your-autonomous-database-has-never-been-easier)
> -   **No wallet downloads required**. For connections using JDBC Thin Driver with JDK8 or higher, a wallet is not required. This includes connections coming from the clients such as SQL Developer and SQL Command Line (SQLcl) as well.

Oracle Cloud Developer ImageにはSQLclがあらかじめインストールされていますのでSQLclを使って確認してみます。
OCIコンソールのATPインスタンスの「DB接続」でTLS認証を選択し、適当な接続文字列をコピーします。
![Pasted image 20211030133249.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45466/b456aef9-c2ea-990e-5d9c-17b8b8d6d3a1.png)

SQLclに接続文字列として上記でコピーした内容指定して実行します。

```
[opc@test01 ~]$ which sql
/usr/bin/sql
[opc@test01 ~]$ sql admin@'(description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1522)(host=atp20211030.adb.ap-tokyo-1.oraclecloud.com))(connect_data=(service_name=sya6vphk3pzlkhq_atp20211030_tp.adb.oraclecloud.com))(security=(ssl_server_cert_dn="CN=adb.ap-tokyo-1.oraclecloud.com, OU=Oracle ADB TOKYO, O=Oracle Corporation, L=Redwood City, ST=California, C=US")))'


SQLcl: Release 21.2 Production on Sat Oct 30 04:34:37 2021

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Password? (**********?) *************
Last Successful login time: Sat Oct 30 2021 04:34:46 +00:00

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.13.0.1.0

SQL>
```

接続できました。
## Javaアプリケーションからの接続確認
次にJDK8、Oracle Instant Client 19.3 を利用したJavaアプリケーションからの接続確認を実施します。
私はjavaのスキルに乏しいので、[こちら](https://qiita.com/ora_gonsuke777/items/2ab32f37cc91ae4a137b)の ora_gonsuke777 さんのテストアプリケーションを利用させていただきました。(感謝！！)

### Walletありの接続
mTLS接続を確認します。

```
[opc@test01 ~]$ cd /home/opc/atp01
[opc@test01 atp01]$ ls -la
total 72
drwxrwxr-x.  2 opc opc  4096 Oct 30 01:12 .
drwx------. 10 opc opc  4096 Oct 30 04:26 ..
-rw-rw-r--.  1 opc opc  6701 Oct 30 00:55 cwallet.sso
-rw-rw-r--.  1 opc opc  6656 Oct 30 00:55 ewallet.p12
-rw-rw-r--.  1 opc opc  3191 Oct 30 00:55 keystore.jks
-rw-rw-r--.  1 opc opc   691 Oct 30 00:55 ojdbc.properties
-rw-rw-r--.  1 opc opc  3095 Oct 30 00:55 README
-rw-rw-r--.  1 opc opc   115 Oct 30 01:07 sqlnet.ora
-rw-rw-r--.  1 opc opc  1925 Oct 30 00:55 tnsnames.ora
-rw-rw-r--.  1 opc opc  3378 Oct 30 00:55 truststore.jks
-rw-r--r--.  1 opc opc 21630 Oct 30 00:55 Wallet_atp20211030.zip
[opc@test01 atp01]$
```

walletのsqlnet.ora のDIRECTORY記述を修正します。

```
WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="?/network/admin")))
↓ 
WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/home/opc/atp01")))
```

サンプルコードのJDBC接続文字列をthin driverに変更し、TNS_ADMINの設定を追加します。(接続文字列にTNS_ADMINを含めることができるのはJDBC driver 18.3以降)

``` java
import java.sql.*;

public class GetContainerName {
    public static void main(String[] args) {
        final String path = "jdbc:oracle:thin:@atp20211030_tp?TNS_ADMIN=/home/opc/atp01";
        //final String path = "jdbc:oracle:oci:@atp20211030_tp";
        final String id = "ADMIN";  //ID
        final String pw = "xxxxxxxxxx";  //password

        try {
            Connection conn = DriverManager.getConnection(path, id, pw);
            Statement  stmt = conn.createStatement();
            ResultSet  rs   = stmt.executeQuery("SELECT NAME FROM V$CONTAINERS");
            while (rs.next()) {
                String cn = rs.getString("name");
                System.out.println("Container Name => " + cn);
            }
        } catch(SQLException ex) {
            ex.printStackTrace();  //Error
        }
    }
}
```

詳細はこちらのOracle Support提供のドキュメントを確認してください。
[How to Connect to Oracle Autonomous Database Using JDBC Thin that Requires Oracle Wallet?  (Doc ID 2746419.1)](https://support.oracle.com/epmos/faces/DocumentDisplay?_afrLoop=417319162211021&id=2746419.1)
また、上記のドキュメントにリンクが記述されている公開情報として以下のサイトの内容も確認してください。
[JDBC Trouble Shooting Tips for Oracle Autonomous Database (ATP and ADW)](https://www.oracle.com/database/technologies/application-development/jdbc-eecloud-troubleshooting-tips.html)

なお、JDBC Thin driver 接続においては、Oracle Instant Client 19.3に含まれている ojdbc8.jar だけでは接続できず、JDBC Trouble Shooting Tips for Oracle Autonomous Database (ATP and ADW) の #3: java.security.KeyStoreException: SSO not found に記述されている対策を取る必要がありました。
具体的には[Oracle Database 19c (19.3) JDBC Driver & UCP Downloads](https://www.oracle.com/database/technologies/appdev/jdbc-ucp-19c-downloads.html) のサイトからoraclepki.jar 、osdt_cert.jar 、osdt_core.jar をダウンロードし、Javaアプリケーションのclasspath指定に含めることで解決しました。

実行コマンドは以下の通り。

``` shell
echo ${JAVA_HOME}
echo ${PATH}
echo $LD_LIBRARY_PATH
which java
java -version
javac -version
pwd
ls *.jar
ls /home/opc/instantclient_19_13/ojdbc8.jar
javac GetContainerName.java
echo java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:oraclepki.jar:osdt_core.jar:osdt_cert.jar:. GetContainerName
java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:oraclepki.jar:osdt_core.jar:osdt_cert.jar:. GetContainerName
```

実行結果は以下の通り。

``` shell
[opc@test01 ~]$ sh GetContainerName.sh
/usr/java/jdk1.8.0_301-amd64
/usr/java/jdk1.8.0_301-amd64/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/usr/lib/oracle/21/client64/bin:/home/opc/.local/bin:/home/opc/bin

/usr/java/jdk1.8.0_301-amd64/bin/java
java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)
javac 1.8.0_301
/home/opc
oraclepki.jar  osdt_cert.jar  osdt_core.jar
/home/opc/instantclient_19_13/ojdbc8.jar
java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:oraclepki.jar:osdt_core.jar:osdt_cert.jar:. GetContainerName
Container Name => SYA6VPHK3PZLKHQ_ATP20211030
[opc@test01 ~]$
```

### Walletなしの接続
TLS接続かつWalletなしの接続を試します。SQLcl で確認した際の文字列を埋め込んでいます。
サンプルコードの接続文字列を変更します。

``` java
import java.sql.*;

public class GetContainerNameNoWallet {
    public static void main(String[] args) {
        final String path = "jdbc:oracle:thin:@(description= (retry_count=20)(retry_delay=3)(address=(protocol=tcps)(port=1521)(host=atp20211030.adb.ap-tokyo-1.oraclecloud.com))(connect_data=(service_name=sya6vphk3pzlkhq_atp20211030_high.adb.oraclecloud.com))(security=(ssl_server_cert_dn=\"CN=adb.ap-tokyo-1.oraclecloud.com, OU=Oracle ADB TOKYO, O=Oracle Corporation, L=Redwood City, ST=California, C=US\")))";
        final String id = "ADMIN";  //ID
        final String pw = "xxxxxxxxxx";  //password

        try {
            Connection conn = DriverManager.getConnection(path, id, pw);
            Statement  stmt = conn.createStatement();
            ResultSet  rs   = stmt.executeQuery("SELECT NAME FROM V$CONTAINERS");
            while (rs.next()) {
                String cn = rs.getString("name");
                System.out.println("Container Name => " + cn);
            }
        } catch(SQLException ex) {
            ex.printStackTrace();  //Error
        }
    }
}
```

mTLS接続ではないので、classpath指定は ojdbc8.jar のみで構いません。
実行コマンドは以下の通り.

``` shell
echo ${JAVA_HOME}
echo ${PATH}
echo $LD_LIBRARY_PATH
which java
java -version
javac -version
pwd
ls /home/opc/instantclient_19_13/ojdbc8.jar
javac GetContainerNameNoWallet.java
echo java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:. GetContainerNameNoWallet
java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:. GetContainerNameNoWallet
```

実行結果は以下の通り。

``` shell
[opc@test01 ~]$ sh GetContainerNameNoWallet.sh
/usr/java/jdk1.8.0_301-amd64
/usr/java/jdk1.8.0_301-amd64/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/usr/lib/oracle/21/client64/bin:/home/opc/.local/bin:/home/opc/bin

/usr/java/jdk1.8.0_301-amd64/bin/java
java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)
javac 1.8.0_301
/home/opc
/home/opc/instantclient_19_13/ojdbc8.jar
java -classpath /home/opc/instantclient_19_13/ojdbc8.jar:. GetContainerNameNoWallet
Container Name => SYA6VPHK3PZLKHQ_ATP20211030
[opc@test01 ~]$
```

TLS接続かつJDBC Thin driver 接続のJavaアプリケーションでwallet無しの接続が確認できました。

余談: 上述のwallet無し接続確認時、TNS_ADMIN 環境変数が設定されていると接続できなかったのでご注意ください。