使用非对称密钥加密和解密数据

本主题介绍了如何使用 RSA 密钥创建和使用非对称加密密钥。如果要使用非对称密钥创建和验证签名,请参阅创建和验证数字签名。如果要使用对称密钥进行加密和解密,请参阅加密和解密数据

非对称加密使用非对称密钥的公钥部分,非对称解密使用该密钥的私钥部分。Cloud Key Management Service 提供检索公钥以及对使用该公钥加密的密文进行解密的功能。Cloud KMS 不允许直接对私钥进行访问。

准备工作

  • 本主题提供在命令行运行的示例。要简化示例的使用,请使用 Cloud Shell。加密示例使用预先安装在 Cloud Shell 上的 OpenSSL

  • 创建一个非对称密钥密钥用途ASYMMETRIC_DECRYPT)。 如需查看密钥用途 ASYMMETRIC_DECRYPT 支持哪些算法,请参阅非对称加密算法您不能使用用途为 ASYMMETRIC_SIGN 的密钥执行此程序。

  • 如果您要使用命令行,请安装 OpenSSL(如果尚未安装)。如果您使用 Cloud Shell,则 OpenSSL 已安装。

  • macOS 用户: 在 macOS 上安装的 OpenSSL 版本不支持用于 解密数据的标志。要在 macOS 上按照以下步骤操作, 请从 Homebrew 安装 OpenSSL。

对密钥的访问权限控制

  • 对于将检索公钥的用户或服务,向非对称密钥授予 cloudkms.cryptoKeyVersions.viewPublicKey 权限。对数据进行加密时需要该公钥。

  • 对于将对使用公钥加密的数据进行解密的用户或服务,向非对称密钥授予 cloudkms.cryptoKeyVersions.useToDecrypt 权限。

如需了解 Cloud KMS 中的权限和角色,请参阅权限和角色

加密数据

要使用非对称加密密钥加密数据,请检索公钥并使用公钥加密数据。

gcloud

此示例要求在本地系统上安装 OpenSSL

下载公钥

下载公钥:

gcloud kms keys versions get-public-key key-version \
    --key key \
    --keyring key-ring \
    --location location  \
    --output-file public-key-path

key-version 替换为具有公钥的密钥版本。将 key 替换为密钥的名称。将 key-ring 替换为密钥所在的密钥环的名称。将 location 替换为密钥环的 Cloud KMS 位置。将 public-key-path 替换为要在本地系统上保存公钥的位置。

加密数据

使用刚才下载的公钥加密数据,并将输出保存到文件中:

openssl pkeyutl -in cleartext-data-input-file \
    -encrypt \
    -pubin \
    -inkey public-key-path \
    -pkeyopt rsa_padding_mode:oaep \
    -pkeyopt rsa_oaep_md:sha256 \
    -pkeyopt rsa_mgf1_md:sha256 \
    > encrypted-data-output-file
  • cleartext-data-input-file 替换为要加密的路径和文件名。

  • public-key-path 替换为下载公钥的路径和文件名。

  • encrypted-data-output-file 替换为用于保存加密数据的路径和文件名 。

C#

要运行此代码,请先设置 C# 开发环境安装 Cloud KMS C# SDK


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

public class EncryptAsymmetricSample
{
    public byte[] EncryptAsymmetric(
      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);

        // Get the public key.
        PublicKey publicKey = client.GetPublicKey(keyVersionName);

        // Split the key into blocks and base64-decode the PEM parts.
        string[] blocks = publicKey.Pem.Split("-", StringSplitOptions.RemoveEmptyEntries);
        byte[] pem = Convert.FromBase64String(blocks[1]);

        // Create a new RSA key.
        RSA rsa = RSA.Create();
        rsa.ImportSubjectPublicKeyInfo(pem, out _);

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

        // Encrypt the data.
        byte[] ciphertext = rsa.Encrypt(plaintext, RSAEncryptionPadding.OaepSHA256);
        return ciphertext;
    }
}

Go

如需在命令行上使用 Cloud KMS,请先 安装或升级到最新版本的 Google Cloud CLI

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	"cloud.google.com/go/kms/apiv1/kmspb"
)

// encryptAsymmetric encrypts data on your local machine using an
// 'RSA_DECRYPT_OAEP_2048_SHA256' public key retrieved from Cloud KMS.
func encryptAsymmetric(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 := "Sample 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()

	// Retrieve the public key from Cloud KMS. This is the only operation that
	// involves Cloud KMS. The remaining operations take place on your local
	// machine.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{
		Name: name,
	})
	if err != nil {
		return fmt.Errorf("failed to get public key: %w", err)
	}

	// Parse the public key. Note, this example assumes the public key is in the
	// RSA format.
	block, _ := pem.Decode([]byte(response.Pem))
	publicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse public key: %w", err)
	}
	rsaKey, ok := publicKey.(*rsa.PublicKey)
	if !ok {
		return fmt.Errorf("public key is not rsa")
	}

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

	// Encrypt data using the RSA public key.
	ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsaKey, plaintext, nil)
	if err != nil {
		return fmt.Errorf("rsa.EncryptOAEP: %w", err)
	}
	fmt.Fprintf(w, "Encrypted ciphertext: %s", ciphertext)
	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.PublicKey;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;

public class EncryptAsymmetric {

  public void encryptAsymmetric() throws IOException, GeneralSecurityException {
    // 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 plaintext = "Plaintext to encrypt";
    encryptAsymmetric(projectId, locationId, keyRingId, keyId, keyVersionId,