mcpField
A
aws-sdk-java-v2-kms

aws-sdk-java-v2-kms

github.com/giuseppe-trisciuoglioセキュリティ3653.7k 回インストール7 週間前に更新
リポジトリ

aws-sdk-java-v2-kms について

AWS SDK for Java 2.xでAWS KMSを利用するJava開発者向けのスキルです。キー管理、暗号化と復号、エンベロープ暗号化、デジタル署名、Spring Boot連携を扱います。

主な機能

  1. 01KMSキーの作成と管理
  2. 02データの暗号化と復号
  3. 03大きなデータ向けのエンベロープ暗号化
  4. 04デジタル署名の生成と検証
  5. 05Spring Bootとの連携

活用シーン

  1. 01データ保護用の対称キー設定
  2. 02データキーによる大きなデータの暗号化
  3. 03アプリケーションのデジタル署名設定
  4. 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.md
spring-boot-integration.md
technical-guide.md
testing.md

詳細

AWS SDK for Java 2.x - AWS KMS(Key Management Service)

概要

AWS SDK for Java 2.x を使用した AWS KMS のパターンを提供します。キー管理、暗号化/復号、エンベロープ暗号化、デジタル署名、Spring Boot との統合を扱います。

手順

  1. IAM 権限を設定する - 最小権限の原則に従って kms:* アクションを許可します
  2. KMS クライアントを作成する - リージョンと認証情報を指定して KmsClient をインスタンス化します
  3. キーを作成する - createKey() を使用します → 続行する前に、キーの状態が ENABLED であることを確認します
  4. キーのポリシーを設定する - キーの使用権限を定義します → 本番環境の前にアクセスをテストします
  5. データを暗号化する - 4KB 未満のデータには encrypt() を使用します。暗号文が空でないことを確認します
  6. エンベロープ暗号化 - 大きなデータには generateDataKey() を使用します → データキーの生成が成功したことを確認します
  7. デジタル署名 - 署名キーを作成します → 署名/検証後に signatureValid=true であることを確認します
  8. キーのローテーション - 自動ローテーションを有効にします → ローテーションのスケジュールが有効であることを確認します

使用する場面

  • データ保護のために対称暗号化キーを作成、管理する
  • 大きなデータにエンベロープ暗号化を実装する
  • 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 を有効にしてください

コメント