v1.1.0: add ssh fingerprint

This commit is contained in:
AJ ONeal 2018-12-02 22:07:51 -07:00
parent 88f688184a
commit 6b6fd5e01d
4 changed files with 99 additions and 49 deletions

View File

@ -11,6 +11,7 @@ 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 * [x] SSH Public Keys
* fingerprint
* [x] RSA Public Keys * [x] RSA Public Keys
* [x] EC Public Keys * [x] EC Public Keys
* P-256 (prime256v1, secp256r1) * P-256 (prime256v1, secp256r1)
@ -47,18 +48,32 @@ ssh-to-jwk ~/.ssh/id_rsa.pub
You can also use it from JavaScript: You can also use it from JavaScript:
**SSH to JWK**
```js ```js
var fs = require('fs'); var fs = require('fs');
var sshtojwk = require('ssh-to-jwk'); var sshtojwk = require('ssh-to-jwk');
var pub = fs.readFileSync("./id_rsa.pub"); var pub = fs.readFileSync("./id_rsa.pub");
var ssh = sshtojwk.parse(pub); var ssh = sshtojwk.parse({ pub: pub });
console.info(ssh.jwk); console.info(ssh.jwk);
``` ```
Legal **SSH Fingerprint**
-----
```js
var fs = require('fs');
var sshtojwk = require('ssh-to-jwk');
var pub = fs.readFileSync("./id_rsa.pub");
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
console.info(fingerprint);
// SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko
});
```
# 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) |
MPL-2.0 | MPL-2.0 |

View File

@ -12,6 +12,11 @@ if (!pubfile) {
} }
var buf = fs.readFileSync(pubfile); var buf = fs.readFileSync(pubfile);
var ssh = sshtojwk.parse(buf.toString('ascii')); var pub = buf.toString('ascii');
var ssh = sshtojwk.parse({ pub: pub });
console.info(JSON.stringify(ssh.jwk, null, 2)); // Finally! https://superuser.com/a/714195
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
console.warn('The key fingerprint is:\n' + fingerprint + ' ' + ssh.comment);
console.info(JSON.stringify(ssh.jwk, null, 2));
});

View File

@ -3,49 +3,37 @@
var SSH = module.exports; var SSH = module.exports;
var Enc = require('./encoding.js'); var Enc = require('./encoding.js');
SSH.parse = function (ssh) { SSH.parse = function (opts) {
var pub = opts.pub || opts;
var ssh = SSH.parseBlock(pub);
ssh = SSH.parseElements(ssh);
//delete ssh.bytes;
return SSH.parsePublicKey(ssh);
};
/*global Promise*/
SSH.fingerprint = function (opts) {
var ssh;
if (opts.bytes) {
ssh = opts;
} else {
ssh = SSH.parseBlock(opts.pub);
}
// for browser compat
return Promise.resolve().then(function () {
return 'SHA256:' + require('crypto').createHash('sha256')
.update(ssh.bytes).digest('base64').replace(/=+$/g, '');
});
};
SSH.parseBlock = function (ssh) {
ssh = ssh.split(/\s+/g); ssh = ssh.split(/\s+/g);
var result = { type: ssh[0], jwk: null, comment: ssh[2] || '' }; return {
var buf = Enc.base64ToBuf(ssh[1]); type: ssh[0]
var els = SSH.parseElements({ bytes: buf }).elements; , bytes: Enc.base64ToBuf(ssh[1])
var typ = Enc.bufToBin(els[0]); , comment: ssh[2]
var len; };
// RSA keys are all the same
if (SSH.types.rsa === typ) {
result.jwk = {
kty: 'RSA'
, n: Enc.bufToUrlBase64(els[2])
, e: Enc.bufToUrlBase64(els[1])
};
return result;
}
// EC keys are each different
if (SSH.types.p256 === typ) {
len = 32;
result.jwk = { kty: 'EC', crv: 'P-256' };
} else if (SSH.types.p384 === typ) {
len = 48;
result.jwk = { kty: 'EC', crv: 'P-384' };
} else {
throw new Error("Unsupported ssh public key type: "
+ Enc.bufToBin(els[0]));
}
// els[1] is just a repeat of a subset of els[0]
var x = els[2].slice(1, 1 + len);
var y = els[2].slice(1 + len, 1 + len + len);
// I don't think EC keys use 0x00 padding, but just in case
while (0x00 === x[0]) { x = x.slice(1); }
while (0x00 === y[0]) { y = y.slice(1); }
result.jwk.x = Enc.bufToUrlBase64(x);
result.jwk.y = Enc.bufToUrlBase64(y);
return result;
}; };
SSH.parseElements = function (ssh) { SSH.parseElements = function (ssh) {
@ -74,15 +62,56 @@ SSH.parseElements = function (ssh) {
index += len; index += len;
} }
if (fulllen !== index) { if (fulllen !== index) {
throw new Error(els.map(function (b) { throw new Error("invalid ssh public key length \n" + els.map(function (b) {
return Enc.bufToHex(b); return Enc.bufToHex(b);
}).join('\n') + "invalid ssh public key length"); }).join('\n'));
} }
ssh.elements = els; ssh.elements = els;
return ssh; return ssh;
}; };
SSH.parsePublicKey = function (ssh) {
var els = ssh.elements;
var typ = Enc.bufToBin(els[0]);
var len;
// RSA keys are all the same
if (SSH.types.rsa === typ) {
ssh.jwk = {
kty: 'RSA'
, n: Enc.bufToUrlBase64(els[2])
, e: Enc.bufToUrlBase64(els[1])
};
return ssh;
}
// EC keys are each different
if (SSH.types.p256 === typ) {
len = 32;
ssh.jwk = { kty: 'EC', crv: 'P-256' };
} else if (SSH.types.p384 === typ) {
len = 48;
ssh.jwk = { kty: 'EC', crv: 'P-384' };
} else {
throw new Error("Unsupported ssh public key type: "
+ Enc.bufToBin(els[0]));
}
// els[1] is just a repeat of a subset of els[0]
var x = els[2].slice(1, 1 + len);
var y = els[2].slice(1 + len, 1 + len + len);
// I don't think EC keys use 0x00 padding, but just in case
while (0x00 === x[0]) { x = x.slice(1); }
while (0x00 === y[0]) { y = y.slice(1); }
ssh.jwk.x = Enc.bufToUrlBase64(x);
ssh.jwk.y = Enc.bufToUrlBase64(y);
return ssh;
};
SSH.types = { SSH.types = {
// 19 '00000013' // 19 '00000013'
// e c d s a - s h a 2 - n i s t p 2 5 6 // e c d s a - s h a 2 - n i s t p 2 5 6

View File

@ -1,6 +1,6 @@
{ {
"name": "ssh-to-jwk", "name": "ssh-to-jwk",
"version": "1.0.2", "version": "1.1.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,6 +29,7 @@
"RSA", "RSA",
"EC", "EC",
"SSH", "SSH",
"fingerprint",
"JWK", "JWK",
"ECDSA" "ECDSA"
], ],