diff --git a/lib/ecdsacsr.js b/lib/ecdsacsr.js index 709838e..bd12e0e 100644 --- a/lib/ecdsacsr.js +++ b/lib/ecdsacsr.js @@ -26,21 +26,21 @@ function ASN1() { } return hex + numToHex(len) + str; } - -function UINT() { +ASN1.UInt = function UINT() { var str = Array.prototype.slice.call(arguments).join(''); var first = parseInt(str.slice(0, 2), 16); // high-order bit means signed, negative // we want positive, so we pad with a leading '00' if (0x80 & first) { str = '00' + str; } return ASN1('02', str); -} +}; -function BITSTR() { +ASN1.BitStr = function BITSTR() { var str = Array.prototype.slice.call(arguments).join(''); // '00' is a mask of how many bits of the next byte to ignore return ASN1('03', '00' + str); -} +}; + function SEQ() { return ASN1('30', Array.prototype.slice.call(arguments).join('')); } @@ -204,22 +204,27 @@ function csrEcSig(r, s) { // (ANSI X9.62 ECDSA algorithm with SHA256) OBJID('2A 86 48 CE 3D 04 03 02') ) - , BITSTR( + , ASN1.BitStr( SEQ( - UINT(toHex(r)) - , UINT(toHex(s)) + ASN1.UInt(toHex(r)) + , ASN1.UInt(toHex(s)) ) ) ].join(''); } var csrDomains = '82 {dlen} {domain.tld}'; // 2+n bytes (type 82?) +// TODO utf8 function strToHex(str) { - return str.split('').map(function (ch) { - var h = ch.charCodeAt(0).toString(16); - if (2 === h.length) { - return h; - } + var escstr = encodeURIComponent(str); + // replaces any uri escape sequence, such as %0A, + // with binary escape, such as 0x0A + var binstr = escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) { + return String.fromCharCode('0x' + p1); + }); + return binstr.split('').map(function (b) { + var h = b.charCodeAt(0).toString(16); + if (2 === h.length) { return h; } return '0' + h; }).join(''); } @@ -249,7 +254,7 @@ function fromHex(hex) { function createCsrBodyEc(domains, xy) { var altnames = domains.map(function (d) { - return csrDomains.replace(/{dlen}/, numToHex(d.length)).replace(/{domain\.tld}/, strToHex(d)); + return ASN1('82', strToHex(d)); }).join('').replace(/\s+/g, ''); var sublen = domains[0].length; var sanlen = (altnames.length/2); @@ -272,56 +277,54 @@ function createCsrBodyEc(domains, xy) { hxy += toHex(xy.y); } + var version = ASN1.UInt('00'); + var subject = ASN1('30' + , ASN1('31' + , ASN1('30' + // object id (commonName) + , ASN1('06', '55 04 03') + , ASN1('0C', strToHex(domains[0]))))); + var pubkey = ASN1('30' + , ASN1('30' + // 1.2.840.10045.2.1 ecPublicKey + // (ANSI X9.62 public key type) + , ASN1('06', '2A 86 48 CE 3D 02 01') + // 1.2.840.10045.3.1.7 prime256v1 + // (ANSI X9.62 named elliptic curve) + , ASN1('06', '2A 86 48 CE 3D 03 01 07') + ) + , ASN1.BitStr(compression + hxy)); + var altnames = ASN1('A0' + , ASN1('30' + // (extensionRequest (PKCS #9 via CRMF)) + , ASN1('06', '2A 86 48 86 F7 0D 01 09 0E') + , ASN1('31' + , ASN1('30' + , ASN1('30' + // (subjectAltName (X.509 extension)) + , ASN1('06', '55 1D 11') + , ASN1('04' + , ASN1('30', domains.map(function (d) { + return csrDomains.replace(/{dlen}/, numToHex(d.length)).replace(/{domain\.tld}/, strToHex(d)); + }).join('')))))))); var body = [ '30 81 {+85+n}' // 4 bytes, sequence - .replace(/{[^}]+}/, numToHex( - 3 - + 13 + sublen - + 27 + publen // Length for EC-related P-256 stuff - + 30 + sanlen - )) + .replace(/{[^}]+}/, numToHex(3 + 13 + sublen + 27 + publen + 30 + sanlen)) // #0 Total 3 - , ASN1('02', '00') + , version // Subject // #1 Total 2+11+n - , ASN1('30' - , ASN1('31' - , ASN1('30' - // object id (commonName) - , ASN1('06', '55 04 03') - , ASN1('0C', strToHex(domains[0])) - ) - ) - ) + , subject // P-256 Public Key // #2 Total 2+25+xy - , ASN1('30' - , ASN1('30' - // 1.2.840.10045.2.1 ecPublicKey - // (ANSI X9.62 public key type) - , ASN1('06', '2A 86 48 CE 3D 02 01') - // 1.2.840.10045.3.1.7 prime256v1 - // (ANSI X9.62 named elliptic curve) - , ASN1('06', '2A 86 48 CE 3D 03 01 07') - ) - , BITSTR(compression + hxy) - ) + , pubkey // Altnames // #3 Total 2+28+n - , ASN1('A0' - , ASN1('30' - // (extensionRequest (PKCS #9 via CRMF)) - , ASN1('06', '2A 86 48 86 F7 0D 01 09 0E') - , ASN1('31' - , ASN1('30' - , ASN1('30' - // (subjectAltName (X.509 extension)) - , ASN1('06', '55 1D 11') - , ASN1('04' - , ASN1('30', altnames))))))) ]; + , altnames + ]; body = body.join('').replace(/\s+/g, ''); return fromHex(body); }