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 PKCS#10 generator class based on RFC5280 25 */ 26 27 var PKIXCommon = require('scsh/x509/PKIXCommon').PKIXCommon; 28 29 30 31 /** 32 * Create a PKCS#10 certificate request 33 * 34 * @class Class implementing a PKCS#10 certificate request 35 * @constructor 36 * 37 * @param {Crypto} crypto the crypto provider to use for signing operations 38 */ 39 function PKCS10Generator(crypto) { 40 this.crypto = crypto; 41 this.reset(); 42 } 43 44 exports.PKCS10Generator = PKCS10Generator; 45 46 47 48 /** 49 * Resets all internal state variables. 50 * 51 */ 52 PKCS10Generator.prototype.reset = function() { 53 this.extensions = new Array(); 54 this.attributes = new Array(); 55 } 56 57 58 59 /** 60 * Sets the subject name. 61 * 62 * <p>The subject name must be a JavaScript object containing the properties:</p> 63 * <ul> 64 * <li>C - the country</li> 65 * <li>O - the organization</li> 66 * <li>OU - the organization unit</li> 67 * <li>CN - the common name</li> 68 * </ul> 69 * <p>Example:</p> 70 * <pre> 71 * var subject = { C:"UT", O:"ACME Corporation", CN:"Joe Doe" }; 72 * </pre> 73 * @see PKIXCommon.encodeName() 74 * 75 * The subject can also be passed an already encoded ASN.1 structure 76 * 77 * @param {Object} subject the subject name 78 */ 79 PKCS10Generator.prototype.setSubject = function(subject) { 80 this.subject = subject; 81 } 82 83 84 85 /** 86 * Sets the subjects public key 87 * 88 * <p>The methods accepts ECC and RSA Public Keys.</p> 89 * 90 * @param {Key} publicKey the subjects public key 91 */ 92 PKCS10Generator.prototype.setPublicKey = function(publicKey) { 93 this.publicKey = publicKey; 94 } 95 96 97 98 /** 99 * Sets the signature algorithm. Currently only Crypto.RSA is supported 100 * 101 * @param {Number} alg the signature algorithm, only Crypto.RSA supported 102 */ 103 PKCS10Generator.prototype.setSignatureAlgorithm = function(alg) { 104 this.signatureAlgorithm = alg; 105 } 106 107 108 109 /** 110 * Adds an extension to the certificate 111 * 112 * <p>The structure is defined as:</p> 113 * <pre> 114 * Extension ::= SEQUENCE { 115 * extnID OBJECT IDENTIFIER, 116 * extnValue OCTET STRING 117 * -- contains the DER encoding of an ASN.1 value 118 * -- corresponding to the extension type identified 119 * -- by extnID 120 * } 121 *</pre> 122 * @param {String} extnID the extensions object identifier 123 * @param {ByteString} the extension value as ByteString 124 */ 125 PKCS10Generator.prototype.addExtension = function(extnID, extnValue) { 126 var t = new ASN1("extension", ASN1.SEQUENCE, 127 new ASN1("extnID", ASN1.OBJECT_IDENTIFIER, new ByteString(extnID, OID)) 128 ); 129 130 t.add(new ASN1("extnValue", ASN1.OCTET_STRING, extnValue)); 131 this.extensions.push(t); 132 } 133 134 135 136 /** 137 * Adds the key usage extension. 138 * 139 * <p>The following flags are defined:</p> 140 * <pre> 141 * PKCS10Generator.digitalSignature = 0x0080; 142 * PKCS10Generator.nonRepudiation = 0x0040; 143 * PKCS10Generator.keyEncipherment = 0x0020; 144 * PKCS10Generator.dataEncipherment = 0x0010; 145 * PKCS10Generator.keyAgreement = 0x0008; 146 * PKCS10Generator.keyCertSign = 0x0004; 147 * PKCS10Generator.cRLSign = 0x0002; 148 * PKCS10Generator.encipherOnly = 0x0001; 149 * PKCS10Generator.decipherOnly = 0x8000; 150 * </pre> 151 * @param {Number} the key usage flags as combination of the flags defined above. 152 */ 153 PKCS10Generator.prototype.addKeyUsageExtension = function(flags) { 154 var t = new ASN1(ASN1.BIT_STRING, PKIXCommon.bitstringForInteger(flags)); 155 this.addExtension("2.5.29.15", t.getBytes()); 156 } 157 158 // Deprecated: Use PKIXCommon. instead 159 PKCS10Generator.digitalSignature = 0x0080; 160 PKCS10Generator.nonRepudiation = 0x0040; 161 PKCS10Generator.keyEncipherment = 0x0020; 162 PKCS10Generator.dataEncipherment = 0x0010; 163 PKCS10Generator.keyAgreement = 0x0008; 164 PKCS10Generator.keyCertSign = 0x0004; 165 PKCS10Generator.cRLSign = 0x0002; 166 PKCS10Generator.encipherOnly = 0x0001; 167 PKCS10Generator.decipherOnly = 0x8000; 168 169 170 171 /** 172 * Adds extended key usages 173 * 174 * @param {String[]} the list of extended key usage object identifier 175 */ 176 PKCS10Generator.prototype.addExtendedKeyUsageExtension = function(keyusages) { 177 var t = new ASN1(ASN1.SEQUENCE); 178 for (var i = 0; i < keyusages.length; i++) { 179 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString(keyusages[i], OID))); 180 } 181 this.addExtension("id-ce-extKeyUsage", t.getBytes()); 182 } 183 184 185 186 /** 187 * Adds the BasicConstraints extension. 188 * 189 * @param {Boolean} cA the certificate belongs to a CA 190 * @param {Number} pathLenConstraint the maximum number of subordinate CA certificates 191 */ 192 PKCS10Generator.prototype.addBasicConstraintsExtension = function(cA, pathLenConstraint) { 193 var t = new ASN1("BasicConstraints",ASN1.SEQUENCE); 194 if (cA) { 195 t.add(new ASN1("cA", ASN1.BOOLEAN, new ByteString("FF", HEX))); 196 } 197 if (pathLenConstraint >= 0) { 198 var bb = new ByteBuffer(); 199 bb.append(pathLenConstraint); 200 t.add(new ASN1("pathLenConstraint", ASN1.INTEGER, bb.toByteString())); 201 } 202 this.addExtension("2.5.29.19", t.getBytes()); 203 } 204 205 206 207 /** 208 * Gets the subject name as TLV object 209 * 210 * @return the issuer RDNSequence 211 * @type ASN1 212 */ 213 PKCS10Generator.prototype.getSubject = function() { 214 if (this.subject instanceof ASN1) { 215 return this.subject; 216 } 217 return PKIXCommon.encodeName(this.subject); 218 } 219 220 221 222 /** 223 * Gets the subject's public key as TLV object 224 * 225 * @return the subject's public key info 226 * @type ASN1 227 */ 228 PKCS10Generator.prototype.getSubjectPublicKeyInfo = function() { 229 if (this.publicKey.getComponent(Key.MODULUS)) { 230 return PKIXCommon.createRSASubjectPublicKeyInfo(this.publicKey); 231 } else { 232 return PKIXCommon.createECSubjectPublicKeyInfo(this.publicKey, this.encodeECDomainParameter); 233 } 234 } 235 236 237 238 /** 239 * Gets the extension attribute as TLV object 240 * 241 * @return the certificate extensions 242 * @type ASN1 243 */ 244 PKCS10Generator.prototype.getExtensions = function() { 245 var t = new ASN1("extensions", ASN1.SEQUENCE); 246 t.add(new ASN1(ASN1.OBJECT_IDENTIFIER, new ByteString("1.2.840.113549 1 9 14", OID))); 247 var s = new ASN1("extensions", ASN1.SET); 248 t.add(s); 249 var l = new ASN1("extensions", ASN1.SEQUENCE); 250 s.add(l); 251 252 for (var i = 0; i < this.extensions.length; i++) { 253 l.add(this.extensions[i]); 254 } 255 return t; 256 } 257 258 259 260 /** 261 * Gets the attributes as TLV object 262 * 263 * @return the request attributes 264 * @type ASN1 265 */ 266 PKCS10Generator.prototype.getAttributes = function() { 267 var t = new ASN1("attributes", 0xA0); 268 269 if (this.extensions.length > 0) { 270 t.add(this.getExtensions()); 271 } 272 273 for (var i = 0; i < this.attributes.length; i++) { 274 t.add(this.attributes[i]); 275 } 276 277 return t; 278 } 279 280 281 282 /** 283 * Gets the part of the request that will be signed 284 * 285 * @return the TBSCertificate part 286 * @type ASN1 287 */ 288 PKCS10Generator.prototype.getTbsRequest = function() { 289 var t = new ASN1("certificationRequestInfo", ASN1.SEQUENCE); 290 t.add(new ASN1(ASN1.INTEGER, new ByteString("00", HEX))); 291 t.add(this.getSubject()); 292 t.add(this.getSubjectPublicKeyInfo()); 293 t.add(this.getAttributes()); 294 return t; 295 } 296 297 298 299 /** 300 * Generates the certificate. 301 * 302 * @return the generated certificate 303 * @type ASN1 304 */ 305 PKCS10Generator.prototype.generateCertificationRequest = function(privateKey) { 306 var request = new ASN1("certificationRequest", ASN1.SEQUENCE); 307 308 var tbs = this.getTbsRequest(); 309 request.add(tbs); 310 request.add(PKIXCommon.encodeSignatureAlgorithm(this.signatureAlgorithm)); 311 312 var signature = this.crypto.sign(privateKey, this.signatureAlgorithm, tbs.getBytes()); 313 signature = (new ByteString("00", HEX)).concat(signature); 314 315 var signatureValue = new ASN1("signatureValue", ASN1.BIT_STRING, signature); 316 request.add(signatureValue); 317 318 return request; 319 } 320 321 322 323 PKCS10Generator.test = function() { 324 325 var crypto = new Crypto(); 326 327 var reqPrivateKey = new Key(); 328 reqPrivateKey.setType(Key.PRIVATE); 329 330 var reqPublicKey = new Key(); 331 reqPublicKey.setType(Key.PUBLIC); 332 reqPublicKey.setSize(1024); 333 334 crypto.generateKeyPair(Crypto.RSA, reqPublicKey, reqPrivateKey); 335 336 var x = new PKCS10Generator(crypto); 337 338 x.reset(); 339 x.setSignatureAlgorithm(Crypto.RSA_SHA256); 340 341 var subject = [{C:"UT"}, {O:"Utopia CA"}, {OU:"ACME Corporation"}, {CN:"Joe Doe"} ]; 342 343 x.setSubject(subject); 344 345 x.setPublicKey(reqPublicKey); 346 347 x.addKeyUsageExtension( PKIXCommon.digitalSignature | 348 PKIXCommon.keyCertSign | 349 PKIXCommon.cRLSign ); 350 351 var req = x.generateCertificationRequest(reqPrivateKey); 352 var fn = GPSystem.mapFilename("cert.csr", GPSystem.USR); 353 PKIXCommon.writeFileToDisk(fn, req.getBytes()); 354 355 print(req); 356 357 return req; 358 } 359