commit 08e9349ee7a5886fc02740524088f6b3174ba090 Author: AJ ONeal Date: Mon Dec 3 00:12:32 2018 -0700 v1.0.0: generate ssh-keygen compatible fingerprints diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad7f5fe --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# Bluecrypt SSH Fingerprint (for Browsers) + +A minimal library to read an SSH public key (`id_rsa.pub`) +and generate its fingerprint. + +Works for RSA and ECDSA public keys. + +# Features + +< 150 lines of code | 1.1kb gzipped | 2.4kb minified | 4.1kb with comments + +* [x] SSH Public Key SHA256 Fingerprints + * RSA (2048-bit, 3072-bit, 4096-bit) + * EC / ECDSA + * P-256 (prime256v1, secp256r1) + * P-384 (secp384r1) +* [x] Node.js version + * [Greenlock SSH Fingerprint](https://git.coolaj86.com/coolaj86/greenlock-ssh-fingerprint.js) + +### Need SSH Private Keys? + +SSH private keys (`id_rsa`) are just normal PEM files, +so you can use Eckles or Rasha, as mentioned above. + +# Web Demo + + + + + +```bash +git clone https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js +pushd bluecrypt-ssh-fingerprint.js/ +``` + +```bash +open index.html +``` + +# Install + +You can use it as a plain-old javascript library: + +```html + +``` + +It's also on npm: + +```bash +npm install bluecrypt-ssh-fingerprint +``` + +# Usage + +Very simple: + +```js +var pub = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCba21UHE+VbDTpmYYFZUOV+OQ8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHFuRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqOyShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcHLkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3 rsa@localhost'; + +SSH.fingerprint({ pub: pub }).then(function (fing) { + console.info('The key fingerprint is:'); + console.info(fing.fingerprint); +}); +``` + +# Other Tools in the Bluecrypt Suite + +* [Browser JWK to SSH](https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js) +* [Browser SSH to JWK](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-to-jwk.js) +* [Browser ASN.1 parser](https://git.coolaj86.com/coolaj86/asn1-parser.js) (for x509, etc) +* [Browser ASN.1 packer](https://git.coolaj86.com/coolaj86/asn1-packer.js) (for x509, etc) + +# Legal + +[ssh-fingerprint.js](https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js) | +MPL-2.0 | +[Terms of Use](https://therootcompany.com/legal/#terms) | +[Privacy Policy](https://therootcompany.com/legal/#privacy) + +Bluecrypt™ is owned by AJ ONeal diff --git a/index.html b/index.html new file mode 100644 index 0000000..796f914 --- /dev/null +++ b/index.html @@ -0,0 +1,66 @@ + + + + SSH Fingerprint Generator - Bluecrypt + + + +

Bluecrypt SSH Fingerprint Generator

+ + + +
 
+ +
+
 
+
+ +
+

Made with ssh-fingerprint.js

+ + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..7637091 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "bluecrypt-ssh-fingerprint", + "version": "1.0.0", + "description": "SSH Fingerprint in < 150 lines of VanillaJS, part of the Bluecrypt suite for Browser Crypto.", + "homepage": "https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js", + "main": "ssh-fingerprint.js", + "scripts": { + "prepare": "uglifyjs ssh-fingerprint.js > ssh-fingerprint.min.js" + }, + "directories": { + "lib": "lib" + }, + "repository": { + "type": "git", + "url": "https://git.coolaj86.com/coolaj86/bluecrypt-ssh-fingerprint.js" + }, + "keywords": [ + "zero-dependency", + "SSH", + "Fingerprint", + "rsa", + "ec", + "ecdsa" + ], + "author": "AJ ONeal (https://coolaj86.com/)", + "license": "MPL-2.0" +} diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..e6e7962 Binary files /dev/null and b/screenshot.png differ diff --git a/ssh-fingerprint.js b/ssh-fingerprint.js new file mode 100644 index 0000000..638b554 --- /dev/null +++ b/ssh-fingerprint.js @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +;(function (exports) { +'use strict'; + +if (!exports.Enc) { exports.Enc = {}; } +if (!exports.SSH) { exports.SSH = {}; } + +var Enc = exports.Enc; +var SSH = exports.SSH; + +SSH.fingerprint = function (opts) { + var ssh; + if (opts.bytes) { + ssh = opts; + } else { + ssh = SSH.parseBlock(opts.pub); + } + ssh = SSH.parseElements(ssh); + + // for browser compat + return window.crypto.subtle.digest('SHA-256', ssh.bytes).then(function (digest) { + digest = new Uint8Array(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:' + Enc.bufToBase64(digest).replace(/=+$/g, '') + , size: ssh.size + , comment: ssh.comment + , kty: ssh.kty + }; + }); +}; + +SSH.parseBlock = function (ssh) { + ssh = ssh.split(/\s+/g); + + return { + type: ssh[0] + , bytes: Enc.base64ToBuf(ssh[1]) + , comment: ssh[2] + }; +}; + +SSH.parseElements = function (ssh) { + var buf = ssh.bytes; + var fulllen = buf.byteLength || buf.length; + var offset = (buf.byteOffset || 0); + var i = 0; + var index = 0; + // using dataview to be browser-compatible (I do want _some_ code reuse) + var dv = new DataView(buf.buffer.slice(offset, offset + fulllen)); + var els = []; + var el; + var len; + + while (index < fulllen) { + i += 1; + if (i > 15) { throw new Error("15+ elements, probably not a public ssh key"); } + len = dv.getUint32(index, false); + index += 4; + el = buf.slice(index, index + len); + // remove BigUInt '00' prefix + if (0x00 === el[0]) { + el = el.slice(1); + } + els.push(el); + index += len; + } + if (fulllen !== index) { + throw new Error("invalid ssh public key length \n" + els.map(function (b) { + return Enc.bufToHex(b); + }).join('\n')); + } + + ssh.elements = els; + return ssh; +}; + +SSH.parseKeyType = function (ssh) { + var els = ssh.elements; + var typ = Enc.bufToBin(els[0]); + + // RSA keys are all the same + if (SSH.types.rsa === typ) { + 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) { + ssh.kty = 'EC'; + ssh.size = 32; + } else if (SSH.types.p384 === typ) { + ssh.kty = 'EC'; + ssh.size = 48; + } else { + throw new Error("Unsupported ssh public key type: " + + Enc.bufToBin(els[0])); + } + + return ssh; +}; + +SSH.types = { + // 19 '00000013' + // e c d s a - s h a 2 - n i s t p 2 5 6 + // 65636473612d736861322d6e69737470323536 + // 6e69737470323536 + p256: 'ecdsa-sha2-nistp256' + + // 19 '00000013' + // e c d s a - s h a 2 - n i s t p 3 8 4 + // 65636473612d736861322d6e69737470333834 + // 6e69737470323536 +, p384: 'ecdsa-sha2-nistp384' + + // 7 '00000007' + // s s h - r s a + // 7373682d727361 +, rsa: 'ssh-rsa' +}; + +Enc.base64ToBuf = function (b64) { + return Enc.binToBuf(atob(b64)); +}; + +Enc.binToBuf = function (bin) { + var arr = bin.split('').map(function (ch) { + return ch.charCodeAt(0); + }); + return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr; +}; + +Enc.bufToBase64 = function (u8) { + var bin = ''; + u8.forEach(function (i) { + bin += String.fromCharCode(i); + }); + return btoa(bin); +}; + +Enc.bufToBin = function (buf) { + var bin = ''; + // cannot use .map() because Uint8Array would return only 0s + buf.forEach(function (ch) { + bin += String.fromCharCode(ch); + }); + return bin; +}; + +}('undefined' !== typeof window ? window : module.exports));