先日、RDS に MySQL Workbench から SSL 接続をしましたが、今日は JDBC からの SSL 接続に挑戦します。
軽くググってみると、証明書を検証しない下記のような接続方法は沢山出てきます。
jdbc:mysql://dbserver:3306/dbname?useSSL=true&requireSSL=true&verifyServerCertificate=false
証明書の検証をしないと SSL の意味がないので、今回は verifyServerCertificate=true にする方法を調べてみました。
以下、作業メモ。
† JKS ファイルを作る
Connector/J のリファレンスに目を通してみると、サーバ証明書を検証するためには trustCertificateKeyStoreUrl、trustCertificateKeyStoreType、trustCertificateKeyStorePasswordの3つのプロパティを設定してやる必要がありそうです。問題は証明書が格納される trustCertificateKeyStoreType で、前述のリファレンスによれば形式は "JKS" and "PKCS12" でなければなりません。Amazon から配布されている証明書は PEM 形式なので、そのままでは使うことができません。
というわけで、.pem を .jks (JKS形式)に変換してやります。これはJava に付属している keytool を使って下記のように行います。
出力が長いですが、入力する項目は パスワードをいれる部分(2回)と最後の yes と答える部分の合わせて3回だけです。
# wget https://rds.amazonaws.com/doc/mysql-ssl-ca-cert.pem
# keytool -keystore amazon.jks -importcert -file mysql-ssl-ca-cert.pem
Enter keystore password:(←見えないけどここで123456というパスワードを入力しています。)
Re-enter new password:(←見えないけどここで123456というパスワードを入力しています。)
Owner: CN=aws.amazon.com/rds/, OU=RDS, O=Amazon.com, L=Seattle, ST=Washington, C=US
Issuer: CN=aws.amazon.com/rds/, OU=RDS, O=Amazon.com, L=Seattle, ST=Washington, C=US
Serial number: e775b657e21a8128
Valid from: Tue Apr 06 07:44:31 JST 2010 until: Sun Apr 05 07:44:31 JST 2015
Certificate fingerprints:
MD5: 36:FD:8B:25:3E:E3:53:FC:E8:82:D1:2E:5C:CC:8C:C6
SHA1: 7F:09:8D:A5:7D:BB:A6:EF:7C:70:D8:CA:4E:49:11:55:7E:89:A7:D3
Signature algorithm name: SHA1withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: FF 1F 7C 7E 72 A4 A4 47 F7 8F 48 88 A7 3E D7 38 ....r..G..H..>.8
0010: C9 62 9E DC .b..
]
]
#2: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
CA:true
PathLen:2147483647
]
#3: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: FF 1F 7C 7E 72 A4 A4 47 F7 8F 48 88 A7 3E D7 38 ....r..G..H..>.8
0010: C9 62 9E DC .b..
]
[CN=aws.amazon.com/rds/, OU=RDS, O=Amazon.com, L=Seattle, ST=Washington, C=US]
SerialNumber: [ e775b657 e21a8128]
]
Trust this certificate? [no]: yes
Certificate was added to keystore
† JKSを使って JDBC 接続をする
あとはこんな感じのテストプログラムを作って動かしてみました。
今回はテストなので、前の手順で作成した jks ファイルはクラスパスに放り込んでおいて、クラスローダーからパスを参照します。
MySQLSSLTest.java
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public final class MySQLSSLTest {
public static void main(final String[] args) throws SQLException {
final String server = "rdsdb.ap-northeast-1.rds.amazonaws.com";
final String username = "rds_user";
final String password = "rds_password";
final String jksFile = "amazon.jks";
final String jksPassword = "123456";
URL jks = ClassLoader.getSystemResource(jksFile);
final String url = "jdbc:mysql://" + server + "/mysql?useSSL=true"
+ "&requireSSL=true" + "&verifyServerCertificate=true"
+ "&trustCertificateKeyStoreUrl=" + jks.toExternalForm()
+ "&trustCertificateKeyStoreType=JKS"
+ "&trustCertificateKeyStorePassword=" + jksPassword;
Connection con = DriverManager.getConnection(url, username, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SHOW VARIABLES LIKE 'hostname'");
while (rs.next()) {
System.out.println(rs.getString(1) + ": " + rs.getString(2));
}
rs.close();
st.close();
st = con.createStatement();
rs = st.executeQuery("SHOW STATUS LIKE 'Ssl_cipher'");
while (rs.next()) {
System.out.println(rs.getString(1) + ": " + rs.getString(2));
}
rs.close();
st.close();
con.close();
}
}
あとはプログラムを起動して、こんな感じでホスト名と暗号化の方式が帰ってくればきちんと SSL 接続されています。
hostname: ip-XXX-XXX-XXX-XXX
Ssl_cipher: AES128-SHA