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