Alibaba Cloud は、Java アプリケーションのクライアント側暗号化を簡素化する JDBC ラッパードライバである EncJDBC を提供しています。 バックグラウンドで暗号文を透過的に復号するため、既存の JDBC コードへの変更は最小限で済みます。 このガイドでは、EncJDBC を Maven プロジェクトに統合し、安全なデータベース接続を構成し、データをクエリする方法について説明します。
EncJDBC ドライバは、マスター暗号化キー(MEK)を使用して、安全なエンドツーエンドの暗号化接続を確立します。 そのクライアントは自動的に復号し、プレーンテキストデータを返します。 これにより、コードは、接続設定に最小限の変更を加えるだけで、プレーンテキストであるかのように機密データを操作できます。
前提条件
開始する前に、次の設定が完了していることを確認してください。
機密データ検出スキャンを実行して、暗号化する列を特定しました。 詳細については、「機密データ検出」をご参照ください。
ターゲットデータベースの列暗号化を有効にし、ターゲットデータベースアカウントに [暗号文権限(JDBC 復号)] を付与しました。 詳細については、「列暗号化」をご参照ください。
Always-Confidential Database 機能が有効になっている RDS インスタンスの接続情報を取得しました。 接続情報には、ドメイン名(ホスト)、ポート番号(ポート)、インスタンス名(dbname)、ユーザー名(ユーザー名)、およびパスワード(パスワード)が含まれます。
MEK の生成
MEK は、クライアントアプリケーションが暗号化データにアクセスすることを承認するルートクレデンシャルです。 これは次のように動作します。
クライアントは、安全な非対称キー暗号化プロトコルを介してキーをデータベースサーバーに送信します。 このプロセスにより、サーバーとクライアントは同じキーを共有できるため、対称暗号化を使用してデータを安全に送信できます。
値は、16 バイトのデータを表す 32 文字の 16 進数文字列である必要があります。
MEK は、クライアントが暗号化データにアクセスすることを承認するために使用するルートクレデンシャルです。 セキュリティを確保するために、Always-Confidential Database 機能は MEK を生成、保存、またはバックアップしません。 MEK を手動で生成し、MEK が安全に保存されていることを確認する必要があります。 Always-Confidential Database 機能が有効になっているデータベースのセキュリティを確保するために、MEK を安全な方法で保存および管理する必要があります。 MEK をバックアップすることをお勧めします。
データベースの列暗号化構成で、[暗号化方式] で [KMS キー] を選択するか、[ローカルキー] を生成して、データベースの復号用のマスター暗号化キー(MEK)として使用します。
KMS キー
KMS キーを使用する場合は、KMS サービスが使用可能であることを確認してください。 そうでない場合、EncJDBC は使用できません。
指定された KMS キーが属する KMS インスタンスのエンドポイント、および KMS の復号権限を持つ対応する Alibaba Cloud アカウントまたは Resource Access Management (RAM) ユーザーの AccessKey ID と AccessKey シークレットを取得する必要があります。これにより、クライアントは KMS キーを読み取ることができます。以下の操作を実行できます。
Alibaba Cloud アカウント または RAM ユーザー を使用して、Alibaba Cloud 管理コンソールにログインします。
ローカルキー
[暗号化方式] パラメータを [ローカルキー] に設定する場合は、MEK を生成する必要があります。 例:00112233445566778899aabbccddeeff。
パスワードジェネレータ、またはプログラミング言語によって提供される random() 関数を使用して、MEK を生成できます。
例:
Linux:OpenSSL はプリインストールされています。
openssl rand -hex 16コマンドを実行して、キーを生成できます。Windows:OpenSSL ソフトウェアパッケージ をインストールする必要があります。
クライアントの構成
Java は JDK 1.8 以降を使用します。
クライアント側で、データベース接続ドライバを EncJDBC に変更し、データベース接続 URL を更新し、MEK を指定して、必要なデータベースの暗号化された列のプレーンテキストデータにアクセスします。
1. 依存関係を追加する
Maven のプロジェクトの pom.xml ファイルに次の依存関係を追加します。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-cls-jdbc</artifactId>
<version>1.0.10-1</version>
</dependency>2. MEK を構成してデータベースに接続する
MEK を構成するには、JDBC プロパティ構成、構成ファイル、URL 構成などの方法を使用できます。 2 つ以上の方法を使用して EncJDBC パラメータを構成する場合、これらの方法は JDBC プロパティ構成、構成ファイル、URL 構成の順に優先されます。
URL 構成方法を使用する場合は、アンパサンド(
&)を使用して複数のパラメータを連結できます。以下の方法では、
MEKはローカルクライアントで構成され、エンベロープ暗号化を使用してサーバーに配布されるため、MEKが漏洩することはありません。
暗号化構成時に指定する暗号化メソッドに基づいて、KMS キーまたはローカルキーを使用します。
KMS キー
STS(Security Token Service)によって提供される一時的なアクセス認証情報を使用して KMS 管理の MEK を取得する場合は、STS SDK を使用して一時的な STS トークンを取得できます。 詳細については、「STS SDK の概要」をご参照ください。
ビジネスコードに AccessKey ペアをハードコードすることはできません。 この例では、システム環境変数を構成して AccessKey ペアを管理する方法について説明します。 詳細については、「Linux、macOS、および Windows での環境変数の構成」をご参照ください。
JDBC プロパティ構成
標準 JDBC からインスタンスに接続する場合は、Properties 設定を構成する必要があります。 コード例:
// 実際のシナリオに基づいて、エンドポイント(hostname)、ポート番号(port)、インスタンス名(dbname)、ユーザー名(username)、パスワード(password)などの接続情報を更新します。
// ...
String hostname = "your-hostname"; // エンドポイント
String port = "your-port"; // ポート番号
String dbname = "your-database-name"; // インスタンス名
String username = "your-username"; // ユーザー名
String password = "your-password"; // パスワード
// 環境変数から AccessKey ID と AccessKey シークレットを取得します。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// STS 一時アクセス認証情報を使用して KMS キーを読み取る場合は、取得した STS セキュリティトークン(SecurityToken)も入力する必要があります。
// String stsToken= "yourSecurityToken";
// KMS インスタンスエンドポイント。パブリックネットワークアクセスにはパブリックエンドポイントを使用します。 VPC ネットワークアクセスには、インスタンス VPC エンドポイントを使用します。
String kmsEndpoint = "kms.cn-hangzhou.aliyuncs.com"; // KMS インスタンスエンドポイント
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_ID", accessKeyId);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET", accessKeySecret);
props.setProperty("ALIBABA_CLOUD_KMS_ENDPOINT", kmsEndpoint);
// props.setProperty("ALIBABA_CLOUD_STS_TOKEN","stsToken");
// MySQL データベースの接続 URL 形式「jdbc:mysql:encdb://%s:%s/%s」。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// MySQL データベースの EncJDBC ドライバを読み込みます。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// ... クエリを開始 ...URL 構成
URL で MEK を指定できます。 コード例:
// 実際のシナリオに基づいて、エンドポイント(hostname)、ポート番号(port)、インスタンス名(dbname)、ユーザー名(username)、パスワード(password)などの接続情報を更新します。
// ...
String hostname = "your-hostname"; // エンドポイント
String port = "your-port"; // ポート番号
String dbname = "your-database-name"; // インスタンス名
String username = "your-username"; // ユーザー名
String password = "your-password"; // パスワード
// 環境変数から AccessKey ID と AccessKey シークレットを取得します。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// STS 一時アクセス認証情報を使用して KMS キーを読み取る場合は、取得した STS セキュリティトークン(SecurityToken)も入力する必要があります。
// String stsToken= "yourSecurityToken";
// KMS インスタンスエンドポイント。パブリックネットワークアクセスにはパブリックエンドポイントを使用します。 VPC ネットワークアクセスには、インスタンス VPC エンドポイントを使用します。
String kmsEndpoint = "kms.cn-hangzhou.aliyuncs.com"; // KMS インスタンスエンドポイント
// MySQL データベースの接続 URL 形式。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s", hostname, port, dbname, accessKeyId,accessKeySecret,kmsEndpoint);
// STS トークンを使用します。
// String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s&ALIBABA_CLOUD_STS_TOKEN=%s", hostname, port, dbname, accessKeyId,accessKeySecret,kmsEndpoint,stsToken);
// MySQL データベースの EncJDBC ドライバを読み込みます。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, username, password);
// ... クエリを開始 ...ローカルキー
JDBC プロパティ構成
標準 JDBC は、接続時に Properties を介してユーザー定義のプロパティを設定できます。 JDBC プロパティを構成して JDBC を実行する例を次に示します。
// 接続アドレス(hostname)、ポート(port)、データベースインスタンス名(dbname)、ユーザー名(username)、パスワード(password)などの接続情報を準備します。
// ...
String hostname = "your-hostname"; // エンドポイント
String port = "your-port"; // ポート番号
String dbname = "your-database-name"; // データベースインスタンス名
String username = "your-username"; // ユーザー名
String password = "your-password"; // パスワード
String mek = "00112233445566778899aabbccddeeff"; // マスターキー
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
// MySQL データベースの接続 URL 形式「jdbc:mysql:encdb://%s:%s/%s」。 PostgreSQL データベースの場合、接続 URL 形式を「jdbc:postgresql:encdb://%s:%s/%s」に置き換える必要があります。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// MySQL データベースの EncJDBC ドライバを読み込んでいます。 PostgreSQL データベースの場合、EncJDBC ドライバの読み込みを「com.aliyun.encdb.postgresql.jdbc.EncDriver」に置き換える必要があります。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// ... クエリを開始 ...ファイル構成
パラメータは構成ファイルからインポートでき、必要な MEK とその他のパラメータを構成できます。
ファイル構成方法は、ローカルキー MEK の構成にのみ適しています。
プロジェクトに encJdbcConfigFile という名前の property を設定し、その値を構成ファイルパスに設定できます(デフォルトでは、ファイル encjdbc.conf を使用します)。 構成ファイルの内容は次のとおりです。
MEK=00112233445566778899aabbccddeeff構成ファイルは、次の 2 つの方法で配置できます。
以下に示すように、ファイルをプロジェクトの resources ディレクトリに配置します。

プログラムのランタイムディレクトリであるプロジェクト ルートディレクトリにファイルを配置します。
ファイル構成方法では、ファイル構成を完了した後、以下に示すように、プログラムで追加の構成を行う必要はありません。
// 接続アドレス(hostname)、ポート(port)、データベースインスタンス名(dbname)、ユーザー名(username)、パスワード(password)などの接続情報を準備します。
// ...
String hostname = "your-hostname"; // エンドポイント
String port = "your-port"; // ポート
String dbname = "your-database-name"; // データベースインスタンス名
String username = "your-username"; // ユーザー名
String password = "your-password"; // パスワード
// MySQL データベースの接続 URL 形式「jdbc:mysql:encdb://%s:%s/%s」。 PostgreSQL データベースの場合、接続 URL 形式を「jdbc:postgresql:encdb://%s:%s/%s」に置き換える必要があります。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// MySQL データベースの EncJDBC ドライバを読み込んでいます。 PostgreSQL データベースの場合、EncJDBC ドライバの読み込みを「com.aliyun.encdb.postgresql.jdbc.EncDriver」に置き換える必要があります。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, username, password);
// ... クエリを開始 ...URL 構成
MEK などのパラメータは、URL リンクに埋め込むことができます。 以下に示すように:
// 接続アドレス(hostname)、ポート(port)、データベースインスタンス名(dbname)、ユーザー名(username)、パスワード(password)などの接続情報を準備します。
// ...
String hostname = "your-hostname"; // エンドポイント
String port = "your-port"; // ポート
String dbname = "your-database-name"; // データベースインスタンス名
String username = "your-username"; // ユーザー名
String password = "your-password"; // パスワード
String mek = "00112233445566778899aabbccddeeff"; // マスターキー
// MySQL データベースの接続 URL 形式「jdbc:mysql:encdb://%s:%s/%s?MEK=%s」。 PostgreSQL データベースの場合、接続 URL 形式を「jdbc:postgresql:encdb://%s:%s/%s?MEK=%s」に置き換える必要があります。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?MEK=%s", hostname, port, dbname, mek);
// MySQL データベースの EncJDBC ドライバを読み込んでいます。 PostgreSQL データベースの場合、EncJDBC ドライバの読み込みを「com.aliyun.encdb.postgresql.jdbc.EncDriver」に置き換える必要があります。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, username, password);
// ... クエリを開始 ...3. 暗号化された列のプレーンテキストデータをクエリする
必要なデータベースに接続した後、標準 JDBC と同じ方法でデータベースをクエリできます。 EncJDBC は暗号化された列を自動的に復号し、プレーンテキストデータを返します。
コード例:
// クエリを開始します。
// クエリ文を作成します。
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table_name"); // クエリ文
// 結果セットをトラバースします。
while (resultSet.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");
}よくある質問
プログラムを実行すると
Exception in thread "main" java.lang.IllegalAccessError: class com.alibaba.encdb.common.SymCrypto (in unnamed module @0x5c0369c4) cannot access class com.sun.crypto.provider.SunJCE (in module java.base) because module java.base does not export com.sun.crypto.provider to unnamed module @0x5c0369c4というエラーメッセージが表示される場合はどうすればよいですか?JDK バージョンが必要なバージョンより後の場合に発生するモジュール間の権限の問題が原因で、エラーメッセージが表示される場合があります。 エラーを解決するには、プログラムを実行するときに VM オプション
--add-exports=java.base/com.sun.crypto.provider=ALL-UNNAMEDを追加して、com.sun.crypto.provider を名前のないモジュールにエクスポートする必要があります。プログラムを実行すると
failed in mek provision: you might have an incorrect mek setting. Detail:gcmEncrypt errorというエラーメッセージが表示される場合はどうすればよいですか?このエラーは、Oracle JDK での一般的なエラーです。 エラーを解決するには、次のいずれかの方法を使用できます。
代わりに Amazon Corretto を使用します。
Oracle JDK を使用する場合は、次の手順を実行してセキュリティプロバイダを構成する必要があります。
JDK インストールパスを見つけます。
Installation path/conf/security/ディレクトリで、java.securityファイルを見つけます。java.securityファイルを編集します。List of providers and their preference orders (see above):セクションに、次の内容を追加します。security.provider.14=org.bouncycastle.jce.provider.BouncyCastleProvider

