디지털 서명 생성 및 검증

이 주제에서는 비대칭 키를 기반으로 한 디지털 서명 생성 및 검증을 설명합니다.

디지털 서명은 비대칭 키의 비공개 키 부분을 사용하여 생성됩니다. 서명은 동일한 비대칭 키의 공개 키 부분을 사용하여 검증됩니다.

시작하기 전에

  • 디지털 서명을 만들 때는 키 용도ASYMMETRIC_SIGN인 키를 사용해야 합니다. 키를 만들 때는 ASYMMETRIC_SIGN을 사용합니다.

  • 서명을 확인하려면 키를 만들 때 사용한 전체 알고리즘을 알아야 합니다. 아래의 openssl 명령어를 사용하는 명령줄 안내의 경우 이 정보를 해당 명령어에 전달해야 합니다.

  • 서명을 수행할 사용자 또는 서비스에 비대칭 키에 대한 cloudkms.cryptoKeyVersions.useToSign 권한을 부여합니다. Cloud Key Management Service의 권한은 권한 및 역할에서 확인할 수 있습니다.

  • 서명을 검증하려면 검증에 사용할 공개 키를 다운로드할 사용자 또는 서비스에 비대칭 키에 대한 cloudkms.cryptoKeyVersions.viewPublicKey 권한을 부여합니다.

  • 명령줄을 사용하려고 하지만 OpenSSL을 아직 설치하지 않았으면 설치합니다. Cloud Shell을 사용하는 경우에는 OpenSSL이 이미 설치되어 있습니다.

서명 만들기

명령줄

명령줄에서 Cloud KMS를 사용하려면 먼저 최신 버전의 Cloud SDK로 설치 또는 업그레이드하세요.

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

key-version을 서명에 사용할 키 버전으로 바꿉니다. key를 키 이름으로 바꿉니다. key-ring을 키가 배치된 키링의 이름으로 바꿉니다. location을 키링의 Cloud KMS 위치로 바꿉니다. digest-algorithm을 사용할 알고리즘으로 바꿉니다. input-filesignature-file을 서명할 파일의 로컬 경로와 서명 파일을 대체합니다.

모든 플래그 및 가능한 값에 대한 정보를 보려면 --help 플래그와 함께 명령어를 실행하세요.

C#

이 코드를 실행하려면 먼저 C# 개발 환경을 설정하고 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

이 코드를 실행하려면 먼저 Go 개발 환경을 설정하고 Cloud KMS Go SDK를 설치합니다.

import (
	"context"
	"crypto/sha256"
	"fmt"
	"hash/crc32"
	"io"

	kms "cloud.google.com/go/kms/apiv1"
	kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
	"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: %v", err)
	}

	// 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.Errorf("failed to create digest: %v", err)
	}

	// Optional but recommended: Compute digest's CRC32C.
	crc32c := func(data []byte) uint32 {
		t := crc32.MakeTable(crc32.Castagnoli)
		return crc32.Checksum(data, t)

	}
	digestCRC32C := crc32c(digest.Sum(nil))

	// Build the signing request.
	//
	// Note: Key algorithms will require a varying hash function. For example,
	// EC_SIGN_P384_SHA384 requires SHA-384.
	req := &kmspb.AsymmetricSignRequest{
		Name: name,
		Digest: &kmspb.Digest{
			Digest: &kmspb.Digest_Sha256{
				Sha256: digest.Sum(nil),
			},
		},
		DigestCrc32C: wrapperspb.Int64(int64(digestCRC32C)),
	}

	// Call the API.
	result, err := client.AsymmetricSign(ctx, req)
	if err != nil {
		return fmt.Errorf("failed to sign digest: %v", err)
	}

	// Optional, but recommended: perform integrity verification on result.
	// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
	// https://cloud.google.com/kms/docs/data-integrity-guidelines
	if result.VerifiedDigestCrc32C == false {
		return fmt.Errorf("AsymmetricSign: request corrupted in-transit")
	}
	// TODO(iamtamjam) Uncomment when this field is populated by the server
	// if result.Name != req.Name {
	//	return fmt.Errorf("AsymmetricSign: request corrupted in-transit")
	// }
	if int64(crc32c(result.Signature)) != result.SignatureCrc32C.Value {
		return fmt.Errorf("AsymmetricSign: response corrupted in-transit")
	}

	fmt.Fprintf(w, "Signed digest: %s", result.Signature)
	return nil
}

자바

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 Cloud KMS 자바 SDK를 설치합니다.

import com.google.cloud.kms.v1.AsymmetricSignResponse;
import com.google.cloud.kms.v1.CryptoKeyVersionName;
import com.google.cloud.kms.v1.Digest;
import com.google.cloud.kms.v1.KeyManagementServiceClient;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;

public class SignAsymmetric {

  public void signAsymmetric() 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 message = "my message";
    signAsymmetric(projectId, locationId, keyRingId, keyId, keyVersionId, message);
  }

  // Get the public key associated with an asymmetric key.
  public void signAsymmetric(
      String projectId,
      String locationId,
      String keyRingId,
      String keyId,
      String keyVersionId,
      String message)
      throws IOException, GeneralSecurityException {
    // 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);

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

      // Calculate the digest.
      MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
      byte[] hash = sha256.digest(plaintext);

      // Build the digest object.
      Digest digest = Digest.newBuilder().setSha256(ByteString.copyFrom(hash)).build();

      // Sign the digest.
      AsymmetricSignResponse result = client.asymmetricSign(keyVersionName, digest);

      // Get the signature.
      byte[] signature = result.getSignature().toByteArray();

      System.out.printf("Signature %s%n", signature);
    }
  }
}

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 message = 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 signAsymmetric() {
  // Create a digest of the message. The digest needs to match the digest
  // configured for the Cloud KMS key.
  const crypto = require('crypto');
  const hash = crypto.createHash('sha256');
  hash.update(message);
  const digest = hash.digest();

  // Optional but recommended: Compute digest's CRC32C.
  const crc32c = require('fast-crc32c');
  const digestCrc32c = crc32c.calculate(digest);

  // Sign the message with Cloud KMS
  const [signResponse] = await client.asymmetricSign({
    name: versionName,
    digest: {
      sha256: digest,
    },
    digestCrc32c: {
      value: digestCrc32c,
    },
  });

  // Optional, but recommended: perform integrity verification on signResponse.
  // For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
  // https://cloud.google.com/kms/docs/data-integrity-guidelines
  if (signResponse.name !== versionName) {
    throw new Error('AsymmetricSign: request corrupted in-transit');
  }
  if (!signResponse.verifiedDigestCrc32c) {
    throw new Error('AsymmetricSign: request corrupted in-transit');
  }
  if (
    crc32c.calculate(signResponse.signature) !==
    Number(signResponse.signatureCrc32c.value)
  ) {
    throw new Error('AsymmetricSign: response corrupted in-transit');
  }

  // 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.signature.toString('base64');
  console.log(`Signature: ${encoded}`);

  return signResponse.signature;
}

return signAsymmetric();

PHP

이 코드를 실행하려면 먼저 Google Cloud에서 PHP 사용에 관해 알아보고 Cloud KMS PHP SDK 설치하세요.

use Google\Cloud\Kms\V1\KeyManagementServiceClient;
use Google\Cloud\Kms\V1\Digest;

function sign_asymmetric_sample(
    string $projectId = 'my-project',
    string $locationId = 'us-east1',
    string $keyRingId = 'my-key-ring',
    string $keyId = 'my-key',
    string $versionId = '123',
    string $message = '...'
) {
    // Create the Cloud KMS client.
    $client = new KeyManagementServiceClient();

    // Build the key version name.
    $keyVersionName = $client->cryptoKeyVersionName($projectId, $locationId, $keyRingId, $keyId, $versionId);

    // Calculate the hash.
    $hash = hash('sha256', $message, true);

    // Build the digest.
    //
    // Note: Key algorithms will require a varying hash function. For
    // example, EC_SIGN_P384_SHA384 requires SHA-384.
    $digest = (new Digest())
        ->setSha256($hash);

    // Call the API.
    $signResponse = $client->asymmetricSign($keyVersionName, $digest);
    printf('Signature: %s' . PHP_EOL, $signResponse->getSignature());
    return $signResponse;
}

Python

이 코드를 실행하려면 먼저 Python 개발 환경을 설정하고 Cloud KMS Python SDK를 설치합니다.

def sign_asymmetric(project_id, location_id, key_ring_id, key_id, version_id, message):
    """
    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').
        message (string): Message to sign.

    Returns:
        AsymmetricSignResponse: Signature.
    """

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

    # Import base64 for printing the ciphertext.
    import base64

    # Import hashlib for calculating hashes.
    import hashlib

    # 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.
    message_bytes = message.encode('utf-8')

    # Calculate the hash.
    hash_ = hashlib.sha256(message_bytes).digest()

    # Build the digest.
    #
    # Note: Key algorithms will require a varying hash function. For
    # example, EC_SIGN_P384_SHA384 requires SHA-384.
    digest = {'sha256': hash_}

    # Optional, but recommended: compute digest's CRC32C.
    # See crc32c() function defined below.
    digest_crc32c = crc32c(hash_)

    # Call the API
    sign_response = client.asymmetric_sign(
        request={'name': key_version_name, 'digest': digest, 'digest_crc32c': digest_crc32c})

    # Optional, but recommended: perform integrity verification on sign_response.
    # For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
    # https://cloud.google.com/kms/docs/data-integrity-guidelines
    if not sign_response.verified_digest_crc32c:
        raise Exception('The request sent to the server was corrupted in-transit.')
    if not sign_response.name == key_version_name:
        raise Exception('The request sent to the server was corrupted in-transit.')
    if not sign_response.signature_crc32c == crc32c(sign_response.signature):
        raise Exception('The response received from the server was corrupted in-transit.')
    # End integrity verification

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

def crc32c(data):
    """
    Calculates the CRC32C checksum of the provided data.
    Args:
        data: the bytes over which the checksum should be calculated.
    Returns:
        An int representing the CRC32C checksum of the provided bytes.
    """
    import crcmod
    import six
    crc32c_fun = crcmod.predefined.mkPredefinedCrcFun('crc-32c')
    return crc32c_fun(six.ensure_binary(data))

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"
# message     = "my message"

# 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

# Calculate the hash.
#
# Note: Key algorithms will require a varying hash function. For
# example, EC_SIGN_P384_SHA384 requires SHA-384.
digest = { sha256: Digest::SHA256.digest(message) }

# Call the API.
sign_response = client.asymmetric_sign name: key_version_name, digest: digest
puts "Signature: #{Base64.strict_encode64 sign_response.signature}"

API

이 예시에서는 curl을 HTTP 클라이언트로 사용하여 API 사용을 보여줍니다. 액세스 제어에 대한 자세한 내용은 Cloud KMS API 액세스를 참조하세요.

CryptoKeys.asymmetricSign 메서드를 사용하여 서명을 수행합니다. 이 메서드의 응답에는 base64 인코딩 서명이 포함됩니다.

타원 곡선 서명 검증

명령줄

명령줄에서 Cloud KMS를 사용하려면 먼저 최신 버전의 Cloud SDK로 설치 또는 업그레이드하세요.

공개 키 가져오기

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

key-version을 키 버전으로 바꿉니다. key를 키 이름으로 바꿉니다. key-ring을 키가 배치된 키링의 이름으로 바꿉니다. location을 키링의 Cloud KMS 위치로 바꿉니다. output-file를 로컬 시스템에 공개 키를 저장할 파일 경로로 바꿉니다.

모든 플래그 및 가능한 값에 대한 정보를 보려면 --help 플래그와 함께 명령어를 실행하세요.

서명 확인

서명을 검증하는 OpenSSL 명령어는 생성된 서명 유형에 따라 달라집니다. 예를 들어 OpenSSL을 사용하여 SHA-256 타원 곡선 서명을 검증하려면 -sha256을 지정해야 합니다. SHA-384 타원 곡선 서명의 유효성을 검사하려면 -sha384를 지정해야 합니다.

openssl dgst \
    -sha256 \
    -verify public-key-file \
    -signature signature-file \
    message-file

변수를 자체 값으로 바꿉니다.

  • public-key-file. 공개 키를 포함하는 파일의 경로입니다(예: "./my-key.pub").

  • signature-file. 확인할 서명을 포함하는 파일의 경로입니다(예: "./my-data.sig").

  • message-file. 메시지를 포함하는 파일의 경로입니다(예: "./my-data.txt").

서명이 유효한 경우 명령은 문자열 Verified OK를 출력합니다.

모든 플래그 및 가능한 값에 대한 정보를 보려면 help 하위 명령어와 함께 명령어를 실행하세요.

C#

이 코드를 실행하려면 먼저 C# 개발 환경을 설정하고 Cloud KMS C# SDK를 설치합니다.


public class VerifyAsymmetricSignatureEcSample
{
    // Cloud KMS returns signatures in a DER-encoded format. .NET requires
    // signatures to be in IEEE 1363 format, and converting between these formats
    // is a few hundred lines of code.
    //
    // https://github.com/dotnet/runtime/pull/1612 exposes these helpers, but will
    // not be available until .NET 5. Until then, you will need to use an external
    // library or package to validate signatures.
}

Go

이 코드를 실행하려면 먼저 Go 개발 환경을 설정하고 Cloud KMS Go SDK를 설치합니다.

import (
	"context"
	"crypto/ecdsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/asn1"
	"encoding/pem"
	"fmt"
	"io"
	"math/big"

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

// verifyAsymmetricSignatureEC will verify that an 'EC_SIGN_P256_SHA256' signature is
// valid for a given message.
func verifyAsymmetricSignatureEC(w io.Writer, name string, message, signature []byte) error {
	// name := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/123"
	// message := "my message"
	// 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)
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: name})
	if err != nil {
		return fmt.Errorf("failed to get public key: %v", err)
	}

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

	// Verify Elliptic Curve signature.
	var parsedSig struct{ R, S *big.Int }
	if _, err = asn1.Unmarshal(signature, &parsedSig); err != nil {
		return fmt.Errorf("asn1.Unmarshal: %v", err)
	}

	digest := sha256.Sum256(message)
	if !ecdsa.Verify(ecKey, digest[:], parsedSig.R, parsedSig.S) {
		return fmt.Errorf("failed to verify signature")
	}
	fmt.Fprintf(w, "Verified signature!")
	return nil
}

자바

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 Cloud KMS 자바 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.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;

public class VerifyAsymmetricEc {

  public void verifyAsymmetricEc() 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 message = "my message";
    byte[] signature = null;
    verifyAsymmetricEc(projectId, locationId, keyRingId, keyId, keyVersionId, message, signature);
  }

  // Verify the signature of a message signed with an RSA key.
  public void verifyAsymmetricEc(
      String projectId,
      String locationId,
      String keyRingId,
      String keyId,
      String keyVersionId,
      String message,
      byte[] signature)
      throws IOException, GeneralSecurityException {
    // 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 name from the project, location, and key ring, key, and key version.
      CryptoKeyVersionName keyVersionName =
          CryptoKeyVersionName.of(projectId, locationId, keyRingId, keyId, keyVersionId);

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

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

      // Convert the public PEM key to a DER key (see helper below).
      byte[] derKey = convertPemToDer(publicKey.getPem());
      X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
      java.security.PublicKey ecKey = KeyFactory.getInstance("EC").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 ecVerify = Signature.getInstance("SHA256withECDSA");
      ecVerify.initVerify(ecKey);
      ecVerify.update(plaintext);

      // Verify the signature.
      boolean verified = ecVerify.verify(signature);
      System.out.printf("Signature verified: %s", verified);
    }
  }

  // Converts a base64-encoded PEM certificate like the one returned from Cloud
  // KMS into a DER formatted certificate for use with the Java APIs.
  private byte[] convertPemToDer(String pem) {
    BufferedReader bufferedReader = new BufferedReader(new StringReader(pem));
    String encoded =
        bufferedReader
            .lines()
            .filter(line -> !line.startsWith("-----BEGIN") && !line.startsWith("-----END"))
            .collect(Collectors.joining());
    return Base64.getDecoder().decode(encoded);
  }
}

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 = '1';
// const message = 'my message to verify';
// const signatureBuffer = Buffer.from('...');

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

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

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

async function verifyAsymmetricSignatureEc() {
  // Get public key
  const [publicKey] = await client.getPublicKey({
    name: versionName,
  });

  // Create the verifier. The algorithm must match the algorithm of the key.
  const crypto = require('crypto');
  const verify = crypto.createVerify('sha256');
  verify.update(message);
  verify.end();

  // Build the key object
  const key = {
    key: publicKey.pem,
  };

  // Verify the signature using the public key
  const verified = verify.verify(key, signatureBuffer);
  return verified;
}

return verifyAsymmetricSignatureEc();

PHP

이 코드를 실행하려면 먼저 Google Cloud에서 PHP 사용에 관해 알아보고 Cloud KMS PHP SDK 설치하세요.

use Google\Cloud\Kms\V1\KeyManagementServiceClient;

function verify_asymmetric_ec_sample(
    string $projectId = 'my-project',
    string $locationId = 'us-east1',
    string $keyRingId = 'my-key-ring',
    string $keyId = 'my-key',
    string $versionId = '123',
    string $message = '...',
    string $signature = '...'
) {
    // Create the Cloud KMS client.
    $client = new KeyManagementServiceClient();

    // Build the key version name.
    $keyVersionName = $client->cryptoKeyVersionName($projectId, $locationId, $keyRingId, $keyId, $versionId);

    // Get the public key.
    $publicKey = $client->getPublicKey($keyVersionName);

    // Verify the signature. The hash algorithm must correspond to the key
    // algorithm. The openssl_verify command returns 1 on success, 0 on falure.
    $verified = openssl_verify($message, $signature, $publicKey->getPem(), OPENSSL_ALGO_SHA256) === 1;
    printf('Signature verified: %s', $verified);
    return $verified;
}

Python

이 코드를 실행하려면 먼저 Python 개발 환경을 설정하고 Cloud KMS Python SDK를 설치합니다.

def verify_asymmetric_ec(project_id, location_id, key_ring_id, key_id, version_id, message, signature):
    """
    Verify the signature of an message signed with an asymmetric EC 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): ID of the version to use (e.g. '1').
        message (string): Original message (e.g. 'my message')
        signature (bytes): Signature from a sign request.

    Returns:
        bool: True if verified, False otherwise

    """

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

    # Import cryptographic helpers from the cryptography package.
    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import ec, utils

    # Import hashlib.
    import hashlib

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

    # 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)

    # Get the public key.
    public_key = client.get_public_key(request={'name': key_version_name})

    # Extract and parse the public key as a PEM-encoded RSA key.
    pem = public_key.pem.encode('utf-8')
    ec_key = serialization.load_pem_public_key(pem, default_backend())
    hash_ = hashlib.sha256(message_bytes).digest()

    # Attempt to verify.
    try:
        sha256 = hashes.SHA256()
        ec_key.verify(signature, hash_, ec.ECDSA(utils.Prehashed(sha256)))
        print('Signature verified')
        return True
    except InvalidSignature:
        print('Signature failed to verify')
        return False

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"
# message     = "my message"
# signature   = "..."

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

# 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

# Get the public key.
public_key = client.get_public_key name: key_version_name

# Parse the public key.
ec_key = OpenSSL::PKey::EC.new public_key.pem

# Verify the signature.
verified = ec_key.verify "sha256", signature, message
puts "Verified: #{verified}"

API

이 예시에서는 curl을 HTTP 클라이언트로 사용하여 API 사용을 보여줍니다. 액세스 제어에 대한 자세한 내용은 Cloud KMS API 액세스를 참조하세요.

CryptoKeyVersions.getPublicKey 메서드를 사용하여 공개 키를 검색한 후 명령줄 예시에 있는 명령어를 사용하여 서명을 검증합니다.

RSA 서명 검증

명령줄

명령줄에서 Cloud KMS를 사용하려면 먼저 최신 버전의 Cloud SDK로 설치 또는 업그레이드하세요.

공개 키 가져오기

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

key-version을 키 버전으로 바꿉니다. key를 키 이름으로 바꿉니다. key-ring을 키가 배치된 키링의 이름으로 바꿉니다. location을 키링의 Cloud KMS 위치로 바꿉니다. output-file를 로컬 시스템에 공개 키를 저장할 경로로 바꿉니다.

모든 플래그 및 가능한 값에 대한 정보를 보려면 --help 플래그와 함께 명령어를 실행하세요.

서명 확인

서명을 검증하는 OpenSSL 명령어는 생성된 서명 유형에 따라 달라집니다. 예를 들어 PSS 패딩으로 SHA-256 RSA 서명을 검증하려면 -sha256-sigopt rsa_padding_mode:pss를 지정해야 합니다. PSS 패딩으로 SHA-512 RSA 서명의 유효성을 검사하려면 -sha512-sigopt rsa_padding_mode:pss를 지정해야 합니다.

openssl dgst \
    -sha256 \
    -sigopt rsa_padding_mode:pss \
    -sigopt rsa_pss_saltlen:-1 \
    -verify public-key-file \
    -signature signature-file \
    message-file

변수를 자체 값으로 바꿉니다.

  • public-key-file. 공개 키를 포함하는 파일의 경로입니다(예: "./my-key.pub").

  • signature-file. 확인할 서명을 포함하는 파일의 경로입니다(예: "./my-data.sig").

  • message-file. 메시지를 포함하는 파일의 경로입니다(예: "./my-data.txt").

서명이 유효한 경우 명령은 문자열 Verified OK를 출력합니다.

모든 플래그 및 가능한 값에 대한 정보를 보려면 help 하위 명령어와 함께 명령어를 실행하세요.

C#

이 코드를 실행하려면 먼저 C# 개발 환경을 설정하고 Cloud KMS C# SDK를 설치합니다.


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

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

        // Calculate the digest of the message.
        SHA256 sha256 = SHA256.Create();
        byte[] digest = sha256.ComputeHash(Encoding.UTF8.GetBytes(message));

        // Get the public key.
        KeyManagementServiceClient client = KeyManagementServiceClient.Create();
        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 _);

        // Verify the signature.
        bool verified = rsa.VerifyHash(digest, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);

        // Return the result.
        return verified;
    }
}

Go

이 코드를 실행하려면 먼저 Go 개발 환경을 설정하고 Cloud KMS Go SDK를 설치합니다.

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

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

// verifyAsymmetricSignatureRSA will verify that an 'RSA_SIGN_PSS_2048_SHA256' signature
// is valid for a given message.
func verifyAsymmetricSignatureRSA(w io.Writer, name string, message, signature []byte) error {
	// name := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/123"
	// message := "my message"
	// 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)
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: name})
	if err != nil {
		return fmt.Errorf("failed to get public key: %v", 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: %v", err)
	}
	rsaKey, ok := publicKey.(*rsa.PublicKey)
	if !ok {
		return fmt.Errorf("public key is not rsa")
	}

	// Verify the RSA signature.
	digest := sha256.Sum256(message)
	if err := rsa.VerifyPSS(rsaKey, crypto.SHA256, digest[:], signature, &rsa.PSSOptions{
		SaltLength: len(digest),
		Hash:       crypto.SHA256,
	}); err != nil {
		return fmt.Errorf("failed to verify signature: %v", err)
	}

	fmt.Fprint(w, "Verified signature!\n")
	return nil
}

자바

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 Cloud KMS 자바 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.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;

public class VerifyAsymmetricRsa {

  public void verifyAsymmetricRsa() 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 message = "my message";
    byte[] signature = null;
    verifyAsymmetricRsa(projectId, locationId, keyRingId, keyId, keyVersionId, message, signature);
  }

  // Verify the signature of a message signed with an RSA key.
  public void verifyAsymmetricRsa(
      String projectId,
      String locationId,
      String keyRingId,
      String keyId,
      String keyVersionId,
      String message,
      byte[] signature)
      throws IOException, GeneralSecurityException {
    // 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 name from the project, location, and key ring, key, and key version.
      CryptoKeyVersionName keyVersionName =
          CryptoKeyVersionName.of(projectId, locationId, keyRingId, keyId, keyVersionId);

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

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

      // Convert the public PEM key to a DER key (see helper below).
      byte[] derKey = convertPemToDer(publicKey.getPem());
      X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
      java.security.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(plaintext);

      // Verify the signature.
      boolean verified = rsaVerify.verify(signature);
      System.out.printf("Signature verified: %s", verified);
    }
  }

  // Converts a base64-encoded PEM certificate like the one returned from Cloud
  // KMS into a DER formatted certificate for use with the Java APIs.
  private byte[] convertPemToDer(String pem) {
    BufferedReader bufferedReader = new BufferedReader(new StringReader(pem));
    String encoded =
        bufferedReader
            .lines()
            .filter(line -> !line.startsWith("-----BEGIN") && !line.startsWith("-----END"))
            .collect(Collectors.joining());
    return Base64.getDecoder().decode(encoded);
  }
}

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 = '1';
// const message = 'my message to verify';
// const signatureBuffer = Buffer.from('...');

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

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

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

async function verifyAsymmetricSignatureRsa() {
  // Get public key
  const [publicKey] = await client.getPublicKey({
    name: versionName,
  });

  // Create the verifier. The algorithm must match the algorithm of the key.
  const crypto = require('crypto');
  const verify = crypto.createVerify('sha256');
  verify.update(message);
  verify.end();

  // Build the key object
  const key = {
    key: publicKey.pem,
    padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
  };

  // Verify the signature using the public key
  const verified = verify.verify(key, signatureBuffer);
  return verified;
}

return verifyAsymmetricSignatureRsa();

PHP

이 코드를 실행하려면 먼저 Google Cloud에서 PHP 사용에 관해 알아보고 Cloud KMS PHP SDK 설치하세요.

function verify_asymmetric_rsa_sample(
    string $projectId = 'my-project',
    string $locationId = 'us-east1',
    string $keyRingId = 'my-key-ring',
    string $keyId = 'my-key',
    string $versionId = '123',
    string $message = '...',
    string $signature = '...'
) {
    // PHP has limited support for asymmetric encryption operations.
    // Specifically, openssl_public_encrypt() does not allow customizing
    // algorithms or padding. Thus, it is not currently possible to use PHP
    // core for asymmetric operations on RSA keys.
    //
    // Third party libraries like phpseclib may provide the required
    // functionality. Google does not endorse this external library.
}

Python

이 코드를 실행하려면 먼저 Python 개발 환경을 설정하고 Cloud KMS Python SDK를 설치합니다.

def verify_asymmetric_rsa(project_id, location_id, key_ring_id, key_id, version_id, message, signature):
    """
    Verify the signature of an message signed with an asymmetric RSA 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): ID of the version to use (e.g. '1').
        message (string): Original message (e.g. 'my message')
        signature (bytes): Signature from a sign request.

    Returns:
        bool: True if verified, False otherwise

    """

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

    # Import cryptographic helpers from the cryptography package.
    from cryptography.exceptions import InvalidSignature
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import padding, utils

    # Import hashlib.
    import hashlib

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

    # 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)

    # Get the public key.
    public_key = client.get_public_key(request={'name': key_version_name})

    # Extract and parse the public key as a PEM-encoded RSA key.
    pem = public_key.pem.encode('utf-8')
    rsa_key = serialization.load_pem_public_key(pem, default_backend())
    hash_ = hashlib.sha256(message_bytes).digest()

    # Attempt to verify.
    try:
        sha256 = hashes.SHA256()
        pad = padding.PKCS1v15()
        rsa_key.verify(signature, hash_, pad, utils.Prehashed(sha256))
        print('Signature verified')
        return True
    except InvalidSignature:
        print('Signature failed to verify')
        return False

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"
# message     = "my message"
# signature   = "..."

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

# 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

# Get the public key.
public_key = client.get_public_key name: key_version_name

# Parse the public key.
rsa_key = OpenSSL::PKey::RSA.new public_key.pem

# Verify the signature.
#
# Note: The verify_pss() method only exists in Ruby 2.5+.
verified = rsa_key.verify_pss "sha256", signature, message, salt_length: :digest, mgf1_hash: "sha256"
puts "Verified: #{verified}"

API

이 예시에서는 curl을 HTTP 클라이언트로 사용하여 API 사용을 보여줍니다. 액세스 제어에 대한 자세한 내용은 Cloud KMS API 액세스를 참조하세요.

CryptoKeyVersions.getPublicKey 메서드를 사용하여 공개 키를 검색한 후 명령줄 예시에 있는 명령어를 사용하여 서명을 검증합니다.