Managed HSMを使って暗号化したり復号化したり

このエントリは2022/04/01現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容との乖離が発生する可能性があります。

このエントリは以下のエントリの続き。

  • Function appでメッセージを受け取り、そのデータを暗号化
  • 結果を永続領域に格納
  • その後別のFunction appで復号して利用

というようなシナリオにおいて、以前はKey Vault(と厳密に言えばDedicated HSMも)が利用できたが、今ではManaged HSMも利用できるようになっている。Managed HSMだと、Key Vaultでも利用可能な楕円暗号 (EC) やRSA暗号だけでなく、対称鍵のAESも利用できる(つまりより高速に暗号化・復号化できる)。

キーの種類、アルゴリズム、および操作 / Key types, algorithms, and operations
https://docs.microsoft.com/azure/key-vault/keys/about-keys-details

今回はKey Vaultと同じシナリオを、Managed HSMでやってみる。

シーケンス

  1. Function (Func-A) にデータを投入
  2. Func-AはManaged HSMで暗号化
  3. Func-AからService BusのTopicにデータを投入
    • Func-AはレスポンスとしてHTTP 202を返す。
  4. Function (Func-B) がService Bus TopicのSubscriptionからデータを取得
  5. Func-BはManaged HSMで復号化
  6. Func-Bがデータ確認用Queueに投入

認証・アクセス管理

Func-A/BのSystem Managed Identityを使って、Service Bus、Managed HSMに対するRBACを構成

利用する暗号鍵

暗号鍵はAES256を利用

準備するもの

以下のインスタンスを準備しておく。なお、実装に使う言語は例によってJavaである。なお、この例ではVNetを作成しない。

コンポーネント価格レベル備考
Managed HSMB12022/04/01時点では、ARMテンプレート、Azure CLI、PowerShellで作成可能。作成手順はクイックスタートを参照。

クイック スタート: Azure CLI を使用してマネージド HSM をプロビジョニングしてアクティブにする / Quickstart: Provision and activate a Managed HSM using Azure CLI
https://docs.microsoft.com/azure/key-vault/managed-hsm/quick-create-cli
Azure FunctionsConsumptionOS : Linux
ランタイム : Java 11
Service BusStandard今回はTopicを使うため、SKUはStandard以上。
Queue、Topicは以下のように構成
mhsmt1 (Topic、サブスクリプションはs1、m1、Func-Bはs1をサブスクライブする。m1はデータ確認用)
mhsmq1 (Queue、データ確認用)

Managed HSMはインスタンスを作成するだけでは使えるようにはならないため、有効化する必要がある。手順はクイックスタートに記載がある。

マネージド HSM をアクティブにする / Activate your Managed HSM
https://docs.microsoft.com/azure/key-vault/managed-hsm/quick-create-cli#activate-your-managed-hsm

Managed HSMでMSI (Managed Identity) による認証・認可が通らない場合は、

  1. az loginでログインするユーザーに対し、Managed HSMの操作権限が付与されていることを確認
  2. 付与されていれば、az loginで明示的にログインし直す

ことを推奨する。

Azure Key Vault Managed HSM のドキュメント / Azure Key Vault Managed HSM documentation
https://docs.microsoft.com/azure/key-vault/managed-hsm/

ユーザーやManaged Identityに対するRBACの構成

1. Managed HSM管理プレーン

Managed HSMでは管理プレーンにAzure RBACを使う。特にManaged HSMの場合は、ドキュメントにも記載がある通り、管理プレーンとデータプレーンでRBACを明示的に設定しておく必要がある。

セキュリティ プリンシパルに、マネージド HSM への管理プレーンのアクセス権を付与しても、キーまたはデータ プレーンのロールの割り当て (Managed HSM ローカル RBAC) にアクセスする、データ プレーンへのアクセス権は付与されません。 この分離は、Managed HSM に格納されているキーへのアクセスに影響する特権が誤って展開されるのを防ぐために、意図的に行われているものです。
Granting a security principal management plane access to an managed HSM does not grant them any access to data plane to access keys or data plane role assignments Managed HSM local RBAC). This isolation is by design to prevent inadvertent expansion of privileges affecting access to keys stored in Managed HSM.

アクセス制御モデル / Access control model

今回は管理プレーンの共同作成者としてキーの管理者(今回の場合は筆者のアカウント)を指定しておく。

ロール対象
Managed HSM 共同作成者
(Managed HSM Contributor)
キーの管理者(ユーザー)

2. Managed HSMデータプレーン

暗号化・復号化のためにキーにアクセスする必要があるため、FunctionのSystem Managed Identityを有効化した上で、System managed identityを使いManaged HSMに対してRBACを構成する。

管理プレーンの場合、Azure RBAC(az role assignment create...)が利用できるものの、データプレーンではローカルRBACのみ(az keyvault role assignment create...)である点に注意が必要。今回割り当てるロールとその対象は以下の通り。

やりたいことロール対象
キーの作成・管理Managed HSM 暗号化ユーザー
(Managed HSM Crypto User)
キーの管理者
(ユーザー)
キーを使ったデータの暗号・復号Managed HSM 暗号化ユーザー
(Managed HSM Crypto User)
Func-A/B

ロールは以下のドキュメントを参照。

組み込みのロール / Built-in roles
https://docs.microsoft.com/azure/key-vault/managed-hsm/built-in-roles#built-in-roles

以下はロール割り当ての例。

assignee_object_id={割り当て先のObject ID、割り当て先がManaged Identityの場合はPrincipal ID}
hsmName={HSMの名前}
# 管理プレーン
az role assignment create --assignee-object-id $assignee_object_id --assignee-principal-type User --role "Managed HSM Contributor"
# データプレーン - この例ではManaged HSM全体をスコープとしている
# Managed HSM Crypto Userに割り当てる場合
role=21dbd100-6940-42c2-9190-5d6cb909625b
az keyvault role assignment create --hsm-name $hsmName --assignee-object-id $assignee_object_id --assignee-principal-type User --role $role --scope /

3. Service Bus

Service BusのRBAC構成は前回のエントリと同様なので省略。

キーの作成

今回は対称鍵であるAES256のキーを作成したいので、

  • キーの種類:oct-HSM
  • サイズ:256

を指定して作成する。キーに複数のバージョンを持たせることが可能なのは、Key Vaultの場合と同様。Keyの作成のドキュメントは以下にある。

az keyvault key create
https://docs.microsoft.com/cli/azure/keyvault/key?view=azure-cli-latest#az-keyvault-key-create

$ hsmName={Managed HSMの名前}
$ keyName={Keyの名前}
$ az keyvault key create --hsm-name $hsmName --kty oct-HSM --name $keyName --protection hsm --size 256

Function appの実装

Maven依存関係は前回のエントリと同じ(以下は2022/04/01現在の最新)。

<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-security-keyvault-keys</artifactId>
    <version>4.4.0-beta.7</version>
</dependency>
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-messaging-servicebus</artifactId>
    <version>7.7.0</version>
</dependency>
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-identity</artifactId>
    <version>1.5.0-beta.2</version>
</dependency>

実装方針も同じ。

書き込みApp読み取りApp
– HTTP Triggerで動作
– Managed HSMにアクセスし、HTTP bodyを暗号化
– Service Bus (mhsmt1) に投入
– Service Bus Triggerで動作
– Managed HSMにアクセスし、Service BusのTopicから取り出した値を復号
– Service Bus (mhsmq1) に投入

実装自体はほとんど変わらないが、今回はCBCモードを使っているので Initialized Vector (初期化ベクトル) を指定する必要がある。それゆえ、暗号化・復号化メソッド呼び出し時のパラメータがRSAやECの場合と異なる点に注意が必要。AESの初期化ベクトルは16バイトなので、以下のような感じで作成しておく。

SecureRandom random = new SecureRandom();
byte[] iv = new byte[16];
random.nextBytes(iv);

暗号化のメソッド呼び出しは以下のよう。

// [暗号化]
// Func-AのHTTP bodyをバイト配列に
byte[] rawBytes = body.getBytes(StandardCharsets.UTF_8);
// Initialized Vectorを使ってパラメータを作成
EncryptParameters encryptParameters = EncryptParameters.createA256CbcParameters(rawBytes, iv);
// バイト配列化した平文ではなく、encryptParameterを渡す
EncryptResult encryptResult = cryptoClient.encrypt(encryptParameters, new Context(key, value));
byte[] cipherText = encryptResult.getCipherText();

復号時に暗号化で使った初期化ベクトルと同じもの使う必要があるので、Service BusのTopicに暗号文を入れる際に添付しておく必要がある。今回は以下のようなクラスを用意して、

public class Payload {
    byte[] cipherText;
    byte[] iv;
    public byte[] getIv() {
        return iv;
    }
    public void setIv(byte[] iv) {
        this.iv = iv;
    }
    public byte[] getCipherText() {
        return cipherText;
    }
    public void setCipherText(byte[] _cipherText) {
        this.cipherText = _cipherText;
    }
}

このPayloadインスタンスに暗号文と初期化ベクトルを設定し、PayloadインスタンスをService BusのTopicに渡している。本来なら署名すべきではあるが、今回は動作確認なので省略した。

Payload payload = new Payload();
payload.setCipherText(encryptResult.getCipherText());
payload.setIv(iv);

復号はService BusのTopicから取得したPayloadの初期化ベクトルと暗号文を使う。

// [復号化]
// 復号対象はService Busから取得したObjectのバイト配列
byte[] cipherText = payload.getCipherText();
// Initialized Vectorを使ってパラメータを作成
DecryptParameters decryptParameters = DecryptParameters.createA256CbcParameters(cipherText, Payload.iv);
// バイト配列の暗号文ではなく、decryptParametersを渡す
DecryptResult decryptResult = cryptoClient.decrypt( decryptParameters, new Context(key, value));
byte[] rawBytes = decryptResult.getPlainText();

その他

今回はテスト目的なのでencrypt/decrypt時のContextを固定値にしているが、本来は適切な値に設定するか、Context.NONEを設定する必要がある。

今回のコードは以下のリポジトリのManaged_HSMにある。

https://github.com/anishi1222/ID-based-connection

コメントを残す

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

WordPress.com ロゴ

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

Twitter 画像

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

Facebook の写真

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

%s と連携中