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