123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- var forge = require('node-forge');
- // a hexString is considered negative if it's most significant bit is 1
- // because serial numbers use ones' complement notation
- // this RFC in section 4.1.2.2 requires serial numbers to be positive
- // http://www.ietf.org/rfc/rfc5280.txt
- function toPositiveHex(hexString){
- var mostSiginficativeHexAsInt = parseInt(hexString[0], 16);
- if (mostSiginficativeHexAsInt < 8){
- return hexString;
- }
- mostSiginficativeHexAsInt -= 8;
- return mostSiginficativeHexAsInt.toString() + hexString.substring(1);
- }
- function getAlgorithm(key) {
- switch (key) {
- case 'sha256':
- return forge.md.sha256.create();
- default:
- return forge.md.sha1.create();
- }
- }
- /**
- *
- * @param {forge.pki.CertificateField[]} attrs Attributes used for subject and issuer.
- * @param {object} options
- * @param {number} [options.days=365] the number of days before expiration
- * @param {number} [options.keySize=1024] the size for the private key in bits
- * @param {object} [options.extensions] additional extensions for the certificate
- * @param {string} [options.algorithm="sha1"] The signature algorithm sha256 or sha1
- * @param {boolean} [options.pkcs7=false] include PKCS#7 as part of the output
- * @param {boolean} [options.clientCertificate=false] generate client cert signed by the original key
- * @param {string} [options.clientCertificateCN="John Doe jdoe123"] client certificate's common name
- * @param {function} [done] Optional callback, if not provided the generation is synchronous
- * @returns
- */
- exports.generate = function generate(attrs, options, done) {
- if (typeof attrs === 'function') {
- done = attrs;
- attrs = undefined;
- } else if (typeof options === 'function') {
- done = options;
- options = {};
- }
- options = options || {};
- var generatePem = function (keyPair) {
- var cert = forge.pki.createCertificate();
- cert.serialNumber = toPositiveHex(forge.util.bytesToHex(forge.random.getBytesSync(9))); // the serial number can be decimal or hex (if preceded by 0x)
- cert.validity.notBefore = new Date();
- cert.validity.notAfter = new Date();
- cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + (options.days || 365));
- attrs = attrs || [{
- name: 'commonName',
- value: 'example.org'
- }, {
- name: 'countryName',
- value: 'US'
- }, {
- shortName: 'ST',
- value: 'Virginia'
- }, {
- name: 'localityName',
- value: 'Blacksburg'
- }, {
- name: 'organizationName',
- value: 'Test'
- }, {
- shortName: 'OU',
- value: 'Test'
- }];
- cert.setSubject(attrs);
- cert.setIssuer(attrs);
- cert.publicKey = keyPair.publicKey;
- cert.setExtensions(options.extensions || [{
- name: 'basicConstraints',
- cA: true
- }, {
- name: 'keyUsage',
- keyCertSign: true,
- digitalSignature: true,
- nonRepudiation: true,
- keyEncipherment: true,
- dataEncipherment: true
- }, {
- name: 'subjectAltName',
- altNames: [{
- type: 6, // URI
- value: 'http://example.org/webid#me'
- }]
- }]);
- cert.sign(keyPair.privateKey, getAlgorithm(options && options.algorithm));
- const fingerprint = forge.md.sha1
- .create()
- .update(forge.asn1.toDer(forge.pki.certificateToAsn1(cert)).getBytes())
- .digest()
- .toHex()
- .match(/.{2}/g)
- .join(':');
- var pem = {
- private: forge.pki.privateKeyToPem(keyPair.privateKey),
- public: forge.pki.publicKeyToPem(keyPair.publicKey),
- cert: forge.pki.certificateToPem(cert),
- fingerprint: fingerprint,
- };
- if (options && options.pkcs7) {
- var p7 = forge.pkcs7.createSignedData();
- p7.addCertificate(cert);
- pem.pkcs7 = forge.pkcs7.messageToPem(p7);
- }
- if (options && options.clientCertificate) {
- var clientkeys = forge.pki.rsa.generateKeyPair(1024);
- var clientcert = forge.pki.createCertificate();
- clientcert.serialNumber = toPositiveHex(forge.util.bytesToHex(forge.random.getBytesSync(9)));
- clientcert.validity.notBefore = new Date();
- clientcert.validity.notAfter = new Date();
- clientcert.validity.notAfter.setFullYear(clientcert.validity.notBefore.getFullYear() + 1);
- var clientAttrs = JSON.parse(JSON.stringify(attrs));
- for(var i = 0; i < clientAttrs.length; i++) {
- if(clientAttrs[i].name === 'commonName') {
- if( options.clientCertificateCN )
- clientAttrs[i] = { name: 'commonName', value: options.clientCertificateCN };
- else
- clientAttrs[i] = { name: 'commonName', value: 'John Doe jdoe123' };
- }
- }
- clientcert.setSubject(clientAttrs);
- // Set the issuer to the parent key
- clientcert.setIssuer(attrs);
- clientcert.publicKey = clientkeys.publicKey;
- // Sign client cert with root cert
- clientcert.sign(keyPair.privateKey);
- pem.clientprivate = forge.pki.privateKeyToPem(clientkeys.privateKey);
- pem.clientpublic = forge.pki.publicKeyToPem(clientkeys.publicKey);
- pem.clientcert = forge.pki.certificateToPem(clientcert);
- if (options.pkcs7) {
- var clientp7 = forge.pkcs7.createSignedData();
- clientp7.addCertificate(clientcert);
- pem.clientpkcs7 = forge.pkcs7.messageToPem(clientp7);
- }
- }
- var caStore = forge.pki.createCaStore();
- caStore.addCertificate(cert);
- try {
- forge.pki.verifyCertificateChain(caStore, [cert],
- function (vfd, depth, chain) {
- if (vfd !== true) {
- throw new Error('Certificate could not be verified.');
- }
- return true;
- });
- }
- catch(ex) {
- throw new Error(ex);
- }
- return pem;
- };
- var keySize = options.keySize || 1024;
- if (done) { // async scenario
- return forge.pki.rsa.generateKeyPair({ bits: keySize }, function (err, keyPair) {
- if (err) { return done(err); }
- try {
- return done(null, generatePem(keyPair));
- } catch (ex) {
- return done(ex);
- }
- });
- }
- var keyPair = options.keyPair ? {
- privateKey: forge.pki.privateKeyFromPem(options.keyPair.privateKey),
- publicKey: forge.pki.publicKeyFromPem(options.keyPair.publicKey)
- } : forge.pki.rsa.generateKeyPair(keySize);
- return generatePem(keyPair);
- };
|