创建和验证 MAC 数字签名

本主题介绍如何基于 MAC 密钥创建和验证数字签名。

生产者和验证者共享一个密钥,用于计算输入数据的 MAC 标记(即签名)。验证者收到消息及其关联的 MAC 标记后,会为其内容生成自己的标记,并比较这些标记以查看它们是否匹配。

准备工作

  • 创建 MAC 数字签名时,您必须使用密钥用途MAC 的密钥。在创建密钥时,请使用 MAC

  • 将 MAC 密钥的 cloudkms.cryptoKeyVersions.useToSign 权限授予将执行签名的用户或服务。您可以参阅权限和角色,了解 Cloud Key Management Service 中的权限。

  • 如果您要验证签名,请将 MAC 密钥的 cloudkms.cryptoKeyVersions.useToVerify 权限授予将执行验证的用户或服务。

创建 MAC 签名

gcloud

要在命令行上使用 Cloud KMS,请先安装或升级到最新版本的 Cloud SDK

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

key-version 替换为用于签名的密钥的版本。将 key 替换为密钥名称。将 key-ring 替换为密钥所在的密钥环的名称。将 location 替换为密钥环的 Cloud KMS 位置。将 input-filesignature-file 分别替换为要签名的文件和签名文件的本地路径。

如需了解所有标志和可能值,请使用 --help 标志运行命令。

C#

要运行此代码,请先设置 C# 开发环境安装 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

要运行此代码,请先设置 Go 开发环境安装 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

要运行此代码,请先设置 Java 开发环境安装 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

要运行此代码,请先设置 Node.js 开发环境安装 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

要运行此代码,请先设置 Python 开发环境安装 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

要运行此代码,请先设置 Ruby 开发环境安装 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

这些示例使用 curl 作为 HTTP 客户端来演示如何使用 API。如需详细了解访问权限控制,请参阅访问 Cloud KMS API

使用 CryptoKeyVersions.macSign 方法执行签名。此方法返回的响应包含 base64 编码的签名。

验证 MAC 签名

gcloud

要在命令行上使用 Cloud KMS,请先安装或升级到最新版本的 Cloud SDK

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

key-version 替换为要用于验证的密钥的版本。将 key 替换为密钥名称。将 key-ring 替换为密钥所在的密钥环的名称。将 location 替换为密钥环的 Cloud KMS 位置。将 input-filesignature-file 分别替换为要验证的输入数据和签名文件的本地路径。

如需了解所有标志和可能值,请使用 --help 标志运行命令。

C#

要运行此代码,请先设置 C# 开发环境安装 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

要运行此代码,请先设置 Go 开发环境安装 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

要运行此代码,请先设置 Java 开发环境安装 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

要运行此代码,请先设置 Node.js 开发环境安装 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

要运行此代码,请先设置 Python 开发环境安装 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

要运行此代码,请先设置 Ruby 开发环境安装 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

这些示例使用 curl 作为 HTTP 客户端来演示如何使用 API。如需详细了解访问权限控制,请参阅访问 Cloud KMS API

使用 CryptoKeyVersions.macVerify 方法执行验证。要验证的签名应采用 base64 编码。此方法返回的响应包含布尔值,指示签名是否验证成功。