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 X509 certificate generator class following RFC5280
 25  */
 26 
 27 var PKIXCommon = require("scsh/x509/PKIXCommon").PKIXCommon;
 28 
 29 
 30 
 31 /**
 32  * Create a X.509 certificate generator.
 33  *
 34  * @class Class implementing a X.509 certificate generator
 35  * @constructor
 36  *
 37  * @param {Crypto} crypto the crypto provider to use for signing operations
 38  */
 39 function X509CertificateGenerator(crypto) {
 40 	this.crypto = crypto;
 41 	this.encodeECDomainParameter = true;
 42 	this.reset();
 43 }
 44 
 45 exports.X509CertificateGenerator = X509CertificateGenerator;
 46 
 47 
 48 
 49 /**
 50  * Resets all internal state variables.
 51  *
 52  */
 53 X509CertificateGenerator.prototype.reset = function() {
 54 	this.extensions = new Array();
 55 
 56 }
 57 
 58 
 59 
 60 /**
 61  * Sets the serial number.
 62  *
 63  * @param {ByteString} serialNumber the serial number for the certificate
 64  */
 65 X509CertificateGenerator.prototype.setSerialNumber = function(serialNumber) {
 66 	this.serialNumber = serialNumber;
 67 }
 68 
 69 
 70 
 71 /**
 72  * Sets the isser name.
 73  *
 74  * <p>The issuer name must be a JavaScript object containing the properties:</p>
 75  * <ul>
 76  *  <li>C - the country</li>
 77  *  <li>O - the organization</li>
 78  *  <li>OU - the organization unit</li>
 79  *  <li>CN - the common name</li>
 80  * </ul>
 81  * <p>Example:</p>
 82  * <pre>
 83  *	var issuer = { C:"UT", O:"ACME Corporation", CN:"Test-CA" };
 84  * </pre>
 85  * @param {Object} issuer the issuer name
 86  */
 87 X509CertificateGenerator.prototype.setIssuer = function(issuer) {
 88 	this.issuer = issuer;
 89 }
 90 
 91 
 92 
 93 /**
 94  * Sets the effective date for the certificate.
 95  *
 96  * @param {String or Date} date the date in format YYMMDDHHMMSSZ
 97  */
 98 X509CertificateGenerator.prototype.setNotBefore = function(date) {
 99 	this.notBefore = date;
100 }
101 
102 
103 
104 /**
105  * Sets the expiration date for the certificate.
106  *
107  * @param {String or Date} date the date in format YYMMDDHHMMSSZ
108  */
109 X509CertificateGenerator.prototype.setNotAfter = function(date) {
110 	this.notAfter = date;
111 }
112 
113 
114 
115 /**
116  * Sets the subject name.
117  *
118  * <p>The subject name must be a JavaScript object containing the properties:</p>
119  * <ul>
120  *  <li>C - the country</li>
121  *  <li>O - the organization</li>
122  *  <li>OU - the organization unit</li>
123  *  <li>CN - the common name</li>
124  * </ul>
125  * <p>Example:</p>
126  * <pre>
127  *	var subject = { C:"UT", O:"ACME Corporation", CN:"Joe Doe" };
128  * </pre>
129  * @param {Object} subject the subject name
130  */
131 X509CertificateGenerator.prototype.setSubject = function(subject) {
132 	this.subject = subject;
133 }
134 
135 
136 
137 /**
138  * Sets the subjects public key
139  *
140  * <p>The methods accepts ECC and RSA Public Keys.</p>
141  *
142  * @param {Key} publicKey the subjects public key
143  */
144 X509CertificateGenerator.prototype.setPublicKey = function(publicKey) {
145 	this.publicKey = publicKey;
146 }
147 
148 
149 
150 /**
151  * Sets the signature algorithm.
152  *
153  * @param {Number} alg the signature algorithm.
154  */
155 X509CertificateGenerator.prototype.setSignatureAlgorithm = function(alg) {
156 	this.signatureAlgorithm = alg;
157 }
158 
159 
160 
161 /**
162  * Adds an extension to the certificate
163  *
164  * <p>The structure is defined as:</p>
165  * <pre>
166  *    Extension  ::=  SEQUENCE  {
167  *        extnID      OBJECT IDENTIFIER,
168  *        critical    BOOLEAN DEFAULT FALSE,
169  *        extnValue   OCTET STRING
170  *                    -- contains the DER encoding of an ASN.1 value
171  *                    -- corresponding to the extension type identified
172  *                    -- by extnID
173  *        }
174  *</pre>
175  * @param {String} extnID the extensions object identifier
176  * @param {Boolean} critical the extension is critical
177  * @param {ByteString} the extension value as ByteString
178  */
179 X509CertificateGenerator.prototype.addExtension = function(extnID, critical, extnValue) {
180 	var t = new ASN1("extension", ASN1.SEQUENCE,
181 				new ASN1("extnID", ASN1.OBJECT_IDENTIFIER, new ByteString(extnID, OID))
182 			);
183 
184 	if (critical) {
185 		t.add(new ASN1("critical", ASN1.BOOLEAN, new ByteString("FF", HEX)));
186 	}
187 
188 	t.add(new ASN1("extnValue", ASN1.OCTET_STRING, extnValue));
189 	this.extensions.push(t);
190 
191 }
192 
193 
194 
195 /**
196  * Adds the key usage extension.
197  *
198  * <p>The following flags are defined:</p>
199  * <pre>
200  * PKIXCommon.digitalSignature = 0x0080;
201  * PKIXCommon.nonRepudiation   = 0x0040;
202  * PKIXCommon.keyEncipherment  = 0x0020;
203  * PKIXCommon.dataEncipherment = 0x0010;
204  * PKIXCommon.keyAgreement     = 0x0008;
205  * PKIXCommon.keyCertSign      = 0x0004;
206  * PKIXCommon.cRLSign          = 0x0002;
207  * PKIXCommon.encipherOnly     = 0x0001;
208  * PKIXCommon.decipherOnly     = 0x8000;
209  * </pre>
210  * @param {Number} the key usage flags as combination of the flags defined above.
211  */
212 X509CertificateGenerator.prototype.addKeyUsageExtension = function(flags) {
213 	var t = new ASN1(ASN1.BIT_STRING, PKIXCommon.bitstringForInteger(flags));
214 	this.addExtension("2.5.29.15", true, t.getBytes());
215 }
216 
217 // Deprecated: Use PKIXCommon. instead
218 X509CertificateGenerator.digitalSignature	= 0x0080;
219 X509CertificateGenerator.nonRepudiation		= 0x0040;
220 X509CertificateGenerator.keyEncipherment	= 0x0020;
221 X509CertificateGenerator.dataEncipherment	= 0x0010;
222 X509CertificateGenerator.keyAgreement		= 0x0008;
223 X509CertificateGenerator.keyCertSign		= 0x0004;
224 X509CertificateGenerator.cRLSign			= 0x0002;
225 X509CertificateGenerator.encipherOnly		= 0x0001;
226 X509CertificateGenerator.decipherOnly		= 0x8000;
227 
228 
229 
230 /**
231  * Adds the BasicConstraints extension.
232  *
233  * @param {Boolean} cA the certificate belongs to a CA
234  * @param {Number} pathLenConstraint the maximum number of subordinate CA certificates
235  */
236 X509CertificateGenerator.prototype.addBasicConstraintsExtension = function(cA, pathLenConstraint) {
237 	var t = new ASN1("BasicConstraints",ASN1.SEQUENCE);
238 	if (cA) {
239 		t.add(new ASN1("cA", ASN1.BOOLEAN, new ByteString("FF", HEX)));
240 	}
241 	if (pathLenConstraint >= 0) {
242 		var bb = new ByteBuffer();
243 		bb.append(pathLenConstraint);
244 		t.add(new ASN1("pathLenConstraint", ASN1.INTEGER, bb.toByteString()));
245 	}
246 	this.addExtension("2.5.29.19", true, t.getBytes());
247 }
248 
249 
250 
251 /**
252  * Adds the subject public key identifier extension based on the certificates subject key.
253  *
254  * <p>The key identifier is calculated as SHA-1 hash over the contents of the
255  * subject public key (Without tag, length and number of unused bits.</p>
256  */
257 X509CertificateGenerator.prototype.addSubjectKeyIdentifierExtension = function() {
258 	var spi = this.getSubjectPublicKeyInfo();
259 	var keyvalue = spi.get(1).value.bytes(1);
260 	var crypto = new Crypto();
261 	var hash = crypto.digest(Crypto.SHA_1, keyvalue);
262 
263 	var t = new ASN1(ASN1.OCTET_STRING, hash);
264 	this.addExtension("2.5.29.14", false, t.getBytes());
265 }
266 
267 
268 
269 /**
270  * Adds the authority public key identifier extension based on the issuers key.
271  *
272  * <p>The key identifier is calculated as SHA-1 hash over the contents of the
273  * issuer public key (Without tag, length and number of unused bits.</p>
274  *
275  * @param {Key/ByteString} publicKeyOrId the authority subject key or authority key identifier
276  */
277 X509CertificateGenerator.prototype.addAuthorityKeyIdentifierExtension = function(publicKeyOrId) {
278 	if (publicKeyOrId instanceof Key) {
279 		if (publicKeyOrId.getComponent(Key.MODULUS)) {
280 			var spi = PKIXCommon.createRSASubjectPublicKeyInfo(publicKeyOrId);
281 		} else {
282 			var spi = PKIXCommon.createECSubjectPublicKeyInfo(publicKeyOrId, this.encodeECDomainParameter);
283 		}
284 
285 		var keyvalue = spi.get(1).value.bytes(1);
286 		var crypto = new Crypto();
287 		var hash = crypto.digest(Crypto.SHA_1, keyvalue);
288 	} else {
289 		var hash = publicKeyOrId;
290 	}
291 
292 	var t = new ASN1(ASN1.SEQUENCE,
293 					new ASN1(0x80, hash)
294 	);
295 	this.addExtension("2.5.29.35", false, t.getBytes());
296 }
297 
298 
299 
300 /**
301  * Adds the CRL distribution point URLs.
302  *
303  * @param {String[]} url a list of URLs
304  */
305 X509CertificateGenerator.prototype.addCRLDistributionPointURL = function(url) {
306 	var t = new ASN1("cRLDistributionPoints", ASN1.SEQUENCE);
307 
308 	for (var i = 0; i < url.length; i++) {
309 		t.add(	new ASN1("cRLDistributionPoint", ASN1.SEQUENCE,
310 					new ASN1("distributionPoint", 0xA0,
311 						new ASN1("fullName", 0xA0,
312 							new ASN1("uniformResourceIdentifier", 0x86, new ByteString(url[i], ASCII))
313 						)
314 					)
315 				));
316 	}
317 	this.addExtension("id-ce-cRLDistributionPoints", false, t.getBytes());
318 }
319 
320 
321 
322 /**
323  * Adds the extended key usage extension
324  *
325  * @param {String[]} oids the list of object identifier names
326  * @param {Boolean} critical the extension is critical
327  */
328 X509CertificateGenerator.prototype.addExtendedKeyUsages = function(oids, critical) {
329 	var t = new ASN1("extKeyUsages", ASN1.SEQUENCE);
330 
331 	for (var i = 0; i < oids.length; i++) {
332 		t.add( new ASN1("keyPurposeId", ASN1.OBJECT_IDENTIFIER, new ByteString(oids[i], OID) ));
333 	}
334 	this.addExtension("id-ce-extKeyUsage", critical, t.getBytes());
335 }
336 
337 
338 
339 /**
340  * Gets the issuer name as TLV object
341  *
342  * @return the issuer RDNSequence
343  * @type ASN1
344  */
345 X509CertificateGenerator.prototype.getIssuer = function() {
346 	if (this.issuer instanceof ASN1) {
347 		return this.issuer;
348 	} else {
349 		return PKIXCommon.encodeName(this.issuer);
350 	}
351 }
352 
353 
354 
355 /**
356  * Gets the certificate validity as TLV object
357  *
358  * @return the certificates validity
359  * @type ASN1
360  */
361 X509CertificateGenerator.prototype.getValidity = function() {
362 	var t = new ASN1("validity", ASN1.SEQUENCE);
363 
364 	var ts = this.notBefore;
365 	if (typeof(ts) != "string") {
366 		ts = PKIXCommon.dtoUTC(this.notBefore);
367 	}
368 	if (ts >= "500101000000Z") {
369 		ts = "20" + ts;
370 		t.add(new ASN1("notBefore", ASN1.GeneralizedTime, new ByteString(ts, ASCII)));
371 	} else {
372 		t.add(new ASN1("notBefore", ASN1.UTCTime, new ByteString(ts, ASCII)));
373 	}
374 
375 	var ts = this.notAfter;
376 	if (typeof(ts) != "string") {
377 		ts = PKIXCommon.dtoUTC(this.notAfter);
378 	}
379 	if (ts >= "500101000000Z") {
380 		ts = "20" + ts;
381 		t.add(new ASN1("notAfter", ASN1.GeneralizedTime, new ByteString(ts, ASCII)));
382 	} else {
383 		t.add(new ASN1("notAfter", ASN1.UTCTime, new ByteString(ts, ASCII)));
384 	}
385 	return t;
386 }
387 
388 
389 
390 /**
391  * Gets the subject name as TLV object
392  *
393  * @return the issuer RDNSequence
394  * @type ASN1
395  */
396 X509CertificateGenerator.prototype.getSubject = function() {
397 	if (this.subject instanceof ASN1) {
398 		return this.subject;
399 	} else {
400 		return PKIXCommon.encodeName(this.subject);
401 	}
402 }
403 
404 
405 
406 /**
407  * Gets the subject's public key as TLV object
408  *
409  * @return the subject's public key info
410  * @type ASN1
411  */
412 X509CertificateGenerator.prototype.getSubjectPublicKeyInfo = function() {
413 	if (this.publicKey.getComponent(Key.MODULUS)) {
414 		return PKIXCommon.createRSASubjectPublicKeyInfo(this.publicKey);
415 	} else {
416 		return PKIXCommon.createECSubjectPublicKeyInfo(this.publicKey, this.encodeECDomainParameter);
417 	}
418 }
419 
420 
421 
422 /**
423  * Gets the certificate extension as TLV object
424  *
425  * @return the certificate extensions
426  * @type ASN1
427  */
428 X509CertificateGenerator.prototype.getExtensions = function() {
429 	var t = new ASN1("extensions", 0xA3);
430 	var s = new ASN1("extensions", ASN1.SEQUENCE);
431 
432 	t.add(s);
433 
434 	for (var i = 0; i < this.extensions.length; i++) {
435 		s.add(this.extensions[i]);
436 	}
437 	return t;
438 }
439 
440 
441 
442 /**
443  * Gets the part of the certificate that will be signed
444  *
445  * @return the TBSCertificate part
446  * @type ASN1
447  */
448 X509CertificateGenerator.prototype.getTbsCertificate = function() {
449 	var t = new ASN1("tbsCertificate", ASN1.SEQUENCE);
450 	t.add(new ASN1("version", 0xA0,
451 			new ASN1("version", ASN1.INTEGER, new ByteString("02", HEX))));
452 	t.add(new ASN1("serialNumber", ASN1.INTEGER, PKIXCommon.convertUnsignedInteger(this.serialNumber)));
453 	t.add(this.getSignatureAlgorithm());
454 	t.add(this.getIssuer());
455 	t.add(this.getValidity());
456 	t.add(this.getSubject());
457 	t.add(this.getSubjectPublicKeyInfo());
458 	t.add(this.getExtensions());
459 	return t;
460 }
461 
462 
463 
464 /**
465  * Gets the signature algorithm TLV object
466  *
467  * @return the signature algorithm object
468  * @type ASN1
469  */
470 X509CertificateGenerator.prototype.getSignatureAlgorithm = function() {
471 	return PKIXCommon.encodeSignatureAlgorithm(this.signatureAlgorithm);
472 }
473 
474 
475 
476 /**
477  * Generates the certificate.
478  *
479  * @return the generated certificate
480  * @type X509
481  */
482 X509CertificateGenerator.prototype.generateX509Certificate = function(privateKey) {
483 	var certificate = new ASN1("certificate", ASN1.SEQUENCE);
484 
485 	var tbs = this.getTbsCertificate();
486 	certificate.add(tbs);
487 	certificate.add(this.getSignatureAlgorithm());
488 
489 	var signature = this.crypto.sign(privateKey, this.signatureAlgorithm, tbs.getBytes());
490 	signature = (new ByteString("00", HEX)).concat(signature);
491 
492 	var signatureValue = new ASN1("signatureValue", ASN1.BIT_STRING, signature);
493 	certificate.add(signatureValue);
494 
495 //	print(certificate);
496 	return new X509(certificate.getBytes());
497 }
498 
499 
500 
501 X509CertificateGenerator.RSATest = function() {
502 
503 	var crypto = new Crypto();
504 
505 	var caPrivateKey = new Key();
506 	caPrivateKey.setType(Key.PRIVATE);
507 
508 	var caPublicKey = new Key();
509 	caPublicKey.setType(Key.PUBLIC);
510 	caPublicKey.setSize(1024);
511 
512 	crypto.generateKeyPair(Crypto.RSA, caPublicKey, caPrivateKey);
513 
514 //	var caPrivKey = new Key("profiles/kp_rsa_private.xml");
515 
516 	var x = new X509CertificateGenerator(crypto);
517 
518 	x.reset();
519 	x.setSerialNumber(new ByteString("01", HEX));
520 	x.setSignatureAlgorithm(Crypto.RSA);
521 	var issuer = { C:"UT", O:"ACME Corporation", CN:"Test-CA" };
522 	x.setIssuer(issuer);
523 	x.setNotBefore("060825120000Z");
524 	x.setNotAfter("160825120000Z");
525 	var subject = { C:"UT", O:"Utopia CA", OU:"ACME Corporation", CN:"Joe Doe" };
526 	x.setSubject(subject);
527 
528 	x.setPublicKey(caPublicKey);
529 
530 	x.addKeyUsageExtension(	PKIXCommon.digitalSignature |
531 							PKIXCommon.keyCertSign |
532 							PKIXCommon.cRLSign );
533 
534 	x.addBasicConstraintsExtension(true, 0);
535 	x.addSubjectKeyIdentifierExtension();
536 	x.addAuthorityKeyIdentifierExtension(caPublicKey);
537 
538 	var cert = x.generateX509Certificate(caPrivateKey);
539 	var fn = GPSystem.mapFilename("cert_rsa.cer", GPSystem.USR);
540 	PKIXCommon.writeFileToDisk(fn, cert.getBytes());
541 
542 	cert.verifyWith(cert);
543 
544 	print(cert);
545 }
546 
547 
548 
549 X509CertificateGenerator.ECCTest = function() {
550 
551 	var crypto = new Crypto();
552 
553 	var caPrivateKey = new Key();
554 	caPrivateKey.setType(Key.PRIVATE);
555 
556 	var caPublicKey = new Key();
557 	caPublicKey.setType(Key.PUBLIC);
558 	caPublicKey.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
559 
560 	crypto.generateKeyPair(Crypto.EC, caPublicKey, caPrivateKey);
561 
562 	var x = new X509CertificateGenerator(crypto);
563 
564 	x.reset();
565 	x.setSerialNumber(new ByteString("01", HEX));
566 	x.setSignatureAlgorithm(Crypto.ECDSA_SHA256);
567 	var issuer = { C:"UT", O:"ACME Corporation", CN:"Test-CA" };
568 	x.setIssuer(issuer);
569 	var t = new Date();
570 	x.setNotBefore(t);
571 	x.setNotAfter(PKIXCommon.addDays(t, 180));
572 	var subject = { C:"UT", O:"Utopia CA", OU:"ACME Corporation", CN:"Joe Doe" };
573 	x.setSubject(subject);
574 
575 	x.setPublicKey(caPublicKey);
576 
577 	x.addKeyUsageExtension(	PKIXCommon.digitalSignature |
578 							PKIXCommon.keyCertSign |
579 							PKIXCommon.cRLSign );
580 
581 	x.addBasicConstraintsExtension(true, 0);
582 	x.addSubjectKeyIdentifierExtension();
583 	x.addAuthorityKeyIdentifierExtension(caPublicKey);
584 
585 	var cert = x.generateX509Certificate(caPrivateKey);
586 
587 	var fn = GPSystem.mapFilename("cert_ecc.cer", GPSystem.USR);
588 	PKIXCommon.writeFileToDisk(fn, cert.getBytes());
589 
590 	cert.verifyWith(cert);
591 
592 	print(cert);
593 	print(new ASN1(cert.getBytes()));
594 }
595