From bf95bbb91c3785e38ef267503032d88721868d55 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 2 Dec 2018 23:11:49 -0700 Subject: [PATCH] v1.0.0: generate ssh-keygen compatible fingerprints --- README.md | 67 ++++++++--------------- bin/{ssh-to-jwk.js => ssh-fingerprint.js} | 7 +-- fixtures/pub-ec-p256.ssh.pub | 1 + fixtures/pub-ec-p384.ssh.pub | 1 + fixtures/pub-rsa-2048.ssh.pub | 1 + index.js | 2 +- lib/{ssh-parser.js => ssh-fingerprint.js} | 54 ++++++++---------- package.json | 23 ++++---- test.sh | 23 ++++++++ 9 files changed, 86 insertions(+), 93 deletions(-) rename bin/{ssh-to-jwk.js => ssh-fingerprint.js} (60%) create mode 100644 fixtures/pub-ec-p256.ssh.pub create mode 100644 fixtures/pub-ec-p384.ssh.pub create mode 100644 fixtures/pub-rsa-2048.ssh.pub rename lib/{ssh-parser.js => ssh-fingerprint.js} (70%) create mode 100755 test.sh diff --git a/README.md b/README.md index 3e01e41..a8aefea 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# SSH to JWK (for node.js) +# SSH Fingerprint (for node.js) -A minimal library to parse an SSH public key (`id_rsa.pub`) -and convert it into a public JWK. +A minimal library to read an SSH public key (`id_rsa.pub`) +and generate its fingerprint. Works for RSA and ECDSA public keys. @@ -10,72 +10,53 @@ Features < 100 lines of code | <1kb gzipped | 1.8kb minified | 3.1kb with comments -* [x] SSH Public Keys - * fingerprint -* [x] RSA Public Keys -* [x] EC Public Keys - * P-256 (prime256v1, secp256r1) - * P-384 (secp384r1) +* [x] SSH Public Key SHA256 Fingerprints + * RSA (2048-bit, 3072-bit, 4096-bit) + * EC / ECDSA + * P-256 (prime256v1, secp256r1) + * P-384 (secp384r1) * [x] Browser Version - * [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. + * [Bluecrypt SSH Fingerprint](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js) # CLI -You can install `ssh-to-jwk` and use it from command line: +You can install `ssh-fingerprint` and use it from command line: ```bash -npm install -g ssh-to-jwk +npm install -g ssh-fingerprint ``` ```bash -ssh-to-jwk ~/.ssh/id_rsa.pub +ssh-fingerprint ~/.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 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** ```js var fs = require('fs'); -var sshtojwk = require('ssh-to-jwk'); +var SSH = require('ssh-fingerprint'); var pub = fs.readFileSync("./id_rsa.pub"); -sshtojwk.fingerprint({ pub: pub }).then(function (fingerprint) { - console.info(fingerprint); - // SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko +SSH.fingerprint({ pub: pub }).then(function (fing) { + console.info('The key fingerprint is:'); + console.info(fing.fingerprint, fing.comment); + // SHA256:yCB62vBVsOwqksgYwy/WDbaMF2PhPijAwcrlzmrxfko root@localhost }); ``` # Legal -[ssh-to-jwk.js](https://git.coolaj86.com/coolaj86/ssh-to-jwk.js) | +[ssh-fingerprint.js](https://git.coolaj86.com/coolaj86/ssh-fingerprint.js) | MPL-2.0 | [Terms of Use](https://therootcompany.com/legal/#terms) | [Privacy Policy](https://therootcompany.com/legal/#privacy) diff --git a/bin/ssh-to-jwk.js b/bin/ssh-fingerprint.js similarity index 60% rename from bin/ssh-to-jwk.js rename to bin/ssh-fingerprint.js index be056c2..a818264 100755 --- a/bin/ssh-to-jwk.js +++ b/bin/ssh-fingerprint.js @@ -13,10 +13,9 @@ if (!pubfile) { var buf = fs.readFileSync(pubfile); var pub = buf.toString('ascii'); -var ssh = sshtojwk.parse({ pub: pub }); // 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)); +sshtojwk.fingerprint({ pub: pub }).then(function (fing) { + var kty = ('RSA' === fing.kty ? 'RSA' : 'ECDSA'); + console.info((fing.size*8).toString(), fing.fingerprint, fing.comment, '(' + kty + ')'); }); diff --git a/fixtures/pub-ec-p256.ssh.pub b/fixtures/pub-ec-p256.ssh.pub new file mode 100644 index 0000000..1d87fdd --- /dev/null +++ b/fixtures/pub-ec-p256.ssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= P-256@localhost diff --git a/fixtures/pub-ec-p384.ssh.pub b/fixtures/pub-ec-p384.ssh.pub new file mode 100644 index 0000000..b07ea6b --- /dev/null +++ b/fixtures/pub-ec-p384.ssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNsxFNGygmu3oyiyCfKDxpy4aoccor+P8N/CtmtEjwunbEnff4JTSfJXKr9LH3+Rm1Q+I57vN0urHJ+v03gI9xGTkRBmzrOnc6FaWroJ/l0DDMgvTuFS2wwxRgWUyZTLGw== P-384@localhost diff --git a/fixtures/pub-rsa-2048.ssh.pub b/fixtures/pub-rsa-2048.ssh.pub new file mode 100644 index 0000000..a00fd4c --- /dev/null +++ b/fixtures/pub-rsa-2048.ssh.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost diff --git a/index.js b/index.js index 9cb5e9a..e9b7cc6 100644 --- a/index.js +++ b/index.js @@ -1 +1 @@ -module.exports = require('./lib/ssh-parser.js'); +module.exports = require('./lib/ssh-fingerprint.js'); diff --git a/lib/ssh-parser.js b/lib/ssh-fingerprint.js similarity index 70% rename from lib/ssh-parser.js rename to lib/ssh-fingerprint.js index 7ac0fff..02bdef7 100644 --- a/lib/ssh-parser.js +++ b/lib/ssh-fingerprint.js @@ -3,14 +3,6 @@ var SSH = module.exports; 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*/ SSH.fingerprint = function (opts) { var ssh; @@ -19,10 +11,23 @@ SSH.fingerprint = function (opts) { } else { ssh = SSH.parseBlock(opts.pub); } + ssh = SSH.parseElements(ssh); + // for browser compat return Promise.resolve().then(function () { - return 'SHA256:' + require('crypto').createHash('sha256') - .update(ssh.bytes).digest('base64').replace(/=+$/g, ''); + var digest = require('crypto').createHash('sha256') + .update(ssh.bytes).digest(); + // 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 + }; }); }; @@ -71,44 +76,29 @@ SSH.parseElements = function (ssh) { return ssh; }; -SSH.parsePublicKey = function (ssh) { +SSH.parseKeyType = 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]) - }; + ssh.kty = 'RSA'; + ssh.size = (ssh.elements[2].byteLength || ssh.lements[2].length); return ssh; } // EC keys are each different if (SSH.types.p256 === typ) { - len = 32; - ssh.jwk = { kty: 'EC', crv: 'P-256' }; + ssh.kty = 'EC'; + ssh.size = 32; } else if (SSH.types.p384 === typ) { - len = 48; - ssh.jwk = { kty: 'EC', crv: 'P-384' }; + ssh.kty = 'EC'; + ssh.size = 48; } 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; }; diff --git a/package.json b/package.json index dd67052..acac9d2 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { - "name": "ssh-to-jwk", - "version": "1.1.0", - "description": "💯 SSH to JWK in a lightweight, zero-dependency library.", - "homepage": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js", + "name": "ssh-fingerprint", + "version": "1.0.0", + "description": "💯 SSH Fingerprint in a lightweight, zero-dependency VanillaJS node library.", + "homepage": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js", "main": "index.js", "bin": { - "ssh-to-jwk": "bin/ssh-to-jwk.js" + "ssh-fingerprint": "bin/ssh-fingerprint.js" }, "files": [ "bin", @@ -21,17 +21,14 @@ }, "repository": { "type": "git", - "url": "https://git.coolaj86.com/coolaj86/ssh-to-jwk.js" + "url": "https://git.coolaj86.com/coolaj86/ssh-fingerprint.js" }, "keywords": [ - "zero-dependency", - "SSH-to-JWK", - "RSA", - "EC", "SSH", - "fingerprint", - "JWK", - "ECDSA" + "Fingerprint", + "rsa", + "ec", + "ecdsa" ], "author": "AJ ONeal (https://coolaj86.com/)", "license": "MPL-2.0" diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..b91b990 --- /dev/null +++ b/test.sh @@ -0,0 +1,23 @@ +#!/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"