Azure Key Vault (2)キーを使って暗号・復号化する

このエントリは2019/09/25現在の情報を基にしています。将来の機能追加・廃止に伴い記載内容との乖離が発生する可能性があります。

このエントリはAzure Key Vaultの3エントリの2回目である。

Azure Key Vault (1) 基礎
https://logico-jp.io/2019/09/19/azure-key-vault-1/
Azure Key Vault (2)キーを使って暗号・復号化する
https://logico-jp.io/2019/09/25/azure-key-vault-2/
Azure Key Vault (3) SDKのメソッド呼び出し回数の制限
https://logico-jp.io/2019/09/25/azure-key-vault-3/

上記エントリを踏まえて、Key Vaultで管理しているキーを使ってフレーズを暗号化・復号化してみる。Key Vaultを使うと、暗号に必要な鍵を露出させる必要なく、暗号化・復号化できる。なお、サンプルコードの言語はいつも通りJavaである。

Key ContainerとKeyの作成

キーコンテナを作成した上で、キーを作成する。キーコンテナの価格レベル(SKU)はどちらでもかまわない(今回はプレミアムを選択)。キーもまた、HSMで保護されたキーでも、ソフトウェア的に保護されたキーでもどちらでもかまわない(今回はHSM-RSAを選択)。

作成したら、キー識別子を確認しておく(この識別子はSDKを呼び出すときに必要)。今回は「許可された操作」として全ての操作を選択しているが、実際には必要最低限の操作のみ許可すべきである。

Azure Active Directory (以下、Azure AD) でサービスプリンシパルを作成

CLIやPowerShellなどで作成すると、クライアントシークレットまで一気に作成できるが、今回はAzure Portalで作成する。Azure ADのページで、管理 > アプリの登録をクリックし、表示された画面で[新規登録]をクリック。

[アプリケーションの登録]では、名前とサポートされているアカウントの種類を指定(リダイレクトURIは省略可能で、今回は指定しない)し、[登録]をクリック。

クライアントシークレットの作成

登録したアプリの画面で、管理 > 証明書とシークレット を選択すると、クライアントシークレットを追加できる画面が現れるので、[+新しいクライアントシークレット]をクリックして追加する。指定する項目は説明と有効期限。今回は1年にしておく。

画面が戻ると、値の場所にランダムな文字が入っているが、これがクライアントシークレット。この値は赤下線部の注意書きの通り、後から取得できないので、この時点でメモしておく。ここまでがAzure ADでの操作。

登録アプリのKey Vaultへのアクセス設定

Key Vaultの画面に戻って、設定 > アクセスポリシーをクリックして、画面に現れる[+アクセスポリシーの追加]をクリックして、先ほど登録したアプリに対するアクセスポリシーを設定する。

今回はキーのアクセス許可だけでよいので、暗号化操作 > 暗号化解除(つまり復号化、decrypt)と暗号化 (encrypt)のみを選択する。

[プリンシパルの選択]では、登録したアプリ(サービスプリンシパル)を検索して[選択]をクリック(今回の場合はkeyvault4logico)。選択後、[追加]をクリック。

アプリケーションの欄に追加されていることを確認し、[保存]をクリック。

アプリケーションの作成 (1.1.2までの場合)

以下はSDK 1.1.2までの場合であるがコードの流れは以下の通り。

  1. KeyVaultCredentials#doAuthenticate()を実装
  2. 認証の上、KeyVaultClientインスタンスを作成
  3. 暗号化対象のフレーズをKeyVaultClient#encryptもしくはencryptAsyncで暗号化
  4. 復号化対象のフレーズをKeyVaultClient#decryptもしくはdecryptAsyncで復号化

encryptに類似したメソッドにwrapKey (wrapKeyAsync) 、decryptに類似したメソッドにunwrapKey (unwrapKeyAsync) があるが、wrap/unrapは対称キー (symmetric key)を使うことに注意。

依存関係

pom.xmlには以下の依存関係を設定しておく。

    <dependency>
      <groupId>com.microsoft.azure</groupId>
      <artifactId>azure-keyvault</artifactId>
      <version>1.1.2</version>
    </dependency>

KeyVaultCredentials#doAuthenticate()を実装

これは以下のSDKリファレンスにサンプルコードがある。

KeyVaultCredentials Class
https://docs.microsoft.com/en-us/java/api/com.microsoft.azure.keyvault.authentication.keyvaultcredentials?view=azure-java-stable

package kvsdk4java;

import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials;

import java.net.MalformedURLException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ClientSecretKeyVaultCredential extends KeyVaultCredentials {

    private String clientId;
    private String clientKey;

    public ClientSecretKeyVaultCredential(String clientId, String clientKey) {
        this.clientId = clientId;
        this.clientKey = clientKey;
    }

    @Override
    public String doAuthenticate(String authorization, String resource, String scope) {
        AuthenticationResult token = getAccessTokenFromClientCredentials(authorization, resource, clientId, clientKey);
        return token.getAccessToken();
    }

    private AuthenticationResult getAccessTokenFromClientCredentials(String authorization, String resource, String clientId, String clientKey) {
        AuthenticationContext context = null;
        AuthenticationResult result = null;
        ExecutorService service = null;

        try {
            service = Executors.newFixedThreadPool(1);
            context = new AuthenticationContext(authorization, false, service);
            ClientCredential credentials = new ClientCredential(clientId, clientKey);
            Future<AuthenticationResult&gt; future = context.acquireToken(resource, credentials, null);
            result = future.get();
        } catch (MalformedURLException | InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            service.shutdown();
        }

        if (result == null) {
            throw new RuntimeException("Authentication results were null.");
        }
        return result;
    }
}

認証の上、KeyVaultClientインスタンスを作成

上記サンプルコードのClientSecretKeyVaultCredentialのコンストラクタを呼び出してKeyVaultCredentialsインスタンスを取得し、KeyVaultClientインスタンスを作成する際にパラメータとして指定する。

KeyVaultCredentials credential = new ClientSecretKeyVaultCredential( AZURE_CLIENT_ID, AZURE_CLIENT_SECRET );
KeyVaultClient client = new KeyVaultClient(credential);

暗号化対象のフレーズをKeyVaultClient#encryptもしくはencryptAsyncで暗号化

KeyVaultClientインスタンスを作成できたら、encryptメソッドを呼び出して暗号化する。このときに、キー識別子、もしくはきーコンテナのURL、キー名称、バージョンの文字列(つまりキー識別子の構成要素)が必要。

byte[] byteText = textToEncrypt.getBytes("UTF-16");
KeyOperationResult encryptResult1 = client.encrypt(KEY_IDENTIFIER, JsonWebKeyEncryptionAlgorithm.RSA_OAEP, byteText);

encryptAsyncの場合はコールバックを指定する必要がある。

復号化対象のフレーズをKeyVaultClient#decryptもしくはdecryptAsyncで復号化

encryptメソッドと呼び出しパラメータは同じ。

KeyOperationResult decryptResult1 = client.decrypt(KEY_IDENTIFIER, JsonWebKeyEncryptionAlgorithm.RSA_OAEP, encryptResult1.result());

アプリケーションの作成 (SDK 4.0以降の場合)

インポート対象のパッケージや、メソッドが大きく変わっているので注意が必要。大きな違いとして、com.microsoft.azure.xxxからcom.azure.xxxにパッケージ名が変わっている。

依存関係

azure-keyvault-keysを追加しておく。

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-keyvault-keys</artifactId>
    <version>4.0.0-preview.4</version>
</dependency>
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-identity</artifactId>
    <version>1.0.0-preview.3</version>
</dependency>

Default HTTP Clientの詳細は以下に記載がある。

Default HTTP Client
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/keyvault/azure-keyvault-keys#default-http-client

クライアントインスタンスは、SecretClient、KeyClient、CryptographyClientなどと分かれていて、今回はCryptographyClient インスタンスを作成する必要がある。

DefaultAzureCredentialを使って認証する場合

DefaultAzureCredentialを使って認証する場合は、以下の環境変数を設定しておく。DefaultAzureCredentialが環境変数から必要な情報を読み取ってよろしくやってくれる。

  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET
  • AZURE_TENANT_ID

ここで、AZURE_TENANT_IDは登録したアプリ(サービスプリンシパル)の概要で確認できる。

簡単のために、pom.xmlにシステムプロパティとして設定することもできる。

<systemProperties>
    <systemProperty>
            <key>AZURE_TENANT_ID</key>
            <value>{AZURE_TENANT_ID}</value>
    </systemProperty>
    <systemProperty>
            <key>AZURE_CLIENT_ID</key>
            <value>{AZURE_CLIENT_ID}</value>
    </systemProperty>
    <systemProperty>
            <key>AZURE_CLIENT_SECRET</key>
            <value>{AZURE_CLIENT_SECRET}</value>
    </systemProperty>
<systemProperties>

認証のコード例は以下を参照。

Authenticating with DefaultAzureCredential
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/identity/azure-identity#authenticating-with-defaultazurecredential

以前のSDKと同じような方法で認証したい場合

以前のSDKと同様、ClientSecretCredentialを別途作成して認証し、CryptographyClient インスタンスを作成することもできる。

ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
        .clientId(AZURE_CLIENT_ID)
        .clientSecret(AZURE_CLIENT_SECRET)
        .tenantId(AZURE_TENANT_ID)
        .build();

Authenticating a service principal with a client secret
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/identity/azure-identity#authenticating-a-service-principal-with-a-client-secret

同期クライアントの場合と非同期クライアントの場合で、末尾のbuildメソッドが異なる。同期型の場合、buildClient()である。

CryptographyClient cryptographyClient = new CryptographyClientBuilder()
        .credential(clientSecretCredential)
        .keyIdentifier(KEY_IDENTIFIER)
        .buildClient();

非同期クライアントの場合はbuildAsyncClient()であることに注意。

CryptographyAsyncClient cryptographyAsyncClient = new CryptographyClientBuilder()
        .credential(clientSecretCredential)
        .keyIdentifier(KEY_IDENTIFIER)
        .buildAsyncClient();

暗号化と復号化

認証が通れば、あとはメソッドが異なるだけで流れは同じ。コード例は以下を参照。このSDKはReactorをベースにしているので、Reactiveな書式でコードを記載できる。

Encrypt
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/keyvault/azure-keyvault-keys#encrypt
Decrypt
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/keyvault/azure-keyvault-keys#decrypt
Encrypt Asynchronously
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/keyvault/azure-keyvault-keys#encrypt-asynchronously
Decrypt Asynchronously
https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/keyvault/azure-keyvault-keys#decrypt-asynchronously

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中