From c089e0590812e273df1e8f70dee8d868f2ac0b99 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 15 Oct 2019 04:06:54 -0600 Subject: [PATCH] v0.7.2: bugfixes for @root/keypairs.js --- dist/x509.all.js | 46 ++++++++++++++++++++++++++++++++++++++++++-- dist/x509.all.min.js | 2 +- dist/x509.js | 46 ++++++++++++++++++++++++++++++++++++++++++-- dist/x509.min.js | 2 +- package.json | 2 +- parsers.js | 46 ++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 135 insertions(+), 9 deletions(-) diff --git a/dist/x509.all.js b/dist/x509.all.js index ea2c8eb..13731c0 100644 --- a/dist/x509.all.js +++ b/dist/x509.all.js @@ -536,7 +536,7 @@ X509.parsePkcs1 = function parseRsaPkcs1(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } @@ -664,7 +664,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } if ( @@ -696,6 +696,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { 'not an RSA PKCS#8 public or private key (wrong format)' ); } + return jwk; }; @@ -739,6 +740,47 @@ X509.parseEcSpki = function(u8, jwk) { X509.parsePkix = X509.parseSpki; +// TODO look for ECDSA as well +X509._parseRsa = function(asn1) { + // accepting der for compatability with other usages + + if (asn1.byteLength) { + asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); + } + + var meta = { kty: 'RSA', format: 'pkcs1', public: true }; + //meta.asn1 = ASN1.parse(u8); + + if ( + asn1.children.every(function(el) { + return 0x02 === el.type; + }) + ) { + if (2 === asn1.children.length) { + // rsa pkcs1 public + //return meta; + } else if (asn1.children.length >= 9) { + // the standard allows for "otherPrimeInfos", hence at least 9 + meta.public = false; + // rsa pkcs1 private + //return meta; + } else { + throw new Error( + 'not an RSA PKCS#1 public or private key (wrong number of ints)' + ); + } + } else { + meta.format = 'pkcs8'; + } + + var jwk = { kty: 'RSA', n: null, e: null }; + if ('pkcs1' === meta.format) { + return X509.parsePkcs1(asn1, jwk); + } else { + return X509.parsePkcs8(asn1, jwk); + } +}; + // 1.2.840.10045.3.1.7 // prime256v1 (ANSI X9.62 named elliptic curve) diff --git a/dist/x509.all.min.js b/dist/x509.all.min.js index b929cee..f124564 100644 --- a/dist/x509.all.min.js +++ b/dist/x509.all.min.js @@ -1 +1 @@ -(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters2+asn1.lengthSize+asn1.length){if(!eager){console.error(JSON.stringify(asn1,ASN1._replacer,2))}throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})();(function(){"use strict";var X509=window.X509={};var ASN1=window.ASN1;var Enc=window.Encoding;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();X509.parsePkcs1=function parseRsaPkcs1(asn1,jwk){if(!jwk){jwk={}}if(!Array.isArray(asn1)){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(!asn1.children.every(function(el){return 2===el.type})){throw new Error("not an RSA PKCS#1 public or private key (not all ints)")}if(2===asn1.children.length){jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(asn1.children.length>=9){jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}return jwk};X509.parseSec1=function parseEcOnlyPrivkey(u8,jwk){var index=7;var len=32;var olen=OBJ_ID_EC.length/2;if("P-384"===jwk.crv){olen=OBJ_ID_EC_384.length/2;index=8;len=48}if(len!==u8[index-1]){throw new Error("Unexpected bitlength "+len)}var d=u8.slice(index,index+len);var ci=index+len+2+olen+2+3;var c=u8[ci];var x,y;if(4===c){y=u8.slice(ci+1+len,ci+1+len+len)}else if(2!==c){throw new Error("not a supported EC private key")}x=u8.slice(ci+1,ci+1+len);return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkcs8=function(u8,jwk){try{return X509.parseRsaPkcs8(u8,jwk)}catch(e){return X509.parseEcPkcs8(u8,jwk)}};X509.parseEcPkcs8=function parseEcPkcs8(u8,jwk){var index=24+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){index=24+OBJ_ID_EC_384.length/2+2;len=48}if(4!==u8[index]){throw new Error("privkey not found")}var d=u8.slice(index+2,index+2+len);var ci=index+2+len+5;var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===u8[ci]){y=u8.slice(yi,yi+len)}else if(2!==u8[ci]){throw new Error("invalid compression bit (expected 0x04 or 0x02)")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parseRsaPkcs8=function parseRsaPkcs8(asn1,jwk){if(!jwk){jwk={}}if(!Array.isArray(asn1)){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(2===asn1.children.length&&3===asn1.children[1].type){asn1=asn1.children[1].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(3===asn1.children.length&&4===asn1.children[2].type&&48===asn1.children[2].children[0].type&&2===asn1.children[2].children[0].children[0].type){asn1=asn1.children[2].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#8 public or private key (wrong format)")}return jwk};X509.parseSpki=function(buf,jwk){try{return X509.parseRsaPkcs8(buf,jwk)}catch(e){return X509.parseEcSpki(buf,jwk)}};X509.parseEcSpki=function(u8,jwk){var ci=16+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){ci=16+OBJ_ID_EC_384.length/2;len=48}var c=u8[ci];var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===c){y=u8.slice(yi,yi+len)}else if(2!==c){throw new Error("not a supported EC private key")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkix=X509.parseSpki;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_PUB="06 07 2A8648CE3D0201".replace(/\s+/g,"").toLowerCase();var Asn1=ASN1.Any;var UInt=ASN1.UInt;var BitStr=ASN1.BitStr;X509.packPkcs1=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));if(!jwk.d){return Enc.hexToBuf(Asn1("30",n,e))}return Enc.hexToBuf(Asn1("30",UInt("00"),n,e,UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))};X509.packSec1=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("01"),Asn1("04",d),Asn1("A0",objId),Asn1("A1",BitStr("04"+x+y))))};X509.packPkcs8=function(jwk){if(/RSA/.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packPkcs8Ec(jwk)};X509.packPkcs8Ec=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",OBJ_ID_EC_PUB,objId),Asn1("04",Asn1("30",UInt("01"),Asn1("04",d),Asn1("A1",BitStr("04"+x+y))))))};X509.packPkcs8Rsa=function(jwk){if(!jwk.d){return Enc.hexToBuf(Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(Asn1("30",UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e))))))}return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),Asn1("04",Asn1("30",UInt("00"),UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e)),UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))))};X509.packSpkiEc=function(jwk){var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",Asn1("30",OBJ_ID_EC_PUB,objId),BitStr("04"+x+y)))};X509.packSpki=function(jwk){if(/RSA/i.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packSpkiEc(jwk)};X509.packPkix=X509.packSpki;X509.packCsrRsaPublicKey=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));var asn1pub=Asn1("30",n,e);return Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(asn1pub))};X509.packCsrEcPublicKey=function(jwk){var ecOid=X509._oids[jwk.crv];if(!ecOid){throw new Error("Unsupported namedCurve '"+jwk.crv+"'. Supported types are "+Object.keys(X509._oids))}var cmp="04";var hxy="";if(!jwk.y){cmp="02"}hxy+=Enc.base64ToHex(jwk.x);if(jwk.y){hxy+=Enc.base64ToHex(jwk.y)}return Asn1("30",Asn1("30",Asn1("06","2a8648ce3d0201"),Asn1("06",ecOid)),BitStr(cmp+hxy))};X509._oids={"P-256":"2a8648ce3d030107","P-384":"2b81040022"}})(); +(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters2+asn1.lengthSize+asn1.length){if(!eager){console.error(JSON.stringify(asn1,ASN1._replacer,2))}throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})();(function(){"use strict";var X509=window.X509={};var ASN1=window.ASN1;var Enc=window.Encoding;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();X509.parsePkcs1=function parseRsaPkcs1(asn1,jwk){if(!jwk){jwk={}}if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(!asn1.children.every(function(el){return 2===el.type})){throw new Error("not an RSA PKCS#1 public or private key (not all ints)")}if(2===asn1.children.length){jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(asn1.children.length>=9){jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}return jwk};X509.parseSec1=function parseEcOnlyPrivkey(u8,jwk){var index=7;var len=32;var olen=OBJ_ID_EC.length/2;if("P-384"===jwk.crv){olen=OBJ_ID_EC_384.length/2;index=8;len=48}if(len!==u8[index-1]){throw new Error("Unexpected bitlength "+len)}var d=u8.slice(index,index+len);var ci=index+len+2+olen+2+3;var c=u8[ci];var x,y;if(4===c){y=u8.slice(ci+1+len,ci+1+len+len)}else if(2!==c){throw new Error("not a supported EC private key")}x=u8.slice(ci+1,ci+1+len);return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkcs8=function(u8,jwk){try{return X509.parseRsaPkcs8(u8,jwk)}catch(e){return X509.parseEcPkcs8(u8,jwk)}};X509.parseEcPkcs8=function parseEcPkcs8(u8,jwk){var index=24+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){index=24+OBJ_ID_EC_384.length/2+2;len=48}if(4!==u8[index]){throw new Error("privkey not found")}var d=u8.slice(index+2,index+2+len);var ci=index+2+len+5;var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===u8[ci]){y=u8.slice(yi,yi+len)}else if(2!==u8[ci]){throw new Error("invalid compression bit (expected 0x04 or 0x02)")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parseRsaPkcs8=function parseRsaPkcs8(asn1,jwk){if(!jwk){jwk={}}if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(2===asn1.children.length&&3===asn1.children[1].type){asn1=asn1.children[1].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(3===asn1.children.length&&4===asn1.children[2].type&&48===asn1.children[2].children[0].type&&2===asn1.children[2].children[0].children[0].type){asn1=asn1.children[2].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#8 public or private key (wrong format)")}return jwk};X509.parseSpki=function(buf,jwk){try{return X509.parseRsaPkcs8(buf,jwk)}catch(e){return X509.parseEcSpki(buf,jwk)}};X509.parseEcSpki=function(u8,jwk){var ci=16+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){ci=16+OBJ_ID_EC_384.length/2;len=48}var c=u8[ci];var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===c){y=u8.slice(yi,yi+len)}else if(2!==c){throw new Error("not a supported EC private key")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkix=X509.parseSpki;X509._parseRsa=function(asn1){if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}var meta={kty:"RSA",format:"pkcs1",public:true};if(asn1.children.every(function(el){return 2===el.type})){if(2===asn1.children.length){}else if(asn1.children.length>=9){meta.public=false}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}}else{meta.format="pkcs8"}var jwk={kty:"RSA",n:null,e:null};if("pkcs1"===meta.format){return X509.parsePkcs1(asn1,jwk)}else{return X509.parsePkcs8(asn1,jwk)}};var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_PUB="06 07 2A8648CE3D0201".replace(/\s+/g,"").toLowerCase();var Asn1=ASN1.Any;var UInt=ASN1.UInt;var BitStr=ASN1.BitStr;X509.packPkcs1=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));if(!jwk.d){return Enc.hexToBuf(Asn1("30",n,e))}return Enc.hexToBuf(Asn1("30",UInt("00"),n,e,UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))};X509.packSec1=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("01"),Asn1("04",d),Asn1("A0",objId),Asn1("A1",BitStr("04"+x+y))))};X509.packPkcs8=function(jwk){if(/RSA/.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packPkcs8Ec(jwk)};X509.packPkcs8Ec=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",OBJ_ID_EC_PUB,objId),Asn1("04",Asn1("30",UInt("01"),Asn1("04",d),Asn1("A1",BitStr("04"+x+y))))))};X509.packPkcs8Rsa=function(jwk){if(!jwk.d){return Enc.hexToBuf(Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(Asn1("30",UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e))))))}return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),Asn1("04",Asn1("30",UInt("00"),UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e)),UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))))};X509.packSpkiEc=function(jwk){var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",Asn1("30",OBJ_ID_EC_PUB,objId),BitStr("04"+x+y)))};X509.packSpki=function(jwk){if(/RSA/i.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packSpkiEc(jwk)};X509.packPkix=X509.packSpki;X509.packCsrRsaPublicKey=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));var asn1pub=Asn1("30",n,e);return Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(asn1pub))};X509.packCsrEcPublicKey=function(jwk){var ecOid=X509._oids[jwk.crv];if(!ecOid){throw new Error("Unsupported namedCurve '"+jwk.crv+"'. Supported types are "+Object.keys(X509._oids))}var cmp="04";var hxy="";if(!jwk.y){cmp="02"}hxy+=Enc.base64ToHex(jwk.x);if(jwk.y){hxy+=Enc.base64ToHex(jwk.y)}return Asn1("30",Asn1("30",Asn1("06","2a8648ce3d0201"),Asn1("06",ecOid)),BitStr(cmp+hxy))};X509._oids={"P-256":"2a8648ce3d030107","P-384":"2b81040022"}})(); diff --git a/dist/x509.js b/dist/x509.js index bd52758..66243d8 100644 --- a/dist/x509.js +++ b/dist/x509.js @@ -18,7 +18,7 @@ X509.parsePkcs1 = function parseRsaPkcs1(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } @@ -146,7 +146,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } if ( @@ -178,6 +178,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { 'not an RSA PKCS#8 public or private key (wrong format)' ); } + return jwk; }; @@ -221,6 +222,47 @@ X509.parseEcSpki = function(u8, jwk) { X509.parsePkix = X509.parseSpki; +// TODO look for ECDSA as well +X509._parseRsa = function(asn1) { + // accepting der for compatability with other usages + + if (asn1.byteLength) { + asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); + } + + var meta = { kty: 'RSA', format: 'pkcs1', public: true }; + //meta.asn1 = ASN1.parse(u8); + + if ( + asn1.children.every(function(el) { + return 0x02 === el.type; + }) + ) { + if (2 === asn1.children.length) { + // rsa pkcs1 public + //return meta; + } else if (asn1.children.length >= 9) { + // the standard allows for "otherPrimeInfos", hence at least 9 + meta.public = false; + // rsa pkcs1 private + //return meta; + } else { + throw new Error( + 'not an RSA PKCS#1 public or private key (wrong number of ints)' + ); + } + } else { + meta.format = 'pkcs8'; + } + + var jwk = { kty: 'RSA', n: null, e: null }; + if ('pkcs1' === meta.format) { + return X509.parsePkcs1(asn1, jwk); + } else { + return X509.parsePkcs8(asn1, jwk); + } +}; + // 1.2.840.10045.3.1.7 // prime256v1 (ANSI X9.62 named elliptic curve) diff --git a/dist/x509.min.js b/dist/x509.min.js index 498fead..9f6a7fa 100644 --- a/dist/x509.min.js +++ b/dist/x509.min.js @@ -1 +1 @@ -(function(){"use strict";var X509=window.X509={};var ASN1=window.ASN1;var Enc=window.Encoding;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();X509.parsePkcs1=function parseRsaPkcs1(asn1,jwk){if(!jwk){jwk={}}if(!Array.isArray(asn1)){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(!asn1.children.every(function(el){return 2===el.type})){throw new Error("not an RSA PKCS#1 public or private key (not all ints)")}if(2===asn1.children.length){jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(asn1.children.length>=9){jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}return jwk};X509.parseSec1=function parseEcOnlyPrivkey(u8,jwk){var index=7;var len=32;var olen=OBJ_ID_EC.length/2;if("P-384"===jwk.crv){olen=OBJ_ID_EC_384.length/2;index=8;len=48}if(len!==u8[index-1]){throw new Error("Unexpected bitlength "+len)}var d=u8.slice(index,index+len);var ci=index+len+2+olen+2+3;var c=u8[ci];var x,y;if(4===c){y=u8.slice(ci+1+len,ci+1+len+len)}else if(2!==c){throw new Error("not a supported EC private key")}x=u8.slice(ci+1,ci+1+len);return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkcs8=function(u8,jwk){try{return X509.parseRsaPkcs8(u8,jwk)}catch(e){return X509.parseEcPkcs8(u8,jwk)}};X509.parseEcPkcs8=function parseEcPkcs8(u8,jwk){var index=24+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){index=24+OBJ_ID_EC_384.length/2+2;len=48}if(4!==u8[index]){throw new Error("privkey not found")}var d=u8.slice(index+2,index+2+len);var ci=index+2+len+5;var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===u8[ci]){y=u8.slice(yi,yi+len)}else if(2!==u8[ci]){throw new Error("invalid compression bit (expected 0x04 or 0x02)")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parseRsaPkcs8=function parseRsaPkcs8(asn1,jwk){if(!jwk){jwk={}}if(!Array.isArray(asn1)){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(2===asn1.children.length&&3===asn1.children[1].type){asn1=asn1.children[1].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(3===asn1.children.length&&4===asn1.children[2].type&&48===asn1.children[2].children[0].type&&2===asn1.children[2].children[0].children[0].type){asn1=asn1.children[2].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#8 public or private key (wrong format)")}return jwk};X509.parseSpki=function(buf,jwk){try{return X509.parseRsaPkcs8(buf,jwk)}catch(e){return X509.parseEcSpki(buf,jwk)}};X509.parseEcSpki=function(u8,jwk){var ci=16+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){ci=16+OBJ_ID_EC_384.length/2;len=48}var c=u8[ci];var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===c){y=u8.slice(yi,yi+len)}else if(2!==c){throw new Error("not a supported EC private key")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkix=X509.parseSpki;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_PUB="06 07 2A8648CE3D0201".replace(/\s+/g,"").toLowerCase();var Asn1=ASN1.Any;var UInt=ASN1.UInt;var BitStr=ASN1.BitStr;X509.packPkcs1=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));if(!jwk.d){return Enc.hexToBuf(Asn1("30",n,e))}return Enc.hexToBuf(Asn1("30",UInt("00"),n,e,UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))};X509.packSec1=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("01"),Asn1("04",d),Asn1("A0",objId),Asn1("A1",BitStr("04"+x+y))))};X509.packPkcs8=function(jwk){if(/RSA/.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packPkcs8Ec(jwk)};X509.packPkcs8Ec=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",OBJ_ID_EC_PUB,objId),Asn1("04",Asn1("30",UInt("01"),Asn1("04",d),Asn1("A1",BitStr("04"+x+y))))))};X509.packPkcs8Rsa=function(jwk){if(!jwk.d){return Enc.hexToBuf(Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(Asn1("30",UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e))))))}return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),Asn1("04",Asn1("30",UInt("00"),UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e)),UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))))};X509.packSpkiEc=function(jwk){var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",Asn1("30",OBJ_ID_EC_PUB,objId),BitStr("04"+x+y)))};X509.packSpki=function(jwk){if(/RSA/i.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packSpkiEc(jwk)};X509.packPkix=X509.packSpki;X509.packCsrRsaPublicKey=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));var asn1pub=Asn1("30",n,e);return Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(asn1pub))};X509.packCsrEcPublicKey=function(jwk){var ecOid=X509._oids[jwk.crv];if(!ecOid){throw new Error("Unsupported namedCurve '"+jwk.crv+"'. Supported types are "+Object.keys(X509._oids))}var cmp="04";var hxy="";if(!jwk.y){cmp="02"}hxy+=Enc.base64ToHex(jwk.x);if(jwk.y){hxy+=Enc.base64ToHex(jwk.y)}return Asn1("30",Asn1("30",Asn1("06","2a8648ce3d0201"),Asn1("06",ecOid)),BitStr(cmp+hxy))};X509._oids={"P-256":"2a8648ce3d030107","P-384":"2b81040022"}})(); +(function(){"use strict";var X509=window.X509={};var ASN1=window.ASN1;var Enc=window.Encoding;var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();X509.parsePkcs1=function parseRsaPkcs1(asn1,jwk){if(!jwk){jwk={}}if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(!asn1.children.every(function(el){return 2===el.type})){throw new Error("not an RSA PKCS#1 public or private key (not all ints)")}if(2===asn1.children.length){jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(asn1.children.length>=9){jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}return jwk};X509.parseSec1=function parseEcOnlyPrivkey(u8,jwk){var index=7;var len=32;var olen=OBJ_ID_EC.length/2;if("P-384"===jwk.crv){olen=OBJ_ID_EC_384.length/2;index=8;len=48}if(len!==u8[index-1]){throw new Error("Unexpected bitlength "+len)}var d=u8.slice(index,index+len);var ci=index+len+2+olen+2+3;var c=u8[ci];var x,y;if(4===c){y=u8.slice(ci+1+len,ci+1+len+len)}else if(2!==c){throw new Error("not a supported EC private key")}x=u8.slice(ci+1,ci+1+len);return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkcs8=function(u8,jwk){try{return X509.parseRsaPkcs8(u8,jwk)}catch(e){return X509.parseEcPkcs8(u8,jwk)}};X509.parseEcPkcs8=function parseEcPkcs8(u8,jwk){var index=24+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){index=24+OBJ_ID_EC_384.length/2+2;len=48}if(4!==u8[index]){throw new Error("privkey not found")}var d=u8.slice(index+2,index+2+len);var ci=index+2+len+5;var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===u8[ci]){y=u8.slice(yi,yi+len)}else if(2!==u8[ci]){throw new Error("invalid compression bit (expected 0x04 or 0x02)")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",d:Enc.bufToUrlBase64(d),x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parseRsaPkcs8=function parseRsaPkcs8(asn1,jwk){if(!jwk){jwk={}}if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}if(2===asn1.children.length&&3===asn1.children[1].type){asn1=asn1.children[1].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[0].value);jwk.e=Enc.bufToUrlBase64(asn1.children[1].value);jwk.kty="RSA"}else if(3===asn1.children.length&&4===asn1.children[2].type&&48===asn1.children[2].children[0].type&&2===asn1.children[2].children[0].children[0].type){asn1=asn1.children[2].children[0];jwk.n=Enc.bufToUrlBase64(asn1.children[1].value);jwk.e=Enc.bufToUrlBase64(asn1.children[2].value);jwk.d=Enc.bufToUrlBase64(asn1.children[3].value);jwk.p=Enc.bufToUrlBase64(asn1.children[4].value);jwk.q=Enc.bufToUrlBase64(asn1.children[5].value);jwk.dp=Enc.bufToUrlBase64(asn1.children[6].value);jwk.dq=Enc.bufToUrlBase64(asn1.children[7].value);jwk.qi=Enc.bufToUrlBase64(asn1.children[8].value);jwk.kty="RSA"}else{throw new Error("not an RSA PKCS#8 public or private key (wrong format)")}return jwk};X509.parseSpki=function(buf,jwk){try{return X509.parseRsaPkcs8(buf,jwk)}catch(e){return X509.parseEcSpki(buf,jwk)}};X509.parseEcSpki=function(u8,jwk){var ci=16+OBJ_ID_EC.length/2;var len=32;if("P-384"===jwk.crv){ci=16+OBJ_ID_EC_384.length/2;len=48}var c=u8[ci];var xi=ci+1;var x=u8.slice(xi,xi+len);var yi=xi+len;var y;if(4===c){y=u8.slice(yi,yi+len)}else if(2!==c){throw new Error("not a supported EC private key")}return{kty:jwk.kty||"EC",crv:jwk.crv||"P-256",x:Enc.bufToUrlBase64(x),y:Enc.bufToUrlBase64(y)}};X509.parsePkix=X509.parseSpki;X509._parseRsa=function(asn1){if(asn1.byteLength){asn1=ASN1.parse({der:asn1,verbose:true,json:false})}var meta={kty:"RSA",format:"pkcs1",public:true};if(asn1.children.every(function(el){return 2===el.type})){if(2===asn1.children.length){}else if(asn1.children.length>=9){meta.public=false}else{throw new Error("not an RSA PKCS#1 public or private key (wrong number of ints)")}}else{meta.format="pkcs8"}var jwk={kty:"RSA",n:null,e:null};if("pkcs1"===meta.format){return X509.parsePkcs1(asn1,jwk)}else{return X509.parsePkcs8(asn1,jwk)}};var OBJ_ID_EC="06 08 2A8648CE3D030107".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_384="06 05 2B81040022".replace(/\s+/g,"").toLowerCase();var OBJ_ID_EC_PUB="06 07 2A8648CE3D0201".replace(/\s+/g,"").toLowerCase();var Asn1=ASN1.Any;var UInt=ASN1.UInt;var BitStr=ASN1.BitStr;X509.packPkcs1=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));if(!jwk.d){return Enc.hexToBuf(Asn1("30",n,e))}return Enc.hexToBuf(Asn1("30",UInt("00"),n,e,UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))};X509.packSec1=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("01"),Asn1("04",d),Asn1("A0",objId),Asn1("A1",BitStr("04"+x+y))))};X509.packPkcs8=function(jwk){if(/RSA/.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packPkcs8Ec(jwk)};X509.packPkcs8Ec=function(jwk){var d=Enc.base64ToHex(jwk.d);var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",OBJ_ID_EC_PUB,objId),Asn1("04",Asn1("30",UInt("01"),Asn1("04",d),Asn1("A1",BitStr("04"+x+y))))))};X509.packPkcs8Rsa=function(jwk){if(!jwk.d){return Enc.hexToBuf(Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(Asn1("30",UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e))))))}return Enc.hexToBuf(Asn1("30",UInt("00"),Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),Asn1("04",Asn1("30",UInt("00"),UInt(Enc.base64ToHex(jwk.n)),UInt(Enc.base64ToHex(jwk.e)),UInt(Enc.base64ToHex(jwk.d)),UInt(Enc.base64ToHex(jwk.p)),UInt(Enc.base64ToHex(jwk.q)),UInt(Enc.base64ToHex(jwk.dp)),UInt(Enc.base64ToHex(jwk.dq)),UInt(Enc.base64ToHex(jwk.qi))))))};X509.packSpkiEc=function(jwk){var x=Enc.base64ToHex(jwk.x);var y=Enc.base64ToHex(jwk.y);var objId="P-256"===jwk.crv?OBJ_ID_EC:OBJ_ID_EC_384;return Enc.hexToBuf(Asn1("30",Asn1("30",OBJ_ID_EC_PUB,objId),BitStr("04"+x+y)))};X509.packSpki=function(jwk){if(/RSA/i.test(jwk.kty)){return X509.packPkcs8Rsa(jwk)}return X509.packSpkiEc(jwk)};X509.packPkix=X509.packSpki;X509.packCsrRsaPublicKey=function(jwk){var n=UInt(Enc.base64ToHex(jwk.n));var e=UInt(Enc.base64ToHex(jwk.e));var asn1pub=Asn1("30",n,e);return Asn1("30",Asn1("30",Asn1("06","2a864886f70d010101"),Asn1("05")),BitStr(asn1pub))};X509.packCsrEcPublicKey=function(jwk){var ecOid=X509._oids[jwk.crv];if(!ecOid){throw new Error("Unsupported namedCurve '"+jwk.crv+"'. Supported types are "+Object.keys(X509._oids))}var cmp="04";var hxy="";if(!jwk.y){cmp="02"}hxy+=Enc.base64ToHex(jwk.x);if(jwk.y){hxy+=Enc.base64ToHex(jwk.y)}return Asn1("30",Asn1("30",Asn1("06","2a8648ce3d0201"),Asn1("06",ecOid)),BitStr(cmp+hxy))};X509._oids={"P-256":"2a8648ce3d030107","P-384":"2b81040022"}})(); diff --git a/package.json b/package.json index 4e4aa65..a922b32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@root/x509", - "version": "0.7.1", + "version": "0.7.2", "description": "VanillaJS, Lightweight, Zero-Dependency, X509 schema encoder and decoder.", "main": "index.js", "browser": { diff --git a/parsers.js b/parsers.js index 92837b7..7b9f147 100644 --- a/parsers.js +++ b/parsers.js @@ -17,7 +17,7 @@ X509.parsePkcs1 = function parseRsaPkcs1(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } @@ -145,7 +145,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { } // might be a buffer - if (!Array.isArray(asn1)) { + if (asn1.byteLength) { asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); } if ( @@ -177,6 +177,7 @@ X509.parseRsaPkcs8 = function parseRsaPkcs8(asn1, jwk) { 'not an RSA PKCS#8 public or private key (wrong format)' ); } + return jwk; }; @@ -219,3 +220,44 @@ X509.parseEcSpki = function(u8, jwk) { }; X509.parsePkix = X509.parseSpki; + +// TODO look for ECDSA as well +X509._parseRsa = function(asn1) { + // accepting der for compatability with other usages + + if (asn1.byteLength) { + asn1 = ASN1.parse({ der: asn1, verbose: true, json: false }); + } + + var meta = { kty: 'RSA', format: 'pkcs1', public: true }; + //meta.asn1 = ASN1.parse(u8); + + if ( + asn1.children.every(function(el) { + return 0x02 === el.type; + }) + ) { + if (2 === asn1.children.length) { + // rsa pkcs1 public + //return meta; + } else if (asn1.children.length >= 9) { + // the standard allows for "otherPrimeInfos", hence at least 9 + meta.public = false; + // rsa pkcs1 private + //return meta; + } else { + throw new Error( + 'not an RSA PKCS#1 public or private key (wrong number of ints)' + ); + } + } else { + meta.format = 'pkcs8'; + } + + var jwk = { kty: 'RSA', n: null, e: null }; + if ('pkcs1' === meta.format) { + return X509.parsePkcs1(asn1, jwk); + } else { + return X509.parsePkcs8(asn1, jwk); + } +};