diff --git a/README.md b/README.md index 437ec74..77b283d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -Generate a ssh keypair using the webcrypto API -== +# Generate a ssh keypair using the webcrypto API See the live demo at https://js-keygen.surge.sh @@ -11,8 +10,6 @@ There is no way to generate a ssh keypair on the chrome book, but we have access * I had to learn about ASN.1 to encode the private key for OpenSSH * I had to lean about the open SSH public format to encode the public key for OpenSSH - The end result is a usable single page app that will locally generate a keypair you can save to local drive. Allowing you to do that straight from chrome on a chrome book. Everywhere else, you should have access to ssh-keygen which is the recommended way to generate keypair for SSH. - diff --git a/base64url.js b/base64url.js index 8a35147..816968c 100644 --- a/base64url.js +++ b/base64url.js @@ -3,26 +3,26 @@ function base64urlEncode(arg) { var s = window.btoa(arg); // Regular base64 encoder - s = s.split('=')[0]; // Remove any trailing '='s - s = s.replace(/\+/g, '-'); // 62nd char of encoding - s = s.replace(/\//g, '_'); // 63rd char of encoding + s = s.split("=")[0]; // Remove any trailing '='s + s = s.replace(/\+/g, "-"); // 62nd char of encoding + s = s.replace(/\//g, "_"); // 63rd char of encoding return s; } function base64urlDecode(s) { - s = s.replace(/-/g, '+'); // 62nd char of encoding - s = s.replace(/_/g, '/'); // 63rd char of encoding + s = s.replace(/-/g, "+"); // 62nd char of encoding + s = s.replace(/_/g, "/"); // 63rd char of encoding switch (s.length % 4) { // Pad with trailing '='s - case 0: // No pad chars in this case - break; - case 2: // Two pad chars - s += "=="; - break; - case 3: // One pad char - s += "="; - break; - default: - throw "Illegal base64url string!"; + case 0: // No pad chars in this case + break; + case 2: // Two pad chars + s += "=="; + break; + case 3: // One pad char + s += "="; + break; + default: + throw "Illegal base64url string!"; } return window.atob(s); // Standard base64 decoder } diff --git a/index.html b/index.html index 54dedda..a1e492a 100644 --- a/index.html +++ b/index.html @@ -1,59 +1,86 @@ + js-keygen - - + + +
-

js-keygen

- - Generate a keypair to be used with openSSH, this replicate ssh-keygen function in javascript in the browser, using the webcrypto api and a bit of glue.
- For an in-depth explanation on how this work, see the blog post.
- Usually you would want to save the private key to the machine initiating the ssh connection, and you want to copy the public key to the system receiving the connection.
- No data is being sent to the server, everything happens within the context of this web page.
- Tweet -
-
-
- -
-
- -
- - -
- Made with by Patrick Roumanoff - Fork me on GitHub -
+
+ + +
+
+ + + + +
+
+ + +
+ + +
+ + +
Made with + by + Patrick Roumanoff + + Fork me on GitHub + + + \ No newline at end of file diff --git a/js-keygen-ui.js b/js-keygen-ui.js index 3e2a894..2c81ea5 100644 --- a/js-keygen-ui.js +++ b/js-keygen-ui.js @@ -2,16 +2,16 @@ var console, generateKeyPair; function copy(id) { - return function () { + return function() { var ta = document.querySelector(id); ta.focus(); ta.select(); try { - var successful = document.execCommand('copy'); - var msg = successful ? 'successful' : 'unsuccessful'; - console.log('Copy key command was ' + msg); + var successful = document.execCommand("copy"); + var msg = successful ? "successful" : "unsuccessful"; + console.log("Copy key command was " + msg); } catch (err) { - console.log('Oops, unable to copy'); + console.log("Oops, unable to copy"); } window.getSelection().removeAllRanges(); ta.blur(); @@ -22,31 +22,33 @@ function buildHref(data) { return "data:application/octet-stream;charset=utf-8;base64," + window.btoa(data); } -document.addEventListener("DOMContentLoaded", function (event) { - document.querySelector('#savePrivate').addEventListener('click', function (event) { - document.querySelector('a#private').click(); +document.addEventListener("DOMContentLoaded", function(event) { + document.querySelector("#savePrivate").addEventListener("click", function(event) { + document.querySelector("a#private").click(); }); - document.querySelector('#copyPrivate').addEventListener('click', copy('#privateKey')); - document.querySelector('#savePublic').addEventListener('click', function (event) { - document.querySelector('a#public').click(); + document.querySelector("#copyPrivate").addEventListener("click", copy("#privateKey")); + document.querySelector("#savePublic").addEventListener("click", function(event) { + document.querySelector("a#public").click(); }); - document.querySelector('#copyPublic').addEventListener('click', copy('#publicKey')); - - document.querySelector('#generate').addEventListener('click', function (event) { - var name = document.querySelector('#name').value || "name"; - document.querySelector('a#private').setAttribute("download", name + "_rsa"); - document.querySelector('a#public').setAttribute("download", name + "_rsa.pub"); - - var alg = document.querySelector('#alg').value || "RSASSA-PKCS1-v1_5"; - var size = parseInt(document.querySelector('#size').value || "2048", 10); - generateKeyPair(alg, size, name).then(function (keys) { - document.querySelector('#private').setAttribute("href", buildHref(keys[0])); - document.querySelector('#public').setAttribute("href", buildHref(keys[1])); - document.querySelector('#privateKey').textContent = keys[0]; - document.querySelector('#publicKey').textContent = keys[1]; - document.querySelector('#result').style.display = "block"; - }).catch(function (err) { - console.error(err); - }); + document.querySelector("#copyPublic").addEventListener("click", copy("#publicKey")); + + document.querySelector("#generate").addEventListener("click", function(event) { + var name = document.querySelector("#name").value || "name"; + document.querySelector("a#private").setAttribute("download", name + "_rsa"); + document.querySelector("a#public").setAttribute("download", name + "_rsa.pub"); + + var alg = document.querySelector("#alg").value || "RSASSA-PKCS1-v1_5"; + var size = parseInt(document.querySelector("#size").value || "2048", 10); + generateKeyPair(alg, size, name) + .then(function(keys) { + document.querySelector("#private").setAttribute("href", buildHref(keys[0])); + document.querySelector("#public").setAttribute("href", buildHref(keys[1])); + document.querySelector("#privateKey").textContent = keys[0]; + document.querySelector("#publicKey").textContent = keys[1]; + document.querySelector("#result").style.display = "block"; + }) + .catch(function(err) { + console.error(err); + }); }); }); diff --git a/js-keygen.css b/js-keygen.css index a562df0..8eeae48 100644 --- a/js-keygen.css +++ b/js-keygen.css @@ -1,10 +1,10 @@ body { - background-color: #cccccc; + background-color: #cccccc; font: 16px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } button#generate { - background-color: rgb(60,200,30); + background-color: rgb(60, 200, 30); padding: 10px; margin-top: 5px; color: white; @@ -29,7 +29,7 @@ label { display: inline-block; width: 80px; } -select{ +select { width: 200px; } input { diff --git a/js-keygen.js b/js-keygen.js index 17e442b..163eabe 100644 --- a/js-keygen.js +++ b/js-keygen.js @@ -4,7 +4,9 @@ var extractable = true; var encodePrivateKey, encodePublicKey; function wrap(text, len) { - var length = len || 72, i, result = ""; + var length = len || 72, + i, + result = ""; for (i = 0; i < text.length; i += length) { result += text.slice(i, i + length) + "\n"; } @@ -16,7 +18,8 @@ function rsaPrivateKey(key) { } function arrayBufferToBase64(buffer) { - var binary = '', i; + var binary = "", + i; var bytes = new Uint8Array(buffer); var len = bytes.byteLength; for (i = 0; i < len; i += 1) { @@ -26,28 +29,28 @@ function arrayBufferToBase64(buffer) { } function generateKeyPair(alg, size, name) { - return window.crypto.subtle.generateKey({ - name: "RSASSA-PKCS1-v1_5", - modulusLength: 2048, //can be 1024, 2048, or 4096 - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), - hash: {name: "SHA-1"} //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" - }, - extractable, - ["sign", "verify"] - ).then(function (key) { - - var privateKey = window.crypto.subtle.exportKey( - "jwk", - key.privateKey - ).then(encodePrivateKey).then(wrap).then(rsaPrivateKey); + return window.crypto.subtle + .generateKey( + { + name: "RSASSA-PKCS1-v1_5", + modulusLength: 2048, //can be 1024, 2048, or 4096 + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), + hash: { name: "SHA-1" }, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512" + }, + extractable, + ["sign", "verify"] + ) + .then(function(key) { + var privateKey = window.crypto.subtle + .exportKey("jwk", key.privateKey) + .then(encodePrivateKey) + .then(wrap) + .then(rsaPrivateKey); - var publicKey = window.crypto.subtle.exportKey( - "jwk", - key.publicKey - ).then(function (jwk) { + var publicKey = window.crypto.subtle.exportKey("jwk", key.publicKey).then(function(jwk) { return encodePublicKey(jwk, name); }); - - return Promise.all([privateKey, publicKey]); - }); + + return Promise.all([privateKey, publicKey]); + }); } diff --git a/package.json b/package.json index 45eca41..b66d911 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,11 @@ "name": "js-keypair", "version": "1.0.0", "description": "Generate a key pair using the webcrypto API", - "main": "keypair.js", - "dependencies": { - }, + "main": "js-keygen.js", + "dependencies": {}, "devDependencies": {}, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"To run test open test/index.html in your browser\"" }, "author": "patrick@roumanoff.com", "license": "Apache 2.0" diff --git a/ssh-util.js b/ssh-util.js index bf261cb..fd6df18 100644 --- a/ssh-util.js +++ b/ssh-util.js @@ -7,7 +7,7 @@ function arrayToString(a) { } function stringToArray(s) { - return s.split('').map(function (c) { + return s.split("").map(function(c) { return c.charCodeAt(); }); } @@ -21,13 +21,18 @@ function pemToArray(pem) { } function arrayToPem(a) { - return window.btoa(a.map(function (c) { - return String.fromCharCode(c); - }).join('')); + return window.btoa( + a + .map(function(c) { + return String.fromCharCode(c); + }) + .join("") + ); } function arrayToLen(a) { - var result = 0, i; + var result = 0, + i; for (i = 0; i < a.length; i += 1) { result = result * 256 + a[i]; } @@ -37,13 +42,14 @@ function arrayToLen(a) { function integerToOctet(n) { var result = []; for (true; n > 0; n = n >> 8) { - result.push(n & 0xFF); + result.push(n & 0xff); } return result.reverse(); } function lenToArray(n) { - var oct = integerToOctet(n), i; + var oct = integerToOctet(n), + i; for (i = oct.length; i < 4; i += 1) { oct.unshift(0); } @@ -54,23 +60,24 @@ function decodePublicKey(s) { var split = s.split(" "); var prefix = split[0]; if (prefix !== "ssh-rsa") { - throw ("Unknown prefix:" + prefix); + throw "Unknown prefix:" + prefix; } var buffer = pemToArray(split[1]); var nameLen = arrayToLen(buffer.splice(0, 4)); var type = arrayToString(buffer.splice(0, nameLen)); if (type !== "ssh-rsa") { - throw ("Unknown key type:" + type); + throw "Unknown key type:" + type; } var exponentLen = arrayToLen(buffer.splice(0, 4)); var exponent = buffer.splice(0, exponentLen); var keyLen = arrayToLen(buffer.splice(0, 4)); var key = buffer.splice(0, keyLen); - return {type: type, exponent: exponent, key: key, name: split[2]}; + return { type: type, exponent: exponent, key: key, name: split[2] }; } function checkHighestBit(v) { - if (v[0] >> 7 === 1) { // add leading zero if first bit is set + if (v[0] >> 7 === 1) { + // add leading zero if first bit is set v.unshift(0); } return v; @@ -81,7 +88,7 @@ function jwkToInternal(jwk) { type: "ssh-rsa", exponent: checkHighestBit(stringToArray(base64urlDecode(jwk.e))), name: "name", - key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))) + key: checkHighestBit(stringToArray(base64urlDecode(jwk.n))), }; } @@ -109,7 +116,7 @@ function asnEncodeLen(n) { function encodePrivateKey(jwk) { var order = ["n", "e", "d", "p", "q", "dp", "dq", "qi"]; - var list = order.map(function (prop) { + var list = order.map(function(prop) { var v = checkHighestBit(stringToArray(base64urlDecode(jwk[prop]))); var len = asnEncodeLen(v.length); return [0x02].concat(len, v); // int tag is 0x02 diff --git a/test/index.html b/test/index.html index 2340e53..52b38ca 100644 --- a/test/index.html +++ b/test/index.html @@ -1,17 +1,22 @@ + QUnit Example +
-
+
+
+
+ \ No newline at end of file diff --git a/test/tests.js b/test/tests.js index b906f65..6d66dd6 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1,27 +1,304 @@ -var public_ssh = ["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwi36YMW0eDS3NXSAM/Gcs0txeLOcZE0LQmGPYmHX09Fm1FC9AdzvDWQIfwVylqNy8G6X8+pE0TMuWav4rQjtWRls3j43LdrXkfaTZV2PNJH0ki2zaCND3cz46hBR1bSwi3O4LoN0ZHXoC4ZXoMBXKtYEOg+9jS+pE3vu2QSPruiRROTOYYvrjWx0Bwi8DJc90TmNVeqvPjewPAm4qaTdmh96jIgJQq+vAdhDHu90i31Kl3JUF94x6pzFmg8ZyXOv0Py2GtK9c5To3C33FXI8yTm/sf2Bp7fwd3MEGNcdVNqa7Tt0z2u5Jcmsws93SZuj4iVjbR6xqme9EmIa3BTB7 name", -"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuew50MaphQgiuM6H7zxMspNojI2Ujf77MuWlAjmw1JxcTkfE7JKzV+9fqmESJNtnZSr3+I2dxQhJ72jttrz+2dFt9ol91muTPWzKrA8XXIBH2o7sEJ+QB8/q7S03d+Zgw6tlo+qdXLOWcKqL5MJhYwzTFEdGTSMF00cBFadcpDq1xFPygGTHRa7m3pK723nGz7TMGWmtBK2bHx+Zlp7geLK/7hl+NRG1lTyIbtdkP2T4Y81Z0bhz9kNHroUei3MFD6HvN93qMJWl3/LZZzTb++1BedNeybGKqbtsB3xp0v3c6bQy49wR3RwrAwL03AKbCwTawAufSeoXyRI+rtgZ/ name", -"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpNNtjZeldPuJ5ZgcjO4i6eSJb6kiuf1sULOoWaW9acwSxAfqrmN6Hn7VGg3GK3kSKJUmBKMsF2u+ECajVBec+OTMlbL7oZrYNl2neUYsI7O0G/8lpozZjADYu8CaMqVSAeTa3ORga9Ht/qgCpqXIyEcTsFSbZ45hhaZF0fXQ0GHDCkV/ylBduQHxheCe1SPBSWIO2BwqSlGx/Q76lkL/BnGdcx7xVi3h2yNbEGxqzFuPK75VADZfWria4x09rTqvu41GWIyqzFcbB7BxNImVNh6WVk/qKTcXbfWwH8ck9Cd5bX9g36QaImZ6tW8i/bl3o75bGgP2hSWpsNx8CMVn9 name"]; -var public_ssh_decoded = [{ - type: "ssh-rsa", - exponent: [1,0,1], - name: "name", - key: [0,176,139,126,152,49,109,30,13,45,205,93,32,12,252,103,44,210,220,94,44,231,25,19,66,208,152,99,216,152,117,244,244,89,181,20,47,64,119,59,195,89,2,31,193,92,165,168,220,188,27,165,252,250,145,52,76,203,150,106,254,43,66,59,86,70,91,55,143,141,203,118,181,228,125,164,217,87,99,205,36,125,36,139,108,218,8,208,247,115,62,58,132,20,117,109,44,34,220,238,11,160,221,25,29,122,2,225,149,232,48,21,202,181,129,14,131,239,99,75,234,68,222,251,182,65,35,235,186,36,81,57,51,152,98,250,227,91,29,1,194,47,3,37,207,116,78,99,85,122,171,207,141,236,15,2,110,42,105,55,102,135,222,163,34,2,80,171,235,192,118,16,199,187,221,34,223,82,165,220,149,5,247,140,122,167,49,102,131,198,114,92,235,244,63,45,134,180,175,92,229,58,55,11,125,197,92,143,50,78,111,236,127,96,105,237,252,29,220,193,6,53,199,85,54,166,187,78,221,51,218,238,73,114,107,48,179,221,210,102,232,248,137,88,219,71,172,106,153,239,68,152,134,183,5,48,123] -}]; +var public_ssh = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCwi36YMW0eDS3NXSAM/Gcs0txeLOcZE0LQmGPYmHX09Fm1FC9AdzvDWQIfwVylqNy8G6X8+pE0TMuWav4rQjtWRls3j43LdrXkfaTZV2PNJH0ki2zaCND3cz46hBR1bSwi3O4LoN0ZHXoC4ZXoMBXKtYEOg+9jS+pE3vu2QSPruiRROTOYYvrjWx0Bwi8DJc90TmNVeqvPjewPAm4qaTdmh96jIgJQq+vAdhDHu90i31Kl3JUF94x6pzFmg8ZyXOv0Py2GtK9c5To3C33FXI8yTm/sf2Bp7fwd3MEGNcdVNqa7Tt0z2u5Jcmsws93SZuj4iVjbR6xqme9EmIa3BTB7 name", + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuew50MaphQgiuM6H7zxMspNojI2Ujf77MuWlAjmw1JxcTkfE7JKzV+9fqmESJNtnZSr3+I2dxQhJ72jttrz+2dFt9ol91muTPWzKrA8XXIBH2o7sEJ+QB8/q7S03d+Zgw6tlo+qdXLOWcKqL5MJhYwzTFEdGTSMF00cBFadcpDq1xFPygGTHRa7m3pK723nGz7TMGWmtBK2bHx+Zlp7geLK/7hl+NRG1lTyIbtdkP2T4Y81Z0bhz9kNHroUei3MFD6HvN93qMJWl3/LZZzTb++1BedNeybGKqbtsB3xp0v3c6bQy49wR3RwrAwL03AKbCwTawAufSeoXyRI+rtgZ/ name", + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCpNNtjZeldPuJ5ZgcjO4i6eSJb6kiuf1sULOoWaW9acwSxAfqrmN6Hn7VGg3GK3kSKJUmBKMsF2u+ECajVBec+OTMlbL7oZrYNl2neUYsI7O0G/8lpozZjADYu8CaMqVSAeTa3ORga9Ht/qgCpqXIyEcTsFSbZ45hhaZF0fXQ0GHDCkV/ylBduQHxheCe1SPBSWIO2BwqSlGx/Q76lkL/BnGdcx7xVi3h2yNbEGxqzFuPK75VADZfWria4x09rTqvu41GWIyqzFcbB7BxNImVNh6WVk/qKTcXbfWwH8ck9Cd5bX9g36QaImZ6tW8i/bl3o75bGgP2hSWpsNx8CMVn9 name", +]; +var public_ssh_decoded = [ + { + type: "ssh-rsa", + exponent: [1, 0, 1], + name: "name", + key: [ + 0, + 176, + 139, + 126, + 152, + 49, + 109, + 30, + 13, + 45, + 205, + 93, + 32, + 12, + 252, + 103, + 44, + 210, + 220, + 94, + 44, + 231, + 25, + 19, + 66, + 208, + 152, + 99, + 216, + 152, + 117, + 244, + 244, + 89, + 181, + 20, + 47, + 64, + 119, + 59, + 195, + 89, + 2, + 31, + 193, + 92, + 165, + 168, + 220, + 188, + 27, + 165, + 252, + 250, + 145, + 52, + 76, + 203, + 150, + 106, + 254, + 43, + 66, + 59, + 86, + 70, + 91, + 55, + 143, + 141, + 203, + 118, + 181, + 228, + 125, + 164, + 217, + 87, + 99, + 205, + 36, + 125, + 36, + 139, + 108, + 218, + 8, + 208, + 247, + 115, + 62, + 58, + 132, + 20, + 117, + 109, + 44, + 34, + 220, + 238, + 11, + 160, + 221, + 25, + 29, + 122, + 2, + 225, + 149, + 232, + 48, + 21, + 202, + 181, + 129, + 14, + 131, + 239, + 99, + 75, + 234, + 68, + 222, + 251, + 182, + 65, + 35, + 235, + 186, + 36, + 81, + 57, + 51, + 152, + 98, + 250, + 227, + 91, + 29, + 1, + 194, + 47, + 3, + 37, + 207, + 116, + 78, + 99, + 85, + 122, + 171, + 207, + 141, + 236, + 15, + 2, + 110, + 42, + 105, + 55, + 102, + 135, + 222, + 163, + 34, + 2, + 80, + 171, + 235, + 192, + 118, + 16, + 199, + 187, + 221, + 34, + 223, + 82, + 165, + 220, + 149, + 5, + 247, + 140, + 122, + 167, + 49, + 102, + 131, + 198, + 114, + 92, + 235, + 244, + 63, + 45, + 134, + 180, + 175, + 92, + 229, + 58, + 55, + 11, + 125, + 197, + 92, + 143, + 50, + 78, + 111, + 236, + 127, + 96, + 105, + 237, + 252, + 29, + 220, + 193, + 6, + 53, + 199, + 85, + 54, + 166, + 187, + 78, + 221, + 51, + 218, + 238, + 73, + 114, + 107, + 48, + 179, + 221, + 210, + 102, + 232, + 248, + 137, + 88, + 219, + 71, + 172, + 106, + 153, + 239, + 68, + 152, + 134, + 183, + 5, + 48, + 123, + ], + }, +]; - var jwk_public = {"alg":"RS1","e":"AQAB","ext":true,"key_ops":["verify"],"kty":"RSA","n":"3PWJ6uDsFPgQo67of3IYw0Svyq95SNh9GS-2gorv68GxWIYYeAShaG_UtTf8mvf6u-VIUr54Re2FoLc78ICR3nRhFH5D1_fNaP9hkMAHBqaJ8ATiq4d7-PfeXTCi0yY0qfWkGjuPtOC3IK7WmnEkiA5qUVpy0oHFPiqoAyNynWJRDFJka00JEpM1QFyF1Tz3PEGp0XlFnClY48iJG9UqXlDgaysnG3ro2sDm8ftva0IjX1Sp7Z9FyWQci-yOYfST00wKHQd7z5-Eo3cTd5M0BhcVXeR0gprdK1TTDLZLznFJQ36HYwrUFEXvTyme6vkZfNPRb0z8KPq5Gs7dujE_cw"}; +var jwk_public = { + alg: "RS1", + e: "AQAB", + ext: true, + key_ops: ["verify"], + kty: "RSA", + n: + "3PWJ6uDsFPgQo67of3IYw0Svyq95SNh9GS-2gorv68GxWIYYeAShaG_UtTf8mvf6u-VIUr54Re2FoLc78ICR3nRhFH5D1_fNaP9hkMAHBqaJ8ATiq4d7-PfeXTCi0yY0qfWkGjuPtOC3IK7WmnEkiA5qUVpy0oHFPiqoAyNynWJRDFJka00JEpM1QFyF1Tz3PEGp0XlFnClY48iJG9UqXlDgaysnG3ro2sDm8ftva0IjX1Sp7Z9FyWQci-yOYfST00wKHQd7z5-Eo3cTd5M0BhcVXeR0gprdK1TTDLZLznFJQ36HYwrUFEXvTyme6vkZfNPRb0z8KPq5Gs7dujE_cw", +}; var jwk_private = { - "kty":"RSA", - "kid":"juliet@capulet.lit", - "use":"enc", - "n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", - "e":"AQAB", - "d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", - "p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", - "q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", - "dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", - "dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", - "qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8" + kty: "RSA", + kid: "juliet@capulet.lit", + use: "enc", + n: + "t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", + e: "AQAB", + d: + "GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", + p: + "2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", + q: + "1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", + dp: + "KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", + dq: + "AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", + qi: + "lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8", }; var tags = { @@ -39,84 +316,93 @@ var tags = { }; QUnit.test("array to PEM", function(assert) { - var a = [1,2,3]; + var a = [1, 2, 3]; var p = arrayToPem(a); var a2 = pemToArray(p); assert.deepEqual(a2, a, "can you count?"); }); QUnit.test("array to String", function(assert) { - var a = "ssh-rsa".split('').map(function (c) {return c.charCodeAt();}); - assert.equal(arrayToString(pemToArray(arrayToPem(a))), "ssh-rsa"); + var a = "ssh-rsa".split("").map(function(c) { + return c.charCodeAt(); + }); + assert.equal(arrayToString(pemToArray(arrayToPem(a))), "ssh-rsa"); }); QUnit.test("lenToArray", function(assert) { - var a = 66051; - assert.deepEqual(lenToArray(a), [0, 1, 2, 3]); + var a = 66051; + assert.deepEqual(lenToArray(a), [0, 1, 2, 3]); }); QUnit.test("arrayToLen", function(assert) { - var a = [0, 1, 2, 3]; - assert.deepEqual(arrayToLen(a), 66051); + var a = [0, 1, 2, 3]; + assert.deepEqual(arrayToLen(a), 66051); }); -public_ssh.forEach(function (public, index) { +public_ssh.forEach(function(public, index) { QUnit.test("decoding ssh public key " + index, function(assert) { var key = decodePublicKey(public); assert.equal(key.type, "ssh-rsa", "type"); assert.equal(key.name, "name", "name"); - if(index===0) { + if (index === 0) { assert.deepEqual(key.key, public_ssh_decoded[0].key, "key"); } - }) + }); }); -QUnit.test("Encoding ssh public key", function (assert) { +QUnit.test("Encoding ssh public key", function(assert) { var result = encodePublicKey(jwk_public, "name"); - assert.equal(result, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name"); + assert.equal( + result, + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name" + ); }); -QUnit.test("base64url", function (assert){ +QUnit.test("base64url", function(assert) { var result = stringToArray(base64urlDecode(jwk_public.n)); assert.equal(result.length, 256); - }); -QUnit.test("high bit", function (assert) { +QUnit.test("high bit", function(assert) { assert.deepEqual(checkHighestBit([0x80]), [0x00, 0x80]); - assert.deepEqual(checkHighestBit([0x0F]), [0x0F]); + assert.deepEqual(checkHighestBit([0x0f]), [0x0f]); }); -QUnit.test("jwk", function (assert){ +QUnit.test("jwk", function(assert) { var sshkey = encodePublicKey(jwk_public, "name"); - assert.deepEqual(stringToArray(base64urlDecode(jwk_public.e)), [1,0,1]); - assert.equal(sshkey, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name") + assert.deepEqual(stringToArray(base64urlDecode(jwk_public.e)), [1, 0, 1]); + assert.equal( + sshkey, + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDc9Ynq4OwU+BCjruh/chjDRK/Kr3lI2H0ZL7aCiu/rwbFYhhh4BKFob9S1N/ya9/q75UhSvnhF7YWgtzvwgJHedGEUfkPX981o/2GQwAcGponwBOKrh3v4995dMKLTJjSp9aQaO4+04LcgrtaacSSIDmpRWnLSgcU+KqgDI3KdYlEMUmRrTQkSkzVAXIXVPPc8QanReUWcKVjjyIkb1SpeUOBrKycbeujawObx+29rQiNfVKntn0XJZByL7I5h9JPTTAodB3vPn4SjdxN3kzQGFxVd5HSCmt0rVNMMtkvOcUlDfodjCtQURe9PKZ7q+Rl809FvTPwo+rkazt26MT9z name" + ); }); -[{len:0x01, octet:[0x01]}, - {len:0x104, octet:[0x01, 0x04]}, - {len:0xFF32, octet:[0xFF,0x32]}, - {len:0x1000000, octet:[1,0,0,0]}, - {len:0x7FFFFFFF, octet:[0x7F,0xFF,0xFF,0xFF]}, //biggest one -].forEach(function (t) { - QUnit.test("Integer to Octet:" + t.len, function (assert) { +[ + { len: 0x01, octet: [0x01] }, + { len: 0x104, octet: [0x01, 0x04] }, + { len: 0xff32, octet: [0xff, 0x32] }, + { len: 0x1000000, octet: [1, 0, 0, 0] }, + { len: 0x7fffffff, octet: [0x7f, 0xff, 0xff, 0xff] }, //biggest one +].forEach(function(t) { + QUnit.test("Integer to Octet:" + t.len, function(assert) { assert.deepEqual(integerToOctet(t.len), t.octet, t.len); }); }); -[{len: 0x34 , asn: [0x34]}, - {len: 256 , asn: [0x80 + 2, 0x01, 0x00]}, - {len: 0x134 , asn: [0x80 + 2, 0x01, 0x34]}, - {len: 0x12345 , asn: [0x80 + 3, 0x01, 0x23, 0x45]}, - {len: 0x123456 , asn: [0x80 + 3, 0x12, 0x34, 0x56]}, - ].forEach(function (t) { - QUnit.test("ASN.1 Len Writing:" + t.len, function (assert) { +[ + { len: 0x34, asn: [0x34] }, + { len: 256, asn: [0x80 + 2, 0x01, 0x00] }, + { len: 0x134, asn: [0x80 + 2, 0x01, 0x34] }, + { len: 0x12345, asn: [0x80 + 3, 0x01, 0x23, 0x45] }, + { len: 0x123456, asn: [0x80 + 3, 0x12, 0x34, 0x56] }, +].forEach(function(t) { + QUnit.test("ASN.1 Len Writing:" + t.len, function(assert) { assert.deepEqual(asnEncodeLen(t.len), t.asn, t.len); }); }); -QUnit.test("encodePrivateKey", function (assert) { +QUnit.test("encodePrivateKey", function(assert) { var encoded = encodePrivateKey(jwk_private); console.log(encoded); assert.ok(true); -}); \ No newline at end of file +});