Vault
Hashicorp

HashiCorp Vault の Data Encryption (Transit Secret Backend) を試す

More than 1 year has passed since last update.

はじめに

HashiCorpのVaultが提供している Data Encryption (Transit Secret Backend)機能について公式ドキュメントを参考に動作を確認しましたので備忘録として残しておきます。

やったこと

Vaultの Data Encryption 機能を利用してにテキストの暗号化・復号化を行う。

HashiCorp Vaultとは?

HashiCorpが提供している機密情報の管理ツールです。

Data Encryption (Transit Secret Backend)とは?

Vaultにデータの暗号化、復号化を行わせる機能です。

この機能でVaultは暗号化復号化に利用するKeyの管理のみ行い、暗号化したデータは保持しません。
暗号化されたデータはVaultの呼び出し元で管理(DBなどに格納)する必要があります。

HashiCorpからRailsアプリ向けに提供されているvault-railsではこの機能が利用されてます。

参考

公式サイト

Vault by HashiCorp

ドキュメント

Transit Secret Backend
Decrypt Data


事前準備

Transit Secret Backendを利用できるようにしておきます。

VaultをDevモードで起動

DevモードではVaultを停止させるとデータが消えてしまいますが、今回は動作確認のためDevモードで起動します。

$ vault server -dev

...
    export VAULT_ADDR='http://127.0.0.1:8200'
...
Root Token: e301d832-a587-0092-7dec-eaf71369c2b3
...

環境変数の設定

Vaultがリクエストを受け付けるアドレスとTokenを環境変数に設定しておきます。

$ export VAULT_ADDR='http://127.0.0.1:8200'
$ export VAULT_TOKEN='e301d832-a587-0092-7dec-eaf71369c2b3'

transitをマウント

transit機能を利用できるようにマウントしておきます。

$ vault mount transit

Keyを生成・削除

データの暗号化・復号化に利用するKeyを生成します。
Keyの名称はmy-keyとします。

Create Key (aes256-gcm96)

暗号化方式のデフォルトはaes256-gcm96です。
typeオプションを指定しないでKeyを生成するとaes256-gcm96でKeyが生成されます。
typeオプションを指定することで ecdsa-p256, ed25519も指定可能です。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    $VAULT_ADDR/v1/transit/keys/my-key

or

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "type": "aes256-gcm96" }' \
    $VAULT_ADDR/v1/transit/keys/my-key

Create Key (ecdsa-p256)

typeオプション(暗号化方式)にecdsa-p256を指定してKeyを生成。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "type": "ecdsa-p256" }' \
    $VAULT_ADDR/v1/transit/keys/my-key

Create Key (ed25519)

typeオプション(暗号化方式)にed25519を指定してKeyを生成。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "type": "ed25519" }' \
    $VAULT_ADDR/v1/transit/keys/my-key

Read Key

Keyの情報を確認できます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    $VAULT_ADDR/v1/transit/keys/my-key

{
  "request_id": "5d0e4a83-da8f-80b8-0ffb-d9cd9158cc0b",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "deletion_allowed": false,
    "derived": false,
    "exportable": false,
    "keys": {
      "1": 1506730923
    },
    "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"
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

List Keys

Keyの一覧を取得できます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request LIST \
    $VAULT_ADDR/v1/transit/keys

{
  "request_id": "bf844792-a14c-4e6e-20f6-b10a0f1af141",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "keys": [
      "my-key"
    ]
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Delete Key

Keyを削除します。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request DELETE \
    $VAULT_ADDR/v1/transit/keys/my-key    

Keyの削除が許可されてない場合、400エラーと以下のメッセージが返却されます。
削除するためにはKeyのdeletion_allowedオプションをtrueに変更する必要があります。

< HTTP/1.1 400 Bad Request
...
{
  "errors": [
    "error deleting policy my-key: deletion is not allowed for this policy"
  ]
}

Update Key Configuration

Keyの設定を変更します。
Keyの削除を許可する場合はdeletion_allowedオプションをtrueにして実行します。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "deletion_allowed": true }' \
    $VAULT_ADDR/v1/transit/keys/my-key/config

データの暗号化・復号化

生成したKeyを利用してデータの暗号化、復号化を実施します。

plaintext

暗号化したいデータ(plaintext)を指定して暗号化します。
plaintextはbase64でエンコードされてる必要があります。

base64 encode

vaultには暗号化対象のデータをbase64で渡す必要があるのでbase64でエンコードしておきます。

$ echo "テスト" | base64
44OG44K544OICg==

Encrypt Data

base64化したテキスト(plaintext)をVaultに暗号化させます。
暗号化されたデータはレスポンスのciphertext部分で返却されます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "plaintext": "44OG44K544OICg==" }' \
    $VAULT_ADDR/v1/transit/encrypt/my-key

{
  "request_id": "3b15a62d-e5d5-dc9e-e60d-1763969dccf4",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "ciphertext": "vault:v1:iwDQyBSyXgo+MutoHXPT76MpVek78fxMQW1LXI3C3C++EVIJyFY="
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Decrypt Data

暗号化したデータ(ciphertext)をVaultに復号化させます。
復号化されたデータはレスポンスのplaintext部分で返却されます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "ciphertext": "vault:v1:iwDQyBSyXgo+MutoHXPT76MpVek78fxMQW1LXI3C3C++EVIJyFY=" }' \
    $VAULT_ADDR/v1/transit/decrypt/my-key

{
  "request_id": "7857e0c1-1f68-73e3-c54d-09fdfafd684f",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "plaintext": "44OG44K544OICg=="
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

base64 decode

Vaultから返却されたデータ(plaintext)をbase64でデコードすると元のテキストに戻ります。

$ echo "44OG44K544OICg==" | base64 -D
テスト

補足

暗号化

plaintextがbase64でエンコードされてないと暗号化できません。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "plaintext": "テスト" }' \
    $VAULT_ADDR/v1/transit/encrypt/my-key

< HTTP/1.1 400 Bad Request
...
{
  "errors": [
    "failed to base64-decode plaintext"
  ]
}

復号化

ciphertextの値が誤っていると復号化できなくなります。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "ciphertext": "vault:v1:iwDQyBSyXgo+MutoHXPT76MpVek78fxMQW1LXI3C3D++EVIJyFY=" }' \
    $VAULT_ADDR/v1/transit/decrypt/my-key

< HTTP/1.1 400 Bad Request
...
{
  "errors": [
    "invalid ciphertext: unable to decrypt"
  ]
}

plaintext & context

Keyを生成する際にderivedオプションにtrueを指定した場合、plaintextに加えてcontextが付与されてないと暗号化・復号化が出来ないようになります。

Keyの生成

derivedオプションにtrueを指定してKeyを生成します。
Keyの名称はmy-key2とします。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "derived": true }' \
    $VAULT_ADDR/v1/transit/keys/my-key2

base64 encode

contextplaintextをbase64でエンコードしておきます。

$ echo "キー" | base64
44Kt44O8Cg==
$ echo "テスト" | base64
44OG44K544OICg==

Encrypt Data

contextplaintextを渡してVaultに暗号化させます。
暗号化したデータはciphertextとして返却されます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "context":"44Kt44O8Cg==" , "plaintext": "44OG44K544OICg==" }' \
    $VAULT_ADDR/v1/transit/encrypt/my-key2

{
  "request_id": "29c41d1b-ec94-3800-af4b-f57b2aa58063",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "ciphertext": "vault:v1:WAJUPIOn65NV7dI+Zlbcr0z8XJvz1AqYQYJN6+yzGT+CVoKuK3s="
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Decrypt Data

contextplaintextを渡してVaultに復号化させます。
復号化したデータはplaintextとして返却されます。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "context":"44Kt44O8Cg==", "ciphertext": "vault:v1:WAJUPIOn65NV7dI+Zlbcr0z8XJvz1AqYQYJN6+yzGT+CVoKuK3s=" }' \
    $VAULT_ADDR/v1/transit/decrypt/my-key2

{
  "request_id": "15ea30d1-9c0c-e711-b947-3ace1a55c622",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "plaintext": "44OG44K544OICg=="
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

base64 decode

Vaultから返却されたデータをbase64でデコードすると元のテキストに戻ります。

$ echo "44OG44K544OICg==" | base64 -D
テスト

補足

暗号化

derivedオプションが有効なKeyではcontextを指定しないと暗号化できません。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "plaintext": "44OG44K544OICg==" }' \
    $VAULT_ADDR/v1/transit/encrypt/my-key2

< HTTP/1.1 400 Bad Request
...
{
  "errors": [
    "missing 'context' for key derivation; the key was created using a derived key, which means additional, per-request information must be included in order to perform operations with the key"
  ]
}

復号化

derivedオプションが有効なKeyではcontextを指定しないと復号化できません。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "ciphertext": "vault:v1:WAJUPIOn65NV7dI+Zlbcr0z8XJvz1AqYQYJN6+yzGT+CVoKuK3s=" }' \
    $VAULT_ADDR/v1/transit/decrypt/my-key2

 < HTTP/1.1 400 Bad Request
 ...
 {
  "errors": [
    "missing 'context' for key derivation; the key was created using a derived key, which means additional, per-request information must be included in order to perform operations with the key"
  ]
}

contextが暗号化した時と不一致でも復号化できません。

$ curl \
    --header "X-Vault-Token: $VAULT_TOKEN" \
    --request POST \
    --data '{ "context":"44Kr44O8Cg==", "ciphertext": "vault:v1:WAJUPIOn65NV7dI+Zlbcr0z8XJvz1AqYQYJN6+yzGT+CVoKuK3s=" }' \
    $VAULT_ADDR/v1/transit/decrypt/my-key2

< HTTP/1.1 400 Bad Request
...
{
  "errors": [
    "invalid ciphertext: unable to decrypt"
  ]
}