Como criar e validar assinaturas digitais

Neste tópico, fornecemos informações sobre como criar e validar assinaturas digitais com base em chaves assimétricas.

Uma assinatura digital é criada usando a parte da chave privada de uma chave assimétrica. A assinatura é validada usando a parte da chave pública da mesma chave assimétrica.

Antes de começar

  • Para criar assinaturas digitais, é necessária uma chave com a finalidade apropriada. Ao criar a chave, use ASYMMETRIC_SIGN. Anote o algoritmo completo especificado pela chave, porque será necessário enviar essas informações ao comando do OpenSSL para validar a assinatura.

  • Conceda a permissão cloudkms.cryptoKeyVersions.useToSign na chave assimétrica ao usuário ou serviço que executará a assinatura. É possível aprender sobre permissões no Cloud KMS em Permissões e funções.

  • Se você validar uma assinatura, conceda a permissão cloudkms.cryptoKeyVersions.viewPublicKey na chave assimétrica ao usuário ou serviço que fará o download da chave pública que será usada na validação.

  • Para usar a linha de comando, instale o OpenSSL, se ainda não o tiver. Se você usa o Cloud Shell, o OpenSSL já está instalado.

Como criar uma assinatura

Linha de comando

Apenas como exemplo, crie um arquivo chamado ~/my-message-file com um texto de amostra.

echo "my message to sign" > ~/my-message-file

Para assinar dados:

gcloud alpha kms asymmetric-sign \
--location [LOCATION] \
--keyring [KEY_RING] \
--key [KEY] \
--version [VERSION] \
--digest-algorithm sha256 \
--input-file ~/my-message-file \
--signature-file ~/my-signature-file

[KEY] precisa ser uma chave com a finalidade ASYMMETRIC_SIGN.

O arquivo de saída ~/my-signature-file contém a assinatura binária.

API

Use o método CryptoKeys.asymmetricSign para executar a assinatura. A resposta desse método contém a assinatura codificada em base64.

Go

// signAsymmetric will sign a plaintext message using a saved asymmetric private key.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func signAsymmetric(keyName string, message []byte) ([]byte, error) {
	// Note: some key algorithms will require a different hash function.
	// For example, EC_SIGN_P384_SHA384 requires SHA-384.
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Find the digest of the message.
	digest := sha256.New()
	digest.Write(message)
	// Build the signing request.
	req := &kmspb.AsymmetricSignRequest{
		Name: keyName,
		Digest: &kmspb.Digest{
			Digest: &kmspb.Digest_Sha256{
				Sha256: digest.Sum(nil),
			},
		},
	}
	// Call the API.
	response, err := client.AsymmetricSign(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("asymmetric sign request failed: %+v", err)
	}
	return response.Signature, nil
}

Java

/**
 *  Create a signature for a message using a private key stored on Cloud KMS
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static byte[] signAsymmetric(String keyName, byte[] message)
    throws IOException, NoSuchAlgorithmException {
  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {

    // Note: some key algorithms will require a different hash function
    // For example, EC_SIGN_P384_SHA384 requires SHA-384
    byte[] messageHash = MessageDigest.getInstance("SHA-256").digest(message);

    AsymmetricSignRequest request = AsymmetricSignRequest.newBuilder()
        .setName(keyName)
        .setDigest(Digest.newBuilder().setSha256(ByteString.copyFrom(messageHash)))
        .build();

    AsymmetricSignResponse response = client.asymmetricSign(request);
    return response.getSignature().toByteArray();
  }
}

Python

def sign_asymmetric(message, key_name):
    """
    Create a signature for a message using a private key stored on Cloud KMS

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      hashlib
    """
    # Note: some key algorithms will require a different hash function
    # For example, EC_SIGN_P384_SHA384 requires SHA384
    client = kms_v1.KeyManagementServiceClient()
    digest_bytes = hashlib.sha256(message).digest()

    digest_json = {'sha256': digest_bytes}

    response = client.asymmetric_sign(key_name, digest_json)
    return response.signature

Como validar uma assinatura de curva elíptica

Linha de comando

Se você ainda não tiver, recupere a chave pública que foi usada para criar a assinatura.

Neste exemplo, supomos que a chave pública esteja em um arquivo chamado signing_key.pub e a mensagem que foi assinada esteja em um arquivo denominado my-message-file.

Os comandos do OpenSSL para validar a assinatura dependem do tipo de assinatura criado. Por exemplo, para validar uma assinatura de curva elíptica SHA-256 usando o OpenSSL:

openssl dgst -sha256 \
-verify ~/signing_key.pub \
-signature ~/my-signature-file ~/my-message-file

Se a assinatura for válida, você verá:

Verified OK

API

Use o método CryptoKeyVersions.getPublicKey para recuperar a chave pública e use os comandos mostrados para o exemplo da linha de comando para validar a assinatura.

Go

// verifySignatureEC will verify that an 'EC_SIGN_P256_SHA256' signature is valid for a given message.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func verifySignatureEC(keyName string, signature, message []byte) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyName})
	if err != nil {
		return fmt.Errorf("failed to fetch public key: %+v", err)
	}
	// Parse the key.
	block, _ := pem.Decode([]byte(response.Pem))
	abstractKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse public key: %+v", err)
	}
	ecKey, ok := abstractKey.(*ecdsa.PublicKey)
	if !ok {
		return fmt.Errorf("key '%s' is not EC", keyName)
	}
	// Verify Elliptic Curve signature.
	var parsedSig struct{ R, S *big.Int }
	_, err = asn1.Unmarshal(signature, &parsedSig)
	if err != nil {
		return fmt.Errorf("failed to parse signature bytes: %+v", err)
	}
	hash := sha256.New()
	hash.Write(message)
	digest := hash.Sum(nil)
	if !ecdsa.Verify(ecKey, digest, parsedSig.R, parsedSig.S) {
		return errors.New("signature verification failed")
	}
	return nil
}

Java

/**
 * Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the
 * specified message
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static boolean verifySignatureEC(String keyName, byte[] message, byte[] signature)
    throws IOException, GeneralSecurityException {

  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // Get the public key
    com.google.cloud.kms.v1.PublicKey pub = client.getPublicKey(keyName);
    String pemKey = pub.getPem();
    pemKey = pemKey.replaceFirst("-----BEGIN PUBLIC KEY-----", "");
    pemKey = pemKey.replaceFirst("-----END PUBLIC KEY-----", "");
    pemKey = pemKey.replaceAll("\\s", "");
    byte[] derKey = BaseEncoding.base64().decode(pemKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
    PublicKey ecKey = KeyFactory.getInstance("EC").generatePublic(keySpec);

    // Verify the 'EC_SIGN_P256_SHA256' signature
    // For other key algorithms:
    // http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature
    Signature ecVerify = Signature.getInstance("SHA256withECDSA");
    ecVerify.initVerify(ecKey);
    ecVerify.update(message);
    return ecVerify.verify(signature);
  }
}

Python

def verify_signature_ec(signature, message, key_name):
    """
    Verify the validity of an 'EC_SIGN_P256_SHA256' signature
    for the specified message

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      cryptography.exceptions.InvalidSignature
      cryptography.hazmat.primitives.asymmetric.ec
      cryptography.hazmat.primitives.asymmetric.utils
      cryptography.hazmat.primitives.hashes
      hashlib
    """
    # get the public key
    client = kms_v1.KeyManagementServiceClient()
    response = client.get_public_key(key_name)
    key_txt = response.pem.encode('ascii')
    public_key = serialization.load_pem_public_key(key_txt, default_backend())

    # get the digest of the message
    digest_bytes = hashlib.sha256(message).digest()

    try:
        # Attempt verification
        public_key.verify(signature,
                          digest_bytes,
                          ec.ECDSA(utils.Prehashed(hashes.SHA256())))
        # No errors were thrown. Verification was successful
        return True
    except InvalidSignature:
        return False

Como validar uma assinatura RSA

Linha de comando

Se você ainda não tiver, recupere a chave pública que foi usada para criar a assinatura.

Neste exemplo, supomos que a chave pública esteja em um arquivo chamado signing_key.pub e a mensagem que foi assinada esteja em um arquivo denominado my-message-file.

Os comandos do OpenSSL para validar a assinatura dependem do tipo de assinatura criado. Para validar uma assinatura RSA SHA-256 com preenchimento PSS usando o OpenSSL:

openssl dgst -sha256 \
-sigopt rsa_padding_mode:pss \
-sigopt rsa_pss_saltlen:-1 \
-signature ~/my-signature-file \
-verify ~/signing_key.pub ~/my-message-file

Se a assinatura for válida, você verá:

Verified OK

API

Use o método CryptoKeyVersions.getPublicKey para recuperar a chave pública e use os comandos mostrados para o exemplo da linha de comando para validar a assinatura.

Go

// verifySignatureRSA will verify that an 'RSA_SIGN_PSS_2048_SHA256' signature is valid for a given message.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func verifySignatureRSA(keyName string, signature, message []byte) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyName})
	if err != nil {
		return fmt.Errorf("failed to fetch public key: %+v", err)
	}
	// Parse the key.
	block, _ := pem.Decode([]byte(response.Pem))
	abstractKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse public key: %+v", err)
	}
	rsaKey, ok := abstractKey.(*rsa.PublicKey)
	if !ok {
		return fmt.Errorf("key '%s' is not RSA", keyName)
	}
	// Verify RSA signature.
	hash := sha256.New()
	hash.Write(message)
	digest := hash.Sum(nil)
	pssOptions := rsa.PSSOptions{SaltLength: len(digest), Hash: crypto.SHA256}
	err = rsa.VerifyPSS(rsaKey, crypto.SHA256, digest, signature, &pssOptions)
	if err != nil {
		return fmt.Errorf("signature verification failed: %+v", err)
	}
	return nil
}

Java

/**
 * Verify the validity of an 'RSA_SIGN_PKCS1_2048_SHA256' signature for the
 * specified message
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static boolean verifySignatureRSA(String keyName, byte[] message, byte[] signature)
    throws IOException, GeneralSecurityException {

  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // Get the public key
    com.google.cloud.kms.v1.PublicKey pub = client.getPublicKey(keyName);
    String pemKey = pub.getPem();
    pemKey = pemKey.replaceFirst("-----BEGIN PUBLIC KEY-----", "");
    pemKey = pemKey.replaceFirst("-----END PUBLIC KEY-----", "");
    pemKey = pemKey.replaceAll("\\s", "");
    byte[] derKey = BaseEncoding.base64().decode(pemKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
    PublicKey rsaKey = KeyFactory.getInstance("RSA").generatePublic(keySpec);

    // Verify the 'RSA_SIGN_PKCS1_2048_SHA256' signature.
    // For other key algorithms:
    // http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Signature
    Signature rsaVerify = Signature.getInstance("SHA256withRSA");
    rsaVerify.initVerify(rsaKey);
    rsaVerify.update(message);
    return rsaVerify.verify(signature);
  }
}

Python

def verify_signature_rsa(signature, message, key_name):
    """
    Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the
    specified message

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      cryptography.exceptions.InvalidSignature
      cryptography.hazmat.primitives.asymmetric.padding
      cryptography.hazmat.primitives.asymmetric.utils
      cryptography.hazmat.primitives.hashes
      hashlib
    """
    # get the public key
    client = kms_v1.KeyManagementServiceClient()
    response = client.get_public_key(key_name)
    key_txt = response.pem.encode('ascii')
    public_key = serialization.load_pem_public_key(key_txt, default_backend())

    # get the digest of the message
    digest_bytes = hashlib.sha256(message).digest()

    try:
        # Attempt verification
        public_key.verify(signature,
                          digest_bytes,
                          padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                                      salt_length=32),
                          utils.Prehashed(hashes.SHA256()))
        # No errors were thrown. Verification was successful
        return True
    except InvalidSignature:
        return False

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Cloud KMS