Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Separate private key importing from runtime & allow passphrase to be …

…changed
  • Loading branch information...
commit 8cb214dbd451eb2ece3481bfafdabb1a42495622 1 parent 5b8f8bf
Martin Kleppmann ept authored
230 js/private_key.js
... ... @@ -1,80 +1,19 @@
1 1 var octokey = octokey || {};
2 2
3   -octokey.privateKey = function (raw_private_key) {
  3 +octokey.privateKey = function (private_key_pem) {
4 4
5 5 var _public = {},
6 6 private_key = null,
7   - decrypt_key = null, // function: passphrase -> private key
8 7 passphrase_timeout = 5 * 60 * 1000, // 5 minutes in milliseconds
9 8 passphrase_timer = null;
10 9
11   - // This matches the logic in OpenSSL's crypto/pem/pem_lib.c
12   - if (raw_private_key.match(/-----BEGIN RSA PRIVATE KEY-----/)) {
13   - // Unencrypted PEM, or encrypted key in OpenSSL 'traditional' format.
14   - var key_parts = raw_private_key.match(/-----BEGIN [^\n]+-----((?:(?:\n[^\n]+:[^\n]*)*\n\s*\n)?)([A-Za-z0-9+\/=\s]+)-----END [^\n]+-----/);
15   - if (!key_parts) {
16   - throw 'the RSA private key file has an invalid structure';
17   - }
18   - var headers = key_parts[1].trim(), data = forge.util.createBuffer(forge.util.decode64(key_parts[2]));
19   -
20   - // The header format parsing is very strict in OpenSSL's PEM_get_EVP_CIPHER_INFO
21   - // (e.g. reordering of headers is not allowed, case sensitive).
22   - var cipher_info = headers.match(/Proc-Type: 4,ENCRYPTED\s*DEK-Info: ([A-Z0-9\-]*),([0-9A-Fa-f]+)/);
23   - if (cipher_info) {
24   - var init_vector = forge.util.hexToBytes(cipher_info[2]);
25   - var cipher = {
26   - 'AES-128-CBC': {algorithm: forge.aes, key_length: 16},
27   - 'DES-EDE3-CBC': {algorithm: forge.des, key_length: 24}
28   - }[cipher_info[1]];
29   -
30   - if (!cipher) {
31   - throw 'unsupported private key encryption cipher: ' + cipher_info[1];
32   - }
33   -
34   - decrypt_key = function (passphrase) {
35   - // The following algorithm for deriving the key is hard-coded in OpenSSL
36   - // in EVP_BytesToKey (crypto/evp/evp_key.c) and PEM_do_header (crypto/pem/pem_lib.c)
37   - var key = new forge.util.ByteBuffer(), digest = '';
38   - while (key.length() < cipher.key_length) {
39   - var md = forge.md.md5.create();
40   - md.update(digest);
41   - md.update(passphrase, 'utf-8');
42   - md.update(init_vector.substr(0, 8)); // the first PKCS5_SALT_LEN (8) bytes of IV are used as salt
43   - digest = md.digest().getBytes();
44   - key.putBytes(digest.substr(0, cipher.key_length - key.length()));
45   - }
46   -
47   - var decrypt = cipher.algorithm.startDecrypting(key, forge.util.createBuffer(init_vector));
48   - decrypt.update(data);
49   - decrypt.finish();
50   - try {
51   - return forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(decrypt.output));
52   - } catch(error) {
53   - return false;
54   - }
55   - };
56   -
57   - } else {
58   - // No headers indicating that the key is encrypted
59   - private_key = forge.pki.privateKeyFromPem(raw_private_key);
60   - }
61   -
62   - } else if (raw_private_key.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
63   - // Encrypted PKCS8, supported directly by Forge
64   - decrypt_key = function (passphrase) {
65   - return forge.pki.decryptRsaPrivateKey(raw_private_key, passphrase);
66   - };
67   -
68   - } else if (raw_private_key.match(/-----BEGIN PRIVATE KEY-----/)) {
69   - // TODO unencrypted PKCS8
70   -
71   - } else {
72   - var keytype = raw_private_key.match(/-----BEGIN ([^\n]+) PRIVATE KEY-----/);
73   - if (keytype && keytype[1]) {
74   - throw keytype[1] + ' private keys are not supported';
75   - } else {
76   - throw 'unrecognised private key format';
77   - }
  10 + // Only support PKCS8-encoded keys here, to discourage people from using the less secure
  11 + // 'traditional' SSH key format. octokey.privateKey.convert can perform the conversion.
  12 + if (private_key_pem.match(/-----BEGIN PRIVATE KEY-----/)) {
  13 + // Unencrypted PKCS8
  14 + private_key = forge.pki.privateKeyFromPem(private_key_pem);
  15 + } else if (!private_key_pem.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
  16 + throw 'Unsupported private key type. Please use octoKey.privateKey.convert to convert it';
78 17 }
79 18
80 19 _public.passphrase_required = !private_key;
@@ -83,11 +22,17 @@ octokey.privateKey = function (raw_private_key) {
83 22 // false if the passphrase is incorrect. The decrypted key is held in this
84 23 // object for 5 minutes, and then discarded.
85 24 _public.setPassphrase = function (passphrase) {
86   - if (!decrypt_key) {
  25 + // Ignore passphrase if the key wasn't encrypted in the first place
  26 + if (private_key && !passphrase_timer) {
87 27 return true;
88 28 }
89 29
90   - private_key = decrypt_key(passphrase);
  30 + try {
  31 + private_key = forge.pki.decryptRsaPrivateKey(private_key_pem, passphrase);
  32 + } catch (error) {
  33 + console.log(error); // FIXME
  34 + return false;
  35 + }
91 36 _public.passphrase_required = !private_key;
92 37
93 38 if (passphrase_timer) {
@@ -132,3 +77,146 @@ octokey.privateKey = function (raw_private_key) {
132 77
133 78 return _public;
134 79 };
  80 +
  81 +
  82 +// Takes a private key in any supported format (currently either PKCS#8 PEM or the SSH 'traditional'
  83 +// private key format) and converts it into a consistent PKCS#8 PEM format. If one passphrase is
  84 +// given, it is used both for decrypting the input and for encrypting the output. If two passphrases
  85 +// are given, the first is used for decrypting and the second is used for encrypting. (This allows
  86 +// you to to change the passphrase for a key.) If the output passphase is empty, the output is
  87 +// unencrypted.
  88 +//
  89 +// Returns an object with properties:
  90 +// * pem: The converted private key PEM string, or null if conversion failed
  91 +// * errors: Array of errors that occurred during conversion (null if successful)
  92 +octokey.privateKey.convert = function (input_pem, input_passphrase, output_passphrase) {
  93 +
  94 + // Encryption options for the output key
  95 + var output_options = {
  96 + encAlg: 'aes128',
  97 +
  98 + // Iteration count -- the higher, the harder it is to brute-force the passphrase. OpenSSL
  99 + // uses 2048. This takes about 700ms in Chrome 21's JS engine on an Intel Core 2 Duo laptop.
  100 + // TODO benchmark this on more browsers & CPUs.
  101 + count: 2048,
  102 +
  103 + // 128 bits of salt, twice the minimum recommended by PBKDF2 (RFC 2898) -- superstitiously
  104 + // making it bigger because I'm unsure of the quality of entropy we can get from the
  105 + // browser. Though hash crackers are so fast these days that size of salt doesn't make too
  106 + // much of a difference anyway.
  107 + saltSize: 16
  108 + };
  109 +
  110 + var private_key = null;
  111 + if (typeof output_passphrase === 'undefined') {
  112 + output_passphrase = input_passphrase;
  113 + }
  114 +
  115 + function error(message) {
  116 + return {pem: null, errors: [message]};
  117 + }
  118 +
  119 + // This matches the logic in OpenSSL's crypto/pem/pem_lib.c
  120 + if (input_pem.match(/-----BEGIN RSA PRIVATE KEY-----/)) {
  121 +
  122 + // Unencrypted PEM, or encrypted key in OpenSSL 'traditional' format.
  123 + var key_parts = input_pem.match(/-----BEGIN [^\n]+-----((?:(?:\s*\n[^\n]+:[^\n]*)*\n\s*\n)?)([A-Za-z0-9+\/=\s]+)-----END [^\n]+-----/);
  124 + if (!key_parts) {
  125 + return error('the RSA private key file has an invalid structure');
  126 + }
  127 + var headers = key_parts[1].trim(), data = forge.util.createBuffer(forge.util.decode64(key_parts[2]));
  128 +
  129 + // The header format parsing is very strict in OpenSSL's PEM_get_EVP_CIPHER_INFO
  130 + // (headers cannot be reordered, added or removed; and the parsing is case sensitive).
  131 + var cipher_info = headers.match(/Proc-Type: 4,ENCRYPTED\s*DEK-Info: ([A-Z0-9\-]*),([0-9A-Fa-f]+)/);
  132 + if (cipher_info) {
  133 + var init_vector = forge.util.hexToBytes(cipher_info[2]);
  134 + var cipher = {
  135 + 'AES-128-CBC': {algorithm: forge.aes, key_length: 16},
  136 + 'DES-EDE3-CBC': {algorithm: forge.des, key_length: 24}
  137 + }[cipher_info[1]];
  138 +
  139 + if (!cipher) {
  140 + return error('unsupported private key encryption cipher: ' + cipher_info[1]);
  141 + }
  142 + if (!input_passphrase) {
  143 + return error('input passphrase required');
  144 + }
  145 +
  146 + // The following algorithm for deriving the key is hard-coded in OpenSSL
  147 + // in EVP_BytesToKey (crypto/evp/evp_key.c) and PEM_do_header (crypto/pem/pem_lib.c)
  148 + var key = new forge.util.ByteBuffer(), digest = '';
  149 + while (key.length() < cipher.key_length) {
  150 + var md = forge.md.md5.create();
  151 + md.update(digest);
  152 + md.update(input_passphrase, 'utf-8');
  153 + md.update(init_vector.substr(0, 8)); // the first PKCS5_SALT_LEN (8) bytes of IV are used as salt
  154 + digest = md.digest().getBytes();
  155 + key.putBytes(digest.substr(0, cipher.key_length - key.length()));
  156 + }
  157 +
  158 + var decrypt = cipher.algorithm.startDecrypting(key, forge.util.createBuffer(init_vector));
  159 + decrypt.update(data);
  160 + decrypt.finish();
  161 +
  162 + // The most likely effect of an incorrect passphrase is that the decrypted string is not
  163 + // valid DER; but it could also be valid DER but not the right kind of ASN.1 structure.
  164 + var asn1;
  165 + try {
  166 + private_key = forge.pki.privateKeyFromAsn1(forge.asn1.fromDer(decrypt.output));
  167 + } catch (e) {
  168 + var message = (typeof(e) === 'object' ? e.message : e);
  169 + return {pem: null, errors: ['incorrect input passphrase', message]};
  170 + }
  171 +
  172 + } else {
  173 + // No headers indicating that the key is encrypted
  174 + private_key = forge.pki.privateKeyFromPem(input_pem);
  175 + }
  176 +
  177 + } else if (input_pem.match(/-----BEGIN ENCRYPTED PRIVATE KEY-----/)) {
  178 + // Encrypted PKCS8, supported directly by Forge
  179 + if (!input_passphrase) {
  180 + return error('input passphrase required');
  181 + }
  182 + try {
  183 + private_key = forge.pki.decryptRsaPrivateKey(input_pem, input_passphrase);
  184 + } catch (e) {
  185 + return error(typeof(e) === 'object' ? e.message : e);
  186 + }
  187 + if (!private_key) {
  188 + return error('incorrect input passphrase');
  189 + }
  190 +
  191 + } else if (input_pem.match(/-----BEGIN PRIVATE KEY-----/)) {
  192 + // Unencrypted PKCS8
  193 + try {
  194 + private_key = forge.pki.privateKeyFromPem(input_pem);
  195 + } catch (e) {
  196 + return error(typeof(e) === 'object' ? e.message : e);
  197 + }
  198 +
  199 + } else {
  200 + var keytype = input_pem.match(/-----BEGIN ([^\n]+) PRIVATE KEY-----/);
  201 + if (keytype && keytype[1]) {
  202 + return error(keytype[1] + ' private keys are not supported');
  203 + } else {
  204 + return error('unrecognised private key format');
  205 + }
  206 + }
  207 +
  208 + var output_pem;
  209 + if (output_passphrase) {
  210 + // Generate PKCS#8 output
  211 + output_pem = forge.pki.encryptRsaPrivateKey(private_key, output_passphrase, output_options);
  212 + } else {
  213 + // Unencrypted key :( not sure if we should even allow this!
  214 + var key_info = forge.pki.wrapRsaPrivateKey(forge.pki.privateKeyToAsn1(private_key));
  215 + output_pem = [
  216 + '-----BEGIN PRIVATE KEY-----',
  217 + forge.util.encode64(forge.asn1.toDer(key_info).getBytes()),
  218 + '-----END PRIVATE KEY-----'
  219 + ].join('\r\n');
  220 + }
  221 + return {pem: output_pem, errors: null};
  222 +};
58 js/test.html
@@ -63,36 +63,34 @@
63 63
64 64 <p>
65 65 <label for="privatekey">Private key (PEM):</label><br>
66   - <textarea id="privatekey" name="privatekey">-----BEGIN RSA PRIVATE KEY-----
67   -Proc-Type: 4,ENCRYPTED
68   -DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995
69   -
70   -BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM
71   -NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm
72   -idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h
73   -Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm
74   -BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK
75   -Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX
76   -isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J
77   -pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf
78   -IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W
79   -NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI
80   -XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1
81   -bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN
82   -XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS
83   -WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi
84   -ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF
85   -405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz
86   -DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j
87   -CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt
88   -bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO
89   -+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP
90   -A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU
91   -1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti
92   -RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4
93   -VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM
94   -5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv
95   ------END RSA PRIVATE KEY-----
  66 + <textarea id="privatekey" name="privatekey">-----BEGIN PRIVATE KEY-----
  67 +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8wjLP42FK43SO
  68 +aSwTPwsnVXX97WtnSSuIPtN00qj62nbaPyrfIOwiWqb4FIrsAoa97uJgYovDZcTk
  69 +BKTzpwLj1rKpQva3elwxFfQcpz4T3jDX+DxODLF36BQSf2QoAsNUTdP12tiDJOgK
  70 +2Qqp//+6iZUHtgM/csZckDpMv0uzNn+do4LFm70ddqJYuZ3r/lFoGLAfWLGBmVMW
  71 +EajYZWP8CfN8/+LLtQ3DPQ9rK0jPwWZDKa9tOTaqWryk33iv9Ecx1ryHyz1Q0Psg
  72 +RixlnaRibr3RKYyFtPl0PBgT92hhrngwWVbkTwOmvvjHCof4D+rV7dUlck+dm9Ss
  73 +xTh5MF5BAgMBAAECggEAHyKBMx00XR/rh9n9NSd+9Xv5PGs+/ghpr819H5Xn3YjP
  74 +dexZa/iIOpptVBo/V/KKuyV+HZvjpdVRhrLlanMv3Nj7G0Q6YcVDE62lWFyVtr09
  75 +nUIK4GzjkP2s4eg1Ywwhn/Q+dB0m/WrHA5MNWUEqs2AiPuVc38hUw8vece5T1Dlu
  76 +il7myYgSccIQ5DTbdg/5kMZGcimlOHe4IkaP+RifdhbaWn5c3eOBrKG+uZnauv0B
  77 +n6Tb6/DUrKtLKCwWTYwuc9PC1Zgd0/aSyia4KnXrWQuboAhuuOTa4l2E7GaqEe2O
  78 +cZiEGTNQpNEEDfT02LIPdTOPCeHUd/AY31fgr2gr4QKBgQDc6EQPDSVCXVzExB6E
  79 +9xUhqo+gN3L7N49NZ1qwH4y2i9ttlUSyGbvuc6XL+lWpbIpUu2Rt0rT5ZdjFYJLs
  80 +KyepQOO+7Q/YbJ2R+3jLFGO3iFE6R9N8E48NqVjeohzHDpoMb/glN7wPcmT/iBUo
  81 +dKpkKnNkSU/f1Bnr4Hi46A0btQKBgQDavob5IihsyDxIkXxdzS+sGWyplxfKOgIN
  82 +BiGg3ume1VfEKAixL8MLIXj3S7AOMsS35zJYRZNtr1xQ5Cs9tuYA+mq1xOkQ9hhA
  83 +6jmlTawHTi5DvwuLIik95FwQzwckCK4zmS/xdLLtkgtsmbsAjr4LfqX2V/g6OXYk
  84 +3kd6IxeH3QKBgAJgWUMUMB9ro7DWL0Hc6pRHIm5lyk3bhiYeA4K7hCb+kCi4n1mP
  85 +H09sXQ85rSw6Z66LqYPo7Vt1dgSBinMR78ZJVWnbYP1CBdvpKZ59pKj3xW/sD+FY
  86 +0IQkGzmh4s9dX9jcZ333AIXmBCIfk6Kwxph3QHCGvV46COFZs9LBZEq1AoGBAJHH
  87 +P/RwizhUCbjjHFr3D7pGB2DOTRB8sDk1yNuJM3CjBaa4d3J+PiIA4LkOO/p6Yxec
  88 +gLTLSYFjyMYwCZFLfuVP/iW9YQXovCkm79v2c5s6wyJrA6ppzcptkd2x2zRshIvm
  89 +n0jvWP9ywSJTIYkxl/3ZHYma/tbuzImtiT0gfeIRAoGAYktMdBJRRVLzjZvc3XAu
  90 +tH8syHcUwlx0C4uQX6kTPmvjI2KupaTVA9UPeRaiq75Gr+bbeND341+cz/lJhlOA
  91 +DpqkJEcTmB0nq4Q8/gXdsgqcVG8Is/8ibCyKt3P3rQALgKUlbKG0tZZ/8cb9Axwl
  92 +1wLGJfQftS76LNdTph8SVWM=
  93 +-----END PRIVATE KEY-----
96 94 </textarea>
97 95 </p>
98 96
55 test/auth_request_spec.js
... ... @@ -1,33 +1,34 @@
1 1 describe('octokey.authRequest', function () {
2 2
3 3 var private_key, private_key_pem = [
4   - '-----BEGIN RSA PRIVATE KEY-----',
5   - 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
6   - 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
7   - 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
8   - '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
9   - 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
10   - 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
11   - 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
12   - 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
13   - 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
14   - '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
15   - 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
16   - 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
17   - 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
18   - '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
19   - 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
20   - 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
21   - '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
22   - 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
23   - 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
24   - 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
25   - 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
26   - 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
27   - 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
28   - 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
29   - '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
30   - '-----END RSA PRIVATE KEY-----'
  4 + '-----BEGIN PRIVATE KEY-----',
  5 + 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsJAbbXe63OoTl',
  6 + 'hWNNUXl7EZEKQT064zssKn8U6492gesVReTLYxBzB/b0ncDiLzPGkRL1F1IRTqIg',
  7 + 'SyjD3zAiLTHX8ud/HqMbljT+JCwbU1GU0IJrJ0a8ZoL4/DBaLBhFzs+0GBXgo4BV',
  8 + 'cxl0wHmcVFp6RDWSx0f/frQoIaCWM+3NM2XT173tAHAcUhnd5rkSKH8eKt5OtFKe',
  9 + '8X7LCbC7Ffi5sOvRl5Jx64XZLlYtcfb5NXQtRvZjEXLfAcMzhl/AEHlT3FmGQhvN',
  10 + 'IvX0zvHwt4Qbce4MOfAR9T3fI5ir2XfO6cJoQbg22Inakdic3pMohCiebrSlWb1+',
  11 + 'dG+xus4nAgMBAAECggEAUmZQbfNx0H2PnfqSxTEJ8JJBFmKSN70L1yFkgZQcsUKR',
  12 + 'UDaLxZvfBOE8Im3dJagopZVOOMn3+9bjHog7tD8C4Gk34cIhdCUrOIpXRYv1HQNu',
  13 + 'GskQlNBRON/tM9gsIQ3YdJoBrJiW6Ff7l2uoNM3pqJ5sTPiXmHISEcgJ3ufx4dOi',
  14 + 'rRZZcuA4RI5H4xE19rBK+eqDpYdBNGK+/FDwRdhGrUQcwXg0sp16qwZP0Vmzg+4D',
  15 + 'kauO/BfC1w7fedpQlHUfjoZ49+PPDk2p17YJXnSM8W5JizrxNMDhiD6w8Ntdxs7k',
  16 + 'rMbNhrXseNlKYYwd47l2D53CnC0txshuAoGy5/GDYQKBgQDWd++5mXRNVwLUbn37',
  17 + 'HTef2aN1bNwSMUeC8hOt99uhwvUig0BAPlepINSEh++a4KcmQNV45upytoqQxxIw',
  18 + 'OjhTMmgy8H1ll8ayR4zMvLtCKLSMfKXJVzLxHweW4DvEdFo2RtHZZi84OS5CUNvN',
  19 + '57wuR8d7bFuNhwrahUhXH+K1MQKBgQDNebukg8DPWKknCh84Vhtl4L4MzSbjEJ8s',
  20 + 'mOYJXsA/ch6up4kTnRbzt5fE+VQrjKBzWfluvT6EawJlCZ20lqG3sCKBbxMijyeQ',
  21 + 'F84/Ls6t1MOxE9q9IS9Byi5+TbEXi/4bAlDmZH/W92AbkBamNqdFipKh3pZjh7j5',
  22 + 'Oi4ugf5C1wKBgEtTcKU1Wn3Xc6z86c6K4oCIRjr18w2ItV8ueu79QOy9tq9affSS',
  23 + 'cON8Hmg1/lfdU5fB6bg/ElUb++sBoEfECwhdie9qPAC0oBr5umAmYXbJKTC2jtv6',
  24 + 'fm/lwBqjwxoq64xor0e19hu/KZotICZfn955Y3zcAAPjTFKXwWKoVk3hAoGAVepf',
  25 + 'vnNkzI4q9Gr0EO9YN2oYrWuepfUOSWyJS0O2KRFDqQ3ecDgQH8oViMQkIK2FbZYi',
  26 + 'iN5SZLYF4095BMizSAY691oFaz7mcQazM5IT03WSedlubgSRKPYsKJ29vbXIg2yd',
  27 + 'fShql+0J35yJC+jwWlCN7WcQBP/8JSHhq4qpUTMCgYAN8yM5Dk6Nt+VUB+voued/',
  28 + '86g7lvH0vj/fC2MJuUjNJMdg/TYLIRm23Pk0e9nFuqbnwxWqvxJqpQd3/6Rhd0xW',
  29 + 'QySQZqr+cbkeszgaW6sohx+FMDc2MHxnyGvcpom4YXkwTTk9thWJ1Cu/VVenQ0kF',
  30 + 'ncK1GzU47lgOUzeEtLqGbg==',
  31 + '-----END PRIVATE KEY-----'
31 32 ].join('\n');
32 33
33 34 beforeEach(function () {
632 test/private_key_spec.js
... ... @@ -1,35 +1,36 @@
1 1 describe('octokey.privateKey', function () {
2 2
3   - describe('with an unencrypted SSH private key', function () {
  3 + describe('with an unencrypted private key', function () {
4 4 var private_key, private_key_pem = [
5   - '-----BEGIN RSA PRIVATE KEY-----',
6   - 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
7   - 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
8   - 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
9   - '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
10   - 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
11   - 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
12   - 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
13   - 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
14   - 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
15   - '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
16   - 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
17   - 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
18   - 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
19   - '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
20   - 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
21   - 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
22   - '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
23   - 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
24   - 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
25   - 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
26   - 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
27   - 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
28   - 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
29   - 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
30   - '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
31   - '-----END RSA PRIVATE KEY-----'
32   - ].join('\n');
  5 + '-----BEGIN PRIVATE KEY-----',
  6 + 'MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCsJAbbXe63OoTl',
  7 + 'hWNNUXl7EZEKQT064zssKn8U6492gesVReTLYxBzB/b0ncDiLzPGkRL1F1IRTqIg',
  8 + 'SyjD3zAiLTHX8ud/HqMbljT+JCwbU1GU0IJrJ0a8ZoL4/DBaLBhFzs+0GBXgo4BV',
  9 + 'cxl0wHmcVFp6RDWSx0f/frQoIaCWM+3NM2XT173tAHAcUhnd5rkSKH8eKt5OtFKe',
  10 + '8X7LCbC7Ffi5sOvRl5Jx64XZLlYtcfb5NXQtRvZjEXLfAcMzhl/AEHlT3FmGQhvN',
  11 + 'IvX0zvHwt4Qbce4MOfAR9T3fI5ir2XfO6cJoQbg22Inakdic3pMohCiebrSlWb1+',
  12 + 'dG+xus4nAgMBAAECggEAUmZQbfNx0H2PnfqSxTEJ8JJBFmKSN70L1yFkgZQcsUKR',
  13 + 'UDaLxZvfBOE8Im3dJagopZVOOMn3+9bjHog7tD8C4Gk34cIhdCUrOIpXRYv1HQNu',
  14 + 'GskQlNBRON/tM9gsIQ3YdJoBrJiW6Ff7l2uoNM3pqJ5sTPiXmHISEcgJ3ufx4dOi',
  15 + 'rRZZcuA4RI5H4xE19rBK+eqDpYdBNGK+/FDwRdhGrUQcwXg0sp16qwZP0Vmzg+4D',
  16 + 'kauO/BfC1w7fedpQlHUfjoZ49+PPDk2p17YJXnSM8W5JizrxNMDhiD6w8Ntdxs7k',
  17 + 'rMbNhrXseNlKYYwd47l2D53CnC0txshuAoGy5/GDYQKBgQDWd++5mXRNVwLUbn37',
  18 + 'HTef2aN1bNwSMUeC8hOt99uhwvUig0BAPlepINSEh++a4KcmQNV45upytoqQxxIw',
  19 + 'OjhTMmgy8H1ll8ayR4zMvLtCKLSMfKXJVzLxHweW4DvEdFo2RtHZZi84OS5CUNvN',
  20 + '57wuR8d7bFuNhwrahUhXH+K1MQKBgQDNebukg8DPWKknCh84Vhtl4L4MzSbjEJ8s',
  21 + 'mOYJXsA/ch6up4kTnRbzt5fE+VQrjKBzWfluvT6EawJlCZ20lqG3sCKBbxMijyeQ',
  22 + 'F84/Ls6t1MOxE9q9IS9Byi5+TbEXi/4bAlDmZH/W92AbkBamNqdFipKh3pZjh7j5',
  23 + 'Oi4ugf5C1wKBgEtTcKU1Wn3Xc6z86c6K4oCIRjr18w2ItV8ueu79QOy9tq9affSS',
  24 + 'cON8Hmg1/lfdU5fB6bg/ElUb++sBoEfECwhdie9qPAC0oBr5umAmYXbJKTC2jtv6',
  25 + 'fm/lwBqjwxoq64xor0e19hu/KZotICZfn955Y3zcAAPjTFKXwWKoVk3hAoGAVepf',
  26 + 'vnNkzI4q9Gr0EO9YN2oYrWuepfUOSWyJS0O2KRFDqQ3ecDgQH8oViMQkIK2FbZYi',
  27 + 'iN5SZLYF4095BMizSAY691oFaz7mcQazM5IT03WSedlubgSRKPYsKJ29vbXIg2yd',
  28 + 'fShql+0J35yJC+jwWlCN7WcQBP/8JSHhq4qpUTMCgYAN8yM5Dk6Nt+VUB+voued/',
  29 + '86g7lvH0vj/fC2MJuUjNJMdg/TYLIRm23Pk0e9nFuqbnwxWqvxJqpQd3/6Rhd0xW',
  30 + 'QySQZqr+cbkeszgaW6sohx+FMDc2MHxnyGvcpom4YXkwTTk9thWJ1Cu/VVenQ0kF',
  31 + 'ncK1GzU47lgOUzeEtLqGbg==',
  32 + '-----END PRIVATE KEY-----'
  33 + ].join('\r\n');
33 34
34 35 beforeEach(function () {
35 36 private_key = octokey.privateKey(private_key_pem);
@@ -55,7 +56,7 @@ describe('octokey.privateKey', function () {
55 56 ].join(''));
56 57 });
57 58
58   - // TODO more granular tests
  59 + // More detailed tests in auth_request_spec.js
59 60 it('should sign an auth request', function () {
60 61 var auth_request = octokey.authRequest({
61 62 challenge: 'KWm5PMQC3UYSFY/xgLimHvLhLcuIdwRZoDm4UfEADO0=',
@@ -82,39 +83,39 @@ describe('octokey.privateKey', function () {
82 83 });
83 84
84 85
85   - describe('with an AES-encrypted SSH private key', function () {
  86 + describe('with an encrypted private key', function () {
86 87 var private_key, private_key_pem = [
87   - '-----BEGIN RSA PRIVATE KEY-----',
88   - 'Proc-Type: 4,ENCRYPTED',
89   - 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
90   - '',
91   - 'BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
92   - 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
93   - 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
94   - 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
95   - 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
96   - 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
97   - 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
98   - 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
99   - 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
100   - 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
101   - 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
102   - 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
103   - 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
104   - 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
105   - 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
106   - '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
107   - 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
108   - 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
109   - 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
110   - '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
111   - 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
112   - '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
113   - 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
114   - 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
115   - '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
116   - '-----END RSA PRIVATE KEY-----'
117   - ].join('\n');
  88 + '-----BEGIN ENCRYPTED PRIVATE KEY-----',
  89 + 'MIIFJzBRBgkqhkiG9w0BBQ0wRDAjBgkqhkiG9w0BBQwwFgQQOfOEW0od494MHaIE',
  90 + 'eyaTVAICCAAwHQYJYIZIAWUDBAECBBAm7F6fzZkGXTdRgzYJids5BIIE0KZb+elW',
  91 + 'MwO8qFnC15gI52ek8OLfxFAY8K3NYeAKQeAJuEoVpysLnogABFZn/LirrIaXYyIg',
  92 + 'CtE/IJPR/xttNAActYJ2SL5lHsHAE8nLwZX+L3XbGoaHEKKa0QzetHlPZJX7Xp7O',
  93 + 's+1SGJaI6MCDlv54DvKr8J7UbIfHEU/1JpXHEqh1twZQPsBEhrwWUjwhRuxIqArP',
  94 + 'H/3cNkDYxqOHkPKYzv8D0DzUrKwbd0ZQbfdtXInE6IL2pglPutjsvqWDXyU86+Vz',
  95 + 'WETDOrozPn0r20LoGhLGflxSD2jqH4YUoHrcK+1202TYI6VFZ+AdjKeX3NL1ZhDn',
  96 + 'Zu8mjAToVBe5jIKBwwi+nHB6s1GxynYtVh+jxJ7LO5s15v80+1LtSuWi5hhGgRq4',
  97 + '5AoGJI0OjhiGlxxBmDIWp36Y+vRJx3SblEu/wLRBVKl2W7oj/6mrqjdNCtN5XAwN',
  98 + '1xY2nruZ6pirE4BJittzmkcXSosw65D8rw8TfLERBJ4IZtXf+JeFNoutPP0z2DO2',
  99 + 'dcX/5aa07XZWF63CgbSm7sitnbEGVJarUuNuD9QhO4CprnX0IYJtmPkf4JgfMYSB',
  100 + 'Etc/btiyj4N6vT7NMdZ9+67/DkxsgblxeUTG9wfUd3Zvo14FOkm7F5DT69z6LZhE',
  101 + 'mQi8317FomyXBM67fGvysNr8dkqAbg023X/ZCMB3B+C4ZkBoSRWIfdK6GG/v9I0a',
  102 + 'r/KES8Jc5OgWtY1Qq4sG3FFlhlQt7V52oiCsGshN69u5vymRWK/MjU8h01x2RXEi',
  103 + '+ARrHx4QGrzzdEGMn7dFrxfJ7ltIyeco9VedT2xiBaS+i0yPDsvtxPd9UXhIIG4A',
  104 + 'Ikd4v41534juDjuKIWbmRBSC/8YzHhmsUN/Wx4aQHpvHoJEwoJ2vi8umwc1kRi2r',
  105 + '2KmK1DksecFCVX00xYpNBb1+IeuPcPaboTtITs7SxWgiBHqS0cj85yntNuHGICLr',
  106 + '2fcfBE5F1A/KjLINqjbRM6XLM6iTCwRdmPaCN3C4hsessM8lK3k5vNsMHrbc0p5t',
  107 + 'I5f/4pkiJk/l6AT5HW/syt/z8PKEZkQ/tSYEqYDJ4mA/urcc+ViUnDQGp6fjQ0oD',
  108 + '0pvKnAywOBwXEQYibCGINQcjudHBdCxONYK5PcvRN2a90Z7lPzyPKQRkgNb9KlD0',
  109 + 'iuaCsOGWrU+QTaEP0B0M6i+K7NV2S4zOKD13xdGqHudwmVhVG6451uV9m4AtUYOV',
  110 + '3H7lBmzN8zq7tK0UvkI9Hxr8qvOx3PePPSuAH34c/m1QKsmiGM9biy+yF9CCnm4m',
  111 + 'MRwhnqrQMS3iVBibpxLc781d4p+xwpYsqAXSjIGHJed7xZjcHeznl8FPuKsjYd04',
  112 + 'sDl38bkIsMigCBiKZT+gZJnbZRglJHmmgJ/SKSidUVHk2cjEJ4BCSgTxzFECfO42',
  113 + '8HMeF55L3Y8G1oo8A4u84Z9z9pnnnd7J+LabOvTtTxuwlyhDwsR8TQ6WfJ8oQT5l',
  114 + '9UW7eaj7FghajPheGrqxldgefikk3a8ug+CoDUwYATvq2lEv4erHsSFCbnIOIlch',
  115 + '324GfodpLtPRyXrc1f5F6H7ZdhhMp8zKADEKGyRER8XF7GUIN0T+9h/UwU0P2Eez',
  116 + 'OKiDhXJniRaM7Z81WuAWeNeg5QaitrEnlxBY',
  117 + '-----END ENCRYPTED PRIVATE KEY-----'
  118 + ].join('\r\n');
118 119
119 120 beforeEach(function () {
120 121 private_key = octokey.privateKey(private_key_pem);
@@ -206,10 +207,143 @@ describe('octokey.privateKey', function () {
206 207 });
207 208 });
208 209 });
  210 +});
209 211
210 212
211   - describe('with a 3DES-encrypted SSH private key', function () {
212   - var private_key, private_key_pem = [
  213 +describe('octokey.privateKey.convert', function () {
  214 +
  215 + describe('with an unencrypted traditional RSA key', function () {
  216 + var input_pem = [
  217 + '-----BEGIN RSA PRIVATE KEY-----',
  218 + 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
  219 + 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
  220 + 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
  221 + '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
  222 + 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
  223 + 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
  224 + 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
  225 + 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
  226 + 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
  227 + '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
  228 + 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
  229 + 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
  230 + 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
  231 + '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
  232 + 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
  233 + 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
  234 + '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
  235 + 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
  236 + 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
  237 + 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
  238 + 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
  239 + 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
  240 + 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5',
  241 + 'NHvZxbqm58MVqr8SaqUHd/+kYXdMVkMkkGaq/nG5HrM4GlurKIcfhTA3NjB8Z8hr',
  242 + '3KaJuGF5ME05PbYVidQrv1VXp0NJBZ3CtRs1OO5YDlM3hLS6hm4=',
  243 + '-----END RSA PRIVATE KEY-----'
  244 + ].join('\n');
  245 +
  246 + it('should convert to unencrypted PKCS#8 if no passphrase is given', function () {
  247 + var converted = octokey.privateKey.convert(input_pem);
  248 + expect(converted.errors).toBeNull();
  249 + expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
  250 + expect(forge.pki.pemToDer(converted.pem).toHex()).toMatch(new RegExp(['^',
  251 + '308204bd', // top-level sequence node
  252 + '020100', // PrivateKeyInfo structure version 0
  253 + '300d06092a864886f70d0101010500', // AlgorithmIdentifier with rsaEncryption OID
  254 + '048204a7', // octet string
  255 + '308204a3', // 9-element sequence containing the RSA parameters
  256 + '020100', // RSA private key structure version 0
  257 + '0282010100ac2406db5deeb73a84e585634d51797b11910a413d3ae33b2c2a7f' // start of the RSA modulus
  258 + ].join('')));
  259 + });
  260 +
  261 + it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
  262 + var converted = octokey.privateKey.convert(input_pem, 'my new passphrase');
  263 + expect(converted.errors).toBeNull();
  264 + expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
  265 + expect(forge.pki.pemToDer(converted.pem).toHex()).toMatch(new RegExp(['^',
  266 + '308205273051', // top-level sequence nodes
  267 + '06092a864886f70d01050d', // OID for PBES2 in PKCS#5 version 2.0 (1.2.840.113549.1.5.13)
  268 + '30443023', // sequence nodes
  269 + '06092a864886f70d01050c', // OID for PBKDF2 in PKCS#5 version 2.0 (1.2.840.113549.1.5.12)
  270 + '3016', // sequence of salt and iteration count
  271 + '0410[0-9a-f]{32}', // 16 bytes of salt
  272 + '02020800', // iteration count = 2048
  273 + '301d', // sequence of cipher identifier and IV
  274 + '0609608648016503040102', // OID for AES-128-CBC (2.16.840.1.101.3.4.1.2)
  275 + '0410[0-9a-f]{32}' // 16 bytes of IV
  276 + ].join('')));
  277 + });
  278 + });
  279 +
  280 +
  281 + describe('with an AES-encrypted traditional RSA key', function () {
  282 + var input_pem = [
  283 + '-----BEGIN RSA PRIVATE KEY-----',
  284 + 'Proc-Type: 4,ENCRYPTED',
  285 + 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
  286 + '',
  287 + 'BmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
  288 + 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
  289 + 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
  290 + 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
  291 + 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
  292 + 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
  293 + 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
  294 + 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
  295 + 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
  296 + 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
  297 + 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
  298 + 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
  299 + 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
  300 + 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
  301 + 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
  302 + '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
  303 + 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
  304 + 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
  305 + 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
  306 + '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
  307 + 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
  308 + '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
  309 + 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
  310 + 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
  311 + '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
  312 + '-----END RSA PRIVATE KEY-----'
  313 + ].join('\r\n');
  314 +
  315 + it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
  316 + var converted = octokey.privateKey.convert(input_pem, 'password', '');
  317 + expect(converted.errors).toBeNull();
  318 + expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
  319 + var key = forge.pki.privateKeyFromPem(converted.pem);
  320 + expect(key.e.toString(10)).toBe('65537');
  321 + });
  322 +
  323 + it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
  324 + var converted = octokey.privateKey.convert(input_pem, 'password');
  325 + expect(converted.errors).toBeNull();
  326 + expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
  327 + var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
  328 + expect(key.e.toString(10)).toBe('65537');
  329 + });
  330 +
  331 + it('should return an error if no passphrase is given', function () {
  332 + var converted = octokey.privateKey.convert(input_pem);
  333 + expect(converted.pem).toBeNull();
  334 + expect(converted.errors).toContain('input passphrase required');
  335 + });
  336 +
  337 + it('should return an error if the wrong passphrase is given', function () {
  338 + var converted = octokey.privateKey.convert(input_pem, 'not the password');
  339 + expect(converted.pem).toBeNull();
  340 + expect(converted.errors).toContain('incorrect input passphrase');
  341 + });
  342 + });
  343 +
  344 +
  345 + describe('with a 3DES-encrypted traditional RSA key', function () {
  346 + var input_pem = [
213 347 '-----BEGIN RSA PRIVATE KEY-----',
214 348 'Proc-Type: 4,ENCRYPTED',
215 349 'DEK-Info: DES-EDE3-CBC,6A9DD947BF7E78D3',
@@ -242,45 +376,345 @@ describe('octokey.privateKey', function () {
242 376 '-----END RSA PRIVATE KEY-----'
243 377 ].join('\n');
244 378
245   - beforeEach(function () {
246   - private_key = octokey.privateKey(private_key_pem);
247   - private_key.setPassphrase('password');
  379 + var modulus = [
  380 + 'd46d9b87230533489282d90d12228fd9da5f68eab6eab57b48a1588b9e05cd01',
  381 + 'cd0ab29f14f871c69bdc2ce73c89bdc82ebca958bc30fea7dbf5ed1bd3dc72a8',
  382 + '20f786757d9d616c86b36b5e20c19a4a5fddf8b4709bc059f93e71b7ef5f57a7',
  383 + '815ffcee7287ccd1a8996719fae1fbf9c20b999b1c668de26b6eac8463dfea05',
  384 + 'ef77d8f2b3b511fb60e6930934ed00ee33afb1ade032dae641f5d029683e92a5',
  385 + 'eb2ef599daf1aeb885bfe6a24112deb8043e4a14f82327cdda038534a7bfd141',
  386 + 'e5ee81f3bdd94c34695352867e0f49c3c93a4f1037935a69d1920f772b66494d',
  387 + 'ede7eab3639e8eeef500a983c6508189650c64754432718bb7c820a0e6df646d'
  388 + ].join('');
  389 +
  390 + it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
  391 + var converted = octokey.privateKey.convert(input_pem, 'password', '');
  392 + expect(converted.errors).toBeNull();
  393 + expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
  394 + var key = forge.pki.privateKeyFromPem(converted.pem);
  395 + expect(key.e.toString(16)).toBe('23');
  396 + expect(key.n.toString(16)).toBe(modulus);
248 397 });
249 398
250   - it('should extract the public key from the private key', function () {
251   - expect(private_key.publicKey().toBase64()).toBe([
252   - 'ssh-rsa ',
253   - 'AAAAB3NzaC1yc2EAAAABIwAAAQEA1G2bhyMFM0iSgtkNEiKP2dpfaOq26rV7SKFYi54FzQ',
254   - 'HNCrKfFPhxxpvcLOc8ib3ILrypWLww/qfb9e0b09xyqCD3hnV9nWFshrNrXiDBmkpf3fi0',
255   - 'cJvAWfk+cbfvX1engV/87nKHzNGomWcZ+uH7+cILmZscZo3ia26shGPf6gXvd9jys7UR+2',
256   - 'Dmkwk07QDuM6+xreAy2uZB9dApaD6Spesu9Zna8a64hb/mokES3rgEPkoU+CMnzdoDhTSn',
257   - 'v9FB5e6B873ZTDRpU1KGfg9Jw8k6TxA3k1pp0ZIPdytmSU3t5+qzY56O7vUAqYPGUIGJZQ',
258   - 'xkdUQycYu3yCCg5t9kbQ=='
259   - ].join(''));
  399 + it('should convert to encrypted PKCS#8 if a passphrase is given', function () {
  400 + var converted = octokey.privateKey.convert(input_pem, 'password');
  401 + expect(converted.errors).toBeNull();
  402 + expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
  403 + var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
  404 + expect(key.e.toString(16)).toBe('23');
  405 + expect(key.n.toString(16)).toBe(modulus);
260 406 });
261 407
262   - it('should sign an auth request', function () {
263   - var auth_request = octokey.authRequest({
264   - challenge: 'KWm5PMQC3UYSFY/xgLimHvLhLcuIdwRZoDm4UfEADO0=',
265   - request_url: 'https://www.example.com/login',
266   - username: 'foo'
267   - });
268   - auth_request.sign(private_key);
269   - expect(auth_request.toBase64()).toBe([
270   - 'AAAAIClpuTzEAt1GEhWP8YC4ph7y4S3LiHcEWaA5uFHxAAztAAAAHWh0dHBzOi8vd3d3Lm',
271   - 'V4YW1wbGUuY29tL2xvZ2luAAAAA2ZvbwAAAAxvY3Rva2V5LWF1dGgAAAAJcHVibGlja2V5',
272   - 'AAAAB3NzaC1yc2EAAAEVAAAAB3NzaC1yc2EAAAABIwAAAQEA1G2bhyMFM0iSgtkNEiKP2d',
273   - 'pfaOq26rV7SKFYi54FzQHNCrKfFPhxxpvcLOc8ib3ILrypWLww/qfb9e0b09xyqCD3hnV9',
274   - 'nWFshrNrXiDBmkpf3fi0cJvAWfk+cbfvX1engV/87nKHzNGomWcZ+uH7+cILmZscZo3ia2',
275   - '6shGPf6gXvd9jys7UR+2Dmkwk07QDuM6+xreAy2uZB9dApaD6Spesu9Zna8a64hb/mokES',
276   - '3rgEPkoU+CMnzdoDhTSnv9FB5e6B873ZTDRpU1KGfg9Jw8k6TxA3k1pp0ZIPdytmSU3t5+',
277   - 'qzY56O7vUAqYPGUIGJZQxkdUQycYu3yCCg5t9kbQAAAQ8AAAAHc3NoLXJzYQAAAQCcCiVU',
278   - 'yZj6+MpeGmTCKNhK+2tPfXw6eIBNJA4PlXiheW2c62FPEeIdyYbdlBskdflMKb41yHNee7',
279   - 'wcUzsjCwl2DH4rWdho80WD9yFSMygUn8j/L3p/MLZEwGwiWpyU6ZIkTvF5gciwXAkTupcK',
280   - '0m2Tm/obo8gR6mwkAaovtaChp2qQOPMYmwCpdW3wtZqx733KJs+Nhg85TWlRHoGCxp+L/A',
281   - 'x5oD3JZUTXVwy9B5Ihqe4+Lv8N19tsI+F9nuWmCCE+lsNpfAOf8WMn39DFnOJDeMRc2sXR',
282   - 'EW/SkckcWgzwNSwx0JniWWQfjDD1EfRZGTN8Q8J7Rg3Pr1kN666W72kz'
283   - ].join(''));
  408 + it('should return an error if no passphrase is given', function () {
  409 + var converted = octokey.privateKey.convert(input_pem);
  410 + expect(converted.pem).toBeNull();
  411 + expect(converted.errors).toContain('input passphrase required');
  412 + });
  413 +
  414 + it('should return an error if the wrong passphrase is given', function () {
  415 + var converted = octokey.privateKey.convert(input_pem, 'not the password');
  416 + expect(converted.pem).toBeNull();
  417 + expect(converted.errors).toContain('incorrect input passphrase');
  418 + });
  419 + });
  420 +
  421 +
  422 + describe('with an AES-encrypted PKCS#8 RSA key', function () {
  423 + var input_pem = [
  424 + '-----BEGIN ENCRYPTED PRIVATE KEY-----',
  425 + 'MIIFJzBRBgkqhkiG9w0BBQ0wRDAjBgkqhkiG9w0BBQwwFgQQRID1T6i8s9pU0Yws',
  426 + 'krvGKwICCAAwHQYJYIZIAWUDBAECBBAGjo4f9gACyIS9WtbtU8iMBIIE0PPGABaJ',
  427 + '/OjqZeu/EAvY1RA9APcY42FLepKsvnnT7ViyDMLLAeXWD0RIFEfQQGanmQNmE/o0',
  428 + 'BXoceI3D1p6kuhP9/FxsZ7QXjNwdMKugo7FI8dOBsltQZp5NBSgEWCKG8yjoFkrC',
  429 + '1D63Jd7Mi38Gxbfh4Q40WI+tknuSTvwmm85gOVCyw97R+xD2EGFVB7VUkdpWla/r',
  430 + 'sO59UTKzF4peT/NLZCG0b95BPa/TGl/Kq9TmySNRj3W9yKpQErcNryQuAOG5JzAL',
  431 + 'HGHFBEnDrA8rjY9W8xI/2lLdPnxQpsF94bVihFCg1GDj5q18iEKvJWAhhzyGrcCW',
  432 + 'HUpm9OQpGb06q+w/IODD2VRtzWSvA4nrf6bdc4FcycjqeeaNgmGky1hFHsZKjCds',
  433 + 'KL7Vub5IPvM+093I6JsexdzhJQTRL0mrlv0lX1aNNGebWtGZftdQUQmjXum+lnHt',
  434 + 'SaYHjIrupPzMgxyRlpd+YTzdcUsth+MUQwmnSdgf/3GdRqtnHsWDO5EWneH2Ua4G',
  435 + 'P+uc3tse8+wHCcQva83Nh7ne+wuPHwztg0Q0LAteGnfS5gdAI02MEl/9HEgsLJJH',
  436 + 'NU2dc+3AbYBkNR9SLuXChhrApF600x7NuO/hUlor3s9PTzyNY4mg35OMclhBaN/8',
  437 + 'AqXsgsOIrDQ0jsrCYPmvCgo9UeCzd3/AtUJn/OV19HDALuR0wVA7fWt1EAn4B7tx',
  438 + '0EhaCrki7bOxd5hj3abxD2RGrDAXeoxGbgRS4ZM9ayAPj2drtU4GV/MJBuvHhzoP',
  439 + 'Dquw2YCVwg0mm4bnJKDwP1u+bL7JSj7iRn77xrAYROcRlJYgfzGMsrbUTlfwk94D',
  440 + 'yzwqa8HdrQZ27nDxk7ztVd0QuE4hZPWoG0nq2VYGfUWS08gIY/KLQZlFfgN1McfJ',
  441 + '/Ff+bsxgrq0OFDgi0pIg5TAlzEEXHcA1/CBvqljtyUdeFWu+VVtO0XCk9rIokrt2',
  442 + 'whkTAkxJpSrCWfrrgNbMOs3exFH/Cvfn10yZQK2IvOwInIE7BR9yIhWBGCVykI2v',
  443 + 'bOqkJrg4FBEl1ATw1oaggtjX1YfKjccr54J1qIbfn5FynOMUKNGsd9Vjg09X1yh5',
  444 + 'E93ad3xHJEArqw6Oqkrq88HLYIVQBl1ejQGwOvthPcsYJFWteTm/woi4llzDwyPQ',
  445 + 'GIAbzMu3KWf+KLC65IGC1zqAuAunRHm2HWiFFY0L96HoO4YsTG/5U+s4qXluAKvb',
  446 + '1faqzhP/4PU6ma2QDCTj8pR+xfHJxjTWXUBTH+Jbbrt7TsjoyxnqiBmrUw5nzV/y',
  447 + 'rSTkGwXqI32hp8Vay7p16Em9WGjRGqAEyRNMgNX5+0ZwMMdM9XVfmqs+Yy5SkX7J',
  448 + 'kCO92HtwG+Kz83AxRIZGDG3N60ZC+BcjLe73k90/rjAZaZD4zuohGQRpdq11FMc7',
  449 + 'iNfvcJazJqSC+1C/Sbbt4vphocpDYnURb9cQti4Pl+fo0OLzwSGn020EbOAcn1wv',
  450 + 'VqAJz9NpqNpHYXH97HOLXFq+n3kb7qCGtGEwFrsYqnjV/z9hY2Uq6J63QpGDUJ+X',
  451 + 'r5nKnCpCxOuJ/MsKUsxCAjXNY1z5CbUj+OHTaGN4KV6tpalnlrShyzVgj5ZUlkO5',
  452 + 'UFvqwsIRhT9t1uk8yPnsuQMPlf5iKzMAwcl2',
  453 + '-----END ENCRYPTED PRIVATE KEY----- '
  454 + ].join('\r\n');
  455 +
  456 + var modulus = [
  457 + 'bcc232cfe3614ae3748e692c133f0b275575fded6b67492b883ed374d2a8fada',
  458 + '76da3f2adf20ec225aa6f8148aec0286bdeee260628bc365c4e404a4f3a702e3',
  459 + 'd6b2a942f6b77a5c3115f41ca73e13de30d7f83c4e0cb177e814127f642802c3',
  460 + '544dd3f5dad88324e80ad90aa9ffffba899507b6033f72c65c903a4cbf4bb336',
  461 + '7f9da382c59bbd1d76a258b99debfe516818b01f58b18199531611a8d86563fc',
  462 + '09f37cffe2cbb50dc33d0f6b2b48cfc1664329af6d3936aa5abca4df78aff447',
  463 + '31d6bc87cb3d50d0fb20462c659da4626ebdd1298c85b4f9743c1813f76861ae',
  464 + '78305956e44f03a6bef8c70a87f80fead5edd525724f9d9bd4acc53879305e41'
  465 + ].join('');
  466 +
  467 + it('should convert to unencrypted PKCS#8 if no output passphrase is given', function () {
  468 + var converted = octokey.privateKey.convert(input_pem, 'password', '');
  469 + expect(converted.errors).toBeNull();
  470 + expect(converted.pem).toMatch(/^-----BEGIN PRIVATE KEY-----/);
  471 + var key = forge.pki.privateKeyFromPem(converted.pem);
  472 + expect(key.e.toString(16)).toBe('10001');
  473 + expect(key.n.toString(16)).toBe(modulus);
  474 + });
  475 +
  476 + it('should convert to encrypted PKCS#8 if the same passphrase is given', function () {
  477 + var converted = octokey.privateKey.convert(input_pem, 'password');
  478 + expect(converted.errors).toBeNull();
  479 + expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
  480 + var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'password');
  481 + expect(key.e.toString(16)).toBe('10001');
  482 + expect(key.n.toString(16)).toBe(modulus);
  483 + });
  484 +
  485 + it('should re-encrypt in PKCS#8 if a different passphrase is given', function () {
  486 + var converted = octokey.privateKey.convert(input_pem, 'password', 'new better passphrase');
  487 + expect(converted.errors).toBeNull();
  488 + expect(converted.pem).toMatch(/^-----BEGIN ENCRYPTED PRIVATE KEY-----/);
  489 + var key = forge.pki.decryptRsaPrivateKey(converted.pem, 'new better passphrase');
  490 + expect(key.e.toString(16)).toBe('10001');
  491 + expect(key.n.toString(16)).toBe(modulus);
  492 + });
  493 +
  494 + it('should return an error if no passphrase is given', function () {
  495 + var converted = octokey.privateKey.convert(input_pem);
  496 + expect(converted.pem).toBeNull();
  497 + expect(converted.errors).toContain('input passphrase required');
  498 + });
  499 +
  500 + it('should return an error if the wrong passphrase is given', function () {
  501 + var converted = octokey.privateKey.convert(input_pem, 'not the password');
  502 + expect(converted.pem).toBeNull();
  503 + expect(converted.errors).toContain('incorrect input passphrase');
  504 + });
  505 + });
  506 +
  507 +
  508 + // TODO encodings in this section might be worth supporting. They are tested for failure here so
  509 + // make sure our error handling is sane in case the user gives a key in one of these encodings.
  510 + // If you add support for it, you're welcome to test for success instead of failure :)
  511 + describe('with an obscure key encoding', function () {
  512 + it('should reject a PBE1 DES-encrypted RSA key', function () {
  513 + var input_pem = [
  514 + '-----BEGIN ENCRYPTED PRIVATE KEY-----',
  515 + 'MIIE6TAbBgkqhkiG9w0BBQMwDgQITQQKgSWQnWkCAggABIIEyLnYZK/8hkLpiWDo',
  516 + 'nFINuywF6pC6R/0ewbJ0Fq/uB7Gpa+TyxLjPZ8PIS9pnP3+HE6kKO3nVSwSlZO0L',
  517 + 'RV7qPkMcIL2djh0jcKFRwu8o8ikAgyt/xyJIaHk3dRmfK8apRmAH4JgyBdrStDqp',
  518 + 'L6ME6oXyjG3nAg9q3P5J1hkQdOyzXn9KTzTpOpxuzRAdUJiOrV63E5tVxa/0hsVy',
  519 + 'mXwlDUE2x6hHgKJP1uHDVrauq3MFAC7wTpyziVRY4CeSbvSpI+diA3ypX7zUhMag',
  520 + 'Ek8K3pvhSl5Rin6VWKTzy5HTcyz/Nn7ppsZw7ysxUKkhylZe2rI/PiKLoLH3JwNb',
  521 + 'LoYAAggOAQLUFUpJMrxNRPyUuIr6FzL3YiPE4ZWi+yVsHETwfUo8zAzgArTVddEE',
  522 + '4Oc1ngqVw78nXYZgEOTqekepTKboa5wEkBCtftUPW+aHbteGMTAPMFq4PEZyzn8d',
  523 + 'YC3rRUpA4renltXlZVKArPv2lvrkmQbfEYxsZypJw/a+tYUwNj4gjlm8ugWbPMci',
  524 + 'Eo8TPg3TDYKt1uHq81cDXkZgOxuMsP2si7KXwXJOfVlbOOlfQjEV2tv24jJ5piVN',
  525 + '5wPO2Gv5kDZtx4Kxoy4Toejsl0s0GQcbm1cbJqG9g3Xb6nVLZ0sagTIJldStIsEY',
  526 + 'vTGwSnPON24jAbUap5QDrAMEGTrBimzAhqfuoZz8qV/PbiTD5yZZhPTqsxcOD0wR',
  527 + 'OxUPIWy/S9KUBmawkqGg7YGre/G5J7UE3iR4+Pw9xYfemEanlQoKiBy5YIioVBjC',
  528 + 'btqgqHPHNxcK3LNnCBwXbhLs1O0MoG58JNnWGbqK6mm1H/kQWJD7Cl+HMZ+Ycrc+',
  529 + 'xhTtFSzr6AkFYKZOoAFoFohvvDA4/UbqIj+wSKRRjqGsoJ1jtoSb/jEN2bZePpgR',
  530 + 'ZAFl9JnOLjc0stenaPHeqLm7+5z3/hem1V3vSuRUdJDZn343uUhGBwOp6gafou7w',
  531 + 'rpZAYauUh7Uqxf3L5ufwawJT1L/K7hnTHytWU0q7X6rhnANscZDxyR4CrLoBFtyF',
  532 + 'LHocwyVmHnFSLRpI4V573OAfwB06CL/527KQnloKMkUKgwUzJ/t/cEH2OhFQqavJ',
  533 + 'N4gSz7KonwXIvGpGugQVfReorqarPTNUhFW2cPw2ChXjgn1f49Mbnz//AgRVt0vz',
  534 + 'jws5Db1m8FqcuYWyIdsvDMS5l3ddlVSW6wPoaBd52+X1Y1nuNRCuNBYHy//Hx5gY',
  535 + 'issopZeVY38kQ2T8vbCMuSFTXtXFHLwBp3qmWcRlYU5lJjZllT33YtvR0U2X1epK',
  536 + '/lGOR/1SiUKSg7oY8dl3pqhSaGZArwZC2eBNj70V58wDsYa3hU5wfu1A6FG4A9G/',
  537 + 'rLnS0ahR2/b7PUbSuZX1+qalFgPynb7ivpyPo4KOrn7I2i62xCEkwHjv6eJl/8Eq',
  538 + 'XNcmLLqxtbmtI9VimzVTHxD3pFRBP0fBEEMrAzgWxkgrKpyqAqnmT8aPnB773ROd',
  539 + 'LauWSG5UdSOI7SbTiNfHqqh36i5+KgzECzFCW1nWM+wb6mZZz2P/ZOcfiaG6P8ZU',
  540 + 'NHtmEHxeQv5e6qlrYoTBqmax70rHnMO5/uMpaWqDPhTpMpKZ99DtBoFzRyTcCvfL',
  541 + '0lXYU8b18LJtYQgrNg==',
  542 + '-----END ENCRYPTED PRIVATE KEY-----'
  543 + ].join('\n');
  544 + var converted = octokey.privateKey.convert(input_pem, 'password');
  545 + expect(converted.pem).toBeNull();
  546 + expect(converted.errors).toContain('Cannot read encrypted private key. Unsupported OID.');
  547 + });
  548 +
  549 + it('should reject a PBE2 3DES-encrypted RSA key', function () {
  550 + var input_pem = [
  551 + '-----BEGIN ENCRYPTED PRIVATE KEY-----',
  552 + 'MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIyvAhIqcNwHMCAggA',
  553 + 'MBQGCCqGSIb3DQMHBAgLQtFdlc5NZwSCBMjN5CUiiNQrCQnEdKh8A13EvyO/EmNs',
  554 + '4YrVRdikOzO/1JAoIfRMcX7EppYEsE96X1CXuImetWkRz8qlOw0rsh0jgi3/Jf3d',
  555 + 'gReNwrZYkjzAqGzmSYXOvxCkUqWKR2zXs3opHIeKrdL0RMfBxsGc5HVsD+CeF2Vu',
  556 + 'CMj0umqOJXrxEMO6zblRSemeYDWy0s5L2c0yReuRxoybOEGvNoPZ/LE1YF8ENt6y',
  557 + 'BcNyk4HlvTbq2TRuyg8eLIFs7Pd1unLvonPbM3S0G8ZH2gnrRpz3OQFHlS5F8u/A',
  558 + 'pxZW37NHPoUsKDRevlVLF3L+3pqx0Kgbqz1TcwwUafUb1UJf8v6q0iJqZdwxIDo6',
  559 + 'XdptCObojoa6jo320wFtkGB9etrWhe3M8j23B8gr0dYi8mrDPr+VbC5njhNwDjxN',
  560 + 'PqY3FpumZIpRDtITVE1OBh7lxZFsIA9W6f7ckSRKquWc4q+mlcYYy+4rdHVD+IfI',
  561 + 'X6l3xbo6A29KuVN4Dl/xQsmmAJv2DvchbE5dZ7ZO5Y73d0W09OxM3pcEfgDjJDw5',
  562 + 'EX4bJwGGhb3e4Cs8drcBglAIrosMO5CnuYtQVoQWd2Aa9kiyhKnrQi9qaRoPbl3H',
  563 + 'SGqBpitEncYXqkz3K+BStKXjb+ZxfDFBsZCDWaV2L/2BRYLd7SLSrjVczmXKUASh',
  564 + '+Y8iFFvQ2bfBMN3+4FRMkxQzRL7NewmoBMnKA74muZ1VOrP8370w1AiRchF91vLj',
  565 + 'xcYlNRwNTQcL1JMcodYZq7LE9SHejO0FzoGE42Bd/xZmJ8fTprVxrE3fSWmyenjz',
  566 + 'ESWwcOf1jwJUmwEyzuc5uhGsjDGOT2bqbZm2xnEGoif1zGRjNj7qf4TJtQljQ6GK',
  567 + '992jTEr69KvLlTJdzBZc+mC8231ni+7KEVbhVDIjtUouqFa2CtzOwJy/DlMVZItP',
  568 + '3B0zT+Voz675qzJFEFHFggUVaFXVsQOyJWH438o0uab5UIhL9yLpPshG/bV4wN5G',
  569 + 'siYxJp9aAIcf7d6YJ/emO6QUluQvq6bGOFmstlTVlizvCo57BiBbj7t4ZiKXjyxw',
  570 + 'g6xQkzfgWe3t+cqy444FFZoduvnBNgV3xMKjRKBWDKfaP6HZZcGrywRgc2D4pVLF',
  571 + 'PaC3nZBIlBW+ILiAFP/RivV+eUPaS6s28GuBfXqfuKSM9/KY4xtiU1N/QaUMpwMH',
  572 + '+0wdiHQQkMnwqNSoQbk4U0S70hgDdUYkkjIlpkVmIBDFpbz99bsX5YvMhMw1pdON',
  573 + 'BihPtaG7HYlGNU1jnw7hvivsSPLUl6ijNZ91e4h07ajP7RdbYfw3IIkf0ymWnSW1',
  574 + '6684o5irLUPwsb+kH+Gt3V9Mq0Ui0WIMD6wkAXF3OqKyVaPqUacwxZU3sYnYmfuV',
  575 + 'TMrPENKhiilgYB23Rwb5F16gmnRSpVv4x8zJRv5XpR5eO+dhllUfeCMAtEKvUiw3',
  576 + 'RKF/t++8c53VXOV5YyOGnYCheycE+qNwGcLHIf6GASCWs4a2A6u2HhSA70joHqB+',
  577 + 'xCd6oJfzK2R+jMhPbmDXlaivkutod3/WEzzB5GghaWtE5W6b5y7bAxkzXQ3EDN3h',
  578 + 'FEXuABK/jLtWdJhpxWEB6ifoqlai07ywrFrFe9PjJf+hpR5nFKDF5aYdKBerHVF6',
  579 + '/dk=',
  580 + '-----END ENCRYPTED PRIVATE KEY-----'
  581 + ].join('\n');
  582 + var converted = octokey.privateKey.convert(input_pem, 'password');
  583 + expect(converted.pem).toBeNull();
  584 + expect(converted.errors).toContain('Cannot read encrypted private key. Unsupported encryption scheme OID.');
  585 + });
  586 + });
  587 +
  588 +
  589 + describe('with an unsupported key type', function () {
  590 + it('should reject a truncated PEM file', function () {
  591 + var input_pem = [ // last 3 lines missing, simulating a copy & paste error
  592 + '-----BEGIN RSA PRIVATE KEY-----',
  593 + 'MIIEogIBAAKCAQEArCQG213utzqE5YVjTVF5exGRCkE9OuM7LCp/FOuPdoHrFUXk',
  594 + 'y2MQcwf29J3A4i8zxpES9RdSEU6iIEsow98wIi0x1/Lnfx6jG5Y0/iQsG1NRlNCC',
  595 + 'aydGvGaC+PwwWiwYRc7PtBgV4KOAVXMZdMB5nFRaekQ1ksdH/360KCGgljPtzTNl',
  596 + '09e97QBwHFIZ3ea5Eih/HireTrRSnvF+ywmwuxX4ubDr0ZeSceuF2S5WLXH2+TV0',
  597 + 'LUb2YxFy3wHDM4ZfwBB5U9xZhkIbzSL19M7x8LeEG3HuDDnwEfU93yOYq9l3zunC',
  598 + 'aEG4NtiJ2pHYnN6TKIQonm60pVm9fnRvsbrOJwIDAQABAoIBAFJmUG3zcdB9j536',
  599 + 'ksUxCfCSQRZikje9C9chZIGUHLFCkVA2i8Wb3wThPCJt3SWoKKWVTjjJ9/vW4x6I',
  600 + 'O7Q/AuBpN+HCIXQlKziKV0WL9R0DbhrJEJTQUTjf7TPYLCEN2HSaAayYluhX+5dr',
  601 + 'qDTN6aiebEz4l5hyEhHICd7n8eHToq0WWXLgOESOR+MRNfawSvnqg6WHQTRivvxQ',
  602 + '8EXYRq1EHMF4NLKdeqsGT9FZs4PuA5GrjvwXwtcO33naUJR1H46GePfjzw5Nqde2',
  603 + 'CV50jPFuSYs68TTA4Yg+sPDbXcbO5KzGzYa17HjZSmGMHeO5dg+dwpwtLcbIbgKB',
  604 + 'sufxg2ECgYEA1nfvuZl0TVcC1G59+x03n9mjdWzcEjFHgvITrffbocL1IoNAQD5X',
  605 + 'qSDUhIfvmuCnJkDVeObqcraKkMcSMDo4UzJoMvB9ZZfGskeMzLy7Qii0jHylyVcy',
  606 + '8R8HluA7xHRaNkbR2WYvODkuQlDbzee8LkfHe2xbjYcK2oVIVx/itTECgYEAzXm7',
  607 + 'pIPAz1ipJwofOFYbZeC+DM0m4xCfLJjmCV7AP3IerqeJE50W87eXxPlUK4ygc1n5',
  608 + 'br0+hGsCZQmdtJaht7AigW8TIo8nkBfOPy7OrdTDsRPavSEvQcoufk2xF4v+GwJQ',
  609 + '5mR/1vdgG5AWpjanRYqSod6WY4e4+TouLoH+QtcCgYBLU3ClNVp913Os/OnOiuKA',
  610 + 'iEY69fMNiLVfLnru/UDsvbavWn30knDjfB5oNf5X3VOXwem4PxJVG/vrAaBHxAsI',
  611 + 'XYnvajwAtKAa+bpgJmF2ySkwto7b+n5v5cAao8MaKuuMaK9HtfYbvymaLSAmX5/e',
  612 + 'eWN83AAD40xSl8FiqFZN4QKBgFXqX75zZMyOKvRq9BDvWDdqGK1rnqX1DklsiUtD',
  613 + 'tikRQ6kN3nA4EB/KFYjEJCCthW2WIojeUmS2BeNPeQTIs0gGOvdaBWs+5nEGszOS',
  614 + 'E9N1knnZbm4EkSj2LCidvb21yINsnX0oapftCd+ciQvo8FpQje1nEAT//CUh4auK',
  615 + 'qVEzAoGADfMjOQ5OjbflVAfr6Lnnf/OoO5bx9L4/3wtjCblIzSTHYP02CyEZttz5'
  616 + ].join('\n');
  617 + var converted = octokey.privateKey.convert(input_pem);
  618 + expect(converted.pem).toBeNull();
  619 + expect(converted.errors).toContain('the RSA private key file has an invalid structure');
  620 + });
  621 +
  622 + it('should reject a corrupted PEM file', function () {
  623 + var input_pem = [ // I changed the first character in this string
  624 + '-----BEGIN RSA PRIVATE KEY-----',
  625 + 'Proc-Type: 4,ENCRYPTED',
  626 + 'DEK-Info: AES-128-CBC,C20284DFE2574C53BAF2C73072F99995',
  627 + '',
  628 + 'bmocszCeDrK1GWwqGEr5jU+VnpdG1mRkdYXQ1EKTWfW35fCLLOl/KZyFe7TF6fuM',
  629 + 'NDK8vaCVCFBKKyfcftPga9jwCuZHvENjYPjj7Pds/iAwjYsxx77Jk4Xd1ulaSYlm',
  630 + 'idOssZs5DllpG2P+UfVS8XH+Te+4Xw7+1Mx7m9OJA9PKM6H/5jIC+XJuRNMTEt8h',
  631 + 'Rie1+8io+BFsk0wnURDcVwRmvBr0VaZuRoOCZB9UmCHS0bXUVDivkFRUr7YZ0vJm',
  632 + 'BS8KFzHV8/H8IFvXQvsOczm4iGp7RvYO7mJ27H/ffQFukE44Dh1K5SbBdF4xSadK',
  633 + 'Ly7btSyvPCT2QFeJdxcF/9TDJDbUaySxXp67x5eY2OGeS6M67ZxTsFOMI0IOEmkX',
  634 + 'isxQXpt29iwyBi0vqmw9Q7yJgT+MRHekTCCozEiW9s9yuKUea1jsJ92bgHaP177J',
  635 + 'pQ3JRhtWJBN2faKpsrLGcnKjTGgRZTJR6tkIiNkmd2tmIjB7m5ViKVIJz0Yoz8xf',
  636 + 'IxF6kJWMBKnx/VitGxKXUb9y8zrBn9TTn2YE9Ag8YyxjLAs/HkKfmQb0nc43Pf0W',
  637 + 'NEhdrmRC9IYI93M/+oZneJjw7ix4PPFLOoNhJ272zALDb8EhkadSSl/fDks3RXmI',
  638 + 'XzZVjNlRDROiM49xxYjINSGhBKYR5t6TG/RnvqyVqVpzgQ4X43XBNkMFHdm5tcB1',
  639 + 'bRas12EfdpgGmDsYCcj2YulH+0tj56fDw8lmOOrkVYQZO8hmRJYrJPXNPoE53UEN',
  640 + 'XjhBvYCsWDBltgZJqWfLLDIJ9Q118332m+oS2W6Q+gMlcdlRzvVpZcruIc6jz0SS',
  641 + 'WgJC/vpuKCF+8MDwO8OCGO6Vd+3CJeyJdMFAEaGZi6LLU+FZsaR3yC4ddieop5Mi',
  642 + 'ZCUoewWoBlbgD8DB6lhlaz9VwW4nc7yJKVLGWXQxycJpcbjbMf9SscKvyScA7/wF',
  643 + '405Y31u59NRafGrJtsvipd8DYK+al7cWCsBUJU4G9QNnAlOVQBbxUnf7VgbhC1xz',
  644 + 'DLZZScLcLIsVPBUzHXPgr5TZLTqjrKFVAGI4C6yMJW7R9r56J4LLgXxAj9Nls93j',
  645 + 'CTBZZEoneu6V4tGNNCh8dE9rsXb7Szz9bFZKjhoIbVXEpSbCta0+m0JYsRvMMdMt',
  646 + 'bQJtJaJBoY4zaIvI9pweE+3UI7NFXBxG23sZ/NJYnpR/wNoo6Qi1JvWxh+Q1cgoO',
  647 + '+BOBvvhuugCz653K2ETIGqrvWhwSpNGWLfG260+1ia7/cSDljHr6jESRAEaqRqIP',
  648 + 'A4ENfGSrzpJJ+6gO08+3lKMwJTGpcbnJxB/dBZzSBZSu86E1/b/Ry9dV9biIhOSU',
  649 + '1HPuXF3ULJJZYzApnTt6AH/vJMncYrV8flB/YfApXoYixaNBKTg0h2K38H5vysti',
  650 + 'RPiYIReVWKaSqg7sAJa03SPJx5tbblAmwz4fd4JXmjFpbAuQvSy1cYwfSkjn4Rk4',
  651 + 'VVFqtqud9MYzVWLBX1EZ3Nd6nSC9UsVUtza3Wzs5lWgNev/9rzIu99SKPW9nRclM',
  652 + '5eD+BFd+EDtrsjPsjfHFru5B9rJCq9LfKDenghbfTTKoQMYj2y3toNYrcETzMvhv',
  653 + '-----END RSA PRIVATE KEY-----'
  654 + ].join('\n');
  655 + var converted = octokey.privateKey.convert(input_pem, 'password');
  656 + expect(converted.pem).toBeNull();
  657 + expect(converted.errors).toContain('incorrect input passphrase'); // can't tell the difference :(
  658 + });
  659 +
  660 + it('should reject an unencrypted traditional DSA key', function () {
  661 + var input_pem = [
  662 + '-----BEGIN DSA PRIVATE KEY-----',
  663 + 'MIIBugIBAAKBgQDVKSUqVuO/gEzML7xwjhdP6en6OiRqD329HipJs7T+QlDKpf4k',
  664 + '5YyRSR3F4Jv+i+mxYNdhrtTvEP+/fLr2+vuScoB4vYQAbu+ukIjvMpSHb+xU/pbu',
  665 + 'EdCqMGa7jq4wzHhL3uFk5nsunZ8q6NLxg4if3GoHud+9sd9fgZcQmSL1XwIVAMG9',
  666 + 'xF9+Lcqom6ZFuoeJv7/8HSDlAoGAVJmPgN9UiEaI8pmHIudqwufA506DNaTDwyMy',
  667 + 'oQ4RBjCzAh9OFQ6BnUmKGNkzXEmbWnuXgBL7wuW7E5cX3FIz80zdxfiWkeSZ1eNp',
  668 + 'oK2GoLQk3OyMamXah8mZWRr0xZlGdCBcE3brGiA6nmfZqe4VZVAB2lwesruXEydu',
  669 + 'hx7j8PYCgYAwoUeWTdT95mYUnJQ39YUZ4ZOIs4f4PKelNcZgOeO4mUO/sSi9Yp+w',
  670 + 'epwY+O1l7dYr2BchVy31OnXwZbTe7zsKavodOs0uP6GtkkO/00BlkG8bVjsgE2BM',
  671 + 'AmjhEmcUYKn0q7JBGp5vhMeAL5mnEDDho2plvqofzuwKm2o5DWXyWAIUQL78qCgx',
  672 + 'n8cVFqWqMgp7bxLPoec=',
  673 + '-----END DSA PRIVATE KEY-----'
  674 + ].join('\n');
  675 + var converted = octokey.privateKey.convert(input_pem);
  676 + expect(converted.pem).toBeNull();
  677 + expect(converted.errors).toContain('DSA private keys are not supported');
  678 + });
  679 +
  680 + it('should reject an encrypted traditional DSA key', function () {
  681 + var input_pem = [
  682 + '-----BEGIN DSA PRIVATE KEY-----',
  683 + 'Proc-Type: 4,ENCRYPTED',
  684 + 'DEK-Info: AES-128-CBC,6C3F67E991F6002818A7232048802D4A',
  685 + '',
  686 + 'l1JHgTEs+qRtwqR/FTxCeH87d/X9lCxw8i8L5pMQwnChr2zSux/1UP+y1VGuWRPr',
  687 + 'Dou2GxnElla81FC93xUupFn8gqp1hMXzhnOEHBP6+wUUswFW1GncuXIpdww5R+o7',
  688 + 'SiWfWjHRw6pHuJ9eBK/DssljVN8IzWpMk5yktRymC4UAflZ/nBYc99qI8mBe8ZV0',
  689 + 'hxZ89urMH309TDV7fhBJGKmITbrJrZo6hRmysEClXzrJWJI2BJ8lAZ4WezLZNu82',
  690 + 'irYmeDIxKpiGuPOjaW7ecAP00Ms89kei7Tk4L22xiji48c085Xh4yr/lMaIfFIXE',
  691 + 'bIPGy3LpowYtX/czxu8YYpmeGt6mLdH4VOC3SHIphybWliN6PgT19l2DPMV1+Kro',
  692 + 'rdkWaMa8GXAvFnu2hCnLRALeDlGxz8pFGPzusZ6oUUYMKJDpNNbZtd6ADJ8Nkp1N',
  693 + 'ExvPIuDbRrRK5+ZzZ7fWMUxmbpIXZPmeAQxf91wg2emUZKfLNL2wpGGqcPMAZUev',
  694 + 'iszHQf7YnQktXqKIV77zpiCfgiDjL3hvvHeBc6RJ1OmSM6uUxKlChUN4F5Z6wSA5',
  695 + '35176vkLKwSlD0I0ceTX/Q==',
  696 + '-----END DSA PRIVATE KEY-----'
  697 + ].join('\n');
  698 + var converted = octokey.privateKey.convert(input_pem, 'password');
  699 + expect(converted.pem).toBeNull();
  700 + expect(converted.errors).toContain('DSA private keys are not supported');
  701 + });
  702 +
  703 + it('should reject an unencrypted PKCS#8-encoded DSA key', function () {
  704 + var input_pem = [
  705 + '-----BEGIN PRIVATE KEY-----',
  706 + 'MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBANwjgJrL28LohOt/SPrmDB+lYtt0',
  707 + 'FjqMbrFppvA1xAsrNRIA78fE8pMYdKpr/+f6CLpELC8fqJfH5mLcPr6occAnbnXd',
  708 + 'hvqzYW3eUWJb1yDrP9D/rAG0r3KQxjXYrVTVUrGcvLWgtnXlGp66c3ikVlfTRByH',
  709 + 'wpSYWoJXNN+4Ah2HAhUA/6XELRMItFHgGRaedtcJDwnSrfUCgYEArj9TvhMmOCvr',
  710 + 'HupJ4N5qQbpdTZRJ0NxgXXWNxzfnK/QjcRJGarQ3Mdb4DFcHexdihFP1zUWIEdPx',
  711 + '4sU3X8ofpCpyMM6dQWD0QtDU5Zk7ZcJzu086bQ57vdkIRxDY+YdIAZCla5w8r1uC',
  712 + 'sfcdIKHDB2JZoQgH70rcSYvdstjk+Q0EFgIUDhbS9IhVyGZ7u4grOp4kec435ew=',
  713 + '-----END PRIVATE KEY-----'
  714 + ].join('\n');
  715 + var converted = octokey.privateKey.convert(input_pem);
  716 + expect(converted.pem).toBeNull();
  717 + expect(converted.errors).toContain('Cannot read private key. ASN.1 object is not an RSAPrivateKey.');
284 718 });
285 719 });
286 720 });

0 comments on commit 8cb214d

Please sign in to comment.
Something went wrong with that request. Please try again.