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