{"id":1614,"date":"2014-12-06T19:17:34","date_gmt":"2014-12-07T03:17:34","guid":{"rendered":"http:\/\/joelinoff.com\/blog\/?p=1614"},"modified":"2014-12-06T19:23:51","modified_gmt":"2014-12-07T03:23:51","slug":"javascript-example-that-shows-how-to-use-the-forge-package-to-encryptdescrypt-objects","status":"publish","type":"post","link":"https:\/\/joelinoff.com\/blog\/?p=1614","title":{"rendered":"Javascript example that shows how to use the forge package to encrypt\/decrypt objects"},"content":{"rendered":"<p>This example shows how to use the <a href=\"https:\/\/github.com\/digitalbazaar\/forge\" title=\"https:\/\/github.com\/digitalbazaar\/forge\">forge<\/a> package to encrypt and decrypt objects. It also shows how to define a number of useful prototypes and other utility functions.<br \/>\n<!--more--><\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:javascript decode:true\">\r\n\/\/ ================================================================\r\n\/\/ This example shows how to use the forge package to encrypt\r\n\/\/ and decrypt a simple object.\r\n\/\/\r\n\/\/ In addition shows how to define a number of useful prototypes\r\n\/\/ and other utility functions.\r\n\/\/ ================================================================\r\n\r\n\/\/ You need this declaration if you are using nodejs.\r\n\/\/ CITATION: https:\/\/github.com\/digitalbazaar\/forge\r\nvar forge = require('.\/forge\/js\/forge.js');\r\n\r\n\/\/ ================================================================\r\n\/\/ basename of a file\r\n\/\/ ================================================================\r\nfunction basename(str)\r\n{\r\n   var base = new String(str).substring(str.lastIndexOf('\/') + 1); \r\n    if(base.lastIndexOf(\".\") != -1)       \r\n        base = base.substring(0, base.lastIndexOf(\".\"));\r\n   return base;\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Convert a string to bytes.\r\n\/\/ http:\/\/stackoverflow.com\/questions\/6226189\/how-to-convert-a-string-to-bytearray\r\n\/\/ ================================================================\r\nString.prototype.getBytes = function() {\r\n    var bytes = [];\r\n    for (var i = 0; i < this.length; i++) {\r\n        var charCode = this.charCodeAt(i);\r\n        var cLen = Math.ceil(Math.log(charCode)\/Math.log(256));\r\n        for (var j = 0; j < cLen; j++) {\r\n            bytes.push((charCode << (j*8)) &#038; 0xFF);\r\n        }\r\n    }\r\n    return bytes;\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Get the stack information for __line and __file (below).\r\n\/\/ CITATION: http:\/\/stackoverflow.com\/questions\/11386492\/accessing-line-number-in-v8-javascript-chrome-node-js\r\n\/\/ ================================================================\r\nObject.defineProperty(global, '__stack', {\r\n    get: function() {\r\n        var orig = Error.prepareStackTrace;\r\n        Error.prepareStackTrace = function(_, stack){ return stack; };\r\n        var err = new Error;\r\n        Error.captureStackTrace(err, arguments.callee);\r\n        var stack = err.stack;\r\n        Error.prepareStackTrace = orig;\r\n        return stack;\r\n    }\r\n});\r\n\r\n\/\/ ================================================================\r\n\/\/ The current source line number. Like __LINE__ in C++.\r\n\/\/ ================================================================\r\nObject.defineProperty(global, '__line', {\r\n  get: function(){\r\n    return __stack[1].getLineNumber();\r\n  }\r\n});\r\n\r\n\/\/ ================================================================\r\n\/\/ The current source file name. Like __FILE__ in C++.\r\n\/\/ ================================================================\r\nObject.defineProperty(global, '__file', {\r\n    get: function(){\r\n        return basename(__stack[1].getFileName());\r\n    }\r\n});\r\n\r\n\/\/ ================================================================\r\n\/\/ Info function that prints out the current line number.\r\n\/\/ ================================================================\r\nObject.defineProperty(global, '__line2', {\r\n  get: function(){\r\n    return __stack[2].getLineNumber();\r\n  }\r\n});\r\n\r\nObject.defineProperty(global, '__file2', {\r\n    get: function(){\r\n        return basename(__stack[2].getFileName());\r\n    }\r\n});\r\n\r\nfunction info(text) {\r\n    console.log('INFO:' + __file2 + ':' + __line2 + ': ' + text);\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Local assert, surprisingly useful.\r\n\/\/ ================================================================\r\nfunction Assert(result, message) {\r\n    if (!result) {\r\n        message = message || 'Assertion failed!';\r\n        if (typeof Error != 'undefined') {\r\n            throw new Error('ERROR:' + __file + ':' + __line + ': ' + message);\r\n        }\r\n        throw message; \/\/ if Error not defined\r\n    }\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Utility function to generate the passwords in a canonical way.\r\n\/\/ ================================================================\r\nfunction make_password_bytes(password) {\r\n    Assert(typeof password != 'undefined', 'Password not specified!');\r\n    Assert(password.length >= 8, 'Password length must be greater than 7 characters!');\r\n    \r\n    var bytes = password.getBytes();\r\n    \r\n    \/\/ Round to the nearest power of 2 larger than the password size and\r\n    \/\/ append bytes from the password. This allows us to regenerate it at\r\n    \/\/ will from the original.\r\n    var nearest_power_of_2 = Math.pow( 2, Math.ceil( Math.log( bytes.length ) \/ Math.log( 2 ) ) );\r\n    if (bytes.length != nearest_power_of_2) {\r\n        var remainder = nearest_power_of_2 - bytes.length;\r\n        for(var i=0; i<remainder; i++) {\r\n            var j = i % bytes.length;\r\n            bytes.push(bytes[j]);\r\n        }\r\n    }\r\n    return bytes;\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Encrypt the data.\r\n\/\/ ================================================================\r\nfunction encrypt(password, data) {\r\n    var key = make_password_bytes(password);\r\n    var iv = forge.random.getBytesSync(16);\r\n    var cipher = forge.cipher.createCipher('AES-CBC', key);\r\n    \r\n    cipher.start({iv: iv});\r\n    cipher.update(forge.util.createBuffer(data));\r\n    cipher.finish();\r\n    \r\n    var encrypted = cipher.output;\r\n    var data = forge.util.bytesToHex(encrypted);\r\n    var obj = {'iv': forge.util.bytesToHex(iv), 'encrypted': data};\r\n    \r\n    return JSON.stringify(obj); \/\/ String rep allows persistence.\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Decrypt the data.\r\n\/\/ ================================================================\r\nfunction decrypt(password, encrypted) {\r\n    var key = make_password_bytes(password);\r\n    var obj = JSON.parse(encrypted);\r\n    \r\n    var iv = forge.util.createBuffer();\r\n    var data = forge.util.createBuffer();\r\n    iv.putBytes(forge.util.hexToBytes(obj.iv));\r\n    data.putBytes(forge.util.hexToBytes(obj.encrypted));\r\n    \r\n    var decipher = forge.cipher.createDecipher('AES-CBC', key);\r\n    decipher.start({iv: iv});\r\n    decipher.update(data);\r\n    decipher.finish();\r\n    return decipher.output;\r\n}\r\n\r\n\/\/ ================================================================\r\n\/\/ Example.\r\n\/\/ ================================================================\r\ninfo('testing encryption and decryption');\r\nvar data = {'id': 123,\r\n            'text': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit, ...'};\r\nvar password = 'top secret!';\r\nvar encrypted = encrypt(password, JSON.stringify(data));\r\ninfo('encrypted data: ' + encrypted.length + ' ' + encrypted);\r\n\r\nvar decrypted = JSON.parse(decrypt(password, encrypted));\r\ninfo('decrypted data: ' + JSON.stringify(decrypted, null, 4));\r\n\r\ninfo('checking the results');\r\nAssert( data.id == decrypted.id, 'Decryption failed, ids do not match!');\r\nAssert( data.text == decrypted.text, 'Decryption failed, text does not match!');\r\ninfo('PASSED');\r\n\r\ninfo('done');\r\n<\/pre>\n<p>When it is run using nodejs, the output looks something like this:<\/p>\n<pre class=\"theme:vs2012-black font-size:14 line-height:17 lang:default decode:true\">\r\n$ NODE_PATH=\/to\/my\/nodejs node example.js\r\nINFO:example:170: testing encryption and decryption\r\nINFO:example:175: encrypted data: 248 {\"iv\":\"b4ad952831578149e43a206bda938740\",\"encrypted\":\"ec3ecaf6533fe3ac9f25ddc6f7c05ca0b0a789425f8a70607a11dcfb27802c2631e4ad38df0354c5efd059b29297b3e6082856284889e569c3934f43c42effed409d96dcb9d1a202dc8c4f5ac7807dd45490dcbe23a7596139a017af7970125f\"}\r\nINFO:example:178: decrypted data: {\r\n    \"id\": 123,\r\n    \"text\": \"Lorem ipsum dolor sit amet, consectetuer adipiscing elit, ...\"\r\n}\r\nINFO:example:180: checking the results\r\nINFO:example:183: PASSED\r\nINFO:example:185: done\r\n<\/pre>\n<p>Enjoy!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This example shows how to use the forge package to encrypt and decrypt objects. It also shows how to define a number of useful prototypes and other utility functions.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[5,38,39],"tags":[],"_links":{"self":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1614"}],"collection":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1614"}],"version-history":[{"count":5,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1614\/revisions"}],"predecessor-version":[{"id":1619,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1614\/revisions\/1619"}],"wp:attachment":[{"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/joelinoff.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}