デジタル署名の作成と検証

このトピックでは、非対称鍵に基づくデジタル署名の作成と検証について説明します。

デジタル署名は、非対称鍵の秘密鍵部分を使用して作成します。署名の検証は、同じ非対称鍵の公開鍵部分を使用して行います。

始める前に

  • デジタル署名を作成するには、鍵の目的を適切に設定した鍵が必要です。鍵の作成には ASYMMETRIC_SIGN を使用します。署名検証時にアルゴリズムの情報を OpenSSL コマンドに渡す必要があるため、鍵で指定されたアルゴリズム全体をメモします。

  • 署名を行うユーザーまたはサービスに、非対称鍵に対する cloudkms.cryptoKeyVersions.useToSign 権限を付与します。Cloud KMS における権限の詳細については、権限と役割をご覧ください。

  • 署名を検証する場合は、検証に使用する公開鍵をダウンロードするユーザーまたはサービスに、非対称鍵に対する cloudkms.cryptoKeyVersions.viewPublicKey 権限を付与します。

  • コマンドラインを使用する場合、OpenSSL をまだインストールしていなければ、ここでインストールしてください。Cloud Shell を使用する場合、OpenSSL はすでにインストールされています。

署名の作成

コマンドライン

たとえば、サンプル テキストを含む ~/my-message-file というファイルを作成します。

echo "my message to sign" > ~/my-message-file

データに署名するには、次のように指定します。

gcloud alpha kms asymmetric-sign \
--location [LOCATION] \
--keyring [KEY_RING] \
--key [KEY] \
--version [VERSION] \
--digest-algorithm sha256 \
--input-file ~/my-message-file \
--signature-file ~/my-signature-file

[KEY] には、目的が ASYMMETRIC_SIGN である鍵を指定する必要があります。

出力ファイル ~/my-signature-file にバイナリ署名が含まれています。

API

CryptoKeys.asymmetricSign メソッドを使用して署名を行います。このメソッドからのレスポンスに、base64 エンコードの署名が含まれています。

Go

// signAsymmetric will sign a plaintext message using a saved asymmetric private key.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func signAsymmetric(keyName string, message []byte) ([]byte, error) {
	// Note: some key algorithms will require a different hash function.
	// For example, EC_SIGN_P384_SHA384 requires SHA-384.
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return nil, err
	}
	// Find the digest of the message.
	digest := sha256.New()
	digest.Write(message)
	// Build the signing request.
	req := &kmspb.AsymmetricSignRequest{
		Name: keyName,
		Digest: &kmspb.Digest{
			Digest: &kmspb.Digest_Sha256{
				Sha256: digest.Sum(nil),
			},
		},
	}
	// Call the API.
	response, err := client.AsymmetricSign(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("asymmetric sign request failed: %+v", err)
	}
	return response.Signature, nil
}

Java

/**
 *  Create a signature for a message using a private key stored on Cloud KMS
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static byte[] signAsymmetric(String keyName, byte[] message)
    throws IOException, NoSuchAlgorithmException {
  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {

    // Note: some key algorithms will require a different hash function
    // For example, EC_SIGN_P384_SHA384 requires SHA-384
    byte[] messageHash = MessageDigest.getInstance("SHA-256").digest(message);

    AsymmetricSignRequest request = AsymmetricSignRequest.newBuilder()
        .setName(keyName)
        .setDigest(Digest.newBuilder().setSha256(ByteString.copyFrom(messageHash)))
        .build();

    AsymmetricSignResponse response = client.asymmetricSign(request);
    return response.getSignature().toByteArray();
  }
}

Python

def sign_asymmetric(message, key_name):
    """
    Create a signature for a message using a private key stored on Cloud KMS

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      hashlib
    """
    # Note: some key algorithms will require a different hash function
    # For example, EC_SIGN_P384_SHA384 requires SHA384
    client = kms_v1.KeyManagementServiceClient()
    digest_bytes = hashlib.sha256(message).digest()

    digest_json = {'sha256': digest_bytes}

    response = client.asymmetric_sign(key_name, digest_json)
    return response.signature

楕円曲線署名の検証

コマンドライン

署名の作成に使用した公開鍵を取得していない場合は、ここで取得してください。

この例では、公開鍵が signing_key.pub というファイルにあり、署名されたメッセージが my-message-file というファイルにあるものと想定しています。

署名を検証する OpenSSL コマンドは、作成された署名の種類によって異なります。たとえば、OpenSSL を使用して SHA-256 楕円曲線署名を検証する場合は、次のようになります。

openssl dgst -sha256 \
-verify ~/signing_key.pub \
-signature ~/my-signature-file ~/my-message-file

署名が有効であれば、次のように表示されます。

Verified OK

API

CryptoKeyVersions.getPublicKey メソッドを使用して公開鍵を取得し、さらにコマンドライン用の例を使用して署名を検証します。

Go

// verifySignatureEC will verify that an 'EC_SIGN_P256_SHA256' signature is valid for a given message.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func verifySignatureEC(keyName string, signature, message []byte) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyName})
	if err != nil {
		return fmt.Errorf("failed to fetch public key: %+v", err)
	}
	// Parse the key.
	block, _ := pem.Decode([]byte(response.Pem))
	abstractKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse public key: %+v", err)
	}
	ecKey, ok := abstractKey.(*ecdsa.PublicKey)
	if !ok {
		return fmt.Errorf("key '%s' is not EC", keyName)
	}
	// Verify Elliptic Curve signature.
	var parsedSig struct{ R, S *big.Int }
	_, err = asn1.Unmarshal(signature, &parsedSig)
	if err != nil {
		return fmt.Errorf("failed to parse signature bytes: %+v", err)
	}
	hash := sha256.New()
	hash.Write(message)
	digest := hash.Sum(nil)
	if !ecdsa.Verify(ecKey, digest, parsedSig.R, parsedSig.S) {
		return errors.New("signature verification failed")
	}
	return nil
}

Java

/**
 * Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the
 * specified message
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static boolean verifySignatureEC(String keyName, byte[] message, byte[] signature)
    throws IOException, GeneralSecurityException {

  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // Get the public key
    com.google.cloud.kms.v1.PublicKey pub = client.getPublicKey(keyName);
    String pemKey = pub.getPem();
    pemKey = pemKey.replaceFirst("-----BEGIN PUBLIC KEY-----", "");
    pemKey = pemKey.replaceFirst("-----END PUBLIC KEY-----", "");
    pemKey = pemKey.replaceAll("\\s", "");
    byte[] derKey = BaseEncoding.base64().decode(pemKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
    PublicKey ecKey = KeyFactory.getInstance("EC").generatePublic(keySpec);

    // Verify the 'EC_SIGN_P256_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(message);
    return ecVerify.verify(signature);
  }
}

Python

def verify_signature_ec(signature, message, key_name):
    """
    Verify the validity of an 'EC_SIGN_P256_SHA256' signature
    for the specified message

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      cryptography.exceptions.InvalidSignature
      cryptography.hazmat.primitives.asymmetric.ec
      cryptography.hazmat.primitives.asymmetric.utils
      cryptography.hazmat.primitives.hashes
      hashlib
    """
    # get the public key
    client = kms_v1.KeyManagementServiceClient()
    response = client.get_public_key(key_name)
    key_txt = response.pem.encode('ascii')
    public_key = serialization.load_pem_public_key(key_txt, default_backend())

    # get the digest of the message
    digest_bytes = hashlib.sha256(message).digest()

    try:
        # Attempt verification
        public_key.verify(signature,
                          digest_bytes,
                          ec.ECDSA(utils.Prehashed(hashes.SHA256())))
        # No errors were thrown. Verification was successful
        return True
    except InvalidSignature:
        return False

RSA 署名の検証

コマンドライン

署名の作成に使用した公開鍵を取得していない場合は、ここで取得してください。

この例では、公開鍵が signing_key.pub というファイルにあり、署名されたメッセージが my-message-file というファイルにあるものと想定しています。

署名を検証する OpenSSL コマンドは、作成された署名の種類によって異なります。OpenSSL を使用して、PSS パディングとともに SHA-256 RSA 署名を検証するには、次のようにします。

openssl dgst -sha256 \
-sigopt rsa_padding_mode:pss \
-sigopt rsa_pss_saltlen:-1 \
-signature ~/my-signature-file \
-verify ~/signing_key.pub ~/my-message-file

署名が有効であれば、次のように表示されます。

Verified OK

API

CryptoKeyVersions.getPublicKey メソッドを使用して公開鍵を取得し、さらにコマンドライン用の例を使用して署名を検証します。

Go

// verifySignatureRSA will verify that an 'RSA_SIGN_PSS_2048_SHA256' signature is valid for a given message.
// example keyName: "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
func verifySignatureRSA(keyName string, signature, message []byte) error {
	ctx := context.Background()
	client, err := cloudkms.NewKeyManagementClient(ctx)
	if err != nil {
		return err
	}

	// Retrieve the public key from KMS.
	response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyName})
	if err != nil {
		return fmt.Errorf("failed to fetch public key: %+v", err)
	}
	// Parse the key.
	block, _ := pem.Decode([]byte(response.Pem))
	abstractKey, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return fmt.Errorf("failed to parse public key: %+v", err)
	}
	rsaKey, ok := abstractKey.(*rsa.PublicKey)
	if !ok {
		return fmt.Errorf("key '%s' is not RSA", keyName)
	}
	// Verify RSA signature.
	hash := sha256.New()
	hash.Write(message)
	digest := hash.Sum(nil)
	pssOptions := rsa.PSSOptions{SaltLength: len(digest), Hash: crypto.SHA256}
	err = rsa.VerifyPSS(rsaKey, crypto.SHA256, digest, signature, &pssOptions)
	if err != nil {
		return fmt.Errorf("signature verification failed: %+v", err)
	}
	return nil
}

Java

/**
 * Verify the validity of an 'RSA_SIGN_PKCS1_2048_SHA256' signature for the
 * specified message
 *
 * Example keyName:
 *   "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys/KEY_ID/cryptoKeyVersions/1"
 */
public static boolean verifySignatureRSA(String keyName, byte[] message, byte[] signature)
    throws IOException, GeneralSecurityException {

  // Create the Cloud KMS client.
  try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
    // Get the public key
    com.google.cloud.kms.v1.PublicKey pub = client.getPublicKey(keyName);
    String pemKey = pub.getPem();
    pemKey = pemKey.replaceFirst("-----BEGIN PUBLIC KEY-----", "");
    pemKey = pemKey.replaceFirst("-----END PUBLIC KEY-----", "");
    pemKey = pemKey.replaceAll("\\s", "");
    byte[] derKey = BaseEncoding.base64().decode(pemKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(derKey);
    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(message);
    return rsaVerify.verify(signature);
  }
}

Python

def verify_signature_rsa(signature, message, key_name):
    """
    Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the
    specified message

    Example key_name:
      "projects/PROJECT_ID/locations/global/keyRings/RING_ID/cryptoKeys\
              /KEY_ID/cryptoKeyVersions/1"

    Requires:
      cryptography.exceptions.InvalidSignature
      cryptography.hazmat.primitives.asymmetric.padding
      cryptography.hazmat.primitives.asymmetric.utils
      cryptography.hazmat.primitives.hashes
      hashlib
    """
    # get the public key
    client = kms_v1.KeyManagementServiceClient()
    response = client.get_public_key(key_name)
    key_txt = response.pem.encode('ascii')
    public_key = serialization.load_pem_public_key(key_txt, default_backend())

    # get the digest of the message
    digest_bytes = hashlib.sha256(message).digest()

    try:
        # Attempt verification
        public_key.verify(signature,
                          digest_bytes,
                          padding.PSS(mgf=padding.MGF1(hashes.SHA256()),
                                      salt_length=32),
                          utils.Prehashed(hashes.SHA256()))
        # No errors were thrown. Verification was successful
        return True
    except InvalidSignature:
        return False

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud KMS ドキュメント