Encapsuler et décapsuler à l'aide de KEM

Ce document décrit l'utilisation de mécanismes d'encapsulation de clé (KEM) avec des clés Cloud KMS pour établir des secrets partagés.

L'encapsulation utilise la clé publique de la paire de clés KEM, et la décapsulation utilise la clé privée de la paire de clés. Cloud KMS vous permet de récupérer la clé publique, que vous pouvez ensuite utiliser avec des bibliothèques standards pour encapsuler votre secret partagé. Pour décapsuler le secret partagé, utilisez les méthodes de décapsulation Cloud KMS. Vous ne pouvez pas utiliser le contenu de la clé privée en dehors de Cloud KMS.

Avant de commencer

  • Ce document fournit des exemples qui s'exécutent sur la ligne de commande. Pour simplifier l'utilisation des exemples, utilisez Cloud Shell. L'exemple de chiffrement utilise OpenSSL, pré-installé sur Cloud Shell. Sinon, installez OpenSSL sur votre ordinateur.
  • Créez une clé KEM avec l'objectif de clé KEY_ENCAPSULATION. Pour savoir quels algorithmes sont compatibles avec l'objectif de clé KEY_ENCAPSULATION, consultez la page Algorithmes d'encapsulation de clé.

Accorder des autorisations sur la clé

  • Attribuez le rôle roles/cloudkms.publicKeyViewer sur la clé à chaque utilisateur ou compte principal qui doit récupérer la clé publique pour encapsuler le secret.
  • Attribuez le rôle roles/cloudkms.decapsulator sur la clé à chaque utilisateur ou compte principal qui doit décapsuler des secrets avec cette clé.

Pour en savoir plus sur les autorisations et les rôles dans Cloud KMS, consultez Autorisations et rôles.

Encapsulation

Pour encapsuler à l'aide d'une clé KEM, récupérez la clé publique et utilisez-la pour encapsuler.

gcloud

Dans cet exemple, OpenSSL doit être installé sur votre système local.

Télécharger la clé publique

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

Remplacez les éléments suivants :

  • KEY_VERSION : numéro de version de la clé que vous souhaitez utiliser pour l'encapsulation, par exemple 2.
  • KEY_NAME : nom de la clé que vous souhaitez utiliser pour l'encapsulation.
  • KEY_RING : nom du trousseau de clés qui inclut la clé
  • LOCATION : emplacement Cloud KMS du trousseau de clés.
  • PUBLIC_KEY_FILE : chemin d'accès local au fichier dans lequel la clé publique sera enregistrée.
  • PUBLIC_KEY_FORMAT : format cible de la clé publique (par exemple, nist-pqc). Le format par défaut est pem.

Reformater la clé publique

La commande "encapsulate" exige que la clé publique soit au format PEM. Si vous avez téléchargé la clé publique dans un autre format, comme nist-pqc, vous devez la convertir au format PEM. Si votre clé publique est déjà au format PEM, passez à Encapsuler.

Utilisez la commande suivante pour convertir la clé publique d'une clé ML-KEM-768 :

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

Utilisez la commande suivante pour convertir la clé publique d'une clé ML-KEM-1024 :

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

Remplacez les éléments suivants :

  • PUBLIC_KEY_FILE : chemin d'accès au fichier de clé publique téléchargé au format brut.
  • PEM_PUBLIC_KEY_FILE : chemin d'accès et nom du fichier dans lequel enregistrer la clé publique au format PEM.

Encapsuler

Pour créer un secret partagé et un texte chiffré, vous pouvez utiliser la commande suivante :

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

Remplacez les éléments suivants :

  • PEM_PUBLIC_KEY_FILE : chemin d'accès au fichier de clé publique téléchargé au format PEM.
  • CIPHERTEXT_FILE : chemin d'accès où vous souhaitez enregistrer le texte chiffré obtenu.
  • SHARED_SECRET_FILE : chemin d'accès où vous souhaitez enregistrer le secret partagé obtenu.

Go

Pour exécuter ce code, commencez par configurer un environnement de développement Go, puis installez le SDK Cloud KMS pour Go.

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
}

Décapsulation

Utilisez Cloud KMS pour décapsuler un texte chiffré.

gcloud

Pour utiliser Cloud KMS sur la ligne de commande, commencez par installer ou mettre à jour 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

Remplacez les éléments suivants :

  • KEY_VERSION : version de la clé à utiliser pour la décapsulation (par exemple, 3).
  • KEY_NAME : nom de la clé à utiliser pour la décapsulation.
  • KEY_RING : nom du trousseau de clés où se trouve la clé.
  • LOCATION : emplacement Cloud KMS du trousseau de clés.
  • CIPHERTEXT_FILE : chemin d'accès local au texte chiffré d'entrée.
  • SHARED_SECRET_FILE : chemin d'accès au fichier local pour enregistrer le secret partagé de sortie.

Go

Pour exécuter ce code, commencez par configurer un environnement de développement Go, puis installez le SDK Cloud KMS pour Go.

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

Ces exemples utilisent curl comme client HTTP pour démontrer l'utilisation de l'API. Pour en savoir plus sur le contrôle des accès, consultez la page Accéder à l'API Cloud KMS.

Exécutez la méthode 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"}'

Remplacez les éléments suivants :

  • PROJECT_ID : ID du projet contenant le trousseau de clés.
  • LOCATION : emplacement Cloud KMS du trousseau de clés.
  • KEY_RING : nom du trousseau de clés qui inclut la clé
  • KEY_NAME : nom de la clé à utiliser pour le chiffrement.
  • KEY_VERSION : ID de la version de clé à utiliser pour le chiffrement.
  • CIPHERTEXT : texte chiffré encodé en base64 que vous souhaitez décapsuler.