From 1e730abff9ddf65f1e8d28684088ccc5a25fcb57 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sun, 2 Dec 2018 01:59:33 -0700 Subject: [PATCH] v1.0.0: pack EC and RSA ssh public keys --- README.md | 79 +++++++++++++++++++++++++++++++++ index.html | 64 +++++++++++++++++++++++++++ jwk-to-ssh.js | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 28 ++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 README.md create mode 100644 index.html create mode 100644 jwk-to-ssh.js create mode 100644 package.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..5bb41ef --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Bluecrypt JWK to SSH (for Browsers) + +A minimal library to parse an SSH public key (`id_rsa.pub`) +and convert it into a public JWK using Vanilla JS. + +Works for RSA and ECDSA public keys. + +# Features + +< 100 lines of code | < 1.0kb gzipped | 2.0kb minified | 2.9kb with comments + +* [x] SSH Public Keys +* [x] RSA Public Keys +* [x] EC Public Keys + * P-256 (prime256v1, secp256r1) + * P-384 (secp384r1) +* [x] node.js version + * [jwk-to-ssh.js](https://git.coolaj86.com/coolaj86/jwk-to-ssh.js) +* [x] on npm as [bluecrypt-jwk-to-ssh](https://www.npmjs.com/package/bluecrypt-jwk-to-ssh) + +### Need SSH Private Keys? + +SSH private keys (`id_rsa`) are just normal PEM files. + +# Web Demo + + + + + +```bash +git clone https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js +pushd bluecrypt-jwk-to-ssh.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-jwk-to-ssh +``` + +# Usage + +Very simple: + +```js +var pub = 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= root@localhost'; + +var ssh = SSH.parse(pub); + +console.info(ssh.jwk); +``` + +# Other Tools in the Bluecrypt Suite + +* [Bluecrypt JWK to SSH](https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js) (RSA, EC, SSH) +* [Bluecrypt ASN.1 decoder](https://git.coolaj86.com/coolaj86/asn1-parser.js) (x509, RSA, EC, etc) +* [Bluecrypt ASN.1 builder](https://git.coolaj86.com/coolaj86/asn1-packer.js) (x509, RSA, EC, etc) + +# Legal + +[jwk-to-ssh.js](https://git.coolaj86.com/coolaj86/jwk-to-ssh.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..fe4df6a --- /dev/null +++ b/index.html @@ -0,0 +1,64 @@ + + + + SSH Pub Generator - Bluecrypt + + + +

Bluecrypt SSH Public Key Generator

+ + +
+ + + + +
 
+ +
+

Made with jwk-to-ssh.js

+ + + + + diff --git a/jwk-to-ssh.js b/jwk-to-ssh.js new file mode 100644 index 0000000..ddb912b --- /dev/null +++ b/jwk-to-ssh.js @@ -0,0 +1,120 @@ +/* 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.pack = function (opts) { + var jwk = opts.jwk; + var els = []; + var ssh = { + type: '' + , _elements: els + , comment: opts.comment || '' + }; + var len; + + if ("RSA" === jwk.kty) { + ssh.type = 'ssh-rsa'; + els.push(Enc.binToHex(ssh.type)); + els.push(SSH._padRsa(Enc.base64ToHex(jwk.e))); + els.push(SSH._padRsa(Enc.base64ToHex(jwk.n))); + return SSH._packElements(ssh); + } + + if ("P-256" === jwk.crv) { + ssh.type = 'ecdsa-sha2-nistp256'; + els.push(Enc.binToHex(ssh.type)); + els.push(Enc.binToHex('nistp256')); + len = 32; + } else if ("P-384" === jwk.crv) { + ssh.type = 'ecdsa-sha2-nistp384'; + els.push(Enc.binToHex(ssh.type)); + els.push(Enc.binToHex('nistp384')); + len = 48; + } else { + throw new Error("unknown key type " + (jwk.crv || jwk.kty)); + } + + els.push('04' + + SSH._padEc(Enc.base64ToHex(jwk.x), len) + + SSH._padEc(Enc.base64ToHex(jwk.y), len) + ); + return SSH._packElements(ssh); +}; + +SSH._packElements = function (ssh) { + var hex = ssh._elements.map(function (hex) { + console.log(hex); + return SSH._numToUint32Hex(hex.length/2) + hex; + }).join(''); + return [ ssh.type, Enc.hexToBase64(hex), ssh.comment ].join(' '); +}; + +SSH._numToUint32Hex = function (num) { + var hex = num.toString(16); + while (hex.length < 8) { + hex = '0' + hex; + } + console.log('length', hex); + return hex; +}; + +SSH._padRsa = function (hex) { + // BigInt is negative if the high order bit 0x80 is set, + // so ASN1, SSH, and many other formats pad with '0x00' + // to signifiy a positive number. + var i = parseInt(hex.slice(0, 2), 16); + if (0x80 & i) { + return '00' + hex; + } + return hex; +}; + +SSH._padEc = function (hex, len) { + while (hex.length < len * 2) { + hex = '00' + hex; + } + return hex; +}; + +Enc.base64ToHex = function (b64) { + var bin = atob(Enc.urlBase64ToBase64(b64)); + return Enc.binToHex(bin); +}; + +Enc.binToHex = function (bin) { + return bin.split('').map(function (ch) { + var h = ch.charCodeAt(0).toString(16); + if (h.length % 2) { h = '0' + h; } + return h; + }).join(''); +}; + +Enc.hexToBase64 = function (hex) { + return btoa(Enc.hexToBin(hex)); +}; + +Enc.hexToBin = function (hex) { + return hex.match(/.{2}/g).map(function (h) { + return String.fromCharCode(parseInt(h, 16)); + }).join(''); +}; + +Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) { + var r = str % 4; + if (2 === r) { + str += '=='; + } else if (3 === r) { + str += '='; + } + return str.replace(/-/g, '+').replace(/_/g, '/'); +}; + +}('undefined' !== typeof window ? window : module.exports)); diff --git a/package.json b/package.json new file mode 100644 index 0000000..c4493ad --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "bluecrypt-jwk-to-ssh", + "version": "1.0.0", + "description": "JWK to SSH in < 100 lines of VanillaJS.", + "homepage": "https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js", + "main": "jwk-to-ssh.js", + "scripts": { + "prepare": "uglifyjs jwk-to-ssh.js > jwk-to-ssh.min.js" + }, + "directories": { + "lib": "lib" + }, + "repository": { + "type": "git", + "url": "https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js" + }, + "keywords": [ + "zero-dependency", + "JWK-to-SSH", + "RSA", + "EC", + "SSH", + "JWK", + "ECDSA" + ], + "author": "AJ ONeal (https://coolaj86.com/)", + "license": "MPL-2.0" +}