はじめに
AWS Key Management Service (KMS) とは、データの暗号化に使用する暗号化キーを管理するためのマネージドサービスです。 KMS では、エンベロープ暗号化という暗号化方式を用いることでデータをセキュアに取り扱うことができます。
エンベロープ暗号化の Java での実装例について、日本語のドキュメントが少ないためまとめてみました。
エンベロープ暗号化とは
ざっくり説明すると、
- データ
- データを暗号化するための鍵(データキー)
- 鍵を暗号化するための鍵(マスターキー)
を使い分けることでセキュアにしようよ、と言う仕組みのことを言います。
一般的な暗号化方式では、平文のデータを何らかの鍵を使って暗号化することで保護します。このとき、この鍵がデータとセットで盗まれてしまうと、暗号を解かれてしまい、生のデータを見られてしまいます。そこで、データを暗号化した鍵を、別の鍵でさらに暗号化することでデータをより強固に保護しよう、というのがエンベロープ暗号化の考え方です。
KMS を使ったデータの暗号化は、以下の流れで実現できます。
- KMS のコンソール画面でマスターキーを生成する
- マスターキーからデータキーを生成する
- データキーを使って平文のデータを暗号化する
- データキーを暗号化する
- 暗号化済みのデータキーと暗号化済みのデータを大切に保管する
実装例
エンベロープ暗号化の実現方法について、 Java での実装例を以下に示します。
なお、マスターキーは KMS のコンソール画面から事前に生成済み、という前提で話を進めます。マスターキーの生成手順については、AWS のマニュアルか Developers.IO 等の記事を参考にしてください。
事前準備
KMS の利用にあたっては AWS から SDK が提供されています。ここでは Gradle を介してライブラリを使用する場合の例を示します。 build.gradle
に以下の dependency を追加してください。
dependencies {
compile "com.amazonaws:aws-java-sdk-kms:1.11.202"
}
暗号化
まずは、 AWS のアクセスキー、シークレットキーを使用して AWSKMSClient
のインスタンスを生成します。
BasicAWSCredentials credentials = new BasicAWSCredentials("accessKey...", "SecretKey...");
AWSKMSClient client = (AWSKMSClient) AWSKMSClientBuilder.standard()
.withRegion("ap-northeast-1")
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.build();
生成したクライアントを使用して、データキーを生成します。 KeyId に KMS のコンソールから生成したマスターキーの ARN を指定して GenerateDataKeyRequest
のインスタンスを生成し、 generateDataKey
することでデータキーを取得できます。
GenerateDataKeyRequest generateDataKeyRequest = new GenerateDataKeyRequest()
.withKeyId("arn:aws:kms:...")
.withKeySpec(DataKeySpec.AES_128);
GenerateDataKeyResult dataKeyResult = client.generateDataKey(generateDataKeyRequest);
生成したデータキーを使って、平文のデータを暗号化します。ここでは AES を使って暗号化していますが、暗号化方式は任意のものをお使いください。
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(dataKeyResult.getPlaintext().array(), "AES"));
String decriptedText = Base64.getEncoder()
.encodeToString(cipher.doFinal("plainText...".getBytes()));
} catch (Exception e) {
// do something.
}
生成された decriptedText
と 暗号化済みのデータキー(dataKeyResult.getCiphertextBlob()
で暗号化済みのものを取得できます)は、 Base64 で encode して、 yaml ファイル等に書き写して保管しておきましょう。
復号化
続いて、暗号化済みデータの復号化の手順です。復号化は、暗号化済みのデータキーから decryptRequest
を生成することで行います。
DecryptRequest decryptRequest = new DecryptRequest()
.withCiphertextBlob(ByteBuffer.wrap(Base64.getDecoder().decode("decryptedDatakey...".getBytes())));
ByteBuffer plainTextKey = client.decrypt(decryptRequest).getPlaintext();
ここまでの手順で、暗号化済みのデータキーを平文のデータキーに戻すことができました。続いて、手に入れたデータキーを用いて暗号化済みのデータを復号化します。
byte[] decodeBase64src = Base64.getDecoder().decode("decryptedText...");
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, plainTextKey);
String plainText = new String(cipher.doFinal(decodeBase64src));
} catch (Exception e) {
// do something.
}
取り出した平文のデータキー( plainTextKey
)は速やかに破棄しましょう。
plainTextKey.clear();