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