AWS SDK for JavaでAmazonS3ClientBuilderを使ってS3にデータをアップロード/ダウンロード/一括削除する のアップロードを、サーバーサイド暗号化に対応させる方法です。
結論を先に書くと、AmazonS3ClientBuilderを使う場合もAmazonS3Clientのときと同じ記述でOKです。
なお、S3のデータ暗号化の方法には色々な種類がありますが、ここでは「SSE-KMS」を取り上げます。
0. S3のデータ暗号化の種類について
大きく分けて、サーバー側の暗号化(SSE)とクライアント側の暗号化(CSE)があります。
前者は、クライアントから送信したデータをサーバ側で受け取ってから暗号化してストレージに保存する方法、後者はクライアントから送信する前にデータを暗号化してサーバ側に送信する方法です。
但し、サーバ側の暗号化を使う際にはエンドポイントへの接続にHTTPSを使って通信経路をSSL/TLSで暗号化する必要があるため、いずれにせよ生データがインターネット上を流れるわけではありません。
それぞれ、さらにいくつかの方法に分かれますが、CSEについてはここでは取り上げません。
- S3で管理された暗号化キーを使うサーバ側暗号化(SSE-S3)
- AWS KMSで管理された暗号化キーを使うサーバ側暗号化(SSE-KMS)
- ユーザが用意した暗号化キーを使うサーバ側暗号化(SSE-C)
これらのうち、今回使うSSE-KMSでは、さらに、
- デフォルトの暗号化キー(無料で自動発行されるキー)を使う方法
- ユーザが明示的に発行したカスタマーマスターキー(CMK)を使う方法
があります。
1. 準備/CMK(キー)の発行とS3バケットへのポリシー設定(必要な場合のみ)
CMKを明示的に発行する場合は、IAMの「暗号化キー」のメニューを使います。
上の画面で、①(ARN)はバケットポリシー設定の際に、②(キーID)はJavaのコードで暗号化キーIDを指定する際に使います。
なお、S3のアクセスに使うユーザがこのキーを使用できるよう、権限を付与しておくのを忘れないようにしてください。
CMKを明示的に発行せず、デフォルトの暗号化キーを使う場合は、これらの作業は不要です。
続いて、アップロード時に暗号化を強制したい場合は、バケットポリシーを設定します。
使用する暗号化キーを限定する場合は、13行目末尾の「,」および14行目を記述します。限定しない(暗号化のみ強制する)場合や、デフォルトの暗号化キーを使う場合は、この記述は不要です。
2. Javaのコードを変更する
先の記事のサンプルコードを修正します。
なお、「ダウンロード」以降に変更はありません(ダウンロード時には暗号化の指定は不要です)。
package s3test;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
import com.amazonaws.services.s3.model.DeleteObjectsRequest.KeyVersion;
import com.amazonaws.services.s3.model.DeleteObjectsResult;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.SSEAwsKeyManagementParams;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
public class S3Access {
private static final String ENDPOINT_URL = "https://s3-ap-northeast-1.amazonaws.com";
private static final String REGION = "ap-northeast-1";
private static final String ACCESS_KEY = "【アクセスキー】";
private static final String SECRET_KEY = "【シークレットキー】";
private static final String KMS_KEY_ID = "【KMSキーID】※②の部分を入れる";
//--------------------------------------------------
// アップロード
//--------------------------------------------------
public void putObject(String bucketName, String objectKey, int objectSize, InputStream is) throws Exception {
// クライアント生成
AmazonS3 client = getClient(bucketName);
ObjectMetadata metadata = new ObjectMetadata();
// 念のためサイズだけセットしておく(不整合ならException)
metadata.setContentLength(objectSize);
// アップロード
PutObjectRequest putRequest = new PutObjectRequest(bucketName, objectKey, is, metadata)
.withSSEAwsKeyManagementParams(new SSEAwsKeyManagementParams(KMS_KEY_ID));
client.putObject(putRequest);
client.putObject(bucketName, objectKey, is, metadata);
}
デフォルトの暗号化キーを使う場合は、PutObjectRequest のところで引数のない .withSSEAwsKeyManagementParams() を記述します。
3. 注意点
先に軽く説明した暗号化の種類(方法)ごとに、アップロード時リクエストヘッダに記述される内容が異なります。
そのため、バケットポリシーで暗号化を強制する場合、暗号化の種類に合った記述をする必要があります。
SSE-S3を使う場合は、以下を参考にしてください。
Amazon S3 で管理された暗号化キーによるサーバー側の暗号化 (SSE-S3) を使用したデータの保護
AWS SDK for Java を使用したサーバー側の暗号化の指定
バケットポリシーとJavaコードの対応を間違えると、アップロード時エラーになります。
SSE-S3を使う方法とSSE-KMSでデフォルトの暗号化キーを使う方法は別物ですので、混同しないよう注意してください。
【2017.7.6追記】
認証にアクセスキーを使うのはセキュアではないので、以下の記事でEC2 IAM Roleを使う形への変更方法を示しています。
EC2 IAM Role環境でSSE-KMS暗号化を使ってS3バケットにアクセスする(AWS SDK for Java)