Compare commits
No commits in common. "v1.0.2" and "master" have entirely different histories.
69
README.md
69
README.md
@ -1,62 +1,81 @@
|
|||||||
# SSH Fingerprint (for node.js)
|
# SSH to JWK (for node.js)
|
||||||
|
|
||||||
A minimal library to read an SSH public key (`id_rsa.pub`)
|
A minimal library to parse an SSH public key (`id_rsa.pub`)
|
||||||
and generate its fingerprint.
|
and convert it into a public JWK.
|
||||||
|
|
||||||
Works for RSA and ECDSA public keys.
|
Works for RSA and ECDSA public keys.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
========
|
========
|
||||||
|
|
||||||
< 125 lines of code | <1kb gzipped | 2.0kb minified | 3.5kb with comments
|
< 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments
|
||||||
|
|
||||||
* [x] SSH Public Key SHA256 Fingerprints
|
* [x] SSH Public Keys
|
||||||
* RSA (2048-bit, 3072-bit, 4096-bit)
|
* fingerprint
|
||||||
* EC / ECDSA
|
* [x] RSA Public Keys
|
||||||
* P-256 (prime256v1, secp256r1)
|
* [x] EC Public Keys
|
||||||
* P-384 (secp384r1)
|
* P-256 (prime256v1, secp256r1)
|
||||||
|
* P-384 (secp384r1)
|
||||||
* [x] Browser Version
|
* [x] Browser Version
|
||||||
* [Bluecrypt SSH Fingerprint](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js)
|
* [Bluecrypt SSH to JWK](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-to-jwk.js)
|
||||||
|
|
||||||
|
### Need JWK to SSH? SSH to PEM?
|
||||||
|
|
||||||
|
Try one of these:
|
||||||
|
|
||||||
|
* [jwk-to-ssh.js](https://git.coolaj86.com/coolaj86/jwk-to-ssh.js) (RSA + EC)
|
||||||
|
* [Eckles.js](https://git.coolaj86.com/coolaj86/eckles.js) (more EC utils)
|
||||||
|
* [Rasha.js](https://git.coolaj86.com/coolaj86/eckles.js) (more RSA utils)
|
||||||
|
|
||||||
|
### Need SSH Private Keys?
|
||||||
|
|
||||||
|
SSH private keys are just normal PEM files,
|
||||||
|
so you can use Eckles or Rasha, as mentioned above.
|
||||||
|
|
||||||
# CLI
|
# CLI
|
||||||
|
|
||||||
You can install `ssh-fingerprint` and use it from command line:
|
You can install `ssh-to-jwk` and use it from command line:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g greenlock-ssh-fingerprint
|
npm install -g ssh-to-jwk
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh-fingerprint ~/.ssh/id_rsa.pub
|
ssh-to-jwk ~/.ssh/id_rsa.pub
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
2048 SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko root@localhost (RSA)
|
|
||||||
```
|
|
||||||
|
|
||||||
(the output is the same as `ssh-keygen -lf ~/.ssh/id_rsa.pub`)
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
You can also use it from JavaScript:
|
You can also use it from JavaScript:
|
||||||
|
|
||||||
|
**SSH to JWK**
|
||||||
|
|
||||||
|
```js
|
||||||
|
var fs = require('fs');
|
||||||
|
var sshtojwk = require('ssh-to-jwk');
|
||||||
|
|
||||||
|
var pub = fs.readFileSync("./id_rsa.pub");
|
||||||
|
var ssh = sshtojwk.parse({ pub: pub });
|
||||||
|
|
||||||
|
console.info(ssh.jwk);
|
||||||
|
```
|
||||||
|
|
||||||
**SSH Fingerprint**
|
**SSH Fingerprint**
|
||||||
|
|
||||||
```js
|
```js
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var SSH = require('greenlock-ssh-fingerprint');
|
var sshtojwk = require('ssh-to-jwk');
|
||||||
var pub = fs.readFileSync("./id_rsa.pub");
|
var pub = fs.readFileSync("./id_rsa.pub");
|
||||||
|
|
||||||
SSH.fingerprint({ pub: pub }).then(function (fing) {
|
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
|
||||||
console.info('The key fingerprint is:');
|
console.info(fingerprint);
|
||||||
console.info(fing.fingerprint, fing.comment);
|
// SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko
|
||||||
// SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko root@localhost
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
# Legal
|
# Legal
|
||||||
|
|
||||||
[ssh-fingerprint.js](https://git.coolaj86.com/coolaj86/ssh-fingerprint.js) |
|
[ssh-to-jwk.js](https://git.coolaj86.com/coolaj86/ssh-to-jwk.js) |
|
||||||
MPL-2.0 |
|
MPL-2.0 |
|
||||||
[Terms of Use](https://therootcompany.com/legal/#terms) |
|
[Terms of Use](https://therootcompany.com/legal/#terms) |
|
||||||
[Privacy Policy](https://therootcompany.com/legal/#privacy)
|
[Privacy Policy](https://therootcompany.com/legal/#privacy)
|
||||||
|
@ -13,9 +13,10 @@ if (!pubfile) {
|
|||||||
|
|
||||||
var buf = fs.readFileSync(pubfile);
|
var buf = fs.readFileSync(pubfile);
|
||||||
var pub = buf.toString('ascii');
|
var pub = buf.toString('ascii');
|
||||||
|
var ssh = sshtojwk.parse({ pub: pub });
|
||||||
|
|
||||||
// Finally! https://superuser.com/a/714195
|
// Finally! https://superuser.com/a/714195
|
||||||
sshtojwk.fingerprint({ pub: pub }).then(function (fing) {
|
sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) {
|
||||||
var kty = ('RSA' === fing.kty ? 'RSA' : 'ECDSA');
|
console.warn('The key fingerprint is:\n' + fingerprint + ' ' + ssh.comment);
|
||||||
console.info((fing.size*8).toString(), fing.fingerprint, fing.comment, '(' + kty + ')');
|
console.info(JSON.stringify(ssh.jwk, null, 2));
|
||||||
});
|
});
|
@ -1 +0,0 @@
|
|||||||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= P-256@localhost
|
|
@ -1 +0,0 @@
|
|||||||
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNsxFNGygmu3oyiyCfKDxpy4aoccor+P8N/CtmtEjwunbEnff4JTSfJXKr9LH3+Rm1Q+I57vN0urHJ+v03gI9xGTkRBmzrOnc6FaWroJ/l0DDMgvTuFS2wwxRgWUyZTLGw== P-384@localhost
|
|
@ -1 +0,0 @@
|
|||||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost
|
|
2
index.js
2
index.js
@ -1 +1 @@
|
|||||||
module.exports = require('./lib/ssh-fingerprint.js');
|
module.exports = require('./lib/ssh-parser.js');
|
||||||
|
@ -3,6 +3,14 @@
|
|||||||
var SSH = module.exports;
|
var SSH = module.exports;
|
||||||
var Enc = require('./encoding.js');
|
var Enc = require('./encoding.js');
|
||||||
|
|
||||||
|
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*/
|
/*global Promise*/
|
||||||
SSH.fingerprint = function (opts) {
|
SSH.fingerprint = function (opts) {
|
||||||
var ssh;
|
var ssh;
|
||||||
@ -11,23 +19,10 @@ SSH.fingerprint = function (opts) {
|
|||||||
} else {
|
} else {
|
||||||
ssh = SSH.parseBlock(opts.pub);
|
ssh = SSH.parseBlock(opts.pub);
|
||||||
}
|
}
|
||||||
ssh = SSH.parseElements(ssh);
|
|
||||||
|
|
||||||
// for browser compat
|
// for browser compat
|
||||||
return Promise.resolve().then(function () {
|
return Promise.resolve().then(function () {
|
||||||
var digest = require('crypto').createHash('sha256')
|
return 'SHA256:' + require('crypto').createHash('sha256')
|
||||||
.update(ssh.bytes).digest();
|
.update(ssh.bytes).digest('base64').replace(/=+$/g, '');
|
||||||
// 2048 SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko rsa@localhost (RSA)
|
|
||||||
// 256 SHA256:wcH95nkL7ZkeRURHpLuoThIBEIIKkYgf9etD18PIx40 P-256@localhost (ECDSA)
|
|
||||||
ssh = SSH.parseKeyType(ssh);
|
|
||||||
return {
|
|
||||||
type: 'sha256'
|
|
||||||
, digest: digest
|
|
||||||
, fingerprint: 'SHA256:' + digest.toString('base64').replace(/=+$/g, '')
|
|
||||||
, size: ssh.size
|
|
||||||
, comment: ssh.comment
|
|
||||||
, kty: ssh.kty
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,29 +71,44 @@ SSH.parseElements = function (ssh) {
|
|||||||
return ssh;
|
return ssh;
|
||||||
};
|
};
|
||||||
|
|
||||||
SSH.parseKeyType = function (ssh) {
|
SSH.parsePublicKey = function (ssh) {
|
||||||
var els = ssh.elements;
|
var els = ssh.elements;
|
||||||
var typ = Enc.bufToBin(els[0]);
|
var typ = Enc.bufToBin(els[0]);
|
||||||
|
var len;
|
||||||
|
|
||||||
// RSA keys are all the same
|
// RSA keys are all the same
|
||||||
if (SSH.types.rsa === typ) {
|
if (SSH.types.rsa === typ) {
|
||||||
ssh.kty = 'RSA';
|
ssh.jwk = {
|
||||||
ssh.size = (ssh.elements[2].byteLength || ssh.lements[2].length);
|
kty: 'RSA'
|
||||||
|
, n: Enc.bufToUrlBase64(els[2])
|
||||||
|
, e: Enc.bufToUrlBase64(els[1])
|
||||||
|
};
|
||||||
return ssh;
|
return ssh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// EC keys are each different
|
// EC keys are each different
|
||||||
if (SSH.types.p256 === typ) {
|
if (SSH.types.p256 === typ) {
|
||||||
ssh.kty = 'EC';
|
len = 32;
|
||||||
ssh.size = 32;
|
ssh.jwk = { kty: 'EC', crv: 'P-256' };
|
||||||
} else if (SSH.types.p384 === typ) {
|
} else if (SSH.types.p384 === typ) {
|
||||||
ssh.kty = 'EC';
|
len = 48;
|
||||||
ssh.size = 48;
|
ssh.jwk = { kty: 'EC', crv: 'P-384' };
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unsupported ssh public key type: "
|
throw new Error("Unsupported ssh public key type: "
|
||||||
+ Enc.bufToBin(els[0]));
|
+ 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;
|
return ssh;
|
||||||
};
|
};
|
||||||
|
|
22
package.json
22
package.json
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "greenlock-ssh-fingerprint",
|
"name": "ssh-to-jwk",
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"description": "💯 SSH Fingerprint in < 125 lines of VanillaJS node, Zero Dependencies.",
|
"description": "💯 SSH to JWK in a lightweight, zero-dependency library.",
|
||||||
"homepage": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js",
|
"homepage": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"ssh-fingerprint": "bin/ssh-fingerprint.js"
|
"ssh-to-jwk": "bin/ssh-to-jwk.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
@ -21,15 +21,17 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js"
|
"url": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"zero-dependency",
|
"zero-dependency",
|
||||||
|
"SSH-to-JWK",
|
||||||
|
"RSA",
|
||||||
|
"EC",
|
||||||
"SSH",
|
"SSH",
|
||||||
"Fingerprint",
|
"fingerprint",
|
||||||
"rsa",
|
"JWK",
|
||||||
"ec",
|
"ECDSA"
|
||||||
"ecdsa"
|
|
||||||
],
|
],
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
||||||
"license": "MPL-2.0"
|
"license": "MPL-2.0"
|
||||||
|
23
test.sh
23
test.sh
@ -1,23 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "Test RSA key"
|
|
||||||
ssh-keygen -lf fixtures/pub-rsa-2048.ssh.pub | tee fingerprint.txt
|
|
||||||
node bin/ssh-fingerprint.js fixtures/pub-rsa-2048.ssh.pub > fingerprint.1.txt
|
|
||||||
diff fingerprint.txt fingerprint.1.txt
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "Test EC P-256 key"
|
|
||||||
ssh-keygen -lf fixtures/pub-ec-p256.ssh.pub | tee fingerprint.txt
|
|
||||||
node bin/ssh-fingerprint.js fixtures/pub-ec-p256.ssh.pub > fingerprint.1.txt
|
|
||||||
diff fingerprint.txt fingerprint.1.txt
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "Test EC P-384 key"
|
|
||||||
ssh-keygen -lf fixtures/pub-ec-p384.ssh.pub | tee fingerprint.txt
|
|
||||||
node bin/ssh-fingerprint.js fixtures/pub-ec-p384.ssh.pub > fingerprint.1.txt
|
|
||||||
diff fingerprint.txt fingerprint.1.txt
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "All Tests Passed"
|
|
||||||
echo "• generated fingerprints of fixtures match ssh-keygen output"
|
|
Loading…
x
Reference in New Issue
Block a user