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