Create RSA-SHA256 sign with PEM key - node.js

I've been creating a gateway for a legacy service, this legacy service needs a signature as a body parameter of a PUT request, in order to create this sign I need to follow the following steps:
Create a hash with certain text as data, this hash needs to be SHA256.
Encrypt the result of the hash using RSA with a PEM key
Encode the result of the RSA to Base64
Following the previous steps I create the following code
export class Signature {
// class body
public static sign(text: string){
const key = readFileSync('key.pem')
const passphrase = '12345678'
const createdSign = createSign('RSA-SHA256')
createdSign.write(text)
createdSign.end()
return createdSign.sign({ key, passphrase }).toString('base64')
}
}
But I'm not sure if this the correct implementation, taking into consideration the previous steps, and the existence of the hash API in NodeJS.
If someone could tell me if I'm correctly implementing this algorithm.

Related

Trying to symmetrically encrypt a value for storage in the client (httpOnly cookie) and having an issue decrypting

I am trying to encrypt a value on my server with a private key to store it on the client within an httpOnly cookie.
I am having trouble with the encryption/decryption lifecycle
function encrypt(input) {
const encryptedData = crypto.privateEncrypt(
privateKey,
Buffer.from(input)
)
return encryptedData.toString('base64')
}
function decrypt(input) {
const decryptedData = crypto.privateDecrypt(
{ key: privateKey },
Buffer.from(input, 'base64'),
)
return decryptedData.toString()
}
const enc = encrypt('something moderately secret')
const dec = decrypt(enc)
console.log(dec) // 'something moderately secret'
However the crypto.privateDecrypt function is throwing with
Error: error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
Side question, is it safe to reuse the same private key the server uses to sign JWTs. It's an rsa key generated using ssh-keygen -t rsa -b 4096 -m PEM -f RS256.key
So, you don't use crypto.privateEncrypt() with crypto.privateDecrypt(). That's not how they work. Those functions are for asymmetric encryption, not for symmetric encryption. You use either of these two pairs:
crypto.publicEncrypt() ==> crypto.privateDescrypt()
crypto.privateEncrypt() ==> crypto.publicDecrypt()
So, that's why you're getting the error you're getting. The nodejs doc for crypto.privateDecript() says this:
Decrypts buffer with privateKey. buffer was previously encrypted using the corresponding public key, for example using crypto.publicEncrypt().
If what you really want is symmetric encryption, there are a bunch of options in the crypto module for that. There are some examples shown here: https://www.section.io/engineering-education/data-encryption-and-decryption-in-node-js-using-crypto/ and https://fireship.io/lessons/node-crypto-examples/#symmetric-encryption-in-nodejs.

Nodejs construct the public key using public key string

I have a public key string as follows
let pk_str = "public key strig here" and I am using the library jose to verify a JWS
(async() => {
const decoder = new TextDecoder();
const jws = vc_proof_value;
const { payload, protectedHeader } = await compactVerify(jws, pk_str);
console.log(protectedHeader)
console.log(decoder.decode(payload))
})();
I am getting the following error when trying to run the script
(node:75986) UnhandledPromiseRejectionWarning: TypeError: Key must be one of type KeyObject, CryptoKey, or Uint8Array. Received type string
Is there a way to construct the key ?
In NodeJS (I refer to NodeJS, since you have tagged this), the public key is passed as KeyObject wich is created with crypto.createPublicKey(). You didn't say much about the key, presumably it's PEM encoded (since it's a string). In this case, you simply have to pass the PEM encoded key:
var key = crypto.createPublicKey(pk_str);
If in the compactVerify() call pk_str is replaced by key, verification works.
In addition to PEM keys (default), JWK and DER keys (X.509/SPKI or PKCS#1) are also supported.
Here is the documentation for the key argument of all applicable functions.

How to generate a PEM-formatted Key from a 64Byte raw hex-formatted Key

I have the following problem:
After recreating the public key from a signed transaction, I try to encrypt some payload with it.
However the node.js-module named "crypto" is expecting a pem-formatted key in the publicEncrypt(key, payload) function.
My Question:
How can I create the pem-formatted key from a raw hex-encoded key?
Here is the recreated 64 Byte public key:
9f9f445051e788461952124dc08647035c0b31d51f6b4653485723f04c9837adb275d41731309f6125c14ea1546d86a27158eec4164c00bab4724eed925e9c60
Information:
I know, that a pem-format-key consists of base64 encoded data, a header and a footer.
-----BEGIN RSA PUBLIC KEY-----
BASE64 ENCODED DATA
-----END RSA PUBLIC KEY-----
I have also found out that within the base64 encoded data the following DER-structure is present:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
So the only question is how to get from the raw hex-encoded key to this DER-structure.
I would appreciate any help!
Problem solved
Thanks to Maarten Bodewes and his comment regarding the key being secp256k1 and not RSA.
After some further research, I finally managed to encrypt/decrypt a message asymmetrically with secp256k1 keys.
With the help of Cryptos ECDH class I managed to create a key-object and then assign the private key to it. When assigned, you can easily derive the public key with getPublicKey(). All participants would create a key object for themselves and assign their private keys to it. Then they share their retrieved public keys (in my case over a shared medium). In addition I used a npm-package named standard-ecies which provides the ECIES encryption-scheme.
Code:
const crypto = require('crypto');
const ecies = require('standard-ecies');
var buffer = new Buffer("Hello World");
var ecdh = crypto.createECDH('secp256k1');
ecdh.setPrivateKey(privateKey);
var encryptedText = ecies.encrypt(ecdh.getPublicKey(), buffer);
var decryptedText = new Buffer(ecies.decrypt(ecdh, encryptedText));
I should have noticed this, because crypto's encryption function (link to the api-doc) clearly works only with RSA keys and not with secp256k1 keys.
Anyway if someone has a similar issue, I hope this answer helps!

Using xml-crypto with PSHA1

Is it possible to use XML Crypto using a PSHA1 (http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1) key?
I have both secrets and generate a PSHA1 key string using them, however this fails with:
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
I'm not what format this key needs to be in to be accepted, it's not a PEM certificate, just a string based on 2 provided nonce's. One provided by the client during the request and one provided by the server in the response.
const sig = new SignedXml();
sig.addReference("/*[local-name()='Envelope']/*[local-name()='Header']/*[local-name()='Security']/*[local-name()='Timestamp']");
sig.signingKey = '<SIGNING_KEY>';
sig.computeSignature(xml);
fs.writeFileSync('signed.xml', sig.getSignedXml());
It fails on the signer.sign line here:
this.getSignature = function(signedInfo, signingKey) {
var signer = crypto.createSign("RSA-SHA1")
signer.update(signedInfo)
var res = signer.sign(signingKey, 'base64')
return res
}
The PSHA1 algorithm isn't implemented in the Crypto Library, but there's a PSHA1 npm package you can use to generate the secret key. After that you can generate a SHA1 hash using the message and key in the standard way.
I asked a very similar question here, which answers the question:
https://stackoverflow.com/a/55053741/5065447

Nodejs Generate a keys to sign a piece of data

I have some blob data for example:
const buffer = Buffer.from('ACDFF12BA','hex');
How I can generate keys in order to sign it?
One way to do that is by using the keypair npm package:
const keypair = require('keypair');
const crypto = require('crypto');
const pair = keypair();
// Create Transmitted Signature
const sign = crypto.createSign('RSA-SHA256');
sign.update('abcdef'); // data from your file would go here
const sig = sign.sign(pair.private, 'hex');
console.log(sig);
// Verifying Signature
const verify = crypto.createVerify('RSA-SHA256');
verify.write('abcdef');
verify.end();
console.log(verify.verify(pair.public, sig,'hex'));
As you can see using the keypair() function I generate the public and the private keys via a sign.sign I generate the signature. Keep in mind to sign with a Private Key. The generated the public key in the following format:
--- RSA BEGIN PUBLIC KEY ---
^some base64 here^
--- RSA END PUBLIC KEY ---
And the generated private key will have the same format:
--- RSA BEGIN PRIVATE KEY ---
^some base64 here^
--- RSA END PRIVATE KEY ---
Now the only thing to do is to stringify the buffer as a hex string and do the job. Keep in mind that the receiving end needs to verify the data as hex string as well.
We can verify the signature with the verify.verify and a public key. Keep in mind on the signature's format as well. In case you generate a hex encoded signature then verify it in the same format.
Also the methodology above is recommended when the native crypto.generateKeyPair is not available (eg. in electron applications)

Resources