Understanding the process of verifying signatures with Cloud KMS

I've been struggling to confirm the validity of a signature generated using Google's cloud KMS, as I'm consistently receiving invalid responses.

Here's my approach to testing it:

const versionName = client.cryptoKeyVersionPath(
      projectId,
      locationId,
      keyRingId,
      keyId,
      versionId
    )

    const [publicKey] = await client.getPublicKey({
      name: versionName,
    })

    const valueToSign = 'hello, how are you'

    const digest = crypto.createHash('sha256').update(valueToSign).digest()

    const [signResponse] = await client.asymmetricSign({
      name: versionName,
      digest: {
        sha256: digest,
      },
    })

    const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)

    if (!valid) return console.log('INVALID SIGNATURE')

    console.log('SIGNATURE IS VALID!')

// output: INVALID SIGNATURE

This script will consistently output 'INVALID SIGNATURE' unless I use the original message rather than its hash:

const valid = crypto.createVerify('sha256').update(valueToSign).verify(publicKey.pem, signResponse.signature) // true

However, when utilizing a local private key, I am able to successfully sign messages and validate them based on their hashes:

const valueToSign = 'hello, how are you'
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');

const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');

const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);
const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true

What is causing this inconsistency? Is there a unique aspect to KMS signatures that I am missing?

Answer №1

After reviewing the example from the crypto module documentation and analyzing your observations, it appears that there may have been a misunderstanding regarding the functionality of client.asymmetricSign. Let's break down the process:

Analysis of your local private key code:

const valueToSign = 'hola, the tal'

// Create sha256 hash
const msgHash = crypto.createHash("sha256").update(valueToSign).digest('base64');

// Sign the sha256(hash)
const signer = crypto.createSign('sha256');
signer.update(msgHash);
const signature = signer.sign(pk, 'base64');

// Verify the sha256(hash)
const verifier = crypto.createVerify('sha256');
verifier.update(msgHash);

const valid = verifier.verify(pubKey, signature, 'base64');
console.log(valid) // true

We are verifying sign(sha256(hash)) using verify(sha256(hash)).


Analysis of your KMS code:

const valueToSign = 'hola, que tal'

// Create sha256 hash
const digest = crypto.createHash('sha256').update(valueToSign).digest()

// KMS signs the hash
const [signResponse] = await client.asymmetricSign({
    name: versionName,
    digest: {
        sha256: digest,
    },
});

// Verify the signed hash
const valid = crypto.createVerify('sha256').update(digest).verify(publicKey.pem, signResponse.signature)

We are verifying sign(hash) using verify(sha256(hash)).


In essence, when working locally, you are signing the hash and verifying the signed hash. With KMS, you are signing the data and verifying the signed hash, which is essentially your signed data; hence why your second attempt with .update(valueToSign) was successful.

The solution? Before letting KMS sign it, hash your sha256 hash again since KMS expects the sha256 hash of the data to be signed, while crypto expects the data to be signed (which it will hash itself based on the algorithm provided to createSign).

Answer №2

Kevin's viewpoint is similar to mine but from a different perspective.

When utilizing

crypto.createSign(<algorithm>)
and
crypto.createVerify(<algorithm>)
, the specified digest algorithm is used for creating and verifying signatures, respectively.

For the GCP KMS asymmetricSign operation, a message digest produced with the designated algorithm over the original data must be provided as an argument. Hence, calculating the message digest with crypto.createHash is necessary.

It's important to note that the behavior of the crypto verification process remains unchanged, always requiring the original data as input. This explains why passing the original data without hashing also works.

In addition to your example, the GCP documentation offers more examples for reference.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I have been working on creating a hangman game, and although I have figured out the logic behind it, I am experiencing an issue where it is not functioning properly. After inspect

I am currently working on a basic hangman game using only HTML and JavaScript. I have the logic in place, but it doesn't seem to be functioning correctly. When I inspected the element, it showed an error saying 'undefined toUppercase.' As a ...

MenuIcon Component is experiencing difficulty being rendered

I am attempting to construct an IconMenu within the AppBar Component. My project is being developed using create-react-app. This is how my code appears: index.js import React from 'react'; import ReactDOM from 'react-dom'; import &a ...

What is the TypeScript equivalent of the Java interface.class?

Can you write a Java code in TypeScript that achieves the same functionality as the code below: Class<?> meta = Object.class; and meta = Processor.class; // Processor is an interface In TypeScript, what would be the equivalent of .class? Specifica ...

Utilizing Google Analytics 4 in a React TypeScript Environment

Currently, there are two options available: Universal Analytics and Google Analytics 4. The reasons why I lean towards using Google Analytics 4: Universal Analytics is set to be retired on July 1, 2023, so it makes sense to start fresh with Google Analyt ...

Superbase Email Forwarding

Is it possible to create a dynamic redirect link in the confirmation email that directs users to a specific page after creating an account? For instance: If a user visits the website using a link such as www.website.com/project/1 or /project/2 etc. and t ...

Ways to update row background color based on specific column values

I need to customize the background color of my table rows based on the value in the "Category" column. For example: Name Category Subcategory A Paid B C Received D If the Category value is 'Paid', I want the ro ...

In the realm of JavaScript, consider logging a message to the console if the array value happens to be

I'm facing an issue with the code below. My goal is to log 'empty array value' whenever there is an empty value, but it's not functioning as expected. Can anyone advise me on what the value of an empty array item is and how I can modify ...

Unable to assign the value 'hello' to an undefined property in TypeScript

I'm attempting to define a class in TypeScript, but I keep encountering the error shown below. Here is the execution log where the error occurs: [LOG]: "adding" [LOG]: undefined [ERR]: Cannot set property 'hello' of undefined class Cust ...

Navigating through dynamic elements using Selenium

I'm having trouble extracting boxer information from the flashcore.com website using Selenium. The code I've written doesn't seem to be working properly. Can anyone point out where the error might be? The expectation is that Selenium should ...

Concealing the TinyNav Drop-Down Menu

Currently, I am utilizing TinyNav on my website and it is working wonderfully. However, due to our extensive menu system, the tinynav dropdown appears quite large. I have been attempting to figure out a way to hide all sub-menus without success. I experim ...

Update the numerical data within a td element using jQuery

Is there a way to use jquery to increase numerical values in a <td> element? I've attempted the following method without success. My goal is to update the value of the td cell by clicking a button with the ID "#increaseNum". Here is the HTML st ...

Handling Errors in Node.js with Express

To access the complete repository for this project, visit: https://github.com/austindickey/FriendFinder After downloading the directory, follow the instructions in the ReadMe to set it up on your computer. Encountering an issue where my survey.html page ...

Dynamic change in the mutation observer is not causing the callback to trigger

I have created a nested structure with two <div> elements. I am monitoring changes in the width of the inner <div>, which is set to width:auto;. Despite adjusting the width of the parent <div>, the mutation observer callback doesn't ...

The ES6 class Inheritance chain does not properly utilize the instanceof keyword

My curiosity lies in understanding why the instanceof operator fails to work properly for the inheritance chain when there are multiple chains of inheritance involved. (optional read) How does the instanceof operator function? When using obj inst ...

Issues with jQuery function arising post-update to newer jQuery version

Recently, I encountered an issue with a function that used to work perfectly fine with jQuery 1.8.3. However, when I upgraded to jQuery 1.10.x or above, the function stopped working. <script type="text/javascript"> $.ajaxSetup({cache: false}); ...

Using JavaScript to toggle the iron-collapse property

I've implemented multiple <iron-collapse> elements with unique IDs, each one corresponding to a <paper-icon-button>. On screens wider than 650px, I can interact with the <iron-collapse>s using their respective buttons. But on narrow ...

Issue with React Google Maps Api: Error occurs while trying to access undefined properties for reading 'emit'

I'm trying to integrate a map using the google-map-react API, but I keep encountering the following error: google_map.js:428 Uncaught TypeError: Cannot read properties of undefined (reading 'emit') at o.r.componentDidUpdate (google_map.js: ...

I'm having trouble understanding why my Javascript validation suddenly stopped functioning. Can anyone assist me in troubleshooting this issue?

I have been working on this webpage for a school project for a few days, and it was running smoothly until about 10 minutes ago. The only change I made was adding an extra JavaScript validation. Now, when I try to register by clicking the "register" butt ...

The click event in jQuery is being blocked by the use of "display:none

Currently, I have implemented a search box feature with search suggestions: <input id="searchBar" /> <div id="searchSuggestion"></div> The searchSuggestion div is dynamically updated using jQuery ajax whenever an input is entered (imple ...

I'm looking for the location of the console.log output while running nodejs/npm start. Where

Just started working with react/node and using npm start to launch a server. The server is up and running, displaying my react code as expected. However, I'm facing an issue with debugging - my console.logs are not showing up in the browser console. I ...