      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 Generic X509 certificate, request and key store
     25  */
     26
     27 var PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon;
     28 var Certificate = require('scsh/pki-db/Certificate').Certificate;
     29 var Holder = require('scsh/pki-db/Holder').Holder;
     30 var LongDate = require('scsh/pki-db/LongDate').LongDate;
     31 var PKCS8 = require('scsh/pkcs/PKCS8').PKCS8;
     32 var PKCS10 = require('scsh/pkcs/PKCS10').PKCS10;
     33
     34
     35 /**
     36  * Create an object to access a X509 certificate store.
     37  *
     38  * @class Class that abstracts a certificate, request and key store for a X509 PKI.
     39  *
     40  * @constructor
     41  * @param {DAOFactory} DAOFactory the factory that can create data access objects for persistent information
     42  */
     43 function X509CertificateStore(daof) {
     44 	assert(daof, "Parameter doaf can't be empty");
     45
     46 	this.daof = daof;
     47 	this.certtype = Holder.X509;
     48 }
     49
     50 exports.X509CertificateStore = X509CertificateStore;
     51
     52
     53
     54 /**
     55  * Strip the last element of the path, effectively defining the parent within the path
     56  *
     57  * @param {String} path the path to strip the last element from
     58  * @returns the parent path or null for the root
     59  * @type String
     60  */
     61 X509CertificateStore.parentPathOf = function(path) {
     62 	assert(typeof(path) == "string", "Argument path must be string");
     63 	var ofs = path.lastIndexOf("/");
     64 	if (ofs <= 0) {
     65 		return null;
     66 	}
     67 	return path.substr(0, ofs);
     68 }
     69
     70
     71
     72 /**
     73  * Return the n-element of the path
     74  *
     75  * @param {String} path the path to return the last element from
     76  * @returns the last path element or null for the root
     77  * @type String
     78  */
     79 X509CertificateStore.nthElementOf = function(path, n) {
     80 	assert(typeof(path) == "string", "Argument path must be string");
     81 	var pe = path.substr(1).split("/");
     82 	if (typeof(n) == "undefined") {
     83 		return pe[pe.length - 1];
     84 	}
     85 	return pe[n];
     86 }
     87
     88
     89
     90 /**
     91  * Return a suitable crypto object. This may be overwritten by derived classes
     92  *
     93  * @type Crypto
     94  * @return the Crypto object
     95  */
     96 X509CertificateStore.prototype.getCrypto = function() {
     97 	if (this.crypto == undefined) {
     98 		this.crypto = new Crypto();
     99 	}
    100 	return this.crypto;
    101 }
    102
    103
    104
    105 /**
    106  * Check if holder exists for path or holderId
    107  *
    108  * @param {String/Number} pathOrHolderId
    109  * @type Holder
    110  * @return true if holder exists
    111  * @private
    112  */
    113 X509CertificateStore.prototype.hasHolder = function(pathOrHolderId) {
    114 	var holderdao = this.daof.getHolderDAO();
    115
    116 	if (typeof(pathOrHolderId) == "number") {
    117 		var holder = holderdao.getHolderById(pathOrHolderId, this.certtype);
    118 	} else {
    119 		var holder = holderdao.getHolder(pathOrHolderId, this.certtype);
    120 	}
    121 	return holder != null;
    122 }
    123
    124
    125
    126 /**
    127  * List certificate holders for a given PKI element
    128  *
    129  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    130  * @returns a list of holder ids, possibly empty
    131  * @type String[]
    132  */
    133 X509CertificateStore.prototype.listHolders = function(pathOrHolderId) {
    134 	GPSystem.log(GPSystem.DEBUG, module.id, "listHolders(" + pathOrHolderId + ")");
    135
    136 	var holderdao = this.daof.getHolderDAO();
    137
    138 	var result = holderdao.getHolderList(pathOrHolderId, this.certtype);
    139 	return result;
    140 }
    141
    142
    143
    144 /**
    145  * Get existing holder object for given path or holderId
    146  *
    147  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    148  * @param {Boolean} create create if holder doesn't exist
    149  * @type Holder
    150  * @return the holder object
    151  * @private
    152  */
    153 X509CertificateStore.prototype.getHolder = function(pathOrHolderId, create) {
    154 	var holderdao = this.daof.getHolderDAO();
    155
    156 	if (typeof(pathOrHolderId) == "number") {
    157 		var holder = holderdao.getHolderById(pathOrHolderId, this.certtype);
    158 	} else {
    159 		var holder = holderdao.getHolder(pathOrHolderId, this.certtype);
    160 	}
    161
    162 	if (!holder) {
    163 		if (!create) {
    164 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate holder for " + pathOrHolderId);
    165 		}
    166 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    167 		if (typeof(pathOrHolderId) == "number") {
    168 			holder = holderdao.newHolderWithParent(0, this.certtype);
    169 		} else {
    170 			holder = holderdao.newHolder(pathOrHolderId, this.certtype);
    171 		}
    172 	}
    173
    174 	return holder;
    175 }
    176
    177
    178
    179 /**
    180  * Create new signer based on key pair generated externally
    181  *
    182  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    183  * @param {String} name the unique name of the signer in the holder context
    184  * @param {Key} puk the public key
    185  * @param {ByteString} keyblob the wrapped private key
    186  * @type ByteString
    187  * @return the subject key identifier
    188  */
    189 X509CertificateStore.prototype.newSigner = function(pathOrHolderId, name, puk, keyblob) {
    190 	GPSystem.log(GPSystem.DEBUG, module.id, "newSigner(" + pathOrHolderId + "," + name + ")");
    191
    192 	var holder = this.getHolder(pathOrHolderId, false);
    193
    194 	var keyId = PKIXCommon.determineKeyIdentifier(puk);
    195
    196 	var template = {};
    197
    198 	if (keyblob instanceof ByteString)  {
    199 		template.keyblob = keyblob;
    200 	}
    201
    202 	var signerDAO = this.daof.getSignerDAO();
    203 	signerDAO.newSigner(holder, name, keyId, template);
    204 	return keyId;
    205 }
    206
    207
    208
    209 /**
    210  * Get the signer identified by the keyId
    211  *
    212  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    213  * @param {ByteString} keyId the key identifier
    214  * @type Signer
    215  * @return this Signer object
    216  */
    217 X509CertificateStore.prototype.getSigner = function(pathOrHolderId, keyId) {
    218 	GPSystem.log(GPSystem.DEBUG, module.id, "getSigner(" + pathOrHolderId + "," + keyId + ")");
    219
    220 	var holder = this.getHolder(pathOrHolderId, false);
    221
    222 	var signerDAO = this.daof.getSignerDAO();
    223
    224 	var signer = signerDAO.getSignerByKeyId(holder, keyId);
    225
    226 	if (!signer) {
    227 		return null;
    228 	}
    229
    230 	return signer;
    231 }
    232
    233
    234
    235 /**
    236  * Generate key pair
    237  *
    238  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    239  * @param {String} name the unique name of the signer in the holder context
    240  * @param {Number} algo the key generation algorithm (Crypto.EC or Crypto.RSA)
    241  * @param {Key} prk the private key template
    242  * @param {Key} puk the public key template
    243  * @type ByteString
    244  * @return the subject key identifier
    245  */
    246 X509CertificateStore.prototype.generateKeyPair = function(pathOrHolderId, name, algo, prk, puk) {
    247 	GPSystem.log(GPSystem.DEBUG, module.id, "generateKeyPair(" + pathOrHolderId + "," + name + ")");
    248
    249 	var holder = this.getHolder(pathOrHolderId, false);
    250
    251 	var crypto = this.getCrypto();
    252
    253 	// Generate key pair
    254 	crypto.generateKeyPair(algo, puk, prk);
    255
    256 	var keyId = PKIXCommon.determineKeyIdentifier(puk);
    257
    258 	var template = {
    259 		keyblob: PKCS8.encodeKeyUsingPKCS8Format(prk)
    260 	};
    261
    262 	var signerDAO = this.daof.getSignerDAO();
    263 	signerDAO.newSigner(holder, name, keyId, template);
    264 	return keyId;
    265 }
    266
    267
    268
    269 /**
    270  * Get a private key in the certificate store
    271  *
    272  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    273  * @param {String} name the unique name of the signer in the holder context
    274  * @returns the private key or null if not found
    275  * @type Key
    276  */
    277 X509CertificateStore.prototype.getPrivateKeyByName = function(pathOrHolderId, name) {
    278 	GPSystem.log(GPSystem.DEBUG, module.id, "getPrivateKey(" + pathOrHolderId + "," + name + ")");
    279
    280 	var holder = this.getHolder(pathOrHolderId, false);
    281
    282 	var signerDAO = this.daof.getSignerDAO();
    283 	var signer = signerDAO.getSignerByName(holder, name);
    284
    285 	if (!signer) {
    286 		return signer;
    287 	}
    288
    289 	return PKCS8.decodeKeyFromPKCS8Format(signer.keyblob);
    290 }
    291
    292
    293
    294 /**
    295  * Get a private key in the certificate store
    296  *
    297  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    298  * @param {ByteString} keyId the unique key identifier
    299  * @returns the private key or null if not found
    300  * @type Key
    301  */
    302 X509CertificateStore.prototype.getPrivateKeyByKeyId = function(pathOrHolderId, keyId) {
    303 	GPSystem.log(GPSystem.DEBUG, module.id, "getPrivateKeyByKeyId(" + pathOrHolderId + "," + keyId + ")");
    304
    305 	var holder = this.getHolder(pathOrHolderId, false);
    306
    307 	var signerDAO = this.daof.getSignerDAO();
    308 	var signer = signerDAO.getSignerByKeyId(holder, keyId);
    309
    310 	if (!signer) {
    311 		return signer;
    312 	}
    313
    314 	return PKCS8.decodeKeyFromPKCS8Format(signer.keyblob);
    315 }
    316
    317
    318
    319 /**
    320  * Remove private key
    321  *
    322  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    323  * @param {String} name the unique name of the signer in the holder context
    324  * @returns true is deleted
    325  * @type boolean
    326  */
    327 X509CertificateStore.prototype.deletePrivateKey = function(pathOrHolderId, name) {
    328 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteRequest(" + pathOrHolderId + "," + name + ")");
    329
    330 	var holder = this.getHolder(pathOrHolderId, true);
    331
    332 	var signerDAO = this.daof.getSignerDAO();
    333 	var signer = signerDAO.getSignerByName(holder, name.toString());
    334
    335 	if (!signer) {
    336 		return false;
    337 	}
    338
    339 	return signerDAO.deleteSigner(signer);
    340 }
    341
    342
    343
    344 /**
    345  * Remove request
    346  *
    347  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    348  * @param {ByteString} keyId the unique keyId of the request in the holder context
    349  * @returns true is deleted
    350  * @type boolean
    351  */
    352 X509CertificateStore.prototype.deleteRequest = function(pathOrHolderId, keyId) {
    353 	GPSystem.log(GPSystem.DEBUG, module.id, "deleteRequest(" + pathOrHolderId + "," + keyId + ")");
    354
    355 	var holder = this.getHolder(pathOrHolderId, true);
    356
    357 	var requestDAO = this.daof.getRequestDAO();
    358 	var request = requestDAO.getRequestByKeyId(holder, keyId);
    359
    360 	if (!request) {
    361 		return false;
    362 	}
    363
    364 	return requestDAO.deleteRequest(request);
    365 }
    366
    367
    368
    369 /**
    370  * Store a certificate request in the certificate store
    371  *
    372  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    373  * @param {PKCS10} req the request
    374  * @type ByteString
    375  * @return the request's keyId
    376  */
    377 X509CertificateStore.prototype.storeRequest = function(pathOrHolderId, req) {
    378 	GPSystem.log(GPSystem.DEBUG, module.id, "storeRequest(" + pathOrHolderId + "," + req + ")");
    379
    380 	var holder = this.getHolder(pathOrHolderId, true);
    381
    382 	var requestDAO = this.daof.getRequestDAO();
    383
    384 	var puk = req.getPublicKey();
    385 	var keyId = PKIXCommon.determineKeyIdentifier(puk);
    386
    387 	requestDAO.newRequest(holder, keyId, req.getBytes());
    388
    389 	return keyId;
    390 }
    391
    392
    393
    394 /**
    395  * Return request for given keyId
    396  *
    397  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    398  * @param {ByteString} keyId the unique keyId of the request in the holder context
    399  * @type PKCS10
    400  * @return the request or null
    401  */
    402 X509CertificateStore.prototype.getRequest = function(pathOrHolderId, keyId) {
    403 	GPSystem.log(GPSystem.DEBUG, module.id, "getRequest(" + pathOrHolderId + "," + keyId + ")");
    404
    405 	var holder = this.getHolder(pathOrHolderId, true);
    406
    407 	var requestDAO = this.daof.getRequestDAO();
    408 	var request = requestDAO.getRequestByKeyId(holder, keyId);
    409
    410 	var req = new PKCS10(request.bytes);
    411 	return req;
    412 }
    413
    414
    415
    416 /**
    417  * Store a certificate in the certificate store
    418  *
    419  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    420  * @param {X509} cert the certificate
    421  * @param {Boolean} makeCurrent true if this certificate becomes the current certificate
    422  * @param {Number} srId service request id to be stored with issued certificate (optional)
    423  * @type Certificate
    424  * @return the Certificate entry from the database or null
    425  */
    426 X509CertificateStore.prototype.storeCertificate = function(pathOrHolderId, cert, makeCurrent, srId) {
    427 	GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificate(" + pathOrHolderId + ",'" + cert + "'," + makeCurrent + ")");
    428
    429 	var holderdao = this.daof.getHolderDAO();
    430
    431 	if (typeof(pathOrHolderId) == "number") {
    432 		var holder = holderdao.getHolderById(pathOrHolderId, this.certtype);
    433 		if (holder == null) {
    434 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "Could not locate holder for id " + pathOrHolderId);
    435 		}
    436 	} else {
    437 		var holder = holderdao.getHolder(pathOrHolderId, this.certtype);
    438
    439 		if (!holder) {
    440 			GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    441 			holder = holderdao.newHolder(pathOrHolderId, Holder.X509);
    442 		}
    443 	}
    444
    445 	var certdao = this.daof.getCertificateDAO();
    446
    447 	var issuer = cert.getIssuerDNString();
    448 	var subject = cert.getSubjectDNString();
    449 	var autid = cert.getAuthorityKeyIdentifier();
    450 	var subid = cert.getSubjectKeyIdentifier();
    451 	var serial = cert.getSerialNumber().toString(HEX);
    452
    453 	if ((autid && autid.equals(subid)) || issuer.equals(subject)) {
    454 		dir = Certificate.SHORT;
    455 	} else {
    456 		dir = Certificate.UP;
    457 	}
    458
    459 	var certificate = certdao.getCertificateBySerial(holder, serial, dir);
    460
    461 	if (certificate) {
    462 		GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a different certificate for that serial number");
    463 		GPSystem.log(GPSystem.INFO, module.id, "Existing: " + (new X509(certificate.bytes)));
    464 		GPSystem.log(GPSystem.INFO, module.id, "New     : " + cert);
    465
    466 		return certificate;
    467 	}
    468
    469 	var template = {
    470 		keyId: PKIXCommon.determineKeyIdentifier(cert.getPublicKey()),
    471 		expiry: new LongDate(cert.getNotAfter())
    472 	};
    473
    474 	if (srId) {
    475 		template.serviceRequestId = srId;
    476 	}
    477
    478 	var certificate = certdao.newCertificate(holder, serial, dir, cert.getBytes(), template);
    479
    480 	if (makeCurrent || !holder.certId) {
    481 		holderdao.updateCurrentCertificate(holder, certificate);
    482 	}
    483
    484 	return certificate;
    485 }
    486
    487
    488
    489 /**
    490  * Import a certificate that can be validated by one of certificates in the database.
    491  *
    492  * @param {X509} cert the certificate
    493  * @param {Boolean} makeCurrent true if this certificate becomes the current certificate
    494  * @param {Number} srId service request id to be stored with issued certificate (optional)
    495  * @type Certificate
    496  * @return the Certificate entry from the database or null
    497  */
    498 X509CertificateStore.prototype.importCertificate = function(cert, makeCurrent, srId) {
    499 	GPSystem.log(GPSystem.DEBUG, module.id, "importCertificate(" + cert + ")");
    500
    501 	var issuer = cert.getIssuerDNString();
    502 	var subject = cert.getSubjectDNString();
    503 	if (issuer.equals(subject)) {
    504 		throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "importCertificate can not import self-signed certificates");
    505 	}
    506
    507 	var certdao = this.daof.getCertificateDAO();
    508
    509 	var autid = cert.getAuthorityKeyIdentifier();
    510 	var subid = cert.getSubjectKeyIdentifier();
    511
    512 	var certdto = certdao.getCertificateByKeyId(autid);
    513
    514 	if (certdto == null) {
    515 		GPSystem.log(GPSystem.WARN, module.id, "No certificate with AuthorityKeyIdentifier " + autid.toString(HEX) + " found");
    516 		return null;
    517 	}
    518
    519 	var ca = new X509(certdto.bytes);
    520 	cert.verifyWith(ca);
    521
    522 	var parentId = certdto.holderId;
    523 	var name = PKIXCommon.makeName(PKIXCommon.parseDN(subject));
    524
    525 	var holderdao = this.daof.getHolderDAO();
    526 	var holder = holderdao.getHolderByTypeAndName(parentId, name, Holder.X509);
    527
    528 	if (!holder) {
    529 		GPSystem.log(GPSystem.DEBUG, module.id, "Holder not found, creating new.");
    530 		holder = holderdao.newHolderForParent(parentId, Holder.X509, { name: name } );
    531 	}
    532
    533 	var holderId = holder.id;
    534
    535 	return this.storeCertificate(holderId, cert, makeCurrent, srId);
    536 }
    537
    538
    539
    540 /**
    541  * Get current key id
    542  *
    543  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    544  * @type ByteString
    545  * @return the current key id or null if none defined
    546  */
    547 X509CertificateStore.prototype.getCurrentKeyIdAndCertificate = function(pathOrHolderId) {
    548 	GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentKeyId(" + pathOrHolderId + ")");
    549
    550 	var holder = this.getHolder(pathOrHolderId, false);
    551
    552 	var certdao = this.daof.getCertificateDAO();
    553
    554 	var certificate = certdao.getCurrentCertificate(holder);
    555
    556 	if (!certificate) {
    557 		return null;
    558 	}
    559
    560 	return { keyId: certificate.keyId, certificate: new X509(certificate.bytes) };
    561 }
    562
    563
    564
    565 /**
    566  * Get current certificate for given path or holderId
    567  *
    568  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    569  * @type X509
    570  * @return the current certificate or null if none defined
    571  */
    572 X509CertificateStore.prototype.getCurrentCertificate = function(pathOrHolderId) {
    573 	GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentCertificate(" + pathOrHolderId + ")");
    574
    575 	var holder = this.getHolder(pathOrHolderId, false);
    576
    577 	var certdao = this.daof.getCertificateDAO();
    578
    579 	var certificate = certdao.getCurrentCertificate(holder);
    580
    581 	if (!certificate) {
    582 		return null;
    583 	}
    584
    585 	return new X509(certificate.bytes);
    586 }
    587
    588
    589
    590 /**
    591  * Get certificate chain for given path or holderId
    592  *
    593  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    594  * @type X509[]
    595  * @return the certificate chain, starting with the current certificate of the holder and ending with the self-signed trust anchor
    596  */
    597 X509CertificateStore.prototype.getCertificateChain = function(pathOrHolderId) {
    598 	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateChain(" + pathOrHolderId + ")");
    599
    600 	var chain = [];
    601 	var certdao = this.daof.getCertificateDAO();
    602
    603 	do	{
    604 		var holder = this.getHolder(pathOrHolderId, false);
    605
    606 		var certificate = certdao.getCurrentCertificate(holder);
    607
    608 		if (!certificate) {
    609 			throw new GPError(module.id, GPError.INVALID_ARGUMENTS, 0, "No current certificate found for " + holder);
    610 		}
    611
    612 		chain.push(new X509(certificate.bytes));
    613 		pathOrHolderId = holder.parentId;
    614 	} while (pathOrHolderId != undefined);
    615
    616 	return chain;
    617 }
    618
    619
    620
    621 /**
    622  * Get current certificate for given path or holderId
    623  *
    624  * @param {String/Number} pathOrHolderId the relative path of the PKI element (e.g. "/UTCVCA1/UTDVCA1") or the holderId from the database
    625  * @type X509
    626  * @return the current certificate or null if none defined
    627  */
    628 X509CertificateStore.prototype.getCurrentCertificateAndSigner = function(pathOrHolderId) {
    629 	GPSystem.log(GPSystem.DEBUG, module.id, "getCurrentCertificateAndSigner(" + pathOrHolderId + ")");
    630
    631 	var holder = this.getHolder(pathOrHolderId, false);
    632
    633 	var certdao = this.daof.getCertificateDAO();
    634
    635 	var certificate = certdao.getCurrentCertificate(holder);
    636
    637 	if (!certificate) {
    638 		return null;
    639 	}
    640
    641 	var cert = new X509(certificate.bytes);
    642
    643 	var signerDAO = this.daof.getSignerDAO();
    644
    645 	var signer = signerDAO.getSignerByKeyId(holder, certificate.keyId);
    646
    647 	if (!signer) {
    648 		return null;
    649 	}
    650
    651 	var prk = PKCS8.decodeKeyFromPKCS8Format(signer.keyblob);
    652
    653 	return { signerPrK: prk, signerCert: cert };
    654 }
    655