Mengenkapsulasi dan mendekapsulasi menggunakan KEM

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

Enkapsulasi menggunakan kunci publik dari pasangan kunci KEM, dan dekapsulasi menggunakan kunci pribadi dari 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 telah diinstal sebelumnya di Cloud Shell. Jika tidak, instal OpenSSL di komputer Anda.
  • Buat kunci KEM dengan tujuan kunci dari KEY_ENCAPSULATION. Untuk melihat algoritma mana 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 principal yang harus mengambil kunci publik untuk mengenkapsulasi secret.
  • Berikan peran 'roles/cloudkms.decapsulator' pada kunci kepada setiap pengguna atau principal yang harus mendekapsulasi secret dengan kunci ini.

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

Enkapsulasi

Untuk mengenkapsulasi menggunakan kunci KEM, ambil kunci publik dan gunakan kunci publik untuk mengenkapsulasi.

gcloud

Contoh ini mengharuskan OpenSSL diinstal di sistem lokal Anda.

Mendownload 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 dari 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 defaultnya adalah pem.

Memformat ulang kunci publik

Perintah enkapsulasi mengharuskan 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 Enkapsulasi.

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 rahasia bersama dan ciphertext, 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 rahasia bersama yang dihasilkan.

Go

Untuk menjalankan kode ini, pertama-tama siapkan lingkungan pengembangan Go 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
}

Dekapsulasi

Gunakan Cloud KMS untuk mendekapsulasi ciphertext.

gcloud

Untuk menggunakan Cloud KMS di command line, pertama-tama Instal atau upgrade ke Google Cloud CLI versi terbaru.

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 rahasia bersama output.

Go

Untuk menjalankan kode ini, pertama-tama siapkan lingkungan pengembangan Go 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 cara menggunakan 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 dari 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: ciphertext berenkode base64 yang ingin Anda dekapsulasi.