Nodejs crypto.publicEncrypt yields different results on different machines - node.js

We are trying to encrypt and decrypt using an asymmetric key in AWS KMS. The configuration for the key is as follows:
In NodeJS, we use the public key to encrypt via the crypto.publicEncrypt:
const encryptRSAPayload = (buffer, publicKey) => {
const encryptedBuffer = crypto.publicEncrypt(
{
key: publicKey,
oaepHash: 'sha256',
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
},
buffer
);
return encryptedBuffer;
};
And we use the function like this (the public key is read from a local file during the minimal repro):
const plainText = '12345678910';
const encrypted = await encryptRSAPayload(Buffer.from(plainText), publicKey);
Now, four developers have ran the exact same code (zipped, with public key etc), this is happening:
NOTE: All of the developers are on the latest OSX system.
Two of us can use AWS to decrypt whatever we produce from the encrypt function, and the other two can not (failing with IvalidCiphertext: null) from AWS.
The encrypted, base64 string from one of the machines that can not encrypt -> decrypt, can not be decrypted on any other machine.
The encrypted base64 string from one of the machines that can encrypt -> decrypt, can be decrypted in aws from any machine.
By now, ive spent two days on this and am a bit lost on what to do. Any ideas?

Problem solved after a few more days of debugging. The problem stemmed from the shipped version of OpenSSL that comes with OSX. For me, that was LibreSSL 2.8, which does not include some of the padding flags used in OAEP nor changing the hash to sha256 (instead of sha1).
The solution was:
Install OpenSSL via Homebrew and set the PATH env to use that version instead of the shipped version.
Reinstall any installed node version to re-link to the correct OpenSSL version.

Related

HTTPS in Nodejs - error:06065064 digital envelope routines evp_decryptfinal_ex bad decrypt

We tried to install our Hapis (Nodejs Version 14) Web service on our customer's server. It ran under HTTP for months, but when we went to enable HTTPS with the appropriate paths to the cert and key it fails when the service starts up with:
error:06065064:digital envelope routines:EVP_Decryptfinal_ex:bad decrypt
Their certificate and key are generated using the Venafi online portal. It gave them a crt and key. The crt uses a Signature algorithm: sha256RSA, Signature hash algorithm of sha256, and Thumbprint algorith: sha1.
Also, the private key is a RSA PRIVATE KEY with Proc-Type: 4,ENCRYPTED and DEK-Info: DES-EDE3-CBC.
I am not sure what is going on, because HTTPS works fine on our development servers.
Is the problem in HapiJS?
Is the problem with the certificate or key themselves?
Is there an Node option I need to be passing in when creating the service?
Please help.
The specified error 06065064:digital envelope routines:EVP_Decryptfinal_ex:bad decrypt occurs in an SSL/TLS connection using OpenSSL (which is what nodejs modules like tls and https actually use) when the privatekey is encrypted (with a passphrase) and the correct passphrase is not provided to decrypt it. The described file format, beginning with a line -----BEGIN RSA PRIVATE KEY----- followed by lines Proc-Type: and DEK-Info: is indeed one of the encrypted formats used by OpenSSL. Specifically this is the encrypted 'traditional' or 'legacy' format; the PKSC8 format added about 2000 but still considered new(!) uses -----BEGIN ENCRYPTED PRIVATE KEY----- and no 822-style headers, only base64 (of the encrypted structure defined by PKCS8); see ursinely-verbose https://security.stackexchange.com/questions/39279/stronger-encryption-for-ssh-keys/#52564 about OpenSSH's use of OpenSSL, which is basically the same as nodejs's use.
The tls module and others that build on it including https ultimately read the key(s) and cert(s) using tls.createSecureContext which accepts in options a member passphrase, or if you need to use multiple keys (and certs) you can provide a passphrase for each key as described in the linked doc.
Alternatively you can avoid the need for a passphrase by converting the key to an unencrypted file, if acceptable under applicable security policies and regulations. (Good policies may prohibit this, but they usually also prohibit getting the privatekey from or providing it to any other system, especially one 'online' somewhere, and your customer is doing the latter.) To retain traditional format do
openssl rsa -in oldfile -out newfile
# and give the passphrase when prompted, or see the man page about -passin
or you can use the 'new' PKCS8 format with
openssl pkey -in oldfile -out newfile
# technically only in release 1.0.0 up, but pretty much everyone is there now
#
# or in all versions back to about 2000
openssl pkcs8 -topk8 -nocrypt -in oldfile -out newfile
For me this error occured after pulling some old code that was workign to a fresh system, I was using too current of a node Version, I downgraded from 17 to 16 and that solved my problem.
Tried checking the github issues related to the TLS, handshake and versions. But couldn't find any.
The final fix was the one suggested by #Greggory Wiley.
Installed nvm - downgraded the node and npm versions. Recomplied the code. And it worked.
In my case I was exporting a certificate from windows to linux inside a docker using openSSL and facing this error.
The problem was in the versions of OpenSLL, when I was converting .pfx file to .crt and .key I was using 3.0.x version on windows, and on linux I had 1.1.1 installed. After I did the same using the same version of OpenSLL on windows it worked.
I had the same issue, I would say that the accepted answer is good expect it does not provide an example where the passphrase is used.
Here's code that worked in my case for express.js
const server = https
.createServer(
{
key: fs.readFileSync("./root/ca/cakey.pem"),
cert: fs.readFileSync("./root/ca/cacert.pem"),
passphrase: "abcdefg",
},
app
)
.listen(PORT, () => {
console.log(`Secure server listening on port:${PORT}`);
});

NodeJS - Get certificate Chain from P7B file

I'm trying to take a CMS base64 encoded string, convert it into a pkcs7 file and extract the leaf and intermediate certificates using javascript/nodejs, similar to the following openssl command:
openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer
I've read almost every article and see solutions in other languages, but not for node. I understand you can achieve what I need using node-forge, but node-forge does not support ECC algorithms. Anyone know of any other solutions/npm packages that can help me accomplish this? Please help me. I'm very new to this.
Did you see PKI.js for Node.js? It is a pure JavaScript library implementing the formats that are used in PKI applications. It is based on the W3C Web Cryptography API with full support for all "Suite B" algorithms in CMS messages. Code snippet submitted by OP:
const cmsSignedBuffer = stringToArrayBuffer(fromBase64(token.signature));
const asn1 = asn1js.fromBER(cmsSignedBuffer);
const cmsContentSimpl = new pkijs.ContentInfo({ schema: asn1.result });
const cmsSignedSimpl = new pkijs.SignedData({ schema: cmsContentSimpl.content })
Another approach is to use a wrapper for openssl like openssl-nodejs, for example. The wrapper simply spawns a child process to invoke openssl. Thus, openssl must be installed on system where the Node.js application is deployed.

Public encryption in crypto on node 0.12

I need to encrypt (and decrypt) a string with a public key previously generated in nodejs (i'm using version 0.12) with crypto module, but i'm unable to do it.
For first i generated the keys in this way:
var diffHell = crypto.createDiffieHellman(60);
diffHell.generateKeys('base64');
var publicKey = diffHell.getPublicKey('base64'); //or whatever 'hex','binary'
var privateKey = diffHell.getPrivateKey('base64'); //or whatever 'hex','binary'
Then i tried to encrypt a string using the generated public key:
crypto.publicEncrypt({key: publicKey}, new Buffer(textToEncrypt));
Running this snippet, node throw this error:
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
at Error (native)
at Object.exports.publicEncrypt (crypto.js:362:18)
[...]
Reading it, I understand that the key must be in PEM format, but i can't find in the documentation how to tranform a public key in PEM.
So, How i can do that? Someone has done this yet?
Diffie-Hellman (Key Exchange) is an algorithm and protocol to derive a shared secret based on modular arithmetic. It is not a public-key cipher in the same way as RSA is. You cannot use Diffie-Hellman for crypto.publicEncrypt().
Node.js' Crypto module doesn't provide a way to generate a public-private RSA key pair, so you either need to use OpenSSL through child_process or use one of the many modules which provide this sort of thing (e.g. ursa).
You do not need to uses ursa for key generation. Instead you can generate keys with openssl then store the generated PEM keys on your server and try to load them in your script

Hex encode ECDSA keys in node.js

I'm building a nodejs app that talks to an API server. The API server requires that each request I make to it includes a X-Signature header using the prime256v1 ECDSA curve and sha256 hash on a particular set of data.
I looked through the crypto and tls documentation but didn't find anything suitable. I have successfully generated a private key with openssl ecparam -name prime256v1 -genkey but it is in PEM format. I have also generated a DER format key. Both of these include some extra metadata like the curve used and in the case of PEM, comments.
I can use these keys for signing operations on my side, but the server requires that I upload a public key using hex encoding (so the server can verify the signatures I make on my requests.)
Specifically the server wants something like the output of the following Python code:
from ecdsa import SigningKey
from binascii import hexlify
hexlify(SigningKey.from_pem(content).to_string())
Sample output for a pubkey (no newlines): c5bd76cd0cd948de17a31261567d219576e992d9066fe1a6bca97496dec634e2c8e06f8949773b300b9f73fabbbc7710d5d6691e96bcf3c9145e15daf6fe07b9
I would prefer not adding python as a dependency to my node app... anyone know of a way I can extract the binary data representing my private key from the PEM or DER files, so I can put it in a buffer and call buffer.toString('hex')? Or a way I can use the native crypto library to generate the ECDSA keypair? Or a library that would do the same?
openssl itself can print out the guts of things, in hex.
Doe the key change? sounds like you can just decode into hex one time, and use that? No need for dependencies - just paste the hex into your node source?

Jasypt StandardPBEStringEncryptor setting password in spring bean configuration file

When using Jasypt's StandardPBEStringEncryptor we have to set password explicitly in spring bean configuration file. Is it ok and secure to have the password in the bean configuration file? Will it be a problem in PCI Compliance to store the encryptor password?
This will not be PCI compliant. Data encrypting keys cannot be stored in plaintext. The specific point is 3.5.2 which is:
Examine system configuration files to
verify that keys are stored in
encrypted format, and that
key-encrypting keys are stored
separately from data-encrypting keys.
You would probably also have other issues around the key management area, such as 3.6.6 (Split knowledge and dual control of keys)
Verify that key-management procedures
are implemented to require split
knowledge and dual control of keys
(for example, requiring two or three
people, each knowing only their own
part of the key, to reconstruct the
whole key).
Key management is the most challenging part of PCI compliance. You may want to consider using a (already PCI compliant) 3rd party to manage your card data. If you are rolling your own then I would advise that you bring in the assistance of a QSA (PCI Qualified Security Assesor) at the earliest opportunity to evaluate the security you're planning on implementing. ultimately it will be the QSA that you need to convince in order to pass your PCI requirements, and they will be more than happy to advise.
You need to store the symmetric key somewhere. A configuration file is a good place, as long as no one has access to it.
I have an idea
you can encrypt all of your plain password with keyPair of keystore.jks. You know that the keystore.jks has its own password. you can remember that password and when your program get started enter it on console. for example when your program start:
Console console = System.console();
keyPair = loadKeystore(new String(console.readPassword()));
private static KeyPair loadKeystore(String pwd) {
InputStream is = Main.class.getResourceAsStream("/keystore.jks");
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, s.toCharArray());
String alias = "youralias";
Key key = keystore.getKey(alias, pwd.toCharArray());
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
when you return the keypair you can uses it for encrypt your password.
key = loadKeystore("yourpass").getPrivate().getEncoded()
goodluck

Resources