Creating and validating digital signatures

This topic provides information about creating and validating digital signatures based on asymmetric keys.

A digital signature is created using the private key portion of an asymmetric key. The signature is validated using the public key portion of the same asymmetric key.

Before you begin

  • When creating digital signatures, you must use a key that has the key purpose of ASYMMETRIC_SIGN. When you create the key, use ASYMMETRIC_SIGN.

  • To validate a signature, you need to know the full algorithm that was used when creating the key. For command-line instructions below that use the openssl command, you need to pass this information to those commands.

  • Grant the cloudkms.cryptoKeyVersions.useToSign permission on the asymmetric key to the user or service that will perform the signing. You can learn about permissions in Cloud Key Management Service at Permissions and roles.

  • If you are going to validate a signature, grant cloudkms.cryptoKeyVersions.viewPublicKey permission on the asymmetric key to the user or service that will download the public key to use for validation.

  • If you are going to use the command line, install OpenSSL if you do not already have it. If you use Cloud Shell, OpenSSL is already installed.

Data versus digest

The input provided for AsymmetricSign requests can be passed through the data field or the digest field. These fields cannot be both specified at the same time. There are some algorithms that require the data field, such as raw algorithms and signing with a Cloud External Key Manager key.

Raw algorithms

"Raw" algorithms, identified by the RSA_SIGN_RAW_ prefix, are a variant of PKCS #1 signing that omits encoding into a DigestInfo. In the variant:

  • A digest is computed over the message that will be signed.
  • PKCS #1 padding is applied to the digest directly.
  • A signature of the padded digest is computed, using the RSA private key.

To use these algorithms:

  • The raw data needs to be provided (instead of a digest) as part of the data field.
  • The data has a length limit of 11 bytes fewer than the RSA key size. For example, PKCS #1 with a 2048-bit RSA key can sign at most 245 bytes.
  • Grant the cloudkms.expertRawPKCS1 role to the appropriate user or service. You can learn about permissions in Cloud Key Management Service at Permissions and roles.

By using raw algorithms, you can also sign a digest type for which a predefined algorithm is not available. For example, you can use an RSA_SIGN_RAW_2048 key to sign a SHA-512 PKCS #1 DigestInfo structure that you already computed externally. This process creates the same results as a standard RSA_SIGN_PKCS1_2048_SHA512 algorithm.

ECDSA support for other hash algorithms

Our ECDSA signing algorithms have the general format:

EC_SIGN_ELLIPTIC_CURVE_[DIGEST_ALGORITHM]

DIGEST_ALGORITHM has the value SHA256, SHA384, or SHA512. Because the hash is performed before you create the signature, these signing algorithms can also be used with digests other than SHA, such as Keccak. To use a Keccak digest, provide a Keccak hash value and use the SHA digest algorithm with the same length. For example, you can use a KECCAK256 digest in a request with the EC_SIGN_P256_SHA256 algorithm.

Creating a signature

gcloud

To use Cloud KMS on the command line, first Install or upgrade to the latest version of Google Cloud CLI.

gcloud kms asymmetric-sign \
    --version key-version \
    --key key \
    --keyring key-ring \
    --location location \
    --digest-algorithm digest-algorithm \
    --input-file input-file \
    --signature-file signature-file

Replace key-version with the version of the key to to use for signing. Replace key with the key name. Replace key-ring with the name of the key ring where the key is located. Replace location with the Cloud KMS location the key ring. Replace digest-algorithm with the algorithm to use. Omit digest-algorithm to send input-file to Cloud KMS to sign. Replace input-file and signature-file with the local paths for the file to sign and the signature file.

For information on all flags and possible values, run the command with the --help flag.

C#

To run this code, first set up a C# development environment and install the Cloud KMS C# SDK.


using Google.Cloud.Kms.V1;
using Google.Protobuf;
using System.Security.Cryptography;
using System.Text;

public class SignAsymmetricSample
{
    public byte[] SignAsymmetric(
      string projectId = "my-project", string locationId = "us-east1", string keyRingId = "my-key-ring", string keyId = "my-key", string keyVersionId = "123",
      string message = "Sample message")
    {
        // Create the client.
        KeyManagementServiceClient client = KeyManagementServiceClient.Create();

        // Build the key version name.
        CryptoKeyVersionName keyVersionName = new CryptoKeyVersionName(projectId, locationId, keyRingId, keyId, keyVersionId);

        // Convert the message into bytes. Cryptographic plaintexts and
        // ciphertexts are always byte arrays.
        byte[] plaintext = Encoding.UTF8.GetBytes(message);

        // Calculate the digest.
        SHA256 sha256 = SHA256.Create();
        byte[] hash = sha256.ComputeHash(plaintext);

        // Build the digest.
        //
        // Note: Key algorithms will require a varying hash function. For
        // example, EC_SIGN_P384_SHA384 requires SHA-384.
        Digest digest = new Digest
        {
            Sha256 = ByteString.CopyFrom(hash),
        };

        // Call the API.
        AsymmetricSignResponse result = client.AsymmetricSign(keyVersionName, digest);

        // Get the signature.
        byte[] signature = result.Signature.ToByteArray();

        // Return the result.
        return signature;
    }
}

Go

To run this code, first set up a Go development environment and install the Cloud KMS Go SDK.

import (
	"context"
	"crypto/sha256"
	"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"
)

// signAsymmetric will sign a plaintext message using a saved asymmetric private
// key stored in Cloud KMS.
func signAsymmetric(w io.Writer, name string, message string) error {
	// name := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/123"
	// message := "my message"

	// 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()

	// Convert the message into bytes. Cryptographic plaintexts and
	// ciphertexts are always byte arrays.
	plaintext := []byte(message)

	// Calculate the digest of the message.
	digest := sha256.New()
	if _, err := digest.Write(plaintext); err != nil {
		return fmt