Creating and validating MAC digital signatures

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

A single key, shared by both the producer and the verifier, is used to compute a MAC tag (i.e. the signature) over the input data. When the verifier receives the message and its associated MAC tag, they generate their own tag for its contents, and compare the tags to see if they match.

Before you begin

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

  • Grant the cloudkms.cryptoKeyVersions.useToSign permission on the MAC 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.useToVerify permission on the MAC key to the user or service that will perform the verification.

Creating a MAC signature

gcloud

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

gcloud kms mac-sign \
    --version key-version \
    --key key \
    --keyring key-ring \
    --location location \
    --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 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;

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

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

        // Convert the data into a ByteString.
        ByteString dataByteString = ByteString.CopyFromUtf8(data);

        // Call the API.
        MacSignResponse result = client.MacSign(keyVersionName, dataByteString);

        // The data comes back as raw bytes, which may include non-printable
        // characters. To print the result, you could encode it as base64.
        // string encodedSignature = result.Mac.ToBase64();

        // Get the signature.
        byte[] signature = result.Mac.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"
	"encoding/base64"
	"fmt"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

// signMac will sign a plaintext message using the HMAC algorithm.
func signMac(w io.Writer, name string, data string) error {
	// name := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/123"
	// data := "my data to sign"

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %v", err)
	}
	defer client.Close()

	// Build the request.
	req := &kmspb.MacSignRequest{
		Name: name,
		Data: []byte(data),
	}

	// Generate HMAC of data.
	result, err := client.MacSign(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to hmac sign: %v", err)
	}

	// The data comes back as raw bytes, which may include non-printable
	// characters. This base64-encodes the result so it can be printed below.
	encodedSignature := base64.StdEncoding.EncodeToString(result.Mac)

	fmt.Fprintf(w, "Signature: %s", encodedSignature)
	return nil
}

Java

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

import com.google.cloud.kms.v1.CryptoKeyVersionName;
import com.google.cloud.kms.v1.KeyManagementServiceClient;
import com.google.cloud.kms.v1.MacSignResponse;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.Base64;

public class SignMac {

  public void signMac() throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String locationId = "us-east1";
    String keyRingId = "my-key-ring";
    String keyId = "my-key";
    String keyVersionId = "123";
    String data = "Data to sign";
    signMac(projectId, locationId, keyRingId, keyId, keyVersionId, data);
  }

  // Sign data with a given mac key.
  public void signMac(
      String projectId,
      String locationId,
      String keyRingId,
      String keyId,
      String keyVersionId,
      String data)
      throws IOException {
    // Initialize client that will be used to send requests. This client only
    // needs to be created once, and can be reused for multiple requests. After
    // completing all of your requests, call the "close" method on the client to
    // safely clean up any remaining background resources.
    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
      // Build the key version name from the project, location, key ring, key,
      // and key version.
      CryptoKeyVersionName keyVersionName =
          CryptoKeyVersionName.of(projectId, locationId, keyRingId, keyId, keyVersionId);

      // Generate an HMAC of the data.
      MacSignResponse response = client.macSign(keyVersionName, ByteString.copyFromUtf8(data));

      // The data comes back as raw bytes, which may include non-printable
      // characters. This base64-encodes the result so it can be printed below.
      String encodedSignature = Base64.getEncoder().encodeToString(response.getMac().toByteArray());
      System.out.printf("Signature: %s%n", encodedSignature);
    }
  }
}

Node.js

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

//
// TODO(developer): Uncomment these variables before running the sample.
//
// const projectId = 'your-project-id';
// const locationId = 'us-east1';
// const keyRingId = 'my-key-ring';
// const keyId = 'my-key';
// const versionId = '123';
// const data = Buffer.from('...');

// Imports the Cloud KMS library
const {KeyManagementServiceClient} = require('@google-cloud/kms');

// Instantiates a client
const client = new KeyManagementServiceClient();

// Build the version name
const versionName = client.cryptoKeyVersionPath(
  projectId,
  locationId,
  keyRingId,
  keyId,
  versionId
);

async function signMac() {
  // Sign the data with Cloud KMS
  const [signResponse] = await client.macSign({
    name: versionName,
    data: data,
  });

  // Example of how to display signature. Because the signature is in a binary
  // format, you need to encode the output before printing it to a console or
  // displaying it on a screen.
  const encoded = signResponse.mac.toString('base64');
  console.log(`Signature: ${encoded}`);

  return signResponse;
}

return signMac();

Python

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

def sign_mac(project_id, location_id, key_ring_id, key_id, version_id, data):
    """
    Sign a message using the public key part of an asymmetric key.

    Args:
        project_id (string): Google Cloud project ID (e.g. 'my-project').
        location_id (string): Cloud KMS location (e.g. 'us-east1').
        key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring').
        key_id (string): ID of the key to use (e.g. 'my-key').
        version_id (string): Version to use (e.g. '1').
        data (string): Data to sign.

    Returns:
        MacSignResponse: Signature.
    """

    # Import the client library.
    from google.cloud import kms

    # Import base64 for printing the ciphertext.
    import base64

    # Create the client.
    client = kms.KeyManagementServiceClient()

    # Build the key version name.
    key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id)

    # Convert the message to bytes.
    data_bytes = data.encode('utf-8')

    # Call the API
    sign_response = client.mac_sign(
        request={'name': key_version_name, 'data': data_bytes})

    print('Signature: {}'.format(base64.b64encode(sign_response.mac)))
    return sign_response

Ruby

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

# TODO(developer): uncomment these values before running the sample.
# project_id  = "my-project"
# location_id = "us-east1"
# key_ring_id = "my-key-ring"
# key_id      = "my-key"
# version_id  = "123"
# data        = "my data"

# Require the library.
require "google/cloud/kms"

# Require digest.
require "digest"

# Create the client.
client = Google::Cloud::Kms.key_management_service

# Build the key version name.
key_version_name = client.crypto_key_version_path project:            project_id,
                                                  location:           location_id,
                                                  key_ring:           key_ring_id,
                                                  crypto_key:         key_id,
                                                  crypto_key_version: version_id

# Call the API.
sign_response = client.mac_sign name: key_version_name, data: data

# The data comes back as raw bytes, which may include non-printable
# characters. This base64-encodes the result so it can be printed below.
encoded_signature = Base64.strict_encode64 sign_response.mac

puts "Signature: #{encoded_signature}"

API

These examples use curl as an HTTP client to demonstrate using the API. For more information about access control, see Accessing the Cloud KMS API.

Use the CryptoKeyVersions.macSign method to perform the signing. The response from this method contains the base64-encoded signature.

Verifying a MAC signature

gcloud

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

gcloud kms mac-verify \
    --version key-version \
    --key key \
    --keyring key-ring \
    --location location \
    --input-file input-file \
    --signature-file signature-file

Replace key-version with the version of the key to to use for verification. 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 input-file and signature-file with the local paths for the input data and the signature file to be verified.

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;

public class VerifyMacSample
{
    public bool VerifyMac(
      string projectId = "my-project", string locationId = "us-east1", string keyRingId = "my-key-ring", string keyId = "my-key", string keyVersionId = "123",
      string data = "my data",
      byte[] signature = null)
    {
        // Build the key version name.
        CryptoKeyVersionName keyVersionName = new CryptoKeyVersionName(projectId, locationId, keyRingId, keyId, keyVersionId);

        // Create the client.
        KeyManagementServiceClient client = KeyManagementServiceClient.Create();

        // Convert the data and signatures into ByteStrings.
        ByteString dataByteString = ByteString.CopyFromUtf8(data);
        ByteString signatureByteString = ByteString.CopyFrom(signature);

        // Verify the signature.
        MacVerifyResponse result = client.MacVerify(keyVersionName, dataByteString, signatureByteString);

        // Return the result.
        return result.Success;
    }
}

Go

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

import (
	"context"
	"fmt"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)

// verifyMac will verify a previous HMAC signature.
func verifyMac(w io.Writer, name string, data, signature []byte) error {
	// name := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/123"
	// data := "my previous data"
	// signature := []byte("...")  // Response from a sign request

	// Create the client.
	ctx := context.Background()
	client, err := kms.NewKeyManagementClient(ctx)
	if err != nil {
		return fmt.Errorf("failed to create kms client: %v", err)
	}
	defer client.Close()

	// Build the request.
	req := &kmspb.MacVerifyRequest{
		Name: name,
		Data: data,
		Mac:  signature,
	}

	// Verify the signature.
	result, err := client.MacVerify(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to verify signature: %v", err)
	}

	fmt.Fprintf(w, "Verified: %t", result.Success)
	return nil
}

Java

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

import com.google.cloud.kms.v1.CryptoKeyVersionName;
import com.google.cloud.kms.v1.KeyManagementServiceClient;
import com.google.cloud.kms.v1.MacVerifyResponse;
import com.google.protobuf.ByteString;
import java.io.IOException;

public class VerifyMac {

  public void verifyMac() throws IOException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String locationId = "us-east1";
    String keyRingId = "my-key-ring";
    String keyId = "my-key";
    String keyVersionId = "123";
    String data = "Data to sign";
    byte[] signature = null;
    verifyMac(projectId, locationId, keyRingId, keyId, keyVersionId, data, signature);
  }

  // Sign data with a given mac key.
  public void verifyMac(
      String projectId,
      String locationId,
      String keyRingId,
      String keyId,
      String keyVersionId,
      String data,
      byte[] signature)
      throws IOException {
    // Initialize client that will be used to send requests. This client only
    // needs to be created once, and can be reused for multiple requests. After
    // completing all of your requests, call the "close" method on the client to
    // safely clean up any remaining background resources.
    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
      // Build the key version name from the project, location, key ring, key,
      // and key version.
      CryptoKeyVersionName keyVersionName =
          CryptoKeyVersionName.of(projectId, locationId, keyRingId, keyId, keyVersionId);

      // Verify the signature
      MacVerifyResponse response =
          client.macVerify(
              keyVersionName, ByteString.copyFromUtf8(data), ByteString.copyFrom(signature));

      // The data comes back as raw bytes, which may include non-printable
      // characters. This base64-encodes the result so it can be printed below.
      System.out.printf("Success: %s%n", response.getSuccess());
    }
  }
}

Node.js

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

//
// TODO(developer): Uncomment these variables before running the sample.
//
// const projectId = 'your-project-id';
// const locationId = 'us-east1';
// const keyRingId = 'my-key-ring';
// const keyId = 'my-key';
// const versionId = '123';
// const data = Buffer.from('...');
// const signature = Buffer.from('...');

// Imports the Cloud KMS library
const {KeyManagementServiceClient} = require('@google-cloud/kms');

// Instantiates a client
const client = new KeyManagementServiceClient();

// Build the version name
const versionName = client.cryptoKeyVersionPath(
  projectId,
  locationId,
  keyRingId,
  keyId,
  versionId
);

async function verifyMac() {
  // Verify the data with Cloud KMS
  const [verifyResponse] = await client.macVerify({
    name: versionName,
    data: data,
    mac: signature,
  });

  console.log(`Verified: ${verifyResponse.success}`);
  return verifyResponse;
}

return verifyMac();

Python

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

def verify_mac(project_id, location_id, key_ring_id, key_id, version_id, data, signature):
    """
    Verify the signature of data from an HMAC key.

    Args:
        project_id (string): Google Cloud project ID (e.g. 'my-project').
        location_id (string): Cloud KMS location (e.g. 'us-east1').
        key_ring_id (string): ID of the Cloud KMS key ring (e.g. 'my-key-ring').
        key_id (string): ID of the key to use (e.g. 'my-key').
        version_id (string): Version to use (e.g. '1').
        data (string): Data that was signed.
        signature (bytes): Signature bytes.

    Returns:
        MacVerifyResponse: Success.
    """

    # Import the client library.
    from google.cloud import kms

    # Create the client.
    client = kms.KeyManagementServiceClient()

    # Build the key version name.
    key_version_name = client.crypto_key_version_path(project_id, location_id, key_ring_id, key_id, version_id)

    # Convert the message to bytes.
    data_bytes = data.encode('utf-8')

    # Call the API
    verify_response = client.mac_verify(
        request={'name': key_version_name, 'data': data_bytes, 'mac': signature})

    print('Verified: {}'.format(verify_response.success))
    return verify_response

Ruby

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

# TODO(developer): uncomment these values before running the sample.
# project_id  = "my-project"
# location_id = "us-east1"
# key_ring_id = "my-key-ring"
# key_id      = "my-key"
# version_id  = "123"
# data        = "my data"
# signature   = "..."

# Require the library.
require "google/cloud/kms"

# Require digest.
require "digest"

# Create the client.
client = Google::Cloud::Kms.key_management_service

# Build the key version name.
key_version_name = client.crypto_key_version_path project:            project_id,
                                                  location:           location_id,
                                                  key_ring:           key_ring_id,
                                                  crypto_key:         key_id,
                                                  crypto_key_version: version_id

# Call the API.
verify_response = client.mac_verify name: key_version_name, data: data, mac: signature
puts "Verified: #{verify_response.success}"

API

These examples use curl as an HTTP client to demonstrate using the API. For more information about access control, see Accessing the Cloud KMS API.

Use the CryptoKeyVersions.macVerify method to perform the verification. The signature to be verified needs to be base64-encoded. The response from this method contains a boolean indicating whether or not the signature was verified successfully.