EncJDBC は、Java アプリケーションが完全暗号化データベースに接続し、クエリロジックを変更することなく、暗号化された列のデータを透過的に復号するための JDBC ドライバーです。
マスター暗号化キー (MEK) を設定すると、EncJDBC は自動的に暗号文を復号し、プレーンテキストをアプリケーションに返します。暗号化と復号のプロセスは、アプリケーションコードからは見えません。
仕組み
EncJDBC は 2 段階のプロセスを使用します。
-
接続時に、EncJDBC はエンベロープ暗号化を使用して MEK をサーバーに配布し、キーがプレーンテキストで送信されないようにします。
-
クエリごとに、EncJDBC は暗号化対象列のパラメーターを自動的に暗号化し、結果を復号してからアプリケーションに返します。
アプリケーションコードは、引き続きプレーンテキスト値を扱います。クエリロジックの変更は不要です。
前提条件
開始する前に、以下をご確認ください:
-
お使いの ApsaraDB RDS for MySQL インスタンスで完全暗号化データベース機能が有効になっていること。詳細については、「完全暗号化データベース機能の有効化」をご参照ください。
-
インスタンスの接続情報 (ホスト名、ポート、データベース名、ユーザー名、パスワード)
-
データ保護ルールが設定されていること。詳細については、「データ保護ルールの設定」をご参照ください。
-
JDK 1.8 以降
このトピックの例では、Maven 3.9.2 と IntelliJ IDEA Community Edition 2022.3.2 を使用しています。
MEK は安全に保管し、決して公開しないでください。紛失した場合、暗号化されたデータは永久にアクセスできなくなります。お使いの RDS インスタンスは、MEK を生成、保存、またはバックアップすることはありません。
Maven 依存関係の追加
プロジェクトの pom.xml ファイルに以下の依存関係を追加します。
<dependencies>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-encdb-mysql-jdbc</artifactId>
<version>1.0.9-1</version>
</dependency>
</dependencies>
設定と接続
EncJDBC は、JDBC URL 形式 jdbc:mysql:encdb://{hostname}:{port}/{dbname} とドライバークラス com.aliyun.encdb.mysql.jdbc.EncDriver を使用します。
接続する前に、2 つの必須パラメーターを設定します。
| パラメーター | 値の例 | 説明 |
|---|---|---|
MEK |
00112233445566778899aabbccddeeff |
お客様が生成および管理するマスター暗号化キー (MEK) です。有効な値:32 文字の 16 進数文字列 (16 バイト)。openssl rand -hex 16、言語の乱数関数、または Key Management Service (KMS) を使用して生成します。 |
ENC_ALGO |
SM4_128_CBC |
暗号化アルゴリズムです。デフォルト:SM4_128_GCM。 |
対応暗号化アルゴリズム:
| カテゴリ | アルゴリズム |
|---|---|
| AES (国際標準) | AES_128_GCM、AES_128_CTR、AES_128_CBC、AES_128_ECB (非推奨) |
| SM4 (中国国家標準) | SM4_128_GCM (デフォルト)、SM4_128_CTR、SM4_128_CBC、SM4_128_ECB (非推奨) |
AES_128_ECBとSM4_128_ECBは使用しないでください。これらのモードは強力なセキュリティを保証しません。
EncJDBC への設定の指定
方法は 3 つあります。複数の方法でパラメーターを設定した場合、以下の優先順位が適用されます。[JDBC プロパティ] > [設定ファイル] > [URL パラメーター]。
複数のパラメーターを & で連結できます。
オプション 1:JDBC プロパティ
接続を開く前に、Properties オブジェクトで MEK と ENC_ALGO を直接設定します。
// プレースホルダーを実際のインスタンス接続情報に置き換えます。
String hostname = "<hostname>";
String port = "<port>";
String dbname = "<dbname>";
String username = "<username>";
String password = "<password>";
String mek = "<your-mek>"; // 32 文字の 16 進数文字列
String encAlgo = "<enc-algo>"; // 例:SM4_128_GCM
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
props.setProperty("ENC_ALGO", encAlgo);
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, props);
オプション 2:設定ファイル
以下の内容で設定ファイルを作成します。
MEK=<your-mek>
ENC_ALGO=<enc-algo>
ファイルを以下のいずれかの場所に配置します。
-
プロジェクトの
resourcesディレクトリ -
プロジェクトのルートディレクトリ (実行時の作業ディレクトリ)
デフォルトのファイル名は encjdbc.conf です。カスタムパスを使用するには、システムプロパティ encJdbcConfigFile を設定ファイルのディレクトリに設定します。
ファイルを配置した後、コードで MEK または ENC_ALGO を指定せずに接続します。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, username, password);
オプション 3:URL パラメーター
MEK と ENC_ALGO を JDBC URL に直接追加します。
String dbUrl = String.format(
"jdbc:mysql:encdb://%s:%s/%s?MEK=%s&ENC_ALGO=%s",
hostname, port, dbname, mek, encAlgo
);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, username, password);
コード例
以下の例では、JDBC プロパティを使用して接続し、テーブルに行を挿入して、プレーンテキストで結果を取得します。
// プレースホルダーを実際のインスタンス接続情報に置き換えます。
String hostname = "hostname";
String port = "port";
String dbname = "db";
String username = "user";
String password = "password";
// 本番環境では、ランダムに生成された強力な MEK を使用してください。
String mek = "00112233445566778899aabbccddeeff";
String encAlgo = "SM4_128_CBC";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
props.setProperty("ENC_ALGO", encAlgo);
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
Connection connection = DriverManager.getConnection(dbUrl, props);
int[] intData = {1, 2, 3, 4, 5, 6};
String[] strData = {"abc", "bcd", "1", "def", "efg", "fgi"};
// テーブルを作成します。
connection.createStatement().executeUpdate("drop table if exists test");
connection.createStatement().executeUpdate("create table test (a int, b text)");
// PreparedStatement を使用して行を挿入します。
// EncJDBC は、暗号化対象列の値を自動的に暗号化します。
for (int i = 0; i < 6; i++) {
PreparedStatement pstmt = connection.prepareStatement("insert into test values (?,?)");
pstmt.setInt(1, intData[i]);
pstmt.setString(2, strData[i]);
pstmt.executeUpdate();
}
// 行をクエリします。EncJDBC は、結果を返す前に自動的に復号します。
ResultSet rs = connection.prepareStatement("select * from test").executeQuery();
while (rs.next()) {
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
System.out.print(rs.getString(i + 1));
System.out.print("\t");
}
System.out.print("\n");
}
出力例:
1 abc
2 bcd
3 1
4 def
5 efg
6 fgi
以下の点にご注意ください:
-
暗号化された列に対するすべての読み取りと書き込みは、
PreparedStatementを使用します。ドライバーはパラメーターを自動的に暗号化し、結果を復号します。クエリロジックの変更は必要ありません。 -
暗号化されていない列に渡された値は影響を受けません。
トラブルシューティング
IllegalAccessError: cannot access class com.sun.crypto.provider.SunJCE
このエラーは、新しいバージョンの JDK ではデフォルトでモジュール間のアクセスが制限されているために発生します。プログラムの実行時に、以下の VM オプションを追加してください。
--add-exports=java.base/com.sun.crypto.provider=ALL-UNNAMED
failed in mek provision: gcmEncrypt error
これは Oracle JDK の既知の問題です。以下のいずれかの修正を適用してください。
-
Amazon Corretto への切り替え — この問題が発生しない、ドロップインの OpenJDK ディストリビューションです。
-
Oracle JDK への BouncyCastle セキュリティプロバイダーの追加:
-
JDK のインストールディレクトリを見つけます。
-
<jdk-path>/conf/security/java.securityを開きます。 -
List of providers and their preference ordersセクションに、security.provider.14=org.bouncycastle.jce.provider.BouncyCastleProviderを追加します。
-