nodejs PKCS7 decryption and verify with PrivateKey and Cert problem - node.js

Im tring to make a nodejs app for decrypt a pem file with some cert.
i made something but couldnt get true verified data.
im getting false and decrypted data wrong.
const crypto = require('crypto');
const forge = require('node-forge');
var privateKeyAssociatedWithCert = fs.readFileSync("./test2.key").toString();
var certOrCertPem = fs.readFileSync("./soft_maya_test_certificate.crt").toString();
let p7d = forge.pkcs7.messageFromPem(decryptedData)
let privateCert = forge.pki.decryptRsaPrivateKey(privateKeyAssociatedWithCert);
p7d.decrypt(p7d.recipients[0], privateCert);
console.log(p7d.content)
var testCert = fs.readFileSync("./test_ca_chain.crt").toString();
var testKey = fs.readFileSync("./test.pem").toString();
var pem = forge.pkcs7.messageToPem(p7d);
console.log(pem)
console.log(Buffer.from(p7d.content.data, 'binary').toString('utf8'))
var v = crypto.createVerify('RSA-SHA256');
v.update(p7d.content.data);
let ffr = v.verify(testCert, testKey, 'binary')
console.log(ffr);

Related

NodeJS AESCFB + pkcs7 padding decryption

I'm trying to port the following Go functions to nodeJS using crypt or crypt-js but i'm having issues trying to figure out what's wrong:
The Go encryption code is available at https://go.dev/play/p/O88Bslwd-qh ( both encrypt and decrypt work)
The current nodejs implementation is:
var decryptKey= "93D87FF936DAB334C2B3CC771C9DC833B517920683C63971AA36EBC3F2A83C24";
const crypto = require('crypto');
const algorithm = 'aes-256-cfb';
const BLOCK_SIZE = 16;
var message = "8a0f6b165236391ac081f5c614265b280f84df882fb6ee14dd8b0f7020962fdd"
function encryptText(keyStr, text) {
const hash = crypto.createHash('sha256');
//Decode hex key
keyStr = Buffer.from(keyStr, "hex")
hash.update(keyStr);
const keyBytes = hash.digest();
const iv = crypto.randomBytes(BLOCK_SIZE);
const cipher = crypto.createCipheriv(algorithm, keyBytes, iv);
cipher.setAutoPadding(true);
let enc = [iv, cipher.update(text,'latin1')];
enc.push(cipher.final());
return Buffer.concat(enc).toString('hex');
}
function decryptText(keyStr, text) {
const hash = crypto.createHash('sha256');
//Decode hex key
keyStr = Buffer.from(keyStr, "hex")
hash.update(keyStr);
const keyBytes = hash.digest();
const contents = Buffer.from(text, 'hex');
const iv = contents.slice(0, BLOCK_SIZE);
const textBytes = contents.slice(BLOCK_SIZE);
const decipher = crypto.createDecipheriv(algorithm, keyBytes, iv);
decipher.setAutoPadding(true);
let res = decipher.update(textBytes,'latin1');
res += decipher.final('latin1');
return res;
}
console.log(message)
result = decryptText(decryptKey,message);
console.log(result);
message = encryptText(decryptKey,'hola').toString();
console.log(message)
result = decryptText(decryptKey,message);
console.log(result);
Any idea why it is not working as expected?
Note: I know that padding is not required with cfb but i can't modify the encryption code, it just for reference.
I don't know Go or the specifics of aes.NewCipher(key), but from its documentation it doesn't look like it's hashing the key in any way. The Go code you're linking to also doesn't hash it, so I'm not sure why you're hashing it in the Node.js code.
This should be sufficient:
function encryptText(keyStr, text) {
const keyBytes = Buffer.from(keyStr, "hex")
…
}
function decryptText(keyStr, text) {
const keyBytes = Buffer.from(keyStr, 'hex');
…
}
As an aside: it looks like you may be encrypting JSON blocks with these functions. If so, I would suggest not using any encoding (like latin1) during the encryption/decryption process, given that JSON text must be encoded using UTF-8.

Encrypt compressed data using zlib and cryptojs

I'm in node js, trying to compress an object using zlib.deflate and then encrypt it using cryptojs AES.
Then, when I try to get the original value, I fail on the zlib.inflate call with the error: "incorrect header check".
const zlib = require('zlib');
const { AES, enc } = require('crypto-js');
const obj = {
a: "a",
b: "b"
};
const inputToDeflate = JSON.stringify(obj);
const compressed = zlib.deflateSync(inputToDeflate);
const encrypted = AES.encrypt(compressed.toString(), 'f11').toString();
const decrypted = AES.decrypt(encrypted, 'f11');
const decompressed = zlib.inflateSync(decrypted.toString(enc.Utf8));
I think you're losing zlib header when converting it to string before encryption. We need to change this compressed.toString() some way that the header still preserve inside, something like base64 encoding.
const buffer = Buffer.from(compressed).toString("base64")
const encrypted = AES.encrypt(buffer, 'f11').toString();
Decryption is remind the same, but we need to decode the result back from base64 to original zlib data like before.
const decrypted = AES.decrypt(encrypted, 'f11').toString(enc.Utf8);
const decryptedBuffer = Buffer.from(decrypted,'base64')
const decompressed = zlib.inflateSync(decryptedBuffer);
const resultString = decompressed.toString()
That's it. But I have suggestion here to do encryption part first before compression. Because the result string is different. Take a look in this full code :
Full Code
const zlib = require('zlib');
const { AES, enc } = require('crypto-js');
const obj = {
a: "a",
b: "b"
};
const inputToDeflate = JSON.stringify(obj);
const compressed = zlib.deflateSync(inputToDeflate);
// Compact the data to base64
const buffer = Buffer.from(compressed).toString("base64")
const encrypted = AES.encrypt(buffer, 'f11').toString();
console.log("compressed + base64 + encrypted length",encrypted.length)
const decrypted = AES.decrypt(encrypted, 'f11').toString(enc.Utf8);
// Convert the result from base64 to original compressed data
const decryptedBuffer = Buffer.from(decrypted,'base64')
const decompressed = zlib.inflateSync(decryptedBuffer);
// Convert buffer to string
const resultString = decompressed.toString()
// vice versa
console.log(resultString)
Output:
compressed + base64 + encrypted length 88
{"a":"a","b":"b"}
Suggestion
const zlib = require('zlib');
const { AES, enc } = require('crypto-js');
const obj = {
a: "a",
b: "b"
};
const inputData = JSON.stringify(obj);
const encrypted = AES.encrypt(inputData, 'f11').toString();
const compressed = zlib.deflateSync(encrypted);
console.log("encrypted + compressed length",encrypted.length)
const decompressed = zlib.inflateSync(compressed);
const decrypted = AES.decrypt(decompressed.toString(), 'f11').toString(enc.Utf8);
console.log(decrypted)
Output:
encrypted + compressed length 64
{"a":"a","b":"b"}
I think it much more efficient to do encryption first and then compression.

Node Crypto Error - wrong final block length

I am getting an error when decrypting a response using crypto that i don't understand Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
I'm decrypting a response that looks like this 'p6\u001e�s�p>l?a%ޟ�=~m�\u0002D�K(�[<\u0007O�6\u001c�a�[sP�=\u00112\u001d�)n�Ⴓ?, i've shortened it for brevity. The end result is that it should be a JSON object
My code is as follows
const crypto = require('crypto');
const secret = "mysecret";
const algorithm = 'aes-128-cbc';
function decryptAES(message) {
const bytes = Buffer.from(message);
const salt = bytes.slice(bytes.length - 8);
const key = crypto.pbkdf2Sync(secret, salt, 10000, 16, 'sha1');
const iv = bytes.slice(bytes.length - 24, bytes.length - 8);
const data = bytes.slice(0, bytes.length - 24);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
let decrpyted = decipher.update(data, 'hex', 'utf8');
decrpyted = Buffer.concat([decrpyted, decipher.final('utf8')])
console.log(decrpyted.toString());
}
What could I be doing wrong and what does the error message mean?
Update
From looking at how the data is encrypted the other side I can see that they are using PKCS7Padding. In my decryption code I am not specifying this. Can this be done with crypto?

I am trying to convert this crypto encrypt and decrypt js code into typescript for an Angular project

I am using crypto in node.js for encryption and decryption of user credentials, which works fine. On the other hand, I have to use the same approach to getting the exact result of encryption and decryption in Angular. As it's confirmed that crypto is not working in Angular. I would like to know any way to achieve the required result.
I added npm i --save-dev #types/node but this is not working for me. Also added crypto but the module was not found. I also use npm crypto-js encryption is done but not the same as my js method.
This is my js file code, I need to implement the same method in my Angular project:
'use strict';
const crypto = require('crypto');
const IV_LENGTH = 16;
encrypt(text: any, key: any) {
console.log('Before Encrption Data is-->', text, key)
let iv = crypto.randomBytes(this.IV_LENGTH);
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
let returnable = iv.toString('hex') + ':' + encrypted.toString('hex');
return returnable;
}
decrypt(text: any, key: any) {
let textParts = text.split(':');
let iv = Buffer.from(textParts.shift(), 'hex');
let encryptedText = Buffer.from(textParts.join(':'), 'hex');
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(key), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
}
When I added crypto this type of error occurred:

custom private key with crypto in node.js

from: https://nodejs.org/api/crypto.html#crypto_class_ecdh
const alice_key = alice.generateKeys();
will generate a random private key and the corresponding public key.
But I would like to set my own private key: e8f32e723decf...
If I use :
alice.setPrivateKey("e8f32e723decf");
the object alice_key is not affected, so later:
const bob_secret = bob.computeSecret(alice_key, 'hex', 'hex');
will be wrong. Is there a way to do something like:
const alice_key = alice.generateKeys("e8f32e723decf");
First of all I suppose your hex string is missing a leading 0, so it should be 0e8f32e723decf.
Then it depends on your node.js version, the implementation of ECDH.setPrivateKey() changed from 5.1 to 5.2
node.js 5.0
You need to generate the keys and override them
You need to have the public and the private key
Working online example
const crypto = require('crypto');
// this is just to generate a private/public key pair
const warmup = crypto.createECDH('secp521r1');
warmup.generateKeys();
const warmup_private_key = warmup.getPrivateKey();
const warmup_public_key = warmup.getPublicKey();
// convert it to hex string to match the example
// you would store these strings somewhere I guess
private_key = warmup_private_key.toString('hex');
public_key = warmup_public_key.toString('hex');
// now let's create the ciphers
const alice = crypto.createECDH('secp521r1');
const bob = crypto.createECDH('secp521r1');
----------
// Bob gets created keys
bob.generateKeys();
// Generate Alice's keys - that's really annoying since you will override it
alice.generateKeys();
// now set the keys:
alice.setPrivateKey(private_key, "hex");
alice.setPublicKey(public_key, "hex");
// Exchange and generate the secret...
const alice_secret = alice.computeSecret(bob.getPublicKey());
const bob_secret = bob.computeSecret(alice.getPublicKey());
console.log("alice's shared secret: " + alice_secret.toString('hex') + "\n");
console.log("bob's shared secret: " + bob_secret.toString('hex') + "\n");
console.log('shared secrets match: ' + alice_secret.equals(bob_secret));
node.js >= 5.2
const crypto = require('crypto');
const alice = crypto.createECDH('secp256k1');
const bob = crypto.createECDH('secp256k1');
bob.generateKeys();
alice.setPrivateKey('0e8f32e723decf', 'hex');
const alice_secret = alice.computeSecret(bob.getPublicKey());
const bob_secret = bob.computeSecret(alice.getPublicKey());
console.log(alice_secret.equals(bob_secret));

Resources