1 /**
  2  *  ---------
  3  * |.##> <##.|  SmartCard-HSM Support Scripts
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 2011-2012 CardContact Software & System Consulting
  6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
  7  *  --------- 
  8  *
  9  * Consult your license package for usage terms and conditions.
 10  *
 11  * @fileoverview A simple X.509 CA setup
 12  */
 13  
 14 if (typeof(__ScriptingServer) == "undefined") {
 15 	load("tools/pkcs8.js");
 16 	load("tools/pkixcommon.js");
 17 	load("tools/x509certificategenerator.js");
 18 	load("tools/crlgenerator.js");
 19 }
 20 
 21 
 22 
 23 /**
 24  * Create a signer using a X.509 certificate
 25  *
 26  * @class Class implementing a signer having a X.509 certificate
 27  * @constructor
 28  * @param {Crypto} crypto the crypto provider
 29  */
 30 function X509Signer(crypto) {
 31 	this.crypto = crypto;
 32 }
 33 
 34 
 35 
 36 /**
 37  * Set the signer key object
 38  *
 39  * @param {Key} signerKey the signer key
 40  */
 41 X509Signer.prototype.setSignerKey = function(signerKey) {
 42 	this.signerKey = signerKey;
 43 }
 44 
 45 
 46 
 47 /**
 48  * Set the signer certificates
 49  *
 50  * @param{X509} signerCert the signer's certificate
 51  */
 52 X509Signer.prototype.setSignerCertificate = function(signerCert) {
 53 	this.signerCert = signerCert;
 54 	this.signerSubject = new ASN1(signerCert.getNative().getSubjectX500Principal().getEncoded());
 55 }
 56 
 57 
 58 
 59 /**
 60  * Return the signer's certificate
 61  *
 62  * @type X509
 63  * @return the signer's certificate
 64  */
 65 X509Signer.prototype.getSignerCertificate = function() {
 66 	return this.signerCert;
 67 }
 68 
 69 
 70 
 71 /**
 72  * Create a certification authority that issues X.509 certificates and CRLs
 73  *
 74  * @class Class implementing a certification authority issuing X.509 certificates and CRLs
 75  * @constructor
 76  * @param {Crypto} crypto the crypto provider
 77  */
 78 function X509CA(crypto) {
 79 	X509Signer.call(this, crypto);
 80 	this.crldp = [];
 81 }
 82 
 83 X509CA.prototype = new X509Signer();
 84 X509CA.constructor = X509CA;
 85 
 86 
 87 
 88 /**
 89  * Add a CRL distribution point to issued certificates
 90  *
 91  * @param {String} crldp the URL of the distribution point
 92  */
 93 X509CA.prototype.addCRLDistributionPoint = function(crldp) {
 94 	this.crldp.push(crldp);
 95 }
 96 
 97 
 98 
 99 /**
100  * Create a new randomly generated certificate serial number
101  *
102  * @private
103  * @type ByteString
104  * @return a 8 byte bytestring that resembles an unsigned integer
105  */
106 X509CA.prototype.newSerialNumber = function() {
107 	var crypto = new Crypto();
108 	var serial = crypto.generateRandom(8);
109 
110 	// Strip first bit to make integer unsigned
111 	if (serial.byteAt(0) > 0x7F) {
112 		serial = ByteString.valueOf(serial.byteAt(0) & 0x7F).concat(serial.bytes(1));
113 	}
114 	return serial;
115 }
116 
117 
118 
119 /**
120  * Issuer a certificate
121  *
122  * @param {Key} publicKey the public key
123  * @param {Object[]} subject an array of RDN objects in the form [ { C:"DE" }, { O:"CardContact" }, { OU:"CardContact Demo CA 1" }, { CN:"TLS client" } ]. 
124  *			See pkixcommon.js for details
125  * @param {String} profile an extension profile name for which a addExtFor<profile> method is defined. Predefined profiles are TLSServer, TLSClient and EmailAndTLSClient.
126  * @param {Object} extvalues JSON object containing extension values
127  * @type X509
128  * @return the new X.509 certificate
129  */
130 X509CA.prototype.issueCertificate = function(publicKey, subject, profile, extvalues) {
131 
132 	var x = new X509CertificateGenerator(this.crypto);
133 
134 	x.encodeECDomainParameter = false;
135 	x.reset();
136 	x.setSerialNumber(this.newSerialNumber());
137 	x.setSignatureAlgorithm(Crypto.RSA_SHA256);
138 	x.setIssuer(this.signerSubject);
139 	var ced = new Date();
140 	var cxd = PKIXCommon.addDays(ced, 1095); // 3 years
141 	x.setNotBefore(ced);
142 	x.setNotAfter(cxd);
143 	x.setSubject(subject);
144 	x.setPublicKey(publicKey);
145 	x.addSubjectKeyIdentifierExtension();
146 	x.addAuthorityKeyIdentifierExtension(this.signerCert.getPublicKey());
147 	x.addBasicConstraintsExtension(false);
148 
149 	if (this.crldp.length > 0) {
150 		x.addCRLDistributionPointURL(this.crldp);
151 	}
152 
153 	if (typeof(this["addExtFor" + profile]) == "function") {
154 		this["addExtFor" + profile](x, extvalues);
155 	}	
156 		
157 	return x.generateX509Certificate(this.signerKey);
158 }
159 
160 
161 
162 /**
163  * Extension handler method for TLS server certificates
164  *
165  * @private
166  */
167 X509CA.prototype.addExtForTLSServer = function(certgen, extvalues) {
168 	
169 	certgen.addKeyUsageExtension( X509CertificateGenerator.keyAgreement |
170 								  X509CertificateGenerator.keyEncipherment);
171 
172 	certgen.addExtendedKeyUsages(["id-csn-369791-tls-server", "id-kp-serverAuth"]);
173 	
174 	var ext = new ASN1("subjectAltName", ASN1.SEQUENCE,
175 						new ASN1("dNSName", 0x82, new ByteString(extvalues["dNSName"], ASCII))
176 					);
177 	certgen.addExtension("id-ce-subjectAltName", false, ext.getBytes());
178 }
179 
180 
181 
182 /**
183  * Extension handler method for TLS client certificates
184  *
185  * @private
186  */
187 X509CA.prototype.addExtForTLSClient = function(certgen, extvalues) {
188 	certgen.addKeyUsageExtension( X509CertificateGenerator.digitalSignature);
189 
190 	// certgen.addExtendedKeyUsages(["id-csn-369791-tls-client", "id-kp-clientAuth"]);
191 	certgen.addExtendedKeyUsages(["id-kp-clientAuth"]);
192 }
193 
194 
195 
196 /**
197  * Extension handler method for certificates suitable for TLS client authentication and e-Mail signature and encryption
198  *
199  * @private
200  */
201 X509CA.prototype.addExtForEmailAndTLSClient = function(certgen, extvalues) {
202 
203 	certgen.addKeyUsageExtension( X509CertificateGenerator.digitalSignature | X509CertificateGenerator.keyEncipherment);
204 
205 //	print(extvalues.email);
206 	var ext = new ASN1("subjectAltName", ASN1.SEQUENCE,
207 						new ASN1("rfc822Name", 0x81, new ByteString(extvalues["email"], ASCII))
208 					);
209 	certgen.addExtension("id-ce-subjectAltName", false, ext.getBytes());
210 
211 	certgen.addExtendedKeyUsages(["id-kp-clientAuth", "id-kp-emailProtection"]);
212 }
213 
214 
215 
216 /**
217  * Issue a CRL
218  *
219  * @type ByteString
220  * @return the encoded CRL
221  */
222 X509CA.prototype.issueCRL = function() {
223 	var x = new CRLGenerator(this.crypto);
224 
225 	x.reset();
226 	x.setSignatureAlgorithm(Crypto.RSA_SHA256);
227 	x.setIssuer(this.signerSubject);
228 	var now = new Date();
229 	x.setThisUpdate(now);
230 	x.setNextUpdate(PKIXCommon.addDays(now, 10));
231 
232 	var crl = x.generateCRL(this.signerKey);
233 	print("CRL:");
234 	print(crl);
235 	return crl.getBytes();
236 }
237 
238 
239 
240 X509CA.dir = GPSystem.mapFilename("", GPSystem.CWD);
241 
242 
243 
244 /**
245  * Setup the CA instance
246  */
247 X509CA.setup = function() {
248 	var crypto = new Crypto();
249 	
250 	var pubKey = new Key();
251 	pubKey.setSize(2048);
252 	pubKey.setType(Key.PUBLIC);
253 
254 	var priKey = new Key();
255 	priKey.setType(Key.PRIVATE);
256 
257 	crypto.generateKeyPair(Crypto.RSA, pubKey, priKey);
258 	
259 	var x = new X509CertificateGenerator(crypto);
260 
261 	x.reset();
262 	x.setSerialNumber((new ByteString("02", HEX)).concat(crypto.generateRandom(7)));
263 	x.setSignatureAlgorithm(Crypto.RSA_SHA256);
264 	var subject = [ { C:"DE" }, { O:"CardContact" }, { CN:"CardContact Demo CA 1" } ];
265 	x.setIssuer(subject);
266 	var ced = new Date();
267 	var cxd = PKIXCommon.addDays(ced, 3650); // 10 years
268 	x.setNotBefore(ced);
269 	x.setNotAfter(cxd);
270 	x.setSubject(subject);
271 	x.setPublicKey(pubKey);
272 	x.addSubjectKeyIdentifierExtension();
273 	x.addAuthorityKeyIdentifierExtension(pubKey);
274 	x.addKeyUsageExtension(	PKIXCommon.keyCertSign |
275 							PKIXCommon.cRLSign );
276 	x.addBasicConstraintsExtension(true, 1);
277 
278 	var cert = x.generateX509Certificate(priKey);
279 	print(cert);
280 	
281 	var ks = new KeyStore("SUN", "JKS");
282 
283 	priKey.setID("DEMOCA");
284 	ks.setKey(priKey, "openscdp", [cert]);
285 	
286 	ks.store(X509CA.dir + "/DEMO-CA.jks", "openscdp");
287 }
288 
289 
290 
291 /**
292  * Test the CA setup
293  */
294 X509CA.test = function() {
295 	var crypto = new Crypto();
296 	var ca = new X509CA(crypto);
297 
298 	var ks = new KeyStore("SUN", "JKS", X509CA.dir + "/DEMO-CA.jks", "openscdp");
299 	var key = new Key();
300 	key.setID("DEMOCA");
301 
302 	ks.getKey(key, "openscdp");
303 	ca.setSignerKey(key);
304 
305 	var cert = ks.getCertificate("DEMOCA");
306 	ca.setSignerCertificate(cert);
307 	
308 	
309 	var subject = [ { C:"DE" }, { O:"CardContact" }, { OU:"CardContact Demo CA 1" }, { CN:"TLS server" } ];
310 	
311 	var pubKey = new Key();
312 	pubKey.setSize(2048);
313 	pubKey.setType(Key.PUBLIC);
314 
315 	var priKey = new Key();
316 	priKey.setType(Key.PRIVATE);
317 
318 	crypto.generateKeyPair(Crypto.RSA, pubKey, priKey);
319 
320 	var extvalues = { dNSName: "www.openehic.org" };
321 	var cert = ca.issueCertificate(pubKey, subject, "TLSServer", extvalues);
322 	print(cert);
323 	
324 	var ks = new KeyStore("SUN", "JKS");
325 
326 	priKey.setID("tlsserver");
327 	ks.setKey(priKey, "openscdp", [cert]);
328 	
329 	ks.store(X509CA.dir + "/www.openehic.org.jks", "openscdp");
330 	
331 	var p8Key = PKCS8.encodeKeyUsingPKCS8Format(priKey, pubKey);
332 	PKIXCommon.writeFileToDisk(X509CA.dir + "/www.openehic.org.pkcs8", p8Key);
333 	PKIXCommon.writeFileToDisk(X509CA.dir + "/www.openehic.org.cer", cert.getBytes());
334 	
335 
336 	var subject = [ { C:"DE" }, { O:"CardContact" }, { OU:"CardContact Demo CA 1" }, { CN:"TLS client" } ];
337 	
338 	var pubKey = new Key();
339 	pubKey.setSize(2048);
340 	pubKey.setType(Key.PUBLIC);
341 
342 	var priKey = new Key();
343 	priKey.setType(Key.PRIVATE);
344 
345 	crypto.generateKeyPair(Crypto.RSA, pubKey, priKey);
346 
347 	var cert = ca.issueCertificate(pubKey, subject, "TLSClient", null);
348 	print(cert);
349 	
350 	var ks = new KeyStore("SUN", "JKS");
351 
352 	priKey.setID("tlsclient");
353 	ks.setKey(priKey, "openscdp", [cert]);
354 	
355 	ks.store(X509CA.dir + "/tlsclient.jks", "openscdp");
356 	
357 	var crl = ca.issueCRL();
358 	PKIXCommon.writeFileToDisk(X509CA.dir + "/democa.crl", crl);
359 }
360