KEM を使用してカプセル化とカプセル化解除を行う

このドキュメントでは、Cloud KMS 鍵で鍵カプセル化メカニズム(KEM)を使用して共有シークレットを確立する方法について説明します。

カプセル化では KEM 鍵ペアの公開鍵が使用され、カプセル化解除では鍵ペアの秘密鍵が使用されます。Cloud KMS では公開鍵を取得できます。この公開鍵は、標準ライブラリで使用して共有シークレットをカプセル化できます。共有シークレットをカプセル化解除するには、Cloud KMS のカプセル化解除メソッドを使用します。秘密鍵マテリアルを Cloud KMS の外部で使用することはできません。

始める前に

  • このドキュメントでは、コマンドラインでの実行例を示します。例をわかりやすくするため、Cloud Shell を使用します。この暗号化の例では、Cloud Shell にプリインストールされている OpenSSL が使用されています。インストールされていない場合は、マシンに OpenSSL をインストールします。
  • KEY_ENCAPSULATION鍵の目的とするKEM 鍵を作成します。鍵の目的 KEY_ENCAPSULATION でサポートされているアルゴリズムについては、鍵カプセル化アルゴリズムをご覧ください。

鍵に対する権限を付与する

  • シークレットをカプセル化するために公開鍵を取得する必要がある各ユーザーまたはプリンシパルに、鍵に対する roles/cloudkms.publicKeyViewer ロールを付与します。
  • この鍵でシークレットをカプセル化解除する必要がある各ユーザーまたはプリンシパルに、鍵に対する roles/cloudkms.decapsulator ロールを付与します。

Cloud KMS の権限とロールの詳細については、権限とロールをご覧ください。

カプセル化

KEM 鍵を使用してカプセル化するには、公開鍵を取得し、その公開鍵を使用してカプセル化します。

gcloud

このサンプルでは、ローカル システムに OpenSSL をインストールする必要があります。

公開鍵をダウンロードする

gcloud kms keys versions get-public-key KEY_VERSION \
    --key KEY_NAME \
    --keyring KEY_RING \
    --location LOCATION  \
    --output-file PUBLIC_KEY_FILE \
    --public-key-format PUBLIC_KEY_FORMAT

次のように置き換えます。

  • KEY_VERSION: カプセル化に使用する鍵のバージョン番号(例: 2)。
  • KEY_NAME: カプセル化に使用する鍵の名前。
  • KEY_RING: 鍵を含むキーリングの名前。
  • LOCATION: キーリングの Cloud KMS のロケーション
  • PUBLIC_KEY_FILE: 公開鍵が保存されるローカル ファイルパス。
  • PUBLIC_KEY_FORMAT: 公開鍵のターゲット形式(例: nist-pqc)。デフォルトの形式は pem です。

公開鍵の形式を変更する

encapsulate コマンドでは、公開鍵が PEM 形式である必要があります。公開鍵を nist-pqc などの別の形式でダウンロードした場合は、鍵を PEM 形式に変換する必要があります。公開鍵がすでに PEM 形式の場合は、カプセル化に進みます。

次のコマンドを使用して、ML-KEM-768 鍵の公開鍵を変換します。

{ echo -n "MIIEsjALBglghkgBZQMEBAIDggShAA==" | base64 -d ; cat PUBLIC_KEY_FILE; } | \
openssl pkey -inform DER -pubin -pubout -out PEM_PUBLIC_KEY_FILE

次のコマンドを使用して、ML-KEM-1024 鍵の公開鍵を変換します。

{ echo -n "MIIGMjALBglghkgBZQMEBAMDggYhAA==" | base64 -d ; cat PUBLIC_KEY_FILE; } | \
openssl pkey -inform DER -pubin -pubout -out PEM_PUBLIC_KEY_FILE

次のように置き換えます。

  • PUBLIC_KEY_FILE: ダウンロードした公開鍵ファイルのパス(未加工形式)。
  • PEM_PUBLIC_KEY_FILE: 公開鍵を PEM 形式で保存するパスとファイル名。

カプセル化

共有シークレットと暗号文を作成するには、次のコマンドを使用します。

openssl pkeyutl \
    -encap \
    -pubin \
    -inkey PEM_PUBLIC_KEY_FILE \
    -out CIPHERTEXT_FILE \
    -secret SHARED_SECRET_FILE

次のように置き換えます。

  • PEM_PUBLIC_KEY_FILE: ダウンロードした公開鍵ファイルのパス(PEM 形式)。
  • CIPHERTEXT_FILE: 結果の暗号テキストを保存するパス。
  • SHARED_SECRET_FILE: 結果の共有シークレットを保存するパス。

Go

このコードを実行するには、まず Go 開発環境を設定し、Cloud KMS Go SDK をインストールします。

import (
	"context"
	"crypto/mlkem"
	"fmt"
	"hash/crc32"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	"cloud.google.com/go/kms/apiv1/kmspb"
)

// encapsulateMLKEM demonstrates how to encapsulate a shared secret using an ML-KEM-768 public key
// from Cloud KMS.
func encapsulateMLKEM(w io.Writer, keyVersionName string) error {
	// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %w", err)
	}
	defer client.Close()

	// crc32c calculates the CRC32C checksum of the given data.
	crc32c := func(data []byte) uint32 {
		t := crc32.MakeTable(crc32.Castagnoli)
		return crc32.Checksum(data, t)
	}

	// Build the request to get the public key in NIST PQC format.
	req := &kmspb.GetPublicKeyRequest{
		Name:            keyVersionName,
		PublicKeyFormat: kmspb.PublicKey_NIST_PQC,
	}

	// Call the API to get the public key.
	response, err := client.GetPublicKey(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to get public key: %w", err)
	}

	// Optional, but recommended: perform integrity verification on the response.
	// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
	// https://cloud.google.com/kms/docs/data-integrity-guidelines
	if response.GetName() != req.GetName() {
		return fmt.Errorf("GetPublicKey: request corrupted in-transit")
	}
	if response.GetPublicKeyFormat() != req.GetPublicKeyFormat() {
		return fmt.Errorf("GetPublicKey: request corrupted in-transit")
	}
	if int64(crc32c(response.GetPublicKey().GetData())) != response.GetPublicKey().GetCrc32CChecksum().GetValue() {
		return fmt.Errorf("GetPublicKey: response corrupted in-transit")
	}

	// Use the public key with crypto/mlkem to encapsulate a shared secret.
	ek, err := mlkem.NewEncapsulationKey768(response.GetPublicKey().GetData())
	if err != nil {
		return fmt.Errorf("NewEncapsulationKey768: %w", err)
	}
	sharedSecret, ciphertext := ek.Encapsulate()

	fmt.Fprintf(w, "Encapsulated ciphertext: %x\n", ciphertext)
	fmt.Fprintf(w, "Shared secret: %x\n", sharedSecret)
	return nil
}

カプセル化解除

Cloud KMS を使用して暗号テキストをカプセル化解除します。

gcloud

コマンドラインで Cloud KMS を使用するには、まず Google Cloud CLI の最新バージョンをインストールまたはアップグレードします

gcloud kms decapsulate \
    --version KEY_VERSION \
    --key KEY_NAME \
    --keyring KEY_RING \
    --location LOCATION  \
    --ciphertext-file CIPHERTEXT_FILE \
    --shared-secret-file SHARED_SECRET_FILE

次のように置き換えます。

  • KEY_VERSION: カプセル化解除に使用する鍵のバージョン(例: 3)。
  • KEY_NAME: カプセル化解除に使用する鍵の名前。
  • KEY_RING: 鍵が配置されているキーリングの名前。
  • LOCATION: キーリングの Cloud KMS のロケーション。
  • CIPHERTEXT_FILE: 入力暗号テキストのローカル ファイルパス。
  • SHARED_SECRET_FILE: 出力共有シークレットを保存するローカル ファイルパス。

Go

このコードを実行するには、まず Go 開発環境を設定し、Cloud KMS Go SDK をインストールします。

import (
	"context"
	"fmt"
	"hash/crc32"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	"cloud.google.com/go/kms/apiv1/kmspb"
	"google.golang.org/protobuf/types/known/wrapperspb"
)

// decapsulate decapsulates the given ciphertext using a saved private key of purpose
// KEY_ENCAPSULATION stored in KMS.
func decapsulate(w io.Writer, keyVersionName string, ciphertext []byte) error {
	// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
	// ciphertext := []byte("...")

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %w", err)
	}
	defer client.Close()

	// crc32c calculates the CRC32C checksum of the given data.
	crc32c := func(data []byte) uint32 {
		t := crc32.MakeTable(crc32.Castagnoli)
		return crc32.Checksum(data, t)
	}

	// Optional but recommended: Compute ciphertext's CRC32C.
	ciphertextCRC32C := crc32c(ciphertext)

	// Build the request.
	req := &kmspb.DecapsulateRequest{
		Name:             keyVersionName,
		Ciphertext:       ciphertext,
		CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)),
	}

	// Call the API.
	result, err := client.Decapsulate(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to decapsulate: %w", err)
	}

	// Optional, but recommended: perform integrity verification on the response.
	// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
	// https://cloud.google.com/kms/docs/data-integrity-guidelines
	if !result.GetVerifiedCiphertextCrc32C() {
		return fmt.Errorf("Decapsulate: request corrupted in-transit")
	}
	if result.GetName() != req.GetName() {
		return fmt.Errorf("Decapsulate: request corrupted in-transit")
	}
	if int64(crc32c(result.GetSharedSecret())) != result.GetSharedSecretCrc32C() {
		return fmt.Errorf("Decapsulate: response corrupted in-transit")
	}

	fmt.Fprintf(w, "Decapsulated plaintext: %x", result.GetSharedSecret())
	return nil
}

API

これらの例では、HTTP クライアントとして curl を使用して API の使用例を示しています。アクセス制御の詳細については、Cloud KMS API へのアクセスをご覧ください。

CryptoKeyVersions.decapsulate メソッドを使用します。

curl "https://cloudkms.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/KEY_VERSION:decapsulate" \
  --request "POST" \
  --header "authorization: Bearer TOKEN" \
  --header "content-type: application/json" \
  --data '{"ciphertext": "CIPHERTEXT"}'

次のように置き換えます。

  • PROJECT_ID: キーリングを含むプロジェクトの ID。
  • LOCATION: キーリングの Cloud KMS のロケーション
  • KEY_RING: 鍵を含むキーリングの名前
  • KEY_NAME: 暗号化に使用する鍵の名前。
  • KEY_VERSION: 暗号化に使用する鍵バージョンの ID
  • CIPHERTEXT: カプセル化解除する base64 でエンコードされた暗号テキスト。