디지털 서명 생성 및 검증

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

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

시작하기 전에

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

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

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

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

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

데이터와 다이제스트 비교

AsymmetricSign 요청에 제공된 입력은 data 필드 또는 digest 필드를 통해 전달될 수 있습니다. 두 필드를 동시에 지정할 수는 없습니다. 원시 알고리즘Cloud 외부 키 관리자 키로 서명하는 것과 같이 데이터 필드가 필요한 몇 가지 알고리즘이 있습니다.

원시 알고리즘

RSA_SIGN_RAW_ 프리픽스로 식별되는 '원시' 알고리즘은 DigestInfo로의 인코딩을 생략하는 PKCS #1 서명의 변형입니다. 이 변형은 다음과 같은 특징이 있습니다.

  • 다이제스트는 서명될 메시지를 대상으로 계산됩니다.
  • PKCS #1 패딩은 다이제스트에 직접 적용됩니다.
  • 패딩된 다이제스트의 서명은 RSA 비공개 키를 사용하여 계산됩니다.

이러한 알고리즘을 사용하려면 다음 안내를 따르세요.

  • data 필드의 일부로 다이제스트 대신 원시 데이터를 제공해야 합니다.
  • 데이터의 길이 한도는 RSA 키 크기보다 11바이트 적습니다. 예를 들어, 2048비트 RSA 키가 있는 PKCS #1은 최대 245바이트에 서명할 수 있습니다.
  • cloudkms.expertRawPKCS1 역할을 적절한 사용자 또는 서비스에 부여합니다. Cloud Key Management Service의 권한은 권한 및 역할에서 확인할 수 있습니다.

원시 알고리즘을 사용하면 사전 정의된 알고리즘을 사용할 수 없는 다이제스트 유형에 서명할 수도 있습니다. 예를 들어 RSA_SIGN_RAW_2048 키를 사용하여 이미 외부에서 계산한 SHA-512 PKCS #1 DigestInfo 구조에 서명할 수 있습니다. 이 프로세스는 표준 RSA_SIGN_PKCS1_2048_SHA512 알고리즘과 동일한 결과를 생성합니다.

기타 해시 알고리즘에 대한 ECDSA 지원

ECDSA 서명 알고리즘은 일반적인 형식을 가집니다.

 EC_SIGN_ELLIPTIC_CURVE_[DIGEST_ALGORITHM] 

DIGEST_ALGORITHM의 값이 SHA256, SHA384 또는 SHA512입니다. 해시는 서명을 만들기 전에 실행되므로 이러한 서명 알고리즘은 Keccak과 같은 SHA 이외의 다이제스트와도 사용할 수 있습니다. Keccak 다이제스트를 사용하려면 Keccak 해시 값을 제공하고 동일한 길이의 SHA 다이제스트 알고리즘을 사용합니다. 예를 들어 EC_SIGN_P256_SHA256 알고리즘을 사용하는 요청에서 KECCAK256 다이제스트를 사용할 수 있습니다.

서명 만들기

gcloud

명령줄에서 Cloud KMS를 사용하려면 먼저 최신 버전의 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 

key-version을 서명에 사용할 키 버전으로 바꿉니다. key를 키 이름으로 바꿉니다. key-ring을 키가 배치된 키링의 이름으로 바꿉니다. location을 키링의 Cloud KMS 위치로 바꿉니다. digest-algorithm을 사용할 알고리즘으로 바꿉니다. digest-algorithm을 생략하여 input-file을 서명할 Cloud KMS로 보냅니다. 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" 	"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.Errorf("failed to create digest: %w", 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: %w", 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") 	} 	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 } 

Java

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 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.   // Ensure fast-crc32c has been installed, `npm i fast-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\AsymmetricSignRequest; use Google\Cloud\Kms\V1\Client\KeyManagementServiceClient; use Google\Cloud\Kms\V1\Digest;  function sign_asymmetric(     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.     $asymmetricSignRequest = (new AsymmetricSignRequest())         ->setName($keyVersionName)         ->setDigest($digest);     $signResponse = $client->asymmetricSign($asymmetricSignRequest);     printf('Signature: %s' . PHP_EOL, $signResponse->getSignature());      return $signResponse; }

Python

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

# Import base64 for printing the ciphertext. import base64  # Import hashlib for calculating hashes. import hashlib  # Import the client library. from google.cloud import kms   def sign_asymmetric(     project_id: str,     location_id: str,     key_ring_id: str,     key_id: str,     version_id: str,     message: str, ) -> kms.AsymmetricSignResponse:     """     Sign a message using the private 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.     """      # 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(f"Signature: {base64.b64encode(sign_response.signature)!r}")     return sign_response   def crc32c(data: bytes) -> int:     """     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  # type: ignore      crc32c_fun = crcmod.predefined.mkPredefinedCrcFun("crc-32c")     return crc32c_fun(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 액세스를 참조하세요.

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

타원 곡선 서명 검증

gcloud

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

공개 키 가져오기

 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" 	"cloud.google.com/go/kms/apiv1/kmspb" )  // 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: %w", err) 	} 	defer client.Close()  	// 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: %w", 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: %w", err) 	} 	ecKey, ok := publicKey.(*ecdsa.PublicKey) 	if !ok { 		return fmt.Errorf("public key is not elliptic curve") 	}  	// Verify Elliptic Curve signature. 	var parsedSig struct{ R, S *big.Int } 	if _, err = asn1.Unmarshal(signature, &parsedSig); err != nil { 		return fmt.Errorf("asn1.Unmarshal: %w", 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 } 

Java

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 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\Client\KeyManagementServiceClient; use Google\Cloud\Kms\V1\GetPublicKeyRequest;  function verify_asymmetric_ec(     string $projectId = 'my-project',     string $locationId = 'us-east1',     string $keyRingId = 'my-key-ring',     string $keyId = 'my-key',     string $versionId = '123',     string $message = '...',     string $signature = '...' ): bool {     // 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.     $getPublicKeyRequest = (new GetPublicKeyRequest())         ->setName($keyVersionName);     $publicKey = $client->getPublicKey($getPublicKeyRequest);      // 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를 설치합니다.

# Import hashlib. import hashlib  # 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 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import utils  # Import the client library. from google.cloud import kms   def verify_asymmetric_ec(     project_id: str,     location_id: str,     key_ring_id: str,     key_id: str,     version_id: str,     message: str,     signature: str, ) -> bool:     """     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      """      # 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 EC 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 서명 검증

gcloud

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

공개 키 가져오기

 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" 	"cloud.google.com/go/kms/apiv1/kmspb" )  // 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: %w", err) 	} 	defer client.Close()  	// 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: %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") 	}  	// 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: %w", err) 	}  	fmt.Fprint(w, "Verified signature!\n") 	return nil } 

Java

이 코드를 실행하려면 먼저 자바 개발 환경을 설정하고 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(     string $projectId = 'my-project',     string $locationId = 'us-east1',     string $keyRingId = 'my-key-ring',     string $keyId = 'my-key',     string $versionId = '123',     string $message = '...',     string $signature = '...' ): void {     // 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를 설치합니다.

 # Import hashlib. import hashlib  # 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 from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives.asymmetric import utils  # Import the client library. from google.cloud import kms   def verify_asymmetric_rsa(     project_id: str,     location_id: str,     key_ring_id: str,     key_id: str,     version_id: str,     message: str,     signature: str, ) -> bool:     """     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      """      # 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 메서드를 사용하여 공개 키를 검색한 후 명령줄 예시에 있는 명령어를 사용하여 서명을 검증합니다.