Decrypt secret using ECDH and nodejs crypto - node.js

This is the nodejs documentation example:
const crypto = require('crypto');
const alice = crypto.createECDH('secp256k1');
const bob = crypto.createECDH('secp256k1');
// Note: This is a shortcut way to specify one of Alice's previous private
// keys. It would be unwise to use such a predictable private key in a real
// application.
alice.setPrivateKey(
crypto.createHash('sha256').update('alice', 'utf8').digest()
);
// Bob uses a newly generated cryptographically strong
// pseudorandom key pair bob.generateKeys();
const alice_secret = alice.computeSecret(bob.getPublicKey(), null, 'hex');
const bob_secret = bob.computeSecret(alice.getPublicKey(), null, 'hex');
// alice_secret and bob_secret should be the same shared secret value
console.log(alice_secret === bob_secret);
I don't understand where the secret comes in. Suppose I want to decrypt a message foo-bar from Bob (encrypted with Alice public key). I have Alice's private and public key, and Bob's encrypted message how can I decrypt the message having all this?

The steps above constitute the ECDH key agreement protocol to establish a shared secret (a symmetric key) between Alice and Bob which they can subsequently use to communicate securely.
The secret key alice_secret is computed using Alice's private key and Bob's public key at Alice's end.
The key bob_secret is computed using Bob's private key and Alice's public key at Bob's end.
Both keys will be equal. Now Alice and Bob has a shared secret (alice_secret=bob_secret) which they can use to ecnrypt/decrypt messages.
Note that only public keys are exchanged here and a Man-In-The-Middle cannot get hold of either Alice's or Bob's private key.
The shared secret should be ideally converted to a proper symmetric key suitable for algorithms like AES by using a Key Derivation Function. Refer KDF
Pseudo-code
-Bob encrypts using bob_secret and AES:
var crypto = require('crypto'),
algo = 'aes-256-ctr',
var cipher = crypto.createCipher(algo,bob_secret)
var encrypted = cipher.update("foo-bar",'utf8','hex')
encrypted += cipher.final('hex');
-Alice decrypts:
var decipher = crypto.createDecipher(algo,alice_secret)
var decrypted = decipher.update(encrypted,'hex','utf8')
decrypted += decipher.final('utf8');

Related

Is there a way to get same encrypted hash value with some secret key?

I want to encrypt sensitive data in an encrypted format and save it to db. But later I have to be able to decrypt with a secret key used to decrypt. Importantly, encryption must give always the same hash.
const algorithm = 'aes256';
const iv = crypto.randomBytes(16).toString('hex').slice(0, 16);
const key = crypto
.createHash('sha256')
.digest('base64')
.substr(0, 32);
const cipher = crypto.createCipheriv(algorithm, key, iv);
const encrypted =
cipher.update(String('tobeEncrypted'), 'utf8', 'hex') + cipher.final('hex');
console.log(encrypted);
console.log(encrypted);
//e08f733a4dace8b22db763cbd2d0029e
//90086251f083c33dd6aa017a2c6f35f4
// How can I always get the same hash value?
First, your key will be the same key value. Because the value to be hashed will be empty.
const key = crypto
.createHash("sha256") // Hash algorithm
.update(process.env.SECRET_KEY) // Data to hash
.digest('base64')
.substr(0, 32);
Your result will be always different because the IV is random in each execution. So, you could store the IV in the database, in the final message, or use a unique depending on other values like the key or the data.
There is no security risk if you save the IV in your database or if you expose it.
Refs:
Is it safe to store AES IV prepended to CipherText in a DB?
When using AES and CBC, is it necessary to keep the IV secret?

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!

RSAES-PKCS1- V1_5 Public encryption using NodeJs crypto

I am facing up a bit of a wall trying to send encrypted data over to a remote server using the NodeJs crypto module.
According to the API docs, the payload needs to be encrypted using the AES-256 algorithm with randomly generated KEY and IV.
The randomly generated KEY and IV [above] are then encrypted with a shared private key using the RSAES-PKCS1-V1_5 standard.
Finally, the encrypted payload is signed with the private key, using the RSASSA-PKCS1-V1_5 signature scheme and then SHA1 hashed.
Once that is done, I compose an HTTP request and pass the encrypted KEY, IV, encrypted payload and the signature to the remove server.
I am not an expert when it comes to cryptography, so I am convinced that I am doing something wrong somewhere.
The server is able to verify the signature, which gives me confidence that there is no problem with the shared private key file.
However, the server fails to decrypt the encrypted KEY and IV which are needed to decrypt the payload.
I am using the code below for testing:
const crypto = require('crypto');
const fs = require('fs');
//Generate random KEY and IV
const randomKey = crypto.randomBytes(32);
const randomIV = crypto.randomBytes(16);
//Load private key from disk
const privateKey = fs.readFileSync(__dirname + '/private.key');
//Get data payload that should be encrypted with AES-256
const payload = 'Payload to be sent';
//Encrypt payload with AES-256
const cipher = crypto.createCipheriv('aes-256-cbc', randomKey, randomIV);
const encryptedPayload = Buffer.concat([cipher.update(payload), cipher.final()]);
//Sign the encrypted payload using the RSASSA-PKCS1-V1_5 algorithm
const signer = crypto.createSign('RSA-SHA1');
signer.update(encryptedPayload);
signer.end();
const signature = signer.sign(privateKey); //Sign with the private key
//Encrypt both KEY and IV
const encryptOptions = {
key: privateKey,
padding: constants.RSA_PKCS1_PADDING
}
const encryptedKey = crypto.publicEncrypt(encryptOptions, randomKey);
const encryptedIV = crypto.publicEncrypt(encryptOptions, randomIV);
//A function that encodes Buffer type data to base64
const encode = buffer => buffer.toString('base64');
const request = {
encryptedKey: encode(encryptedKey),
encryptedIV: encode(encryptedIV),
encryptedPayload: encode(encryptedPayload),
signature: encode(signature)
};
const endPoint = require('./end-point');
endPoint.call(request);
//-> Server successfully verifies signature but fails to decrypt encrypted KEY and IV
Could someone point me to where I am doing it wrong?

Do string decryption require guid and iv (initialization vector)

I am doing string encryption like below using the guid and storing the cipherout.ciphertext value but not storing the cipherout.iv value.
But when decrypting , why cant i just pass the key , instead of passing both key and cipherout.iv values. Because it is asking for both values while decrypting it. Do i need to store both key and cipherout.iv values for decrypting?
Please advice
FIRSTFILE.js :
var guid = "4ab23a136dc347d";
var inputString = "sometext";
// Create the key
var key = crypto.createSecretKey({guid:guid, encoding:encode.Encoding.UTF_8});
// Encrypt
var cipher = crypto.createCipher({algorithm: crypto.EncryptionAlg.AES, key: key});
cipher.update({input: inputString, inputEncoding: encode.Encoding.UTF_8});
var cipherout = cipher.final({outputEncoding: encode.Encoding.HEX});```
SECONDFILE.JS
// Decrypt
var decipher = crypto.createDecipher({algorithm: crypto.EncryptionAlg.AES, key: key, iv:cipherout.iv}); //HERE
decipher.update({input: cipherout.ciphertext, inputEncoding: encode.Encoding.HEX});
var decipherout = decipher.final({outputEncoding: encode.Encoding.UTF_8});```
Yes, You need to store both. Without initial vector it's not possible to decrypt message even with password. The idea is: password can be same for all messages, but IV is created when it was encrypted and it's different always.
IV can be encrypted with 4096 RSA, but all message cab be too long for RSA and you encryp it with AES.
AES is not good from security perspective, so we need use password AND one-time-key - initial vector.

Is it secure to encrypt and save third party API secret of users in Database?

I want to store third party API secret (like AWS) of my users in database. Leakage of which can lead to a potential loss to my users. It should be kept confidential.
So how can I secure their data on NodeJS platform? I'm thinking to encrypt and store the store the secret key so it will not directly visible to intruders.
Any suggestion on how more security can be maintained?
var crypto = require('crypto');
var assert = require('assert');
var algorithm = 'aes256'; // or any other algorithm supported by OpenSSL
var key = 'password';
var text = 'I love kittens';
var cipher = crypto.createCipher(algorithm, key);
var encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
var decipher = crypto.createDecipher(algorithm, key);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') +
decipher.final('utf8');
console.log(encrypted);
console.log(decrypted);
assert.equal(decrypted, text);
Using crypto.createCipheriv is defiantly one way to keep it more secure. Generate a random IV and store it. Also you can add salt to make your encryption more secure.

Resources