v1.1.0: add ssh fingerprint
This commit is contained in:
parent
88f688184a
commit
6b6fd5e01d
21
README.md
21
README.md
@ -11,6 +11,7 @@ Features
|
|||||||
< 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments
|
< 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 |
|
||||||
|
@ -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));
|
||||||
|
});
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
],
|
],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user