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