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