      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