Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: octokey/octokey-js-client
base: b65bc769cf
...
head fork: octokey/octokey-js-client
compare: 8cb214dbd4
  • 2 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
224 js/forge/prng.js
View
@@ -0,0 +1,224 @@
+/**
+ * A javascript implementation of a cryptographically-secure
+ * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is mostly
+ * followed here. SHA-1 is used instead of SHA-256.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010-2012 Digital Bazaar, Inc.
+ */
+(function() {
+
+// define forge
+if(typeof(window) !== 'undefined') {
+ var forge = window.forge = window.forge || {};
+ forge.prng = {};
+}
+// define node.js module
+else if(typeof(module) !== 'undefined' && module.exports) {
+ var forge = {
+ md: require('./md'),
+ util: require('./util')
+ };
+ forge.md.sha1.create();
+ module.exports = forge.prng = {};
+}
+
+/* PRNG API */
+var prng = forge.prng;
+
+/**
+ * Creates a new PRNG context.
+ *
+ * A PRNG plugin must be passed in that will provide:
+ *
+ * 1. A function that initializes the key and seed of a PRNG context. It
+ * will be given a 16 byte key and a 16 byte seed. Any key expansion
+ * or transformation of the seed from a byte string into an array of
+ * integers (or similar) should be performed.
+ * 2. The cryptographic function used by the generator. It takes a key and
+ * a seed.
+ * 3. A seed increment function. It takes the seed and return seed + 1.
+ * 4. An api to create a message digest.
+ *
+ * For an example, see random.js.
+ *
+ * @param plugin the PRNG plugin to use.
+ */
+prng.create = function(plugin) {
+ var ctx = {
+ plugin: plugin,
+ key: null,
+ seed: null,
+ time: null,
+ // number of reseeds so far
+ reseeds: 0,
+ // amount of data generated so far
+ generated: 0
+ };
+
+ // create 32 entropy pools (each is a message digest)
+ var md = plugin.md;
+ var pools = new Array(32);
+ for(var i = 0; i < 32; ++i) {
+ pools[i] = md.create();
+ }
+ ctx.pools = pools;
+
+ // entropy pools are written to cyclically, starting at index 0
+ ctx.pool = 0;
+
+ /**
+ * Generates random bytes.
+ *
+ * @param count the number of random bytes to generate.
+ *
+ * @return count random bytes as a string.
+ */
+ ctx.generate = function(count) {
+ // do first seed if necessary
+ if(ctx.key === null) {
+ _reseed();
+ }
+
+ // simple generator using counter-based CBC
+ var cipher = ctx.plugin.cipher;
+ var increment = ctx.plugin.increment;
+ var formatKey = ctx.plugin.formatKey;
+ var formatSeed = ctx.plugin.formatSeed;
+ var b = forge.util.createBuffer();
+ while(b.length() < count) {
+ // generate the random bytes
+ var bytes = cipher(ctx.key, ctx.seed);
+ ctx.generated += bytes.length;
+ b.putBytes(bytes);
+
+ // generate bytes for a new key and seed
+ ctx.key = formatKey(cipher(ctx.key, increment(ctx.seed)));
+ ctx.seed = formatSeed(cipher(ctx.key, ctx.seed));
+
+ // if amount of data generated is greater than 1 MiB, reseed
+ if(ctx.generated >= 1048576) {
+ // only do reseed at most 10 times/second (every 100 ms)
+ var now = +new Date();
+ if(now - ctx.time < 100) {
+ _reseed();
+ }
+ }
+ }
+
+ return b.getBytes(count);
+ };
+
+ /**
+ * Private function that reseeds a generator.
+ */
+ function _reseed() {
+ // not enough seed data... but we need to get going so just
+ // be sad and add some weak random data
+ if(ctx.pools[0].messageLength < 32) {
+ /* Draws from Park-Miller "minimal standard" 31 bit PRNG,
+ implemented with David G. Carta's optimization: with 32 bit math
+ and without division (Public Domain). */
+ var needed = (32 - ctx.pools[0].messageLength) << 5;
+ var b = '';
+ var hi, lo, next;
+ var seed = Math.floor(Math.random() * 0xFFFF);
+ while(b.length < needed) {
+ lo = 16807 * (seed & 0xFFFF);
+ hi = 16807 * (seed >> 16);
+ lo += (hi & 0x7FFF) << 16;
+ lo += hi >> 15;
+ lo = (lo & 0x7FFFFFFF) + (lo >> 31);
+ seed = lo & 0xFFFFFFFF;
+
+ // consume lower 3 bytes of seed
+ for(var i = 0; i < 3; ++i) {
+ // throw in more pseudo random
+ next = seed >>> (i << 3);
+ next ^= Math.floor(Math.random() * 0xFF);
+ b += String.fromCharCode(next & 0xFF);
+ }
+ }
+ // will automatically reseed in collect
+ ctx.collect(b);
+ }
+ else {
+ // create a SHA-1 message digest
+ var md = forge.md.sha1.create();
+
+ // digest pool 0's entropy and restart it
+ md.update(ctx.pools[0].digest().getBytes());
+ ctx.pools[0].start();
+
+ // digest the entropy of other pools whose index k meet the
+ // condition '2^k mod n == 0' where n is the number of reseeds
+ var k = 1;
+ for(var i = 1; i < 32; ++i) {
+ // prevent signed numbers from being used
+ k = (k == 31) ? 2147483648 : (k << 2);
+ if(k % ctx.reseeds === 0) {
+ md.update(ctx.pools[i].digest().getBytes());
+ ctx.pools[i].start();
+ }
+ }
+
+ // get digest for key bytes and iterate again for seed bytes
+ var keyBytes = md.digest().getBytes();
+ md.start();
+ md.update(keyBytes);
+ var seedBytes = md.digest().getBytes();
+
+ // update
+ ctx.key = ctx.plugin.formatKey(keyBytes);
+ ctx.seed = ctx.plugin.formatSeed(seedBytes);
+ ++ctx.reseeds;
+ ctx.generated = 0;
+ ctx.time = +new Date();
+ }
+ }
+
+ /**
+ * Adds entropy to a prng ctx's accumulator.
+ *
+ * @param bytes the bytes of entropy as a string.
+ */
+ ctx.collect = function(bytes) {
+ // iterate over pools distributing entropy cyclically
+ var count = bytes.length;
+ for(var i = 0; i < count; ++i) {
+ ctx.pools[ctx.pool].update(bytes.substr(i, 1));
+ ctx.pool = (ctx.pool === 31) ? 0 : ctx.pool + 1;
+ }
+
+ // do reseed if pool 0 has at least 32 bytes (enough to create a new
+ // key and seed)
+ if(ctx.pools[0].messageLength >= 32) {
+ // only do reseed at most 10 times/second (every 100 ms)
+ var now = +new Date();
+ if(ctx.time === null || (now - ctx.time < 100)) {
+ _reseed();
+ }
+ }
+ };
+
+ /**
+ * Collects an integer of n bits.
+ *
+ * @param i the integer entropy.
+ * @param n the number of bits in the integer.
+ */
+ ctx.collectInt = function(i, n) {
+ var bytes = '';
+ do {
+ n -= 8;
+ bytes += String.fromCharCode((i >> n) & 0xFF);
+ }
+ while(n > 0);
+ ctx.collect(bytes);
+ };
+
+ return ctx;
+};
+
+})();
123 js/forge/random.js
View
@@ -0,0 +1,123 @@
+/**
+ * An API for getting cryptographically-secure random bytes. The bytes are
+ * generated using the Fortuna algorithm devised by Bruce Schneier and
+ * Niels Ferguson.
+ *
+ * Getting strong random bytes is not yet easy to do in javascript. The only
+ * truish random entropy that can be collected is from the mouse, keyboard, or
+ * from timing with respect to page loads, etc. This generator makes a poor
+ * attempt at providing random bytes when those sources haven't yet provided
+ * enough entropy to initially seed or to reseed the PRNG.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2009-2012 Digital Bazaar, Inc.
+ */
+(function($) {
+
+// define forge
+if(typeof(window) !== 'undefined') {
+ var forge = window.forge = window.forge || {};
+ forge.random = {};
+}
+// define node.js module
+else if(typeof(module) !== 'undefined' && module.exports) {
+ var forge = {
+ aes: require('./aes'),
+ md: require('./md'),
+ prng: require('./prng'),
+ util: require('./util')
+ };
+ module.exports = forge.random = {};
+}
+
+// the default prng plugin, uses AES-128
+var prng_aes = {};
+var _prng_aes_output = new Array(4);
+var _prng_aes_buffer = forge.util.createBuffer();
+prng_aes.formatKey = function(key) {
+ // convert the key into 32-bit integers
+ var tmp = forge.util.createBuffer(key);
+ key = new Array(4);
+ key[0] = tmp.getInt32();
+ key[1] = tmp.getInt32();
+ key[2] = tmp.getInt32();
+ key[3] = tmp.getInt32();
+
+ // return the expanded key
+ return forge.aes._expandKey(key, false);
+};
+prng_aes.formatSeed = function(seed) {
+ // convert seed into 32-bit integers
+ tmp = forge.util.createBuffer(seed);
+ seed = new Array(4);
+ seed[0] = tmp.getInt32();
+ seed[1] = tmp.getInt32();
+ seed[2] = tmp.getInt32();
+ seed[3] = tmp.getInt32();
+ return seed;
+};
+prng_aes.cipher = function(key, seed) {
+ forge.aes._updateBlock(key, seed, _prng_aes_output, false);
+ _prng_aes_buffer.putInt32(_prng_aes_output[0]);
+ _prng_aes_buffer.putInt32(_prng_aes_output[1]);
+ _prng_aes_buffer.putInt32(_prng_aes_output[2]);
+ _prng_aes_buffer.putInt32(_prng_aes_output[3]);
+ return _prng_aes_buffer.getBytes();
+};
+prng_aes.increment = function(seed) {
+ // FIXME: do we care about carry or signed issues?
+ ++seed[3];
+ return seed;
+};
+prng_aes.md = forge.md.sha1;
+
+// create default prng context
+var _ctx = forge.prng.create(prng_aes);
+
+// get load time entropy
+_ctx.collectInt(+new Date(), 32);
+
+// add some entropy from navigator object
+if(typeof(navigator) !== 'undefined') {
+ var _navBytes = '';
+ for(var key in navigator) {
+ if(typeof(navigator[key]) == 'string') {
+ _navBytes += navigator[key];
+ }
+ }
+ _ctx.collect(_navBytes);
+ _navBytes = null;
+}
+
+// add mouse and keyboard collectors if jquery is available
+if($) {
+ // set up mouse entropy capture
+ $().mousemove(function(e) {
+ // add mouse coords
+ _ctx.collectInt(e.clientX, 16);
+ _ctx.collectInt(e.clientY, 16);
+ });
+
+ // set up keyboard entropy capture
+ $().keypress(function(e) {
+ _ctx.collectInt(e.charCode, 8);
+ });
+}
+
+/* Random API */
+
+/**
+ * Gets random bytes. This method tries to make the bytes more
+ * unpredictable by drawing from data that can be collected from
+ * the user of the browser, ie mouse movement.
+ *
+ * @param count the number of random bytes to get.
+ *
+ * @return the random bytes in a string.
+ */
+forge.random.getBytes = function(count) {
+ return _ctx.generate(count);
+};
+
+})(typeof(jQuery) !== 'undefined' ? jQuery : null);
230 js/private_key.js
View
@@ -1,80 +1,19 @@
var octokey = octokey || {};
-octokey.privateKey = function (raw_private_key) {
+octokey.privateKey = function (private_key_pem) {
var _public = {},
private_key = null,
- decrypt_key = null, // function: passphrase -> private key
passphrase_timeout = 5 * 60 * 1000, // 5 minutes in milliseconds
passphrase_timer = null;
- // This matches the logic in OpenSSL's crypto/pem/pem_lib.c
- if (raw_private_key.match(/-----BEGIN RSA PRIVATE KEY-----/)) {
- // Unencrypted PEM, or encrypted key in OpenSSL 'traditional' format.
- var key_parts = raw_private_key.match(/-----BEGIN [^\n]+-----((?:(?:\n[^\n]+:[^\n]*)*\n\s*\n)?)([A-Za-z0-9+\/=\s]+)-----END [^\n]+-----/);
- if (!key_parts) {
- throw 'the RSA private key file has an invalid structure';
- }
- var headers = key_parts[1].trim(), data = forge.util.createBuffer(forge.util.decode64(key_parts[2]));
-
- // The header format parsing is very strict in OpenSSL's PEM_get_EVP_CIPHER_INFO
- // (e.g. reordering of headers is not allowed, case sensitive).
- var cipher_info = headers.match(/Proc-Type: 4,ENCRYPTED\s*DEK-Info: ([A-Z0-9\-]*),([0-9A-Fa-f]+)/);
- if (cipher_info) {
- var init_vector = forge.util.hexToBytes(cipher_info[2]);
- var cipher = {
- 'AES-128-CBC': {algorithm: forge.aes, key_length: 16},
- 'DES-EDE3-CBC': {algorithm: forge.des, key_length: 24}
- }[cipher_info[1]];
-
- if (!cipher) {
- throw 'unsupported private key encryption cipher: ' + cipher_info[1];
- }
-
- decrypt_key = function (passphrase) {
- // The following algorithm for deriving the key is hard-coded in OpenSSL
- // in EVP_BytesToKey (crypto/evp/evp_key.c) and PEM_do_header (crypto/pem/pem_lib.c)
- var key = new forge.util.ByteBuffer(), digest = '';
- while (key.length() < cipher.key_length) {
- var md = forge.md.md5.create();
- md.update(digest);
- md.update(passphrase, 'utf-8');
- md.update(init_vector.substr(0, 8)); // the first PKCS5_SALT_LEN (8) bytes of IV are used as salt
- digest = md.digest().getBytes();
- key.putBytes(digest.substr(0, cipher.key_length - key.length()));
- }
-
- var decrypt = cipher.algorithm.startDecrypting(key, forge.util.createBuffer(init_vector));
- decrypt.update(data);
- decrypt.finish();
- try {
- return forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(decrypt.output));
- } catch(error) {
- return false;
- }
- };
-
- } else {
- // No headers indicating that the key is encrypted
- private_key = forge.pki.privateKeyFromPem(raw_private_key);
- }
-
- } else if (raw_private_key.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
- // Encrypted PKCS8, supported directly by Forge
- decrypt_key = function (passphrase) {
- return forge.pki.decryptRsaPrivateKey(raw_private_key, passphrase);
- };
-
- } else if (raw_private_key.match(/-----BEGIN PRIVATE KEY-----/)) {
- // TODO unencrypted PKCS8
-
- } else {
- var keytype = raw_private_key.match(/-----BEGIN ([^\n]+) PRIVATE KEY-----/);
- if (keytype && keytype[1]) {
- throw keytype[1] + ' private keys are not supported';
- } else {
- throw 'unrecognised private key format';
- }
+ // Only support PKCS8-encoded keys here, to discourage people from using the less secure
+ // 'traditional' SSH key format. octokey.privateKey.convert can perform the conversion.
+ if (private_key_pem.match(/-----BEGIN PRIVATE KEY-----/)) {
+ // Unencrypted PKCS8
+ private_key = forge.pki.privateKeyFromPem(private_key_pem);
+ } else if (!private_key_pem.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
+ throw 'Unsupported private key type. Please use octoKey.privateKey.convert to convert it';
}
_public.passphrase_required = !private_key;
@@ -83,11 +22,17 @@ octokey.privateKey = function (raw_private_key) {
// false if the passphrase is incorrect. The decrypted key is held in this
// object for 5 minutes, and then discarded.
_public.setPassphrase = function (passphrase) {
- if (!decrypt_key) {
+ // Ignore passphrase if the key wasn't encrypted in the first place
+ if (private_key && !passphrase_timer) {
return true;
}
- private_key = decrypt_key(passphrase);
+ try {
+ private_key = forge.pki.decryptRsaPrivateKey(private_key_pem, passphrase);
+ } catch (error) {
+ console.log(error); // FIXME
+ return false;
+ }
_public.passphrase_required = !private_key;
if (passphrase_timer) {
@@ -132,3 +77,146 @@ octokey.privateKey = function (raw_private_key) {
return _public;
};
+
+
+// Takes a private key in any supported format (currently either PKCS#8 PEM or the SSH 'traditional'
+// private key format) and converts it into a consistent PKCS#8 PEM format. If one passphrase is
+// given, it is used both for decrypting the input and for encrypting the output. If two passphrases
+// are given, the first is used for decrypting and the second is used for encrypting. (This allows
+// you to to change the passphrase for a key.) If the output passphase is empty, the output is
+// unencrypted.
+//
+// Returns an object with properties:
+// * pem: The converted private key PEM string, or null if conversion failed
+// * errors: Array of errors that occurred during conversion (null if successful)
+octokey.privateKey.convert = function (input_pem, input_passphrase, output_passphrase) {
+
+ // Encryption options for the output key
+ var output_options = {
+ encAlg: 'aes128',
+
+ // Iteration count -- the higher, the harder it is to brute-force the passphrase. OpenSSL
+ // uses 2048. This takes about 700ms in Chrome 21's JS engine on an Intel Core 2 Duo laptop.
+ // TODO benchmark this on more browsers & CPUs.
+ count: 2048,
+
+ // 128 bits of salt, twice the minimum recommended by PBKDF2 (RFC 2898) -- superstitiously
+ // making it bigger because I'm unsure of the quality of entropy we can get from the
+ // browser. Though hash crackers are so fast these days that size of salt doesn't make too
+ // much of a difference anyway.
+ saltSize: 16
+ };
+
+ var private_key = null;
+ if (typeof output_passphrase === 'undefined') {
+ output_passphrase = input_passphrase;
+ }
+
+ function error(message) {
+ return {pem: null, errors: [message]};
+ }
+
+ // This matches the logic in OpenSSL's crypto/pem/pem_lib.c
+ if (input_pem.match(/-----BEGIN RSA PRIVATE KEY-----/)) {
+
+ // Unencrypted PEM, or encrypted key in OpenSSL 'traditional' format.
+ var key_parts = input_pem.match(/-----BEGIN [^\n]+-----((?:(?:\s*\n[^\n]+:[^\n]*)*\n\s*\n)?)([A-Za-z0-9+\/=\s]+)-----END [^\n]+-----/);
+ if (!key_parts) {
+ return error('the RSA private key file has an invalid structure');
+ }
+ var headers = key_parts[1].trim(), data = forge.util.createBuffer(forge.util.decode64(key_parts[2]));
+
+ // The header format parsing is very strict in OpenSSL's PEM_get_EVP_CIPHER_INFO
+ // (headers cannot be reordered, added or removed; and the parsing is case sensitive).
+ var cipher_info = headers.match(/Proc-Type: 4,ENCRYPTED\s*DEK-Info: ([A-Z0-9\-]*),([0-9A-Fa-f]+)/);
+ if (cipher_info) {
+ var init_vector = forge.util.hexToBytes(cipher_info[2]);
+ var cipher = {
+ 'AES-128-CBC': {algorithm: forge.aes, key_length: 16},
+ 'DES-EDE3-CBC': {algorithm: forge.des, key_length: 24}
+ }[cipher_info[1]];
+
+ if (!cipher) {
+ return error('unsupported private key encryption cipher: ' + cipher_info[1]);
+ }
+ if (!input_passphrase) {
+ return error('input passphrase required');
+ }
+
+ // The following algorithm for deriving the key is hard-coded in OpenSSL
+ // in EVP_BytesToKey (crypto/evp/evp_key.c) and PEM_do_header (crypto/pem/pem_lib.c)
+ var key = new forge.util.ByteBuffer(), digest = '';
+ while (key.length() < cipher.key_length) {
+ var md = forge.md.md5.create();
+ md.update(digest);
+ md.update(input_passphrase, 'utf-8');
+ md.update(init_vector.substr(0, 8)); // the first PKCS5_SALT_LEN (8) bytes of IV are used as salt
+ digest = md.digest().getBytes();
+ key.putBytes(digest.substr(0, cipher.key_length - key.length()));
+ }
+
+ var decrypt = cipher.algorithm.startDecrypting(key, forge.util.createBuffer(init_vector));
+ decrypt.update(data);
+ decrypt.finish();
+
+ // The most likely effect of an incorrect passphrase is that the decrypted string is not
+ // valid DER; but it could also be valid DER but not the right kind of ASN.1 structure.
+ var asn1;
+ try {
+ private_key = forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(decrypt.output));
+ } catch (e) {
+ var message = (typeof(e) === 'object' ? e.message : e);
+ return {pem: null, errors: ['incorrect input passphrase', message]};
+ }
+
+ } else {
+ // No headers indicating that the key is encrypted
+ private_key = forge.pki.privateKeyFromPem(input_pem);
+ }
+
+ } else if (input_pem.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
+ // Encrypted PKCS8, supported directly by Forge
+ if (!input_passphrase) {
+ return error('input passphrase required');
+ }
+ try {
+ private_key = forge.pki.decryptRsaPrivateKey(input_pem, input_passphrase);
+ } catch (e) {
+ return error(typeof(e) === 'object' ? e.message : e);
+ }
+ if (!private_key) {
+ return error('incorrect input passphrase');
+ }
+
+ } else if (input_pem.match(/-----BEGIN PRIVATE KEY-----/)) {
+ // Unencrypted PKCS8
+ try {
+ private_key = forge.pki.privateKeyFromPem(input_pem);
+ } catch (e) {
+ return error(typeof(e) === 'object' ? e.message : e);
+ }
+
+ } else {
+ var keytype = input_pem.match(/-----BEGIN ([^\n]+) PRIVATE KEY-----/);
+ if (keytype && keytype[1]) {
+ return error(keytype[1] + ' private keys are not supported');
+ } else {
+ return error('unrecognised private key format');
+ }
+ }
+
+ var output_pem;
+ if (output_passphrase) {
+ // Generate PKCS#8 output
+ output_pem = forge.pki.encryptRsaPrivateKey(private_key, output_passphrase, output_options);
+ } else {
+ // Unencrypted key :( not sure if we should even allow this!
+ var key_info = forge.pki.wrapRsaPrivateKey(forge.pki.privateKeyToAsn1(private_key));
+ output_pem = [
+ '-----BEGIN PRIVATE KEY-----',
+ forge.util.encode64(forge.asn1.toDer(key_info).getBytes()),
+ '-----END PRIVATE KEY-----'
+ ].join('\r\n');
+ }
+ return {pem: output_pem, errors: null};
+};
60 js/test.html
View
@@ -12,6 +12,8 @@
<script src="forge/hmac.js" type="text/javascript"></script>
<script src="forge/pbkdf2.js" type="text/javascript"></script>
<script src="forge/pki.js" type="text/javascript"></script>
+ <script src="forge/prng.js" type="text/javascript"></script>
+ <script src="forge/random.js" type="text/javascript"></script>
<script src="lib/jquery-1.7.2.js" type="text/javascript"></script>
<script src="buffer.js" type="text/javascript"></script>
<script src="public_key.js" type="text/javascript"></script>
@@ -61,36 +63,34 @@
<p>
<label for="privatekey">Private key (PEM):</label><br>
- <textarea id="privatekey" name="privatekey">-----BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995
-
-BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM
-NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm
-idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h
-Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm
-BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK
-Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX
-isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J
-pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf
-IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W
-NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI
-XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1
-bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN
-XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS
-WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi
-ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF
-405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz
-DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j
-CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt
-bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO
-+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP
-A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU
-1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti
-RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4
-VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM
-5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv
------END RSA PRIVATE KEY-----
+ <textarea id="privatekey" name="privatekey">-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8wjLP42FK43SO
+aSwTPwsnVXX97WtnSSuIPtN00qj62nbaPyrfIOwiWqb4FIrsAoa97uJgYovDZcTk
+BKTzpwLj1rKpQva3elwxFfQcpz4T3jDX+DxODLF36BQSf2QoAsNUTdP12tiDJOgK
+2Qqp//+6iZUHtgM/csZckDpMv0uzNn+do4LFm70ddqJYuZ3r/lFoGLAfWLGBmVMW
+EajYZWP8CfN8/+LLtQ3DPQ9rK0jPwWZDKa9tOTaqWryk33iv9Ecx1ryHyz1Q0Psg
+RixlnaRibr3RKYyFtPl0PBgT92hhrngwWVbkTwOmvvjHCof4D+rV7dUlck+dm9Ss
+xTh5MF5BAgMBAAECggEAHyKBMx00XR/rh9n9NSd+9Xv5PGs+/ghpr819H5Xn3YjP
+dexZa/iIOpptVBo/V/KKuyV+HZvjpdVRhrLlanMv3Nj7G0Q6YcVDE62lWFyVtr09
+nUIK4GzjkP2s4eg1Ywwhn/Q+dB0m/WrHA5MNWUEqs2AiPuVc38hUw8vece5T1Dlu
+il7myYgSccIQ5DTbdg/5kMZGcimlOHe4IkaP+RifdhbaWn5c3eOBrKG+uZnauv0B
+n6Tb6/DUrKtLKCwWTYwuc9PC1Zgd0/aSyia4KnXrWQuboAhuuOTa4l2E7GaqEe2O
+cZiEGTNQpNEEDfT02LIPdTOPCeHUd/AY31fgr2gr4QKBgQDc6EQPDSVCXVzExB6E
+9xUhqo+gN3L7N49NZ1qwH4y2i9ttlUSyGbvuc6XL+lWpbIpUu2Rt0rT5ZdjFYJLs
+KyepQOO+7Q/YbJ2R+3jLFGO3iFE6R9N8E48NqVjeohzHDpoMb/glN7wPcmT/iBUo
+dKpkKnNkSU/f1Bnr4Hi46A0btQKBgQDavob5IihsyDxIkXxdzS+sGWyplxfKOgIN
+BiGg3ume1VfEKAixL8MLIXj3S7AOMsS35zJYRZNtr1xQ5Cs9tuYA+mq1xOkQ9hhA
+6jmlTawHTi5DvwuLIik95FwQzwckCK4zmS/xdLLtkgtsmbsAjr4LfqX2V/g6OXYk
+3kd6IxeH3QKBgAJgWUMUMB9ro7DWL0Hc6pRHIm5lyk3bhiYeA4K7hCb+kCi4n1mP
+H09sXQ85rSw6Z66LqYPo7Vt1dgSBinMR78ZJVWnbYP1CBdvpKZ59pKj3xW/sD+FY
+0IQkGzmh4s9dX9jcZ333AIXmBCIfk6Kwxph3QHCGvV46COFZs9LBZEq1AoGBAJHH
+P/RwizhUCbjjHFr3D7pGB2DOTRB8sDk1yNuJM3CjBaa4d3J+PiIA4LkOO/p6Yxec
+gLTLSYFjyMYwCZFLfuVP/iW9YQXovCkm79v2c5s6wyJrA6ppzcptkd2x2zRshIvm
+n0jvWP9ywSJTIYkxl/3ZHYma/tbuzImtiT0gfeIRAoGAYktMdBJRRVLzjZvc3XAu
+tH8syHcUwlx0C4uQX6kTPmvjI2KupaTVA9UPeRaiq75Gr+bbeND341+cz/lJhlOA
+DpqkJEcTmB0nq4Q8/gXdsgqcVG8Is/8ibCyKt3P3rQALgKUlbKG0tZZ/8cb9Axwl
+1wLGJfQftS76LNdTph8SVWM=
+-----END PRIVATE KEY-----
</textarea>
</p>
55 test/auth_request_spec.js
View
@@ -1,33 +1,34 @@
describe('octokey.authRequest', function () {
var private_key, private_key_pem = [
- '-----BEGIN RSA PRIVATE KEY-----',
- 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
- 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
- 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
- '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
- 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
- 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
- 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
- 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
- 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
- '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
- 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
- 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
- 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
- '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
- 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
- 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
- '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
- 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
- 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
- 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
- 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
- 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
- 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
- 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
- '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
- '-----END RSA PRIVATE KEY-----'
+ '-----BEGIN PRIVATE KEY-----',
+ 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsJAbbXe63OoTl',
+ 'hWNNUXl7EZEKQT064zssKn8U6492gesVReTLYxBzB/b0ncDiLzPGkRL1F1IRTqIg',
+ 'SyjD3zAiLTHX8ud/HqMbljT+JCwbU1GU0IJrJ0a8ZoL4/DBaLBhFzs+0GBXgo4BV',
+ 'cxl0wHmcVFp6RDWSx0f/frQoIaCWM+3NM2XT173tAHAcUhnd5rkSKH8eKt5OtFKe',
+ '8X7LCbC7Ffi5sOvRl5Jx64XZLlYtcfb5NXQtRvZjEXLfAcMzhl/AEHlT3FmGQhvN',
+ 'IvX0zvHwt4Qbce4MOfAR9T3fI5ir2XfO6cJoQbg22Inakdic3pMohCiebrSlWb1+',
+ 'dG+xus4nAgMBAAECggEAUmZQbfNx0H2PnfqSxTEJ8JJBFmKSN70L1yFkgZQcsUKR',
+ 'UDaLxZvfBOE8Im3dJagopZVOOMn3+9bjHog7tD8C4Gk34cIhdCUrOIpXRYv1HQNu',
+ 'GskQlNBRON/tM9gsIQ3YdJoBrJiW6Ff7l2uoNM3pqJ5sTPiXmHISEcgJ3ufx4dOi',
+ 'rRZZcuA4RI5H4xE19rBK+eqDpYdBNGK+/FDwRdhGrUQcwXg0sp16qwZP0Vmzg+4D',
+ 'kauO/BfC1w7fedpQlHUfjoZ49+PPDk2p17YJXnSM8W5JizrxNMDhiD6w8Ntdxs7k',
+ 'rMbNhrXseNlKYYwd47l2D53CnC0txshuAoGy5/GDYQKBgQDWd++5mXRNVwLUbn37',
+ 'HTef2aN1bNwSMUeC8hOt99uhwvUig0BAPlepINSEh++a4KcmQNV45upytoqQxxIw',
+ 'OjhTMmgy8H1ll8ayR4zMvLtCKLSMfKXJVzLxHweW4DvEdFo2RtHZZi84OS5CUNvN',
+ '57wuR8d7bFuNhwrahUhXH+K1MQKBgQDNebukg8DPWKknCh84Vhtl4L4MzSbjEJ8s',
+ 'mOYJXsA/ch6up4kTnRbzt5fE+VQrjKBzWfluvT6EawJlCZ20lqG3sCKBbxMijyeQ',
+ 'F84/Ls6t1MOxE9q9IS9Byi5+TbEXi/4bAlDmZH/W92AbkBamNqdFipKh3pZjh7j5',
+ 'Oi4ugf5C1wKBgEtTcKU1Wn3Xc6z86c6K4oCIRjr18w2ItV8ueu79QOy9tq9affSS',
+ 'cON8Hmg1/lfdU5fB6bg/ElUb++sBoEfECwhdie9qPAC0oBr5umAmYXbJKTC2jtv6',
+ 'fm/lwBqjwxoq64xor0e19hu/KZotICZfn955Y3zcAAPjTFKXwWKoVk3hAoGAVepf',
+ 'vnNkzI4q9Gr0EO9YN2oYrWuepfUOSWyJS0O2KRFDqQ3ecDgQH8oViMQkIK2FbZYi',
+ 'iN5SZLYF4095BMizSAY691oFaz7mcQazM5IT03WSedlubgSRKPYsKJ29vbXIg2yd',
+ 'fShql+0J35yJC+jwWlCN7WcQBP/8JSHhq4qpUTMCgYAN8yM5Dk6Nt+VUB+voued/',
+ '86g7lvH0vj/fC2MJuUjNJMdg/TYLIRm23Pk0e9nFuqbnwxWqvxJqpQd3/6Rhd0xW',
+ 'QySQZqr+cbkeszgaW6sohx+FMDc2MHxnyGvcpom4YXkwTTk9thWJ1Cu/VVenQ0kF',
+ 'ncK1GzU47lgOUzeEtLqGbg==',
+ '-----END PRIVATE KEY-----'
].join('\n');
beforeEach(function () {
632 test/private_key_spec.js
View
@@ -1,35 +1,36 @@
describe('octokey.privateKey', function () {
- describe('with an unencrypted SSH private key', function () {
+ describe('with an unencrypted private key', function () {
var private_key, private_key_pem = [
- '-----BEGIN RSA PRIVATE KEY-----',
- 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
- 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
- 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
- '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
- 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
- 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
- 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
- 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
- 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
- '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
- 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
- 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
- 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
- '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
- 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
- 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
- '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
- 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
- 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
- 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
- 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
- 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
- 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
- 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
- '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
- '-----END RSA PRIVATE KEY-----'
- ].join('\n');
+ '-----BEGIN PRIVATE KEY-----',
+ 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsJAbbXe63OoTl',
+ 'hWNNUXl7EZEKQT064zssKn8U6492gesVReTLYxBzB/b0ncDiLzPGkRL1F1IRTqIg',
+ 'SyjD3zAiLTHX8ud/HqMbljT+JCwbU1GU0IJrJ0a8ZoL4/DBaLBhFzs+0GBXgo4BV',
+ 'cxl0wHmcVFp6RDWSx0f/frQoIaCWM+3NM2XT173tAHAcUhnd5rkSKH8eKt5OtFKe',
+ '8X7LCbC7Ffi5sOvRl5Jx64XZLlYtcfb5NXQtRvZjEXLfAcMzhl/AEHlT3FmGQhvN',
+ 'IvX0zvHwt4Qbce4MOfAR9T3fI5ir2XfO6cJoQbg22Inakdic3pMohCiebrSlWb1+',
+ 'dG+xus4nAgMBAAECggEAUmZQbfNx0H2PnfqSxTEJ8JJBFmKSN70L1yFkgZQcsUKR',
+ 'UDaLxZvfBOE8Im3dJagopZVOOMn3+9bjHog7tD8C4Gk34cIhdCUrOIpXRYv1HQNu',
+ 'GskQlNBRON/tM9gsIQ3YdJoBrJiW6Ff7l2uoNM3pqJ5sTPiXmHISEcgJ3ufx4dOi',
+ 'rRZZcuA4RI5H4xE19rBK+eqDpYdBNGK+/FDwRdhGrUQcwXg0sp16qwZP0Vmzg+4D',
+ 'kauO/BfC1w7fedpQlHUfjoZ49+PPDk2p17YJXnSM8W5JizrxNMDhiD6w8Ntdxs7k',
+ 'rMbNhrXseNlKYYwd47l2D53CnC0txshuAoGy5/GDYQKBgQDWd++5mXRNVwLUbn37',
+ 'HTef2aN1bNwSMUeC8hOt99uhwvUig0BAPlepINSEh++a4KcmQNV45upytoqQxxIw',
+ 'OjhTMmgy8H1ll8ayR4zMvLtCKLSMfKXJVzLxHweW4DvEdFo2RtHZZi84OS5CUNvN',
+ '57wuR8d7bFuNhwrahUhXH+K1MQKBgQDNebukg8DPWKknCh84Vhtl4L4MzSbjEJ8s',
+ 'mOYJXsA/ch6up4kTnRbzt5fE+VQrjKBzWfluvT6EawJlCZ20lqG3sCKBbxMijyeQ',
+ 'F84/Ls6t1MOxE9q9IS9Byi5+TbEXi/4bAlDmZH/W92AbkBamNqdFipKh3pZjh7j5',
+ 'Oi4ugf5C1wKBgEtTcKU1Wn3Xc6z86c6K4oCIRjr18w2ItV8ueu79QOy9tq9affSS',
+ 'cON8Hmg1/lfdU5fB6bg/ElUb++sBoEfECwhdie9qPAC0oBr5umAmYXbJKTC2jtv6',
+ 'fm/lwBqjwxoq64xor0e19hu/KZotICZfn955Y3zcAAPjTFKXwWKoVk3hAoGAVepf',
+ 'vnNkzI4q9Gr0EO9YN2oYrWuepfUOSWyJS0O2KRFDqQ3ecDgQH8oViMQkIK2FbZYi',
+ 'iN5SZLYF4095BMizSAY691oFaz7mcQazM5IT03WSedlubgSRKPYsKJ29vbXIg2yd',
+ 'fShql+0J35yJC+jwWlCN7WcQBP/8JSHhq4qpUTMCgYAN8yM5Dk6Nt+VUB+voued/',
+ '86g7lvH0vj/fC2MJuUjNJMdg/TYLIRm23Pk0e9nFuqbnwxWqvxJqpQd3/6Rhd0xW',
+ 'QySQZqr+cbkeszgaW6sohx+FMDc2MHxnyGvcpom4YXkwTTk9thWJ1Cu/VVenQ0kF',
+ 'ncK1GzU47lgOUzeEtLqGbg==',
+ '-----END PRIVATE KEY-----'
+ ].join('\r\n');
beforeEach(function () {
private_key = octokey.privateKey(private_key_pem);
@@ -55,7 +56,7 @@ describe('octokey.privateKey', function () {
].join(''));
});
- // TODO more granular tests
+ // More detailed tests in auth_request_spec.js
it('should sign an auth request', function () {
var auth_request = octokey.authRequest({
challenge: 'KWm5PMQC3UYSFY/xgLimHvLhLcuIdwRZoDm4UfEADO0=',
@@ -82,39 +83,39 @@ describe('octokey.privateKey', function () {
});
- describe('with an AES-encrypted SSH private key', function () {
+ describe('with an encrypted private key', function () {
var private_key, private_key_pem = [
- '-----BEGIN RSA PRIVATE KEY-----',
- 'Proc-Type: 4,ENCRYPTED',
- 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
- '',
- 'BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
- 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
- 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
- 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
- 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
- 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
- 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
- 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
- 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
- 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
- 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
- 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
- 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
- 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
- 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
- '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
- 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
- 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
- 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
- '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
- 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
- '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
- 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
- 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
- '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
- '-----END RSA PRIVATE KEY-----'
- ].join('\n');
+ '-----BEGIN ENCRYPTED PRIVATE KEY-----',
+ 'MIIFJzBRBgkqhkiG9w0BBQ0wRDAjBgkqhkiG9w0BBQwwFgQQOfOEW0od494MHaIE',
+ 'eyaTVAICCAAwHQYJYIZIAWUDBAECBBAm7F6fzZkGXTdRgzYJids5BIIE0KZb+elW',
+ 'MwO8qFnC15gI52ek8OLfxFAY8K3NYeAKQeAJuEoVpysLnogABFZn/LirrIaXYyIg',
+ 'CtE/IJPR/xttNAActYJ2SL5lHsHAE8nLwZX+L3XbGoaHEKKa0QzetHlPZJX7Xp7O',
+ 's+1SGJaI6MCDlv54DvKr8J7UbIfHEU/1JpXHEqh1twZQPsBEhrwWUjwhRuxIqArP',
+ 'H/3cNkDYxqOHkPKYzv8D0DzUrKwbd0ZQbfdtXInE6IL2pglPutjsvqWDXyU86+Vz',
+ 'WETDOrozPn0r20LoGhLGflxSD2jqH4YUoHrcK+1202TYI6VFZ+AdjKeX3NL1ZhDn',
+ 'Zu8mjAToVBe5jIKBwwi+nHB6s1GxynYtVh+jxJ7LO5s15v80+1LtSuWi5hhGgRq4',
+ '5AoGJI0OjhiGlxxBmDIWp36Y+vRJx3SblEu/wLRBVKl2W7oj/6mrqjdNCtN5XAwN',
+ '1xY2nruZ6pirE4BJittzmkcXSosw65D8rw8TfLERBJ4IZtXf+JeFNoutPP0z2DO2',
+ 'dcX/5aa07XZWF63CgbSm7sitnbEGVJarUuNuD9QhO4CprnX0IYJtmPkf4JgfMYSB',
+ 'Etc/btiyj4N6vT7NMdZ9+67/DkxsgblxeUTG9wfUd3Zvo14FOkm7F5DT69z6LZhE',
+ 'mQi8317FomyXBM67fGvysNr8dkqAbg023X/ZCMB3B+C4ZkBoSRWIfdK6GG/v9I0a',
+ 'r/KES8Jc5OgWtY1Qq4sG3FFlhlQt7V52oiCsGshN69u5vymRWK/MjU8h01x2RXEi',
+ '+ARrHx4QGrzzdEGMn7dFrxfJ7ltIyeco9VedT2xiBaS+i0yPDsvtxPd9UXhIIG4A',
+ 'Ikd4v41534juDjuKIWbmRBSC/8YzHhmsUN/Wx4aQHpvHoJEwoJ2vi8umwc1kRi2r',
+ '2KmK1DksecFCVX00xYpNBb1+IeuPcPaboTtITs7SxWgiBHqS0cj85yntNuHGICLr',
+ '2fcfBE5F1A/KjLINqjbRM6XLM6iTCwRdmPaCN3C4hsessM8lK3k5vNsMHrbc0p5t',
+ 'I5f/4pkiJk/l6AT5HW/syt/z8PKEZkQ/tSYEqYDJ4mA/urcc+ViUnDQGp6fjQ0oD',
+ '0pvKnAywOBwXEQYibCGINQcjudHBdCxONYK5PcvRN2a90Z7lPzyPKQRkgNb9KlD0',
+ 'iuaCsOGWrU+QTaEP0B0M6i+K7NV2S4zOKD13xdGqHudwmVhVG6451uV9m4AtUYOV',
+ '3H7lBmzN8zq7tK0UvkI9Hxr8qvOx3PePPSuAH34c/m1QKsmiGM9biy+yF9CCnm4m',
+ 'MRwhnqrQMS3iVBibpxLc781d4p+xwpYsqAXSjIGHJed7xZjcHeznl8FPuKsjYd04',
+ 'sDl38bkIsMigCBiKZT+gZJnbZRglJHmmgJ/SKSidUVHk2cjEJ4BCSgTxzFECfO42',
+ '8HMeF55L3Y8G1oo8A4u84Z9z9pnnnd7J+LabOvTtTxuwlyhDwsR8TQ6WfJ8oQT5l',
+ '9UW7eaj7FghajPheGrqxldgefikk3a8ug+CoDUwYATvq2lEv4erHsSFCbnIOIlch',
+ '324GfodpLtPRyXrc1f5F6H7ZdhhMp8zKADEKGyRER8XF7GUIN0T+9h/UwU0P2Eez',
+ 'OKiDhXJniRaM7Z81WuAWeNeg5QaitrEnlxBY',
+ '-----END ENCRYPTED PRIVATE KEY-----'
+ ].join('\r\n');
beforeEach(function () {
private_key = octokey.privateKey(private_key_pem);
@@ -206,10 +207,143 @@ describe('octokey.privateKey', function () {
});
});
});
+});
- describe('with a 3DES-encrypted SSH private key', function () {
- var private_key, private_key_pem = [
+describe('octokey.privateKey.convert', function () {
+
+ describe('with an unencrypted traditional RSA key', function () {
+ var input_pem = [
+ '-----BEGIN RSA PRIVATE KEY-----',
+ 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
+ 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
+ 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
+ '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
+ 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
+ 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
+ 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
+ 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
+ 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
+ '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
+ 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
+ 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
+ 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
+ '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
+ 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
+ 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
+ '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
+ 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
+ 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
+ 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
+ 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
+ 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
+ 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
+ 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
+ '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
+ '-----END RSA PRIVATE KEY-----'
+ ].join('\n');
+
+ it('should convert to unencrypted PKCS#8 if no passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
+ expect(forge.pki.pemToDer(converted.pem).toHex()).toMatch(new RegExp(['^',
+ '308204bd', // top-level sequence node
+ '020100', // PrivateKeyInfo structure version 0
+ '300d06092a864886f70d0101010500', // AlgorithmIdentifier with rsaEncryption OID
+ '048204a7', // octet string
+ '308204a3', // 9-element sequence containing the RSA parameters
+ '020100', // RSA private key structure version 0
+ '0282010100ac2406db5deeb73a84e585634d51797b11910a413d3ae33b2c2a7f' // start of the RSA modulus
+ ].join('')));
+ });
+
+ it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'my new passphrase');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
+ expect(forge.pki.pemToDer(converted.pem).toHex()).toMatch(new RegExp(['^',
+ '308205273051', // top-level sequence nodes
+ '06092a864886f70d01050d', // OID for PBES2 in PKCS#5 version 2.0 (1.2.840.113549.1.5.13)
+ '30443023', // sequence nodes
+ '06092a864886f70d01050c', // OID for PBKDF2 in PKCS#5 version 2.0 (1.2.840.113549.1.5.12)
+ '3016', // sequence of salt and iteration count
+ '0410[0-9a-f]{32}', // 16 bytes of salt
+ '02020800', // iteration count = 2048
+ '301d', // sequence of cipher identifier and IV
+ '0609608648016503040102', // OID for AES-128-CBC (2.16.840.1.101.3.4.1.2)
+ '0410[0-9a-f]{32}' // 16 bytes of IV
+ ].join('')));
+ });
+ });
+
+
+ describe('with an AES-encrypted traditional RSA key', function () {
+ var input_pem = [
+ '-----BEGIN RSA PRIVATE KEY-----',
+ 'Proc-Type: 4,ENCRYPTED',
+ 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
+ '',
+ 'BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
+ 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
+ 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
+ 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
+ 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
+ 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
+ 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
+ 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
+ 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
+ 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
+ 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
+ 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
+ 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
+ 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
+ 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
+ '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
+ 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
+ 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
+ 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
+ '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
+ 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
+ '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
+ 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
+ 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
+ '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
+ '-----END RSA PRIVATE KEY-----'
+ ].join('\r\n');
+
+ it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password', '');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
+ var key = forge.pki.privateKeyFromPem(converted.pem);
+ expect(key.e.toString(10)).toBe('65537');
+ });
+
+ it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
+ var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
+ expect(key.e.toString(10)).toBe('65537');
+ });
+
+ it('should return an error if no passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('input passphrase required');
+ });
+
+ it('should return an error if the wrong passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'not the password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('incorrect input passphrase');
+ });
+ });
+
+
+ describe('with a 3DES-encrypted traditional RSA key', function () {
+ var input_pem = [
'-----BEGIN RSA PRIVATE KEY-----',
'Proc-Type: 4,ENCRYPTED',
'DEK-Info: DES-EDE3-CBC,6A9DD947BF7E78D3',
@@ -242,45 +376,345 @@ describe('octokey.privateKey', function () {
'-----END RSA PRIVATE KEY-----'
].join('\n');
- beforeEach(function () {
- private_key = octokey.privateKey(private_key_pem);
- private_key.setPassphrase('password');
+ var modulus = [
+ 'd46d9b87230533489282d90d12228fd9da5f68eab6eab57b48a1588b9e05cd01',
+ 'cd0ab29f14f871c69bdc2ce73c89bdc82ebca958bc30fea7dbf5ed1bd3dc72a8',
+ '20f786757d9d616c86b36b5e20c19a4a5fddf8b4709bc059f93e71b7ef5f57a7',
+ '815ffcee7287ccd1a8996719fae1fbf9c20b999b1c668de26b6eac8463dfea05',
+ 'ef77d8f2b3b511fb60e6930934ed00ee33afb1ade032dae641f5d029683e92a5',
+ 'eb2ef599daf1aeb885bfe6a24112deb8043e4a14f82327cdda038534a7bfd141',
+ 'e5ee81f3bdd94c34695352867e0f49c3c93a4f1037935a69d1920f772b66494d',
+ 'ede7eab3639e8eeef500a983c6508189650c64754432718bb7c820a0e6df646d'
+ ].join('');
+
+ it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password', '');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
+ var key = forge.pki.privateKeyFromPem(converted.pem);
+ expect(key.e.toString(16)).toBe('23');
+ expect(key.n.toString(16)).toBe(modulus);
});
- it('should extract the public key from the private key', function () {
- expect(private_key.publicKey().toBase64()).toBe([
- 'ssh-rsa ',
- 'AAAAB3NzaC1yc2EAAAABIwAAAQEA1G2bhyMFM0iSgtkNEiKP2dpfaOq26rV7SKFYi54FzQ',
- 'HNCrKfFPhxxpvcLOc8ib3ILrypWLww/qfb9e0b09xyqCD3hnV9nWFshrNrXiDBmkpf3fi0',
- 'cJvAWfk+cbfvX1engV/87nKHzNGomWcZ+uH7+cILmZscZo3ia26shGPf6gXvd9jys7UR+2',
- 'Dmkwk07QDuM6+xreAy2uZB9dApaD6Spesu9Zna8a64hb/mokES3rgEPkoU+CMnzdoDhTSn',
- 'v9FB5e6B873ZTDRpU1KGfg9Jw8k6TxA3k1pp0ZIPdytmSU3t5+qzY56O7vUAqYPGUIGJZQ',
- 'xkdUQycYu3yCCg5t9kbQ=='
- ].join(''));
+ it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
+ var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
+ expect(key.e.toString(16)).toBe('23');
+ expect(key.n.toString(16)).toBe(modulus);
});
- it('should sign an auth request', function () {
- var auth_request = octokey.authRequest({
- challenge: 'KWm5PMQC3UYSFY/xgLimHvLhLcuIdwRZoDm4UfEADO0=',
- request_url: 'https://www.example.com/login',
- username: 'foo'
- });
- auth_request.sign(private_key);
- expect(auth_request.toBase64()).toBe([
- 'AAAAIClpuTzEAt1GEhWP8YC4ph7y4S3LiHcEWaA5uFHxAAztAAAAHWh0dHBzOi8vd3d3Lm',
- 'V4YW1wbGUuY29tL2xvZ2luAAAAA2ZvbwAAAAxvY3Rva2V5LWF1dGgAAAAJcHVibGlja2V5',
- 'AAAAB3NzaC1yc2EAAAEVAAAAB3NzaC1yc2EAAAABIwAAAQEA1G2bhyMFM0iSgtkNEiKP2d',
- 'pfaOq26rV7SKFYi54FzQHNCrKfFPhxxpvcLOc8ib3ILrypWLww/qfb9e0b09xyqCD3hnV9',
- 'nWFshrNrXiDBmkpf3fi0cJvAWfk+cbfvX1engV/87nKHzNGomWcZ+uH7+cILmZscZo3ia2',
- '6shGPf6gXvd9jys7UR+2Dmkwk07QDuM6+xreAy2uZB9dApaD6Spesu9Zna8a64hb/mokES',
- '3rgEPkoU+CMnzdoDhTSnv9FB5e6B873ZTDRpU1KGfg9Jw8k6TxA3k1pp0ZIPdytmSU3t5+',
- 'qzY56O7vUAqYPGUIGJZQxkdUQycYu3yCCg5t9kbQAAAQ8AAAAHc3NoLXJzYQAAAQCcCiVU',
- 'yZj6+MpeGmTCKNhK+2tPfXw6eIBNJA4PlXiheW2c62FPEeIdyYbdlBskdflMKb41yHNee7',
- 'wcUzsjCwl2DH4rWdho80WD9yFSMygUn8j/L3p/MLZEwGwiWpyU6ZIkTvF5gciwXAkTupcK',
- '0m2Tm/obo8gR6mwkAaovtaChp2qQOPMYmwCpdW3wtZqx733KJs+Nhg85TWlRHoGCxp+L/A',
- 'x5oD3JZUTXVwy9B5Ihqe4+Lv8N19tsI+F9nuWmCCE+lsNpfAOf8WMn39DFnOJDeMRc2sXR',
- 'EW/SkckcWgzwNSwx0JniWWQfjDD1EfRZGTN8Q8J7Rg3Pr1kN666W72kz'
- ].join(''));
+ it('should return an error if no passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('input passphrase required');
+ });
+
+ it('should return an error if the wrong passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'not the password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('incorrect input passphrase');
+ });
+ });
+
+
+ describe('with an AES-encrypted PKCS#8 RSA key', function () {
+ var input_pem = [
+ '-----BEGIN ENCRYPTED PRIVATE KEY-----',
+ 'MIIFJzBRBgkqhkiG9w0BBQ0wRDAjBgkqhkiG9w0BBQwwFgQQRID1T6i8s9pU0Yws',
+ 'krvGKwICCAAwHQYJYIZIAWUDBAECBBAGjo4f9gACyIS9WtbtU8iMBIIE0PPGABaJ',
+ '/OjqZeu/EAvY1RA9APcY42FLepKsvnnT7ViyDMLLAeXWD0RIFEfQQGanmQNmE/o0',
+ 'BXoceI3D1p6kuhP9/FxsZ7QXjNwdMKugo7FI8dOBsltQZp5NBSgEWCKG8yjoFkrC',
+ '1D63Jd7Mi38Gxbfh4Q40WI+tknuSTvwmm85gOVCyw97R+xD2EGFVB7VUkdpWla/r',
+ 'sO59UTKzF4peT/NLZCG0b95BPa/TGl/Kq9TmySNRj3W9yKpQErcNryQuAOG5JzAL',
+ 'HGHFBEnDrA8rjY9W8xI/2lLdPnxQpsF94bVihFCg1GDj5q18iEKvJWAhhzyGrcCW',
+ 'HUpm9OQpGb06q+w/IODD2VRtzWSvA4nrf6bdc4FcycjqeeaNgmGky1hFHsZKjCds',
+ 'KL7Vub5IPvM+093I6JsexdzhJQTRL0mrlv0lX1aNNGebWtGZftdQUQmjXum+lnHt',
+ 'SaYHjIrupPzMgxyRlpd+YTzdcUsth+MUQwmnSdgf/3GdRqtnHsWDO5EWneH2Ua4G',
+ 'P+uc3tse8+wHCcQva83Nh7ne+wuPHwztg0Q0LAteGnfS5gdAI02MEl/9HEgsLJJH',
+ 'NU2dc+3AbYBkNR9SLuXChhrApF600x7NuO/hUlor3s9PTzyNY4mg35OMclhBaN/8',
+ 'AqXsgsOIrDQ0jsrCYPmvCgo9UeCzd3/AtUJn/OV19HDALuR0wVA7fWt1EAn4B7tx',
+ '0EhaCrki7bOxd5hj3abxD2RGrDAXeoxGbgRS4ZM9ayAPj2drtU4GV/MJBuvHhzoP',
+ 'Dquw2YCVwg0mm4bnJKDwP1u+bL7JSj7iRn77xrAYROcRlJYgfzGMsrbUTlfwk94D',
+ 'yzwqa8HdrQZ27nDxk7ztVd0QuE4hZPWoG0nq2VYGfUWS08gIY/KLQZlFfgN1McfJ',
+ '/Ff+bsxgrq0OFDgi0pIg5TAlzEEXHcA1/CBvqljtyUdeFWu+VVtO0XCk9rIokrt2',
+ 'whkTAkxJpSrCWfrrgNbMOs3exFH/Cvfn10yZQK2IvOwInIE7BR9yIhWBGCVykI2v',
+ 'bOqkJrg4FBEl1ATw1oaggtjX1YfKjccr54J1qIbfn5FynOMUKNGsd9Vjg09X1yh5',
+ 'E93ad3xHJEArqw6Oqkrq88HLYIVQBl1ejQGwOvthPcsYJFWteTm/woi4llzDwyPQ',
+ 'GIAbzMu3KWf+KLC65IGC1zqAuAunRHm2HWiFFY0L96HoO4YsTG/5U+s4qXluAKvb',
+ '1faqzhP/4PU6ma2QDCTj8pR+xfHJxjTWXUBTH+Jbbrt7TsjoyxnqiBmrUw5nzV/y',
+ 'rSTkGwXqI32hp8Vay7p16Em9WGjRGqAEyRNMgNX5+0ZwMMdM9XVfmqs+Yy5SkX7J',
+ 'kCO92HtwG+Kz83AxRIZGDG3N60ZC+BcjLe73k90/rjAZaZD4zuohGQRpdq11FMc7',
+ 'iNfvcJazJqSC+1C/Sbbt4vphocpDYnURb9cQti4Pl+fo0OLzwSGn020EbOAcn1wv',
+ 'VqAJz9NpqNpHYXH97HOLXFq+n3kb7qCGtGEwFrsYqnjV/z9hY2Uq6J63QpGDUJ+X',
+ 'r5nKnCpCxOuJ/MsKUsxCAjXNY1z5CbUj+OHTaGN4KV6tpalnlrShyzVgj5ZUlkO5',
+ 'UFvqwsIRhT9t1uk8yPnsuQMPlf5iKzMAwcl2',
+ '-----END ENCRYPTED PRIVATE KEY----- '
+ ].join('\r\n');
+
+ var modulus = [
+ 'bcc232cfe3614ae3748e692c133f0b275575fded6b67492b883ed374d2a8fada',
+ '76da3f2adf20ec225aa6f8148aec0286bdeee260628bc365c4e404a4f3a702e3',
+ 'd6b2a942f6b77a5c3115f41ca73e13de30d7f83c4e0cb177e814127f642802c3',
+ '544dd3f5dad88324e80ad90aa9ffffba899507b6033f72c65c903a4cbf4bb336',
+ '7f9da382c59bbd1d76a258b99debfe516818b01f58b18199531611a8d86563fc',
+ '09f37cffe2cbb50dc33d0f6b2b48cfc1664329af6d3936aa5abca4df78aff447',
+ '31d6bc87cb3d50d0fb20462c659da4626ebdd1298c85b4f9743c1813f76861ae',
+ '78305956e44f03a6bef8c70a87f80fead5edd525724f9d9bd4acc53879305e41'
+ ].join('');
+
+ it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password', '');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
+ var key = forge.pki.privateKeyFromPem(converted.pem);
+ expect(key.e.toString(16)).toBe('10001');
+ expect(key.n.toString(16)).toBe(modulus);
+ });
+
+ it('should convert to encrypted PKCS#8 if the same passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
+ var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
+ expect(key.e.toString(16)).toBe('10001');
+ expect(key.n.toString(16)).toBe(modulus);
+ });
+
+ it('should re-encrypt in PKCS#8 if a different passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'password', 'new better passphrase');
+ expect(converted.errors).toBeNull();
+ expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
+ var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'new better passphrase');
+ expect(key.e.toString(16)).toBe('10001');
+ expect(key.n.toString(16)).toBe(modulus);
+ });
+
+ it('should return an error if no passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('input passphrase required');
+ });
+
+ it('should return an error if the wrong passphrase is given', function () {
+ var converted = octokey.privateKey.convert(input_pem, 'not the password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('incorrect input passphrase');
+ });
+ });
+
+
+ // TODO encodings in this section might be worth supporting. They are tested for failure here so
+ // make sure our error handling is sane in case the user gives a key in one of these encodings.
+ // If you add support for it, you're welcome to test for success instead of failure :)
+ describe('with an obscure key encoding', function () {
+ it('should reject a PBE1 DES-encrypted RSA key', function () {
+ var input_pem = [
+ '-----BEGIN ENCRYPTED PRIVATE KEY-----',
+ 'MIIE6TAbBgkqhkiG9w0BBQMwDgQITQQKgSWQnWkCAggABIIEyLnYZK/8hkLpiWDo',
+ 'nFINuywF6pC6R/0ewbJ0Fq/uB7Gpa+TyxLjPZ8PIS9pnP3+HE6kKO3nVSwSlZO0L',
+ 'RV7qPkMcIL2djh0jcKFRwu8o8ikAgyt/xyJIaHk3dRmfK8apRmAH4JgyBdrStDqp',
+ 'L6ME6oXyjG3nAg9q3P5J1hkQdOyzXn9KTzTpOpxuzRAdUJiOrV63E5tVxa/0hsVy',
+ 'mXwlDUE2x6hHgKJP1uHDVrauq3MFAC7wTpyziVRY4CeSbvSpI+diA3ypX7zUhMag',
+ 'Ek8K3pvhSl5Rin6VWKTzy5HTcyz/Nn7ppsZw7ysxUKkhylZe2rI/PiKLoLH3JwNb',
+ 'LoYAAggOAQLUFUpJMrxNRPyUuIr6FzL3YiPE4ZWi+yVsHETwfUo8zAzgArTVddEE',
+ '4Oc1ngqVw78nXYZgEOTqekepTKboa5wEkBCtftUPW+aHbteGMTAPMFq4PEZyzn8d',
+ 'YC3rRUpA4renltXlZVKArPv2lvrkmQbfEYxsZypJw/a+tYUwNj4gjlm8ugWbPMci',
+ 'Eo8TPg3TDYKt1uHq81cDXkZgOxuMsP2si7KXwXJOfVlbOOlfQjEV2tv24jJ5piVN',
+ '5wPO2Gv5kDZtx4Kxoy4Toejsl0s0GQcbm1cbJqG9g3Xb6nVLZ0sagTIJldStIsEY',
+ 'vTGwSnPON24jAbUap5QDrAMEGTrBimzAhqfuoZz8qV/PbiTD5yZZhPTqsxcOD0wR',
+ 'OxUPIWy/S9KUBmawkqGg7YGre/G5J7UE3iR4+Pw9xYfemEanlQoKiBy5YIioVBjC',
+ 'btqgqHPHNxcK3LNnCBwXbhLs1O0MoG58JNnWGbqK6mm1H/kQWJD7Cl+HMZ+Ycrc+',
+ 'xhTtFSzr6AkFYKZOoAFoFohvvDA4/UbqIj+wSKRRjqGsoJ1jtoSb/jEN2bZePpgR',
+ 'ZAFl9JnOLjc0stenaPHeqLm7+5z3/hem1V3vSuRUdJDZn343uUhGBwOp6gafou7w',
+ 'rpZAYauUh7Uqxf3L5ufwawJT1L/K7hnTHytWU0q7X6rhnANscZDxyR4CrLoBFtyF',
+ 'LHocwyVmHnFSLRpI4V573OAfwB06CL/527KQnloKMkUKgwUzJ/t/cEH2OhFQqavJ',
+ 'N4gSz7KonwXIvGpGugQVfReorqarPTNUhFW2cPw2ChXjgn1f49Mbnz//AgRVt0vz',
+ 'jws5Db1m8FqcuYWyIdsvDMS5l3ddlVSW6wPoaBd52+X1Y1nuNRCuNBYHy//Hx5gY',
+ 'issopZeVY38kQ2T8vbCMuSFTXtXFHLwBp3qmWcRlYU5lJjZllT33YtvR0U2X1epK',
+ '/lGOR/1SiUKSg7oY8dl3pqhSaGZArwZC2eBNj70V58wDsYa3hU5wfu1A6FG4A9G/',
+ 'rLnS0ahR2/b7PUbSuZX1+qalFgPynb7ivpyPo4KOrn7I2i62xCEkwHjv6eJl/8Eq',
+ 'XNcmLLqxtbmtI9VimzVTHxD3pFRBP0fBEEMrAzgWxkgrKpyqAqnmT8aPnB773ROd',
+ 'LauWSG5UdSOI7SbTiNfHqqh36i5+KgzECzFCW1nWM+wb6mZZz2P/ZOcfiaG6P8ZU',
+ 'NHtmEHxeQv5e6qlrYoTBqmax70rHnMO5/uMpaWqDPhTpMpKZ99DtBoFzRyTcCvfL',
+ '0lXYU8b18LJtYQgrNg==',
+ '-----END ENCRYPTED PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('Cannot read encrypted private key. Unsupported OID.');
+ });
+
+ it('should reject a PBE2 3DES-encrypted RSA key', function () {
+ var input_pem = [
+ '-----BEGIN ENCRYPTED PRIVATE KEY-----',
+ 'MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIyvAhIqcNwHMCAggA',
+ 'MBQGCCqGSIb3DQMHBAgLQtFdlc5NZwSCBMjN5CUiiNQrCQnEdKh8A13EvyO/EmNs',
+ '4YrVRdikOzO/1JAoIfRMcX7EppYEsE96X1CXuImetWkRz8qlOw0rsh0jgi3/Jf3d',
+ 'gReNwrZYkjzAqGzmSYXOvxCkUqWKR2zXs3opHIeKrdL0RMfBxsGc5HVsD+CeF2Vu',
+ 'CMj0umqOJXrxEMO6zblRSemeYDWy0s5L2c0yReuRxoybOEGvNoPZ/LE1YF8ENt6y',
+ 'BcNyk4HlvTbq2TRuyg8eLIFs7Pd1unLvonPbM3S0G8ZH2gnrRpz3OQFHlS5F8u/A',
+ 'pxZW37NHPoUsKDRevlVLF3L+3pqx0Kgbqz1TcwwUafUb1UJf8v6q0iJqZdwxIDo6',
+ 'XdptCObojoa6jo320wFtkGB9etrWhe3M8j23B8gr0dYi8mrDPr+VbC5njhNwDjxN',
+ 'PqY3FpumZIpRDtITVE1OBh7lxZFsIA9W6f7ckSRKquWc4q+mlcYYy+4rdHVD+IfI',
+ 'X6l3xbo6A29KuVN4Dl/xQsmmAJv2DvchbE5dZ7ZO5Y73d0W09OxM3pcEfgDjJDw5',
+ 'EX4bJwGGhb3e4Cs8drcBglAIrosMO5CnuYtQVoQWd2Aa9kiyhKnrQi9qaRoPbl3H',
+ 'SGqBpitEncYXqkz3K+BStKXjb+ZxfDFBsZCDWaV2L/2BRYLd7SLSrjVczmXKUASh',
+ '+Y8iFFvQ2bfBMN3+4FRMkxQzRL7NewmoBMnKA74muZ1VOrP8370w1AiRchF91vLj',
+ 'xcYlNRwNTQcL1JMcodYZq7LE9SHejO0FzoGE42Bd/xZmJ8fTprVxrE3fSWmyenjz',
+ 'ESWwcOf1jwJUmwEyzuc5uhGsjDGOT2bqbZm2xnEGoif1zGRjNj7qf4TJtQljQ6GK',
+ '992jTEr69KvLlTJdzBZc+mC8231ni+7KEVbhVDIjtUouqFa2CtzOwJy/DlMVZItP',
+ '3B0zT+Voz675qzJFEFHFggUVaFXVsQOyJWH438o0uab5UIhL9yLpPshG/bV4wN5G',
+ 'siYxJp9aAIcf7d6YJ/emO6QUluQvq6bGOFmstlTVlizvCo57BiBbj7t4ZiKXjyxw',
+ 'g6xQkzfgWe3t+cqy444FFZoduvnBNgV3xMKjRKBWDKfaP6HZZcGrywRgc2D4pVLF',
+ 'PaC3nZBIlBW+ILiAFP/RivV+eUPaS6s28GuBfXqfuKSM9/KY4xtiU1N/QaUMpwMH',
+ '+0wdiHQQkMnwqNSoQbk4U0S70hgDdUYkkjIlpkVmIBDFpbz99bsX5YvMhMw1pdON',
+ 'BihPtaG7HYlGNU1jnw7hvivsSPLUl6ijNZ91e4h07ajP7RdbYfw3IIkf0ymWnSW1',
+ '6684o5irLUPwsb+kH+Gt3V9Mq0Ui0WIMD6wkAXF3OqKyVaPqUacwxZU3sYnYmfuV',
+ 'TMrPENKhiilgYB23Rwb5F16gmnRSpVv4x8zJRv5XpR5eO+dhllUfeCMAtEKvUiw3',
+ 'RKF/t++8c53VXOV5YyOGnYCheycE+qNwGcLHIf6GASCWs4a2A6u2HhSA70joHqB+',
+ 'xCd6oJfzK2R+jMhPbmDXlaivkutod3/WEzzB5GghaWtE5W6b5y7bAxkzXQ3EDN3h',
+ 'FEXuABK/jLtWdJhpxWEB6ifoqlai07ywrFrFe9PjJf+hpR5nFKDF5aYdKBerHVF6',
+ '/dk=',
+ '-----END ENCRYPTED PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('Cannot read encrypted private key. Unsupported encryption scheme OID.');
+ });
+ });
+
+
+ describe('with an unsupported key type', function () {
+ it('should reject a truncated PEM file', function () {
+ var input_pem = [ // last 3 lines missing, simulating a copy & paste error
+ '-----BEGIN RSA PRIVATE KEY-----',
+ 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
+ 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
+ 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
+ '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
+ 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
+ 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
+ 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
+ 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
+ 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
+ '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
+ 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
+ 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
+ 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
+ '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
+ 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
+ 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
+ '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
+ 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
+ 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
+ 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
+ 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
+ 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
+ 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('the RSA private key file has an invalid structure');
+ });
+
+ it('should reject a corrupted PEM file', function () {
+ var input_pem = [ // I changed the first character in this string
+ '-----BEGIN RSA PRIVATE KEY-----',
+ 'Proc-Type: 4,ENCRYPTED',
+ 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
+ '',
+ 'bmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
+ 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
+ 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
+ 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
+ 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
+ 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
+ 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
+ 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
+ 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
+ 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
+ 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
+ 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
+ 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
+ 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
+ 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
+ '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
+ 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
+ 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
+ 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
+ '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
+ 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
+ '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
+ 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
+ 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
+ '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
+ '-----END RSA PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('incorrect input passphrase'); // can't tell the difference :(
+ });
+
+ it('should reject an unencrypted traditional DSA key', function () {
+ var input_pem = [
+ '-----BEGIN DSA PRIVATE KEY-----',
+ 'MIIBugIBAAKBgQDVKSUqVuO/gEzML7xwjhdP6en6OiRqD329HipJs7T+QlDKpf4k',
+ '5YyRSR3F4Jv+i+mxYNdhrtTvEP+/fLr2+vuScoB4vYQAbu+ukIjvMpSHb+xU/pbu',
+ 'EdCqMGa7jq4wzHhL3uFk5nsunZ8q6NLxg4if3GoHud+9sd9fgZcQmSL1XwIVAMG9',
+ 'xF9+Lcqom6ZFuoeJv7/8HSDlAoGAVJmPgN9UiEaI8pmHIudqwufA506DNaTDwyMy',
+ 'oQ4RBjCzAh9OFQ6BnUmKGNkzXEmbWnuXgBL7wuW7E5cX3FIz80zdxfiWkeSZ1eNp',
+ 'oK2GoLQk3OyMamXah8mZWRr0xZlGdCBcE3brGiA6nmfZqe4VZVAB2lwesruXEydu',
+ 'hx7j8PYCgYAwoUeWTdT95mYUnJQ39YUZ4ZOIs4f4PKelNcZgOeO4mUO/sSi9Yp+w',
+ 'epwY+O1l7dYr2BchVy31OnXwZbTe7zsKavodOs0uP6GtkkO/00BlkG8bVjsgE2BM',
+ 'AmjhEmcUYKn0q7JBGp5vhMeAL5mnEDDho2plvqofzuwKm2o5DWXyWAIUQL78qCgx',
+ 'n8cVFqWqMgp7bxLPoec=',
+ '-----END DSA PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('DSA private keys are not supported');
+ });
+
+ it('should reject an encrypted traditional DSA key', function () {
+ var input_pem = [
+ '-----BEGIN DSA PRIVATE KEY-----',
+ 'Proc-Type: 4,ENCRYPTED',
+ 'DEK-Info: AES-128-CBC,6C3F67E991F6002818A7232048802D4A',
+ '',
+ 'l1JHgTEs+qRtwqR/FTxCeH87d/X9lCxw8i8L5pMQwnChr2zSux/1UP+y1VGuWRPr',
+ 'Dou2GxnElla81FC93xUupFn8gqp1hMXzhnOEHBP6+wUUswFW1GncuXIpdww5R+o7',
+ 'SiWfWjHRw6pHuJ9eBK/DssljVN8IzWpMk5yktRymC4UAflZ/nBYc99qI8mBe8ZV0',
+ 'hxZ89urMH309TDV7fhBJGKmITbrJrZo6hRmysEClXzrJWJI2BJ8lAZ4WezLZNu82',
+ 'irYmeDIxKpiGuPOjaW7ecAP00Ms89kei7Tk4L22xiji48c085Xh4yr/lMaIfFIXE',
+ 'bIPGy3LpowYtX/czxu8YYpmeGt6mLdH4VOC3SHIphybWliN6PgT19l2DPMV1+Kro',
+ 'rdkWaMa8GXAvFnu2hCnLRALeDlGxz8pFGPzusZ6oUUYMKJDpNNbZtd6ADJ8Nkp1N',
+ 'ExvPIuDbRrRK5+ZzZ7fWMUxmbpIXZPmeAQxf91wg2emUZKfLNL2wpGGqcPMAZUev',
+ 'iszHQf7YnQktXqKIV77zpiCfgiDjL3hvvHeBc6RJ1OmSM6uUxKlChUN4F5Z6wSA5',
+ '35176vkLKwSlD0I0ceTX/Q==',
+ '-----END DSA PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem, 'password');
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('DSA private keys are not supported');
+ });
+
+ it('should reject an unencrypted PKCS#8-encoded DSA key', function () {
+ var input_pem = [
+ '-----BEGIN PRIVATE KEY-----',
+ 'MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBANwjgJrL28LohOt/SPrmDB+lYtt0',
+ 'FjqMbrFppvA1xAsrNRIA78fE8pMYdKpr/+f6CLpELC8fqJfH5mLcPr6occAnbnXd',
+ 'hvqzYW3eUWJb1yDrP9D/rAG0r3KQxjXYrVTVUrGcvLWgtnXlGp66c3ikVlfTRByH',
+ 'wpSYWoJXNN+4Ah2HAhUA/6XELRMItFHgGRaedtcJDwnSrfUCgYEArj9TvhMmOCvr',
+ 'HupJ4N5qQbpdTZRJ0NxgXXWNxzfnK/QjcRJGarQ3Mdb4DFcHexdihFP1zUWIEdPx',
+ '4sU3X8ofpCpyMM6dQWD0QtDU5Zk7ZcJzu086bQ57vdkIRxDY+YdIAZCla5w8r1uC',
+ 'sfcdIKHDB2JZoQgH70rcSYvdstjk+Q0EFgIUDhbS9IhVyGZ7u4grOp4kec435ew=',
+ '-----END PRIVATE KEY-----'
+ ].join('\n');
+ var converted = octokey.privateKey.convert(input_pem);
+ expect(converted.pem).toBeNull();
+ expect(converted.errors).toContain('Cannot read private key. ASN.1 object is not an RSAPrivateKey.');
});
});
});
2  test/test_runner.html
View
@@ -17,6 +17,8 @@
<script src="../js/forge/hmac.js" type="text/javascript"></script>
<script src="../js/forge/pbkdf2.js" type="text/javascript"></script>
<script src="../js/forge/pki.js" type="text/javascript"></script>
+ <script src="../js/forge/prng.js" type="text/javascript"></script>
+ <script src="../js/forge/random.js" type="text/javascript"></script>
<script src="../js/lib/jquery-1.7.2.js" type="text/javascript"></script>
<script src="../js/buffer.js" type="text/javascript"></script>
<script src="../js/public_key.js" type="text/javascript"></script>

No commit comments for this range

Something went wrong with that request. Please try again.