diff --git a/fixtures/privkey-rsa-2048.jwt.json b/fixtures/privkey-rsa-2048.jwt.json new file mode 100644 index 0000000..f344c22 --- /dev/null +++ b/fixtures/privkey-rsa-2048.jwt.json @@ -0,0 +1,11 @@ +{ + "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", + "dp": "tzDGjECFOU0ehqtuqhcuT63a7h8hj19-7MJqoFwY9HQ-ALkfXyYLXeBSGxHbyiIYuodZg6LsfMNgUJ3r3Eyhc_nAVfYPEC_2IdAG4WYmq7iXYF9LQV09qEsKbFykm7QekE3hO7wswo5k-q2tp3ieBYdVGAXJoGOdv5VpaZ7B1QE", + "dq": "kh5dyDk7YCz7sUFbpsmuAeuPjoH2ghooh2u3xN7iUVmAg-ToKjwbVnG5-7eXiC779rQVwnrD_0yh1AFJ8wjRPqDIR7ObXGHikIxT1VSQWqiJm6AfZzDsL0LUD4YS3iPdhob7-NxLKWzqao_u4lhnDQaX9PKa12HFlny6K1daL48", + "qi": "AlHWbx1gp6Z9pbw_1hlS7HuXAgWoX7IjbTUelldf4gkriDWLOrj3QCZcO4ZvZvEwJhVlsny9LO8IkbwGJEL6cXraK08ByVS2mwQyflgTgGNnpzixyEUL_mrQLx6y145FHcxfeqNInMhep-0Mxn1D5nlhmIOgRApS0t9VoXtHhFU" +} diff --git a/lib/encoding.js b/lib/encoding.js index b2d7cd6..5b66e05 100644 --- a/lib/encoding.js +++ b/lib/encoding.js @@ -15,6 +15,10 @@ Enc.bufToHex = function toHex(u8) { return hex.join('').toLowerCase(); }; +Enc.hexToBuf = function (hex) { + return Buffer.from(hex, 'hex'); +}; + Enc.numToHex = function numToHex(d) { d = d.toString(16); if (d.length % 2) { @@ -23,6 +27,10 @@ Enc.numToHex = function numToHex(d) { return d; }; +Enc.base64ToHex = function base64ToHex(b64) { + return Enc.bufToHex(Enc.base64ToBuf(b64)); +} + Enc.bufToBase64 = function toHex(u8) { // we want to maintain api compatability with browser APIs, // so we assume that this could be a Uint8Array diff --git a/lib/pem.js b/lib/pem.js index 0cad9e5..581f3a6 100644 --- a/lib/pem.js +++ b/lib/pem.js @@ -37,3 +37,11 @@ PEM.parseBlock = function pemToDer(pem) { return { kty: typ, pub: pub, der: der }; }; + +PEM.packBlock = function (opts) { + // TODO allow for headers? + return '-----BEGIN ' + opts.type + '-----\n' + + Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join('\n') + '\n' + + '-----END ' + opts.type + '-----' + ; +}; diff --git a/lib/rasha.js b/lib/rasha.js index 6ace4b9..111de93 100644 --- a/lib/rasha.js +++ b/lib/rasha.js @@ -90,7 +90,11 @@ RSA.pack = function (opts) { } if ('pkcs1' === format) { - return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) }); + if (jwk.d) { + return PEM.packBlock({ type: "RSA PRIVATE KEY", bytes: x509.packPkcs1(jwk) }); + } else { + return PEM.packBlock({ type: "RSA PUBLIC KEY", bytes: x509.packPkcs1(jwk) }); + } } else if ('pkcs8' === format) { return PEM.packBlock({ type: "PRIVATE KEY", bytes: x509.packPkcs8(jwk) }); } else if (-1 !== [ 'spki', 'pkix' ].indexOf(format)) { diff --git a/lib/x509.js b/lib/x509.js index 32a2a87..5cac827 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -1,5 +1,7 @@ 'use strict'; +// TODO fun idea: create a template from an existing file + var x509 = module.exports; var ASN1 = require('./asn1.js'); var Enc = require('./encoding.js'); @@ -91,3 +93,24 @@ x509.parsePkcs8 = function parseRsaPkcs8(buf, asn1, jwk) { } return jwk; }; + +x509.packPkcs1 = function (jwk) { + var n = ASN1.UInt(Enc.base64ToHex(jwk.n)); + var e = ASN1.UInt(Enc.base64ToHex(jwk.e)); + + if (!jwk.d) { + return Enc.hexToBuf(ASN1('30', n, e)); + } + + return Enc.hexToBuf(ASN1('30' + , ASN1.UInt('00') + , n + , e + , ASN1.UInt(Enc.base64ToHex(jwk.d)) + , ASN1.UInt(Enc.base64ToHex(jwk.p)) + , ASN1.UInt(Enc.base64ToHex(jwk.q)) + , ASN1.UInt(Enc.base64ToHex(jwk.dp)) + , ASN1.UInt(Enc.base64ToHex(jwk.dq)) + , ASN1.UInt(Enc.base64ToHex(jwk.qi)) + )); +};