Encapsule e desencapsule com KEMs

Este documento descreve a utilização de mecanismos de encapsulamento de chaves (KEMs) com chaves do Cloud KMS para estabelecer segredos partilhados.

A encapsulação usa a chave pública do par de chaves KEM e a desencapsulação usa a chave privada do par de chaves. O Cloud KMS permite-lhe obter a chave pública, que pode usar com bibliotecas padrão para encapsular o seu segredo partilhado. Para desencapsular o segredo partilhado, use os métodos de desencapsulamento do Cloud KMS. Não pode usar o material da chave privada fora do Cloud KMS.

Antes de começar

  • Este documento fornece exemplos que são executados na linha de comandos. Para simplificar a utilização dos exemplos, use a Cloud Shell. O exemplo de encriptação usa o OpenSSL, que está pré-instalado no Cloud Shell. Caso contrário, instale o OpenSSL no seu computador.
  • Crie uma chave KEM com o objetivo da chave de KEY_ENCAPSULATION. Para ver que algoritmos são suportados para a finalidade da chave KEY_ENCAPSULATION, consulte os algoritmos de encapsulamento de chaves.

Conceda autorizações na chave

Para mais informações sobre autorizações e funções no Cloud KMS, consulte o artigo Autorizações e funções.

Encapsulamento

Para encapsular através de uma chave KEM, obtenha a chave pública e use-a para encapsular.

gcloud

Este exemplo requer que o OpenSSL esteja instalado no seu sistema local.

Transfira a chave pública

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

Substitua o seguinte:

  • KEY_VERSION: o número da versão da chave que quer usar para a encapsulagem, por exemplo, 2.
  • KEY_NAME: o nome da chave que quer usar para a encapsulagem.
  • KEY_RING: o nome do conjunto de chaves que contém a chave.
  • LOCATION: a localização do Cloud KMS do conjunto de chaves.
  • PUBLIC_KEY_FILE: o caminho do ficheiro local onde a chave pública vai ser guardada.
  • PUBLIC_KEY_FORMAT: O formato de destino da chave pública, por exemplo, nist-pqc. O formato predefinido é pem.

Reformate a chave pública

O comando de encapsulamento requer que a chave pública esteja no formato PEM. Se tiver transferido a chave pública noutro formato, como nist-pqc, tem de converter a chave para o formato PEM. Se a sua chave pública já estiver no formato PEM, continue a partir de Encapsular.

Use o seguinte comando para converter a chave pública de uma chave ML-KEM-768:

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

Use o seguinte comando para converter a chave pública de uma chave ML-KEM-1024:

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

Substitua o seguinte:

  • PUBLIC_KEY_FILE: o caminho para o ficheiro de chave pública transferido no formato não processado.
  • PEM_PUBLIC_KEY_FILE: o caminho e o nome do ficheiro para guardar a chave pública no formato PEM.

Encapsular

Para criar um segredo partilhado e um texto cifrado, pode usar o seguinte comando:

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

Substitua o seguinte:

  • PEM_PUBLIC_KEY_FILE: o caminho para o ficheiro de chave pública transferido no formato PEM.
  • CIPHERTEXT_FILE: O caminho onde quer guardar o texto cifrado resultante.
  • SHARED_SECRET_FILE: o caminho onde quer guardar o segredo partilhado resultante.

Go

Para executar este código, primeiro configure um ambiente de desenvolvimento Go e instale o SDK Go do Cloud KMS.

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
}

Decapsulação

Use o Cloud KMS para desencapsular um texto cifrado.

gcloud

Para usar o Cloud KMS na linha de comandos, primeiro instale ou atualize para a versão mais recente da CLI do Google Cloud.

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

Substitua o seguinte:

  • KEY_VERSION: a versão da chave a usar para a desencapsulagem, por exemplo, 3.
  • KEY_NAME: o nome da chave a usar para a desencapsulagem.
  • KEY_RING: o nome do conjunto de chaves onde a chave se encontra.
  • LOCATION: a localização do Cloud KMS para o conjunto de chaves.
  • CIPHERTEXT_FILE: o caminho do ficheiro local para o texto cifrado de entrada.
  • SHARED_SECRET_FILE: o caminho do ficheiro local para guardar o símbolo secreto partilhado de saída.

Go

Para executar este código, primeiro configure um ambiente de desenvolvimento Go e instale o SDK Go do Cloud KMS.

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

Estes exemplos usam o curl como cliente HTTP para demonstrar a utilização da API. Para mais informações sobre o controlo de acesso, consulte o artigo Aceder à API Cloud KMS.

Use o método 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"}'

Substitua o seguinte:

  • PROJECT_ID: o ID do projeto que contém o conjunto de chaves.
  • LOCATION: a localização do Cloud KMS do conjunto de chaves.
  • KEY_RING: o nome do conjunto de chaves que contém a chave.
  • KEY_NAME: o nome da chave a usar para a encriptação.
  • KEY_VERSION: o ID da versão da chave a usar para a encriptação
  • CIPHERTEXT: o texto cifrado codificado em base64 que quer desencapsular.