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