      1 /**
      2  *  ---------
      3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
      4  * |#       #|
      5  * |#       #|  Copyright (c) 1999-2009 CardContact Software & System Consulting
      6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
      7  *  ---------
      8  *
      9  *  This file is part of OpenSCDP.
     10  *
     11  *  OpenSCDP is free software; you can redistribute it and/or modify
     12  *  it under the terms of the GNU General Public License version 2 as
     13  *  published by the Free Software Foundation.
     14  *
     15  *  OpenSCDP is distributed in the hope that it will be useful,
     16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *  GNU General Public License for more details.
     19  *
     20  *  You should have received a copy of the GNU General Public License
     21  *  along with OpenSCDP; if not, write to the Free Software
     22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     23  *
     24  * @fileoverview Support for a card verifiable certificates store according to EAC 1.1/2.0 using Data Access Objects
     25  */
     26
     27 // Imports
     28 var CVC = require('scsh/eac/CVC').CVC;
     29 var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference;
     30 var Certificate = require('scsh/pki-db/Certificate').Certificate;
     31 var Holder = require('scsh/pki-db/Holder').Holder;
     32 var PKCS8 = require('scsh/pkcs/PKCS8').PKCS8;
     33
     34
     35
     36 /**
     37  * Create an object to access a certificate store.
     38  *
     39  * @class Class that abstracts a certificate and key store for a EAC PKI.
     40  *
     41  * @constructor
     42  * @param {DAOFactory} DAOFactory the factory that can create data access objects for persistent information
     43  */
     44 function CVCertificateStore(daof) {
     45 	assert(daof, "Parameter doaf can't be empty");
     46
     47 	if (typeof(daof) == "string") {
     48 		var DAOFactoryFileSystem    = require('scsh/pki-db/DAOFactoryFileSystem').DAOFactoryFileSystem;
     49 		daof = new DAOFactoryFileSystem(daof);
     50 	}
     51
     52 	this.daof = daof;
     53 	this.certtype = Holder.CVC;
     54 }
     55
     56 exports.CVCertificateStore = CVCertificateStore;
     57
     58
     59
     60 /**
     61  * Check path
     62  *
     63  * This method validates the path for semantic errors and should be called for input
     64  * validation before using the path in the certificate store API.
     65  *
     66  * @param {String} path the path to validate
     67  */
     68 CVCertificateStore.checkPath = function(path) {
     69 	if ((path.indexOf("/..") >= 0) ||
     70 		(path.indexOf("../") >= 0) ||
     71 		(path.indexOf("\\..") >= 0) ||
     72 		(path.indexOf("..\\") >= 0) ||
     73 		(path.indexOf("\0") >= 0) ||
     74 		(path.indexOf("~") >= 0)) {
     75 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Path \"" + path + "\" contains illegal characters");
     76 	}
     77
     78 	var pa = path.split("/");
     79 	if ((pa.length < 2) || (pa.length > 4)) {
     80 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Path must not contain less than one or more that three elements");
     81 	}
     82 	if (pa[0] != "") {
     83 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Path must start with /");
     84 	}
     85 	for (var i = 1; i < pa.length; i++) {
     86 		if ((pa[i].length < 3) || (pa[i].length > 11)) {
     87 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Path element length must be between 3 and 11");
     88 		}
     89 	}
     90 }
     91
     92
     93
     94 /**
     95  * Strip the last element of the path, effectively defining the parent within the path
     96  *
     97  * @param {String} path the path to strip the last element from
     98  * @returns the parent path or null for the root
     99  * @type String
    100  */
    101 CVCertificateStore.parentPathOf = function(path) {
    102 	var ofs = path.lastIndexOf("/");
    103 	if (ofs <= 0) {
    104 		return null;
    105 	}
    106 	return path.substr(0, ofs);
    107 }
    108
    109
    110
    111 /**
    112  * Return the n-element of the path
    113  *
    114  * @param {String} path the path to return the last element from
    115  * @returns the last path element or null for the root
    116  * @type String
    117  */
    118 CVCertificateStore.nthElementOf = function(path, n) {
    119 	var pe = path.substr(1).split("/");
    120 	if (typeof(n) == "undefined") {
    121 		return pe[pe.length - 1];
    122 	}
    123 	return pe[n];
    124 }
    125
    126
    127
    128 /**
    129  * Set a context marker that goes into the certificate type
    130  *
    131  * The context marker allows to have different certificate stores with different
    132  * namespaces in the same database. The default context marker is 0.
    133  *
    134  * The context marker is stored with the Holder table.
    135  *
    136  * @param {Number} contextMarker the marker
    137  */
    138 CVCertificateStore.prototype.setContextMarker = function(contextMarker) {
    139 	this.certtype = (contextMarker << 4) + Holder.CVC;
    140 }
    141
    142
    143
    144 /**
    145  * Return a suitable crypto object. This may be overwritten by derived classes
    146  *
    147  * @type Crypto
    148  * @return the Crypto object
    149  */
    150 CVCertificateStore.prototype.getCrypto = function() {
    151 	if (this.crypto == undefined) {
    152 		this.crypto = new Crypto();
    153 	}
    154 	return this.crypto;
    155 }
    156
    157
    158
    159 /**
    160  * Check if holder exists for path
    161  *
    162  * @param {String} path
    163  * @type Holder
    164  * @return true if holder exists
    165  * @private
    166  */
    167 CVCertificateStore.prototype.hasHolder = function(path) {
    168 	var holderdao = this.daof.getHolderDAO();
    169
    170 	var holder = holderdao.getHolder(path, this.certtype);
    171 	return holder != null;
    172 }
    173
    174
    175
    176 /**
    177  * List certificate holders for a given PKI element
    178  *
    179  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    180  * @returns a list of holder ids, possibly empty
    181  * @type String[]
    182  */
    183 CVCertificateStore.prototype.listHolders = function(path) {
    184 	GPSystem.log(GPSystem.DEBUG, module.id, "listHolders(" + path + ")");
    185
    186 	var holderdao = this.daof.getHolderDAO();
    187
    188 	var result = holderdao.getHolderList(path, this.certtype);
    189 	return result;
    190 }
    191
    192
    193
    194 /**
    195  * Get existing holder object for given path
    196  *
    197  * @param {String} path
    198  * @param {Boolean} create create if holder doesn't exist
    199  * @type Holder
    200  * @return the holder object
    201  * @private
    202  */
    203 CVCertificateStore.prototype.getHolder = function(path, create) {
    204 	var holderdao = this.daof.getHolderDAO();
    205
    206 	var holder = holderdao.getHolder(path, this.certtype);
    207
    208 	if (!holder) {
    209 		if (!create) {
    210 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate holder for " + path);
    211 		}
    212 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    213 		holder = holderdao.newHolder(path, this.certtype);
    214 	}
    215
    216 	return holder;
    217 }
    218
    219
    220
    221 /**
    222  * Return the current CHR for which a valid certificate exists
    223  *
    224  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    225  * @returns the current CHR for which a certificate exists or null if none exists
    226  * @type PublicKeyReference
    227  */
    228 CVCertificateStore.prototype.getCurrentCHR = function(path) {
    229 	GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentCHR(" + path + ")");
    230
    231 	var holderdao = this.daof.getHolderDAO();
    232
    233 	var holder = holderdao.getHolder(path, this.certtype);
    234
    235 	if (!holder) {
    236 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found");
    237 		return null;
    238 	}
    239
    240 	var certdao = this.daof.getCertificateDAO();
    241
    242 	var certificate = certdao.getCurrentCertificate(holder);
    243 	if (!certificate) {
    244 		GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentCHR(" + path + ") : No current certificate");
    245 		return null;
    246 	}
    247
    248 	var cvc = new CVC(certificate.bytes);
    249 	var chr = cvc.getCHR();
    250 	GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentCHR(" + path + ") : " + chr);
    251 	return chr;
    252 }
    253
    254
    255
    256 /**
    257  * Return the next CHR
    258  *
    259  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    260  * @param {String} countryseq the 2 digit country code to include in the sequence number (optional)
    261  * @returns the next CHR based on the sequence counter maintained in the configuration file
    262  * @type PublicKeyReference
    263  */
    264 CVCertificateStore.prototype.getNextCHR = function(path, countryseq) {
    265 	GPSystem.log(GPSystem.DEBUG, module.id, "getNextCHR(" + path + "," + countryseq + ")");
    266
    267 	var holderdao = this.daof.getHolderDAO();
    268
    269 	var holder = holderdao.getHolder(path, this.certtype);
    270
    271 	if (!holder) {
    272 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    273 		holder = holderdao.newHolder(path, this.certtype);
    274 	}
    275
    276 	holderdao.updateSignerNo(holder, holder.signerNo + 1);
    277
    278 	return this.getCHRForSequenceNumber(path, holder.signerNo, countryseq);
    279 }
    280
    281
    282
    283 /**
    284  * Set the new signer number, if it is larger than the current
    285  *
    286  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    287  * @param {Number} signerNo the new signer number
    288  */
    289 CVCertificateStore.prototype.setSignerNo = function(path, signerNo) {
    290 	GPSystem.log(GPSystem.DEBUG, module.id, "setSignerNo(" + path + "," + signerNo + ")");
    291
    292 	var holderdao = this.daof.getHolderDAO();
    293
    294 	var holder = holderdao.getHolder(path, this.certtype);
    295
    296 	if (!holder) {
    297 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    298 		holder = holderdao.newHolder(path, this.certtype);
    299 	}
    300
    301 	if (signerNo > holder.signerNo) {
    302 		holderdao.updateSignerNo(holder, signerNo);
    303 	}
    304 }
    305
    306
    307
    308 /**
    309  * Generate key pair
    310  *
    311  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    312  * @param {PublicKeyReference} chr the public key reference for this key pair
    313  * @param {Number} algo the key generation algorithm (Crypto.EC or Crypto.RSA)
    314  * @param {Key} prk the private key template
    315  * @param {Key} puk the public key template
    316  */
    317 CVCertificateStore.prototype.generateKeyPair = function(path, chr, algo, prk, puk) {
    318 	GPSystem.log(GPSystem.DEBUG, module.id, "generateKeyPair(" + path + "," + chr + ")");
    319
    320 	var holder = this.getHolder(path, false);
    321
    322 	var crypto = this.getCrypto();
    323
    324 	// Generate key pair
    325 	crypto.generateKeyPair(algo, puk, prk);
    326
    327 	var signerDAO = this.daof.getSignerDAO();
    328 	var signer = signerDAO.newSigner(holder, chr.toString(), chr.getBytes());
    329
    330 	var p8 = PKCS8.encodeKeyUsingPKCS8Format(prk);
    331 	signerDAO.updateSignerKey(signer, p8);
    332 }
    333
    334
    335
    336 /**
    337  * Get a private key in the certificate store
    338  *
    339  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    340  * @param {PublicKeyReference} chr the public key reference for this key
    341  * @returns the private key or null if not found
    342  * @type Key
    343  */
    344 CVCertificateStore.prototype.getPrivateKey = function(path, chr) {
    345 	GPSystem.log(GPSystem.DEBUG, module.id, "getPrivateKey(" + path + "," + chr + ")");
    346
    347 	var holder = this.getHolder(path, false);
    348
    349 	var signerDAO = this.daof.getSignerDAO();
    350 	var signer = signerDAO.getSignerByKeyId(holder, chr.getBytes());
    351
    352 	if (!signer) {
    353 		return null;
    354 	}
    355
    356 	return PKCS8.decodeKeyFromPKCS8Format(signer.keyblob);
    357 }
    358
    359
    360
    361 /**
    362  * Remove private key
    363  *
    364  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    365  * @param {PublicKeyReference} chr the public key reference for this key
    366  * @returns true is deleted
    367  * @type boolean
    368  */
    369 CVCertificateStore.prototype.deletePrivateKey = function(path, chr) {
    370 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteRequest(" + path + "," + chr + ")");
    371
    372 	var holder = this.getHolder(path, true);
    373
    374 	var signerDAO = this.daof.getSignerDAO();
    375 	var signer = signerDAO.getSignerByName(holder, chr.toString());
    376
    377 	if (!signer) {
    378 		return false;
    379 	}
    380
    381 	return signerDAO.deleteSigner(signer);
    382 }
    383
    384
    385
    386 /**
    387  * Create Signer
    388  *
    389  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    390  * @param {PublicKeyReference} chr the public key reference for this key pair
    391  * @param {Number} algo the key generation algorithm (Crypto.EC or Crypto.RSA)
    392  * @param {Key} prk the private key template
    393  * @param {Key} puk the public key template
    394  */
    395 CVCertificateStore.prototype.newSigner = function(path, chr) {
    396 	GPSystem.log(GPSystem.DEBUG, module.id, "newSigner(" + path + "," + chr + ")");
    397
    398 	var holder = this.getHolder(path, false);
    399
    400 	var signerDAO = this.daof.getSignerDAO();
    401 	var signer = signerDAO.newSigner(holder, chr.toString(), chr.getBytes());
    402 }
    403
    404
    405
    406 /**
    407  * Get Signer
    408  *
    409  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    410  * @param {PublicKeyReference} chr the public key reference for this key
    411  * @returns the private key blob
    412  * @type ByteString
    413  */
    414 CVCertificateStore.prototype.getSigner = function(path, chr) {
    415 	GPSystem.log(GPSystem.DEBUG, module.id, "getSigner(" + path + "," + chr + ")");
    416
    417 	if (!this.hasHolder(path)) {
    418 		return null;
    419 	}
    420
    421 	var holder = this.getHolder(path, false);
    422
    423 	var signerDAO = this.daof.getSignerDAO();
    424 	var signer = signerDAO.getSignerByKeyId(holder, chr.getBytes());
    425
    426 	if (!signer) {
    427 		return null;
    428 	}
    429
    430 	var blob = signer.keyblob;
    431 	if (!blob) {
    432 		blob = new ByteString("", HEX);
    433 	}
    434
    435 	return blob;
    436 }
    437
    438
    439
    440 /**
    441  * Remove Signer
    442  *
    443  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    444  * @param {PublicKeyReference} chr the public key reference for this key
    445  * @returns true is deleted
    446  * @type boolean
    447  */
    448 CVCertificateStore.prototype.deleteSigner = function(path, chr) {
    449 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteSigner(" + path + "," + chr + ")");
    450
    451 	var holder = this.getHolder(path, true);
    452
    453 	var signerDAO = this.daof.getSignerDAO();
    454 	var signer = signerDAO.getSignerByName(holder, chr.toString());
    455
    456 	if (!signer) {
    457 		return false;
    458 	}
    459
    460 	return signerDAO.deleteSigner(signer);
    461 }
    462
    463
    464
    465 /**
    466  * Store a certificate request in the certificate store
    467  *
    468  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    469  * @param {CVC} req the request
    470  */
    471 CVCertificateStore.prototype.storeRequest = function(path, req) {
    472 	GPSystem.log(GPSystem.DEBUG, module.id, "storeRequest(" + path + "," + req + ")");
    473
    474 	var chr = req.getCHR();
    475
    476 	var holder = this.getHolder(path, true);
    477
    478 	var requestDAO = this.daof.getRequestDAO();
    479 	requestDAO.newRequest(holder, chr.getBytes(), req.getBytes());
    480 }
    481
    482
    483
    484 /**
    485  * Return request for given CHR
    486  *
    487  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    488  * @param {PublicKeyReference} chr the public key reference for the certificate
    489  * @type CVC
    490  * @return the request or null
    491  */
    492 CVCertificateStore.prototype.getRequest = function(path, chr) {
    493 	GPSystem.log(GPSystem.DEBUG, module.id, "getRequest(" + path + "," + chr + ")");
    494
    495 	var holder = this.getHolder(path, true);
    496
    497 	var requestDAO = this.daof.getRequestDAO();
    498 	var request = requestDAO.getRequestByKeyId(holder, chr.getBytes());
    499
    500 	if (!request) {
    501 		return null;
    502 	}
    503
    504 	var req = new CVC(request.bytes);
    505 	return req;
    506 }
    507
    508
    509
    510 /**
    511  * Remove request
    512  *
    513  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    514  * @param {PublicKeyReference} chr the public key reference for this request
    515  * @returns true is deleted
    516  * @type boolean
    517  */
    518 CVCertificateStore.prototype.deleteRequest = function(path, chr) {
    519 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteRequest(" + path + "," + chr + ")");
    520
    521 	var holder = this.getHolder(path, true);
    522
    523 	var requestDAO = this.daof.getRequestDAO();
    524 	var request = requestDAO.getRequestByKeyId(holder, chr.getBytes());
    525
    526 	if (!request) {
    527 		return false;
    528 	}
    529
    530 	return requestDAO.deleteRequest(request);
    531 }
    532
    533
    534
    535 /**
    536  * Store a certificate in the certificate store
    537  *
    538  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    539  * @param {CVC} cert the certificate
    540  * @param {Boolean} makeCurrent true if this certificate becomes the current certificate
    541  * @param {Boolean} forceUpdate true if certificate should be updated even if one already exists
    542  */
    543 CVCertificateStore.prototype.storeCertificate = function(path, cert, makeCurrent, forceUpdate) {
    544 	GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificate(" + path + ",'" + cert + "'," + makeCurrent + ")");
    545
    546 	var holderdao = this.daof.getHolderDAO();
    547
    548 	var holder = holderdao.getHolder(path, this.certtype);
    549
    550 	if (!holder) {
    551 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    552 		holder = holderdao.newHolder(path, this.certtype);
    553 	}
    554
    555 	var certdao = this.daof.getCertificateDAO();
    556
    557 	var car = cert.getCAR();
    558 	var chr = cert.getCHR();
    559 	var dir = Certificate.UP;
    560
    561 	if (car.equals(chr)) {
    562 		dir = Certificate.SHORT;
    563 	}
    564
    565 	var certificate = certdao.getCertificateBySerial(holder, chr.toString(), dir);
    566
    567 	if (certificate) {
    568 		var ecvc = new CVC(certificate.bytes);
    569
    570 		GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a certificate for that serial number");
    571 		GPSystem.log(GPSystem.INFO, module.id, "Existing: " + ecvc);
    572 		GPSystem.log(GPSystem.INFO, module.id, "New     : " + cert);
    573 		GPSystem.log(GPSystem.INFO, module.id, "Both are " + (cert.getBytes().equals(certificate.bytes) ? "identical" : "different"));
    574
    575 		if (ecvc.getCAR().equals(ecvc.getCHR()) && (dir == Certificate.UP)) {
    576 			GPSystem.log(GPSystem.INFO, module.id, "This is an additional link certificate for an existing root certificate");
    577 		} else {
    578 			if (!forceUpdate) {
    579 				return;
    580 			}
    581 		}
    582 	}
    583
    584 	var template = {
    585 		keyId: chr.getBytes()
    586 	};
    587
    588 	var certificate = certdao.newCertificate(holder, chr.toString(), dir, cert.getBytes(), template);
    589
    590 	if (makeCurrent) {
    591 		holderdao.updateCurrentCertificate(holder, certificate);
    592 	}
    593 }
    594
    595
    596
    597 /**
    598  * Return certificate for a given CHR in binary format
    599  *
    600  * <p>This method returns a self-signed root certificate if the selfsigned
    601  *    parameter is set. If not set or set to false, then matching link certificate,
    602  *    if any, is returned rather than the self-signed certificate.</p>
    603  *
    604  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    605  * @param {PublicKeyReference} chr the public key reference for the certificate
    606  * @param {boolean} selfsigned return the self-signed root certificate rather than a link certificate
    607  * @returns the certificate or null if not found
    608  * @type ByteString
    609  */
    610 CVCertificateStore.prototype.getCertificateBinary = function(path, chr, selfsigned) {
    611 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateBinary(" + path + "," + chr + "," + selfsigned + ")");
    612
    613 	var holderdao = this.daof.getHolderDAO();
    614
    615 	var holder = holderdao.getHolder(path, this.certtype);
    616
    617 	if (!holder) {
    618 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder at " + path + " not found");
    619 		return null;
    620 	}
    621
    622 	var certificateDAO = this.daof.getCertificateDAO();
    623
    624 	var certificate = certificateDAO.getCertificateBySerial(holder, chr.toString(), selfsigned ? Certificate.SHORT : Certificate.UP );
    625
    626 	if (!certificate) {
    627 		GPSystem.log(GPSystem.DEBUG, module.id, "Certificate for " + chr.toString() + " not found");
    628 		return null;
    629 	}
    630
    631 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateBinary:" + certificate.bytes.bytes(0,10).toString(HEX));
    632 	return certificate.bytes;
    633 }
    634
    635
    636
    637 /**
    638  * Return certificate for a given CHR
    639  *
    640  * <p>This method returns a self-signed root certificate if the selfsigned
    641  *    parameter is set. If not set or set to false, then matching link certificate,
    642  *    if any, is returned rather than the self-signed certificate.</p>
    643  *
    644  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    645  * @param {PublicKeyReference} chr the public key reference for the certificate
    646  * @param {boolean} selfsigned return the self-signed root certificate rather than a link certificate
    647  * @returns the certificate or null if not found
    648  * @type CVC
    649  */
    650 CVCertificateStore.prototype.getCertificate = function(path, chr, selfsigned) {
    651 	var bin = this.getCertificateBinary(path, chr, selfsigned);
    652
    653 	if (bin == null) {
    654 		return null;
    655 	}
    656
    657 	var cvc = null;
    658 	try	{
    659 		cvc = new CVC(bin);
    660 	}
    661 	catch (e) {
    662 		GPSystem.log(GPSystem.ERROR, module.id, e);
    663 	}
    664 	return cvc;
    665 }
    666
    667
    668
    669 /**
    670  * Try locating a certificate with the given CHR
    671  *
    672  * This method tries to find a specific certificate in the store, irrespectively of the holder.
    673  *
    674  * @param {PublicKeyReference} chr the public key reference for the certificate
    675  * @returns the certificate or null if none found or more than one found
    676  * @type CVC
    677  */
    678 CVCertificateStore.prototype.locateCertificate = function(chr) {
    679 	GPSystem.log(GPSystem.DEBUG, module.id, "locateCertificate(" + chr + ")");
    680
    681 	var certificateDAO = this.daof.getCertificateDAO();
    682
    683 	if (typeof(certificateDAO.getCertificateBySerialAndType) == "undefined") {
    684 		return null;
    685 	}
    686
    687 	var certificate = certificateDAO.getCertificateBySerialAndType(chr.toString(), this.certtype);
    688
    689 	if (!certificate) {
    690 		GPSystem.log(GPSystem.DEBUG, module.id, "Certificate for " + chr.toString() + " not found or not unique");
    691 		return null;
    692 	}
    693
    694 	cvc = new CVC(certificate.bytes);
    695 	return cvc;
    696 }
    697
    698
    699
    700 /**
    701  * Remove certificate
    702  *
    703  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    704  * @param {PublicKeyReference} chr the public key reference for this certificate
    705  * @param {boolean} selfsigned delete the self-signed root certificate rather than a link certificate
    706  * @returns true is deleted
    707  * @type boolean
    708  */
    709 CVCertificateStore.prototype.deleteCertificate = function(path, chr, selfsigned) {
    710 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteCertificate(" + path + "," + chr + "," + selfsigned + ")");
    711
    712 	var holder = this.getHolder(path, false);
    713
    714 	if (!holder) {
    715 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found");
    716 		return null;
    717 	}
    718
    719 	var certificateDAO = this.daof.getCertificateDAO();
    720
    721 	var certificate = certificateDAO.getCertificateBySerial(holder, chr.toString(), selfsigned ? Certificate.SHORT : Certificate.UP );
    722
    723 	if (!certificate) {
    724 		return false;
    725 	}
    726 	return certificateDAO.deleteCertificate(certificate);
    727 }
    728
    729
    730
    731 /**
    732  * Return a chain of certificates resembling a path from root to end entity.
    733  *
    734  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    735  * @param {PublicKeyReference} tochr the public key reference for the certificate at the end of the chain
    736  * @param {PublicKeyReference} fromcar the public key reference for the certificate to start with or root if undefined
    737  * @returns the list of certificates starting with a self signed root certificate (fromcar undefined) a certificate
    738  *          issued by fromcar up to an including the certificate referenced by tochr. Return null if fromcar is not found.
    739  * @type CVC[]
    740  */
    741 CVCertificateStore.prototype.getCertificateChain = function(path, tochr, fromcar) {
    742 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateChain(" + path + "," + tochr + "," + fromcar + ")");
    743
    744 	var chain = [];
    745 	var chr = tochr;
    746
    747 	while (true) {
    748 		var cvc = this.getCertificate(path, chr, false);
    749 		if (cvc == null) {
    750 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr);
    751 		}
    752 		chain.push(cvc);
    753 		if (typeof(fromcar) == "undefined") {
    754 			if (cvc.getCAR().equals(cvc.getCHR())) {
    755 				break;
    756 			}
    757 		} else {
    758 			if (cvc.getCAR().equals(fromcar)) {
    759 				break;
    760 			}
    761 			if (cvc.getCAR().equals(cvc.getCHR())) {
    762 				return null;	// fromcar not found along the chain
    763 			}
    764 		}
    765 		var ofs = path.lastIndexOf("/");
    766 		if (ofs > 0) {
    767 			path = path.substr(0, ofs);
    768 		}
    769 		chr = cvc.getCAR();
    770 	}
    771
    772 	return chain.reverse();
    773 }
    774
    775
    776
    777 /**
    778  * List certificates stored for given PKI element sorted by CHR
    779  *
    780  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
    781  * @returns a list of certificates, possibly empty
    782  * @type CVC[]
    783  */
    784 CVCertificateStore.prototype.listCertificates = function(path) {
    785 	GPSystem.log(GPSystem.DEBUG, module.id, "listCertificates(" + path + ")");
    786
    787 	var result = [];
    788
    789 	var holder = this.getHolder(path, true);
    790
    791 	var certificateDAO = this.daof.getCertificateDAO();
    792
    793 	var certificates = certificateDAO.enumerateCertificates(holder);
    794
    795 	if (!certificates) {
    796 		GPSystem.log(GPSystem.DEBUG, module.id, "No certificates found");
    797 		return result;
    798 	}
    799
    800 	for (var i = 0; i < certificates.length; i++) {
    801 		var cvc = new CVC(certificates[i].bytes);
    802 		result.push(cvc);
    803 	}
    804
    805 	result.sort(function(a,b) { return a.getCHR().toString() < b.getCHR().toString() ? -1 : (a.getCHR().toString() > b.getCHR().toString() ? 1 : 0) } );
    806 	GPSystem.log(GPSystem.DEBUG, module.id, "listCertificates:" + result.length);
    807 	return result;
    808
    809 }
    810
    811
    812
    813 /**
    814  * Returns the domain parameter for a certificate identified by its CHR
    815  *
    816  * <p>This method traverses the certificate hierachie upwards and follows link certificates
    817  *    until domain parameter are found.</p>
    818  *
    819  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    820  * @param {PublicKeyReference} chr the CHR of the certificate to start the search with
    821  * @return the domain parameter
    822  * @type Key
    823  */
    824 CVCertificateStore.prototype.getDomainParameter = function(path, chr) {
    825 	GPSystem.log(GPSystem.DEBUG, module.id, "getDomainParameter(" + path + "," + chr + ")");
    826
    827 	if (typeof(chr) == "undefined") {	// ToDo remove after migration
    828 		chr = path;
    829 		var path = "/" + chr.getHolder();
    830 	}
    831
    832 	do	{
    833 		var ofs = path.lastIndexOf("/");
    834 		if (ofs > 0) {
    835 			var cvc = this.getCertificate(path, chr);
    836 			if (cvc == null) {
    837 				throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr);
    838 			}
    839 			chr = cvc.getCAR();
    840 			path = path.substr(0, ofs);
    841 		}
    842 	} while (ofs > 0);
    843
    844 	do {
    845 		var cvc = this.getCertificate(path, chr);
    846
    847 		if (cvc == null) {
    848 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr + " in " + path);
    849 		}
    850
    851 		var p = cvc.getPublicKey();
    852 		if (typeof(p.getComponent(Key.ECC_P)) != "undefined") {
    853 			return p;
    854 		}
    855 		chr = cvc.getCAR();
    856 	} while (!chr.equals(cvc.getCHR()));
    857
    858 	throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate CVCA certificate with domain parameter");
    859 }
    860
    861
    862
    863 /**
    864  * Returns the default domain parameter for a given PKI
    865  *
    866  * @param {String} path the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
    867  * @return the domain parameter
    868  * @type Key
    869  */
    870 CVCertificateStore.prototype.getDefaultDomainParameter = function(path) {
    871 	GPSystem.log(GPSystem.DEBUG, module.id, "getDefaultDomainParameter(" + path + ")");
    872
    873 	var pe = path.substr(1).split("/");
    874 	chr = this.getCurrentCHR("/" + pe[0]);
    875 	if (chr == null) {
    876 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
    877 	}
    878 	return this.getDomainParameter(chr);
    879 }
    880
    881
    882
    883 /**
    884  * Returns the default algorithm identifier OID from the most recent link certificate
    885  *
    886  * @param {String} path the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
    887  * @return the algorithm identifier
    888  * @type ByteString
    889  */
    890 CVCertificateStore.prototype.getDefaultPublicKeyOID = function(path) {
    891 	GPSystem.log(GPSystem.DEBUG, module.id, "getDefaultPublicKeyOID(" + path + ")");
    892
    893 	var pe = path.substr(1).split("/");
    894 	chr = this.getCurrentCHR("/" + pe[0]);
    895 	if (chr == null) {
    896 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
    897 	}
    898 	var cvc = this.getCertificate("/" + pe[0], chr);
    899 	if (cvc == null) {
    900 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
    901 	}
    902
    903 	return cvc.getPublicKeyOID();
    904 }
    905
    906
    907
    908 /**
    909  * Encode a three character alpha-numeric sequence number
    910  *
    911  * <p>This function encodes values in the range 0 to 999 as numeric string with leading zeros.</p>
    912  * <p>Value in the range 1000 to 34695 (999 + 26 * 36 * 36) are encoded as alphanumeric string.</p>
    913  * <p>Value beyond 34696 are truncated</p>
    914  *
    915  * @param {Number} value integer sequence value
    916  * @type String
    917  * @return the 3 character string
    918  * @private
    919  */
    920 CVCertificateStore.encodeBase36 = function(value) {
    921 	value = value % (1000 + 26 * 36 * 36);
    922 	var seq;
    923 	if (value < 1000) {
    924 		seq = "" + value;
    925 	} else {
    926 		value += 11960;			10 * 36 * 36 - 1000
    927 		seq = "";
    928 		while(value > 0) {
    929 			var c = value % 36;
    930 			if (c >= 10) {
    931 				c += 55;
    932 			} else {
    933 				c += 48;
    934 			}
    935 			seq = String.fromCharCode(c) + seq;
    936 			value = Math.floor(value / 36);
    937 		}
    938 	}
    939 	seq = "000".substr(0, 3 - seq.length) + seq;
    940 	return seq;
    941 }
    942
    943
    944
    945 /**
    946  * Create a CHR for the given path and sequence number
    947  *
    948  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
    949  * @param {Number} the sequence number to be translated
    950  * @param {String} countryseq the 2 digit country code to include in the sequence number (optional)
    951  * @return the CHR
    952  * @type PublicKeyReference
    953  * @private
    954  */
    955 CVCertificateStore.prototype.getCHRForSequenceNumber = function(path, sequence, countryseq) {
    956 	var pe = path.substr(1).split("/");
    957 	var l = pe[pe.length - 1];
    958
    959 	var str;
    960 	if (countryseq) {
    961 		str = countryseq + CVCertificateStore.encodeBase36(sequence);
    962 	} else {
    963 		str = "" + sequence;
    964 		str = "0000".substr(4 - (5 - str.length)).concat(str);
    965 	}
    966 	return new PublicKeyReference(l + str);
    967 }
    968
    969
    970
    971 /**
    972  * Determine path for certificate issuer
    973  *
    974  * For CVCA and DVCA certificates we can determined the path from the CAR. For Terminal
    975  * certificates we dont know the full path, as we don't know under which CVCA the DVCA operates
    976  * that issued the Terminal certificate. So we use a two-step heuristic which first tries to locate
    977  * the DVCA certificate based on the CAR and if that is not unique uses the cvcahint to determine the path
    978  * of the issuer
    979  */
    980 CVCertificateStore.prototype.getIssuerPathFor = function(cvc, cvcahint) {
    981 	GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor('" + cvc + "'," + cvcahint + ")");
    982
    983 	var car = cvc.getCAR();
    984 	var level = cvc.getLevel();
    985
    986 	if (level != 3) {
    987 		var path = "/" + car.getHolder();
    988 	} else {
    989 		var cacert = this.locateCertificate(car);
    990 		if (cacert) {
    991 			path = "/" + cacert.getCAR().getHolder() + "/" + car.getHolder();
    992 			GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor() using path based on located issuer certificate " + path);
    993 		} else {
    994 			path = "/" + CVCertificateStore.nthElementOf(cvcahint, 0) + "/" + car.getHolder();
    995 			GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor() using path based on hint " + path);
    996 		}
    997 	}
    998
    999 	return path;
    1000 }
    1001
    1002
    1003
    1004 /**
    1005  * Validate a certificate against a certificate already stored in the certificate store
    1006  *
    1007  * <p>If the certificate is a terminal certificate, then the first element of the path given
    1008  *    in cvcahint is used to determine the correct CVCA.</p>
    1009  *
    1010  * Throws an exception if the certificate can not be validated
    1011  *
    1012  * @param {Crypto} crypto the crypto provider to be used for certificate verification
    1013  * @param {CVC} cvc the certificate
    1014  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
    1015  * @returns object containing the path and domain parameter used for validating the certificate. Returns null if no issuer could be found
    1016  * @type Object
    1017  */
    1018 CVCertificateStore.prototype.validateCertificate = function(crypto, cvc, cvcahint) {
    1019 	GPSystem.log(GPSystem.DEBUG, module.id, "validateCertificate('" + cvc + "'," + cvcahint + ")");
    1020
    1021 	var car = cvc.getCAR();
    1022 	var level = cvc.getLevel();
    1023
    1024 	if (car.equals(cvc.getCHR())) { // Self signed
    1025 		if (level != 1) {
    1026 			GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificates only allowed for CVCA: " + cvc);
    1027 			throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Self-signed certificates only allowed for CVCA: " + cvc);
    1028 		}
    1029 		if (!cvc.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID())) {
    1030 			GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificate failed signature verification. " + cvc);
    1031 			throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Self-signed certificate failed signature verification. " + cvc);
    1032 		}
    1033 		return;
    1034 	}
    1035
    1036 	var path = this.getIssuerPathFor(cvc, cvcahint);
    1037 	var cacert = this.getCertificate(path, car);
    1038
    1039 	if (cacert == null) {
    1040 		GPSystem.log(GPSystem.ERROR, module.id, "Can't find issuer " + car);
    1041 		return;
    1042 	}
    1043
    1044 	var oid = cacert.getPublicKeyOID();
    1045 	if (CVC.isECDSA(oid)) {
    1046 		var dp = this.getDomainParameter(path, car);
    1047 	} else {
    1048 		var dp = null;
    1049 	}
    1050 	var result = cvc.verifyWith(crypto, cacert.getPublicKey(dp), oid);
    1051 	if (!result) {
    1052 		GPSystem.log(GPSystem.ERROR, module.id, "Certificate " + cvc + " failed signature verification with " + cacert);
    1053 		throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Certificate " + cvc + " failed signature verification with " + cacert);
    1054 	}
    1055
    1056 	return { path: path + "/" + cvc.getCHR().getHolder(), dp: dp };
    1057 }
    1058
    1059
    1060
    1061 /**
    1062  * Insert a single certificates into the certificate store
    1063  *
    1064  * <p>Before a certificate is imported, the signature is verified.</p>
    1065  * <p>If the certificate is a terminal certificate, then the first element of the path given
    1066  *    in cvcahint is used to determine the correct CVCA.</p>
    1067  *
    1068  * @param {Crypto} crypto the crypto provider to be used for certificate verification
    1069  * @param {CVC} cvc the certificate
    1070  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
    1071  * @param {Boolean} forceUpdate true if certificate should be updated even if one already exists
    1072  * @returns true, if the certificate was inserted
    1073  * @type boolean
    1074  */
    1075 CVCertificateStore.prototype.insertCertificate = function(crypto, cvc, cvcahint, forceUpdate) {
    1076 	GPSystem.log(GPSystem.DEBUG, module.id, "insertCertificate('" + cvc + "'," + cvcahint + ")");
    1077
    1078 	var car = cvc.getCAR();
    1079 	var path = this.getIssuerPathFor(cvc, cvcahint);
    1080
    1081 	var cacert = this.getCertificate(path, car);
    1082 	if (cacert == null) {
    1083 		GPSystem.log(GPSystem.ERROR, module.id, "Can't find issuer " + car);
    1084 		return false;
    1085 	}
    1086
    1087 	if (CVC.isECDSA(cacert.getPublicKeyOID())) {
    1088 		var dp = this.getDomainParameter(path, car);
    1089 	} else {
    1090 		var dp = null;
    1091 	}
    1092 	var result = cvc.verifyWith(crypto, cacert.getPublicKey(dp), cacert.getPublicKeyOID());
    1093 	if (!result) {
    1094 		GPSystem.log(GPSystem.ERROR, module.id, "Certificate " + cvc + " failed signature verification with " + cacert);
    1095 		return false;
    1096 	}
    1097
    1098 	var chr = cvc.getCHR();
    1099 	var holder = chr.getHolder();
    1100
    1101 	if (holder == car.getHolder()) {	// Link certificate
    1102 		this.storeCertificate("/" + holder, cvc, true, forceUpdate);
    1103 	} else {							// Subordinate certificate
    1104 		this.storeCertificate(path + "/" + holder, cvc, true, forceUpdate);
    1105 	}
    1106
    1107 	return true;
    1108 }
    1109
    1110
    1111
    1112 /**
    1113  * Insert certificates into certificate store
    1114  *
    1115  * <p>The import into the internal data structure is done in three steps:</p>
    1116  * <ol>
    1117  *  <li>If allowed, all self-signed certificates are imported</li>
    1118  *  <li>All possible certificate chains are build</li>
    1119  *  <li>Certificate chains are processed starting with the topmost certificate in the hierachie</li>
    1120  * </ol>
    1121  * <p>Certificates at the terminal level can only be imported, if the issuing
    1122  *    DVCA certificate is contained in the list or a hint for the relevant CVCA is
    1123  *    given in the first element of the path contained in parameter cvcahint.</p>
    1124  * <p>Before a certificate is imported, the signature is verified.</p>
    1125  *
    1126  * @param {Crypto} crypto the crypto provider to be used for certificate verification
    1127  * @param {CVC[]} certlist the unordered list of certificates
    1128  * @param {Boolean} insertSelfSigned true, if the import of root certificates is allowed
    1129  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
    1130  * @param {Boolean} forceUpdate true if certificate should be updated even if one already exists
    1131  * @returns the (ideally empty) list of unprocessed certificates. This does not contains certificates
    1132  *          that fail signature verification.
    1133  * @type CVC[]
    1134  */
    1135 CVCertificateStore.prototype.insertCertificates = function(crypto, certlist, insertSelfSigned, cvcahint, forceUpdate) {
    1136 	GPSystem.log(GPSystem.DEBUG, module.id, "insertCertificates('" + certlist + "'," + insertSelfSigned + "," + cvcahint + ")");
    1137
    1138 	var chrmap = [];
    1139
    1140 	// Iterate certificate list and store self-signed certificates, if allowed
    1141 	// Generate a map of certificate holder references
    1142 	var unprocessed = [];
    1143 	for (var i = 0; i < certlist.length; i++) {
    1144 		var cvc = certlist[i];
    1145 		var chr = cvc.getCHR().toString();
    1146
    1147 		if (chr == cvc.getCAR().toString()) { // Self signed
    1148 			var result = cvc.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
    1149
    1150 			if (result) {
    1151 				var path = "/" + cvc.getCHR().getHolder();
    1152 				if (insertSelfSigned) {		// Store self-signed certificates
    1153 					this.storeCertificate(path, cvc, true, forceUpdate);
    1154 				}
    1155 			} else {
    1156 				GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificate failed signature verification. " + cvc);
    1157 			}
    1158 		} else {
    1159 			var state = { cvc: cvc, end: true, stored: false };
    1160 			unprocessed.push(state);
    1161 			if (typeof(chrmap[chr]) == "undefined") {
    1162 				chrmap[chr] = state;
    1163 			} else {
    1164 				// Duplicate CHRs for terminals are allowed
    1165 				chrmap[cvc.getCAR().toString() + "/" + chr] = state;
    1166 			}
    1167 		}
    1168 	}
    1169
    1170 	// Mark certificates that are surely CAs, because an issued certificate is contained in the list
    1171 	certlist = unprocessed;
    1172 	for (var i = 0; i < certlist.length; i++) {
    1173 		var cvc = certlist[i].cvc;
    1174 		var state = chrmap[cvc.getCAR().toString()];
    1175 		if (typeof(state) != "undefined") {
    1176 			GPSystem.log(GPSystem.DEBUG, module.id, "Mark as CA: " + state.cvc);
    1177 			state.end = false;
    1178 		}
    1179 	}
    1180
    1181 	var unprocessed = [];
    1182 	for (var i = 0; i < certlist.length; i++) {
    1183 		var state = certlist[i];
    1184 		if (state.end) {		// Find all certificates which are at the end of the chain
    1185 			var list = [];
    1186 			var lastpathelement = state.cvc.getCHR().getHolder();
    1187 			var path = "/" + lastpathelement;
    1188 			var singlecert = true;
    1189 			while(true)	{		// Build a certificate chain and the path for the last certificate
    1190 				var pathelement = state.cvc.getCAR().getHolder();
    1191 				if (pathelement != lastpathelement) {		// CVCA Link Certificates don't add to the path
    1192 					path = "/" + pathelement + path;
    1193 				}
    1194 				lastpathelement = pathelement;
    1195
    1196 				if (!state.stored) {			// If not already stored, add to the list
    1197 					list.push(state);
    1198 					state.stored = true;
    1199 				}
    1200 				state = chrmap[state.cvc.getCAR().toString()];
    1201 				if (typeof(state) == "undefined") {
    1202 					break;
    1203 				}
    1204 				singlecert = false;
    1205 			}
    1206 			if (singlecert && cvcahint) {
    1207 				GPSystem.log(GPSystem.DEBUG, module.id, "Single certificate might be a terminal certificate, using cvca hint " + cvcahint);
    1208 				path = cvcahint;
    1209 			} else {
    1210 				GPSystem.log(GPSystem.DEBUG, module.id, "Path is " + path);
    1211 			}
    1212 			for (var j = list.length - 1; j >= 0; j--) {	// Process chain in reverse order
    1213 				var cvc = list[j].cvc;
    1214 				if (!this.insertCertificate(crypto, cvc, path, forceUpdate)) {
    1215 					unprocessed.push(cvc);
    1216 				}
    1217 			}
    1218 		}
    1219 	}
    1220
    1221 	return unprocessed;
    1222 }
    1223 // For backward compatibility
    1224 CVCertificateStore.prototype.insertCertificates2 = CVCertificateStore.prototype.insertCertificates;
    1225