Java
SDK
kms
AlibabaCloud
Alibabaクラウド

AlibabaCloudのKMS(Key Management Service)サービスを使ってみた

前回は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を使用したテキスト情報の暗号化複合の実装まで説明します。
1. Customer Master Key(CMK)の作成
2. AccessKeyの取得
3. 暗号化と複合処理の検証

1. Customer Master Key(CMK)の作成

■CMKの作成

・[KMS コンソール]にログオンします。
・ページの右上隅にある [キーの作成] ボタンをクリックします。
スクリーンショット 2018-05-12 21.24.32.png
・説明などの情報を入力します。
・[OK]をクリックします。
スクリーンショット 2018-05-12 21.25.17.png
※作成されたCMK情報が一覧画面から確認できます。
スクリーンショット 2018-05-12 21.27.45.png

2. AccessKeyの取得

■AccessKeyの取得

・ホームページの上部にある [AccessKeys] ボタンをクリックします。
スクリーンショット 2018-05-12 21.31.46.png
・セキュリティに関するヒントの確認画面の「AccessKeyの管理を続ける」ボタンをクリックします。
スクリーンショット 2018-05-12 21.32.26.png
・Access Key管理ページに遷移されたら、[Access Keyの作成]ボタンをクリックします。
スクリーンショット 2018-05-12 21.37.34.png
・キー作成が完了しましたら、[表示する]リンクをクリックし、表示されているAccess Key IDとAccess Key Secret情報をメモしておきます。※後述の暗号化複合プログラムに利用する必要があります。
スクリーンショット 2018-05-12 21.37.59.png
スクリーンショット 2018-05-12 21.38.16.png

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ファイルの内容は下記となります。

pom.xml
<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で複合

App.java
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ライブラリのバージョンも確認できます。
スクリーンショット 2018-05-12 22.30.03.png

・プロジェクトを実行します。
 実行結果は下記となります。

===========================================
 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に作成管理しましょう。