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

creating a JSON array within a function

I am currently developing an Angular application and working on a component with the following method: createPath(node, currentPath = []){ if(node.parent !==null) { return createPath(node.parent, [node.data.name, ...currentPath]) } else { retu ...

Is it possible to use nodemailer locally with NodeJS? The issue is that the greeting emails are not being received

Every time I attempt to send an email using nodemailer within my local network, I encounter the following error: *Greeting never received at SMTPConnection._formatError (C:\Users\PI_TEAM\Desktop\node_modules\nodemailer\lib ...

What is the correct location for the webpack.config.js file within a React project?

I recently inherited a React project that I need to continue working on, but I have never used React before. I've been advised to use a web worker in the project and have found a tool that seems suitable for my needs: Worker Loader The suggested meth ...

Setting field names and values dynamically in MongoDB can be achieved by using variables to dynamically build the update query

I am working on a website where users can place ads, but since the ads can vary greatly in content, I need to be able to set the field name and field value based on user input. To achieve this, I am considering creating an array inside each document to sto ...

Code Not Functioning on Website Despite Working in Console

I've developed a jQuery script that eliminates any delivery methods containing the phrase "Royal Mail" if certain products are present in the user's cart. The script functions flawlessly when executed in Google Chrome Console but fails to work wh ...

utilizing the identical characteristics of the parent component

In order for the properties in InputGroup.js to be accessible as this.props in lower-level components like TextInput.js, Checkbox.js, I have created a simple component called InputComponent.js. In this component, I assign this.props to this.prpt so that it ...

The error message "Each item within a list must be assigned a unique 'key' prop" is being displayed

At the moment, I'm immersed in a project that utilizes React, Next.js, and Ant-Design. However, during the development process, I encountered an error due to the absence of a unique key like so: Here's the detailed log of the error: Warning: Ea ...

Unlocking elements in Vue.js through functions

Looking to dynamically add a class to the label element when focusing on an input element below it. The current HTML and JS code I'm using is as follows: HTML: <label for="formProductId" ref="productIdLabel" class="form-element-title">Product ...

When utilizing jQuery and Ajax for form submission, PHP is unable to retrieve any data

I'm encountering an issue when trying to submit a form with only a radiobutton group named radiob. The script I am using for submitting the data is as follows: <script type="text/javascript"> $(function() { $("#myForm").submit(funct ...

Ajax: client dilemma created by the interaction of two functions

My university homework assignment requires me to develop a small e-commerce website. After logging in, the user will be directed to the homepage where they will receive a JSON object from the server containing product information to dynamically generate th ...

Can the default position of the scrollbar be set to remain at the bottom?

I have a select option tag with a scrollbar to view the contents in the dropdown. I am looking for a way to automatically position the scroll at the bottom when an item is selected from the dropdown. jquery code $('document').ready(func ...

The menu isn't displaying properly and the onclick function seems to be malfunctioning

My onclick event is acting strange. Whenever I click the mobile menu in the menubar, it just appears briefly like a flash and then disappears. It's not staying stable on the screen. The classes are being added and removed abruptly when I try to click ...

Assigning a click event to an element within CKEditor

Looking to add a click event to an element in ckeditor4-angular for custom functionality <div class="fractional-block" id="fractional-block"><span>5</span><svg height="5" width="100%"><line ...

Guide: Displaying components within a Vue2 string

Within my Vue2 component, I am working with a string that looks something like this: <template> <div><h1>Hello World</h1> <div v-html="testString"></div> </div> </template> <script> exp ...

Include a "remember me" feature in the Stripe form

I am currently working on an exciting project using Angular 6. Within my website, I have decided to integrate the Stripe payment system. However, I would like to incorporate a unique and default "remember me" feature offered by Stripe. <div id="card-e ...

When AJAX is invoked, the HTML content fails to display within a div

The following is the ajax script I am currently utilizing: mypage.php $(".sales_anchor").click(function(e) { e.preventDefault(); var store_username = $("#storeUsername").val(); var store_id = $("#storeID").val(); var sale_id = $(this).a ...

JavaScript application that features an audio player complete with a dynamic progress bar and customizable tags

Looking to create a custom audio player using HTML, CSS, and JS. The player should have basic functionality like a play button and progress bar. However, I want to allow users to add tags on the progress bar of the audio file, similar to how SoundCloud&apo ...

What is the method to retrieve the total number of days in a moment-jalaali using NodeJS?

I'm trying to determine the number of days in the moment-jalaali package for NodeJS. Despite checking their API on GitHub, I couldn't find any reference to a specific method like numOfDay. ...

Vue: setInterval not updating timer variable

Here is my code for updating and displaying the number of elapsed seconds: <template> <div> {{timerValue}} </div> </template> <script> export default { name: "App", components: { }, da ...

How to customize TextField error color in Material-UI using conditional logic in React

Currently, I am incorporating the React Material-UI library into my project and faced with a challenge of conditionally changing the error color of a TextField. My goal is to alter the color of the helper text, border, text, and required marker to yellow ...