You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.5 KiB

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