Compare commits

..
40 Commits
Author SHA1 Message Date
mcbueller 531be027f5 package-lock update 2025-02-03 15:01:48 +00:00
mcbueller 3deb4ccf9f CA missing required keyUsage and extKeyUsage 2025-01-29 19:08:45 +00:00
Lyndsey Strauss b44c79c3af updating package.json version number 2022-07-05 15:19:27 +00:00
buhler 39dbf747e6 Merge pull request #22 from NationalSecurityAgency/add-new-rdn-types
add new attribute types to certificates
2022-07-05 11:03:29 -04:00
Lyndsey Strauss 663bb10e0b added note on only server cert options in table 2022-07-05 15:01:10 +00:00
Lyndsey Strauss 0c30721bca added table for attribute types 2022-07-05 14:16:56 +00:00
Lyndsey Strauss 5c976801fb add new attribute types to certificates 2022-06-29 21:01:26 +00:00
straussly e7f88431c6 Merge pull request #21 from typoBot3000/main
fix some typos
2022-05-13 14:37:23 -04:00
typoBot3000 f8e3721210 fix some typos
fixed some typos
2022-05-09 18:47:53 -04:00
Lyndsey Strauss e8deada753 updating madcert to 2.3.1 and node-forge version number in package.json 2022-03-30 16:03:00 +00:00
straussly a21293fe94 Merge pull request #20 from NationalSecurityAgency/dependabot/npm_and_yarn/node-forge-1.3.0
Bump node-forge from 1.2.1 to 1.3.0
2022-03-30 10:55:52 -04:00
dependabot[bot] b9067f2342 Bump node-forge from 1.2.1 to 1.3.0
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-03-25 23:54:01 +00:00
straussly aa4746a13e updating version number 2022-02-10 18:03:31 -05:00
straussly d978f6acb9 Merge pull request #19 from NationalSecurityAgency/remove-key-usages
remove nonRepudiation and dataEncipherment key usages
2022-02-10 11:07:54 -05:00
rtbuhle 3bc0fefe5f remove nonRepudiation and datEncipherment key usages 2022-02-10 11:14:03 +00:00
buhler 0828d1b4cf Merge pull request #18 from NationalSecurityAgency/upgrade-mocha-fsextra-yargs
upgrade mocha, fsextra, yargs, and madcert version number
2022-01-28 07:44:23 -05:00
Lyndsey Strauss f14953fc14 upgrade date fns, chai,and node forge 2022-01-27 22:49:10 +00:00
Lyndsey Strauss 52dae35d1d upgrade mocha, fsextra, yargs, and madcert version number 2022-01-27 15:05:20 +00:00
buhler ae650a88a6 Merge pull request #17 from NationalSecurityAgency/server-cert-cn-fix
added fix to automatically fill in cn from server name
2022-01-20 07:20:56 -05:00
straussly 594173b92e Merge pull request #16 from NationalSecurityAgency/dependabot/npm_and_yarn/node-forge-1.0.0
Bump node-forge from 0.10.0 to 1.0.0
2022-01-19 08:43:37 -05:00
Lyndsey Strauss d0d6b2ff0b added fix to automatically fill in cn from server name 2022-01-19 13:42:49 +00:00
dependabot[bot] 8e8b3807da Bump node-forge from 0.10.0 to 1.0.0
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 0.10.0 to 1.0.0.
- [Release notes](https://github.com/digitalbazaar/forge/releases)
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/0.10.0...v1.0.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2022-01-15 07:53:29 +00:00
Seth Marple swmarpl 3e303ae429 Update version to 2.2.0 2021-07-06 16:56:13 +00:00
buhler b9b59e30d4 Merge pull request #15 from Marpyre/cert-to-json
Add capability to print MADCert certificates via commandline
2021-07-06 12:08:16 -04:00
Seth Marple swmarpl c966de9bec Add documentation for print command and certToJSON function to README.md 2021-07-06 15:42:45 +00:00
Seth Marple swmarpl 7f40edba5b Modifying madcert print commandline api to match existing api 2021-06-24 13:12:51 +00:00
Seth Marple swmarpl 0a339b75ad Add madcert commandline commands and options for cert printing. 2021-06-22 19:18:19 +00:00
Seth Marple swmarpl daaefa8d35 Use global variable across tests 2021-06-21 19:38:21 +00:00
Seth Marple swmarpl b0bd906e5f Remove .only from tests 2021-06-21 19:01:05 +00:00
Seth Marple swmarpl 2b514276e5 Add cert to JSON capability 2021-06-21 18:55:42 +00:00
buhler bd11bed32a Merge pull request #14 from Marpyre/v2.1.3
Release version 2.1.3
2021-06-10 14:57:56 -04:00
Seth Marple swmarpl 371d563b03 Release version 2.1.3 2021-06-10 18:54:43 +00:00
Kyle Miracle cd8ee3ed07 Fix docs typo for CLI (intermediate CA) (#13)
The documentation example describing how to create an intermediate CA has a typo.
2021-06-10 14:27:05 -04:00
buhler 09f5993dda update version to 2.1.2 (#12) 2021-06-10 10:03:48 -04:00
buhlerandMarpyre 34582c77e9 add node.js examples to the readme as well as additional cli examples (#11)
* add node.js examples to the readme as well as additional cli examples

* provide node.js basePath example

Co-authored-by: Marpyre <[email protected]>
2021-06-10 09:25:13 -04:00
Seth Marple swmarpl 40e19eb40c Release version 2.1.1 2021-05-27 13:35:54 +00:00
Seth Marple swmarpl b3aa9a4872 Add keywords to package.json 2021-05-27 13:34:15 +00:00
Seth Marple swmarpl 88dee9e9d2 Release version 2.1.0 2021-05-27 12:30:34 +00:00
buhler 101d1cc751 Merge pull request #10 from Marpyre/certificate-improvements
Add and remove and mark critical extensions based on certificate lint…
2021-05-27 07:28:51 -04:00
Seth Marple swmarpl 9cc105eb20 Add and remove and mark critical extensions based on certificate linting to improve validity of generated certificates. 2021-05-26 19:05:13 +00:00
16 changed files with 817 additions and 595 deletions

No files matched your search

+2 -1
View File
@@ -1 +1,2 @@
node_modules/
node_modules/
.idea/
+162 -24
View File
@@ -2,50 +2,119 @@
Use MADCert to create root and intermediate Certificate Authorities, issue user and server certificates, etc for testing purposes.
MADCert is a cross-platform tool that does not require OpenSSL or any other programs to be installed in the Operating System.
MADCert is a cross-platform tool that consists of a certificate generator, a file system certificate manager, and a command line interface.
## Prerequisites
MADCert does not require openssl or any other programs to be installed in the Operating System.
- You will need Node.js installed on your operating system. For Linux and Mac OS X, we recommend using [nvm - Node Version Manager](https://github.com/nvm-sh/nvm). For Windows, we recommend using [nvm-windows](https://github.com/coreybutler/nvm-windows).
Both Node.js and CLI examples are provided below.
## Installation
## Usage
### Node.js Examples
To add MADCert to your project:
npm install madcert
## Usage
var madcert = require("madcert")
npx madcert <command> <arguments> [options]
- Create a Certificate Authority (CA)
## Environment
madcert.createCACert("Test Root CA", {validFrom: "2019-08-08", validTo: "2024-08-09"})
- `MADCERT_LH_SUBJECT_ALT_DNS_NAME` - Only used when creating a localhost server certificate (`--localhost true`). When generating a localhost server certificate, MADCert will add a subject alternative DNS name to the certificate. This environment variable can be used to specify the subject alternative DNS name that will be added. If this environment variable is not set, then the host name will be used.
- Create an Intermediate CA (Root CA must exist)
## Commands, Arguments, and Options
madcert.createIntermediateCACert("Test CA","rootCA", {validFrom: "2019-08-08", validTo: "2024-08-09"});
When you run MADCert help (using `npx madcert --help`), it will display all of the commands, arguments, and options.
- Create a Server Certificate (CA must exist)
### Arguments
madcert.createServerCert("Test Server", "test-ca", false, {validFrom: "2019-08-08", validTo: "2024-08-09"});
- `name` Name is used for the name of the directory under which PKI files are created. If the `--common-name` option is not used then `name` will also be used for the Common Name in the certificate subject.
- `ca_name` CA name is used to identify the Certificate Authority to use when creating or removing users and servers.
- `root_ca_name` When creating an intermediate Certificate Authority, `root_ca_name` identifies the root CA that issues the intermediate CA's certificate.
- Create a localhost Server Certificate (CA must exist)
### Options
madcert.createServerCert("localhost", "test-ca", true, {validFrom: "2019-08-08", validTo: "2024-08-09"});
The MADCert options are grouped in the help output by the commands that they can be used with.
Note: A subject alternative name is automatically added to the generated certificate. See ENVIRONMENT above.
- Creation Options - these options can be used with any of the commands that create PKI files for CAs, servers, and users
- User Creation Options - these options can be used with the `user-create` command
- Server Creation Options - these options can be used with the `server-create` command
- Options - these options are not specific to any particular command
- Create a User Certificate with CN=test-user (CA must exist)
## Certificate Storage Location
madcert.createUserCert("test-user", "test-ca", {commonName: "test-user", organizationalUnits: ["SECURITY", "A456"], organizations: ["Some Organization"], country: "US", validFrom: "2019-08-08", validTo: "2024-08-09"});
By default, the certs created by MADCert will be placed under a "pki" folder in the current working directory. MADCert commands also have the `--path` or `-p` option to specify a top-level path where the certs will be stored. It is important that all of the certs are placed under the same top-level folder, so either run MADCert from within the same directory each time or always provide the same path option when calling the MADCert commands.
- Create a User Certificate CN=Alice Example (CA must exist)
madcert.createUserCert("Alice Example", "test-ca", {commonName: "Alice Example", organizationalUnits: ["SECURITY", "A456"], organizations: ["Some Organization"], country: "US", validFrom: "2019-08-08", validTo: "2024-08-09"});
- List CA Certificates
madcert.listCACerts();
- List Server Certificates
madcert.listServerCerts();
- List User Certificates
madcert.listUserCerts();
- Remove a CA Certificate
madcert.removeCACert("test-root-ca");
- Remove a Server Certificate
madcert.removeServerCert("test-server", "test-ca");
- Remove a User Certificate
madcert.removeUserCert("test-user", "test-ca");
- Retrieve a CA Certificate as JSON
madcert.caCertToJSON("test-root-ca");
- Retrieve a Server Certificate as JSON
madcert.serverCertToJSON("test-server", "test-ca");
- Retrieve a User Certificate as JSON
madcert.userCertToJSON("test-user", "test-ca");
- Convert name to a File Path Friendly Name
madcert.normalizeName("Test 123");
> 'test-123'
### Additional Node.js Options
#### Certificate Storage Location
By default, the certs created by MADCert will be placed under a "pki" folder in the current working directory. MADCert commands also have the `basePath` option to specify a top-level path where the certs will be stored. It is important that all of the certs are placed under the same top-level folder, so either run MADCert from within the same location each time or always provide the same basepath option when calling the MADCert commands.
- Example Specifying the Certificate Storage Location (available on all certificate related commands)
madcert.createCACert("Test Root CA", {validFrom: "2019-08-08", validTo: "2024-08-09", basePath: "/some/path"})
### CLI Examples
- General Syntax
npx madcert <command> <arguments> [options]
- Help
npx madcert --help
The MADCert options are grouped in the help output by the commands that they can be used with.
- Creation Options - these options can be used with any of the commands that create PKI files for CAs, servers, and users
- User Creation Options - these options can be used with the `user-create` command
- Server Creation Options - these options can be used with the `server-create` command
- Print Options - these options can be used with the `ca-print`, `server-print`, and `user-print` commands
- Options - these options are not specific to any particular command
## Examples
- Create a Certificate Authority (CA)
@@ -53,7 +122,7 @@ By default, the certs created by MADCert will be placed under a "pki" folder in
- Create an Intermediate CA (Root CA must exist)
npx madcert ca-create test-ca test-root-ca --common-name "Test CA" --org-unit "orgA" --org-unit "A123" --org "Some Organization" --country "US"
npx madcert ca-intermediate-create test-ca test-root-ca --common-name "Test CA" --org-unit "orgA" --org-unit "A123" --org "Some Organization" --country "US"
- Create a Server Certificate (CA must exist)
@@ -72,3 +141,72 @@ By default, the certs created by MADCert will be placed under a "pki" folder in
- Create a User Certificate CN=Alice Example (CA must exist)
npx madcert user-create test-user test-ca --common-name "Alice Example" --org-unit "SECURITY" --org-unit "A456" --org "Some Organization" --country "US"
- List CA Certificates
npx madcert ca-list
- List Server Certificates
npx madcert server-list
- List User Certificates
npx madcert user-list
- Remove a CA Certificate
npx madcert ca-remove test-ca
- Remove a Server Certificate
npx madcert server-remove test-server test-ca
- Remove a User Certificate
npx madcert user-remove test-user test-ca
- Print a CA Certificate
npx madcert ca-print test-ca
- Print a Server Certificate
npx madcert server-print test-server test-ca
- Print a User Certificate
npx madcert user-print test-user test-ca
### Additional CLI Options
#### Certificate Storage Location
By default, the certs created by MADCert will be placed under a "pki" folder in the current working directory. MADCert commands also have the `--path` or `-p` option to specify a top-level path where the certs will be stored. It is important that all of the certs are placed under the same top-level folder, so either run MADCert from within the same directory each time or always provide the same path option when calling the MADCert commands.
#### Environment
- `MADCERT_LH_SUBJECT_ALT_DNS_NAME` - Only used when creating a localhost server certificate (`--localhost true`). When generating a localhost server certificate, MADCert will add a subject alternative DNS name to the certificate. This environment variable can be used to specify the subject alternative DNS name that will be added. If this environment variable is not set, then the host name will be used.
#### Arguments
| | Alias | Type | Default Value | Description |
| :--- | :----:| :----: | :----: | ---: |
| `name` | | String | | Name is used for the name of the directory under which PKI files are created. If the `--common-name` option is not used then `name` will also be used for the Common Name in the certificate subject. |
| `ca-name` | | String | | CA name is used to identify the Certificate Authority to use when creating or removing users and servers. |
| `root_ca-name` | `-r` | String | | When creating an intermediate Certificate Authority, `root_ca_name` identifies the root CA that issues the intermediate CA's certificate.
| `path` | `-p` | String | '/pki' | Base path for pki |
| `common-name` | `-n` | String | | Common Name in the Distinguished Name |
| `country` | `-c` | String | | Country |
| `locality` | | String | | Locality|
| `state` | `-st` | String | | State|
| `expired` | `-e` | Boolean | | Create an expired certificate |
| `org` | `-o` | Array | | Organization name. This option can be specified multiple times|
| `org-unit` | `-u` | Array | | Organizational unit name. This option can be specified multiple times.|
| `password` | `-w` | String | 'changeme' | Certificate password |
| `subject-alt-dns` | `-d` | Array | | Create certificate with DNS subject alternative name. This option can be specified multiple times and only applies to server certs.|
| `subject-alt-ip` | `-i` | Array | | Create certificate with IP subject alternative name. This option can be specified multiple times and only applies to server certs. |
| `subject-alt-email`| `-m` | Array | | Create certificate with rfc822/email subject alternative name. This option can be specified multiple times and only applies to server certs. |
| `valid-from` | `-f` | Date | | Valid from date in ISO 8601 format |
| `valid-to` | `-t` | Date | | Valid to date in ISO 8601 format|
+290 -442
View File
File diff suppressed because it is too large. Load diff
+32 -7
View File
@@ -1,6 +1,6 @@
{
"name": "madcert",
"version": "2.0.0",
"version": "2.5.0",
"description": "Make a Development Cert",
"engines": {
"node": ">=10.16.0"
@@ -19,22 +19,47 @@
"url": "git+https://github.com/NationalSecurityAgency/MADCert.git"
},
"keywords": [
"madcert",
"ca",
"cert",
"certificates",
"testing"
"client certificates",
"crt",
"crypto",
"cryptography",
"development",
"encryption",
"https",
"keypair",
"keys",
"localhost",
"madcert",
"node-forge",
"openssl",
"pem",
"pki",
"p12",
"self",
"server",
"signed",
"ssl",
"testing",
"tls",
"trust",
"user",
"x509"
],
"bugs": {
"url": "https://github.com/NationalSecurityAgency/MADCert/issues"
},
"dependencies": {
"date-fns": "^2.19.0",
"fs-extra": "^9.1.0",
"fs-extra": "10.0.0",
"lodash": "^4.17.21",
"node-forge": "^0.10.0",
"yargs": "^16.2.0"
"node-forge": "^1.3.0",
"yargs": "17.3.1"
},
"devDependencies": {
"chai": "^4.3.4",
"mocha": "^8.3.2"
"mocha": "9.2.0"
}
}
+29 -25
View File
@@ -9,7 +9,7 @@ const subjectAttrs = require('./subjectAttributes');
const utils = require('./utils');
const normalizeName = utils.normalizeName;
function buildCACert(keys, options, caSubject = null) {
function buildCACert(keys, options, caCert = null) {
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = utils.getSerial();
@@ -29,45 +29,49 @@ function buildCACert(keys, options, caSubject = null) {
// Parse the validTo from ISO 8601 format
cert.validity.notAfter = new Date(options.validTo);
}
const caSubject = _.get(caCert, "subject.attributes", null);
cert.setSubject(attrs);
cert.setIssuer(caSubject ? caSubject : attrs);
cert.setExtensions([
const extensions = [
{
name: 'basicConstraints',
cA: true,
critical: true,
},
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
critical: true,
cRLSign: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true,
cRLSign: true,
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true,
},
{
name: 'nsCertType',
client: true,
server: false,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true,
dataEncipherment: true
},
{
name: 'subjectKeyIdentifier',
},
]);
];
if(caCert){
extensions.push({
name: 'authorityKeyIdentifier',
keyIdentifier: caCert.generateSubjectKeyIdentifier().getBytes(),
});
extensions.push(
{
name: 'extKeyUsage',
serverAuth: true,
critical: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true
});
}
cert.setExtensions(extensions);
return cert;
}
@@ -141,7 +145,7 @@ function createIntermediateCACert(caName, rootCaName, options) {
const caKey = forge.pki.privateKeyFromPem(rootCaKeyPem);
const keys = pki.rsa.generateKeyPair(2048);
const cert = buildCACert(keys, options, caCert.subject.attributes);
const cert = buildCACert(keys, options, caCert);
fs.ensureDirSync(newCaPath);
// self-sign certificate
+76
View File
@@ -0,0 +1,76 @@
const _ = require('lodash');
const forge = require('node-forge');
const fs = require('fs-extra');
forge.options.usePureJavaScript = true;
const extensionsFilter = [
'name',
'cA',
'critical',
'digitalSignature',
'nonRepudiation',
'keyEncipherment',
'dataEncipherment',
'keyAgreement',
'keyCertSign',
'cRLSign',
'encipherOnly',
'decipherOnly',
'subjectKeyIdentifier',
'altNames',
'serverAuth',
'clientAuth',
'codeSigning',
'emailProtection',
'timeStamping',
'client',
'server',
'email',
'objsign',
'reserved:',
'sslCA',
'emailCA',
'objCA',
];
/**
*
* @param {Array} attributes
* @returns {String} Distinguished name
*/
function attributesToDN(attributes){
let dn = '';
for (let i=0; i<attributes.length; i++){
const attribute = attributes[i];
dn += `${attribute.shortName}=${attribute.value}${i<attributes.length-1 ? ',' : ''}`
}
return dn;
}
/**
*
* @param {String} path Path to pem certificate
* @param {Array} propertiesFilter Optional Array of Strings to filter response object properties by
* @returns {Object} Simplified JSON representation of pem certificate
*/
const certToJSON = function certToJSON(path, propertiesFilter) {
const crt = fs.readFileSync(path);
const forgeCrtObj = forge.pki.certificateFromPem(crt.toString());
const crtJSON = _.pick(forgeCrtObj, ['version', 'serialNumber', 'validity']);
crtJSON.subject = attributesToDN(forgeCrtObj.subject.attributes);
crtJSON.issuer = attributesToDN(forgeCrtObj.issuer.attributes);
crtJSON['signature algorithm'] = forge.oids[forgeCrtObj.signatureOid];
crtJSON.extensions = _.transform(forgeCrtObj.extensions, (result, value) => {
result.push(_.pick(value, extensionsFilter));
}, []);
if(propertiesFilter && propertiesFilter.length > 0)
return _.pick(crtJSON, propertiesFilter);
return crtJSON;
}
module.exports = certToJSON;
+20
View File
@@ -1,5 +1,6 @@
const fs = require('fs-extra');
const _ = require('lodash');
const path = require('path');
const caDb = require('./caDatabase');
const ca = require('./ca');
@@ -8,6 +9,7 @@ const user = require('./user');
const utils = require('./utils');
const normalizeName = utils.normalizeName;
const createSubjectAttributes = require('./subjectAttributes');
const certToJSON = require('./certToJSON');
function jsonListCerts(basePath = 'pki/') {
const certs = {};
@@ -80,6 +82,21 @@ function createCertDatabase(caName, { basePath = 'pki/', revokedCNs = [] } = {})
);
}
function caCertToJSONWrapper(basePath, caName, propertiesFilter){
const certPath = basePath ? path.join(basePath, normalizeName(caName), 'ca/crt.pem') : path.join(__dirname, 'pki', normalizeName(caName), 'ca/crt.pem');
return certToJSON(certPath, propertiesFilter);
}
function serverCertToJSONWrapper(basePath, caName, serverName, propertiesFilter){
const certPath = basePath ? path.join(basePath, normalizeName(caName), 'servers', serverName, 'crt.pem') : path.join(__dirname, 'pki', normalizeName(caName), 'servers', serverName, 'crt.pem');
return certToJSON(certPath, propertiesFilter);
}
function userCertToJSONWrapper(basePath, caName, userName, propertiesFilter){
const certPath = basePath ? path.join(basePath, normalizeName(caName), 'users', userName, 'crt.pem') : path.join(__dirname, 'pki', normalizeName(caName), 'users', userName, 'crt.pem');
return certToJSON(certPath, propertiesFilter);
}
const certs = {
createCACert: ca.createCACert,
createIntermediateCACert: ca.createIntermediateCACert,
@@ -95,6 +112,9 @@ const certs = {
createCertDatabase,
normalizeName,
createSubjectAttributes,
caCertToJSON: caCertToJSONWrapper,
serverCertToJSON: serverCertToJSONWrapper,
userCertToJSON: userCertToJSONWrapper,
};
module.exports = certs;
+48
View File
@@ -2,6 +2,7 @@
const version = require('../package.json').version;
const util = require('util')
const yargs = require('yargs');
const certs = require('./certs');
@@ -44,6 +45,15 @@ const argv = yargs
executed = true;
certs.listCACerts(argv.path);
})
.command(
'ca-print <name>',
'print certificate authority details',
{},
function(argv) {
executed = true;
console.log(util.inspect(certs.caCertToJSON(argv.path, argv.name, argv.properties), {showHidden: false, depth: null}));
}
)
.command(
'ca-remove <name>',
'remove a certificate authority and all associated users and servers',
@@ -59,6 +69,8 @@ const argv = yargs
basePath: argv.path,
commonName: argv['common-name'],
country: argv.country,
locality: argv.locality,
state: argv.state,
expired: argv.expired,
organizations: argv.org,
organizationalUnits: argv['org-unit'],
@@ -74,6 +86,15 @@ const argv = yargs
executed = true;
certs.listServerCerts(argv.path);
})
.command(
'server-print <name> <ca_name>',
'print server certificate details',
{},
function(argv) {
executed = true;
console.log(util.inspect(certs.serverCertToJSON(argv.path, argv.ca_name, argv.name, argv.properties), {showHidden: false, depth: null}));
}
)
.command('server-remove <name> <ca_name>', 'remove a server certificate', {}, function(argv) {
executed = true;
certs.removeServerCert(argv.name, argv.ca_name, argv.path);
@@ -84,6 +105,8 @@ const argv = yargs
basePath: argv.path,
commonName: argv['common-name'],
country: argv.country,
locality: argv.locality,
state: argv.state,
expired: argv.expired,
rootCaName: argv['root-ca-name'],
organizations: argv.org,
@@ -98,6 +121,15 @@ const argv = yargs
executed = true;
certs.listUserCerts(argv.path);
})
.command(
'user-print <name> <ca_name>',
'print user certificate details',
{},
function(argv) {
executed = true;
console.log(util.inspect(certs.userCertToJSON(argv.path, argv.ca_name, argv.name, argv.properties), {showHidden: false, depth: null}));
}
)
.command('user-remove <name> <ca_name>', 'remove a user certificates', {}, function(argv) {
executed = true;
certs.removeUserCert(argv.name, argv.ca_name, argv.path);
@@ -130,6 +162,13 @@ const argv = yargs
default: 'US',
requiresArg: true,
})
.option('locality', {
describe: 'Locality',
})
.option('state', {
alias: 'st',
describe: 'State',
})
.option('expired', {
alias: 'e',
describe: 'Create an expired certificate.',
@@ -199,6 +238,12 @@ const argv = yargs
describe: 'Valid to date in ISO 8601 format.',
requiresArg: true,
})
.option('properties', {
describe: 'Properties to filter print result. This option can be specified multiple times.',
type: 'array',
default: [],
requiresArg: true,
})
.alias('version', 'v')
.alias('h', 'help')
.conflicts('expired', 'valid-to')
@@ -206,6 +251,8 @@ const argv = yargs
.help('help')
.group('common-name', 'Creation Options:')
.group('country', 'Creation Options:')
.group('locality', 'Creation Options:')
.group('state', 'Creation Options:')
.group('expired', 'Creation Options:')
.group('org-unit', 'Creation Options:')
.group('org', 'Creation Options:')
@@ -218,6 +265,7 @@ const argv = yargs
.group('password', 'Server Creation Options:')
.group('subject-alt-dns', 'Server Creation Options:')
.group('subject-alt-ip', 'Server Creation Options:')
.group('properties', 'Print Options:')
.wrap(yargs.terminalWidth()).argv;
if (!executed) {
+14 -19
View File
@@ -34,7 +34,7 @@ function getIpAddresses() {
return localIPs;
}
function buildServerCert(keys, caName, caSubject, localhost, options) {
function buildServerCert(keys, caName, caCert, localhost, options) {
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = utils.getSerial();
@@ -56,41 +56,32 @@ function buildServerCert(keys, caName, caSubject, localhost, options) {
const attrs = subjectAttrs(options);
cert.setSubject(attrs);
cert.setIssuer(caSubject);
cert.setIssuer(caCert.subject.attributes);
const extensions = [
{
name: 'basicConstraints',
cA: false,
critical: true,
},
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true,
critical: true,
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true,
},
{
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true,
},
{
name: 'subjectKeyIdentifier',
},
{
name: 'authorityKeyIdentifier',
keyIdentifier: caCert.generateSubjectKeyIdentifier().getBytes(),
},
];
const altNames = [];
@@ -202,6 +193,10 @@ function createServerCert(
return;
}
// Adding a check for the common name to be populated
// with the name field if it is blank.
options.commonName = _.get(options, 'commonName', serverName);
const serverCertPath = path.join(serverDir, '/crt.pem');
const serverKeyPath = path.join(serverDir, '/key.pem');
const serverP12Path = path.join(serverDir, '/bundle.p12');
@@ -241,7 +236,7 @@ function createServerCert(
const cert = buildServerCert(
keys,
caCertName,
caCert.subject.attributes,
caCert,
localhost,
options
);
@@ -282,7 +277,7 @@ function createServerCert(
expired ? 'expired ' : ''
}server certificate was created and signed by ${caCertName}.`,
};
console.error(success.message);
console.log(success.message);
callback(null, success);
} else {
const err = {
+15
View File
@@ -33,6 +33,21 @@ function createSubjectAttributes(options) {
});
}
const l = _.get(options, 'locality');
if (l) {
attrs.push({
shortName: 'L',
value: l,
});
}
const st = _.get(options, 'state');
if(st) {
attrs.push({
shortName: 'ST',
value: st,
});
}
return attrs;
}
+9 -18
View File
@@ -9,7 +9,7 @@ const subjectAttrs = require('./subjectAttributes');
const utils = require('./utils');
const normalizeName = utils.normalizeName;
function buildUserCert(keys, options, caSubject) {
function buildUserCert(keys, options, caCert) {
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = utils.getSerial();
@@ -31,41 +31,32 @@ function buildUserCert(keys, options, caSubject) {
const attrs = subjectAttrs(options);
cert.setSubject(attrs);
cert.setIssuer(caSubject);
cert.setIssuer(caCert.subject.attributes);
const extensions = [
{
name: 'basicConstraints',
cA: false,
critical: true,
},
{
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true,
critical: true,
},
{
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true,
},
{
name: 'nsCertType',
client: true,
server: false,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true,
},
{
name: 'subjectKeyIdentifier',
},
{
name: 'authorityKeyIdentifier',
keyIdentifier: caCert.generateSubjectKeyIdentifier().getBytes(),
},
];
const altNames = [];
@@ -172,7 +163,7 @@ function createUserCert(userName, caCertName, options, callback = (err, data) =>
const caKeyPem = fs.readFileSync(caPath + '/key.pem', 'utf8');
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
const cert = buildUserCert(keys, options, caCert.subject.attributes);
const cert = buildUserCert(keys, options, caCert);
cert.sign(caKey, utils.createMessageDigest());
+4 -4
View File
@@ -77,17 +77,17 @@ function setExpirationDate(cert, expired) {
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
// if expired, set cert to be valid starting 5 years before today
// if expired, set cert to be valid starting 1 year before today
// if not expired, set cert to be valid starting yesterday
expired
? cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 5)
? cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 1)
: cert.validity.notBefore.setDate(cert.validity.notBefore.getDate() - 1);
// if expired, set cert to be valid until yesterday
// if not expired, set cert to be valid until 5 years from today
// if not expired, set cert to be valid until 1 year from today
expired
? cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() - 1)
: cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 5);
: cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 1);
}
function setValidFrom(cert, date) {}
+5
View File
@@ -0,0 +1,5 @@
const fs = require('fs-extra');
after('Remove PKI directory', function() {
fs.removeSync(global.pkiPath);
});
+9
View File
@@ -0,0 +1,9 @@
const certs = require('..');
const fs = require('fs-extra');
before('Create PKI directory', function(done) {
global.pkiPath = `${__dirname}/pki`;
fs.ensureDirSync(global.pkiPath);
certs.createCACert('example', { basePath: global.pkiPath });
done();
});
+19
View File
@@ -0,0 +1,19 @@
const certToJSON = require('../src/certToJSON');
const expect = require('chai').expect;
const path = require('path');
const util = require('util');
describe('Testing certToJSON.js', function() {
it('Converts a PEM certificate to JSON', function() {
const certJSON = certToJSON(path.join(global.pkiPath, 'example/ca/crt.pem'));
expect(certJSON).to.be.an('object');
expect(certJSON).to.include.all.keys('version', 'serialNumber', 'validity', 'subject', 'issuer', 'signature algorithm', 'extensions');
});
it('Filters properties in returned JSON object', function(){
const propertiesArray = ['subject', 'issuer'];
const certJSON = certToJSON(path.join(global.pkiPath, 'example/ca/crt.pem'), propertiesArray);
expect(certJSON).to.be.an('object');
expect(Object.keys(certJSON)).to.deep.equal(propertiesArray)
});
});
+83 -55
View File
@@ -4,22 +4,12 @@ const fs = require('fs-extra');
const path = require('path');
const expect = require('chai').expect;
const pkiPath = `${__dirname}/pki`;
describe('Testing MADCert', function() {
before('Create PKI directory', function() {
fs.ensureDirSync(pkiPath);
});
after('Remove PKI directory', function() {
fs.removeSync(pkiPath);
});
describe('CA Tests', function() {
it('Create CA Cert', function(done) {
try {
certs.createCACert('CA Cert', { basePath: pkiPath });
const created = fs.pathExistsSync(path.join(pkiPath, 'ca-cert/ca/crt.pem'));
certs.createCACert('CA Cert', { basePath: global.pkiPath });
const created = fs.pathExistsSync(path.join(global.pkiPath, 'ca-cert/ca/crt.pem'));
expect(created).to.be.true;
done();
} catch (e) {
@@ -29,8 +19,8 @@ describe('Testing MADCert', function() {
it('Create intermediate CA Cert', function(done) {
try {
certs.createIntermediateCACert('CA Cert 2', 'CA Cert', { basePath: pkiPath });
const created = fs.pathExistsSync(path.join(pkiPath, 'ca-cert-2/ca/crt.pem'));
certs.createIntermediateCACert('CA Cert 2', 'CA Cert', { basePath: global.pkiPath });
const created = fs.pathExistsSync(path.join(global.pkiPath, 'ca-cert-2/ca/crt.pem'));
expect(created).to.be.true;
done();
} catch (e) {
@@ -39,19 +29,19 @@ describe('Testing MADCert', function() {
});
it('List CA Certs', function() {
certs.listCACerts(pkiPath);
certs.listCACerts(global.pkiPath);
});
it('Remove CA Cert', function() {
certs.createCACert('CA Cert Test Removal', { basePath: pkiPath });
certs.removeCACert('CA Cert Test Removal', pkiPath);
certs.createCACert('CA Cert Test Removal', { basePath: global.pkiPath });
certs.removeCACert('CA Cert Test Removal', global.pkiPath);
});
});
describe('Server Tests', function() {
it('Create Server Cert including Root CA in bundle', function() {
certs.createServerCert('Server', 'ca-cert-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert',
});
@@ -59,23 +49,43 @@ describe('Testing MADCert', function() {
it('Create Server Cert excluding Root CA in bundle', function() {
certs.createServerCert('Server 2', 'ca-cert-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: null,
});
});
it('Create Server Cert with locality and state', function(done) {
try {
certs.createServerCert('Server Locality and State', 'ca-cert-2', false, {
locality: 'Baltimore',
state: 'Maryland',
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert',
});
const created = fs.pathExistsSync(
path.join(global.pkiPath, 'ca-cert-2/servers/server-locality-and-state/crt.pem')
);
expect(created).to.be.true;
done();
} catch (e) {
done(e);
}
});
it('Create Server Cert with start and expiration dates', function(done) {
try {
certs.createServerCert('Server Valid From and To', 'ca-cert-2', false, {
validFrom: '2050-01-01T00:00:00',
validTo: '2051-01-01T00:00:00',
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert',
});
const created = fs.pathExistsSync(
path.join(pkiPath, 'ca-cert-2/servers/server-valid-from-and-to/crt.pem')
path.join(global.pkiPath, 'ca-cert-2/servers/server-valid-from-and-to/crt.pem')
);
expect(created).to.be.true;
done();
@@ -89,12 +99,12 @@ describe('Testing MADCert', function() {
certs.createServerCert('Server Valid To before From', 'ca-cert-2', false, {
validFrom: '2051-01-01T00:00:00',
validTo: '2050-01-01T00:00:00',
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert',
});
const created = fs.pathExistsSync(
path.join(pkiPath, 'ca-cert-2/servers/server-valid-to-before-from/crt.pem')
path.join(global.pkiPath, 'ca-cert-2/servers/server-valid-to-before-from/crt.pem')
);
expect(created).to.be.false;
done();
@@ -105,7 +115,7 @@ describe('Testing MADCert', function() {
it('Create Expired Server Cert', function() {
certs.createServerCert('Server 3', 'ca-cert-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: true,
rootCaName: 'CA Cert',
});
@@ -113,23 +123,23 @@ describe('Testing MADCert', function() {
it('Create Expired Server Cert for existing server', function() {
certs.createServerCert('Server 2', 'ca-cert-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: true,
rootCaName: null,
});
});
it('List Server Certs', function() {
certs.listServerCerts(pkiPath);
certs.listServerCerts(global.pkiPath);
});
it('Remove Server Cert', function() {
certs.createServerCert('Server Test Removal', 'ca-cert-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert',
});
certs.removeServerCert('Server Test Removal', 'ca-cert-2', pkiPath);
certs.removeServerCert('Server Test Removal', 'ca-cert-2', global.pkiPath);
});
});
@@ -138,18 +148,18 @@ describe('Testing MADCert', function() {
const caDir = 'user-tests-ca';
before(function(done) {
certs.createCACert(caName, { basePath: pkiPath });
certs.createCACert(caName, { basePath: global.pkiPath });
done();
});
after(function(done) {
certs.removeCACert(caName, pkiPath);
certs.removeCACert(caName, global.pkiPath);
done();
});
it('Create User Cert', function(done) {
certs.createUserCert('User 1', caName, { basePath: pkiPath, expired: false });
const userCerts = certs.listUserCerts(pkiPath);
certs.createUserCert('User 1', caName, { basePath: global.pkiPath, expired: false });
const userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).to.contain('user-1');
done();
});
@@ -159,11 +169,29 @@ describe('Testing MADCert', function() {
certs.createUserCert('User Valid From and To', caName, {
validFrom: '2050-01-01T00:00:00',
validTo: '2051-01-01T00:00:00',
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
});
const created = fs.pathExistsSync(
path.join(pkiPath, caDir, 'users/user-valid-from-and-to/crt.pem')
path.join(global.pkiPath, caDir, 'users/user-valid-from-and-to/crt.pem')
);
expect(created).to.be.true;
done();
} catch (e) {
done(e);
}
});
it('Create User Cert with locality and state', function(done) {
try {
certs.createUserCert('User locality and state', caName, {
locality: 'Baltimore',
state: 'Maryland',
basePath: global.pkiPath,
expired: false,
});
const created = fs.pathExistsSync(
path.join(global.pkiPath, caDir, 'users/user-locality-and-state/crt.pem')
);
expect(created).to.be.true;
done();
@@ -177,11 +205,11 @@ describe('Testing MADCert', function() {
certs.createUserCert('User Valid To before From', caName, {
validFrom: '2051-01-01T00:00:00',
validTo: '2050-01-01T00:00:00',
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
});
const created = fs.pathExistsSync(
path.join(pkiPath, caDir, 'users/user-valid-to-before-from/crt.pem')
path.join(global.pkiPath, caDir, 'users/user-valid-to-before-from/crt.pem')
);
expect(created).to.be.false;
done();
@@ -191,42 +219,42 @@ describe('Testing MADCert', function() {
});
it('Create Expired User Cert', function(done) {
certs.createUserCert('User 2', caName, { basePath: pkiPath, expired: true });
const userCerts = certs.listUserCerts(pkiPath);
certs.createUserCert('User 2', caName, { basePath: global.pkiPath, expired: true });
const userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).to.contain('user-2');
done();
});
it('Create Expired User Cert for existing User', function() {
certs.createUserCert('User 1', caName, { basePath: pkiPath, expired: true });
certs.createUserCert('User 1', caName, { basePath: global.pkiPath, expired: true });
});
it('List User Certs', function(done) {
const userCerts = certs.listUserCerts(pkiPath);
const userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).to.not.be.empty;
done();
});
it('Create User Cert with rfc822 Subject Alternative', function(done) {
certs.createUserCert('User 3', caName, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
subjectAltEmailNames: ['[email protected]'],
});
const userCerts = certs.listUserCerts(pkiPath);
const userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).to.contain('user-3');
done();
});
it('Remove User Cert', function(done) {
certs.createUserCert('User Test Removal', caName, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
});
let userCerts = certs.listUserCerts(pkiPath);
let userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).to.contain('user-test-removal');
certs.removeUserCert('User Test Removal', caName, pkiPath);
userCerts = certs.listUserCerts(pkiPath);
certs.removeUserCert('User Test Removal', caName, global.pkiPath);
userCerts = certs.listUserCerts(global.pkiPath);
expect(userCerts).not.to.contain('user-test-removal');
done();
});
@@ -242,19 +270,19 @@ describe('Testing MADCert', function() {
it('when CA, Servers, and Users Exist', function() {
this.timeout(newTimeout); //this test creates 4 certs, so we allow 4x the time
certs.createCACert('CA Cert List', { basePath: pkiPath });
certs.createIntermediateCACert('CA Cert List 2', 'CA Cert List', { basePath: pkiPath });
certs.createCACert('CA Cert List', { basePath: global.pkiPath });
certs.createIntermediateCACert('CA Cert List 2', 'CA Cert List', { basePath: global.pkiPath });
certs.createServerCert('Server List', 'ca-cert-list-2', false, {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
rootCaName: 'CA Cert List',
});
certs.createUserCert('User List 1', 'ca-cert-list-2', {
basePath: pkiPath,
basePath: global.pkiPath,
expired: false,
});
const list = certs.listCerts(pkiPath);
const list = certs.listCerts(global.pkiPath);
expect(list.ca).to.not.be.null;
const caCert = _.find(list.ca, { name: 'ca-cert-list' });
@@ -269,20 +297,20 @@ describe('Testing MADCert', function() {
});
it('when none exist', function() {
fs.removeSync(pkiPath);
fs.removeSync(global.pkiPath);
const list = certs.listCerts(pkiPath);
const list = certs.listCerts(global.pkiPath);
expect(list.ca).to.be.undefined;
});
});
describe('Cert Database Tests', function() {
before('', function() {
certs.createCACert('ca cert db', { basePath: pkiPath });
certs.createCACert('ca cert db', { basePath: global.pkiPath });
});
it('Create Cert DB', function() {
certs.createCertDatabase('ca cert db', { basePath: pkiPath });
certs.createCertDatabase('ca cert db', { basePath: global.pkiPath });
});
});