(function (exports) { 'use strict'; // 30 sequence // 03 bit string // 05 null // 06 object id // 00 00 00 00 // 30 82 01 22 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82 01 0F 00 30 82 01 0A 02 82 01 01 // 00 ... 02 03 01 00 01 // 30 82 02 22 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82 02 0F 00 30 82 02 0A 02 82 02 01 // 00 ... 02 03 01 00 01 function parsePem(pem) { var typ; var pub; var der = fromBase64(pem.toString('ascii').split(/\n/).filter(function (line, i) { if (0 === i) { if (/ PUBLIC /.test(line)) { pub = true; } else if (/ PRIVATE /.test(line)) { pub = false; } if (/ RSA /.test(line)) { typ = 'RSA'; } else if (/ EC/.test(line)) { typ = 'EC'; } } return !/---/.test(line); }).join('')); if (!typ) { if (pub) { // This is the RSA object ID if ('06092A864886F70D010101'.toLowerCase() === der.slice(6, 6 + 11).toString('hex')) { typ = 'RSA'; } } else { // TODO } } return { typ: typ, pub: pub, der: der }; } function toHex(ab) { var hex = []; var u8 = new Uint8Array(ab); var size = u8.byteLength; var i; var h; for (i = 0; i < size; i += 1) { h = u8[i].toString(16); if (2 === h.length) { hex.push(h); } else { hex.push('0' + h); } } return hex.join(''); } function readPubkey(der) { var offset = 28 + 5; // header plus size var ksBytes = der.slice(30, 32); // not sure why it shows 257 instead of 256 var keysize = new DataView(ksBytes).getUint16(0, false) - 1; var pub = der.slice(offset, offset + keysize); return pub; } function readPrivkey(der) { var offset = 7 + 5; // header plus size var ksBytes = der.slice(9, 11); // not sure why it shows 257 instead of 256 var keysize = new DataView(ksBytes).getUint16(0, false) - 1; var pub = der.slice(offset, offset + keysize); return pub; } // I used OpenSSL to create RSA keys with sizes 2048 and 4096. // Then I used https://lapo.it/asn1js/ to see which bits changed. // And I created a template from the bits that do and don't. // No ASN.1 and X.509 parsers or generators. Yay! var rsaAsn1Head = ( '30 82 xx 22 30 0D 06 09' + '2A 86 48 86 F7 0D 01 01' + '01 05 00 03 82 xx 0F 00' + '30 82 xx 0A 02 82 xx 01' + '00').replace(/\s+/g, ''); var rsaAsn1Foot = ('02 03 01 00 01').replace(/\s+/g, ''); function toRsaPub(pub) { // 256 // 2048-bit var len = '0' + (pub.byteLength / 256); var head = rsaAsn1Head.replace(/xx/g, len); var headSize = (rsaAsn1Head.length / 2); var foot = rsaAsn1Foot; var footSize = (foot.length / 2); var size = headSize + pub.byteLength + footSize; var der = new Uint8Array(new ArrayBuffer(size)); var i, j; for (i = 0, j = 0; i < headSize; i += 1) { der[i] = parseInt(head.slice(j,j+2), 16); j += 2; } pub = new Uint8Array(pub); for (i = 0; i < pub.byteLength; i += 1) { der[headSize + i] = pub[i]; } for (i = 0, j = 0; i < footSize; i += 1) { der[headSize + pub.byteLength + i] = parseInt(foot.slice(j,j+2), 16); j += 2; } return der.buffer; } function formatAsPem(str, privacy, pemName) { var pemstr = (pemName ? pemName + ' ' : ''); var privstr = (privacy ? privacy + ' ' : ''); var finalString = '-----BEGIN ' + pemstr + privstr + 'KEY-----\n'; while (str.length > 0) { finalString += str.substring(0, 64) + '\n'; str = str.substring(64); } finalString = finalString + '-----END ' + pemstr + privstr + 'KEY-----'; return finalString; } function formatAsPublicPem(str) { return formatAsPem(str, 'PUBLIC', ''); } function toBase64(der) { if ('undefined' === typeof btoa) { return Buffer.from(der).toString('base64'); } var chs = []; der = new Uint8Array(der); der.forEach(function (b) { chs.push(String.fromCharCode(b)); }); return btoa(chs.join('')); } function fromBase64(b64) { var buf; var ab; if ('undefined' === typeof atob) { buf = Buffer.from(b64, 'base64'); return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); } buf = atob(b64); ab = new ArrayBuffer(buf.length); ab = new Uint8Array(ab); buf.split('').forEach(function (ch, i) { ab[i] = ch.charCodeAt(0); }); return ab.buffer; } exports.parsePem = parsePem; exports.toBase64 = toBase64; exports.toRsaPub = toRsaPub; exports.formatAsPublicPem = formatAsPublicPem; exports.formatAsPem = formatAsPem; exports.readPubkey = readPubkey; exports.readPrivkey = readPrivkey; exports.toHex = toHex; }('undefined' !== typeof module ? module.exports: window));