Carpe Diem

備忘録

HashiCorp VaultのTransit secret engineでデータを暗号化・復号する

概要

HashiCorp VaultにはTransit Secrets Engineという、暗号化・復号をしてくれる機能があります。 このTransit Secrets EngineはKey/Valueのように暗号化データを保存することはなく、暗号化するための暗号化キーを保存&バージョン管理します。
また複数の機能を持っており、AWS KMSのようなデータキーを生成する機能もあります。
今回はこれの使い方を紹介します。

環境

  • Vault 0.10.3

基本的な使い方

暗号化キーの生成

まずはtransitを有効化します。

$ vault secrets enable transit
Success! Enabled the transit secrets engine at: transit/

次に暗号化キー(マスターキー)を生成します。my-keyの部分は任意です。

$ vault write -f transit/keys/my-key
Success! Data written to: transit/keys/my-key

確認します。

$ vault list transit/keys
Keys
----
my-key

$ vault read transit/keys/my-key
Key                       Value
---                       -----
allow_plaintext_backup    false
deletion_allowed          false
derived                   false
exportable                false
keys                      map[1:1531699587]
latest_version            1
min_decryption_version    1
min_encryption_version    0
name                      my-key
supports_decryption       true
supports_derivation       true
supports_encryption       true
supports_signing          false
type                      aes256-gcm96

暗号化

plaintextというフィールドにbase64化したデータをいれると暗号化できます。

$ vault write transit/encrypt/my-key plaintext=$(base64 <<< "my secret data")

Key           Value
---           -----
ciphertext    vault:v1:z9SaQnk5U02vbY4VkVHutELTfObIOwZZ+jny1f80lFQqyKfZanV5LbB3cQ==

復号

ciphertextというフィールドに暗号化したデータを入れると復号できます。

$ vault write transit/decrypt/my-key \
    ciphertext=vault:v1:z9SaQnk5U02vbY4VkVHutELTfObIOwZZ+jny1f80lFQqyKfZanV5LbB3cQ==

Key          Value
---          -----
plaintext    bXkgc2VjcmV0IGRhdGEK

復号されたデータはbase64化されてるのでデコードが必要です。

$ base64 --decode <<< bXkgc2VjcmV0IGRhdGEK

my secret data

ワンライナーだと以下です。

$ vault write -field=plaintext transit/decrypt/my-key \
    ciphertext=vault:v1:z9SaQnk5U02vbY4VkVHutELTfObIOwZZ+jny1f80lFQqyKfZanV5LbB3cQ== | base64 --decode

my secret data

キーの更新

基本的にこのマスターキーは削除できません。なぜなら暗号化したデータを復号する手段が無くなってしまうからです。
代わりに鍵を新しくするrotateを使うことを推奨しています。

$ vault write -f transit/keys/my-key/rotate
Success! Data written to: transit/keys/my-key/rotate

マスターキーのバージョンが上がっています。

$ vault read transit/keys/my-key
Key                       Value
---                       -----
allow_plaintext_backup    false
deletion_allowed          false
derived                   false
exportable                false
keys                      map[1:1531699587 2:1531700123]
latest_version            2
min_decryption_version    1
min_encryption_version    0
name                      my-key
supports_decryption       true
supports_derivation       true
supports_encryption       true
supports_signing          false
type                      aes256-gcm96

以降の暗号化では新しいバージョンのキーが利用されますが、古い暗号化データは復号可能です。

$ vault write -field=plaintext transit/decrypt/my-key \
    ciphertext=vault:v1:z9SaQnk5U02vbY4VkVHutELTfObIOwZZ+jny1f80lFQqyKfZanV5LbB3cQ== | base64 --decode

my secret data

古い暗号化データを新しいバージョンのキーで再暗号化したい

rewrapを使います。

$ vault write transit/rewrap/my-key \
    ciphertext=vault:v1:z9SaQnk5U02vbY4VkVHutELTfObIOwZZ+jny1f80lFQqyKfZanV5LbB3cQ==

Key           Value
---           -----
ciphertext    vault:v2:tQcvLtYUrIvMw7Xwo+z3+zdwW25K6AXpZVEPAZNthSlGqmJtxRwwyvlxvA==

v1だった暗号化データがv2に変わりました。

マスターキーを削除したい

ただどうしても削除したい場合はconfigをいじって削除OKにしてから

$ vault write transit/keys/my-key/config deletion_allowed=true
Success! Data written to: transit/keys/my-key/config

deleteコマンドを実行します。

$ vault delete transit/keys/my-key
Success! Data deleted (if it existed) at: transit/keys/my-key

KMSのような暗号化

Transit Secret EngineにはAWS KMSのようなデータの暗号化に使用する暗号化キーを簡単に作成および管理できる仕組みがあります。

エンベロープ暗号化(Envelope Encryption)

KMSの暗号化の仕組みを説明します。これはエンベロープ暗号化と呼ばれます。

データキーの生成

最初にマスターキーが

  • 暗号化用のデータキー
  • ↑のデータキー自体を暗号化した暗号化済みデータキー

の2つを生成します。

f:id:quoll00:20180716092524p:plain

ref: AWS Key Management Service の概念 - AWS Key Management Service

データの暗号化

データキーで暗号化します。 f:id:quoll00:20180716092552p:plain

ref: AWS Key Management Service の概念 - AWS Key Management Service

暗号化したらこのデータキーは破棄します。これが漏れると復号できちゃうので。
代わりに暗号化済みのデータキーを一緒に保存します。これは暗号化されてるので漏れても構いません。

データの復号

復号する時は暗号化済みデータキーをマスターキーで復号して、得られたデータキーを使ってデータを復号します

f:id:quoll00:20180716092619p:plain

ref: AWS Key Management Service の概念 - AWS Key Management Service

エンベロープ暗号化のメリットは?

キーの管理が簡単になる

普通にデータを暗号化しようとした時、

  • 暗号化する鍵を共有化すると漏れたときのリスク大
  • かといってデータ毎に暗号化キーを用意すると管理する鍵が増大

というどっちつかずな問題が存在します。
エンベロープ暗号化にすることで

  • 最小限マスターキーを管理しつつ、データ暗号化の鍵をユニーク化できる

という解決策になります。

マスターキーが漏れたら同じこと(リスク大)じゃない?と思うかもしれませんが、先程のケースと違って

  • データ暗号化時はマスターキーを使わない
  • データ復号時もデータキーさえ復号すればいいのでマスターキーを保持する必要がない

という点で漏洩リスクが大きく減っています。

データを復号せずともマスターキーローテーションが可能

マスターキーをローテーションする際も、暗号化データキーを

  • 古いマスターキーで復号
  • 新しいマスターキーで暗号化

とすれば良いので、わざわざデータを復号・再暗号化せずともローテーション可能です。

Transitで実演

概要が分かったと思うのでTransitで実際に試します。

データキーの生成

エンベロープ暗号用のマスターキーを用意します。

$ vault write -f transit/keys/envelope-key
Success! Data written to: transit/keys/envelope-key

データキーを生成します。

$ vault write -f transit/datakey/plaintext/envelope-key

Key           Value
---           -----
ciphertext    vault:v1:9uoqioBTNJKrYh6eqlpg3o6rIKGyZP7CXFvYm1KjStjoH3hT/4a3fekkkgiQBCkzVReOwAOzsQkOCIkw
plaintext     0kyhOVlCxBx+wN2iY9MIoPoV7gOH9qiFEexN75BYv5o=
  • plaintextが暗号化用のデータキーです。デフォルトだと256bitです。
  • ciphertextがデータキーplaintextを暗号化したものです。

暗号化

OpenSSLで暗号化します。パスワードに先程のデータキーを使います。

$ openssl aes-256-cbc -e -in plain.txt -out cipher.txt
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:

生成されました。

$ cat cipher.txt 
Salted__?^D][?6    7?e?VדS??2??[A,П??d??u`??nš?y

暗号化できたらデータキーは破棄します。

復号

データキーは手元にないので、まずは暗号化済みデータキーをマスターキーで復号します。

$ vault write transit/decrypt/envelope-key \
    ciphertext=vault:v1:9uoqioBTNJKrYh6eqlpg3o6rIKGyZP7CXFvYm1KjStjoH3hT/4a3fekkkgiQBCkzVReOwAOzsQkOCIkw

Key          Value
---          -----
plaintext    0kyhOVlCxBx+wN2iY9MIoPoV7gOH9qiFEexN75BYv5o=

データキーが復号できたら先程の暗号化データを復号します。

$ openssl aes-256-cbc -d -in cipher.txt -out decrypt.txt
enter aes-256-cbc decryption password:

$ cat decrypt.txt 
my secret data

無事復号できました。

Bitcasaのような重複を排除した暗号化

収束暗号化(Convergent Encryption)

通常の暗号化は、nonceなど使って同じ平文でも出力される暗号文を毎回異なるものにするべきとされてます。
でないと頻度分析などで手がかりを与えることになってしまうためです。
なので最初の暗号化についても、inputが同じでもoutputは毎回異なります。

$ vault write transit/encrypt/my-key plaintext=$(base64 <<< "my secret data")
Key           Value
---           -----
ciphertext    vault:v1:sYeeQLbzRnDFppIc6Hm9VzI6meD7t0ih6mcSpWTlcug4DfsXJdey/OySCw==

$ vault write transit/encrypt/my-key plaintext=$(base64 <<< "my secret data")
Key           Value
---           -----
ciphertext    vault:v1:WlZpLCYy716BmvT107wY1IaInOsco7QiILwaMbckYB0d9W3TyX+T3dgU5w==

しかしながらクラウドストレージのようなケースでは

  • データは暗号化したい
  • 重複データは容量を無駄使いしないよう同じ暗号データであってほしい

という要件があります。これを解決するのが収束暗号化(Convergent Encryption)です。

収束暗号化では暗号化したいデータをハッシュ化した値を暗号化キーにすると言う方針をとることで、同じデータであれば同じ暗号化データになるようにしています。

ただそのままだと同じデータを持っている他の人にバレると言う問題が起きるので、vaultではこれに加えてcontextというパラメータを用意し、鍵導出関数(Key Derivation Function)を使って暗号化キー生成をすることでよりセキュアにしています。

Transitで実演

概要が分かったところで実際に試します。

マスターキーの生成

収束暗号化の場合はマスターキーにパラメータを設定する必要があります。

$ vault write -f transit/keys/conv-key \
    convergent_encryption=true derived=true

暗号化

収束暗号化の場合はcontextパラメータが必須です。
base64化したデータを入れます。

$ vault write transit/encrypt/conv-key plaintext=$(base64 <<< "my secret data") context=$(base64 <<< "some password")
Key           Value
---           -----
ciphertext    vault:v1:N4d5WkZIuplx+FKNeqX3hwVRqjdce9viI7+MV5VY7tahF7t/wnr/cf2Ypw==

暗号化できました。

ではもう一度同じinputで暗号化してみましょう。

$ vault write transit/encrypt/conv-key plaintext=$(base64 <<< "my secret data") context=$(base64 <<< "some password")
Key           Value
---           -----
ciphertext    vault:v1:N4d5WkZIuplx+FKNeqX3hwVRqjdce9viI7+MV5VY7tahF7t/wnr/cf2Ypw==

同じ暗号化データになりました!
同じデータであれば同じ暗号化データになるという収束暗号化だからですね。

まとめ

Transit Secret Engineを使って

を試しました。

ソース