概要
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つを生成します。
ref: AWS Key Management Service の概念 - AWS Key Management Service
データの暗号化
データキーで暗号化します。
ref: AWS Key Management Service の概念 - AWS Key Management Service
暗号化したらこのデータキーは破棄します。これが漏れると復号できちゃうので。
代わりに暗号化済みのデータキーを一緒に保存します。これは暗号化されてるので漏れても構いません。
データの復号
復号する時は暗号化済みデータキーをマスターキーで復号して、得られたデータキーを使ってデータを復号します
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を使って
- 通常の暗号化
- エンベロープ暗号化
- 収束暗号化
を試しました。