Compare commits

..

No commits in common. "master" and "v1.2.0" have entirely different histories.

11 changed files with 79 additions and 143 deletions

View File

@ -5,23 +5,23 @@ and convert it into a public JWK.
Works for RSA and ECDSA public keys. Works for RSA and ECDSA public keys.
# Features Features
========
&lt; 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments &lt; 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments
* [x] SSH Public Keys ([RFC 4253](https://coolaj86.com/articles/the-ssh-public-key-format/)) * [x] SSH Public Keys
* fingerprint * fingerprint
* [x] OpenSSH Private Keys * [x] SSH EC Private Keys
* [x] RSA * [ ] SSH RSA Private Keys
* 2048, 3072, 4096 * `dp` and `dq` values are unavailable
* [x] RSA Public Keys
* [x] EC Public Keys * [x] EC Public Keys
* P-256 (prime256v1, secp256r1) * P-256 (prime256v1, secp256r1)
* P-384 (secp384r1) * P-384 (secp384r1)
* [x] Browser Version * [x] Browser Version
* [Bluecrypt SSH to JWK](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-to-jwk.js) * [Bluecrypt SSH to JWK](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-to-jwk.js)
Note: Lines of code have increased by about 2x since adding private key support.
### Need JWK to SSH? SSH to PEM? ### Need JWK to SSH? SSH to PEM?
Try one of these: Try one of these:
@ -36,7 +36,9 @@ Many SSH private keys are just normal PEM files,
so you can use Eckles or Rasha, as mentioned above. so you can use Eckles or Rasha, as mentioned above.
As for the [OpenSSH-specific Private Keys](https://coolaj86.com/articles/the-openssh-private-key-format/), As for the [OpenSSH-specific Private Keys](https://coolaj86.com/articles/the-openssh-private-key-format/),
both EC and RSA are fully supported. EC is **fully supported**, but RSA has only partial support.
For more information see the "SSH Private Keys" section at the end of this file.
# CLI # CLI
@ -50,10 +52,6 @@ npm install -g ssh-to-jwk
ssh-to-jwk ~/.ssh/id_rsa.pub ssh-to-jwk ~/.ssh/id_rsa.pub
``` ```
```bash
ssh-to-jwk ~/.ssh/id_rsa
```
# Usage # Usage
You can also use it from JavaScript: You can also use it from JavaScript:
@ -63,13 +61,10 @@ You can also use it from JavaScript:
```js ```js
var fs = require('fs'); var fs = require('fs');
var sshtojwk = require('ssh-to-jwk'); var sshtojwk = require('ssh-to-jwk');
var ssh;
ssh = sshtojwk.parse({ pub: fs.readFileSync("./id_rsa.pub") }); var pub = fs.readFileSync("./id_rsa.pub");
console.info(ssh.jwk); var ssh = sshtojwk.parse({ pub: pub });
// For OpenSSH PEMs only, use Rasha for standard RSA or Eckles for standard EC
ssh = sshtojwk.parse({ pem: fs.readFileSync("./id_rsa") });
console.info(ssh.jwk); console.info(ssh.jwk);
``` ```
@ -86,6 +81,25 @@ sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
}); });
``` ```
# SSH Private Keys
As mentioned above, EC private keys are fully supported,
and RSA private keys are partially supported.
It's unlikely that we'll support full SSH-to-JWK conversion for private RSA keys
because OpenSSH omits the `dp` and `dq` values.
Although they are "optional" (they can be computed from the available values),
to compute them in JavaScript would require a large and expensive BigInt library -
and including (or writing) such a library would require contradicting the
"lightweight" and/or "zero dependency" goals for this library.
That said, for someone willing to include a BigInt library in their code
it should be trivial to perform the operations to derive `dp` and `dq`.
If that's you please open an issue because I am interested in creating
a `ssh-to-jwk-bigint` library... I just don't have a use case for it right now.
# Legal # Legal
[ssh-to-jwk.js](https://git.coolaj86.com/coolaj86/ssh-to-jwk.js) | [ssh-to-jwk.js](https://git.coolaj86.com/coolaj86/ssh-to-jwk.js) |

View File

@ -6,27 +6,17 @@ var path = require('path');
var sshtojwk = require('../index.js'); var sshtojwk = require('../index.js');
var pubfile = process.argv[2]; var pubfile = process.argv[2];
var pub = process.argv[3];
if (!pubfile) { if (!pubfile) {
pubfile = path.join(require('os').homedir(), '.ssh/id_rsa.pub'); pubfile = path.join(require('os').homedir(), '.ssh/id_rsa.pub');
} }
var buf = fs.readFileSync(pubfile); var buf = fs.readFileSync(pubfile);
var txt = buf.toString('ascii'); var pub = buf.toString('ascii');
var opts = { public: 'public' === pub }; var ssh = sshtojwk.parse({ pub: pub });
var ssh;
if ('-' === txt[0]) {
opts.pem = txt;
} else {
opts.pub = txt;
}
ssh = sshtojwk.parse(opts);
// Finally! https://superuser.com/a/714195 // Finally! https://superuser.com/a/714195
sshtojwk.fingerprint(ssh).then(function (fingerprint) { sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
console.warn('The key fingerprint is:\n' + fingerprint + ' ' + ssh.comment); console.warn('The key fingerprint is:\n' + fingerprint + ' ' + ssh.comment);
console.info(JSON.stringify(ssh.jwk, null, 2)); console.info(JSON.stringify(ssh.jwk, null, 2));
}); });

View File

@ -1,7 +0,0 @@
{
"kty": "EC",
"crv": "P-256",
"d": "iYydo27aNGO9DBUWeGEPD8oNi1LZDqfxPmQlieLBjVQ",
"x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
"y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
}

View File

@ -1,9 +1,9 @@
-----BEGIN OPENSSH PRIVATE KEY----- -----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQhPVJYvGxpw+ITlnXqOSikCfz/7zms 1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR9WZPeBSvixkhjQOh9yCXXlEx5CN9M
yODIKiSueMN+3pj9icDgDnTJl7sKcWyp4Nymc9u5s/pyliJVyd680hjKAAAAqGJjanNiY2 yh94CJJ1rigf8693gc90HmahIR5oMGHwlqMoS7kKrRw+4KpxqsF7LGvxAAAAqJZtgRuWbY
pzAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOW EbAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBH1Zk94FK+LGSGNA
deo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGM 6H3IJdeUTHkI30zKH3gIknWuKB/zr3eBz3QeZqEhHmgwYfCWoyhLuQqtHD7gqnGqwXssa/
oAAAAhAImMnaNu2jRjvQwVFnhhDw/KDYtS2Q6n8T5kJYniwY1UAAAADnJvb3RAbG9jYWxo EAAAAgBzKpRmMyXZ4jnSt3ARz0ul6R79AXAr5gQqDAmoFeEKwAAAAOYWpAYm93aWUubG9j
b3N0AQ== YWwBAg==
-----END OPENSSH PRIVATE KEY----- -----END OPENSSH PRIVATE KEY-----

View File

@ -1,7 +0,0 @@
{
"kty": "EC",
"crv": "P-384",
"d": "XlyuCEWSTTS8U79O_Mz05z18vh4kb10szvu_7pdXuGWV6lfEyPExyUYWsA6A2kdV",
"x": "2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4_w38K2a0SPC6dsSd9_glNJ8lcqv0sff5Gb",
"y": "VD4jnu83S6scn6_TeAj3EZOREGbOs6dzoVpaugn-XQMMyC9O4VLbDDFGBZTJlMsb"
}

View File

@ -1,10 +1,10 @@
-----BEGIN OPENSSH PRIVATE KEY----- -----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTbMRTRsoJrt6Mosgnyg8acuGqHHKK/ 1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTDNcLzjWUqG5H+grU/Z+RNOsvqH4V5
j/DfwrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdLqxyfr9N4CPcRk5EQZs6zp3OhWl qU1UlkUzqTImYvm7ClYgYtXqbReCzLn1E+DOQw1N1f5E/YjPNduLlklsEv3q55k7BDTTiN
q6Cf5dAwzIL07hUtsMMUYFlMmUyxsAAADYYmNqc2JjanMAAAATZWNkc2Etc2hhMi1uaXN0 k5c15CpCbIV4eWeLRSFSJBGQHlv+sAAADYZYs6wGWLOsAAAAATZWNkc2Etc2hhMi1uaXN0
cDM4NAAAAAhuaXN0cDM4NAAAAGEE2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4/w38K2a0SPC6 cDM4NAAAAAhuaXN0cDM4NAAAAGEEwzXC841lKhuR/oK1P2fkTTrL6h+FealNVJZFM6kyJm
dsSd9/glNJ8lcqv0sff5GbVD4jnu83S6scn6/TeAj3EZOREGbOs6dzoVpaugn+XQMMyC9O L5uwpWIGLV6m0Xgsy59RPgzkMNTdX+RP2IzzXbi5ZJbBL96ueZOwQ004jZOXNeQqQmyFeH
4VLbDDFGBZTJlMsbAAAAMF5crghFkk00vFO/TvzM9Oc9fL4eJG9dLM77v+6XV7hllepXxM lni0UhUiQRkB5b/rAAAAMQDbyZ7XRFtCCvdmdYJPkPuMzQBO5VJ1g/9eeFjI2ZLyIhtPh3
jxMclGFrAOgNpHVQAAAA5yb290QGxvY2FsaG9zdAEC tvrki2EjEi8X4iLroAAAAOYWpAYm93aWUubG9jYWwB
-----END OPENSSH PRIVATE KEY----- -----END OPENSSH PRIVATE KEY-----

View File

@ -1,11 +0,0 @@
{
"kty": "RSA",
"n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw",
"e": "AQAB",
"d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ",
"p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE",
"q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c",
"dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE",
"dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48",
"qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU"
}

View File

@ -1,27 +1,27 @@
-----BEGIN OPENSSH PRIVATE KEY----- -----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY----- -----END OPENSSH PRIVATE KEY-----

View File

@ -10,20 +10,6 @@ Enc.base64ToBuf = function (str) {
return Buffer.from(str, 'base64'); return Buffer.from(str, 'base64');
}; };
Enc.base64ToHex = function (str) {
return Buffer.from(str, 'base64').toString('hex');
};
Enc.base64ToUrlBase64 = function (b64) {
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
};
Enc.bnToUrlBase64 = function (bn) {
var hex = bn.toString(16);
if (hex.length % 2) { hex = '0' + hex; }
return Enc.base64ToUrlBase64(Buffer.from(hex, 'hex').toString('base64'));
};
Enc.bufToBase64 = function (u8) { Enc.bufToBase64 = function (u8) {
return Buffer.from(u8).toString('base64'); return Buffer.from(u8).toString('base64');
}; };
@ -37,5 +23,6 @@ Enc.bufToHex = function (u8) {
}; };
Enc.bufToUrlBase64 = function (u8) { Enc.bufToUrlBase64 = function (u8) {
return Enc.base64ToUrlBase64(Enc.bufToBase64(u8)); return Enc.bufToBase64(u8)
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}; };

View File

@ -1,21 +1,14 @@
'use strict'; 'use strict';
/*global BigInt*/
var SSH = module.exports; var SSH = module.exports;
var Enc = require('./encoding.js'); var Enc = require('./encoding.js');
var PEM = require('./pem.js'); var PEM = require('./pem.js');
var bnwarn = false;
SSH.parse = function (opts) { SSH.parse = function (opts) {
var pub = opts.pem || opts.pub || opts; var pub = opts.pub || opts;
var ssh = SSH.parseBlock(pub); var ssh = SSH.parseBlock(pub);
if ('OPENSSH PRIVATE KEY' === ssh.type) { if ('OPENSSH PRIVATE KEY' === ssh.type) {
ssh = SSH.parsePrivateElements(ssh); ssh = SSH.parsePrivateElements(ssh);
if (7 === ssh.elements.length) {
// RSA Private Keys have the `e` and `n` swapped (which is actually more normal)
// but we have to reswap them to make them consistent with the public key format
ssh.elements.splice(1, 0, ssh.elements.splice(2 ,1)[0]);
}
if (opts.public) { if (opts.public) {
ssh.elements = ssh.elements.slice(0, 3); ssh.elements = ssh.elements.slice(0, 3);
} }
@ -64,7 +57,6 @@ SSH.parsePrivateElements = function (ssh) {
var index = 0; var index = 0;
var padlen = 0; var padlen = 0;
var len; var len;
var pub;
// The last byte will be either // The last byte will be either
// * a non-printable pad character // * a non-printable pad character
@ -99,7 +91,6 @@ SSH.parsePrivateElements = function (ssh) {
len = dv.getUint32(index, false); len = dv.getUint32(index, false);
// throw away public key (it's in the private key) // throw away public key (it's in the private key)
index += 4 + len; index += 4 + len;
pub = ssh.bytes.slice(index - len, index);
// length of dummy checksum + private key + padding // length of dummy checksum + private key + padding
len = dv.getUint32(index, false) - padlen; len = dv.getUint32(index, false) - padlen;
@ -114,7 +105,6 @@ SSH.parsePrivateElements = function (ssh) {
// comment will exist, even if it's an empty string // comment will exist, even if it's an empty string
ssh.comment = Enc.bufToBin(ssh.elements.pop()); ssh.comment = Enc.bufToBin(ssh.elements.pop());
ssh.bytes = pub;
return ssh; return ssh;
}; };
SSH.parseElements = function (buf) { SSH.parseElements = function (buf) {
@ -166,6 +156,7 @@ SSH.parsePublicKey = function (ssh) {
, e: Enc.bufToUrlBase64(els[1]) , e: Enc.bufToUrlBase64(els[1])
}; };
} else { } else {
console.log('len:', els.length);
ssh.jwk = { ssh.jwk = {
kty: 'RSA' kty: 'RSA'
, n: Enc.bufToUrlBase64(els[2]) , n: Enc.bufToUrlBase64(els[2])
@ -173,28 +164,10 @@ SSH.parsePublicKey = function (ssh) {
, d: Enc.bufToUrlBase64(els[3]) , d: Enc.bufToUrlBase64(els[3])
, p: Enc.bufToUrlBase64(els[5]) , p: Enc.bufToUrlBase64(els[5])
, q: Enc.bufToUrlBase64(els[6]) , q: Enc.bufToUrlBase64(els[6])
, dp: 0 //, dp: Enc.bufToUrlBase64(els[x])
, dq: 0 //, dq: Enc.bufToUrlBase64(els[x])
, qi: Enc.bufToUrlBase64(els[4]) , qi: Enc.bufToUrlBase64(els[4])
}; };
if ('undefined' !== typeof BigInt) {
// BigInt doesn't use new
/*jshint newcap: false*/
// d mod (p - 1)
ssh.jwk.dp = Enc.bnToUrlBase64(BigInt('0x' + Enc.base64ToHex(ssh.jwk.d))
% (BigInt('0x' + Enc.base64ToHex(ssh.jwk.p)) - BigInt(1)));
ssh.jwk.dq = Enc.bnToUrlBase64(BigInt('0x' + Enc.base64ToHex(ssh.jwk.d))
% (BigInt('0x' + Enc.base64ToHex(ssh.jwk.q)) - BigInt(1)));
} else {
if (!bnwarn) {
bnwarn = true;
// TODO maybe conditionally bring in BigInt polyfill?
console.warn("ssh-to-jwk.js: Your version of node is outdated doesn't support BigInt");
console.log("JWKs will be missing `dp` and `dq` values. Update or use a BigInt polyfill.");
}
delete ssh.jwk.dp;
delete ssh.jwk.dq;
}
} }
return ssh; return ssh;
} }
@ -221,9 +194,7 @@ SSH.parsePublicKey = function (ssh) {
while (0x00 === y[0]) { y = y.slice(1); } while (0x00 === y[0]) { y = y.slice(1); }
if (els[3]) { if (els[3]) {
d = els[3]; ssh.jwk.d = Enc.bufToUrlBase64(els[3]);
while (0x00 === d[0]) { d = d.slice(1); }
ssh.jwk.d = Enc.bufToUrlBase64(d);
} }
ssh.jwk.x = Enc.bufToUrlBase64(x); ssh.jwk.x = Enc.bufToUrlBase64(x);
ssh.jwk.y = Enc.bufToUrlBase64(y); ssh.jwk.y = Enc.bufToUrlBase64(y);

View File

@ -1,6 +1,6 @@
{ {
"name": "ssh-to-jwk", "name": "ssh-to-jwk",
"version": "1.2.6", "version": "1.2.0",
"description": "💯 SSH to JWK in a lightweight, zero-dependency library.", "description": "💯 SSH to JWK in a lightweight, zero-dependency library.",
"homepage": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js", "homepage": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js",
"main": "index.js", "main": "index.js",
@ -29,7 +29,6 @@
"RSA", "RSA",
"EC", "EC",
"SSH", "SSH",
"OpenSSH",
"fingerprint", "fingerprint",
"JWK", "JWK",
"ECDSA" "ECDSA"