Encrypting data with ruby decrypting with node
OK. I want to thank everyone for helping me out. Basically this thread here answers my question: https://github.com/joyent/node/issues/1395. I am going to go ahead and post the two programs in case anyone else has to go through this rigamarole. Keep in mind this isn't mean to be hardcore secure, this is a stepping stone for ruby encrypting data and node decrypting it. You will have to take more steps to make sure higher security measures are taken.
The code is located at this gist: https://gist.github.com/799d6021890f34734470
These were run on ruby 1.9.2p290 and node 0.4.10
Reading Ruby Active Record Encryption from a Node Script
The NodeJS code returns the same results as the Ruby code if
- no key derivation (SHA256) is used
- for the ciphertext a Base64 encoding is applied instead of a hex encoding
Full NodeJS code:
var crypto = require('crypto');
const algorithm = 'aes-256-gcm';
function encrypt(secret, originalData) {
console.log('encrypting with ', {
secret,
originalData
});
const iv = Buffer.from('377YrtFzBASSjvol', 'base64'); // crypto.randomBytes(16); // disabled for testing (in the final solution a random nonce MUST be applied for security reasons)
var crypt = crypto.createCipheriv(algorithm, Buffer.from(secret, 'base64'), iv);
var encoded = crypt.update(originalData, 'utf8', 'base64'); // Fix: Base64 encoding
encoded += crypt.final('base64'); // Fix: Base64 encoding
const at = crypt.getAuthTag();
return { encrypted: encoded, iv: iv.toString('base64'), at: at.toString('base64') };
};
function decrypt(secret, encryptedData, iv, at) {
console.log('decrypting with ', {
secret,
encryptedData,
iv,
at
});
var crypt = crypto.createDecipheriv(algorithm, Buffer.from(secret, 'base64'), Buffer.from(iv, 'base64'));
crypt.setAuthTag(Buffer.from(at, 'base64'));
var decoded = crypt.update(encryptedData, 'base64', 'utf8'); // Fix: Base64 encoding
decoded += crypt.final('utf8');
return decoded;
};
var originalData = 'This is some debug text';
var secret = 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs='; // Fix: No key derivation
const encryptionResult = encrypt(
secret,
originalData,
);
console.log(encryptionResult);
var secret = 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs='; // Fix: No key derivation
var encryptedData = 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=';
var iv = '377YrtFzBASSjvol';
var at = 'QqQcyOhGKPP0sPFOANToFw==';
const decryptionResult = decrypt(
secret,
encryptedData,
iv,
at
);
console.log(decryptionResult);
Output:
encrypting with {
secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
originalData: 'This is some debug text'
}
{
encrypted: 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=',
iv: '377YrtFzBASSjvol',
at: 'QqQcyOhGKPP0sPFOANToFw=='
}
decrypting with {
secret: 'eKAyTQoTH0apq8+fsQYHSYelUy3mL34gl2M+rnvpXhs=',
encryptedData: 'LtbIG7tnBVtbMUlnvG+qtmoK2NgL+18=',
iv: '377YrtFzBASSjvol',
at: 'QqQcyOhGKPP0sPFOANToFw=='
}
This is some debug text
how to encode and decode a json array to base64 in ruby
So the following should work for you: (I used the structure from the comment rather than the Post)
JS:
var obj = [{"edmc":"78787041","house_no":"987654234","type":"0","abc_type":"AABB","bbcname":"Unknown","abc_nick_name":"notKnown", "allot":"1.00", "hhic":"BAC1235","address1":"abcdw","city":"abcde", "state":"UU", "pincode":"123456", "email":"anccss@gmail.com","phone":"1234567898"}];
btoa(JSON.stringify(obj));
Results in:
W3siZWRtYyI6Ijc4Nzg3MDQxIiwiaG91c2Vfbm8iOiI5ODc2NTQyMzQiLCJ0eXBlIjoiMCIsImFiY190eXBlIjoiQUFCQiIsImJiY25hbWUiOiJVbmtub3duIiwiYWJjX25pY2tfbmFtZSI6Im5vdEtub3duIiwiYWxsb3QiOiIxLjAwIiwiaGhpYyI6IkJBQzEyMzUiLCJhZGRyZXNzMSI6ImFiY2R3IiwiY2l0eSI6ImFiY2RlIiwic3RhdGUiOiJVVSIsInBpbmNvZGUiOiIxMjM0NTYiLCJlbWFpbCI6ImFuY2Nzc0BnbWFpbC5jb20iLCJwaG9uZSI6IjEyMzQ1Njc4OTgifV0=
Ruby
obj = [{"edmc":"78787041","house_no":"987654234","type":"0","abc_type":"AABB","bbcname":"Unknown","abc_nick_name":"notKnown", "allot":"1.00", "hhic":"BAC1235","address1":"abcdw","city":"abcde", "state":"UU", "pincode":"123456", "email":"anccss@gmail.com","phone":"1234567898"}]
Base64.strict_encode64(obj.to_json)
Results in
W3siZWRtYyI6Ijc4Nzg3MDQxIiwiaG91c2Vfbm8iOiI5ODc2NTQyMzQiLCJ0eXBlIjoiMCIsImFiY190eXBlIjoiQUFCQiIsImJiY25hbWUiOiJVbmtub3duIiwiYWJjX25pY2tfbmFtZSI6Im5vdEtub3duIiwiYWxsb3QiOiIxLjAwIiwiaGhpYyI6IkJBQzEyMzUiLCJhZGRyZXNzMSI6ImFiY2R3IiwiY2l0eSI6ImFiY2RlIiwic3RhdGUiOiJVVSIsInBpbmNvZGUiOiIxMjM0NTYiLCJlbWFpbCI6ImFuY2Nzc0BnbWFpbC5jb20iLCJwaG9uZSI6IjEyMzQ1Njc4OTgifV0=
AES-256-CBC with Digest from Ruby to NodeJS
The critical missing piece is the IV, which is required when encryption/decryption is to be made across language boundaries as apparently the encrypter will generate a random IV (or something like that - still don't understand how Ruby decrypts the string without an IV.... but then what do I know....), if one is not provided.
The following snippets show how to encrypt a string in Ruby and decrypt in NodeJS.
#!/usr/bin/env ruby
require 'openssl'
require 'base64'
require 'openssl/cipher'
require 'openssl/digest'
aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc')
aes.encrypt
aes.key = Digest::SHA256.digest('IHazSekretKey')
aes.iv = '1234567890123456'
p Base64.encode64( aes.update('text to be encrypted') << aes.final )
The above prints: "eiLbdhFSFrDqvUJmjbUgwD8REjBRoRWWwHHImmMLNZA=\n"
#!/usr/bin/env node
var crypto = require('crypto');
function decrypto(toDecryptStr) {
var result,
encoded = new Buffer(toDecryptStr, 'base64'),
decodeKey = crypto.createHash('sha256').update('IHazSekretKey', 'ascii').digest(),
decipher = crypto.createDecipheriv('aes-256-cbc', decodeKey, '1234567890123456');
result = decipher.update(encoded);
result += decipher.final();
return result;
}
console.log(decrypto('eiLbdhFSFrDqvUJmjbUgwD8REjBRoRWWwHHImmMLNZA=\n'))
The JS script now properly decrypts the string.
One unfortunate side effect is that existing encrypted data will need to be decrypted and then re-encrypted with an IV that is then used in the decrypting implementation.
A PITA but nonetheless a working solution.
How to properly encode strings so to decrypt with CryptoJs in NodeJS?
- You are using the wrong encoders for data, key and IV. All three are Base64 encoded (and not hex or Utf8). So apply the Base64 encoder.
- The ciphertext must be passed to
CryptoJS.AES.decrypt()
as aCipherParams
object or alternatively Base64 encoded, which is implicitly converted to aCipherParams
object.
When both are fixed, the plain text is: "[\"001\",\"001\"]"
.
var symmetricKey = "bDKJVr5wFtQZaPrs4ZoMkP2RjtaYpXo5HHKbzrNELs8="
var symmetricNonce = "Z8q66bFkbEqQiVbrUrts+A=="
var dataToReceive = "hX/BFO7b+6eYV1zt3+hu3o5g61PFB4V3myyU8tI3W7I="
var decrypted = CryptoJS.AES.decrypt(
dataToReceive, // pass Base64 encoded
//{ciphertext: CryptoJS.enc.Base64.parse(dataToReceive)}, // pass as CipherParams object, works also
CryptoJS.enc.Base64.parse(symmetricKey),
{iv: CryptoJS.enc.Base64.parse(symmetricNonce)}
);
console.log(decrypted.toString(CryptoJS.enc.Utf8));
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Related Topics
Copy Output of a JavaScript Variable to the Clipboard
Finding All Indexes of a Specified Character Within a String
How to Invert (Transpose) the Rows and Columns of an HTML Table
Differencebetween 'Let' and 'Const' Ecmascript 2015 (Es6)
Understanding Asynchronous Code in Layman's Terms
How to Set a JavaScript Object Values Dynamically
How to Prevent the Backspace Key from Navigating Back
How to Detect Faces Using Ruby
Js Replace Not Working on String
Google Maps Places API V3 Autocomplete - Select First Option on Enter
Differencebetween Regexp's Exec() Function and String's Match() Function
The Best Way to Synchronize Client-Side JavaScript Clock with Server Date
(...()) VS. (...)() in JavaScript Closures
Jquery Beforeunload When Closing (Not Leaving) the Page
What Is Wkerrordomain Error 4 from Wkwebview