diff --git a/README.md b/README.md index 2840fb7..9d4d580 100644 --- a/README.md +++ b/README.md @@ -11,13 +11,17 @@ Features < 75 lines of code | < 0.7kb gzipped | 1.5kb minified | 2.1kb with comments * [x] SSH Public Keys -* [x] RSA Public Keys -* [x] EC Public Keys +* [x] OpenSSH Private Keys +* [x] RSA Keys +* [x] EC Keys * P-256 (prime256v1, secp256r1) * P-384 (secp384r1) * [x] Browser Version * [Bluecrypt JWK to SSH](https://git.coolaj86.com/coolaj86/bluecrypt-jwk-to-ssh.js) +Note: the file size stats are for v1.0 which did not include private key packing. +I plan to go back and update the stats, but just know that it grew a little over 2x. + ### Need JWK to SSH? PEM to SSH? Try one of these: @@ -26,12 +30,35 @@ Try one of these: * [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? +### Need Alternate SSH Private Keys? -SSH private keys are just normal PEM files, -so you can use Eckles or Rasha, as mentioned above. +This library supports OpenSSH private keys. -# CLI + * [x] OpenSSH + * [ ] Normal PKCS1 / SEC1 / PKCS8 + * [x] Rasha.js + * [x] Eckles.js + * [ ] Putty + +# Library Usage + +You can also use it from JavaScript: + +```js +var fs = require('fs'); +var jwktossh = require('jwk-to-ssh'); + +var jwk = JSON.parse(fs.readFileSync("./privkey.jwk.json")); +var pub = jwktossh.pack({ + jwk: jwk +, comment: 'root@localhost' +, public: true +}); + +console.info(pub); +``` + +# CLI Usage You can install `jwk-to-ssh` and use it from command line: @@ -39,25 +66,20 @@ You can install `jwk-to-ssh` and use it from command line: npm install -g jwk-to-ssh ``` +```bash +jwk-to-ssh [keyfile] [comment] [public] +``` + ```bash jwk-to-ssh pubkey.jwk.json ``` -# Usage +```bash +jwk-to-ssh privkey.jwk.json root@localhost +``` -You can also use it from JavaScript: - -```js -var fs = require('fs'); -var jwktossh = require('jwk-to-ssh'); - -var jwk = JSON.parse(fs.readFileSync("./pubkey.jwk.json")); -var pub = jwktossh.pack({ - jwk: jwk -, comment: 'root@localhost' -}); - -console.info(pub); +```bash +jwk-to-ssh privkey.jwk.json root@localhost public ``` Legal diff --git a/bin/jwk-to-ssh.js b/bin/jwk-to-ssh.js index 9493044..c4ea6fa 100755 --- a/bin/jwk-to-ssh.js +++ b/bin/jwk-to-ssh.js @@ -5,6 +5,8 @@ var path = require('path'); var jwktossh = require('../index.js'); var pubfile = process.argv[2]; +var comment = process.argv[3] || 'root@localhost'; +var pub = ('public' === process.argv[4]); if (!pubfile) { console.error("specify a path to JWK"); @@ -12,7 +14,6 @@ if (!pubfile) { } var jwk = require(path.join(process.cwd(), pubfile)); -var comment = process.argv[3] || 'root@localhost'; -var pub = jwktossh.pack({ jwk: jwk, comment: comment }); +var out = jwktossh.pack({ jwk: jwk, comment: comment, public: pub }); -console.info(pub); +console.info(out); diff --git a/fixtures/privkey-ec-p256.jwk.json b/fixtures/privkey-ec-p256.jwk.json new file mode 100644 index 0000000..2e9455d --- /dev/null +++ b/fixtures/privkey-ec-p256.jwk.json @@ -0,0 +1,7 @@ +{ + "kty": "EC", + "crv": "P-256", + "d": "iYydo27aNGO9DBUWeGEPD8oNi1LZDqfxPmQlieLBjVQ", + "x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4", + "y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo" +} diff --git a/fixtures/privkey-ec-p256.openssh.pem b/fixtures/privkey-ec-p256.openssh.pem new file mode 100644 index 0000000..25720bf --- /dev/null +++ b/fixtures/privkey-ec-p256.openssh.pem @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQhPVJYvGxpw+ITlnXqOSikCfz/7zms +yODIKiSueMN+3pj9icDgDnTJl7sKcWyp4Nymc9u5s/pyliJVyd680hjKAAAAqGJjanNiY2 +pzAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOW +deo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGM +oAAAAhAImMnaNu2jRjvQwVFnhhDw/KDYtS2Q6n8T5kJYniwY1UAAAADnJvb3RAbG9jYWxo +b3N0AQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/fixtures/privkey-ec-p384.jwk.json b/fixtures/privkey-ec-p384.jwk.json new file mode 100644 index 0000000..7bb65f8 --- /dev/null +++ b/fixtures/privkey-ec-p384.jwk.json @@ -0,0 +1,7 @@ +{ + "kty": "EC", + "crv": "P-384", + "d": "XlyuCEWSTTS8U79O_Mz05z18vh4kb10szvu_7pdXuGWV6lfEyPExyUYWsA6A2kdV", + "x": "2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4_w38K2a0SPC6dsSd9_glNJ8lcqv0sff5Gb", + "y": "VD4jnu83S6scn6_TeAj3EZOREGbOs6dzoVpaugn-XQMMyC9O4VLbDDFGBZTJlMsb" +} diff --git a/fixtures/privkey-ec-p384.openssh.pem b/fixtures/privkey-ec-p384.openssh.pem new file mode 100644 index 0000000..5e7ad93 --- /dev/null +++ b/fixtures/privkey-ec-p384.openssh.pem @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTbMRTRsoJrt6Mosgnyg8acuGqHHKK/ +j/DfwrZrRI8Lp2xJ33+CU0nyVyq/Sx9/kZtUPiOe7zdLqxyfr9N4CPcRk5EQZs6zp3OhWl +q6Cf5dAwzIL07hUtsMMUYFlMmUyxsAAADYYmNqc2JjanMAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEE2zEU0bKCa7ejKLIJ8oPGnLhqhxyiv4/w38K2a0SPC6 +dsSd9/glNJ8lcqv0sff5GbVD4jnu83S6scn6/TeAj3EZOREGbOs6dzoVpaugn+XQMMyC9O +4VLbDDFGBZTJlMsbAAAAMF5crghFkk00vFO/TvzM9Oc9fL4eJG9dLM77v+6XV7hllepXxM +jxMclGFrAOgNpHVQAAAA5yb290QGxvY2FsaG9zdAEC +-----END OPENSSH PRIVATE KEY----- diff --git a/fixtures/privkey-rsa-2048.jwk.json b/fixtures/privkey-rsa-2048.jwk.json new file mode 100644 index 0000000..130d540 --- /dev/null +++ b/fixtures/privkey-rsa-2048.jwk.json @@ -0,0 +1,9 @@ +{ + "kty": "RSA", + "n": "m2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJefLukC-xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6-I70on0_iDZm7-jcqOPgADAmbWHhy67BXkk4yy_YzD4yOGZFXZcNp915_TW5bRd__AKPHUHxJasPiyEFqlNKBR2DSD-LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9RGps_50-CNw", + "e": "AQAB", + "d": "Cpfo7Mm9Nu8YMC_xrZ54W9mKHPkCG9rZ93Ds9PNp-RXUgb-ljTbFPZWsYxGNKLllFz8LNosr1pT2ZDMrwNk0Af1iWNvD6gkyXaiQdCyiDPSBsJyNv2LJZon-e85X74nv53UlIkmo9SYxdLz2JaJ-iIWEe8Qh-7llLktrTJV_xr98_tbhgSppz_IeOymq3SEZaQHM8pTU7w7XvCj2pb9r8fN0M0XcgWZIaf3LGEfkhF_WtX67XJ0C6-LbkT51jtlLRNGX6haGdscXS0OWWjKOJzKGuV-NbthEn5rmRtVnjRZ3yaxQ0ud8vC-NONn7yvGUlOur1IdDzJ_YfHPt9sHMQQ", + "p": "ynG-t9HwKCN3MWRYFdnFzi9-02Qcy3p8B5pu3ary2E70hYn2pHlUG2a9BNE8c5xHQ3Hx43WoWf6s0zOunPV1G28LkU_UYEbAtPv_PxSmzpQp9n9XnYvBLBF8Y3z7gxgLn1vVFNARrQdRtj87qY3aw7E9S4DsGcAarIuOT2TsTCE", + "q": "xIkAjgUzB1zaUzJtW2Zgvp9cYYr1DmpH30ePZl3c_8397_DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJBEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSrBFEVnxjpnPh1Q1c", + "qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU" +} diff --git a/fixtures/privkey-rsa-2048.openssh.pem b/fixtures/privkey-rsa-2048.openssh.pem new file mode 100644 index 0000000..ca14bc9 --- /dev/null +++ b/fixtures/privkey-rsa-2048.openssh.pem @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAm2ttVBxPlWw06ZmGBWVDlfjkPAJ4DgnY0TrDwtCohHzLxGhDNzUJ +efLukC+xu0LBKylYojT5vTkxaOhxeSYo31syu4WhxbkTBLICOFcCGMob6pSQ38P8LdAIlb +0pqDHxEJ9adWomjuFf0SUhN1cP7s9m8Yk9trkpEqjskocn2BOnTB57qAZM6+I70on0/iDZ +m7+jcqOPgADAmbWHhy67BXkk4yy/YzD4yOGZFXZcNp915/TW5bRd//AKPHUHxJasPiyEFq +lNKBR2DSD+LbX5eTmzCh2ikrwTMja7mUdBJf2bK3By5AB0Qi49OykUCfNZeQlEz7UNNj9R +Gps/50+CNwAAA8hiY2pzYmNqcwAAAAdzc2gtcnNhAAABAQCba21UHE+VbDTpmYYFZUOV+O +Q8AngOCdjROsPC0KiEfMvEaEM3NQl58u6QL7G7QsErKViiNPm9OTFo6HF5JijfWzK7haHF +uRMEsgI4VwIYyhvqlJDfw/wt0AiVvSmoMfEQn1p1aiaO4V/RJSE3Vw/uz2bxiT22uSkSqO +yShyfYE6dMHnuoBkzr4jvSifT+INmbv6Nyo4+AAMCZtYeHLrsFeSTjLL9jMPjI4ZkVdlw2 +n3Xn9NbltF3/8Ao8dQfElqw+LIQWqU0oFHYNIP4ttfl5ObMKHaKSvBMyNruZR0El/ZsrcH +LkAHRCLj07KRQJ81l5CUTPtQ02P1Eamz/nT4I3AAAAAwEAAQAAAQAKl+jsyb027xgwL/Gt +nnhb2Yoc+QIb2tn3cOz082n5FdSBv6WNNsU9laxjEY0ouWUXPws2iyvWlPZkMyvA2TQB/W +JY28PqCTJdqJB0LKIM9IGwnI2/Yslmif57zlfvie/ndSUiSaj1JjF0vPYlon6IhYR7xCH7 +uWUuS2tMlX/Gv3z+1uGBKmnP8h47KardIRlpAczylNTvDte8KPalv2vx83QzRdyBZkhp/c +sYR+SEX9a1frtcnQLr4tuRPnWO2UtE0ZfqFoZ2xxdLQ5ZaMo4nMoa5X41u2ESfmuZG1WeN +FnfJrFDS53y8L4042fvK8ZSU66vUh0PMn9h8c+32wcxBAAAAgAJR1m8dYKemfaW8P9YZUu +x7lwIFqF+yI201HpZXX+IJK4g1izq490AmXDuGb2bxMCYVZbJ8vSzvCJG8BiRC+nF62itP +AclUtpsEMn5YE4BjZ6c4schFC/5q0C8esteORR3MX3qjSJzIXqftDMZ9Q+Z5YZiDoEQKUt +LfVaF7R4RVAAAAgQDKcb630fAoI3cxZFgV2cXOL37TZBzLenwHmm7dqvLYTvSFifakeVQb +Zr0E0TxznEdDcfHjdahZ/qzTM66c9XUbbwuRT9RgRsC0+/8/FKbOlCn2f1edi8EsEXxjfP +uDGAufW9UU0BGtB1G2PzupjdrDsT1LgOwZwBqsi45PZOxMIQAAAIEAxIkAjgUzB1zaUzJt +W2Zgvp9cYYr1DmpH30ePZl3c/8397/DZDDo46fnFYjs6uPa03HpmKUnbjwr14QHlfXlntJ +BEuXxcqLjkdKdJ4ob7xueLTK4suo9V8LSrkLChVxlZQwnFD2E5ll0sVeeDeMJHQw38ahSr +BFEVnxjpnPh1Q1cAAAAOcm9vdEBsb2NhbGhvc3QBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/fixtures/pub-ec-p256.ssh.pub b/fixtures/pub-ec-p256.ssh.pub new file mode 100644 index 0000000..5b6b77b --- /dev/null +++ b/fixtures/pub-ec-p256.ssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCE9Uli8bGnD4hOWdeo5KKQJ/P/vOazI4MgqJK54w37emP2JwOAOdMmXuwpxbKng3KZz27mz+nKWIlXJ3rzSGMo= root@localhost diff --git a/fixtures/pub-ec-p384.ssh.pub b/fixtures/pub-ec-p384.ssh.pub new file mode 100644 index 0000000..552074d --- /dev/null +++ b/fixtures/pub-ec-p384.ssh.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNsxFNGygmu3oyiyCfKDxpy4aoccor+P8N/CtmtEjwunbEnff4JTSfJXKr9LH3+Rm1Q+I57vN0urHJ+v03gI9xGTkRBmzrOnc6FaWroJ/l0DDMgvTuFS2wwxRgWUyZTLGw== root@localhost diff --git a/fixtures/pub-rsa-2048.ssh.pub b/fixtures/pub-rsa-2048.ssh.pub new file mode 100644 index 0000000..97429c2 --- /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 root@localhost diff --git a/lib/encoding.js b/lib/encoding.js index a369e6c..8bc0d0d 100644 --- a/lib/encoding.js +++ b/lib/encoding.js @@ -6,6 +6,14 @@ Enc.base64ToHex = function (b64) { return Buffer.from(b64, 'base64').toString('hex'); }; +Enc.binToBuf = function (bin) { + return Buffer.from(bin, 'binary'); +}; + +Enc.bufToBase64 = function (u8) { + return Buffer.from(u8).toString('base64'); +}; + Enc.binToHex = function (bin) { return Buffer.from(bin, 'binary').toString('hex'); }; @@ -13,3 +21,7 @@ Enc.binToHex = function (bin) { Enc.hexToBase64 = function (hex) { return Buffer.from(hex, 'hex').toString('base64'); }; + +Enc.hexToBin = function (hex) { + return Buffer.from(hex, 'hex').toString('binary'); +}; diff --git a/lib/pem.js b/lib/pem.js new file mode 100644 index 0000000..468362b --- /dev/null +++ b/lib/pem.js @@ -0,0 +1,11 @@ +'use strict'; + +var PEM = module.exports; +var Enc = require('./encoding.js'); + +PEM.packBlock = function (opts) { + return '-----BEGIN ' + opts.type + '-----\n' + + Enc.bufToBase64(opts.bytes).match(/.{1,70}/g).join('\n') + '\n' + + '-----END ' + opts.type + '-----' + ; +}; diff --git a/lib/ssh-packer.js b/lib/ssh-packer.js index d6470b0..5c4d7c3 100644 --- a/lib/ssh-packer.js +++ b/lib/ssh-packer.js @@ -1,34 +1,92 @@ 'use strict'; var Enc = require('./encoding.js'); +var PEM = require('./pem.js'); var SSH = module.exports; SSH.pack = function (opts) { + if (opts.jwk.d && !opts.public) { + return SSH._packPrivate(opts); + } else { + delete opts.jwk.d; + return SSH._packPublic(opts); + } +}; + +// https://tools.ietf.org/html/rfc4253#section-6.6 +SSH._packPublic = function (opts) { + var els = SSH._packKey(opts); + var hex = SSH._packElements(els); + var typ = Enc.hexToBin(els[0]); + var parts = [ typ, Enc.hexToBase64(hex) ]; + if (opts.comment) { parts.push(opts.comment); } + return parts.join(' '); +}; + +SSH._packPrivate = function (opts) { + var pubjwk = JSON.parse(JSON.stringify(opts.jwk)); + delete pubjwk.d; + var pubels = SSH._packKey({ jwk: pubjwk }); + var pubhex = SSH._packElements(pubels); + var els = SSH._packKey(opts); + var hex = SSH._packElements(els); + var privlen = hex.length/2; + var padlen = (privlen % 8) && (8 - (privlen % 8)) || 0; // blocksize is 8 (no cipher) + var bin = "openssh-key-v1" + String.fromCharCode(0) + + Enc.hexToBin( + SSH._packElements([ + Enc.binToHex("none") // ciphername + , Enc.binToHex("none") // kdfname + , "" // empty kdf + ]) + + SSH._numToUint32Hex(1) // number of keys (always 1) + + SSH._numToUint32Hex(pubhex.length/2) // pubkey length + + pubhex + + SSH._numToUint32Hex(8 + privlen + padlen) // privkey length + + '62636a7362636a73' // 64-bit dummy checksum ("bcjs", "bcjs") + + hex // (only cihpered keys use real checksums) + ); + var pad = ''; + var i; + for (i = 1; i <= padlen; i += 1) { + pad += '0' + i; + } + return PEM.packBlock({ + type: "OPENSSH PRIVATE KEY" + , bytes: Enc.binToBuf(bin + Enc.hexToBin(pad)) + }); +}; + +SSH._packKey = 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); + els.push(Enc.binToHex('ssh-rsa')); + if (jwk.d) { + // unswap n and e for private key format + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.n))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.e))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.d))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.qi))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.p))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.q))); + els.push(Enc.binToHex(opts.comment || '')); + } else { + // swap n and e for public key format + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.e))); + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.n))); + } + return els; } if ("P-256" === jwk.crv) { - ssh.type = 'ecdsa-sha2-nistp256'; - els.push(Enc.binToHex(ssh.type)); + els.push(Enc.binToHex('ecdsa-sha2-nistp256')); 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('ecdsa-sha2-nistp384')); els.push(Enc.binToHex('nistp384')); len = 48; } else { @@ -36,17 +94,26 @@ SSH.pack = function (opts) { } els.push('04' - + SSH._padEc(Enc.base64ToHex(jwk.x), len) - + SSH._padEc(Enc.base64ToHex(jwk.y), len) + + SSH._padBytes(Enc.base64ToHex(jwk.x), len) + + SSH._padBytes(Enc.base64ToHex(jwk.y), len) ); - return SSH._packElements(ssh); + if (jwk.d) { + // I was able to empirically confirm that the leading 00 is expected for + // ambiguous BigInt negatives (0x80 set), and that the length can dip down + // to 31 bytes when the leading byte is 0x00. I suspect that if I had tried + // 65k iterations that I'd have seen at least one 30 byte number + els.push(SSH._padBigInt(Enc.base64ToHex(jwk.d))); + //console.warn('els:', els[els.length - 1]); + els.push(Enc.binToHex(opts.comment || '')); + } + + return els; }; -SSH._packElements = function (ssh) { - var hex = ssh._elements.map(function (hex) { +SSH._packElements = function(els) { + return els.map(function (hex) { return SSH._numToUint32Hex(hex.length/2) + hex; }).join(''); - return [ ssh.type, Enc.hexToBase64(hex), ssh.comment ].join(' '); }; SSH._numToUint32Hex = function (num) { @@ -57,18 +124,20 @@ SSH._numToUint32Hex = function (num) { return hex; }; -SSH._padRsa = function (hex) { +SSH._padBigInt = 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); + //console.warn('l', hex.length/2, 'i', i); if (0x80 & i) { + //console.warn('0x80 true'); return '00' + hex; } return hex; }; -SSH._padEc = function (hex, len) { +SSH._padBytes = function (hex, len) { while (hex.length < len * 2) { hex = '00' + hex; } diff --git a/package.json b/package.json index 9e85bbf..47f001b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jwk-to-ssh", - "version": "1.0.1", + "version": "1.2.0", "description": "💯 JWK to SSH in a lightweight, zero-dependency library.", "homepage": "https://git.coolaj86.com/coolaj86/jwk-to-ssh.js", "main": "index.js", diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..e4d1d8f --- /dev/null +++ b/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +chmod 0600 fixtures/* + +# creates a new key +#ssh-keygen -t rsa -b 2048 -C root@localhost -N '' -f fixtures/privkey-rsa-2048.openssh.pem.3 +node bin/jwk-to-ssh.js fixtures/privkey-rsa-2048.jwk.json root@localhost > fixtures/privkey-rsa-2048.openssh.pem +node bin/jwk-to-ssh.js fixtures/privkey-rsa-2048.jwk.json root@localhost public > fixtures/pub-rsa-2048.ssh.pub + +node bin/jwk-to-ssh.js fixtures/privkey-ec-p256.jwk.json root@localhost > fixtures/privkey-ec-p256.openssh.pem +node bin/jwk-to-ssh.js fixtures/privkey-ec-p256.jwk.json root@localhost public > fixtures/pub-ec-p256.ssh.pub + +node bin/jwk-to-ssh.js fixtures/privkey-ec-p384.jwk.json root@localhost > fixtures/privkey-ec-p384.openssh.pem +node bin/jwk-to-ssh.js fixtures/privkey-ec-p384.jwk.json root@localhost public > fixtures/pub-ec-p384.ssh.pub + +# changes embedded comment and creates new random dummy checksum +ssh-keygen -c -C root@localhost -f fixtures/privkey-rsa-2048.openssh.pem +ssh-keygen -c -C root@localhost -f fixtures/privkey-ec-p256.openssh.pem +ssh-keygen -c -C root@localhost -f fixtures/privkey-ec-p384.openssh.pem + +# convert to public key +ssh-keygen -y -C root@localhost -f fixtures/privkey-rsa-2048.openssh.pem +ssh-keygen -y -C root@localhost -f fixtures/privkey-ec-p256.openssh.pem +ssh-keygen -y -C root@localhost -f fixtures/privkey-ec-p384.openssh.pem + +diff fixtures/pub-rsa-2048.ssh.pub fixtures/privkey-rsa-2048.openssh.pem.pub +diff fixtures/pub-ec-p256.ssh.pub fixtures/privkey-ec-p256.openssh.pem.pub +diff fixtures/pub-ec-p384.ssh.pub fixtures/privkey-ec-p384.openssh.pem.pub + +#ssh-keygen -e -m PKCS8 -f fixtures/privkey-ec-p256.openssh.pem +#ssh-keygen -e -m RFC4716 -f fixtures/privkey-ec-p256.openssh.pem