      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 A Signer backed by a X.509 certificate
     25  */
     26
     27 var Holder = require('scsh/pki-db/Holder').Holder;
     28 var Certificate = require('scsh/pki-db/Certificate').Certificate;
     29 var PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon;
     30 var PKCS10 = require('scsh/pkcs/PKCS10').PKCS10;
     31 var PKCS10Generator = require('scsh/x509/PKCS10Generator').PKCS10Generator;
     32 var LongDate = require('scsh/pki-db/LongDate').LongDate;
     33
     34
     35
     36 /**
     37  * Create a signer based on a X.509 certificate
     38  *
     39  * @class Class implementing a signer backed by a X.509 certificate
     40  * @constructor
     41  * @param {DAOFactory} daof the factory that can create the required data access objects
     42  * @param {CryptoProviderFactory} cpf factory implementing getCryptoProvider() used to get access to crypto providers
     43  * @param {Holder} holder the holder object for this signer
     44  */
     45 function X509Signer(daof, cpf, holder) {
     46 	this.daof = daof;
     47 	this.cpf = cpf;
     48 	this.holder = holder;
     49
     50 	if (this.holder == null) {
     51 		throw new GPError(module.id, GPError.INVALID_DATA, 1, "Certificate holder " + holder + " not found");
     52 	}
     53
     54 	if (this.getSigner()) {
     55 		this.parsePolicyFromSigner();
     56 	}
     57 }
     58
     59 exports.X509Signer = X509Signer;
     60
     61
     62
     63 /**
     64  * Create a new signer
     65  *
     66  * @param {DAOFactory} daof the factory that can create the required data access objects
     67  * @param {String/Number} pathOrHolderId the path of holderIDs (eg. "/UTCVCA/UTDVCA/UTTERM") or the holderId from the database
     68  * @param {Number} certtype optional argument, default Holder.X509
     69  * @param {Object} template template for database entry
     70  * @type Number
     71  * @return the newly created holder id
     72  */
     73 X509Signer.createSigner = function(daof, pathOrHolderId, certtype, template) {
     74 	if (typeof(certtype) == "undefined") {
     75 		certtype = Holder.X509;
     76 	}
     77
     78 	var holderdao = daof.getHolderDAO();
     79 	if (typeof(pathOrHolderId) == "string") {
     80 		var holder = holderdao.newHolder(pathOrHolderId, certtype, template);
     81 	} else {
     82 		if (typeof(pathOrHolderId) == "undefined") {
     83 			pathOrHolderId = 0;
     84 		}
     85
     86 		var holder = holderdao.newHolderForParent(pathOrHolderId, certtype, template);
     87 	}
     88
     89 	return holder.id;
     90 }
     91
     92
     93
     94 /**
     95  * Set policy for signer object.
     96  *
     97  * The policy object shall contain the following properties. Some elements are only used in the
     98  * derive X509CertificateIssuer class.
     99  *
    100  * <ul>
    101  * <li>distinguishedName - The distinguishedName object as defined in PKIXCommon.encodeName()</li>
    102  * <li>keySpecification - A Key object initialized with the key parameter.</li>
    103  * <li>signatureAlgorithm - A ByteString encoding the object identifier for the signature algorithm</li>
    104  * <li>validityDaysSelfSigned - Number of days the self-signed certificate is valid</li>
    105  * <li>validityDaysCertificates - Number of days the issued certificate is valid</li>
    106  * <li>validityDaysCRL - Number of days the issued certificate revocation list is valid</li>
    107  * <li>pathLenConstraint - Number of subordinate CAs</li>
    108  * <li>requestFormat - "pkcs10" or "sc-hsm"</li>
    109  * <li>overwriteKey - Set to true to overwrite a key with the same label</li>
    110  * <li>restrictPublicKey - Limit the public key of the self-signed root as per PKIXCommon.restrictedPublicKeyAlgorithmIdentifier()</li>
    111  * </ul>
    112  *
    113  * @see PKIXCommon.encodeName()
    114  * @param {String} crldp the URL of the distribution point
    115  */
    116 X509Signer.prototype.setPolicy = function(policy) {
    117 	this.policy = policy;
    118 }
    119
    120
    121
    122 /**
    123  * Determine signer name for newly generated signer
    124  *
    125  * @type String
    126  * @return the unique name
    127  */
    128 X509Signer.prototype.determineSignerName = function() {
    129 	var name = this.holder.id + ":X509Signer " + this.holder.signerNo + 1 + " [" + Date() + "]";
    130 	var holderdao = this.daof.getHolderDAO();
    131 	holderdao.updateSignerNo(this.holder, this.holder.signerNo + 1);
    132 	return name;
    133 }
    134
    135
    136
    137 /**
    138  * Determine subject distinguished name for new signer
    139  *
    140  * @param {String} name
    141  * @type String
    142  * @return the distringuished name for the new signer
    143  */
    144 X509Signer.prototype.determineDistinguishedName = function(name) {
    145 	return this.policy.distinguishedName;
    146 }
    147
    148
    149
    150 /**
    151  * Determine the key usage for the request
    152  *
    153  * @type Number
    154  * @return the key usage defined in PKIXCommon
    155  */
    156 X509Signer.prototype.getRequestKeyUsage = function() {
    157 	return PKIXCommon.digitalSignature;
    158 }
    159
    160
    161
    162 /**
    163  * Create a new signer key pair
    164  *
    165  * @param {String} name the signer name
    166  * @type ByteString
    167  * @return the subject key identifier
    168  */
    169 X509Signer.prototype.newSigner = function(name, template) {
    170 	if (!name) {
    171 		name = this.determineSignerName();
    172 	}
    173
    174 	if (!template || !template.keyDomain) {
    175 		throw new GPError(module.id, GPError.INVALID_DATA, 1, "Cannot create a signer without a template containing the keyDomain");
    176 	}
    177
    178 	var cp = this.cpf.getCryptoProvider(template.keyDomain, true);
    179 	try	{
    180 		if (this.policy.overwriteKey) {
    181 			cp.deleteIfExists = true;
    182 		}
    183
    184 		var k = cp.generateKeyPair(name, this.policy.keySpecification);
    185 		var prk = k.prk;
    186 		var puk = k.puk;
    187 		var req = k.req;
    188 		var keyblob;
    189
    190 		var keyId = PKIXCommon.determineKeyIdentifier(puk);
    191
    192 		if (typeof(cp.getWrappedKey) == "function") {
    193 			template.keyblob = cp.getWrappedKey(name);
    194 		}
    195
    196 		var signerDAO = this.daof.getSignerDAO();
    197 		this.signer = signerDAO.newSigner(this.holder, name, keyId, template);
    198
    199 		if (this.policy.requestFormat != "sc-hsm") {
    200 			var gen = new PKCS10Generator(cp.getCrypto());
    201 			gen.setSignatureAlgorithm(this.policy.reqSignatureAlgorithm);
    202 			gen.setSubject(this.determineDistinguishedName());
    203 			gen.setPublicKey(puk);
    204 			gen.addKeyUsageExtension(this.getRequestKeyUsage());
    205 			req = gen.generateCertificationRequest(prk);
    206 			req = req.getBytes();
    207 		}
    208
    209 		var requestDAO = this.daof.getRequestDAO();
    210 		requestDAO.newRequest(this.holder, keyId, req);
    211 	}
    212 	finally {
    213 		cp.release();
    214 	}
    215
    216 	return keyId;
    217 }
    218
    219
    220
    221 /**
    222  * Return the holderId from the holder database for this element
    223  *
    224  * @type Number
    225  * @return the holderId
    226  */
    227 X509Signer.prototype.getHolderId = function() {
    228 	return this.holder.id;
    229 }
    230
    231
    232
    233 /**
    234  * Return the holder from the holder database for this element
    235  *
    236  * @type Number
    237  * @return the holderId
    238  */
    239 X509Signer.prototype.getHolder = function() {
    240 	return this.holder;
    241 }
    242
    243
    244
    245 /**
    246  * Return the signer from the signer database for this element
    247  *
    248  * @type Signer
    249  * @return the signer value object or null
    250  */
    251 X509Signer.prototype.getSigner = function() {
    252 	GPSystem.log(GPSystem.DEBUG, module.id, "getSigner()");
    253 	if (this.signer) {
    254 		GPSystem.log(GPSystem.DEBUG, module.id, "return cached signer");
    255 		return this.signer;
    256 	}
    257
    258 	var certDAO = this.daof.getCertificateDAO();
    259 	this.currentCertificate = certDAO.getCurrentCertificate(this.holder);
    260
    261 	if (!this.currentCertificate) { // no active signer
    262 		GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id);
    263 		return null;
    264 	}
    265
    266 	var signerDAO = this.daof.getSignerDAO();
    267 	this.signer = signerDAO.getSignerByKeyId(this.holder, this.currentCertificate.keyId);
    268
    269 	if (this.signer) {
    270 		this.cpid = this.signer.keyDomain;
    271 	}
    272
    273 	return this.signer;
    274 }
    275
    276
    277
    278 /**
    279  * Parse the policy from the signer's values object
    280  *
    281  * @type Object
    282  * @return the policy
    283  */
    284 X509Signer.prototype.parsePolicyFromSigner = function() {
    285 	var signer = this.getSigner();
    286
    287 	if (signer == null) {
    288 		throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer");
    289 	}
    290
    291 	var c = signer.getContent();
    292
    293 	if (!c || !c.keySpecification) {
    294 		return; // no policy
    295 	}
    296
    297 	var dp = new Key();
    298 	if (c.keySpecification.type == "EC") {
    299 		dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID));
    300 	} else {
    301 		dp.setSize(c.keySpecification.keysize);
    302 	}
    303
    304 	var dn = PKIXCommon.parseDN(c.distinguishedName);
    305
    306 	this.policy = {
    307 		distinguishedName: dn,
    308 		signatureAlgorithm: c.signatureAlgorithm,
    309 		reqSignatureAlgorithm: c.keySpecification.sigalg,
    310 		validityDaysCertificates: c.validityDaysCertificates,
    311 		validityDaysCRL: c.validityDaysCRL,
    312 		pathLenConstraint: c.pathLenConstraint,
    313 		keySpecification: dp
    314 	}
    315
    316 	return this.policy;
    317 }
    318
    319
    320
    321 X509Signer.prototype.getSubjectPolicyForRequest = function() {
    322 	GPSystem.log(GPSystem.DEBUG, module.id, "getSubjectPolicyForRequest()");
    323
    324 	var signer = this.getSigner();
    325
    326 	if (signer == null) {
    327 		throw new GPError(module.id, GPError.INVALID_DATA, 1, "No active signer");
    328 	}
    329
    330 	var c = signer.getContent();
    331
    332 	var dp = new Key();
    333 	if (c.keySpecification.type == "EC") {
    334 		dp.setComponent(Key.ECC_CURVE_OID, new ByteString(c.keySpecification.curve, OID));
    335 	} else {
    336 		dp.setSize(c.keySpecification.keysize);
    337 	}
    338
    339 	var policy = {
    340 		reqSignatureAlgorithm: c.keySpecification.sigalg,
    341 		keySpecification: dp
    342 	}
    343
    344 	return policy;
    345 }
    346
    347
    348
    349 /**
    350  * Get request for the given subject key identifier
    351  *
    352  * @param {ByteString} keyId the subject key identifier
    353  * @type ByteString
    354  * @return the raw request
    355  */
    356 X509Signer.prototype.getRequestBinary = function(keyId) {
    357 	var requestDAO = this.daof.getRequestDAO();
    358 	var request = requestDAO.getRequestByKeyId(this.holder, keyId);
    359
    360 	if (request == null) {
    361 		return null;
    362 	}
    363
    364 	return request.bytes;
    365 }
    366
    367
    368
    369 /**
    370  * Get request for the given subject key identifier
    371  *
    372  * @param {ByteString} keyId the subject key identifier
    373  * @type PKCS10
    374  * @return the PKCS10 request
    375  */
    376 X509Signer.prototype.getRequest = function(keyId) {
    377 	var requestDAO = this.daof.getRequestDAO();
    378 	var request = requestDAO.getRequestByKeyId(this.holder, keyId);
    379
    380 	if (request == null) {
    381 		return null;
    382 	}
    383
    384 	var req = new PKCS10(request.bytes);
    385 	return req;
    386 }
    387
    388
    389
    390 /*
    391 X509Signer.prototype.activateSigner = function(name) {
    392 }
    393
    394
    395
    396 X509Signer.prototype.importCertificate = function(name) {
    397 }
    398
    399
    400
    401 X509Signer.prototype.getSignerList = function(usableOnly) {
    402 }
    403 */
    404
    405
    406
    407 /**
    408  * Store a certificate issued for a certain holder
    409  *
    410  * @param {X509} cert the certificate
    411  * @param {Boolean} makeCurrent true if this certificate becomes the current certificate
    412  * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier)
    413  * @param {Number} srId service request id
    414  * @type Number
    415  * @return the database id of the certificate
    416  */
    417 X509Signer.prototype.storeCertificateForHolder = function(holder, cert, makeCurrent, keyId, srId) {
    418 	GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificateForHolder(" + holder + "'" + cert + "'," + makeCurrent + "," + keyId + ")");
    419
    420 	var certdao = this.daof.getCertificateDAO();
    421
    422 	var issuer = cert.getIssuerDNString();
    423 	var subject = cert.getSubjectDNString();
    424 	var autid = cert.getAuthorityKeyIdentifier();
    425 	var subid = cert.getSubjectKeyIdentifier();
    426 	var serial = cert.getSerialNumber().toString(HEX);
    427
    428 	if ((autid && autid.equals(subid)) || issuer.equals(subject)) {
    429 		var dir = Certificate.SHORT;
    430 	} else {
    431 		var dir = Certificate.UP;
    432 	}
    433
    434 	var certificate = certdao.getCertificateBySerial(holder, serial, dir);
    435
    436 	if (certificate) {
    437 		GPSystem.log(GPSystem.INFO, module.id, "storeCertificate() : We already have a different certificate for that serial number");
    438 		GPSystem.log(GPSystem.INFO, module.id, "Existing: " + (new X509(certificate.bytes)));
    439 		GPSystem.log(GPSystem.INFO, module.id, "New     : " + cert);
    440
    441 		return;
    442 	}
    443
    444 	if (typeof(keyId) == "undefined") {
    445 		keyId = PKIXCommon.determineKeyIdentifier(cert.getPublicKey());
    446 	}
    447
    448 	var template = {
    449 		keyId: keyId,
    450 		expiry: new LongDate(cert.getNotAfter())
    451 	};
    452
    453 	if (srId) {
    454 		template.serviceRequestId = srId;
    455 	}
    456
    457 	var certificate = certdao.newCertificate(holder, serial, dir, cert.getBytes(), template);
    458
    459 	if (makeCurrent || !holder.certId) {
    460 		var holderdao = this.daof.getHolderDAO();
    461 		holderdao.updateCurrentCertificate(holder, certificate);
    462 	}
    463
    464 	return certificate.id;
    465 }
    466
    467
    468
    469 /**
    470  * Store a certificate issued for this signer
    471  *
    472  * @param {X509} cert the certificate
    473  * @param {Boolean} makeCurrent true if this certificate becomes the current certificate
    474  * @param {ByteString} keyId the key id that links this certificate to the signer (usually the subjectKeyIdentifier)
    475  * @param {Number} srId service request id
    476  */
    477 X509Signer.prototype.storeCertificate = function(cert, makeCurrent, keyId, srId) {
    478 	return this.storeCertificateForHolder(this.holder, cert, makeCurrent, keyId, srId);
    479 }
    480
    481
    482
    483 /**
    484  * Return the signer's certificate
    485  *
    486  * @type X509
    487  * @return the signer's certificate
    488  */
    489 X509Signer.prototype.getSignerCertificate = function() {
    490 	GPSystem.log(GPSystem.DEBUG, module.id, "getSignerCertificate()");
    491 	if (!this.currentCertificate) {
    492 		this.getSigner();
    493 	}
    494
    495 	if (this.signer && !this.currentCertificate) {
    496 		var certDAO = this.daof.getCertificateDAO();
    497 		this.currentCertificate = certDAO.getCurrentCertificate(this.holder);
    498 	} else if (!this.signer) {
    499 		GPSystem.log(GPSystem.DEBUG, module.id, "no active signer for holder " + this.holder.id);
    500 		return null;
    501 	}
    502
    503 	return new X509(this.currentCertificate.bytes);
    504 }
    505