how to use Crypto to encrypt and decrypt binary data in Nodejs - node.js

I have new to use Crypto to encrypt an audio data in NodeJs. I got some error output when I trying to decrypt the data. here is my test code.
function encrypt (buf, key) {
const cipher = crypto.createCipheriv('des-ecb', key, new Buffer(0))
let c = cipher.update(Buffer.from(buf))
c += cipher.final('binary')
return c
}
function decrypt (buf, key) {
const cipher = crypto.createDecipheriv('des-ecb', key, new Buffer(0))
let c = cipher.update(buf)
c += cipher.final('binary')
return c
}
let pcmbuf = fs.readFileSync("test.pcm")
let enc = encrypt(pcmbuf,gen_key())
let dec = decrypt(enc,gen_key())
fs.writeFileSync('dec.pcm',dec)
It has produced an Error when I running this code. The detail is like bellow.
internal/crypto/cipher.js:104
var ret = this._handle.final();
^
Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
at Decipheriv.final (internal/crypto/cipher.js:104:26)
at decrypt (/home/zsc/asr-js/test.js:60:17)
at Object.<anonymous> (/home/zsc/asr-js/test.js:67:11)
at Module._compile (module.js:660:30)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
at Function.Module._load (module.js:505:3)
at Function.Module.runMain (module.js:701:10)
at startup (bootstrap_node.js:190:16)
How could I fix that problem. thanks for answer my problem!

Apparently the problem is with the padding...because of different languages of encrypting/decrypting...the answer is here on node.js github issues page
Quoting the answer
So calling decrypt.setAutoPadding(false); after you create the decipher instance will make it work as expected:
var crypto = require('crypto');
//the encrypted result
var theCipher = "ccZmMULq3tlzAY+iafZz+96xz+qFsAuGpEjhN7CckJTcdBT03fgobfSVGCGYzILyPNSA3e3msUqHUTCpv8kRnWvFdLv9c+GTEhg+Lj5dOThGDHtkQX2j5bd6Eubw9/l+Lcwj0PeyW0ZoVkB5Nnp1yCnmKAn2Euliq+IurgthT+wln6cQmTjXfL4IB5VxwUEb72FcbeiCfbKxa+MxxbcQTCpli3ErSptwdp9on2k87JTPFqyyMmMRFA9VgOXpHNe43IwFzME01DyHZ+Rp/eQguTmY9FtkFIZeD2e2nrbbDbW6tlk/KOtdhGVIlIGMPNS5m8LYqlrGZlJU3JythEy+J0z1wW1owjVe9Yto2OtUe8WeKI744enBKAX4FnD4My7+/XRjbF5kf6loT9lqeMCdXFb3LDej3GVcKWbJuZjXmD4="
var key = "abcdefghijklmnopqrstuvwx"
var decrypt = crypto.createDecipheriv('des-ede3', key, "");
//Add the auto padding here....IT HAVE TO BE after creating the decipher immeditely
decrypt.setAutoPadding(false);
var s = decrypt.update(theCipher, 'base64', 'utf8');
console.log(s + decrypt.final('utf8'));

Finally. I found a way to solve that case. Pass binary encoded string as the parameter instead pass buffer. Code as below.
function encrypt (buf, key) {
const cipher = crypto.createCipheriv('des-ecb', key, new Buffer(0))
let c = cipher.update(buf,'binary','base64')
c += cipher.final('base64')
return c
}
function decrypt (buf, key) {
const cipher = crypto.createDecipheriv('des-ecb', key, new Buffer(0))
let c = cipher.update(buf,'base64','base64')
c += cipher.final('base64')
return c
}
let pcmbuf = fs.readFileSync("test.pcm")
let enc = encrypt(pcmbuf.toString('binary'),gen_key())
let dec = decrypt(enc,gen_key())
fs.writeFileSync('dec.pcm',Buffer.from(dec,'base64'))

Related

Creating a PEM file from javascript looks different from Java causing a failed signature verification

I'm trying to verify a SHA1 with RSA signature with its public key, I have written the java code below which works perfectly but but javascript cannot verify when it uses same modulus and exponent which is created by crypto or ursa. though If i use it against the pem file created by java it works.
any idea or suggestion of the best way of converting the java code to javascript?
JAVA:
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
public class Verify {
static public Boolean verifySignature(String modulus, String exponent , String encodedMsg, String encodedSignature, String signatureAlgorithm)
throws Exception {
try {
RSAPublicKeySpec senderPublicKeySpec =
new RSAPublicKeySpec(
new BigInteger(modulus, 16),
new BigInteger(exponent, 16)
);
RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance(Constants.RSA_KEY).generatePublic(senderPublicKeySpec);
System.out.println("------------ pem file ----------------");
System.out.println("format:" + rsaPublicKey.getFormat());//x.509
byte[] data = rsaPublicKey.getEncoded();
rsaPublicKey.getFormat();
String base64encoded = new String(Base64.getEncoder().encode(data));
System.out.println(base64encoded);
System.out.println("----------------------------");
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(rsaPublicKey);
signature.update(encodedMsg.getBytes());
//this returns true;
return signature.verify(Base64.getDecoder().decode(encodedSignature.getBytes()));
} catch (Exception invaKeySpeExp) {
throw new Exception(invaKeySpeExp.getMessage());
}
}
Javascript:
'use strict';
var ursa = require('ursa');
function verifySignature( modulus, exponent, encodedMsg) {
var pem = fs.readFileSync('./java.pem');
var publicKeyNode = ursa.createPublicKeyFromComponents(new Buffer(modulus,'binary'), new Buffer(exponent,'binary'));
var publicKeyJava = ursa.createPublicKey(pem);
var sig = ursa.createVerifier(signatureAlgorithm);
sig.update(encodedMsg);
var successJ = sig.verify(publicKeyJava , new Buffer(encodedSignature, 'base64'), 'base64');
console.log(successJ);
var successN = sig.verify(publicKeyNode , new Buffer(encodedSignature, 'base64'), 'base64');
console.log(successN);
}
OUTPUT:
true //from verifying against PEM file created by Java
crypto.js:126 //from Verifying against modulus and exponent
return this._handle.digest(outputEncoding);
^
Error: Not initialized
at Error (native)
at Hash.digest (crypto.js:126:23)
at Object.verify (/Users/aemami/WebstormProjects/temp/node_modules/ursa/lib/ursa.js:705:39)
at verifySignature (/Users/aemami/WebstormProjects/temp/index.js:28:22)
at Object.<anonymous> (/Users/aemami/WebstormProjects/temp/index.js:64:1)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)

How to create an RSA key from hex-encoded modulus and encrypt in node.js

I'm finding the way to RSA encryption in node.js, I want to handle big numbers, so I use this: https://github.com/eschnou/node-bignumber
What I'm going to do is just encryption, with modulus and exponent. I don't have to do decryption. anyway, I got an error on and on, but I don't know what's wrong. Can anyone know what it is wrong? Thanks.
Code
require("node-bignumber");
var nvalue="ad6eb61316ff805e9c94667ab04aa45aa3203eef71ba8c12afb353a5c7f11657e43f5ce4483d4e6eca46af6b3bde4981499014730d3b233420bf3ecd3287a2768da8bd401f0abd7a5a137d700f0c9d0574ef7ba91328e9a6b055820d03c98d56943139075d";
var evalue="010001";
var encpw="";
var rsa = new RSAKey;
function encryptMessage() {
var message = "All your bases are belong to us.";
rsa.setPublic(evalue, nvalue);
encpw.value = rsa.encrypt(message);
console.log(encpw);
}
encryptMessage();
Error
ReferenceError: RSAKey is not defined
at Object.<anonymous> (c:\Users\win\Desktop\untitled\juntae.js:5:15)
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:311:12)
at Function.Module.runMain (module.js:467:10)
at startup (node.js:136:18)
at node.js:963:3
You should be able to load an RSA public key from modulus and public exponent in this way:
var rsa = require("node-bignumber");
var n = "ad6eb61316ff805e9c94667ab04aa45aa3203eef71ba8c12afb353a5c7f11657e43f5ce4483d4e6eca46af6b3bde4981499014730d3b233420bf3ecd3287a2768da8bd401f0abd7a5a137d700f0c9d0574ef7ba91328e9a6b055820d03c98d56943139075d";
var e = "010001";
var pub = new rsa.Key();
pub.setPublic(n, e);
var message = "Message";
var encrypted = pub.encrypt(message);
console.log(encrypted);
The public key in RSA consists of the modulus and the public exponent, because both values are needed during encryption.

node-forge package error with RSA enc/dec

I'm using node-forge npm on my meteor.js web app. I'm trying to do RSA encryption on some plain text according to this example: https://github.com/digitalbazaar/forge#rsa
The problem arrives when I want to decrypt the ciphertext where I want to get back the plaintext where it says the encryption block is invalid.
Following the example, I have to encrypt the bytes from the string so all that is done, but I don't understand why it fails on decryption? Any guesses?
rsaEncrypt:function(pubPem,privPem,plainText){
console.log(plainText);
var str = plainText;
var bytes = [];
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
console.log("BAJTOVI:");
console.log(bytes);
var publicKey = pki.publicKeyFromPem(pubPem);
console.log(publicKey);
var encrypted = publicKey.encrypt(bytes);
console.log("Encryption: ");
console.log(encrypted);
var privateKey = pki.privateKeyFromPem(privPem);
var decrypted = privateKey.decrypt(encrypted);
console.log("Decryption: ");
console.log(decrypted);
function bin2String(decrypted) {
var result = "";
for (var i = 0; i < decrypted.length; i++) {
result += String.fromCharCode(parseInt(decrypted[i], 2));
}
return result;
}
console.log("OPET TEXT:");
console.log(result);
return decryted;
},
The problem i get from the server:
Exception while invoking method 'rsaEncrypt' Error: Encryption block is invalid.
I20151110-21:22:05.279(1)? at Object._decodePkcs1_v1_5 [as decode] (/Users/mrcina/Meteor Projects/cryptonic/.meteor/local/isopacks/npm-container/npm/node_modules/node-forge/js/rsa.js:1446:11)
I20151110-21:22:05.279(1)? at Object.key.decrypt (/Users/mrcina/Meteor Projects/cryptonic/.meteor/local/isopacks/npm-container/npm/node_modules/node-forge/js/rsa.js:1083:19)
I20151110-21:22:05.279(1)? at [object Object].Meteor.methods.rsaEncrypt (server/methods.js:49:32)
I20151110-21:22:05.279(1)? at maybeAuditArgumentChecks (livedata_server.js:1698:12)
I20151110-21:22:05.279(1)? at livedata_server.js:708:19
I20151110-21:22:05.279(1)? at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20151110-21:22:05.279(1)? at livedata_server.js:706:40
I20151110-21:22:05.279(1)? at [object Object]._.extend.withValue (packages/meteor/dynamics_nodejs.js:56:1)
I20151110-21:22:05.280(1)? at livedata_server.js:704:46
I20151110-21:22:05.280(1)? at tryCallTwo (/Users/mrcina/.meteor/packages/promise/.0.5.1.1wnrf8h++os+web.browser+web.cordova/npm/node_modules/meteor-promise/node_modules/promise/lib/core.js:45:5)
THis line: var decrypted = privateKey.decrypt(encrypted);
Forge doesn't use arrays of integers to represent bytes; it uses binary-encoded strings. Try simplifying your code to the following:
rsaEncrypt:function(pubPem,privPem,plainText){
console.log(plainText);
var publicKey = pki.publicKeyFromPem(pubPem);
console.log(publicKey);
var encrypted = publicKey.encrypt(forge.util.encodeUtf8(plainText));
console.log("Encryption: ");
console.log(encrypted);
var privateKey = pki.privateKeyFromPem(privPem);
var decrypted = forge.util.decodeUtf8(privateKey.decrypt(encrypted));
console.log("Decryption: ");
console.log(decrypted);
return decrypted;
}

Error handling in using Crypto in Node.js

I am using Crypto library of Node.js for encryption/decryption as follows :
encrypt = function(text, passPhrase){
var cipher = crypto.createCipher('AES-128-CBC-HMAC-SHA1', passPhrase);
var crypted = cipher.update(text,'utf8','hex');
crypted += cipher.final('hex');
return crypted;
} ,
decrypt = function(text, passPhrase){
var decipher = crypto.createDecipher('AES-128-CBC-HMAC-SHA1', passPhrase)
var dec = decipher.update(text,'hex','utf8')
dec += decipher.final('utf8');
return dec;
}
There is no problem with encryption part. and if I send the correct passPhrase for decryption there is also no problem. My problem is,if I send 'wrong' passPhrase for decryption, code breaks and throw an error :
TypeError: Bad input string
at Decipher.Cipher.update (crypto.js:279:27)
at module.exports.decrypt (/xxxx/yyyyy/jjj/ssss/encryptionService.js:19:28)
at Object.module.exports.passwordDecryptor (/xxxx/yyyyy/jjj/ssss/encryptionService.js:59:56)
at Object.<anonymous> (/xxxx/yyyyy/jjj/ssss/test.js:32:33)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
I dont want it happen. I want for example decrypt function returns 'Passpharse is wrong' sentence. According to the documentation enter link description here createDecipher
function doesn't accept a callback function.
I solved the problem with try and catch. (The callback function doesn't work.)
decrypt = function(text, passPhrase){
var decipher = crypto.createDecipher('AES-128-CBC-HMAC-SHA1', passPhrase);
try {
var dec = decipher.update(text,'hex','utf8');
dec += decipher.final('utf8');
return dec;
} catch (ex) {
console.log('failed');
return;
}
}

NodeJS Decrypt des3 Unicode

I have the following snippet of code
var crypto = require("crypto");
var iv = new Buffer('d146ec4ce3f955cb', "hex");
var key = new Buffer('dc5c3319dc25c1f6f11f6a792a6dd28864c9dd48be26c2e4', "hex");
var encrypted = new Buffer('6A57201D19B07ABFAE74B453BA46381C', "hex");
var cipher = crypto.createDecipheriv('des3', key, iv);
var result = cipher.update(encrypted);
result += cipher.final();
console.log("result: " + result);
The result is "password"
This snippet works great for ASCII based words.
However, I have some unicode passwords.
So for instance this is Pi:
UU__3185CDAA15C1CDED
I have tried using this value, plus the removal of the "UU__" but no gain.
I also tried something like this for the encrypted data:
var encrypted = new Buffer('UU__3185CDAA15C1CDED', "utf16le");
and
var result = cipher.update(encrypted, 'ucs2');
but no go..
I get the following error
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decr ypt
at Error (native)
at Decipheriv.Cipher.final (crypto.js:202:26)
at Object.<anonymous> (/Users/miker/Local Projects/rec10_decryption/server2.js:14:18)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
Any assistance would be greatly appreciated.
Dropping the UU_ prefix and using this code works for me:
var crypto = require('crypto');
var iv = new Buffer('d146ec4ce3f955cb', 'hex');
var key = new Buffer('dc5c3319dc25c1f6f11f6a792a6dd28864c9dd48be26c2e4', 'hex');
var encrypted = new Buffer('3185CDAA15C1CDED', 'hex');
var cipher = crypto.createDecipheriv('des3', key, iv);
var result = Buffer.concat([
cipher.update(encrypted),
cipher.final()
]).toString('ucs2');
console.log('result: ' + result);
// outputs: result: Π
When you do result += cipher.final(), it's first converting result from a Buffer to a (utf8) string, and then appending cipher.final() converted from a Buffer to a (utf8) string. When you have multi-byte characters, this can cause data corruption if you have a character's bytes span across calls to .update() and .final(). Keeping them as Buffers, concatenating them as binary, and then converting the final concatenated result to a utf16 string will work and is much safer.

Resources