UNPKG

6.53 kBJavaScriptView Raw
1var forge = require('node-forge');
2
3// a hexString is considered negative if it's most significant bit is 1
4// because serial numbers use ones' complement notation
5// this RFC in section 4.1.2.2 requires serial numbers to be positive
6// http://www.ietf.org/rfc/rfc5280.txt
7function toPositiveHex(hexString){
8 var mostSiginficativeHexAsInt = parseInt(hexString[0], 16);
9 if (mostSiginficativeHexAsInt < 8){
10 return hexString;
11 }
12
13 mostSiginficativeHexAsInt -= 8;
14 return mostSiginficativeHexAsInt.toString() + hexString.substring(1);
15}
16
17function getAlgorithm(key) {
18 switch (key) {
19 case 'sha256':
20 return forge.md.sha256.create();
21 default:
22 return forge.md.sha1.create();
23 }
24}
25
26/**
27 *
28 * @param {forge.pki.CertificateField[]} attrs Attributes used for subject and issuer.
29 * @param {object} options
30 * @param {number} [options.days=365] the number of days before expiration
31 * @param {number} [options.keySize=1024] the size for the private key in bits
32 * @param {object} [options.extensions] additional extensions for the certificate
33 * @param {string} [options.algorithm="sha1"] The signature algorithm sha256 or sha1
34 * @param {boolean} [options.pkcs7=false] include PKCS#7 as part of the output
35 * @param {boolean} [options.clientCertificate=false] generate client cert signed by the original key
36 * @param {string} [options.clientCertificateCN="John Doe jdoe123"] client certificate's common name
37 * @param {function} [done] Optional callback, if not provided the generation is synchronous
38 * @returns
39 */
40exports.generate = function generate(attrs, options, done) {
41 if (typeof attrs === 'function') {
42 done = attrs;
43 attrs = undefined;
44 } else if (typeof options === 'function') {
45 done = options;
46 options = {};
47 }
48
49 options = options || {};
50
51 var generatePem = function (keyPair) {
52 var cert = forge.pki.createCertificate();
53
54 cert.serialNumber = toPositiveHex(forge.util.bytesToHex(forge.random.getBytesSync(9))); // the serial number can be decimal or hex (if preceded by 0x)
55
56 cert.validity.notBefore = options.notBeforeDate || new Date();
57
58 var notAfter = new Date();
59 cert.validity.notAfter = notAfter;
60 cert.validity.notAfter.setDate(notAfter.getDate() + (options.days || 365));
61
62 attrs = attrs || [{
63 name: 'commonName',
64 value: 'example.org'
65 }, {
66 name: 'countryName',
67 value: 'US'
68 }, {
69 shortName: 'ST',
70 value: 'Virginia'
71 }, {
72 name: 'localityName',
73 value: 'Blacksburg'
74 }, {
75 name: 'organizationName',
76 value: 'Test'
77 }, {
78 shortName: 'OU',
79 value: 'Test'
80 }];
81
82 cert.setSubject(attrs);
83 cert.setIssuer(attrs);
84
85 cert.publicKey = keyPair.publicKey;
86
87 cert.setExtensions(options.extensions || [{
88 name: 'basicConstraints',
89 cA: true
90 }, {
91 name: 'keyUsage',
92 keyCertSign: true,
93 digitalSignature: true,
94 nonRepudiation: true,
95 keyEncipherment: true,
96 dataEncipherment: true
97 }, {
98 name: 'subjectAltName',
99 altNames: [{
100 type: 6, // URI
101 value: 'http://example.org/webid#me'
102 }]
103 }]);
104
105 cert.sign(keyPair.privateKey, getAlgorithm(options && options.algorithm));
106
107 const fingerprint = forge.md.sha1
108 .create()
109 .update(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes())
110 .digest()
111 .toHex()
112 .match(/.{2}/g)
113 .join(':');
114
115 var pem = {
116 private: forge.pki.privateKeyToPem(keyPair.privateKey),
117 public: forge.pki.publicKeyToPem(keyPair.publicKey),
118 cert: forge.pki.certificateToPem(cert),
119 fingerprint: fingerprint,
120 };
121
122 if (options && options.pkcs7) {
123 var p7 = forge.pkcs7.createSignedData();
124 p7.addCertificate(cert);
125 pem.pkcs7 = forge.pkcs7.messageToPem(p7);
126 }
127
128 if (options && options.clientCertificate) {
129 var clientkeys = forge.pki.rsa.generateKeyPair(options.clientCertificateKeySize || 1024);
130 var clientcert = forge.pki.createCertificate();
131 clientcert.serialNumber = toPositiveHex(forge.util.bytesToHex(forge.random.getBytesSync(9)));
132 clientcert.validity.notBefore = new Date();
133 clientcert.validity.notAfter = new Date();
134 clientcert.validity.notAfter.setFullYear(clientcert.validity.notBefore.getFullYear() + 1);
135
136 var clientAttrs = JSON.parse(JSON.stringify(attrs));
137
138 for(var i = 0; i < clientAttrs.length; i++) {
139 if(clientAttrs[i].name === 'commonName') {
140 if( options.clientCertificateCN )
141 clientAttrs[i] = { name: 'commonName', value: options.clientCertificateCN };
142 else
143 clientAttrs[i] = { name: 'commonName', value: 'John Doe jdoe123' };
144 }
145 }
146
147 clientcert.setSubject(clientAttrs);
148
149 // Set the issuer to the parent key
150 clientcert.setIssuer(attrs);
151
152 clientcert.publicKey = clientkeys.publicKey;
153
154 // Sign client cert with root cert
155 clientcert.sign(keyPair.privateKey);
156
157 pem.clientprivate = forge.pki.privateKeyToPem(clientkeys.privateKey);
158 pem.clientpublic = forge.pki.publicKeyToPem(clientkeys.publicKey);
159 pem.clientcert = forge.pki.certificateToPem(clientcert);
160
161 if (options.pkcs7) {
162 var clientp7 = forge.pkcs7.createSignedData();
163 clientp7.addCertificate(clientcert);
164 pem.clientpkcs7 = forge.pkcs7.messageToPem(clientp7);
165 }
166 }
167
168 var caStore = forge.pki.createCaStore();
169 caStore.addCertificate(cert);
170
171 try {
172 forge.pki.verifyCertificateChain(caStore, [cert],
173 function (vfd, depth, chain) {
174 if (vfd !== true) {
175 throw new Error('Certificate could not be verified.');
176 }
177 return true;
178 });
179 }
180 catch(ex) {
181 throw new Error(ex);
182 }
183
184 return pem;
185 };
186
187 var keySize = options.keySize || 1024;
188
189 if (done) { // async scenario
190 return forge.pki.rsa.generateKeyPair({ bits: keySize }, function (err, keyPair) {
191 if (err) { return done(err); }
192
193 try {
194 return done(null, generatePem(keyPair));
195 } catch (ex) {
196 return done(ex);
197 }
198 });
199 }
200
201 var keyPair = options.keyPair ? {
202 privateKey: forge.pki.privateKeyFromPem(options.keyPair.privateKey),
203 publicKey: forge.pki.publicKeyFromPem(options.keyPair.publicKey)
204 } : forge.pki.rsa.generateKeyPair(keySize);
205
206 return generatePem(keyPair);
207};