aws-sdk-java-v2-kms について
AWS SDK for Java 2.xでAWS KMSを利用するJava開発者向けのスキルです。キー管理、暗号化と復号、エンベロープ暗号化、デジタル署名、Spring Boot連携を扱います。
主な機能
- 01KMSキーの作成と管理
- 02データの暗号化と復号
- 03大きなデータ向けのエンベロープ暗号化
- 04デジタル署名の生成と検証
- 05Spring Bootとの連携
活用シーン
- 01データ保護用の対称キー設定
- 02データキーによる大きなデータの暗号化
- 03アプリケーションのデジタル署名設定
- 04暗号化サービスのSpring Bootへの組み込み
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill aws-sdk-java-v2-kmsインストール後、Claude Code で/aws-sdk-java-v2-kmsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/best-practices.mdspring-boot-integration.mdtechnical-guide.mdtesting.md詳細
AWS SDK for Java 2.x - AWS KMS(Key Management Service)
概要
AWS SDK for Java 2.x を使用した AWS KMS のパターンを提供します。キー管理、暗号化/復号、エンベロープ暗号化、デジタル署名、Spring Boot との統合を扱います。
手順
- IAM 権限を設定する - 最小権限の原則に従って kms:* アクションを許可します
- KMS クライアントを作成する - リージョンと認証情報を指定して KmsClient をインスタンス化します
- キーを作成する - createKey() を使用します → 続行する前に、キーの状態が ENABLED であることを確認します
- キーのポリシーを設定する - キーの使用権限を定義します → 本番環境の前にアクセスをテストします
- データを暗号化する - 4KB 未満のデータには encrypt() を使用します。暗号文が空でないことを確認します
- エンベロープ暗号化 - 大きなデータには generateDataKey() を使用します → データキーの生成が成功したことを確認します
- デジタル署名 - 署名キーを作成します → 署名/検証後に signatureValid=true であることを確認します
- キーのローテーション - 自動ローテーションを有効にします → ローテーションのスケジュールが有効であることを確認します
使用する場面
- データ保護のために対称暗号化キーを作成、管理する
- 大きなデータにエンベロープ暗号化を実装する
- KMS 管理キーを使用したローカル暗号化用のデータキーを生成する
- 非対称キーを使用してデジタル署名を設定する
- Spring Boot アプリケーションに暗号化を統合する
依存関係
Maven
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>kms</artifactId>
</dependency>
Gradle
implementation 'software.amazon.awssdk:kms:2.x.x'
クライアントの設定
基本的な同期クライアント
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kms.KmsClient;
KmsClient kmsClient = KmsClient.builder()
.region(Region.US_EAST_1)
.build();
基本的な非同期クライアント
import software.amazon.awssdk.services.kms.KmsAsyncClient;
KmsAsyncClient kmsAsyncClient = KmsAsyncClient.builder()
.region(Region.US_EAST_1)
.build();
高度なクライアント設定
KmsClient kmsClient = KmsClient.builder()
.region(Region.of(System.getenv("AWS_REGION")))
.credentialsProvider(DefaultCredentialsProvider.create())
.overrideConfiguration(c -> c.retryPolicy(RetryPolicy.builder()
.numRetries(3)
.build()))
.build();
基本的なキー管理
暗号化キーの作成
public String createEncryptionKey(KmsClient kmsClient, String description) {
CreateKeyRequest request = CreateKeyRequest.builder()
.description(description)
.keyUsage(KeyUsageType.ENCRYPT_DECRYPT)
.build();
CreateKeyResponse response = kmsClient.createKey(request);
return response.keyMetadata().keyId();
}
キーの説明
public KeyMetadata getKeyMetadata(KmsClient kmsClient, String keyId) {
DescribeKeyRequest request = DescribeKeyRequest.builder()
.keyId(keyId)
.build();
return kmsClient.describeKey(request).keyMetadata();
}
キーの有効化/無効化
public void toggleKeyState(KmsClient kmsClient, String keyId, boolean enable) {
if (enable) {
kmsClient.enableKey(EnableKeyRequest.builder().keyId(keyId).build());
} else {
kmsClient.disableKey(DisableKeyRequest.builder().keyId(keyId).build());
}
}
基本的な暗号化と復号
データの暗号化
public String encryptData(KmsClient kmsClient, String keyId, String plaintext) {
SdkBytes plaintextBytes = SdkBytes.fromString(plaintext, StandardCharsets.UTF_8);
EncryptRequest request = EncryptRequest.builder()
.keyId(keyId)
.plaintext(plaintextBytes)
.build();
EncryptResponse response = kmsClient.encrypt(request);
return Base64.getEncoder().encodeToString(
response.ciphertextBlob().asByteArray());
}
データの復号
public String decryptData(KmsClient kmsClient, String ciphertextBase64) {
byte[] ciphertext = Base64.getDecoder().decode(ciphertextBase64);
SdkBytes ciphertextBytes = SdkBytes.fromByteArray(ciphertext);
DecryptRequest request = DecryptRequest.builder()
.ciphertextBlob(ciphertextBytes)
.build();
DecryptResponse response = kmsClient.decrypt(request);
return response.plaintext().asString(StandardCharsets.UTF_8);
}
エンベロープ暗号化パターン
データキーの生成と使用
public DataKeyResult encryptWithEnvelope(KmsClient kmsClient, String masterKeyId, byte[] data) {
try {
GenerateDataKeyRequest keyRequest = GenerateDataKeyRequest.builder()
.keyId(masterKeyId)
.keySpec(DataKeySpec.AES_256)
.build();
GenerateDataKeyResponse keyResponse = kmsClient.generateDataKey(keyRequest);
// Validate response
if (keyResponse.plaintext() == null || keyResponse.ciphertextBlob() == null) {
throw new IllegalStateException("Data key generation returned null");
}
byte[] encryptedData = encryptWithAES(data, keyResponse.plaintext().asByteArray());
// Clear plaintext key from memory
Arrays.fill(keyResponse.plaintext().asByteArray(), (byte) 0);
return new DataKeyResult(encryptedData, keyResponse.ciphertextBlob().asByteArray());
} catch (KmsException e) {
throw new RuntimeException("Envelope encryption failed: " + e.awsErrorDetails().errorCode(), e);
}
}
public byte[] decryptWithEnvelope(KmsClient kmsClient, DataKeyResult encryptedEnvelope) {
try {
DecryptRequest keyDecryptRequest = DecryptRequest.builder()
.ciphertextBlob(SdkBytes.fromByteArray(encryptedEnvelope.encryptedKey()))
.build();
DecryptResponse keyDecryptResponse = kmsClient.decrypt(keyDecryptRequest);
// Validate response
if (keyDecryptResponse.plaintext() == null) {
throw new IllegalStateException("Key decryption returned null");
}
byte[] decryptedData = decryptWithAES(
encryptedEnvelope.encryptedData(),
keyDecryptResponse.plaintext().asByteArray());
// Clear plaintext key from memory
Arrays.fill(keyDecryptResponse.plaintext().asByteArray(), (byte) 0);
return decryptedData;
} catch (KmsException e) {
throw new RuntimeException("Envelope decryption failed: " + e.awsErrorDetails().errorCode(), e);
}
}
デジタル署名
署名キーの作成とデータへの署名
public String createAndSignData(KmsClient kmsClient, String description, String message) {
// Create signing key
CreateKeyRequest keyRequest = CreateKeyRequest.builder()
.description(description)
.keySpec(KeySpec.RSA_2048)
.keyUsage(KeyUsageType.SIGN_VERIFY)
.build();
CreateKeyResponse keyResponse = kmsClient.createKey(keyRequest);
String keyId = keyResponse.keyMetadata().keyId();
// Sign data
SignRequest signRequest = SignRequest.builder()
.keyId(keyId)
.message(SdkBytes.fromString(message, StandardCharsets.UTF_8))
.signingAlgorithm(SigningAlgorithmSpec.RSASSA_PSS_SHA_256)
.build();
SignResponse signResponse = kmsClient.sign(signRequest);
return Base64.getEncoder().encodeToString(
signResponse.signature().asByteArray());
}
署名の検証
public boolean verifySignature(KmsClient kmsClient,
String keyId,
String message,
String signatureBase64) {
byte[] signature = Base64.getDecoder().decode(signatureBase64);
VerifyRequest verifyRequest = VerifyRequest.builder()
.keyId(keyId)
.message(SdkBytes.fromString(message, StandardCharsets.UTF_8))
.signature(SdkBytes.fromByteArray(signature))
.signingAlgorithm(SigningAlgorithmSpec.RSASSA_PSS_SHA_256)
.build();
VerifyResponse verifyResponse = kmsClient.verify(verifyRequest);
return verifyResponse.signatureValid();
}
Spring Boot との統合
設定クラス
@Configuration
public class KmsConfiguration {
@Bean
public KmsClient kmsClient() {
return KmsClient.builder()
.region(Region.US_EAST_1)
.build();
}
@Bean
public KmsAsyncClient kmsAsyncClient() {
return KmsAsyncClient.builder()
.region(Region.US_EAST_1)
.build();
}
}
暗号化サービス
@Service
@RequiredArgsConstructor
public class KmsEncryptionService {
private final KmsClient kmsClient;
@Value("${kms.encryption-key-id}")
private String keyId;
public String encrypt(String plaintext) {
try {
EncryptRequest request = EncryptRequest.builder()
.keyId(keyId)
.plaintext(SdkBytes.fromString(plaintext, StandardCharsets.UTF_8))
.build();
EncryptResponse response = kmsClient.encrypt(request);
return Base64.getEncoder().encodeToString(
response.ciphertextBlob().asByteArray());
} catch (KmsException e) {
throw new RuntimeException("Encryption failed", e);
}
}
public String decrypt(String ciphertextBase64) {
try {
byte[] ciphertext = Base64.getDecoder().decode(ciphertextBase64);
DecryptRequest request = DecryptRequest.builder()
.ciphertextBlob(SdkBytes.fromByteArray(ciphertext))
.build();
DecryptResponse response = kmsClient.decrypt(request);
return response.plaintext().asString(StandardCharsets.UTF_8);
} catch (KmsException e) {
throw new RuntimeException("Decryption failed", e);
}
}
}
例
基本的な暗号化の例
public class BasicEncryptionExample {
public static void main(String[] args) {
KmsClient kmsClient = KmsClient.builder()
.region(Region.US_EAST_1)
.build();
// Create key
String keyId = createEncryptionKey(kmsClient, "Example encryption key");
System.out.println("Created key: " + keyId);
// Encrypt and decrypt
String plaintext = "Hello, World!";
String encrypted = encryptData(kmsClient, keyId, plaintext);
String decrypted = decryptData(kmsClient, encrypted);
System.out.println("Original: " + plaintext);
System.out.println("Decrypted: " + decrypted);
}
}
エンベロープ暗号化の例
public class EnvelopeEncryptionExample {
public static void main(String[] args) {
KmsClient kmsClient = KmsClient.builder()
.region(Region.US_EAST_1)
.build();
String masterKeyId = "alias/your-master-key";
String largeData = "This is a large amount of data that needs encryption...";
byte[] data = largeData.getBytes(StandardCharsets.UTF_8);
// Encrypt using envelope pattern
DataKeyResult encryptedEnvelope = encryptWithEnvelope(
kmsClient, masterKeyId, data);
// Decrypt
byte[] decryptedData = decryptWithEnvelope(
kmsClient, encryptedEnvelope);
String result = new String(decryptedData, StandardCharsets.UTF_8);
System.out.println("Decrypted: " + result);
}
}
ベストプラクティス
セキュリティ
- 大きなデータには必ずエンベロープ暗号化を使用する - データはローカルで暗号化し、データキーのみを KMS で暗号化します
- 暗号化コンテキストを使用する - 使用状況の追跡と監査のため、コンテキスト情報を追加します
- 機密データをログに記録しない - 平文や暗号化キーをログに記録しないようにします
- 適切なキーライフサイクルを実装する - 自動ローテーションを有効にし、削除ポリシーを設定します
- 用途ごとに別のキーを使用する - 複数のアプリケーションでキーを使い回さないでください
パフォーマンス
- 暗号化されたデータキーをキャッシュする - データキーをキャッシュして KMS API 呼び出しを減らします
- 非同期操作を使用する - 非ブロッキング I/O には非同期クライアントを活用します
- クライアントインスタンスを再利用する - 操作ごとに新しいクライアントを作成しないでください
- 接続プーリングを実装する - 適切な接続プーリング設定を構成します
エラー処理
- 再試行ロジックを実装する - 指数バックオフでスロットリング例外に対処します
- キーの状態を確認する - 操作を実行する前にキーが有効であることを確認します
- サーキットブレーカーを使用する - KMS の障害時に連鎖的な障害を防ぎます
- エラーを詳細にログに記録する - KMS のエラーコードとコンテキストを含めます
参考資料
詳細な実装パターン、高度な手法、包括的な例については、以下を参照してください。
- @references/technical-guide.md - 技術的な実装パターンの全容
- @references/spring-boot-integration.md - Spring Boot との統合パターン
- @references/testing.md - テスト戦略と例
- @references/best-practices.md - セキュリティと運用に関するベストプラクティス
関連スキル
@aws-sdk-java-v2-core - AWS SDK の基本的なパターンと設定@aws-sdk-java-v2-dynamodb - DynamoDB との統合パターン@aws-sdk-java-v2-secrets-manager - シークレット管理のパターン@spring-boot-dependency-injection - Spring の依存性注入パターン
外部参考資料
制約事項と警告
- データサイズの制限:直接暗号化できるのは 4KB までです。より大きなデータにはエンベロープ暗号化を使用してください
- キーの使用制限:KMS には、1 秒あたりの API 呼び出し数に関するクォータがあります
- キーマテリアル:インポートしたキーマテリアルは、AWS によるローテーション管理の対象外です
- キーの削除:キーの削除には 7~30 日の待機期間が必要です
- リージョンの境界:KMS キーはリージョンをまたいで使用できません
- コストに関する考慮事項:KMS では API 呼び出しごと、およびキーの保存に対して料金が発生します
- 非対称キー:すべてのリージョンで非対称キーの種類がサポートされているわけではありません
- キーポリシー:キーポリシーの変更には、IAM の慎重な確認が必要です
- エンベロープ暗号化:データキーのセキュリティを確保するには、適切な実装が必要です
- ログ記録:すべての KMS API の使用状況を監査できるよう、CloudTrail を有効にしてください
コメント
Markdown 対応