How to generate crypto.createCipheriv arguments from static source - javascript

I am using the below code to encrypt strings in my node.js code.
I would like to understand how to generate KEY and HMAC_KEY from a static source. In my program, it's generated randomly as of now. As it's generated randomly, I am not able to encrypt my database password using the below algorithm.
crypto = require('crypto');
ALGORITHM = "AES-256-CBC";
HMAC_ALGORITHM = "SHA256";
KEY = crypto.randomBytes(32);
HMAC_KEY = crypto.randomBytes(32);
function (plain_text) {
var IV = new Buffer(crypto.randomBytes(16)); // ensure that the IV (initialization vector) is random
var cipher_text;
var hmac;
var encryptor;
encryptor = crypto.createCipheriv(ALGORITHM, KEY, IV);
encryptor.setEncoding('hex');
encryptor.write(plain_text);
encryptor.end();
cipher_text = encryptor.read();
hmac = crypto.createHmac(HMAC_ALGORITHM, HMAC_KEY);
hmac.update(cipher_text);
hmac.update(IV.toString('hex')); // ensure that both the IV and the cipher-text is protected by the HMAC
// The IV isn't a secret so it can be stored along side everything else
return cipher_text + "$" + IV.toString('hex') + "$" + hmac.digest('hex')
};

You have to split your code into two executions:
Code that generates your keys and presents them in a storable format
KEY = crypto.randomBytes(32);
HMAC_KEY = crypto.randomBytes(32);
console.log(KEY.toString('hex'));
console.log(HMAC_KEY.toString('hex'));
Code that uses the stored keys
KEY = Buffer.from('some key string', 'hex');
HMAC_KEY = Buffer.from('some other key string', 'hex');
You just have to make sure that your keys aren't actually in your code, but rather in some file, because hardcoding key in code and checking them into your version control system is a bad idea and might give your developers access to production systems which they probably shouldn't have.

Related

ScryptSync Key Cannot Be Used in createCipheriv, Nodejs, Crypto lib

I am new to topics related to encoding and I am having troubles with being able to convert my scryptsync key into something createCipheriv (Crypto library integrated into Nodejs) can use in the iv parameter.
const algorithm = 'aes-256-gcm';
var text = 'default'
var encrypted = secret;
class Auth
{
SignUp(pass)
{
console.log(pass);
var pair = ec.genKeyPair();
text = pair.getPrivate.toString('hex');
var key = crypto.scryptSync(pass, 'baethrowssalt', 32);
console.log(`The key is:${key}`); //this is not a string
key=key.toString('hex');
var cipher = crypto.createCipheriv(algorithm, key);
var encrypted = cipher.update(text, 'hex', 'hex') + cipher.final('hex');
fs.writeFileSync(file, encrypted);
return alert(`Close and reopen your app to integrate your wallet securely`);
}
as you can see above, I tried converting the scryptSync return to string hex so that createCipheriv is willing to use it, but I get the following error:
pass is passrig
passrig
The key is:�→A�r;yR�����▲�h�8��f�����v�A�,
TypeError [ERR_INVALID_ARG_TYPE]: The "iv" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
at Cipheriv.createCipherWithIV (internal/crypto/cipher.js:120:29)
at new Cipheriv (internal/crypto/cipher.js:227:22)
at Object.createCipheriv (crypto.js:117:10)
So my question contains two parts: how can I use scryptSync with the createCipheriv? To help me understand, what are the reasons behind your solutions?
I've created an example that encrypts some data using the algorithm aes-256-gcm and uses crypto.scryptSync to derive the key from a password.
The steps are as follows:
Derive our key from our password using crypto.scryptSync.
Create an IV using crypto.randomBytes.
Use our key and iv to encrypt the plaintext using our encrypt function.
Test our encrypted data by decrypting using our decrypt function.
This code is as follows:
const crypto = require("crypto");
const Algorithm = "aes-256-gcm";
function encrypt(plainText, key, iv) {
const cipher = crypto.createCipheriv(Algorithm, key, iv);
return { encrypted: Buffer.concat([cipher.update(plainText), cipher.final()]), authTag: cipher.getAuthTag() }
}
function decrypt(encrypted, key, iv, authTag) {
const decipher = crypto.createDecipheriv(Algorithm, key, iv).setAuthTag(authTag);
return Buffer.concat([decipher.update(encrypted), decipher.final()]);
}
const password = "Speak Friend and Enter";
const plainText = "There is nothing either good or bad but thinking makes it so.";
const salt = crypto.randomBytes(32);
// Create an encryption key from our password, ensuring it is 32 bytes long - AES-256 needs a 256 bit (32 byte) key
const KEY = crypto.scryptSync(password, salt, 32);
const IV = crypto.randomBytes(16);
console.log("Key (derived from password, hex):", KEY.toString("hex"));
console.log("IV (hex):", IV.toString("hex"));
console.log("Plaintext:", plainText);
const { encrypted, authTag } = encrypt(plainText, KEY, IV);
console.log("Encrypted (hex):", encrypted.toString("hex"));
const decrypted = decrypt(encrypted, KEY, IV, authTag)
console.log("Decrypted:", decrypted.toString("utf-8"));

How to implement this encryption related code in JavaScript?

I have following code in Java.
String secretString = 'AAABBBCCC'
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom securerandom = SecureRandom.getInstance("SHA1PRNG");
securerandom.setSeed(secretString.getBytes());
kgen.init(256, securerandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] byteContent = content.getBytes("utf-8");
byte[] cryptograph = cipher.doFinal(byteContent);
String enc1 = Base64.getEncoder().encodeToString(cryptograph);
return enc1;
I need to implement it in JavaScript/Node.js, however I can only figure out the last half in js like below
'use strict';
const crypto = require('crypto');
const ALGORITHM = 'AES-256-ECB';
const secretString = 'AAABBBCCC'
// missing part in JS (how to convert secretString to key)
function encrypt(plaintext, key) {
const cipher = crypto.createCipheriv(ALGORITHM, key, Buffer.alloc(0));
return cipher.update(plaintext, 'utf8', 'base64') + cipher.final('base64');
}
For the previous Java part( from secretString to key generated by KeyGenerator), I don't know how to implement it in JavaScript, neither do I know whether there is a thing like KeyGenerator in JavaScript world could help me to do the heavy lifting work.
I think this is what you're after:
const crypto = require("crypto-js");
// Encrypt
const ciphertext = crypto.AES.encrypt('SOMETHING SECRET', 'secret key 123');
// Decrypt
const bytes = crypto.AES.decrypt(ciphertext.toString(), 'secret key 123');
const decryptedData = bytes.toString(crypto.enc.Utf8);
console.log(decryptedData);
https://runkit.com/mswilson4040/5b74f914d4998d0012cccdc0
UPDATE
JavaScript does not have a native equivalent for key generation. The answer is to create your own or use a third party module. I would recommend something like uuid for starters.
You can use crypto.randomBytes().
As per its documentation:
Generates cryptographically strong pseudo-random data. The size argument is a number indicating the number of bytes to generate.
Also, it uses openssl's RAND_bytes API behind scenes

Uncaught URIError: URI malformed error with jQuery

I'm using node forge to encrypt a form before sending it to the server using AES.
The code for the crypto part for now is
const bigInt = require("big-integer");
const forge = require('node-forge');
function generateParams() {
// Cryptographic random number generator
var array = new Uint32Array(2);
var _key = bigInt(window.crypto.getRandomValues(array)[0]).toString();
var _iv = bigInt(window.crypto.getRandomValues(array)[1]).toString();
// generate random key and IV
var key = forge.util.encode64(_key);
var iv = forge.util.encode64(_iv);
const params = {
key: key,
iv: iv
}
return params;
}
function encrypt(params) {
var cipher = forge.rc2.createEncryptionCipher(params.key);
cipher.start(params.iv);
// Encrypting "testing"
cipher.update(forge.util.createBuffer("testing"));
cipher.finish();
return cipher.output;
}
function decrypt(params, encrypted) {
var cipher = forge.rc2.createDecryptionCipher(params.key);
cipher.start(params.iv);
cipher.update(encrypted);
cipher.finish();
return cipher.output;
}
and the jQuery function is (not posting yet)
$('#recordForm').submit(function(event) {
// Stop form from submitting normally
event.preventDefault();
// Grab form data
// Crypto
const params = generateParams();
const encryptedForm = {
test: encrypt(params),
}
console.log("Encrypted: " + encryptedForm.test);
const decryptedForm = {
test: decrypt(params, encryptedForm.id).data,
}
console.log("Decrypted: " + decryptedForm.test);
});
My problem is that I keep getting back (cryptob.js is the name of my file, generated with browserify)
Uncaught URIError: URI malformed
at decodeURIComponent (<anonymous>)
at Object.util.decodeUtf8 (cryptob.js:24437)
at ByteStringBuffer.util.ByteStringBuffer.toString (cryptob.js:23490)
at HTMLFormElement.<anonymous> (cryptob.js:1282)
at HTMLFormElement.dispatch (jquery-3.1.1.slim.min.js:3)
at HTMLFormElement.q.handle (jquery-3.1.1.slim.min.js:3)
when calling encrypt().
There is this answer here which recommends including a special meta tag. I have done that but it still doesn't work. Since some resources online say it is related to UTF-8 encoding, I tried replacing
cipher.update(forge.util.createBuffer("testing"));
with
cipher.update(forge.util.createBuffer(encodeURIComponent("testing")));
or
cipher.update(forge.util.createBuffer("testing", 'utf8'));
but it didn't work either (based on encodeURIComponent(str)).
You can test forge here, and if you run this code (which is essentially what I'm doing)
var forge = require("node-forge")
// generate a random key and IV
var key = forge.util.encode64("12354523465");
var iv = forge.util.encode64("2315");
// encrypt some bytes
var cipher = forge.rc2.createEncryptionCipher(key);
cipher.start(iv);
cipher.update(forge.util.createBuffer("testing"));
cipher.finish();
var encrypted = cipher.output;
console.log(encrypted);
// decrypt some bytes
var cipher = forge.rc2.createDecryptionCipher(key);
cipher.start(iv);
cipher.update(encrypted);
cipher.finish();
console.log(cipher.output.data)
it works fine.
How can I solve this?
It looks like this error is actually happening in the toString, where you generate your _key and _iv.
Try testing with some hard-coded strings, as used in the example code you posted. Then, use a method to generate random byte strings for the key and IV.
Also, for AES-256, the key should have 32 bytes (not bits) of entropy. The IV should have 16 bytes of entropy.

Decrypting CryptoJS AES data (with passphrase) in Forge.js

So I have a piece of code which encrypts a string with a passphrase. It uses the CryptoJS AES encrypt function (CryptoJS.AES.encrypt) and looks like this...
CryptoJS.AES.encrypt(data, password).toString();
Going forward, I don't want to be using CryptoJS, as it's officially deprecated/not maintained, and I would instead like to use Forge.js. I've attempted to read through the Forge.js docs on GitHub to find a solution, but haven't been able to find anything which uses passphrases instead of manually creating the key & IV.
I've taken a look at the CryptoJS archive at https://code.google.com/archive/p/crypto-js/ and it seems that if the encrypt function is passed a string as the second argument (key) it's used as a passphrase to derive a key and IV. But it doesn't detail how it does this.
It seems that base64 decoding the result gives a string that starts with Salted__ then a comma and then the encrypted blob of binary text, and I'm unsure even how I would pass the "salt" through to Forge.
How would I go about decrypting this blob of data using Forge.js only?
CryptoJS supports OpenSSL's EVP_BytesToKey function, which derives a key and IV from a freshly generated salt and password with one round of MD5. There is an example on the forge documentation page:
Using forge in node.js to match openssl's "enc" command line tool
(Note: OpenSSL "enc" uses a non-standard file format with a custom key
derivation function and a fixed iteration count of 1, which some
consider less secure than alternatives such as OpenPGP/GnuPG):
var forge = require('node-forge');
var fs = require('fs');
// openssl enc -des3 -in input.txt -out input.enc
function encrypt(password) {
var input = fs.readFileSync('input.txt', {encoding: 'binary'});
// 3DES key and IV sizes
var keySize = 24;
var ivSize = 8;
// get derived bytes
// Notes:
// 1. If using an alternative hash (eg: "-md sha1") pass
// "forge.md.sha1.create()" as the final parameter.
// 2. If using "-nosalt", set salt to null.
var salt = forge.random.getBytesSync(8);
// var md = forge.md.sha1.create(); // "-md sha1"
var derivedBytes = forge.pbe.opensslDeriveBytes(
password, salt, keySize + ivSize/*, md*/);
var buffer = forge.util.createBuffer(derivedBytes);
var key = buffer.getBytes(keySize);
var iv = buffer.getBytes(ivSize);
var cipher = forge.cipher.createCipher('3DES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input, 'binary'));
cipher.finish();
var output = forge.util.createBuffer();
// if using a salt, prepend this to the output:
if(salt !== null) {
output.putBytes('Salted__'); // (add to match openssl tool output)
output.putBytes(salt);
}
output.putBuffer(cipher.output);
fs.writeFileSync('input.enc', output.getBytes(), {encoding: 'binary'});
}
// openssl enc -d -des3 -in input.enc -out input.dec.txt
function decrypt(password) {
var input = fs.readFileSync('input.enc', {encoding: 'binary'});
// parse salt from input
input = forge.util.createBuffer(input, 'binary');
// skip "Salted__" (if known to be present)
input.getBytes('Salted__'.length);
// read 8-byte salt
var salt = input.getBytes(8);
// Note: if using "-nosalt", skip above parsing and use
// var salt = null;
// 3DES key and IV sizes
var keySize = 24;
var ivSize = 8;
var derivedBytes = forge.pbe.opensslDeriveBytes(
password, salt, keySize + ivSize);
var buffer = forge.util.createBuffer(derivedBytes);
var key = buffer.getBytes(keySize);
var iv = buffer.getBytes(ivSize);
var decipher = forge.cipher.createDecipher('3DES-CBC', key);
decipher.start({iv: iv});
decipher.update(input);
var result = decipher.finish(); // check 'result' for true/false
fs.writeFileSync(
'input.dec.txt', decipher.output.getBytes(), {encoding: 'binary'});
}
This example is shown for Triple DES, but it works in the same way for AES. You just have to change the ivSize to 16.

Encrypting data with forgejs on the client side, and decrypting with ruby

For a given project, I'm looking to encrypt a piece of data with AES 256, and then RSA encrypt the key. I've been using Forge and the Encryptor gem in ruby, and i can't seem get matching encryption values:
var key = 'strengthstrengthstrengthstrength';
var iv = 'cakecakecakecakecakecakecakecake';
var cipher = forge.aes.createEncryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(forge.util.createBuffer("some string"));
cipher.finish();
var encrypted = cipher.output;
console.log(btoa(encrypted.data)); // outputs: CjLmWObDO2Dlwa5tJnRBRw==
Then in IRB:
Encryptor.encrypt 'some string', :key => 'strengthstrengthstrengthstrength', :key => 'cakecakecakecakecakecakecakecake'
Base64.encode64 _
# outputs: C9Gtk9YfciVMJEsbhZrQTw==\n
Over using string values for Key & IV, tried:
var key = forge.random.getBytesSync(32);
var iv = forge.random.getBytesSync(32);
Then doing a btoa() call on each of them. Using the Base64.decode64 on the ruby side, before passing them to Encryptor.decrypt, but still no luck.
Any idea where i've gone wrong?
I managed to get it to work. Since i'm just using one key to encrypt one value, i just used the key as the IV & Salt as well. This is not recommended if you are using the key to encrypt multiple values. You will then need to generate proper salt & iv values.
Also the gen key value is a pretty poor way, as Math.random is not secure. Just ran out of time to do this properly, but works okay for this case.
var Encryption = (function () {
var api = {
getKey: function () {
var possible = "ABCDEFabcdef0123456789";
var key = '';
for (var i = 0; i < 32; i++) {
key += possible.charAt(Math.floor(Math.random() * possible.length));
}
return key;
},
encryptPII: function (rawKey, value) {
var salt = rawKey;
var iv = rawKey;
var key = forge.pkcs5.pbkdf2(rawKey, salt, 2000, 32);
var cipher = forge.aes.createEncryptionCipher(key, 'CBC');
cipher.start(iv);
cipher.update(forge.util.createBuffer(value));
cipher.finish();
return btoa(cipher.output.data);
}
};
return api;
})();
The rawKey is the value returned from getKey(). The value property is the the string to be encrypted. I use the rawkey for iv & salt values, generate a key in the same way that the Encryptor gem in ruby does. Use forge to then encrypt the string value.
If i take the base64, decode it in ruby, and pass the same rawKey value to the encryptor gem for key, salt and iv, it works.

Categories