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 become the current certificate
541  */
542 CVCertificateStore.prototype.storeCertificate = function(path, cert, makeCurrent) {
543 	GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificate(" + path + ",'" + cert + "'," + makeCurrent + ")");
544 
545 	var holderdao = this.daof.getHolderDAO();
546 
547 	var holder = holderdao.getHolder(path, this.certtype);
548 
549 	if (!holder) {
550 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
551 		holder = holderdao.newHolder(path, this.certtype);
552 	}
553 
554 	var certdao = this.daof.getCertificateDAO();
555 
556 	var car = cert.getCAR();
557 	var chr = cert.getCHR();
558 	var dir = Certificate.UP;
559 
560 	if (car.equals(chr)) {
561 		dir = Certificate.SHORT;
562 	}
563 
564 	var certificate = certdao.getCertificateBySerial(holder, chr.toString(), dir);
565 
566 	if (certificate) {
567 		var ecvc = new CVC(certificate.bytes);
568 
569 		GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a certificate for that serial number");
570 		GPSystem.log(GPSystem.INFO, module.id, "Existing: " + ecvc);
571 		GPSystem.log(GPSystem.INFO, module.id, "New     : " + cert);
572 		GPSystem.log(GPSystem.INFO, module.id, "Both are " + (cert.getBytes().equals(certificate.bytes) ? "identical" : "different"));
573 
574 		if (ecvc.getCAR().equals(ecvc.getCHR()) && (dir == Certificate.UP)) {
575 			GPSystem.log(GPSystem.INFO, module.id, "This is an additional link certificate for an existing root certificate");
576 		} else {
577 			return;
578 		}
579 	}
580 
581 	var template = {
582 		keyId: chr.getBytes()
583 	};
584 
585 	var certificate = certdao.newCertificate(holder, chr.toString(), dir, cert.getBytes(), template);
586 
587 	if (makeCurrent) {
588 		holderdao.updateCurrentCertificate(holder, certificate);
589 	}
590 }
591 
592 
593 
594 /**
595  * Return certificate for a given CHR in binary format
596  *
597  * <p>This method returns a self-signed root certificate if the selfsigned
598  *    parameter is set. If not set or set to false, then matching link certificate,
599  *    if any, is returned rather than the self-signed certificate.</p>
600  *
601  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
602  * @param {PublicKeyReference} chr the public key reference for the certificate
603  * @param {boolean} selfsigned return the self-signed root certificate rather than a link certificate
604  * @returns the certificate or null if not found
605  * @type ByteString
606  */
607 CVCertificateStore.prototype.getCertificateBinary = function(path, chr, selfsigned) {
608 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateBinary(" + path + "," + chr + "," + selfsigned + ")");
609 
610 	var holderdao = this.daof.getHolderDAO();
611 
612 	var holder = holderdao.getHolder(path, this.certtype);
613 
614 	if (!holder) {
615 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder at " + path + " not found");
616 		return null;
617 	}
618 
619 	var certificateDAO = this.daof.getCertificateDAO();
620 
621 	var certificate = certificateDAO.getCertificateBySerial(holder, chr.toString(), selfsigned ? Certificate.SHORT : Certificate.UP );
622 
623 	if (!certificate) {
624 		GPSystem.log(GPSystem.DEBUG, module.id, "Certificate for " + chr.toString() + " not found");
625 		return null;
626 	}
627 
628 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateBinary:" + certificate.bytes.bytes(0,10).toString(HEX));
629 	return certificate.bytes;
630 }
631 
632 
633 
634 /**
635  * Return certificate for a given CHR
636  *
637  * <p>This method returns a self-signed root certificate if the selfsigned
638  *    parameter is set. If not set or set to false, then matching link certificate,
639  *    if any, is returned rather than the self-signed certificate.</p>
640  *
641  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
642  * @param {PublicKeyReference} chr the public key reference for the certificate
643  * @param {boolean} selfsigned return the self-signed root certificate rather than a link certificate
644  * @returns the certificate or null if not found
645  * @type CVC
646  */
647 CVCertificateStore.prototype.getCertificate = function(path, chr, selfsigned) {
648 	var bin = this.getCertificateBinary(path, chr, selfsigned);
649 
650 	if (bin == null) {
651 		return null;
652 	}
653 
654 	var cvc = null;
655 	try	{
656 		cvc = new CVC(bin);
657 	}
658 	catch (e) {
659 		GPSystem.log(GPSystem.ERROR, module.id, e);
660 	}
661 	return cvc;
662 }
663 
664 
665 
666 /**
667  * Try locating a certificate with the given CHR
668  *
669  * This method tries to find a specific certificate in the store, irrespectively of the holder.
670  *
671  * @param {PublicKeyReference} chr the public key reference for the certificate
672  * @returns the certificate or null if none found or more than one found
673  * @type CVC
674  */
675 CVCertificateStore.prototype.locateCertificate = function(chr) {
676 	GPSystem.log(GPSystem.DEBUG, module.id, "locateCertificate(" + chr + ")");
677 
678 	var certificateDAO = this.daof.getCertificateDAO();
679 
680 	if (typeof(certificateDAO.getCertificateBySerialAndType) == "undefined") {
681 		return null;
682 	}
683 
684 	var certificate = certificateDAO.getCertificateBySerialAndType(chr.toString(), this.certtype);
685 
686 	if (!certificate) {
687 		GPSystem.log(GPSystem.DEBUG, module.id, "Certificate for " + chr.toString() + " not found or not unique");
688 		return null;
689 	}
690 
691 	cvc = new CVC(certificate.bytes);
692 	return cvc;
693 }
694 
695 
696 
697 /**
698  * Remove certificate
699  *
700  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
701  * @param {PublicKeyReference} chr the public key reference for this certificate
702  * @param {boolean} selfsigned delete the self-signed root certificate rather than a link certificate
703  * @returns true is deleted
704  * @type boolean
705  */
706 CVCertificateStore.prototype.deleteCertificate = function(path, chr, selfsigned) {
707 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteCertificate(" + path + "," + chr + "," + selfsigned + ")");
708 
709 	var holder = this.getHolder(path, false);
710 
711 	if (!holder) {
712 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found");
713 		return null;
714 	}
715 
716 	var certificateDAO = this.daof.getCertificateDAO();
717 
718 	var certificate = certificateDAO.getCertificateBySerial(holder, chr.toString(), selfsigned ? Certificate.SHORT : Certificate.UP );
719 
720 	if (!certificate) {
721 		return false;
722 	}
723 	return certificateDAO.deleteCertificate(certificate);
724 }
725 
726 
727 
728 /**
729  * Return a chain of certificates resembling a path from root to end entity.
730  *
731  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
732  * @param {PublicKeyReference} tochr the public key reference for the certificate at the end of the chain
733  * @param {PublicKeyReference} fromcar the public key reference for the certificate to start with or root if undefined
734  * @returns the list of certificates starting with a self signed root certificate (fromcar undefined) a certificate
735  *          issued by fromcar up to an including the certificate referenced by tochr. Return null if fromcar is not found.
736  * @type CVC[]
737  */
738 CVCertificateStore.prototype.getCertificateChain = function(path, tochr, fromcar) {
739 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateChain(" + path + "," + tochr + "," + fromcar + ")");
740 
741 	var chain = [];
742 	var chr = tochr;
743 
744 	while (true) {
745 		var cvc = this.getCertificate(path, chr, false);
746 		if (cvc == null) {
747 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr);
748 		}
749 		chain.push(cvc);
750 		if (typeof(fromcar) == "undefined") {
751 			if (cvc.getCAR().equals(cvc.getCHR())) {
752 				break;
753 			}
754 		} else {
755 			if (cvc.getCAR().equals(fromcar)) {
756 				break;
757 			}
758 			if (cvc.getCAR().equals(cvc.getCHR())) {
759 				return null;	// fromcar not found along the chain
760 			}
761 		}
762 		var ofs = path.lastIndexOf("/");
763 		if (ofs > 0) {
764 			path = path.substr(0, ofs);
765 		}
766 		chr = cvc.getCAR();
767 	}
768 
769 	return chain.reverse();
770 }
771 
772 
773 
774 /**
775  * List certificates stored for given PKI element sorted by CHR
776  *
777  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1/UTTERM")
778  * @returns a list of certificates, possibly empty
779  * @type CVC[]
780  */
781 CVCertificateStore.prototype.listCertificates = function(path) {
782 	GPSystem.log(GPSystem.DEBUG, module.id, "listCertificates(" + path + ")");
783 
784 	var result = [];
785 
786 	var holder = this.getHolder(path, true);
787 
788 	var certificateDAO = this.daof.getCertificateDAO();
789 
790 	var certificates = certificateDAO.enumerateCertificates(holder);
791 
792 	if (!certificates) {
793 		GPSystem.log(GPSystem.DEBUG, module.id, "No certificates found");
794 		return result;
795 	}
796 
797 	for (var i = 0; i < certificates.length; i++) {
798 		var cvc = new CVC(certificates[i].bytes);
799 		result.push(cvc);
800 	}
801 
802 	result.sort(function(a,b) { return a.getCHR().toString() < b.getCHR().toString() ? -1 : (a.getCHR().toString() > b.getCHR().toString() ? 1 : 0) } );
803 	GPSystem.log(GPSystem.DEBUG, module.id, "listCertificates:" + result.length);
804 	return result;
805 
806 }
807 
808 
809 
810 /**
811  * Returns the domain parameter for a certificate identified by its CHR
812  *
813  * <p>This method traverses the certificate hierachie upwards and follows link certificates
814  *    until domain parameter are found.</p>
815  *
816  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
817  * @param {PublicKeyReference} chr the CHR of the certificate to start the search with
818  * @return the domain parameter
819  * @type Key
820  */
821 CVCertificateStore.prototype.getDomainParameter = function(path, chr) {
822 	GPSystem.log(GPSystem.DEBUG, module.id, "getDomainParameter(" + path + "," + chr + ")");
823 
824 	if (typeof(chr) == "undefined") {	// ToDo remove after migration
825 		chr = path;
826 		var path = "/" + chr.getHolder();
827 	}
828 
829 	do	{
830 		var ofs = path.lastIndexOf("/");
831 		if (ofs > 0) {
832 			var cvc = this.getCertificate(path, chr);
833 			if (cvc == null) {
834 				throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr);
835 			}
836 			chr = cvc.getCAR();
837 			path = path.substr(0, ofs);
838 		}
839 	} while (ofs > 0);
840 
841 	do {
842 		var cvc = this.getCertificate(path, chr);
843 
844 		if (cvc == null) {
845 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate certificate " + chr + " in " + path);
846 		}
847 
848 		var p = cvc.getPublicKey();
849 		if (typeof(p.getComponent(Key.ECC_P)) != "undefined") {
850 			return p;
851 		}
852 		chr = cvc.getCAR();
853 	} while (!chr.equals(cvc.getCHR()));
854 
855 	throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate CVCA certificate with domain parameter");
856 }
857 
858 
859 
860 /**
861  * Returns the default domain parameter for a given PKI
862  *
863  * @param {String} path the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
864  * @return the domain parameter
865  * @type Key
866  */
867 CVCertificateStore.prototype.getDefaultDomainParameter = function(path) {
868 	GPSystem.log(GPSystem.DEBUG, module.id, "getDefaultDomainParameter(" + path + ")");
869 
870 	var pe = path.substr(1).split("/");
871 	chr = this.getCurrentCHR("/" + pe[0]);
872 	if (chr == null) {
873 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
874 	}
875 	return this.getDomainParameter(chr);
876 }
877 
878 
879 
880 /**
881  * Returns the default algorithm identifier OID from the most recent link certificate
882  *
883  * @param {String} path the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
884  * @return the algorithm identifier
885  * @type ByteString
886  */
887 CVCertificateStore.prototype.getDefaultPublicKeyOID = function(path) {
888 	GPSystem.log(GPSystem.DEBUG, module.id, "getDefaultPublicKeyOID(" + path + ")");
889 
890 	var pe = path.substr(1).split("/");
891 	chr = this.getCurrentCHR("/" + pe[0]);
892 	if (chr == null) {
893 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
894 	}
895 	var cvc = this.getCertificate("/" + pe[0], chr);
896 	if (cvc == null) {
897 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate current CVCA certificate");
898 	}
899 
900 	return cvc.getPublicKeyOID();
901 }
902 
903 
904 
905 /**
906  * Encode a three character alpha-numeric sequence number
907  *
908  * <p>This function encodes values in the range 0 to 999 as numeric string with leading zeros.</p>
909  * <p>Value in the range 1000 to 34695 (999 + 26 * 36 * 36) are encoded as alphanumeric string.</p>
910  * <p>Value beyond 34696 are truncated</p>
911  *
912  * @param {Number} value integer sequence value
913  * @type String
914  * @return the 3 character string
915  * @private
916  */
917 CVCertificateStore.encodeBase36 = function(value) {
918 	value = value % (1000 + 26 * 36 * 36);
919 	var seq;
920 	if (value < 1000) {
921 		seq = "" + value;
922 	} else {
923 		value += 11960;			10 * 36 * 36 - 1000
924 		seq = "";
925 		while(value > 0) {
926 			var c = value % 36;
927 			if (c >= 10) {
928 				c += 55;
929 			} else {
930 				c += 48;
931 			}
932 			seq = String.fromCharCode(c) + seq;
933 			value = Math.floor(value / 36);
934 		}
935 	}
936 	seq = "000".substr(0, 3 - seq.length) + seq;
937 	return seq;
938 }
939 
940 
941 
942 /**
943  * Create a CHR for the given path and sequence number
944  *
945  * @param {String} path the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1")
946  * @param {Number} the sequence number to be translated
947  * @param {String} countryseq the 2 digit country code to include in the sequence number (optional)
948  * @return the CHR
949  * @type PublicKeyReference
950  * @private
951  */
952 CVCertificateStore.prototype.getCHRForSequenceNumber = function(path, sequence, countryseq) {
953 	var pe = path.substr(1).split("/");
954 	var l = pe[pe.length - 1];
955 
956 	var str;
957 	if (countryseq) {
958 		str = countryseq + CVCertificateStore.encodeBase36(sequence);
959 	} else {
960 		str = "" + sequence;
961 		str = "0000".substr(4 - (5 - str.length)).concat(str);
962 	}
963 	return new PublicKeyReference(l + str);
964 }
965 
966 
967 
968 /**
969  * Determine path for certificate issuer
970  *
971  * For CVCA and DVCA certificates we can determined the path from the CAR. For Terminal
972  * certificates we dont know the full path, as we don't know under which CVCA the DVCA operates
973  * that issued the Terminal certificate. So we use a two-step heuristic which first tries to locate
974  * the DVCA certificate based on the CAR and if that is not unique uses the cvcahint to determine the path
975  * of the issuer
976  */
977 CVCertificateStore.prototype.getIssuerPathFor = function(cvc, cvcahint) {
978 	GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor('" + cvc + "'," + cvcahint + ")");
979 
980 	var car = cvc.getCAR();
981 	var level = cvc.getLevel();
982 
983 	if (level != 3) {
984 		var path = "/" + car.getHolder();
985 	} else {
986 		var cacert = this.locateCertificate(car);
987 		if (cacert) {
988 			path = "/" + cacert.getCAR().getHolder() + "/" + car.getHolder();
989 			GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor() using path based on located issuer certificate " + path);
990 		} else {
991 			path = "/" + CVCertificateStore.nthElementOf(cvcahint, 0) + "/" + car.getHolder();
992 			GPSystem.log(GPSystem.DEBUG, module.id, "getIssuerPathFor() using path based on hint " + path);
993 		}
994 	}
995 
996 	return path;
997 }
998 
999 
1000 
1001 /**
1002  * Validate a certificate against a certificate already stored in the certificate store
1003  *
1004  * <p>If the certificate is a terminal certificate, then the first element of the path given
1005  *    in cvcahint is used to determine the correct CVCA.</p>
1006  *
1007  * Throws an exception if the certificate can not be validated
1008  *
1009  * @param {Crypto} crypto the crypto provider to be used for certificate verification
1010  * @param {CVC} cvc the certificate
1011  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
1012  * @returns object containing the path and domain parameter used for validating the certificate. Returns null if no issuer could be found
1013  * @type Object
1014  */
1015 CVCertificateStore.prototype.validateCertificate = function(crypto, cvc, cvcahint) {
1016 	GPSystem.log(GPSystem.DEBUG, module.id, "validateCertificate('" + cvc + "'," + cvcahint + ")");
1017 
1018 	var car = cvc.getCAR();
1019 	var level = cvc.getLevel();
1020 
1021 	if (car.equals(cvc.getCHR())) { // Self signed
1022 		if (level != 1) {
1023 			GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificates only allowed for CVCA: " + cvc);
1024 			throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Self-signed certificates only allowed for CVCA: " + cvc);
1025 		}
1026 		if (!cvc.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID())) {
1027 			GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificate failed signature verification. " + cvc);
1028 			throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Self-signed certificate failed signature verification. " + cvc);
1029 		}
1030 		return;
1031 	}
1032 
1033 	var path = this.getIssuerPathFor(cvc, cvcahint);
1034 	var cacert = this.getCertificate(path, car);
1035 
1036 	if (cacert == null) {
1037 		GPSystem.log(GPSystem.ERROR, module.id, "Can't find issuer " + car);
1038 		return;
1039 	}
1040 
1041 	var oid = cacert.getPublicKeyOID();
1042 	if (CVC.isECDSA(oid)) {
1043 		var dp = this.getDomainParameter(path, car);
1044 	} else {
1045 		var dp = null;
1046 	}
1047 	var result = cvc.verifyWith(crypto, cacert.getPublicKey(dp), oid);
1048 	if (!result) {
1049 		GPSystem.log(GPSystem.ERROR, module.id, "Certificate " + cvc + " failed signature verification with " + cacert);
1050 		throw new GPError(module.id, GPError.CRYPTO_FAILED, 0, "Certificate " + cvc + " failed signature verification with " + cacert);
1051 	}
1052 
1053 	return { path: path + "/" + cvc.getCHR().getHolder(), dp: dp };
1054 }
1055 
1056 
1057 
1058 /**
1059  * Insert a single certificates into the certificate store
1060  *
1061  * <p>Before a certificate is imported, the signature is verified.</p>
1062  * <p>If the certificate is a terminal certificate, then the first element of the path given
1063  *    in cvcahint is used to determine the correct CVCA.</p>
1064  *
1065  * @param {Crypto} crypto the crypto provider to be used for certificate verification
1066  * @param {CVC} cvc the certificate
1067  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
1068  * @returns true, if the certificate was inserted
1069  * @type boolean
1070  */
1071 CVCertificateStore.prototype.insertCertificate = function(crypto, cvc, cvcahint) {
1072 	GPSystem.log(GPSystem.DEBUG, module.id, "insertCertificate('" + cvc + "'," + cvcahint + ")");
1073 
1074 	var car = cvc.getCAR();
1075 	var path = this.getIssuerPathFor(cvc, cvcahint);
1076 
1077 	var cacert = this.getCertificate(path, car);
1078 	if (cacert == null) {
1079 		GPSystem.log(GPSystem.ERROR, module.id, "Can't find issuer " + car);
1080 		return false;
1081 	}
1082 
1083 	if (CVC.isECDSA(cacert.getPublicKeyOID())) {
1084 		var dp = this.getDomainParameter(path, car);
1085 	} else {
1086 		var dp = null;
1087 	}
1088 	var result = cvc.verifyWith(crypto, cacert.getPublicKey(dp), cacert.getPublicKeyOID());
1089 	if (!result) {
1090 		GPSystem.log(GPSystem.ERROR, module.id, "Certificate " + cvc + " failed signature verification with " + cacert);
1091 		return false;
1092 	}
1093 
1094 	var chr = cvc.getCHR();
1095 	var holder = chr.getHolder();
1096 
1097 	if (holder == car.getHolder()) {	// Link certificate
1098 		this.storeCertificate("/" + holder, cvc, true);
1099 	} else {							// Subordinate certificate
1100 		this.storeCertificate(path + "/" + holder, cvc, true);
1101 	}
1102 
1103 	return true;
1104 }
1105 
1106 
1107 
1108 /**
1109  * Insert certificates into certificate store
1110  *
1111  * <p>The import into the internal data structure is done in three steps:</p>
1112  * <ol>
1113  *  <li>If allowed, all self-signed certificates are imported</li>
1114  *  <li>All possible certificate chains are build</li>
1115  *  <li>Certificate chains are processed starting with the topmost certificate in the hierachie</li>
1116  * </ol>
1117  * <p>Certificates at the terminal level can only be imported, if the issuing
1118  *    DVCA certificate is contained in the list or a hint for the relevant CVCA is
1119  *    given in the first element of the path contained in parameter cvcahint.</p>
1120  * <p>Before a certificate is imported, the signature is verified.</p>
1121  *
1122  * @param {Crypto} crypto the crypto provider to be used for certificate verification
1123  * @param {CVC[]} certlist the unordered list of certificates
1124  * @param {Boolean} insertSelfSigned true, if the import of root certificates is allowed
1125  * @param {String} cvcahint the PKI path (e.g. "/UTCVCA1/UTDVCA1/UTTERM"). Only the first path element is relevant
1126  * @returns the (ideally empty) list of unprocessed certificates. This does not contains certificates
1127  *          that fail signature verification.
1128  * @type CVC[]
1129  */
1130 CVCertificateStore.prototype.insertCertificates = function(crypto, certlist, insertSelfSigned, cvcahint) {
1131 	GPSystem.log(GPSystem.DEBUG, module.id, "insertCertificates('" + certlist + "'," + insertSelfSigned + "," + cvcahint + ")");
1132 
1133 	var chrmap = [];
1134 
1135 	// Iterate certificate list and store self-signed certificates, if allowed
1136 	// Generate a map of certificate holder references
1137 	var unprocessed = [];
1138 	for (var i = 0; i < certlist.length; i++) {
1139 		var cvc = certlist[i];
1140 		var chr = cvc.getCHR().toString();
1141 
1142 		if (chr == cvc.getCAR().toString()) { // Self signed
1143 			var result = cvc.verifyWith(crypto, cvc.getPublicKey(), cvc.getPublicKeyOID());
1144 
1145 			if (result) {
1146 				var path = "/" + cvc.getCHR().getHolder();
1147 				if (insertSelfSigned) {		// Store self-signed certificates
1148 					this.storeCertificate(path, cvc, true);
1149 				}
1150 			} else {
1151 				GPSystem.log(GPSystem.ERROR, module.id, "Self-signed certificate failed signature verification. " + cvc);
1152 			}
1153 		} else {
1154 			var state = { cvc: cvc, end: true, stored: false };
1155 			unprocessed.push(state);
1156 			if (typeof(chrmap[chr]) == "undefined") {
1157 				chrmap[chr] = state;
1158 			} else {
1159 				// Duplicate CHRs for terminals are allowed
1160 				chrmap[cvc.getCAR().toString() + "/" + chr] = state;
1161 			}
1162 		}
1163 	}
1164 
1165 	// Mark certificates that are surely CAs, because an issued certificate is contained in the list
1166 	certlist = unprocessed;
1167 	for (var i = 0; i < certlist.length; i++) {
1168 		var cvc = certlist[i].cvc;
1169 		var state = chrmap[cvc.getCAR().toString()];
1170 		if (typeof(state) != "undefined") {
1171 			GPSystem.log(GPSystem.DEBUG, module.id, "Mark as CA: " + state.cvc);
1172 			state.end = false;
1173 		}
1174 	}
1175 
1176 	var unprocessed = [];
1177 	for (var i = 0; i < certlist.length; i++) {
1178 		var state = certlist[i];
1179 		if (state.end) {		// Find all certificates which are at the end of the chain
1180 			var list = [];
1181 			var lastpathelement = state.cvc.getCHR().getHolder();
1182 			var path = "/" + lastpathelement;
1183 			var singlecert = true;
1184 			while(true)	{		// Build a certificate chain and the path for the last certificate
1185 				var pathelement = state.cvc.getCAR().getHolder();
1186 				if (pathelement != lastpathelement) {		// CVCA Link Certificates don't add to the path
1187 					path = "/" + pathelement + path;
1188 				}
1189 				lastpathelement = pathelement;
1190 
1191 				if (!state.stored) {			// If not already stored, add to the list
1192 					list.push(state);
1193 					state.stored = true;
1194 				}
1195 				state = chrmap[state.cvc.getCAR().toString()];
1196 				if (typeof(state) == "undefined") {
1197 					break;
1198 				}
1199 				singlecert = false;
1200 			}
1201 			if (singlecert && cvcahint) {
1202 				GPSystem.log(GPSystem.DEBUG, module.id, "Single certificate might be a terminal certificate, using cvca hint " + cvcahint);
1203 				path = cvcahint;
1204 			} else {
1205 				GPSystem.log(GPSystem.DEBUG, module.id, "Path is " + path);
1206 			}
1207 			for (var j = list.length - 1; j >= 0; j--) {	// Process chain in reverse order
1208 				var cvc = list[j].cvc;
1209 				if (!this.insertCertificate(crypto, cvc, path)) {
1210 					unprocessed.push(cvc);
1211 				}
1212 			}
1213 		}
1214 	}
1215 
1216 	return unprocessed;
1217 }
1218 // For backward compatibility
1219 CVCertificateStore.prototype.insertCertificates2 = CVCertificateStore.prototype.insertCertificates;
1220