/** * @param {Object} z */ function supplyZgaCertsChain(z){ //Only for nodejs Start// if(z.forge){ var forge = z.forge; } //Only for nodejs End// /** * When converting the issuer of signature to asn1, forge will encode the * value of issuer to utf8 if the valueTagClass is UTF8. * But the value load from a file which is DER format, is already utf8 encoded, * so the encoding action will break the final data. * To avoid the broken data issue, we decode the value before the later actions. * @param {forge_cert} cert */ z.fixCertAttributes = function(cert){ cert.issuer.attributes.forEach(function(a_ele){ if(a_ele.valueTagClass === forge.asn1.Type.UTF8){ a_ele.value = forge.util.decodeUtf8(a_ele.value); } }); }; /** * @param {forge.asn1|string} asnder * @return {forge_cert} */ z.loadCert = function(asnder){ /** @type {forge.asn1} */ var asn1 = null; if(typeof asnder === "string"){ asn1 = forge.asn1.fromDer(asnder); }else{ asn1 = asnder; } /** @type {forge_cert} */ var cert = forge.pki.certificateFromAsn1(asn1); return cert; }; /** * @param {string} oid * @return {string} */ z.oidToDstr = function(oid){ return forge.asn1.oidToDer(oid).getBytes(); }; /** * @param {forge.asn1} asn1 * @param {Object} vadt * @param {string|Array=} oid * @return {Object} */ z.parseAsn1 = function(asn1, vadt, oid){ /** @type {Object} */ var capt = {}; /** @type {Array} */ var errs = []; forge.asn1.validate(asn1, vadt, capt, errs); if(errs.length > 0){ // console.log(errs); return null; }else if(oid){ if(capt.oid){ if(Array.isArray(oid)){ if(oid.indexOf(capt.oid) >= 0){ return capt; }else{ return null; } }else if(oid == capt.oid){ return capt; }else{ return null; } }else{ return null; } }else{ return capt; } }; /** * @param {string|number|boolean|forge.asn1|Array} aval * @param {number=} atyp * @param {number=} atag * @return {forge.asn1} */ z.createAsn1 = function(aval, atyp, atag){ /** @lends {forge.asn1} */ const asn1 = forge.asn1; /** @lends {forge.asn1.Class} */ const asnc = asn1.Class; /** @lends {forge.asn1.Type} */ const asnt = asn1.Type; /** @type {string|number|forge.asn1|Array} */ var a_val = null; /** @type {number} */ var a_typ = (atyp || atyp === asnt.NONE) ? atyp : -1; /** @type {boolean} */ var a_con = false; if(Array.isArray(aval)){ a_con = true; }else{ switch(typeof aval){ case "string": if(a_typ == asnt.OID){ a_val = asn1.oidToDer(aval).getBytes(); }else if(a_typ < 0){ a_typ = asnt.OCTETSTRING; } break; case "number": a_val = asn1.integerToDer(aval).getBytes(); if(a_typ < 0){ a_typ = asnt.INTEGER; } break; case "boolean": if(aval){ a_val = 1; }else{ a_val = 0; } if(a_typ < 0){ a_typ = asnt.BOOLEAN; } } } if(a_typ < 0){ a_typ = asnt.SEQUENCE; } if(!a_val && a_val !== 0){ a_val = /** @type {Array|forge.asn1|number|string} */(aval); } return asn1.create(atag ? atag : asnc.UNIVERSAL, a_typ, a_con, a_val); }; /** * @param {string} url * @param {forge.asn1} asn1Req * @param {Object} headers * @return {Promise} */ z.queryAsn1 = async function(url, asn1Req, headers){ /** @type {string} */ var tsr = forge.asn1.toDer(asn1Req).getBytes(); /** @type {Uint8Array} */ var tu8s = z.rawToU8arr(tsr); /** @type {UrlFetchParams} */ var options = { "method": "POST", "headers": headers, "payload": tu8s, }; /** @type {Uint8Array} */ var tesp = await z.urlFetch(url, options); // /** @type {string} */ // var tstr = z.u8arrToRaw(tesp); return tesp; }; /** * @param {forge_cert} cert1 * @param {forge_cert} cert2 * @return {boolean} */ z.sameCert = function(cert1, cert2){ /** @type {forge_cert_issuer} */ var c1 = cert1.subject; /** @type {forge_cert_issuer} */ var c2 = cert2.subject; if(c1.attributes.length === c2.attributes.length) { // all attributes are the same so issuer matches subject /** @type {?forge_cert_attr} */ var attr1 = null; /** @type {?forge_cert_attr} */ var attr2 = null; for(var n = 0; n < c1.attributes.length; n++) { attr1 = c1.attributes[n]; attr2 = c2.attributes[n]; if(attr1.type !== attr2.type || attr1.value !== attr2.value){ // attribute mismatch return false; } } return true; }else{ return false; } }; z.CertsChain = class{ /** * @param {Array=} certs */ constructor(certs){ /** @private @lends {forge.asn1} */ this.asn1 = forge.asn1; /** @private @lends {forge.asn1.Class} */ this.asnc = forge.asn1.Class; /** @private @lends {forge.asn1.Type} */ this.asnt = forge.asn1.Type; /** @private @const {string} */ this.ocsp_oid = z.oidToDstr("1.3.6.1.5.5.7.48.1"); /** @private @const {string} */ this.issuer_oid = z.oidToDstr("1.3.6.1.5.5.7.48.2"); /** @private @const {string} */ this.ocspBasic_oid = z.oidToDstr("1.3.6.1.5.5.7.48.1.1"); /** @private @const {Object} */ this.oidval_validator = { name: "Seq", tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, value: [{ name: "Oid", tagClass: this.asnc.UNIVERSAL, type: this.asnt.OID, constructed: false, capture: "oid", }, { name: "Ovalue", tagClass: this.asnc.CONTEXT_SPECIFIC, type: 6, constructed: false, capture: "oval", }], }; /** @private @const {Object} */ this.oresp_validator = { tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, value: [{ name: "Status", tagClass: this.asnc.UNIVERSAL, type: this.asnt.ENUMERATED, constructed: false, capture: "status", }, { tagClass: this.asnc.CONTEXT_SPECIFIC, type: 0, constructed: true, value: [{ tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, value: [{ name: "Oid", tagClass: this.asnc.UNIVERSAL, type: this.asnt.OID, constructed: false, capture: "oid", }, { name: "OcspBasic", tagClass: this.asnc.UNIVERSAL, type: this.asnt.OCTETSTRING, constructed: false, capture: "ob", }], }], }], }; /** @private @const {Object} */ this.ob_validator = { tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, value: [{ tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, }, { tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, }, { tagClass: this.asnc.UNIVERSAL, type: this.asnt.BITSTRING, constructed: false, }, { tagClass: this.asnc.CONTEXT_SPECIFIC, type: 0, constructed: true, optional: true, value: [{ name: "Certs", tagClass: this.asnc.UNIVERSAL, type: this.asnt.SEQUENCE, constructed: true, capture: "certs", }], }], }; /** @private @type {Array} */ this.certs = null; /** @private @type {forge_cert} */ this.rootCert = null; /** @private @type {Array} */ this.crls = null; /** @private @type {Array} */ this.ocspDatas = null; /** @private @type {Array} */ this.extraChains = null; /** @private @type {boolean} */ this.ocspOk = false; if(certs){ this.setCerts(certs); } } /** * @public * @param {Array} certs */ setCerts(certs){ /** @type {z.CertsChain} */ var _this = this; /** @type {Array} */ var fcertarr = []; _this.certs = []; /** @type {forge_cert} */ var wkcert = null; certs.forEach(function(/** @type {forge_cert|forge.asn1|string} */a_data){ if(a_data.issuer){ wkcert = /** @type {forge_cert} */(a_data); }else{ wkcert = z.loadCert(/** @type {forge.asn1|string} */(a_data)); } if(wkcert.isIssuer(wkcert)){ // A selfSigned cert is a root cert. _this.rootCert = wkcert; }else{ /** @type {forge_cert_extension} */ var bext = wkcert.getExtension("basicConstraints"); if(bext && bext.cA){ fcertarr.push(wkcert); }else{ _this.certs.push(wkcert); } } }); /** @type {number} */ var cnt = fcertarr.length; if(_this.certs.length == 1){ wkcert = _this.certs[0]; while(cnt > 0){ for(var j=0; j 0){ _this.certs.push(fcertarr.pop()); cnt--; while(cnt > 0){ for(var j=0; j 0){ return this.certs[0]; }else{ return this.rootCert; } } /** * @public * @return {boolean} */ isSelfSignedCert(){ return (this.certs.length == 0); } /** * @public * @return {Array} */ getAllCerts(){ /** @type {Array} */ var ret = [].concat(this.certs); if(this.rootCert){ ret.push(this.rootCert); } return ret; } /** * @public * @param {forge_cert} cert * @return {boolean} */ isCertInChain(cert){ return this.isCertInArray(this.getAllCerts(), cert); } /** * @public * @param {forge_cert} cert * @return {Promise} */ async buildChain(cert){ /** @type {z.CertsChain} */ var _this = this; if(cert.isIssuer(cert)){ _this.certs = []; _this.rootCert = cert; }else{ _this.certs = [cert]; _this.rootCert = null; } return /** @type {boolean} */(await _this.amendRootCert()); } /** * @public * @param {boolean=} crlOnly * @return {Promise} */ async prepareDSSInf(crlOnly){ /** @type {z.CertsChain} */ var _this = this; var flg = /** @type {boolean} */(await _this.amendRootCert()); if(!flg){ throw new Error("Can't prepare DSS infomation because of unable to find the root certificate."); } if(crlOnly){ flg = false; }else{ _this.ocspDatas = []; _this.extraChains = []; flg = await _this.checkAllOcsps(_this.extraChains); } /** @type {Array} */ var ocsps = []; /** @type {Array} */ var certs = []; if(flg){ _this.ocspDatas.forEach(function(/** @type {OcspData} */a_dat){ ocsps.push(a_dat.resp); }); _this.extraChains.forEach(function(/** @type {z.CertsChain} */a_chain){ a_chain.ocspDatas.forEach(function(/** @type {OcspData} */b_dat){ ocsps.push(b_dat.resp); }); a_chain.certs.forEach(function(/** @type {forge_cert} */b_cert){ if(!(_this.isCertInArray(_this.certs, b_cert) || _this.isCertInArray(certs, b_cert))){ certs.push(b_cert); } }); }); } /** @type {Array} */ var crls = []; if(!flg){ _this.crls = []; for(var i=0; i<_this.certs.length; i++){ /** @type {forge.asn1} */ var cdpts = _this.getExtAsn1(_this.certs[i], "cRLDistributionPoints"); //"2.5.29.31" if(cdpts){ _this.findCrls(cdpts); } } for(var i=0; i<_this.crls.length; i++){ z.log("Query crl for [" + _this.crls[i] + "]"); /** @type {Uint8Array} */ var crl = await z.urlFetch(_this.crls[i]); if(crl){ crls.push(crl); } } } /** @type {DSSInfo} */ var ret = {}; if(certs.length > 0){ ret.certs = certs; } if(ocsps.length > 0){ ret.ocsps = ocsps; } if(crls.length > 0){ ret.crls = crls; } return ret; } /** * @private * @return {Promise} */ async amendRootCert(){ while(!this.rootCert){ /** @type {forge_cert} */ var cert = await this.queryIssuerCert(-1); if(!cert){ return false; } } return true; } /** * @private * @param {forge_cert|number} certIdx * @return {Promise} */ async queryIssuerCert(certIdx){ /** @type {z.CertsChain} */ var _this = this; /** @type {forge_cert} */ var cert = null; if(typeof certIdx === "number"){ if(certIdx < 0){ cert = _this.certs[_this.certs.length - 1]; }else{ cert = _this.certs[certIdx]; } }else{ cert = /** @type {forge_cert} */(certIdx); } /** @type {forge.asn1} */ var aiass = _this.getExtAsn1(cert, "authorityInfoAccess"); if(!aiass){ return null; } /** @type {string} */ var issuerUrl = _this.findOidValue(aiass, _this.issuer_oid); if(!issuerUrl){ return null; } z.log("Query certificate of [" + issuerUrl + "]"); /** @type {Uint8Array} */ var u8cert = await z.urlFetch(issuerUrl); if(!u8cert){ return null; } cert = z.loadCert(z.u8arrToRaw(u8cert)); if(typeof certIdx === "number"){ if(cert.isIssuer(cert)){ _this.rootCert = cert; }else if(certIdx < 0){ _this.certs.push(cert); }else{ _this.certs[certIdx + 1] = cert; } } return cert; } /** * @private * @param {Array} xchains * @return {Promise} */ async checkAllOcsps(xchains){ /** @type {z.CertsChain} */ var _this = this; /** @type {number} */ var i = 0; /** @type {boolean} */ var ret = (_this.certs.length > 0); for(i=0; i<_this.certs.length; i++){ if(!(await _this.queryOcsp(i, xchains))){ ret = false; break; } } _this.ocspOk = ret; return ret; } /** * @private * @param {number} idx * @param {Array} xchains * @return {Promise} */ async queryOcsp(idx, xchains){ /** @type {z.CertsChain} */ var _this = this; /** @type {forge_cert} */ var cert = _this.certs[idx]; z.log("Query ocsp for [" + cert.subject.getField("CN").value + "]"); /** @type {string} */ var ocspUrl = ""; /** @type {forge.asn1} */ var aiass = _this.getExtAsn1(cert, "authorityInfoAccess"); //"1.3.6.1.5.5.7.1.1" if(aiass){ ocspUrl = _this.findOidValue(aiass, _this.ocsp_oid); } if(!ocspUrl){ z.log("Can't find the url of ocsp."); return false; } /** @type {string} */ var serialNum = z.u8arrToRaw(forge.util.binary.hex.decode(cert.serialNumber)); /** @type {number} */ var pidx = idx + 1; /** @type {forge_cert} *///parent cert(issuer cert) var pcert = pidx < _this.certs.length ? _this.certs[pidx] : _this.rootCert; /** @type {forge.asn1} */ var subjectAsn1 = forge.pki.distinguishedNameToAsn1(pcert.subject); /** @type {forge.asn1} */ var publicKeyAsn1 = forge.pki.publicKeyToRSAPublicKey(pcert.publicKey); /** @type {string} */ var dnsha1 = _this.sha1(subjectAsn1); /** @type {string} */ var keysha1 = _this.sha1(publicKeyAsn1); /** @type {string} */ var nonce = forge.random.getBytesSync(16); nonce = _this.asn1.toDer(z.createAsn1(nonce)).getBytes(); /** @type {forge.asn1} */ var asn1Req = z.createAsn1([ z.createAsn1([ z.createAsn1([ z.createAsn1([ z.createAsn1([ z.createAsn1([ z.createAsn1(forge.oids.sha1, _this.asnt.OID), z.createAsn1("", _this.asnt.NULL), ]), z.createAsn1(dnsha1), z.createAsn1(keysha1), z.createAsn1(serialNum, _this.asnt.INTEGER), ]) ]) ]), z.createAsn1([ z.createAsn1([ z.createAsn1([ z.createAsn1("1.3.6.1.5.5.7.48.1.2", _this.asnt.OID), //OCSP Nonce z.createAsn1(nonce), ]) ]) ], 2, _this.asnc.CONTEXT_SPECIFIC) ]) ]); /** @type {Object} */ var hds = { "Content-Type": "application/ocsp-request", }; /** @type {Uint8Array} */ var ou8arr = await z.queryAsn1(ocspUrl, asn1Req, hds); // console.log(forge.util.createBuffer(ou8arr).toHex()); /** @type {forge.asn1} */ var asn1Oresp = _this.asn1.fromDer(z.u8arrToRaw(ou8arr)); /** @type {Object} */ var ocapt = z.parseAsn1(asn1Oresp, _this.oresp_validator, _this.ocspBasic_oid); if(ocapt){ /** @type {number} */ var respsts = ocapt.status.charCodeAt(0); if(respsts != 0){ /** @type {string} */ var msg = "ocsp response is not successful. "; switch(respsts){ case 1: msg += "malformedRequest"; break; case 2: msg += "internalError"; break; case 3: msg += "tryLater"; break; case 5: msg += "sigRequired"; break; case 6: msg += "unauthorized"; break; default: msg += "unknown error"; } msg += "(" + respsts + ")"; z.log(msg); return false; } }else{ z.log("ocsp response is an unknown format."); return false; } if(ocapt.ob){ /** @type {forge.asn1} */ var asn1Obasic = _this.asn1.fromDer(ocapt.ob); /** @type {Object} */ var obcapt = z.parseAsn1(asn1Obasic, _this.ob_validator); if(obcapt){ _this.ocspDatas[idx] = { resp: ou8arr, }; if(obcapt.certs){ /** @type {z.CertsChain} */ var xchain = new z.CertsChain(obcapt.certs); /** @type {number} */ var i = 0; for(i=0; i} */ var capt = z.parseAsn1(asn1, _this.oidval_validator, oid); if(capt && capt.oval){ return capt.oval; } var oval = ""; if(Array.isArray(asn1.value)){ for(var i=0; i} certs * @param {forge_cert} cert * @return {boolean} */ isCertInArray(certs, cert){ /** @type {number} */ var i = 0; for(i=0; i|undefined} */ this.headers = inf.headers; /** @private @type {forge.asn1} */ this.respAsn1 = null; } /** * @public * @param {string=} data * @return {Promise} Error message */ async queryTsa(data){ /** @type {z.TsaFetcher} */ var _this = this; // Generate SHA256 hash from data for TSA /** @type {forge.md.digest} */ var md = forge.md.sha256.create(); md.update(data); // Generate TSA request /** @type {forge.asn1} */ var asn1Req = z.createAsn1([ // Version z.createAsn1(1), z.createAsn1([ z.createAsn1([ z.createAsn1(forge.oids.sha256, _this.asnt.OID), z.createAsn1("", _this.asnt.NULL), ]), // Message imprint z.createAsn1(md.digest().getBytes()), ]), // Get REQ certificates z.createAsn1(true), ]); /** @type {Object} */ var hds = _this.headers ? _this.headers : {}; if(!hds["Content-Type"]){ hds["Content-Type"] = "application/timestamp-query"; } /** @type {Uint8Array} */ var tu8arr = await z.queryAsn1(_this.url, asn1Req, hds); /** @type {string} */ var tstr = z.u8arrToRaw(tu8arr); // console.log(forge.util.createBuffer(tstr).toHex()); _this.respAsn1 = _this.asn1.fromDer(tstr); /** @type {string} */ var respsts = _this.respAsn1.value[0].value[0].value; if(respsts == "\x00"){ return ""; }else{ /** @type {forge.asn1} */ var msgAsn1 = _this.respAsn1.value[0].value[1].value[0]; var msg = /** @type {string} */(msgAsn1.value); if(msgAsn1.type == _this.asnt.UTF8){ msg = forge.util.decodeUtf8(msg); } return msg + "(" + respsts.charCodeAt(0) + ")"; } } /** * @public * @param {boolean=} forP7 * @return {forge.asn1} */ getToken(forP7){ /** @type {z.TsaFetcher} */ var _this = this; /** @type {forge.asn1} */ var token = _this.respAsn1.value[1]; if(forP7){ // create the asn1 to append to the p7 signature return z.createAsn1([ z.createAsn1([ // Attribute Type (forge.pki.oids.timeStampToken) z.createAsn1("1.2.840.113549.1.9.16.2.14", _this.asnt.OID), // Attribute Value z.createAsn1([token], _this.asnt.SET), ]), ], 1, _this.asnc.CONTEXT_SPECIFIC); }else{ return token; } } /** * @public * @return {z.CertsChain} */ getCertsChain(){ /** @type {z.TsaFetcher} */ var _this = this; if(!_this.respAsn1){ throw new Error("You must query tsa first."); } var tsdats = /** @type {Array} */(_this.respAsn1.value[1].value[1].value[0].value); // Get all certs in tsa token. /** @type {Array} */ var certAsn1s = null; for(var i=3; i} */ var capt = z.parseAsn1(tsdat, { name: "Certs", tagClass: _this.asnc.CONTEXT_SPECIFIC, type: _this.asnt.NONE, constructed: true, capture: "certs", }); if(capt){ certAsn1s = capt.certs; break; } } if(certAsn1s){ return new z.CertsChain(certAsn1s); }else{ return null; } } }; } //Only for nodejs Start// if(typeof exports === "object" && typeof module !== "undefined"){ module.exports = supplyZgaCertsChain; } //Only for nodejs End//