Decryption of Aes Created with Sjcl.Js in Ruby

Decryption of AES created with sjcl.js in ruby

For those coming here from Google, I managed to use the sjcl library to encrypt on Appcelerator's Titanium and decrypt on Ruby/Rails.

Encrypt (javascript):

var data = SJCL.encrypt('your key here', 
'plain text',
{ mode: 'gcm',
iv: SJCL.random.randomWords(3, 0) });

The important bit is using a smaller IV.

Decrypt (ruby):

def self.decrypt(h)
h = HashWithIndifferentAccess.new(JSON.parse(h))

key = OpenSSL::PKCS5.pbkdf2_hmac('your key here', Base64.decode64(h[:salt]), 1000, h[:ks]/8, 'SHA256')

puts "Key: #{key.unpack('H*')}"

puts "Salt: #{Base64.decode64(h[:salt]).unpack('H*')}"

c = OpenSSL::Cipher.new("#{h[:cipher]}-#{h[:ks]}-#{h[:mode]}")

c.decrypt

c.key = key

c.iv = Base64.decode64(h[:iv])

puts "IV: #{Base64.decode64(h[:iv]).unpack('H*')}"

c.auth_data = ""

c.update(Base64.decode64(h[:ct]))
end

How to decrypt of AES-256-GCM created with ruby in sjcl.js

AES-GCM is an authenticated encryption algorithm. It automatically generates an authentication tag during encryption, which is used for authentication during decryption. This tag is not considered in the current Ruby code. It is 16 bytes by default, can be retrieved with cipher.auth_tag and must be added, e.g.:

ciphertext = cipher.update(plaintext) + cipher.final + cipher.auth_tag 

Regarding nonce/IV, note that Base64 encoding should actually be done after concatenation (which, however, is not critical for a 12 bytes nonce/IV commonly used with GCM).


On the JavaScript side the separation of the nonce/IV is missing. Ciphertext and tag do not need to be separated because the sjcl processes the concatenation of both (ciphertext|tag):

const GCM_NONCE_LENGTH = 12 * 8
const GCM_TAG_LENGTH = 16 * 8

// Separate IV and ciptertext/tag combination
let ivCiphertextTagB64 = "2wLsVLuOJFX1pfwwjoLhQrW7f/86AefyZ7FwJEhJVIpU+iG2EITzushCpDRxgqK2cwVYvfNt7KFZ39obMMmIqhrDCIeifzs="
let ivCiphertextTag = sjcl.codec.base64.toBits(ivCiphertextTagB64)
let iv = sjcl.bitArray.bitSlice(ivCiphertextTag, 0, GCM_NONCE_LENGTH)
let ciphertextTag = sjcl.bitArray.bitSlice(ivCiphertextTag, GCM_NONCE_LENGTH)

// Derive key via SHA256
let key = sjcl.hash.sha256.hash("my password")

// Decrypt
let cipher = new sjcl.cipher.aes(key)
let plaintext = sjcl.mode.gcm.decrypt(cipher, ciphertextTag, iv, null, GCM_TAG_LENGTH)
//let plaintext = sjcl.mode.gcm.decrypt(cipher, ciphertextTag, iv) // works also; here the defaults for the AAD ([]) and the tag size (16 bytes) are applied
console.log(sjcl.codec.utf8String.fromBits(plaintext))
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.8/sjcl.min.js "></script>

is sjcl.encrypt using AES or SHA256

pbkdf2 for key generation is using HMAC with SHA256. But the default encryption key size with sjcl for AES-CCM is only 128 bits. If you want AES-CCM-256, I think you need to do the following, you also don't have to call pbkdf2 directly.

var encMessage =sjcl.encrypt(somePassword,message,{count:2048,salt:salt,ks:256});

JS / Ruby AES 256 symmetry

In the end I used Gibberish AES which has both a Ruby and JavaScript implementation written by the same author:

https://github.com/mdp/gibberish-aes

https://github.com/mdp/gibberish

How to Decrypt AES SubtleCrypto Web API at SJCL Library

There are two problems in your code:

  • kkey is the Base64url encoded raw key. This must first be converted to Base64 and then to a bitArray:
let kkeyB64 = kkey.replace(/-/g, '+').replace(/_/g, '/');   // Base64url -> Base64 (ignore optional padding)
let bkey = sjcl.codec.base64.toBits(kkeyB64); // conert to bitArray
  • Ciphertext and IV are given as ArrayBuffer, so it makes sense to use the ArrayBuffer codec, e.g.:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/core/codecArrayBuffer.js"></script>
let bdata = sjcl.codec.arrayBuffer.toBits(encryptedObject)
let ive = sjcl.codec.arrayBuffer.toBits(new Uint8Array(12).buffer)

With these changes, decryption with sjcl is successful.


Full code:

const generateKey = async () =>
{
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 128
}, true, ["encrypt", "decrypt"]);
const key_exported = await window.crypto.subtle.exportKey("jwk", key);
return key_exported.k;
}

const text = "This is an encrypted message";
const printCurrent = async () =>
{
let kkey = await generateKey();
await window.crypto.subtle.importKey(
"jwk",
{
k: kkey,
alg: "A128GCM",
ext: true,
key_ops: ["encrypt", "decrypt"],
kty: "oct",
},
{
name: "AES-GCM",
length: 128
},
false,
["encrypt", "decrypt"]
).then(function(key)
{
window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: new Uint8Array(12)
}, key, new TextEncoder().encode(JSON.stringify(text))).then(function(encryptedObject)
{
//console.log({kkey}); //{kkey: 'eKM_Cen2Z-jhedM284cltA'}

let kkeyB64 = kkey.replace(/-/g, '+').replace(/_/g, '/'); // Fix 1
let bkey = sjcl.codec.base64.toBits(kkeyB64);
//console.log({bkey}); //{bkey: Array(6)}
let cipher = new sjcl.cipher.aes(bkey);


let bdata = sjcl.codec.arrayBuffer.toBits(encryptedObject) // Fix 2
let ive = sjcl.codec.arrayBuffer.toBits(new Uint8Array(12).buffer) // Fix 3

// decrypt
let decbits = sjcl.mode.gcm.decrypt(cipher, bdata, ive);

// convert into utf8string
decryptedData = sjcl.codec.utf8String.fromBits(decbits);
document.getElementById("pt").innerHTML = JSON.parse(decryptedData);
});
});
}

printCurrent();
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/sjcl.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/sjcl@1.0.8/core/codecArrayBuffer.js"></script>
<p style="font-family:'Courier New', monospace;" id="pt"></p>

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=

SJCL library decryption issue

I managed to solve my problem. Basically it was two/three issues. I needed to do a jquery "encodeURIComponent" on the parameter before I sent it to the php script doing the database insert, because the PHP extract function stripped the "+" in the encrypted string.

The second issue was having to use PHP rawurldecode (as opposed to urldecode) to get my string back to pre ajax format with "+" chars, as opposed to " ".

The third issue was using jquerys "JSON.stringify" to turn it from a javascript 'object' into a string that the sjcl.decrypt would work with. The original javascript error makes sense now!



Related Topics



Leave a reply



Submit