[Fixed] Node.js + crypto sign and verify data over express

Issue

I want to be able to ensure that the public and private key are valid. I would be storing the public key on the server rather than each request which is uploaded by a user. However, it won’t be allowed to be sent.

server.js(Server)

const express = require('express')
const crypto = require("crypto")
const fetch = require("node-fetch")
const app = express()
const port = 3002

app.get('/', async (req, res) => {
    var res2 = await fetch('http://localhost:3001/data')
    var verifiableData = await res2.text()

    var res2 = await fetch('http://localhost:3001/key')
    var publicKey = await res2.text()

    var res2 = await fetch('http://localhost:3001/sig')
    var signature = await res2.text()


    const isVerified = crypto.verify(
        "sha256",
        Buffer.from(verifiableData),
        {
            key: publicKey,
            padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
        },
        signature
    )
    
    res.send(isVerified)
})

app.listen(port, () => {
    console.log(`Listening at http://localhost:${port}`)
})

index.js(Client)

const express = require('express')
const crypto = require("crypto")
const app = express()
const port = 3001

const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
    // The standard secure default length for RSA keys is 2048 bits
    modulusLength: 2048,
})

const verifiableData = "this need to be verified"

const signature = crypto.sign("sha256", Buffer.from(verifiableData), {
    key: privateKey,
    padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
})

app.get('/data', (req, res) => {
    res.send(verifiableData)
})

app.get('/sig', (req, res) => {
  res.send(signature.toString("base64"))
})

app.get('/key', (req, res) => {
    res.send(publicKey)
})

app.listen(port, () => {
    console.log(`Listening at http://localhost:${port}`)
})

I know I should be requesting the information from the server rather than the client but I am just trying to get it working.

I got this error with the server.js code with the signature

(node:19312) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The "signature" argument must 
be an instance of Buffer, TypedArray, or DataView. Received type string ('OFEush86vZIuFnTLFBiFt4Be...)
    at Object.verifyOneShot [as verify] (internal/crypto/sig.js:212:11)
    at C:\Users\user\Documents\coding\nodestuff\stesting\server\server.js:20:31
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:19312) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:19312) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise 
rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Updated the index.js to

app.get('/sig', (req, res) => {
  res.send(signature.toString("base64"))
})

And the server.js to

const isVerified = crypto.verify(
        "sha256",
        Buffer.from(verifiableData),
        {
            key: publicKey,
            padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
        },
        Buffer.from(signature, 'base64')
    )

Then got this an error:

(node:5580) UnhandledPromiseRejectionWarning: Error: error:0909006C:PEM routines:get_name:no start line
    at Object.verifyOneShot [as verify] (internal/crypto/sig.js:219:10)
    at C:\Users\user\Documents\coding\nodestuff\stesting\server\server.js:20:31
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
(node:5580) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:5580) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

It works perfectly fine in a single file just doesn’t work when sent over an express API. How do I fix the error/errors?

Solution

What needed to be done was that the publicKey was invalid. This is because it was a PublicKeyObject meaning it wouldn’t be sent with express leaving the value as {}

I had to do

const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
    // The standard secure default length for RSA keys is 2048 bits
    publicKeyEncoding: {
        type: 'spki',
        format: 'pem'
    },
    modulusLength: 2048,
})

As well as using this in the signature part.

Buffer.from(signature, 'base64')

Leave a Reply

(*) Required, Your email will not be published