old-keypairs.js/pubkey.js

182 lines
4.6 KiB
JavaScript

(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));