前回はAlibabaCloudのCloudMonitorサービスを使ってみましたが、今回は、AlibabaCloudのKMS(Key Management Service)サービスを操作してみます。
※AlibabaCloudは、AWSと類似するサービスが多いですが、 今回紹介するAlibabaCloudのKMS(Key Management Service)は、AWSのKMS(Key Management Service)と類似するサービスです。
KMS(Key Management Service)サービスについて、各ベンダーのサービス名一覧は下記にまとめました。
ベンダー | サービス名 |
---|---|
AlibabaCloud | Key Management Service |
AWS | Key Management Service |
GCP | Cloud Key Management Service |
Azure | Azure Vault |
Key Management Service(KMS)
KMS(Key Management Service)は、データの保護に使用する暗号鍵の作成、制御および安全な保管ができる使いやすいサービスです。API、SDKまたはコンソールを使用して、Customer Master Key(CMK)の生成およびそれを用いたユーザーの暗号鍵の管理ができます。
KMSを使用して何の問題を解決できますか。
役割 | 問題 | KMSを使用して問題を解決する方法 |
---|---|---|
アプリケーション/ウェブサイト開発者 | プログラムは、暗号化のためのキーを使用する必要があります。キーを安全かつ独立して管理する必要があります。アプリケーションがどこにあってもキーに安全にアクセスする必要があります。 | エンベデッド暗号化技術により、ユーザーはKMSにCustomer Master Key(CMK)を格納し、暗号化されたデータキーのみを展開できます。さらに、ユーザーはKMSのAPIを呼び出して、データキーが必要なときにのみ解読することができます。 |
サービス開発者 | 開発者が、ユーザーのキーとデータの安全性については責任を負いたくありません。ユーザーは自分のキーを個人的に管理する必要がありますが、開発者が指定されたキーを使用するのみで、開発に集中することができます。 | エンベロープ暗号化テクノロジとKMSの公開APIに基づいて、サービス開発者は指定されたCMKを使用してデータキーを暗号化/復号化できます。キーの管理は、ユーザー側に委ねます。 |
セキュリティ責任者 | コンプライアンス要件を満たすためには、会社のキー管理が必要です。責任者は、キーが手順を追って承認され、キーの使用が監査されることを確実にする必要があります。 | KMSは、統合された認証管理のためにRAMに関連付けることができます。 |
今回は、アプリケーション/ウェブサイト開発者の立場で、KMS(Key Management Service)のSDK(Java)を使用して、Customer Master Key(CMK)で、テキスト情報の暗号化と複合処理を検証してみたいです。
手順
さて、作業をしていきましょう。
今回の目標は、Customer Master Key(CMK)の作成から、SDKを使用したテキスト情報の暗号化複合の実装まで説明します。
- Customer Master Key(CMK)の作成
- AccessKeyの取得
- 暗号化と複合処理の検証
1. Customer Master Key(CMK)の作成
■CMKの作成
・[KMS コンソール]にログオンします。
・ページの右上隅にある [キーの作成] ボタンをクリックします。
・説明などの情報を入力します。
・[OK]をクリックします。
※作成されたCMK情報が一覧画面から確認できます。
2. AccessKeyの取得
■AccessKeyの取得
・ホームページの上部にある [AccessKeys] ボタンをクリックします。
・セキュリティに関するヒントの確認画面の「AccessKeyの管理を続ける」ボタンをクリックします。
・Access Key管理ページに遷移されたら、[Access Keyの作成]ボタンをクリックします。
・キー作成が完了しましたら、[表示する]リンクをクリックし、表示されているAccess Key IDとAccess Key Secret情報をメモしておきます。※後述の暗号化複合プログラムに利用する必要があります。
3. 暗号化と複合処理の検証
■検証用プロジェクトの作成
・EclipseからMaven Projectを作成します。※作成時の入力情報は、ご参考をください。変更可
Group Id:com.kms
Artifact Id:kms-test
・検証で利用する下記の2つのAlibaba CloudのSDKライブラリの依存設定をpomファイルに追加
aliyun-java-sdk-core
aliyun-java-sdk-kms
※pomファイルの内容は下記となります。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kms</groupId>
<artifactId>kms-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>kms-test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-kms</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
</project>
■検証用プログラムの作成
下記の検証フローのプログラムを作成します。
・全てのCMKのリストアップ
・CMKの情報取得
・hello world平文をCMKで暗号化
・hello world暗号文をCMKで複合
package com.kms.kms_test;
import java.util.*;
import java.util.List;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
//現在の KMS SDK バージョン:2016-01-20
import com.aliyuncs.kms.model.v20160120.DecryptRequest;
import com.aliyuncs.kms.model.v20160120.DecryptResponse;
import com.aliyuncs.kms.model.v20160120.DescribeKeyRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyResponse;
import com.aliyuncs.kms.model.v20160120.EncryptRequest;
import com.aliyuncs.kms.model.v20160120.EncryptResponse;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import com.aliyuncs.kms.model.v20160120.ListKeysRequest;
import com.aliyuncs.kms.model.v20160120.ListKeysResponse;
import com.aliyuncs.kms.model.v20160120.ListKeysResponse.Key;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
public class App
{
static DefaultAcsClient kmsClient;
//Aliyunクライアント作成
private static DefaultAcsClient kmsClient(String regionId, String accessKeyId, String accessKeySecret) {
/**
* Aliyunクライアント作成:
* RegionId, AccessKeyId, AccessKeySecret を設定
*/
IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
return client;
}
//CMKの詳細情報を取得
private static DescribeKeyResponse DescribeKey(String keyId) throws ClientException {
final DescribeKeyRequest decKeyReq = new DescribeKeyRequest();
decKeyReq.setProtocol(ProtocolType.HTTPS);
decKeyReq.setAcceptFormat(FormatType.JSON);
decKeyReq.setMethod(MethodType.POST);
decKeyReq.setKeyId(keyId);
final DescribeKeyResponse decKeyRes = kmsClient.getAcsResponse(decKeyReq);
return decKeyRes;
}
//登録されているCMKの一覧情報を取得
private static ListKeysResponse ListKey(int pageNumber, int pageSize) throws ClientException {
final ListKeysRequest listKeysReq = new ListKeysRequest();
listKeysReq.setProtocol(ProtocolType.HTTPS);
listKeysReq.setAcceptFormat(FormatType.JSON);
listKeysReq.setMethod(MethodType.POST);
listKeysReq.setPageNumber(pageNumber);
listKeysReq.setPageSize(pageSize);
final ListKeysResponse listKeysRes = kmsClient.getAcsResponse(listKeysReq);
return listKeysRes;
}
//データキー情報の取得
private static GenerateDataKeyResponse GenerateDataKey(String keyId, String keyDesc, int numOfBytes) throws ClientException {
final GenerateDataKeyRequest genDKReq = new GenerateDataKeyRequest();
genDKReq.setProtocol(ProtocolType.HTTPS);
genDKReq.setAcceptFormat(FormatType.JSON);
genDKReq.setMethod(MethodType.POST);
/**
* KMS openAPI ドキュメントに従ってパラメータを設定します。
* 1.KeyId
* 2.KeyDescription
* 3.NumberOfBytes
*/
genDKReq.setKeySpec(keyDesc);
genDKReq.setKeyId(keyId);
genDKReq.setNumberOfBytes(numOfBytes);
final GenerateDataKeyResponse genDKRes = kmsClient.getAcsResponse(genDKReq);
return genDKRes;
}
//暗号化処理を実施
private static EncryptResponse Encrypt(String keyId, String plainText) throws ClientException {
final EncryptRequest encReq = new EncryptRequest();
encReq.setProtocol(ProtocolType.HTTPS);
encReq.setAcceptFormat(FormatType.JSON);
encReq.setMethod(MethodType.POST);
encReq.setKeyId(keyId);
encReq.setPlaintext(plainText);
final EncryptResponse encResponse = kmsClient.getAcsResponse(encReq);
return encResponse;
}
//複合処理を実施
private static DecryptResponse Decrypt(String cipherBlob) throws ClientException {
final DecryptRequest decReq = new DecryptRequest();
decReq.setProtocol(ProtocolType.HTTPS);
decReq.setAcceptFormat(FormatType.JSON);
decReq.setMethod(MethodType.POST);
decReq.setCiphertextBlob(cipherBlob);
final DecryptResponse decResponse = kmsClient.getAcsResponse(decReq);
return decResponse;
}
public static void main(String[] args) {
System.out.println("===========================================");
System.out.println(" KMS サービスで開始しています");
System.out.println("===========================================\n");
/**
* アクセス情報をセット
*/
String regionId = "ap-northeast-1";
String accessKeyId = "★★前の手順でメモしたAccess Key IDを設定★★";
String accessKeySecret = "★★前の手順でメモしたAccess Key Secretを設定★★";
kmsClient = kmsClient(regionId, accessKeyId, accessKeySecret);
String keyId = null;
String plainText = "hello world";
String cipherBlob = null;
/*アカウント中の全てのマスターキーをリストアップ*/
try {
final ListKeysResponse listKeysRes = ListKey(1, 100);
/**
* レスポンスを解析し、さらに処理する
*/
System.out.println("TotalCount: " + listKeysRes.getTotalCount());
System.out.println("PageNumber: " + listKeysRes.getPageNumber());
System.out.println("PageSize: " + listKeysRes.getPageSize());
List<Key> keys = listKeysRes.getKeys();
Iterator<Key> iterator = keys.iterator();
while (iterator.hasNext()) {
keyId = iterator.next().getKeyId();
System.out.println("KeyId: " + keyId);
}
System.out.println("===========================================");
System.out.println("全てのCMKのリストアップ成功!\n");
System.out.println("===========================================\n");
} catch (ClientException eResponse) {
System.out.println("Failed.");
System.out.println("Error code: " + eResponse.getErrCode());
System.out.println("Error message: " + eResponse.getErrMsg());
}
/* キーの記述 */
try {
final DescribeKeyResponse decKeyRes = DescribeKey(keyId);
/**
* レスポンスを解析し、さらに処理する
*/
System.out.println("DescribeKey Response: ");
DescribeKeyResponse.KeyMetadata meta = decKeyRes.getKeyMetadata();
System.out.println("KeyId: " + meta.getKeyId());
System.out.println("Description: " + meta.getDescription());
System.out.println("KeyState: " + meta.getKeyState());
System.out.println("KeyUsage: " + meta.getKeyUsage());
System.out.println("===========================================");
System.out.println("CMKの情報取得成功!");
System.out.println("===========================================\n");
} catch (ClientException eResponse) {
System.out.println("Failed.");
System.out.println("Error code: " + eResponse.getErrCode());
System.out.println("Error message: " + eResponse.getErrMsg());
}
/*データキー作成*/
/**
* リスポンスをリクエストして取得する
*/
try {
final GenerateDataKeyResponse genDKResponse = GenerateDataKey(keyId, "AES_256", 64);
/**
* レスポンスを解析し、さらに処理する
*/
System.out.println("CiphertextBlob: " + genDKResponse.getCiphertextBlob());
System.out.println("KeyId: " + genDKResponse.getKeyId());
System.out.println("Plaintext: " + genDKResponse.getPlaintext());
System.out.println("===========================================");
System.out.println("データキー作成成功!");
System.out.println("===========================================\n");
} catch (ClientException eResponse) {
System.out.println("Failed.");
System.out.println("Error code: " + eResponse.getErrCode());
System.out.println("Error message: " + eResponse.getErrMsg());
}
/**
* プレーンテキストを暗号化して暗号を取得する
*/
try {
EncryptResponse encResponse = Encrypt(keyId, plainText);
cipherBlob = encResponse.getCiphertextBlob();
System.out.println("CiphertextBlob: " + cipherBlob);
System.out.println("KeyId: " + encResponse.getKeyId());
System.out.println("===========================================");
System.out.println("平文を暗号化成功!");
System.out.println("===========================================\n");
} catch (ClientException eResponse) {
System.out.println("Failed.");
System.out.println("Error code: " + eResponse.getErrCode());
System.out.println("Error message: " + eResponse.getErrMsg());
}
/**
* 暗号テキストを復号化し、元の平文で結果を検証する。
*/
try {
DecryptResponse decResponse = Decrypt(cipherBlob);
System.out.println("Plaintext: " + decResponse.getPlaintext());
String verifyPlainText = decResponse.getPlaintext();
int isMatch = verifyPlainText.compareTo(plainText);
System.out.println("KeyId: " + decResponse.getKeyId());
System.out.println("===========================================");
System.out.printf("暗号テキストの復号成功、結果 " + (isMatch == 0 ? "match" + "\n" : "mismatch" + "\n"));
System.out.println("===========================================\n");
} catch (ClientException eResponse) {
System.out.println("Failed.");
System.out.println("Error code: " + eResponse.getErrCode());
System.out.println("Error message: " + eResponse.getErrMsg());
}
}
}
■検証用プロジェクトのビルドと実行
・Mavenでビルドしたプロジェクトのフォルド構成は、下記となります。※Alibaba CloudのSDKライブラリのバージョンも確認できます。
・プロジェクトを実行します。
実行結果は下記となります。
===========================================
KMS サービスで開始しています
===========================================
TotalCount: 3
PageNumber: 1
PageSize: 100
KeyId: 0cc16ce2-a542-4859-a778-3131a2185d79
KeyId: 4f9cb01a-f2b8-49f8-8518-4f0faf99bfa8
KeyId: bb8749d6-06a6-42bd-9724-6811a46c3bb8
===========================================
全てのCMKのリストアップ成功!
===========================================
DescribeKey Response:
KeyId: bb8749d6-06a6-42bd-9724-6811a46c3bb8
Description: KMSテスト用
KeyState: Enabled
KeyUsage: ENCRYPT/DECRYPT
===========================================
CMKの情報取得成功!
===========================================
CiphertextBlob: NTJiODkwZTMtOTE5My00YTlkLTkxMmYtZDU1YjRkZTMxMTYxTURGUlQvMTFtbTFId1ZOMHFhVzEySWpYVmhjWmRiR0xBQUFBQUFBQUFBQVFsREU2TXV5Ukd3R2FCUWtMSjlHRjUycDh4aTQ3YmxTWDFYR0xjOEd0SWU5cWtsRWdGWm40WGpnK2xlbzVXbXl1WkpHY04wOWJRdUh3dnNZTXFCQUxtY3NLUkdGUTJxdUYzdFF3aXhIYVBzcXMxbmkxVzBEeGFkVGZWcGtXdmZtNDBnN0JFbDNTV3c9PQ==
KeyId: bb8749d6-06a6-42bd-9724-6811a46c3bb8
Plaintext: hlSY2y9iOEWKg39MAC5p6sxm7b3KtKyHchgs2urCG9lG0bEp2p3yKugDUTASbMF6WQhj4dhZUqNeNry0R2FpGg==
===========================================
データキー作成成功!
===========================================
CiphertextBlob: NTJiODkwZTMtOTE5My00YTlkLTkxMmYtZDU1YjRkZTMxMTYxaytYdGpzRS9sK2ZQRjhCM21ad3pZZmlGOHM4NUp3MVpBQUFBQUFBQUFBQXlaa0syTzdocEI2bjJubTFwTWtHVnJLa2NyYnRMa2xCcCs1WT0=
KeyId: bb8749d6-06a6-42bd-9724-6811a46c3bb8
===========================================
平文を暗号化成功!
===========================================
Plaintext: hello world
KeyId: bb8749d6-06a6-42bd-9724-6811a46c3bb8
===========================================
暗号テキストの復号成功、結果 match
===========================================
おわりに
今回は、KMS(Key Management Service)のSDKを使用して、テキスト情報の暗号化と複合処理を検証してみました。
今後は、プログラムに、使用する暗号化キーを、安全かつ独立して管理できるように、KMSに作成管理しましょう。