Mengenkapsulasi dan mendekapsulasi menggunakan KEM

Dokumen ini menjelaskan penggunaan mekanisme enkapsulasi kunci (KEM) dengan kunci Cloud KMS untuk membuat secret bersama.

Enkapsulasi menggunakan kunci publik pasangan kunci KEM, dan dekapsulasi menggunakan kunci pribadi pasangan kunci. Cloud KMS memungkinkan Anda mengambil kunci publik, yang kemudian dapat Anda gunakan dengan library standar untuk mengenkapsulasi rahasia bersama Anda. Untuk mendekapsulasi rahasia bersama, gunakan metode dekapsulasi Cloud KMS. Anda tidak dapat menggunakan materi kunci pribadi di luar Cloud KMS.

Sebelum memulai

  • Dokumen ini memberikan contoh yang berjalan di command line. Untuk menyederhanakan penggunaan contoh, gunakan Cloud Shell. Contoh enkripsi menggunakan OpenSSL, yang sudah diinstal sebelumnya di Cloud Shell. Jika tidak, instal OpenSSL di komputer Anda.
  • Buat kunci KEM dengan tujuan kunci KEY_ENCAPSULATION. Untuk melihat algoritma yang didukung untuk tujuan kunci KEY_ENCAPSULATION, lihat algoritma enkapsulasi kunci.

Memberikan izin pada kunci

  • Berikan peran roles/cloudkms.publicKeyViewer pada kunci kepada setiap pengguna atau akun utama yang harus mengambil kunci publik untuk mengenkapsulasi rahasia.
  • Berikan peran 'roles/cloudkms.decapsulator' pada kunci kepada setiap pengguna atau akun utama yang harus mendekapsulasi rahasia dengan kunci ini.

Untuk mengetahui informasi selengkapnya tentang izin dan peran di Cloud KMS, lihat Izin dan Peran.

Enkapsulasi

Untuk melakukan enkapsulasi menggunakan kunci KEM, ambil kunci publik dan gunakan kunci publik untuk melakukan enkapsulasi.

gcloud

Contoh ini mengharuskan OpenSSL diinstal di sistem lokal Anda.

Download kunci publik

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

Ganti kode berikut:

  • KEY_VERSION: Nomor versi kunci yang ingin Anda gunakan untuk enkapsulasi—misalnya, 2.
  • KEY_NAME: Nama kunci yang ingin Anda gunakan untuk enkapsulasi.
  • KEY_RING: nama key ring yang berisi kunci.
  • LOCATION: lokasi Cloud KMS key ring.
  • PUBLIC_KEY_FILE: Jalur file lokal tempat kunci publik akan disimpan.
  • PUBLIC_KEY_FORMAT: Format target untuk kunci publik—misalnya, nist-pqc. Format default-nya adalah pem.

Memformat ulang kunci publik

Perintah encapsulate memerlukan kunci publik dalam format PEM. Jika Anda telah mendownload kunci publik dalam format lain, seperti nist-pqc, Anda harus mengonversi kunci ke format PEM. Jika kunci publik Anda sudah dalam format PEM, lanjutkan dari Encapsulate.

Gunakan perintah berikut untuk mengonversi kunci publik untuk kunci ML-KEM-768:

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

Gunakan perintah berikut untuk mengonversi kunci publik untuk kunci ML-KEM-1024:

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

Ganti kode berikut:

  • PUBLIC_KEY_FILE: Jalur ke file kunci publik yang didownload dalam format mentah.
  • PEM_PUBLIC_KEY_FILE: Jalur dan nama file untuk menyimpan kunci publik dalam format PEM.

Enkapsulasi

Untuk membuat secret dan ciphertext bersama, Anda dapat menggunakan perintah berikut:

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

Ganti kode berikut:

  • PEM_PUBLIC_KEY_FILE: Jalur ke file kunci publik yang didownload dalam format PEM.
  • CIPHERTEXT_FILE: Jalur tempat Anda ingin menyimpan ciphertext yang dihasilkan.
  • SHARED_SECRET_FILE: Jalur tempat Anda ingin menyimpan secret bersama yang dihasilkan.

Go

Untuk menjalankan kode ini, siapkan lingkungan pengembangan Go terlebih dahulu dan instal 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
}

Pelepasan kapsul

Gunakan Cloud KMS untuk mendekapsulasi ciphertext.

gcloud

Untuk menggunakan Cloud KMS di command line, Instal atau upgrade ke versi terbaru Google Cloud CLI terlebih dahulu.

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

Ganti kode berikut:

  • KEY_VERSION: versi kunci yang akan digunakan untuk dekapsulasi—misalnya, 3.
  • KEY_NAME: nama kunci yang akan digunakan untuk dekapsulasi.
  • KEY_RING: nama key ring tempat kunci berada.
  • LOCATION: lokasi Cloud KMS untuk key ring.
  • CIPHERTEXT_FILE: jalur file lokal untuk ciphertext input.
  • SHARED_SECRET_FILE: jalur file lokal untuk menyimpan secret bersama output.

Go

Untuk menjalankan kode ini, siapkan lingkungan pengembangan Go terlebih dahulu dan instal 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

Contoh ini menggunakan curl sebagai klien HTTP untuk menunjukkan penggunaan API. Untuk mengetahui informasi selengkapnya tentang kontrol akses, lihat Mengakses Cloud KMS API.

Gunakan metode 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"}'

Ganti kode berikut:

  • PROJECT_ID: ID project yang berisi key ring.
  • LOCATION: lokasi Cloud KMS key ring.
  • KEY_RING: nama key ring yang berisi kunci.
  • KEY_NAME: nama kunci yang akan digunakan untuk enkripsi.
  • KEY_VERSION: ID versi kunci yang akan digunakan untuk enkripsi
  • CIPHERTEXT: teks tersandi berenkode base64 yang ingin Anda lepaskan.