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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
// ================================================================ // This example shows how to use the forge package to encrypt // and decrypt a simple object. // // In addition shows how to define a number of useful prototypes // and other utility functions. // ================================================================ // You need this declaration if you are using nodejs. // CITATION: https://github.com/digitalbazaar/forge var forge = require('./forge/js/forge.js'); // ================================================================ // basename of a file // ================================================================ function basename(str) { var base = new String(str).substring(str.lastIndexOf('/') + 1); if(base.lastIndexOf(".") != -1) base = base.substring(0, base.lastIndexOf(".")); return base; } // ================================================================ // Convert a string to bytes. // http://stackoverflow.com/questions/6226189/how-to-convert-a-string-to-bytearray // ================================================================ String.prototype.getBytes = function() { var bytes = []; for (var i = 0; i < this.length; i++) { var charCode = this.charCodeAt(i); var cLen = Math.ceil(Math.log(charCode)/Math.log(256)); for (var j = 0; j < cLen; j++) { bytes.push((charCode << (j*8)) & 0xFF); } } return bytes; } // ================================================================ // Get the stack information for __line and __file (below). // CITATION: http://stackoverflow.com/questions/11386492/accessing-line-number-in-v8-javascript-chrome-node-js // ================================================================ Object.defineProperty(global, '__stack', { get: function() { var orig = Error.prepareStackTrace; Error.prepareStackTrace = function(_, stack){ return stack; }; var err = new Error; Error.captureStackTrace(err, arguments.callee); var stack = err.stack; Error.prepareStackTrace = orig; return stack; } }); // ================================================================ // The current source line number. Like __LINE__ in C++. // ================================================================ Object.defineProperty(global, '__line', { get: function(){ return __stack[1].getLineNumber(); } }); // ================================================================ // The current source file name. Like __FILE__ in C++. // ================================================================ Object.defineProperty(global, '__file', { get: function(){ return basename(__stack[1].getFileName()); } }); // ================================================================ // Info function that prints out the current line number. // ================================================================ Object.defineProperty(global, '__line2', { get: function(){ return __stack[2].getLineNumber(); } }); Object.defineProperty(global, '__file2', { get: function(){ return basename(__stack[2].getFileName()); } }); function info(text) { console.log('INFO:' + __file2 + ':' + __line2 + ': ' + text); } // ================================================================ // Local assert, surprisingly useful. // ================================================================ function Assert(result, message) { if (!result) { message = message || 'Assertion failed!'; if (typeof Error != 'undefined') { throw new Error('ERROR:' + __file + ':' + __line + ': ' + message); } throw message; // if Error not defined } } // ================================================================ // Utility function to generate the passwords in a canonical way. // ================================================================ function make_password_bytes(password) { Assert(typeof password != 'undefined', 'Password not specified!'); Assert(password.length >= 8, 'Password length must be greater than 7 characters!'); var bytes = password.getBytes(); // Round to the nearest power of 2 larger than the password size and // append bytes from the password. This allows us to regenerate it at // will from the original. var nearest_power_of_2 = Math.pow( 2, Math.ceil( Math.log( bytes.length ) / Math.log( 2 ) ) ); if (bytes.length != nearest_power_of_2) { var remainder = nearest_power_of_2 - bytes.length; for(var i=0; i<remainder; i++) { var j = i % bytes.length; bytes.push(bytes[j]); } } return bytes; } // ================================================================ // Encrypt the data. // ================================================================ function encrypt(password, data) { var key = make_password_bytes(password); var iv = forge.random.getBytesSync(16); var cipher = forge.cipher.createCipher('AES-CBC', key); cipher.start({iv: iv}); cipher.update(forge.util.createBuffer(data)); cipher.finish(); var encrypted = cipher.output; var data = forge.util.bytesToHex(encrypted); var obj = {'iv': forge.util.bytesToHex(iv), 'encrypted': data}; return JSON.stringify(obj); // String rep allows persistence. } // ================================================================ // Decrypt the data. // ================================================================ function decrypt(password, encrypted) { var key = make_password_bytes(password); var obj = JSON.parse(encrypted); var iv = forge.util.createBuffer(); var data = forge.util.createBuffer(); iv.putBytes(forge.util.hexToBytes(obj.iv)); data.putBytes(forge.util.hexToBytes(obj.encrypted)); var decipher = forge.cipher.createDecipher('AES-CBC', key); decipher.start({iv: iv}); decipher.update(data); decipher.finish(); return decipher.output; } // ================================================================ // Example. // ================================================================ info('testing encryption and decryption'); var data = {'id': 123, 'text': 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit, ...'}; var password = 'top secret!'; var encrypted = encrypt(password, JSON.stringify(data)); info('encrypted data: ' + encrypted.length + ' ' + encrypted); var decrypted = JSON.parse(decrypt(password, encrypted)); info('decrypted data: ' + JSON.stringify(decrypted, null, 4)); info('checking the results'); Assert( data.id == decrypted.id, 'Decryption failed, ids do not match!'); Assert( data.text == decrypted.text, 'Decryption failed, text does not match!'); info('PASSED'); info('done'); |
When it is run using nodejs, the output looks something like this:
1 2 3 4 5 6 7 8 9 10 |
$ NODE_PATH=/to/my/nodejs node example.js INFO:example:170: testing encryption and decryption INFO:example:175: encrypted data: 248 {"iv":"b4ad952831578149e43a206bda938740","encrypted":"ec3ecaf6533fe3ac9f25ddc6f7c05ca0b0a789425f8a70607a11dcfb27802c2631e4ad38df0354c5efd059b29297b3e6082856284889e569c3934f43c42effed409d96dcb9d1a202dc8c4f5ac7807dd45490dcbe23a7596139a017af7970125f"} INFO:example:178: decrypted data: { "id": 123, "text": "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, ..." } INFO:example:180: checking the results INFO:example:183: PASSED INFO:example:185: done |
Enjoy!