Tink と Cloud KMS を使用したクライアントサイド暗号化

このトピックでは、Tink と Cloud Key Management Service(Cloud KMS)を使用してデータをローカルに暗号化し、Cloud Storage にアップロードする方法について説明します。Tink は、Google の暗号担当者とセキュリティ エンジニアが作成したオープンソースの暗号化ライブラリです。

概要

クライアント側の暗号化は、クラウドにデータを送信する前に実行する暗号化です。クライアントサイド暗号化を使用する場合、クラウドに送信する前に暗号鍵を作成して管理し、データを暗号化する必要があります。

このトピックでは、Cloud KMS の暗号鍵を使用して、Tink でクライアントサイドのエンベロープ暗号化を実装します。

このチュートリアルの Terraform ベースのブループリント バージョンは、 kms-solutions GitHub リポジトリにあります。

始める前に

  1. 暗号化用の対称 Cloud KMS 暗号鍵を作成します。鍵の URI をメモします。後で必要になります。
  2. Cloud KMS で使用する Tink をインストールします。
  3. Cloud Storage でバケットを作成し、暗号化されたデータをアップロードします。

必要なロール

Tink で Cloud KMS 鍵を使用するために必要な権限をサービス アカウントに付与するには、鍵に対するCloud KMS CryptoKey の暗号化/復号 roles/cloudkms.cryptoKeyEncrypterDecrypter)IAM ロールをサービス アカウントに付与するよう管理者に依頼してください。

ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

管理者は、カスタムロールや他の事前定義ロールを使用して、必要な権限をサービス アカウントに付与することもできます。

Tink を使用したエンベロープ暗号化

エンベロープ暗号化では、Cloud KMS 鍵は鍵暗号鍵(KEK)として機能します。つまり、実際のデータの暗号化に使用されるデータ暗号鍵(DEK)の暗号化に使用されます。

Cloud KMS で KEK を作成した後、各メッセージを暗号化するには、次のことを行う必要があります。

  1. データ暗号鍵(DEK)をローカルで作成します。
  2. この DEK をローカルで使用してメッセージを暗号化します。
  3. Cloud KMS を呼び出し KEK を使用して DEK を暗号化(ラップ)します。
  4. 暗号化されたデータとラップされた DEK を保管します。

Tink を使用する場合、このエンベロープ暗号化プロセスを最初から実装する必要はありません。

エンベロープ暗号化に Tink を使用するには、鍵 URI と認証情報を Tink に指定します。鍵 URI は Cloud KMS の KEK を指し、認証情報により Tink は KEK を使用できます。Tink は、DEK を生成し、データの暗号化と DEK のラップを行います。そして、暗号化したデータとラップした DEK を含む単一の暗号テキストを返します。

Tinkは、Python、Java、C ++、およびGoで、認証付き暗号化(AEAD)プリミティブを使用したエンベロープ暗号化をサポートしています。

Tink と Cloud KMS を接続する

Cloud KMS の KEK を使用して Tink で生成された DEK を暗号化するには、KEK の URI を取得する必要があります。Cloud KMS では、KEK URI の形式は次のとおりです。

gcp-kms://projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/KEY_VERSION

鍵へのパスを取得する方法については、Cloud KMS リソース ID の取得をご覧ください。

Tink を初期化してデータを暗号化します。

Tink は、プリミティブ(基盤となるアルゴリズムの詳細を管理する暗号構成要素)を使用しているため、タスクを安全に実行できます。各プリミティブは、特定のタスクを処理する API を提供します。ここでは AEAD を使用するため、Tink AEAD プリミティブを使用します。

Python

Python

Cloud KMS 用のクライアント ライブラリをインストールして使用する方法については、 Cloud KMS クライアント ライブラリをご覧ください。

Cloud KMS に対する認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証の設定をご覧ください。

"""A command-line utility for performing file encryption using GCS.

It is inteded for use with small files, utilizes envelope encryption and
facilitates ciphertexts stored in GCS.
"""

from absl import app
from absl import flags
from absl import logging
from google.cloud import storage

import tink
from tink import aead
from tink.integration import gcpkms


FLAGS = flags.FLAGS

flags.DEFINE_enum('mode', None, ['encrypt', 'decrypt'],
                  'The operation to perform.')
flags.DEFINE_string('kek_uri', None,
                    'The Cloud KMS URI of the key encryption key.')
flags.DEFINE_string('gcp_credential_path', None,
                    'Path to the GCP credentials JSON file.')
flags.DEFINE_string('gcp_project_id', None,
                    'The ID of the GCP project hosting the GCS blobs.')
flags.DEFINE_string('local_path', None, 'Path to the local file.')
flags.DEFINE_string('gcs_blob_path', None, 'Path to the GCS blob.')


_GCS_PATH_PREFIX = 'gs://'


def main(argv):
  del argv  # Unused.

  # Initialise Tink
  aead.register()

  try:
    # Read the GCP credentials and setup client
    client = gcpkms.GcpKmsClient(FLAGS.kek_uri, FLAGS.gcp_credential_path)
  except tink.TinkError as e:
    logging.exception('Error creating GCP KMS client: %s', e)
    return 1

  # Create envelope AEAD primitive using AES256 GCM for encrypting the data
  try:
    remote_aead = client.get_aead(FLAGS.kek_uri)
    env_aead = aead.KmsEnvelopeAead(
        aead.aead_key_templates.AES256_GCM, remote_aead
    )
  except tink.TinkError as e:
    logging.exception('Error creating primitive: %s', e)
    return 1

  storage_client = storage.Client.from_service_account_json(
      FLAGS.gcp_credential_path)

  try:
    bucket_name, object_name = _get_bucket_and_object(FLAGS.gcs_blob_path)
  except ValueError as e:
    logging.exception('Error parsing GCS blob path: %s', e)
    return 1
  bucket = storage_client.bucket(bucket_name)
  blob = bucket.blob(object_name)
  associated_data = FLAGS.gcs_blob_path.