mirror of
https://github.com/NationalSecurityAgency/MADCert.git
synced 2026-11-10 14:01:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
531be027f5 | ||
|
|
3deb4ccf9f | ||
|
|
b44c79c3af | ||
|
|
39dbf747e6 | ||
|
|
663bb10e0b | ||
|
|
0c30721bca | ||
|
|
5c976801fb | ||
|
|
e7f88431c6 | ||
|
|
f8e3721210 | ||
|
|
e8deada753 | ||
|
|
a21293fe94 | ||
|
|
b9067f2342 | ||
|
|
aa4746a13e | ||
|
|
d978f6acb9 | ||
|
|
3bc0fefe5f | ||
|
|
0828d1b4cf | ||
|
|
f14953fc14 | ||
|
|
52dae35d1d | ||
|
|
ae650a88a6 | ||
|
|
594173b92e | ||
|
|
d0d6b2ff0b | ||
|
|
8e8b3807da | ||
|
|
3e303ae429 | ||
|
|
b9b59e30d4 | ||
|
|
c966de9bec | ||
|
|
7f40edba5b | ||
|
|
0a339b75ad | ||
|
|
daaefa8d35 | ||
|
|
b0bd906e5f | ||
|
|
2b514276e5 | ||
|
|
bd11bed32a | ||
|
|
371d563b03 | ||
|
|
cd8ee3ed07 | ||
|
|
09f5993dda | ||
|
|
34582c77e9 | ||
|
|
40e19eb40c | ||
|
|
b3aa9a4872 | ||
|
|
88dee9e9d2 | ||
|
|
101d1cc751 | ||
|
|
9cc105eb20 | ||
|
|
31dfef67d3 | ||
|
|
1aa6f942f2 | ||
|
|
9b68121938 | ||
|
|
9dc66d7ce6 | ||
|
|
0120acb529 | ||
|
|
7ea29a8849 | ||
|
|
1e77afd0d6 | ||
|
|
e24fb69f15 | ||
|
|
e6eaf78e27 |
No files matched your search
+2
-3
@@ -1,3 +1,2 @@
|
||||
# IntelliJ IDEA files
|
||||
.idea/
|
||||
*.iml
|
||||
node_modules/
|
||||
.idea/
|
||||
+1
-1
@@ -10,8 +10,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
MADCert uses a number of libraries that are licensed under various licenses:
|
||||
|
||||
- [date-fns](https://www.npmjs.com/package/date-fns) is licensed under the MIT license.
|
||||
- [fs-extra](https://www.npmjs.com/package/fs-extra) is licensed under the MIT license.
|
||||
- [lodash](https://www.npmjs.com/package/lodash) is licensed under the MIT license.
|
||||
- [moment](https://www.npmjs.com/package/moment) is licensed under the MIT license.
|
||||
- [node-forge](https://www.npmjs.com/package/node-forge) is licensed under either the BSD 3 Clause.
|
||||
- [yargs](https://www.npmjs.com/package/yargs) is licensed under the MIT license.
|
||||
@@ -1,80 +1,212 @@
|
||||
# Overview
|
||||
|
||||
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.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. You will need Node installed on your operating system. For Linux and Mac OS X we recommend using nvm
|
||||
at http://nvm.sh . For Windows we recommend using nvm-windows at https://github.com/coreybutler/nvm-windows .
|
||||
|
||||
2. Install the Yarn Package Manager. Follow instructions for your Operating System at https://yarnpkg.com/en/docs/install .
|
||||
|
||||
## Installation
|
||||
|
||||
From within the MADCert directory:
|
||||
|
||||
yarn
|
||||
|
||||
## Running MADCert
|
||||
|
||||
yarn run madcert <command> <arguments> [options]
|
||||
|
||||
## 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 the host name will be used.
|
||||
|
||||
## Commands, Arguments, and Options
|
||||
|
||||
When you run MADCert help (using `yarn run madcert --help`) it will display all of the commands, arguments, and options.
|
||||
|
||||
### Arguments
|
||||
|
||||
- `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.
|
||||
|
||||
### Options
|
||||
|
||||
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
|
||||
- Options - these options are not specific to any particular command
|
||||
|
||||
## 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.
|
||||
|
||||
## Examples
|
||||
|
||||
- Create a Certificate Authority (CA)
|
||||
|
||||
yarn run madcert ca-create test-root-ca --common-name "Test Root CA" --org "Some Organization" --country "US"
|
||||
|
||||
- Create an Intermediate CA (Root CA must exist)
|
||||
|
||||
yarn run madcert ca-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)
|
||||
|
||||
yarn run madcert server-create test-server test-ca --common-name "test.server" --org-unit "S333" --org "Some Organization" --country "US" --subject-alt-ip "123.45.23.111"
|
||||
|
||||
- Create a localhost Server Certificate (CA must exist)
|
||||
|
||||
yarn run madcert server-create localhost test-ca --localhost true --common-name "localhost" --org-unit "D009" --org-unit "DoD" --org "U.S. Government" --country "US"
|
||||
|
||||
Note: A subject alternative name is automatically added to the generated certificate. See ENVIRONMENT above.
|
||||
|
||||
- Create a User Certificate with CN=test-user (CA must exist)
|
||||
|
||||
yarn run madcert user-create test-user test-ca --org-unit "SECURITY" --org-unit "A456" --org "Some Organization" --country "US"
|
||||
|
||||
- Create a User Certificate CN=Alice Example (CA must exist)
|
||||
|
||||
yarn run madcert user-create test-user test-ca --common-name "Alice Example" --org-unit "SECURITY" --org-unit "A456" --org "Some Organization" --country "US"
|
||||
# MADCert
|
||||
|
||||
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 consists of a certificate generator, a file system certificate manager, and a command line interface.
|
||||
|
||||
MADCert does not require openssl or any other programs to be installed in the Operating System.
|
||||
|
||||
Both Node.js and CLI examples are provided below.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
### Node.js Examples
|
||||
|
||||
To add MADCert to your project:
|
||||
|
||||
npm install madcert
|
||||
|
||||
var madcert = require("madcert")
|
||||
|
||||
- Create a Certificate Authority (CA)
|
||||
|
||||
madcert.createCACert("Test Root CA", {validFrom: "2019-08-08", validTo: "2024-08-09"})
|
||||
|
||||
- Create an Intermediate CA (Root CA must exist)
|
||||
|
||||
madcert.createIntermediateCACert("Test CA","rootCA", {validFrom: "2019-08-08", validTo: "2024-08-09"});
|
||||
|
||||
- Create a Server Certificate (CA must exist)
|
||||
|
||||
madcert.createServerCert("Test Server", "test-ca", false, {validFrom: "2019-08-08", validTo: "2024-08-09"});
|
||||
|
||||
- Create a localhost Server Certificate (CA must exist)
|
||||
|
||||
madcert.createServerCert("localhost", "test-ca", true, {validFrom: "2019-08-08", validTo: "2024-08-09"});
|
||||
|
||||
Note: A subject alternative name is automatically added to the generated certificate. See ENVIRONMENT above.
|
||||
|
||||
- Create a User Certificate with CN=test-user (CA must exist)
|
||||
|
||||
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"});
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
- Create a Certificate Authority (CA)
|
||||
|
||||
npx madcert ca-create test-root-ca --common-name "Test Root CA" --org "Some Organization" --country "US"
|
||||
|
||||
- Create an Intermediate CA (Root CA must exist)
|
||||
|
||||
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)
|
||||
|
||||
npx madcert server-create test-server test-ca --common-name "test.server" --org-unit "S333" --org "Some Organization" --country "US" --subject-alt-ip "123.45.23.111"
|
||||
|
||||
- Create a localhost Server Certificate (CA must exist)
|
||||
|
||||
npx madcert server-create localhost test-ca --localhost true --common-name "localhost" --org-unit "D009" --org-unit "DoD" --org "U.S. Government" --country "US"
|
||||
|
||||
Note: A subject alternative name is automatically added to the generated certificate. See ENVIRONMENT above.
|
||||
|
||||
- Create a User Certificate with CN=test-user (CA must exist)
|
||||
|
||||
npx madcert user-create test-user test-ca --org-unit "SECURITY" --org-unit "A456" --org "Some Organization" --country "US"
|
||||
|
||||
- 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|
|
||||
Generated
+1887
File diff suppressed because it is too large.
Load diff
+35
-11
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"name": "madcert",
|
||||
"version": "1.0.2",
|
||||
"version": "2.5.0",
|
||||
"description": "Make a Development Cert",
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
},
|
||||
"main": "src/certs.js",
|
||||
"scripts": {
|
||||
"madcert": "node src/madcert.js",
|
||||
"test": "mocha --timeout 15000 --recursive"
|
||||
},
|
||||
"author": "National Security Agency (https://code.nsa.gov/)",
|
||||
@@ -20,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": {
|
||||
"fs-extra": "^9.0.1",
|
||||
"lodash": "^4.17.11",
|
||||
"moment": "^2.27.0",
|
||||
"node-forge": "^0.10.0",
|
||||
"yargs": "^15.4.0"
|
||||
"date-fns": "^2.19.0",
|
||||
"fs-extra": "10.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"node-forge": "^1.3.0",
|
||||
"yargs": "17.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^8.2.1"
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "9.2.0"
|
||||
}
|
||||
}
|
||||
@@ -1,196 +1,200 @@
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
function buildCACert(keys, options, caSubject = null) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// Parse the validTo from ISO 8601 format
|
||||
cert.validity.notAfter = new Date(options.validTo);
|
||||
}
|
||||
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caSubject ? caSubject : attrs);
|
||||
cert.setExtensions([
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: true,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
keyCertSign: true,
|
||||
digitalSignature: 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,
|
||||
},
|
||||
{
|
||||
name: 'subjectKeyIdentifier',
|
||||
},
|
||||
]);
|
||||
return cert;
|
||||
}
|
||||
|
||||
function createCACert(caName, options) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
console.log(
|
||||
`Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const caPath = basePath + normalizeName(caName) + '/ca/';
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
const cert = buildCACert(keys, options);
|
||||
|
||||
fs.ensureDirSync(caPath);
|
||||
// self-sign certificate
|
||||
cert.sign(keys.privateKey, utils.createMessageDigest());
|
||||
// convert a Forge certificate to PEM
|
||||
const certPem = pki.certificateToPem(cert);
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
//write out crt, key PEM, and serial.txt files
|
||||
fs.outputFileSync(caPath + '/' + 'crt.pem', certPem);
|
||||
fs.outputFileSync(caPath + '/' + 'key.pem', privateKeyPem);
|
||||
fs.outputFileSync(caPath + '/' + 'serial.txt', '01');
|
||||
|
||||
console.log('Certificate authority ' + caName + ' was created.');
|
||||
} else {
|
||||
console.log('Certificate authority already exists');
|
||||
}
|
||||
}
|
||||
|
||||
function createIntermediateCACert(caName, rootCaName, options) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
console.log(
|
||||
`Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new intermediate CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const newCaPath = basePath + normalizeName(caName) + '/ca/';
|
||||
const rootCaPath = basePath + normalizeName(rootCaName) + '/ca/';
|
||||
|
||||
if (!fs.existsSync(rootCaPath)) {
|
||||
console.log(
|
||||
`Certificate Authority ${rootCaName} does not exist, aborting creation of new intermediate CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(newCaPath)) {
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(rootCaPath + '/crt.pem', 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const rootCaKeyPem = fs.readFileSync(rootCaPath + '/key.pem', 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(rootCaKeyPem);
|
||||
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
const cert = buildCACert(keys, options, caCert.subject.attributes);
|
||||
|
||||
fs.ensureDirSync(newCaPath);
|
||||
// self-sign certificate
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
// convert a Forge certificate to PEM
|
||||
const certPem = pki.certificateToPem(cert);
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
//write out crt, key PEM, and serial.txt files
|
||||
fs.outputFileSync(newCaPath + '/crt.pem', certPem);
|
||||
fs.outputFileSync(newCaPath + '/key.pem', privateKeyPem);
|
||||
fs.outputFileSync(newCaPath + '/serial.txt', '01');
|
||||
fs.outputFileSync(
|
||||
basePath + normalizeName(caName) + '/parent.txt',
|
||||
normalizeName(rootCaName)
|
||||
);
|
||||
|
||||
const bundleFile = `${rootCaPath}/${normalizeName(rootCaName)}.ca-bundle`;
|
||||
if (!fs.existsSync(bundleFile)) {
|
||||
//TODO: Create bundle file
|
||||
} else {
|
||||
//TODO: Update bundle file
|
||||
}
|
||||
|
||||
console.log('Certificate authority ' + caName + ' was created.');
|
||||
} else {
|
||||
console.log('Certificate authority already exists');
|
||||
}
|
||||
}
|
||||
|
||||
function listCACerts(path) {
|
||||
utils.listCerts('ca', path);
|
||||
}
|
||||
|
||||
function removeCACert(name, basePath = 'pki/') {
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath + '/' + normalizeName(name))) {
|
||||
fs.removeSync(basePath + '/' + normalizeName(name));
|
||||
console.log(
|
||||
'Certificate Authority ' + name + ' and all associated certificates were removed.'
|
||||
);
|
||||
} else {
|
||||
console.log('Certificate Authority ' + name + ' does not exist.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createCACert,
|
||||
createIntermediateCACert,
|
||||
listCACerts,
|
||||
removeCACert,
|
||||
};
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
function buildCACert(keys, options, caCert = null) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// 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);
|
||||
const extensions = [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: true,
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
keyCertSign: true,
|
||||
digitalSignature: true,
|
||||
critical: true,
|
||||
cRLSign: true,
|
||||
nonRepudiation: true,
|
||||
keyEncipherment: 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;
|
||||
}
|
||||
|
||||
function createCACert(caName, options) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
console.log(
|
||||
`Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const caPath = basePath + normalizeName(caName) + '/ca/';
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
const cert = buildCACert(keys, options);
|
||||
|
||||
fs.ensureDirSync(caPath);
|
||||
// self-sign certificate
|
||||
cert.sign(keys.privateKey, utils.createMessageDigest());
|
||||
// convert a Forge certificate to PEM
|
||||
const certPem = pki.certificateToPem(cert);
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
//write out crt, key PEM, and serial.txt files
|
||||
fs.outputFileSync(caPath + '/' + 'crt.pem', certPem);
|
||||
fs.outputFileSync(caPath + '/' + 'key.pem', privateKeyPem);
|
||||
fs.outputFileSync(caPath + '/' + 'serial.txt', '01');
|
||||
|
||||
console.log('Certificate authority ' + caName + ' was created.');
|
||||
} else {
|
||||
console.log('Certificate authority already exists');
|
||||
}
|
||||
}
|
||||
|
||||
function createIntermediateCACert(caName, rootCaName, options) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
console.log(
|
||||
`Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new intermediate CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const newCaPath = basePath + normalizeName(caName) + '/ca/';
|
||||
const rootCaPath = basePath + normalizeName(rootCaName) + '/ca/';
|
||||
|
||||
if (!fs.existsSync(rootCaPath)) {
|
||||
console.log(
|
||||
`Certificate Authority ${rootCaName} does not exist, aborting creation of new intermediate CA certificate`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(newCaPath)) {
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(rootCaPath + '/crt.pem', 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const rootCaKeyPem = fs.readFileSync(rootCaPath + '/key.pem', 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(rootCaKeyPem);
|
||||
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
const cert = buildCACert(keys, options, caCert);
|
||||
|
||||
fs.ensureDirSync(newCaPath);
|
||||
// self-sign certificate
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
// convert a Forge certificate to PEM
|
||||
const certPem = pki.certificateToPem(cert);
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
//write out crt, key PEM, and serial.txt files
|
||||
fs.outputFileSync(newCaPath + '/crt.pem', certPem);
|
||||
fs.outputFileSync(newCaPath + '/key.pem', privateKeyPem);
|
||||
fs.outputFileSync(newCaPath + '/serial.txt', '01');
|
||||
fs.outputFileSync(
|
||||
basePath + normalizeName(caName) + '/parent.txt',
|
||||
normalizeName(rootCaName)
|
||||
);
|
||||
|
||||
const bundleFile = `${rootCaPath}/${normalizeName(rootCaName)}.ca-bundle`;
|
||||
if (!fs.existsSync(bundleFile)) {
|
||||
//TODO: Create bundle file
|
||||
} else {
|
||||
//TODO: Update bundle file
|
||||
}
|
||||
|
||||
console.log('Certificate authority ' + caName + ' was created.');
|
||||
} else {
|
||||
console.log('Certificate authority already exists');
|
||||
}
|
||||
}
|
||||
|
||||
function listCACerts(path) {
|
||||
utils.listCerts('ca', path);
|
||||
}
|
||||
|
||||
function removeCACert(name, basePath = 'pki/') {
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath + '/' + normalizeName(name))) {
|
||||
fs.removeSync(basePath + '/' + normalizeName(name));
|
||||
console.log(
|
||||
'Certificate Authority ' + name + ' and all associated certificates were removed.'
|
||||
);
|
||||
} else {
|
||||
console.log('Certificate Authority ' + name + ' does not exist.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createCACert,
|
||||
createIntermediateCACert,
|
||||
listCACerts,
|
||||
removeCACert,
|
||||
};
|
||||
+56
-56
@@ -1,56 +1,56 @@
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs');
|
||||
const moment = require('moment');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
|
||||
const pki = forge.pki;
|
||||
|
||||
function getSubject(cert) {
|
||||
const parts = cert.subject.attributes.map(function(attr) {
|
||||
return `${attr.shortName}=${attr.value}`;
|
||||
});
|
||||
|
||||
return `/${parts.reverse().join('/')}`;
|
||||
}
|
||||
|
||||
function expired(cert) {
|
||||
return moment(cert.validity.notAfter).isBefore(moment());
|
||||
}
|
||||
|
||||
function getExpires(cert) {
|
||||
return moment(cert.validity.notAfter).format('YYMMDDHHmmss[Z]');
|
||||
}
|
||||
|
||||
function createDatabaseEntries(pemCerts, revokedCNs = []) {
|
||||
return pemCerts.map(pemCert => {
|
||||
const pem = fs.readFileSync(pemCert, 'utf8');
|
||||
const cert = pki.certificateFromPem(pem);
|
||||
const subject = getSubject(cert);
|
||||
|
||||
// Determine if the list of revoked CNs contains this cert so we can mark it revoked in the database.
|
||||
const revokedCn = _.find(revokedCNs, cn => {
|
||||
return subject.toUpperCase().indexOf(cn.toUpperCase()) > -1;
|
||||
});
|
||||
|
||||
if (revokedCn) {
|
||||
return `R\t${getExpires(cert)}\t${moment().format('YYMMDDHHmmss[Z]')}\t${cert.serialNumber}\tunknown\t${subject}`;
|
||||
} else {
|
||||
return `${expired(cert) ? 'E' : 'V'}\t${getExpires(cert)}\t\t${cert.serialNumber}\tunknown\t${subject}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeDatabaseFile(entries, dbFile) {
|
||||
fs.writeFileSync(dbFile, entries.join('\n'));
|
||||
fs.writeFileSync(`${dbFile}.attr`, 'unique_subject = no'); // Seems to be required by openssl to generate a CRL.
|
||||
}
|
||||
|
||||
function createDatabase(pemCerts, dbFile = 'index.txt', revokedCNs = []) {
|
||||
writeDatabaseFile(createDatabaseEntries(pemCerts, revokedCNs), dbFile);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create: createDatabase
|
||||
};
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs');
|
||||
const { isBefore, format } = require('date-fns');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
|
||||
const pki = forge.pki;
|
||||
|
||||
function getSubject(cert) {
|
||||
const parts = cert.subject.attributes.map(function(attr) {
|
||||
return `${attr.shortName}=${attr.value}`;
|
||||
});
|
||||
|
||||
return `/${parts.reverse().join('/')}`;
|
||||
}
|
||||
|
||||
function expired(cert) {
|
||||
return isBefore(cert.validity.notAfter, new Date());
|
||||
}
|
||||
|
||||
function getExpires(cert) {
|
||||
return format(cert.validity.notAfter, 'yyMMddHHmmssX');
|
||||
}
|
||||
|
||||
function createDatabaseEntries(pemCerts, revokedCNs = []) {
|
||||
return pemCerts.map(pemCert => {
|
||||
const pem = fs.readFileSync(pemCert, 'utf8');
|
||||
const cert = pki.certificateFromPem(pem);
|
||||
const subject = getSubject(cert);
|
||||
|
||||
// Determine if the list of revoked CNs contains this cert so we can mark it revoked in the database.
|
||||
const revokedCn = _.find(revokedCNs, cn => {
|
||||
return subject.toUpperCase().indexOf(cn.toUpperCase()) > -1;
|
||||
});
|
||||
|
||||
if (revokedCn) {
|
||||
return `R\t${getExpires(cert)}\t${format(new Date(), 'yyMMddHHmmssX')}\t${cert.serialNumber}\tunknown\t${subject}`;
|
||||
} else {
|
||||
return `${expired(cert) ? 'E' : 'V'}\t${getExpires(cert)}\t\t${cert.serialNumber}\tunknown\t${subject}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeDatabaseFile(entries, dbFile) {
|
||||
fs.writeFileSync(dbFile, entries.join('\n'));
|
||||
fs.writeFileSync(`${dbFile}.attr`, 'unique_subject = no'); // Seems to be required by openssl to generate a CRL.
|
||||
}
|
||||
|
||||
function createDatabase(pemCerts, dbFile = 'index.txt', revokedCNs = []) {
|
||||
writeDatabaseFile(createDatabaseEntries(pemCerts, revokedCNs), dbFile);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create: createDatabase
|
||||
};
|
||||
@@ -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;
|
||||
+120
-100
@@ -1,100 +1,120 @@
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
|
||||
const caDb = require('./caDatabase');
|
||||
const ca = require('./ca');
|
||||
const server = require('./server');
|
||||
const user = require('./user');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
const createSubjectAttributes = require('./subjectAttributes');
|
||||
|
||||
function jsonListCerts(basePath = 'pki/') {
|
||||
const certs = {};
|
||||
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath)) {
|
||||
certs.ca = [];
|
||||
const files = fs.readdirSync(basePath);
|
||||
|
||||
_.forEach(files, filename => {
|
||||
if (fs.lstatSync(basePath + filename).isDirectory()) {
|
||||
const caCerts = {
|
||||
name: filename,
|
||||
servers: [],
|
||||
users: [],
|
||||
};
|
||||
const tempServersBase = `${basePath}${filename}/servers/`;
|
||||
if (fs.existsSync(tempServersBase)) {
|
||||
const serverFiles = fs.readdirSync(tempServersBase);
|
||||
_.forEach(serverFiles, serverName => {
|
||||
caCerts.servers.push(serverName);
|
||||
});
|
||||
}
|
||||
|
||||
const tempUsersBase = `${basePath}${filename}/users/`;
|
||||
if (fs.existsSync(tempUsersBase)) {
|
||||
const userFiles = fs.readdirSync(tempUsersBase);
|
||||
_.forEach(userFiles, userName => {
|
||||
caCerts.users.push(userName);
|
||||
});
|
||||
}
|
||||
|
||||
certs.ca.push(caCerts);
|
||||
}
|
||||
});
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
function getCertFilenames(type, caName, basePath) {
|
||||
let certs = [];
|
||||
|
||||
if (!basePath.endsWith('/')) basePath += '/';
|
||||
const certDir = `${basePath}${normalizeName(caName)}/${type}`;
|
||||
|
||||
if (fs.existsSync(certDir)) {
|
||||
const certNames = fs.readdirSync(certDir);
|
||||
|
||||
certNames.forEach(certName => {
|
||||
if (fs.existsSync(`${certDir}/${certName}/crt.pem`)) {
|
||||
certs.push(`${certDir}/${certName}/crt.pem`);
|
||||
}
|
||||
if (fs.existsSync(`${certDir}/${certName}/expired/crt.pem`)) {
|
||||
certs.push(`${certDir}/${certName}/expired/crt.pem`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
function createCertDatabase(caName, { basePath = 'pki/', revokedCNs = [] } = {}) {
|
||||
const serverCerts = getCertFilenames('users', caName, basePath);
|
||||
const userCerts = getCertFilenames('servers', caName, basePath);
|
||||
caDb.create(
|
||||
userCerts.concat(serverCerts),
|
||||
`${basePath}/${normalizeName(caName)}/ca/index.txt`,
|
||||
revokedCNs
|
||||
);
|
||||
}
|
||||
|
||||
const certs = {
|
||||
createCACert: ca.createCACert,
|
||||
createIntermediateCACert: ca.createIntermediateCACert,
|
||||
listCACerts: ca.listCACerts,
|
||||
removeCACert: ca.removeCACert,
|
||||
createServerCert: server.createServerCert,
|
||||
listServerCerts: server.listServerCerts,
|
||||
removeServerCert: server.removeServerCert,
|
||||
createUserCert: user.createUserCert,
|
||||
listUserCerts: user.listUserCerts,
|
||||
removeUserCert: user.removeUserCert,
|
||||
listCerts: jsonListCerts,
|
||||
createCertDatabase,
|
||||
normalizeName,
|
||||
createSubjectAttributes,
|
||||
};
|
||||
|
||||
module.exports = certs;
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
|
||||
const caDb = require('./caDatabase');
|
||||
const ca = require('./ca');
|
||||
const server = require('./server');
|
||||
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 = {};
|
||||
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath)) {
|
||||
certs.ca = [];
|
||||
const files = fs.readdirSync(basePath);
|
||||
|
||||
_.forEach(files, filename => {
|
||||
if (fs.lstatSync(basePath + filename).isDirectory()) {
|
||||
const caCerts = {
|
||||
name: filename,
|
||||
servers: [],
|
||||
users: [],
|
||||
};
|
||||
const tempServersBase = `${basePath}${filename}/servers/`;
|
||||
if (fs.existsSync(tempServersBase)) {
|
||||
const serverFiles = fs.readdirSync(tempServersBase);
|
||||
_.forEach(serverFiles, serverName => {
|
||||
caCerts.servers.push(serverName);
|
||||
});
|
||||
}
|
||||
|
||||
const tempUsersBase = `${basePath}${filename}/users/`;
|
||||
if (fs.existsSync(tempUsersBase)) {
|
||||
const userFiles = fs.readdirSync(tempUsersBase);
|
||||
_.forEach(userFiles, userName => {
|
||||
caCerts.users.push(userName);
|
||||
});
|
||||
}
|
||||
|
||||
certs.ca.push(caCerts);
|
||||
}
|
||||
});
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
function getCertFilenames(type, caName, basePath) {
|
||||
let certs = [];
|
||||
|
||||
if (!basePath.endsWith('/')) basePath += '/';
|
||||
const certDir = `${basePath}${normalizeName(caName)}/${type}`;
|
||||
|
||||
if (fs.existsSync(certDir)) {
|
||||
const certNames = fs.readdirSync(certDir);
|
||||
|
||||
certNames.forEach(certName => {
|
||||
if (fs.existsSync(`${certDir}/${certName}/crt.pem`)) {
|
||||
certs.push(`${certDir}/${certName}/crt.pem`);
|
||||
}
|
||||
if (fs.existsSync(`${certDir}/${certName}/expired/crt.pem`)) {
|
||||
certs.push(`${certDir}/${certName}/expired/crt.pem`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
function createCertDatabase(caName, { basePath = 'pki/', revokedCNs = [] } = {}) {
|
||||
const serverCerts = getCertFilenames('users', caName, basePath);
|
||||
const userCerts = getCertFilenames('servers', caName, basePath);
|
||||
caDb.create(
|
||||
userCerts.concat(serverCerts),
|
||||
`${basePath}/${normalizeName(caName)}/ca/index.txt`,
|
||||
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,
|
||||
listCACerts: ca.listCACerts,
|
||||
removeCACert: ca.removeCACert,
|
||||
createServerCert: server.createServerCert,
|
||||
listServerCerts: server.listServerCerts,
|
||||
removeServerCert: server.removeServerCert,
|
||||
createUserCert: user.createUserCert,
|
||||
listUserCerts: user.listUserCerts,
|
||||
removeUserCert: user.removeUserCert,
|
||||
listCerts: jsonListCerts,
|
||||
createCertDatabase,
|
||||
normalizeName,
|
||||
createSubjectAttributes,
|
||||
caCertToJSON: caCertToJSONWrapper,
|
||||
serverCertToJSON: serverCertToJSONWrapper,
|
||||
userCertToJSON: userCertToJSONWrapper,
|
||||
};
|
||||
|
||||
module.exports = certs;
|
||||
Regular → Executable
+273
-225
@@ -1,225 +1,273 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const version = require('../package.json').version;
|
||||
|
||||
const yargs = require('yargs');
|
||||
const certs = require('./certs');
|
||||
|
||||
let executed = false;
|
||||
const argv = yargs
|
||||
.scriptName('madcert')
|
||||
.usage('madcert <cmd>')
|
||||
.command('ca-create <name>', 'create a certificate authority', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createCACert(argv.name, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command(
|
||||
'ca-intermediate-create <name> <root_ca_name>',
|
||||
'create an intermediate certificate authority',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.createIntermediateCACert(argv.name, argv.root_ca_name, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
}
|
||||
)
|
||||
.command('ca-list', 'list certificate authorities', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.listCACerts(argv.path);
|
||||
})
|
||||
.command(
|
||||
'ca-remove <name>',
|
||||
'remove a certificate authority and all associated users and servers',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.removeCACert(argv.name, argv.path);
|
||||
}
|
||||
)
|
||||
.command('server-create <name> <ca_name>', 'create a server certificate', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createServerCert(argv.name, argv.ca_name, argv.localhost, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
password: argv.password,
|
||||
rootCaName: argv['root-ca-name'],
|
||||
subjectAltDnsNames: argv['subject-alt-dns'],
|
||||
subjectAltIpNames: argv['subject-alt-ip'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command('server-list', 'list server certificates', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.listServerCerts(argv.path);
|
||||
})
|
||||
.command('server-remove <name> <ca_name>', 'remove a server certificate', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.removeServerCert(argv.name, argv.ca_name, argv.path);
|
||||
})
|
||||
.command('user-create <name> <ca_name>', 'create a user certificate', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createUserCert(argv.name, argv.ca_name, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
rootCaName: argv['root-ca-name'],
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
password: argv.password,
|
||||
subjectAltEmailNames: argv['subject-alt-email'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command('user-list', 'list user certificates', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.listUserCerts(argv.path);
|
||||
})
|
||||
.command('user-remove <name> <ca_name>', 'remove a user certificates', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.removeUserCert(argv.name, argv.ca_name, argv.path);
|
||||
})
|
||||
.command(
|
||||
'create-db <ca_name>',
|
||||
'create an openssl database file from existing certs',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.createCertDatabase(argv.ca_name, {
|
||||
basePath: argv.path ? argv.path : undefined,
|
||||
});
|
||||
}
|
||||
)
|
||||
.option('path', {
|
||||
alias: 'p',
|
||||
describe: 'Base path for pki.',
|
||||
default: 'pki/',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('common-name', {
|
||||
alias: 'n',
|
||||
describe: 'Common Name in the Distinguished Name.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('country', {
|
||||
alias: 'c',
|
||||
describe: 'Country.',
|
||||
default: 'US',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('expired', {
|
||||
alias: 'e',
|
||||
describe: 'Create an expired certificate.',
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('org', {
|
||||
alias: 'o',
|
||||
describe: 'Organization name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('org-unit', {
|
||||
alias: 'u',
|
||||
describe: 'Organizational unit name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('root-ca-name', {
|
||||
alias: 'r',
|
||||
describe: 'Root CA name.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('localhost', {
|
||||
alias: 'l',
|
||||
describe: 'Create a localhost server certificate with subject alternative names.',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
})
|
||||
.option('password', {
|
||||
alias: 'w',
|
||||
describe: 'Create the key with the the password (defaults to "changeme").',
|
||||
type: 'string',
|
||||
default: 'changeme',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-dns', {
|
||||
alias: 'd',
|
||||
describe:
|
||||
'Create certificate with DNS subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-ip', {
|
||||
alias: 'i',
|
||||
describe:
|
||||
'Create certificate with IP subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-email', {
|
||||
alias: ['m', 'subject-alt-rfc822'],
|
||||
describe:
|
||||
'Create certificate with rfc822/email subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('valid-from', {
|
||||
alias: 'f',
|
||||
describe: 'Valid from date in ISO 8601 format.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('valid-to', {
|
||||
alias: 't',
|
||||
describe: 'Valid to date in ISO 8601 format.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.alias('version', 'v')
|
||||
.alias('h', 'help')
|
||||
.conflicts('expired', 'valid-to')
|
||||
.version(version)
|
||||
.help('help')
|
||||
.group('common-name', 'Creation Options:')
|
||||
.group('country', 'Creation Options:')
|
||||
.group('expired', 'Creation Options:')
|
||||
.group('org-unit', 'Creation Options:')
|
||||
.group('org', 'Creation Options:')
|
||||
.group('root-ca-name', 'Creation Options:')
|
||||
.group('valid-from', 'Creation Options:')
|
||||
.group('valid-to', 'Creation Options:')
|
||||
.group('password', 'User Creation Options:')
|
||||
.group('subject-alt-email', 'User Creation Options:')
|
||||
.group('localhost', 'Server Creation Options:')
|
||||
.group('password', 'Server Creation Options:')
|
||||
.group('subject-alt-dns', 'Server Creation Options:')
|
||||
.group('subject-alt-ip', 'Server Creation Options:')
|
||||
.wrap(yargs.terminalWidth()).argv;
|
||||
|
||||
if (!executed) {
|
||||
yargs.showHelp();
|
||||
}
|
||||
#!/usr/bin/env node
|
||||
|
||||
const version = require('../package.json').version;
|
||||
|
||||
const util = require('util')
|
||||
const yargs = require('yargs');
|
||||
const certs = require('./certs');
|
||||
|
||||
let executed = false;
|
||||
const argv = yargs
|
||||
.scriptName('madcert')
|
||||
.usage('madcert <cmd>')
|
||||
.command('ca-create <name>', 'create a certificate authority', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createCACert(argv.name, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command(
|
||||
'ca-intermediate-create <name> <root_ca_name>',
|
||||
'create an intermediate certificate authority',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.createIntermediateCACert(argv.name, argv.root_ca_name, {
|
||||
basePath: argv.path,
|
||||
commonName: argv['common-name'],
|
||||
country: argv.country,
|
||||
expired: argv.expired,
|
||||
organizations: argv.org,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
}
|
||||
)
|
||||
.command('ca-list', 'list certificate authorities', {}, function(argv) {
|
||||
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',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.removeCACert(argv.name, argv.path);
|
||||
}
|
||||
)
|
||||
.command('server-create <name> <ca_name>', 'create a server certificate', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createServerCert(argv.name, argv.ca_name, argv.localhost, {
|
||||
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'],
|
||||
password: argv.password,
|
||||
rootCaName: argv['root-ca-name'],
|
||||
subjectAltDnsNames: argv['subject-alt-dns'],
|
||||
subjectAltIpNames: argv['subject-alt-ip'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command('server-list', 'list server certificates', {}, function(argv) {
|
||||
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);
|
||||
})
|
||||
.command('user-create <name> <ca_name>', 'create a user certificate', {}, function(argv) {
|
||||
executed = true;
|
||||
certs.createUserCert(argv.name, argv.ca_name, {
|
||||
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,
|
||||
organizationalUnits: argv['org-unit'],
|
||||
password: argv.password,
|
||||
subjectAltEmailNames: argv['subject-alt-email'],
|
||||
validFrom: argv['valid-from'],
|
||||
validTo: argv['valid-to'],
|
||||
});
|
||||
})
|
||||
.command('user-list', 'list user certificates', {}, function(argv) {
|
||||
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);
|
||||
})
|
||||
.command(
|
||||
'create-db <ca_name>',
|
||||
'create an openssl database file from existing certs',
|
||||
{},
|
||||
function(argv) {
|
||||
executed = true;
|
||||
certs.createCertDatabase(argv.ca_name, {
|
||||
basePath: argv.path ? argv.path : undefined,
|
||||
});
|
||||
}
|
||||
)
|
||||
.option('path', {
|
||||
alias: 'p',
|
||||
describe: 'Base path for pki.',
|
||||
default: 'pki/',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('common-name', {
|
||||
alias: 'n',
|
||||
describe: 'Common Name in the Distinguished Name.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('country', {
|
||||
alias: 'c',
|
||||
describe: 'Country.',
|
||||
default: 'US',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('locality', {
|
||||
describe: 'Locality',
|
||||
})
|
||||
.option('state', {
|
||||
alias: 'st',
|
||||
describe: 'State',
|
||||
})
|
||||
.option('expired', {
|
||||
alias: 'e',
|
||||
describe: 'Create an expired certificate.',
|
||||
type: 'boolean',
|
||||
})
|
||||
.option('org', {
|
||||
alias: 'o',
|
||||
describe: 'Organization name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('org-unit', {
|
||||
alias: 'u',
|
||||
describe: 'Organizational unit name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('root-ca-name', {
|
||||
alias: 'r',
|
||||
describe: 'Root CA name.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('localhost', {
|
||||
alias: 'l',
|
||||
describe: 'Create a localhost server certificate with subject alternative names.',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
})
|
||||
.option('password', {
|
||||
alias: 'w',
|
||||
describe: 'Create the key with the the password (defaults to "changeme").',
|
||||
type: 'string',
|
||||
default: 'changeme',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-dns', {
|
||||
alias: 'd',
|
||||
describe:
|
||||
'Create certificate with DNS subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-ip', {
|
||||
alias: 'i',
|
||||
describe:
|
||||
'Create certificate with IP subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('subject-alt-email', {
|
||||
alias: ['m', 'subject-alt-rfc822'],
|
||||
describe:
|
||||
'Create certificate with rfc822/email subject alternative name. This option can be specified multiple times.',
|
||||
type: 'array',
|
||||
default: [],
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('valid-from', {
|
||||
alias: 'f',
|
||||
describe: 'Valid from date in ISO 8601 format.',
|
||||
requiresArg: true,
|
||||
})
|
||||
.option('valid-to', {
|
||||
alias: 't',
|
||||
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')
|
||||
.version(version)
|
||||
.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:')
|
||||
.group('root-ca-name', 'Creation Options:')
|
||||
.group('valid-from', 'Creation Options:')
|
||||
.group('valid-to', 'Creation Options:')
|
||||
.group('password', 'User Creation Options:')
|
||||
.group('subject-alt-email', 'User Creation Options:')
|
||||
.group('localhost', 'Server Creation Options:')
|
||||
.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) {
|
||||
yargs.showHelp();
|
||||
}
|
||||
+305
-310
@@ -1,310 +1,305 @@
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
const DNS_TYPE = 2;
|
||||
const IP_TYPE = 7;
|
||||
|
||||
const madcertLhSubjectAltDNSName = process.env.MADCERT_LH_SUBJECT_ALT_DNS_NAME || '';
|
||||
|
||||
function getIpAddresses() {
|
||||
const localIPs = [];
|
||||
|
||||
// Pull the IP address
|
||||
const nic = os.networkInterfaces();
|
||||
for (let i in nic) {
|
||||
const names = nic[i];
|
||||
for (let j = 0; j < names.length; j++) {
|
||||
const k = names[j];
|
||||
if (k.family === 'IPv4' && k.address !== '127.0.0.1' && !k.internal) {
|
||||
localIPs.push(k.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return localIPs;
|
||||
}
|
||||
|
||||
function buildServerCert(keys, caName, caSubject, localhost, options) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// Parse the validTo from ISO 8601 format
|
||||
cert.validity.notAfter = new Date(options.validTo);
|
||||
}
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caSubject);
|
||||
const extensions = [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: false,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
keyCertSign: true,
|
||||
digitalSignature: true,
|
||||
nonRepudiation: true,
|
||||
keyEncipherment: true,
|
||||
dataEncipherment: 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',
|
||||
},
|
||||
];
|
||||
|
||||
const altNames = [];
|
||||
|
||||
// Create the subject alternative names if this is creating the localhost certificate
|
||||
if (localhost) {
|
||||
// If the environment variable MADCERT_LH_SUBJECT_ALT_DNS_NAME is not set use the hostname
|
||||
const localHostName = madcertLhSubjectAltDNSName || os.hostname();
|
||||
|
||||
// Add to the extensions object
|
||||
altNames.push.apply(altNames, [
|
||||
{
|
||||
type: DNS_TYPE,
|
||||
value: localHostName,
|
||||
},
|
||||
{
|
||||
type: DNS_TYPE,
|
||||
value: 'localhost',
|
||||
},
|
||||
{
|
||||
type: IP_TYPE,
|
||||
ip: '127.0.0.1',
|
||||
},
|
||||
]);
|
||||
|
||||
const localIPs = getIpAddresses().map(ip => {
|
||||
return {
|
||||
type: IP_TYPE,
|
||||
ip,
|
||||
};
|
||||
});
|
||||
|
||||
altNames.push.apply(altNames, localIPs);
|
||||
}
|
||||
|
||||
// Add any subject alternative names provided on the command line
|
||||
_.each(_.get(options, 'subjectAltDnsNames', []), name => {
|
||||
altNames.push({
|
||||
type: DNS_TYPE,
|
||||
value: name,
|
||||
});
|
||||
});
|
||||
|
||||
_.each(_.get(options, 'subjectAltIpNames', []), name => {
|
||||
altNames.push({
|
||||
type: IP_TYPE,
|
||||
ip: name,
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(altNames)) {
|
||||
extensions.push.apply(extensions, [{ name: 'subjectAltName', altNames: altNames }]);
|
||||
}
|
||||
|
||||
// Add all extensions
|
||||
cert.setExtensions(extensions);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
function createServerCert(
|
||||
serverName,
|
||||
caCertName,
|
||||
localhost,
|
||||
options,
|
||||
callback = (err, data) => {}
|
||||
) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
const err = {
|
||||
message: `Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let expired = _.get(options, 'expired', false);
|
||||
|
||||
// If valid-to was passed to madcert then check if it is in the past
|
||||
if (options.validTo) {
|
||||
const date = new Date(options.validTo);
|
||||
if (date.getTime() < new Date().getTime()) {
|
||||
expired = true;
|
||||
}
|
||||
}
|
||||
|
||||
const password = _.get(options, 'password', 'changeme');
|
||||
|
||||
const caPath = path.join(basePath, normalizeName(caCertName), '/ca/');
|
||||
const serverPath = path.join(basePath, normalizeName(caCertName), '/servers/');
|
||||
const serverDir = path.join(
|
||||
serverPath,
|
||||
normalizeName(serverName),
|
||||
options.expired ? '/expired' : ''
|
||||
);
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Certificate Authority ${caCertName} does not exist, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const serverCertPath = path.join(serverDir, '/crt.pem');
|
||||
const serverKeyPath = path.join(serverDir, '/key.pem');
|
||||
const serverP12Path = path.join(serverDir, '/bundle.p12');
|
||||
|
||||
if (
|
||||
!fs.existsSync(serverDir) ||
|
||||
!fs.existsSync(serverCertPath) ||
|
||||
!fs.existsSync(serverKeyPath) ||
|
||||
!fs.existsSync(serverP12Path)
|
||||
) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
|
||||
let rootCaCert;
|
||||
|
||||
const rootCaName = _.get(options, 'rootCaName', null);
|
||||
if (rootCaName !== null) {
|
||||
const rootCaPath = path.join(basePath, normalizeName(rootCaName), '/ca/');
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Root Certificate Authority ${rootCaName} does not exist, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootCaCertPem = fs.readFileSync(path.join(rootCaPath, '/crt.pem'), 'utf8');
|
||||
rootCaCert = forge.pki.certificateFromPem(rootCaCertPem);
|
||||
}
|
||||
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(path.join(caPath, '/crt.pem'), 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const caKeyPem = fs.readFileSync(path.join(caPath, '/key.pem'), 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
|
||||
const cert = buildServerCert(
|
||||
keys,
|
||||
caCertName,
|
||||
caCert.subject.attributes,
|
||||
localhost,
|
||||
options
|
||||
);
|
||||
fs.ensureDirSync(serverPath);
|
||||
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
|
||||
// convert a Forge certificate to PEM
|
||||
const pem = pki.certificateToPem(cert);
|
||||
fs.outputFileSync(serverCertPath, pem);
|
||||
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
fs.outputFileSync(serverKeyPath, privateKeyPem);
|
||||
|
||||
let p12Asn1;
|
||||
//create .p12 file
|
||||
if (rootCaCert) {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(
|
||||
keys.privateKey,
|
||||
[cert, caCertPem, rootCaCert],
|
||||
password,
|
||||
{
|
||||
algorithm: '3des',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert, caCertPem], password, {
|
||||
algorithm: '3des',
|
||||
});
|
||||
}
|
||||
const p12Der = forge.asn1.toDer(p12Asn1).getBytes();
|
||||
fs.writeFileSync(serverP12Path, p12Der, {
|
||||
encoding: 'binary',
|
||||
});
|
||||
|
||||
const success = {
|
||||
message: `${serverName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}server certificate was created and signed by ${caCertName}.`,
|
||||
};
|
||||
console.error(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const err = {
|
||||
message: `${serverName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}server certificate already exists in ${caCertName}.`,
|
||||
};
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function listServerCerts(path) {
|
||||
utils.listCerts('servers', path);
|
||||
}
|
||||
|
||||
function removeServerCert(name, caName, basePath = 'pki/', callback) {
|
||||
utils.removeCerts('servers', name, caName, basePath, callback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createServerCert,
|
||||
listServerCerts,
|
||||
removeServerCert,
|
||||
};
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
const DNS_TYPE = 2;
|
||||
const IP_TYPE = 7;
|
||||
|
||||
const madcertLhSubjectAltDNSName = process.env.MADCERT_LH_SUBJECT_ALT_DNS_NAME || '';
|
||||
|
||||
function getIpAddresses() {
|
||||
const localIPs = [];
|
||||
|
||||
// Pull the IP address
|
||||
const nic = os.networkInterfaces();
|
||||
for (let i in nic) {
|
||||
const names = nic[i];
|
||||
for (let j = 0; j < names.length; j++) {
|
||||
const k = names[j];
|
||||
if (k.family === 'IPv4' && k.address !== '127.0.0.1' && !k.internal) {
|
||||
localIPs.push(k.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return localIPs;
|
||||
}
|
||||
|
||||
function buildServerCert(keys, caName, caCert, localhost, options) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// Parse the validTo from ISO 8601 format
|
||||
cert.validity.notAfter = new Date(options.validTo);
|
||||
}
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caCert.subject.attributes);
|
||||
const extensions = [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: false,
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
digitalSignature: true,
|
||||
keyEncipherment: true,
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: 'extKeyUsage',
|
||||
serverAuth: true,
|
||||
clientAuth: true,
|
||||
emailProtection: true,
|
||||
},
|
||||
{
|
||||
name: 'subjectKeyIdentifier',
|
||||
},
|
||||
{
|
||||
name: 'authorityKeyIdentifier',
|
||||
keyIdentifier: caCert.generateSubjectKeyIdentifier().getBytes(),
|
||||
},
|
||||
];
|
||||
|
||||
const altNames = [];
|
||||
|
||||
// Create the subject alternative names if this is creating the localhost certificate
|
||||
if (localhost) {
|
||||
// If the environment variable MADCERT_LH_SUBJECT_ALT_DNS_NAME is not set use the hostname
|
||||
const localHostName = madcertLhSubjectAltDNSName || os.hostname();
|
||||
|
||||
// Add to the extensions object
|
||||
altNames.push.apply(altNames, [
|
||||
{
|
||||
type: DNS_TYPE,
|
||||
value: localHostName,
|
||||
},
|
||||
{
|
||||
type: DNS_TYPE,
|
||||
value: 'localhost',
|
||||
},
|
||||
{
|
||||
type: IP_TYPE,
|
||||
ip: '127.0.0.1',
|
||||
},
|
||||
]);
|
||||
|
||||
const localIPs = getIpAddresses().map(ip => {
|
||||
return {
|
||||
type: IP_TYPE,
|
||||
ip,
|
||||
};
|
||||
});
|
||||
|
||||
altNames.push.apply(altNames, localIPs);
|
||||
}
|
||||
|
||||
// Add any subject alternative names provided on the command line
|
||||
_.each(_.get(options, 'subjectAltDnsNames', []), name => {
|
||||
altNames.push({
|
||||
type: DNS_TYPE,
|
||||
value: name,
|
||||
});
|
||||
});
|
||||
|
||||
_.each(_.get(options, 'subjectAltIpNames', []), name => {
|
||||
altNames.push({
|
||||
type: IP_TYPE,
|
||||
ip: name,
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(altNames)) {
|
||||
extensions.push.apply(extensions, [{ name: 'subjectAltName', altNames: altNames }]);
|
||||
}
|
||||
|
||||
// Add all extensions
|
||||
cert.setExtensions(extensions);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
function createServerCert(
|
||||
serverName,
|
||||
caCertName,
|
||||
localhost,
|
||||
options,
|
||||
callback = (err, data) => {}
|
||||
) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
const err = {
|
||||
message: `Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let expired = _.get(options, 'expired', false);
|
||||
|
||||
// If valid-to was passed to madcert then check if it is in the past
|
||||
if (options.validTo) {
|
||||
const date = new Date(options.validTo);
|
||||
if (date.getTime() < new Date().getTime()) {
|
||||
expired = true;
|
||||
}
|
||||
}
|
||||
|
||||
const password = _.get(options, 'password', 'changeme');
|
||||
|
||||
const caPath = path.join(basePath, normalizeName(caCertName), '/ca/');
|
||||
const serverPath = path.join(basePath, normalizeName(caCertName), '/servers/');
|
||||
const serverDir = path.join(
|
||||
serverPath,
|
||||
normalizeName(serverName),
|
||||
options.expired ? '/expired' : ''
|
||||
);
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Certificate Authority ${caCertName} does not exist, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
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');
|
||||
|
||||
if (
|
||||
!fs.existsSync(serverDir) ||
|
||||
!fs.existsSync(serverCertPath) ||
|
||||
!fs.existsSync(serverKeyPath) ||
|
||||
!fs.existsSync(serverP12Path)
|
||||
) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
|
||||
let rootCaCert;
|
||||
|
||||
const rootCaName = _.get(options, 'rootCaName', null);
|
||||
if (rootCaName !== null) {
|
||||
const rootCaPath = path.join(basePath, normalizeName(rootCaName), '/ca/');
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Root Certificate Authority ${rootCaName} does not exist, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootCaCertPem = fs.readFileSync(path.join(rootCaPath, '/crt.pem'), 'utf8');
|
||||
rootCaCert = forge.pki.certificateFromPem(rootCaCertPem);
|
||||
}
|
||||
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(path.join(caPath, '/crt.pem'), 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const caKeyPem = fs.readFileSync(path.join(caPath, '/key.pem'), 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
|
||||
const cert = buildServerCert(
|
||||
keys,
|
||||
caCertName,
|
||||
caCert,
|
||||
localhost,
|
||||
options
|
||||
);
|
||||
fs.ensureDirSync(serverPath);
|
||||
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
|
||||
// convert a Forge certificate to PEM
|
||||
const pem = pki.certificateToPem(cert);
|
||||
fs.outputFileSync(serverCertPath, pem);
|
||||
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
fs.outputFileSync(serverKeyPath, privateKeyPem);
|
||||
|
||||
let p12Asn1;
|
||||
//create .p12 file
|
||||
if (rootCaCert) {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(
|
||||
keys.privateKey,
|
||||
[cert, caCertPem, rootCaCert],
|
||||
password,
|
||||
{
|
||||
algorithm: '3des',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert, caCertPem], password, {
|
||||
algorithm: '3des',
|
||||
});
|
||||
}
|
||||
const p12Der = forge.asn1.toDer(p12Asn1).getBytes();
|
||||
fs.writeFileSync(serverP12Path, p12Der, {
|
||||
encoding: 'binary',
|
||||
});
|
||||
|
||||
const success = {
|
||||
message: `${serverName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}server certificate was created and signed by ${caCertName}.`,
|
||||
};
|
||||
console.log(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const err = {
|
||||
message: `${serverName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}server certificate already exists in ${caCertName}.`,
|
||||
};
|
||||
console.error(err);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function listServerCerts(path) {
|
||||
utils.listCerts('servers', path);
|
||||
}
|
||||
|
||||
function removeServerCert(name, caName, basePath = 'pki/', callback) {
|
||||
utils.removeCerts('servers', name, caName, basePath, callback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createServerCert,
|
||||
listServerCerts,
|
||||
removeServerCert,
|
||||
};
|
||||
+54
-39
@@ -1,39 +1,54 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
function createSubjectAttributes(options) {
|
||||
const attrs = [];
|
||||
|
||||
const country = _.get(options, 'country', 'US');
|
||||
attrs.push({
|
||||
shortName: 'C',
|
||||
value: country,
|
||||
});
|
||||
|
||||
const orgs = _.get(options, 'organizations', []);
|
||||
_.each(orgs, org => {
|
||||
attrs.push({
|
||||
shortName: 'O',
|
||||
value: org,
|
||||
});
|
||||
});
|
||||
|
||||
const ous = _.get(options, 'organizationalUnits', []);
|
||||
_.each(ous, ou => {
|
||||
attrs.push({
|
||||
shortName: 'OU',
|
||||
value: ou,
|
||||
});
|
||||
});
|
||||
|
||||
const cn = _.get(options, 'commonName');
|
||||
if (cn) {
|
||||
attrs.push({
|
||||
shortName: 'CN',
|
||||
value: cn,
|
||||
});
|
||||
}
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
module.exports = createSubjectAttributes;
|
||||
const _ = require('lodash');
|
||||
|
||||
function createSubjectAttributes(options) {
|
||||
const attrs = [];
|
||||
|
||||
const country = _.get(options, 'country', 'US');
|
||||
attrs.push({
|
||||
shortName: 'C',
|
||||
value: country,
|
||||
});
|
||||
|
||||
const orgs = _.get(options, 'organizations', []);
|
||||
_.each(orgs, org => {
|
||||
attrs.push({
|
||||
shortName: 'O',
|
||||
value: org,
|
||||
});
|
||||
});
|
||||
|
||||
const ous = _.get(options, 'organizationalUnits', []);
|
||||
_.each(ous, ou => {
|
||||
attrs.push({
|
||||
shortName: 'OU',
|
||||
value: ou,
|
||||
});
|
||||
});
|
||||
|
||||
const cn = _.get(options, 'commonName');
|
||||
if (cn) {
|
||||
attrs.push({
|
||||
shortName: 'CN',
|
||||
value: cn,
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
module.exports = createSubjectAttributes;
|
||||
+229
-238
@@ -1,239 +1,230 @@
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
function buildUserCert(keys, options, caSubject) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// Parse the validTo from ISO 8601 format
|
||||
cert.validity.notAfter = new Date(options.validTo);
|
||||
}
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caSubject);
|
||||
const extensions = [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: false,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
keyCertSign: true,
|
||||
digitalSignature: true,
|
||||
nonRepudiation: true,
|
||||
keyEncipherment: true,
|
||||
dataEncipherment: 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',
|
||||
},
|
||||
];
|
||||
|
||||
const altNames = [];
|
||||
|
||||
// Add any subject alternative names provided on the command line
|
||||
_.each(_.get(options, 'subjectAltEmailNames', []), name => {
|
||||
altNames.push({
|
||||
type: 1, // rfc822Name
|
||||
value: name,
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(altNames)) {
|
||||
extensions.push.apply(extensions, [{ name: 'subjectAltName', altNames: altNames }]);
|
||||
}
|
||||
|
||||
// Add all extensions
|
||||
cert.setExtensions(extensions);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
function createUserCert(userName, caCertName, options, callback = (err, data) => {}) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
const err = {
|
||||
message: `Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const caPath = basePath + normalizeName(caCertName) + '/ca/';
|
||||
const userPath = basePath + normalizeName(caCertName) + '/users/';
|
||||
|
||||
let expired = _.get(options, 'expired', false);
|
||||
|
||||
// If valid-to was passed to madcert then check if it is in the past
|
||||
if (options.validTo) {
|
||||
const date = new Date(options.validTo);
|
||||
if (date.getTime() < new Date().getTime()) {
|
||||
expired = true;
|
||||
}
|
||||
}
|
||||
|
||||
const password = _.get(options, 'password', 'changeme');
|
||||
|
||||
const userDir = userPath + normalizeName(userName) + (expired ? '/expired' : '');
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Certificate Authority ${caCertName} does not exist, aborting creation of new user certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Adding a check for the common name to be populated
|
||||
// with the name field if it is blank.
|
||||
options.commonName = _.get(options, 'commonName', userName);
|
||||
|
||||
const userCertPath = userDir + '/crt.pem';
|
||||
const userKeyPath = userDir + '/key.pem';
|
||||
const userP12Path = userDir + '/bundle.p12';
|
||||
|
||||
if (
|
||||
!fs.existsSync(userDir) ||
|
||||
!fs.existsSync(userCertPath) ||
|
||||
!fs.existsSync(userKeyPath) ||
|
||||
!fs.existsSync(userP12Path)
|
||||
) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
|
||||
let rootCaCert;
|
||||
|
||||
const rootCaName = _.get(options, 'rootCaName', null);
|
||||
if (rootCaName !== null) {
|
||||
const rootCaPath = basePath + normalizeName(rootCaName) + '/ca/';
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Root Certificate Authority ${rootCaName} does not exist, aborting creation of new user certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootCaCertPem = fs.readFileSync(rootCaPath + '/crt.pem', 'utf8');
|
||||
rootCaCert = forge.pki.certificateFromPem(rootCaCertPem);
|
||||
}
|
||||
|
||||
fs.ensureDirSync(userPath);
|
||||
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(caPath + '/crt.pem', 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const caKeyPem = fs.readFileSync(caPath + '/key.pem', 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
|
||||
const cert = buildUserCert(keys, options, caCert.subject.attributes);
|
||||
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
|
||||
// convert a Forge certificate to PEM
|
||||
const pem = pki.certificateToPem(cert);
|
||||
fs.outputFileSync(userCertPath, pem);
|
||||
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
fs.outputFileSync(userKeyPath, privateKeyPem);
|
||||
|
||||
let p12Asn1;
|
||||
//create .p12 file
|
||||
if (rootCaCert) {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(
|
||||
keys.privateKey,
|
||||
[cert, caCert, rootCaCert],
|
||||
password,
|
||||
{
|
||||
algorithm: '3des',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert, caCert], password, {
|
||||
algorithm: '3des',
|
||||
});
|
||||
}
|
||||
|
||||
const p12Der = forge.asn1.toDer(p12Asn1).getBytes();
|
||||
fs.writeFileSync(userP12Path, p12Der, {
|
||||
encoding: 'binary',
|
||||
});
|
||||
|
||||
const success = {
|
||||
message: `${userName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}user certificate was created and signed by ${caCertName}.`,
|
||||
};
|
||||
|
||||
console.log(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const err = {
|
||||
message: `${userName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}user certificate already exists in ${caCertName}.`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function listUserCerts(path) {
|
||||
return utils.listCerts('users', path);
|
||||
}
|
||||
|
||||
function removeUserCert(name, caName, basePath = 'pki/', callback) {
|
||||
utils.removeCerts('users', name, caName, basePath, callback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createUserCert,
|
||||
listUserCerts,
|
||||
removeUserCert,
|
||||
const _ = require('lodash');
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
const pki = forge.pki;
|
||||
|
||||
const subjectAttrs = require('./subjectAttributes');
|
||||
const utils = require('./utils');
|
||||
const normalizeName = utils.normalizeName;
|
||||
|
||||
function buildUserCert(keys, options, caCert) {
|
||||
const cert = pki.createCertificate();
|
||||
cert.publicKey = keys.publicKey;
|
||||
cert.serialNumber = utils.getSerial();
|
||||
|
||||
// Set the default expiration, then override with valid-from and/or valid-to
|
||||
const expired = _.get(options, 'expired', false);
|
||||
utils.setExpirationDate(cert, expired);
|
||||
|
||||
if (options.validFrom) {
|
||||
// Parse the validFrom from ISO 8601 format
|
||||
cert.validity.notBefore = new Date(options.validFrom);
|
||||
}
|
||||
|
||||
if (options.validTo) {
|
||||
// Parse the validTo from ISO 8601 format
|
||||
cert.validity.notAfter = new Date(options.validTo);
|
||||
}
|
||||
|
||||
const attrs = subjectAttrs(options);
|
||||
|
||||
cert.setSubject(attrs);
|
||||
cert.setIssuer(caCert.subject.attributes);
|
||||
const extensions = [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: false,
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: 'keyUsage',
|
||||
digitalSignature: true,
|
||||
keyEncipherment: true,
|
||||
critical: true,
|
||||
},
|
||||
{
|
||||
name: 'extKeyUsage',
|
||||
serverAuth: true,
|
||||
clientAuth: true,
|
||||
emailProtection: true,
|
||||
},
|
||||
{
|
||||
name: 'subjectKeyIdentifier',
|
||||
},
|
||||
{
|
||||
name: 'authorityKeyIdentifier',
|
||||
keyIdentifier: caCert.generateSubjectKeyIdentifier().getBytes(),
|
||||
},
|
||||
];
|
||||
|
||||
const altNames = [];
|
||||
|
||||
// Add any subject alternative names provided on the command line
|
||||
_.each(_.get(options, 'subjectAltEmailNames', []), name => {
|
||||
altNames.push({
|
||||
type: 1, // rfc822Name
|
||||
value: name,
|
||||
});
|
||||
});
|
||||
|
||||
if (!_.isEmpty(altNames)) {
|
||||
extensions.push.apply(extensions, [{ name: 'subjectAltName', altNames: altNames }]);
|
||||
}
|
||||
|
||||
// Add all extensions
|
||||
cert.setExtensions(extensions);
|
||||
|
||||
return cert;
|
||||
}
|
||||
|
||||
function createUserCert(userName, caCertName, options, callback = (err, data) => {}) {
|
||||
let basePath = _.get(options, 'basePath', 'pki/');
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (options.validFrom && options.validTo) {
|
||||
const validFrom = new Date(options.validFrom);
|
||||
const validTo = new Date(options.validTo);
|
||||
if (validTo.getTime() < validFrom.getTime()) {
|
||||
const err = {
|
||||
message: `Expiration date ${options.validTo} before start date ${options.validFrom}, aborting creation of new server certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const caPath = basePath + normalizeName(caCertName) + '/ca/';
|
||||
const userPath = basePath + normalizeName(caCertName) + '/users/';
|
||||
|
||||
let expired = _.get(options, 'expired', false);
|
||||
|
||||
// If valid-to was passed to madcert then check if it is in the past
|
||||
if (options.validTo) {
|
||||
const date = new Date(options.validTo);
|
||||
if (date.getTime() < new Date().getTime()) {
|
||||
expired = true;
|
||||
}
|
||||
}
|
||||
|
||||
const password = _.get(options, 'password', 'changeme');
|
||||
|
||||
const userDir = userPath + normalizeName(userName) + (expired ? '/expired' : '');
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Certificate Authority ${caCertName} does not exist, aborting creation of new user certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Adding a check for the common name to be populated
|
||||
// with the name field if it is blank.
|
||||
options.commonName = _.get(options, 'commonName', userName);
|
||||
|
||||
const userCertPath = userDir + '/crt.pem';
|
||||
const userKeyPath = userDir + '/key.pem';
|
||||
const userP12Path = userDir + '/bundle.p12';
|
||||
|
||||
if (
|
||||
!fs.existsSync(userDir) ||
|
||||
!fs.existsSync(userCertPath) ||
|
||||
!fs.existsSync(userKeyPath) ||
|
||||
!fs.existsSync(userP12Path)
|
||||
) {
|
||||
const keys = pki.rsa.generateKeyPair(2048);
|
||||
|
||||
let rootCaCert;
|
||||
|
||||
const rootCaName = _.get(options, 'rootCaName', null);
|
||||
if (rootCaName !== null) {
|
||||
const rootCaPath = basePath + normalizeName(rootCaName) + '/ca/';
|
||||
if (!fs.existsSync(caPath)) {
|
||||
const err = {
|
||||
message: `Root Certificate Authority ${rootCaName} does not exist, aborting creation of new user certificate`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootCaCertPem = fs.readFileSync(rootCaPath + '/crt.pem', 'utf8');
|
||||
rootCaCert = forge.pki.certificateFromPem(rootCaCertPem);
|
||||
}
|
||||
|
||||
fs.ensureDirSync(userPath);
|
||||
|
||||
// sign certificate with CA private key
|
||||
const caCertPem = fs.readFileSync(caPath + '/crt.pem', 'utf8');
|
||||
const caCert = forge.pki.certificateFromPem(caCertPem);
|
||||
const caKeyPem = fs.readFileSync(caPath + '/key.pem', 'utf8');
|
||||
const caKey = forge.pki.privateKeyFromPem(caKeyPem);
|
||||
|
||||
const cert = buildUserCert(keys, options, caCert);
|
||||
|
||||
cert.sign(caKey, utils.createMessageDigest());
|
||||
|
||||
// convert a Forge certificate to PEM
|
||||
const pem = pki.certificateToPem(cert);
|
||||
fs.outputFileSync(userCertPath, pem);
|
||||
|
||||
const privateKeyPem = pki.privateKeyToPem(keys.privateKey);
|
||||
fs.outputFileSync(userKeyPath, privateKeyPem);
|
||||
|
||||
let p12Asn1;
|
||||
//create .p12 file
|
||||
if (rootCaCert) {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(
|
||||
keys.privateKey,
|
||||
[cert, caCert, rootCaCert],
|
||||
password,
|
||||
{
|
||||
algorithm: '3des',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
p12Asn1 = forge.pkcs12.toPkcs12Asn1(keys.privateKey, [cert, caCert], password, {
|
||||
algorithm: '3des',
|
||||
});
|
||||
}
|
||||
|
||||
const p12Der = forge.asn1.toDer(p12Asn1).getBytes();
|
||||
fs.writeFileSync(userP12Path, p12Der, {
|
||||
encoding: 'binary',
|
||||
});
|
||||
|
||||
const success = {
|
||||
message: `${userName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}user certificate was created and signed by ${caCertName}.`,
|
||||
};
|
||||
|
||||
console.log(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const err = {
|
||||
message: `${userName} ${
|
||||
expired ? 'expired ' : ''
|
||||
}user certificate already exists in ${caCertName}.`,
|
||||
};
|
||||
console.error(err.message);
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
function listUserCerts(path) {
|
||||
return utils.listCerts('users', path);
|
||||
}
|
||||
|
||||
function removeUserCert(name, caName, basePath = 'pki/', callback) {
|
||||
utils.removeCerts('users', name, caName, basePath, callback);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createUserCert,
|
||||
listUserCerts,
|
||||
removeUserCert,
|
||||
};
|
||||
+108
-107
@@ -1,107 +1,108 @@
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const path = require('path');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
|
||||
function getSerial() {
|
||||
return moment().format('x'); // Current time formatted in milliseconds since the epoch.
|
||||
}
|
||||
|
||||
function listCerts(type, basePath = 'pki/') {
|
||||
const certs = [];
|
||||
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath)) {
|
||||
const files = fs.readdirSync(basePath);
|
||||
|
||||
_.forEach(files, (c, i) => {
|
||||
if (type !== 'ca') {
|
||||
if (type === 'users') {
|
||||
console.log('User certificates for ' + c + ':');
|
||||
} else {
|
||||
console.log('Server certificates for ' + c + ':');
|
||||
}
|
||||
const tempBase = basePath + c + '/' + type + '/';
|
||||
if (fs.existsSync(tempBase)) {
|
||||
const files = fs.readdirSync(tempBase);
|
||||
_.forEach(files, c => {
|
||||
certs.push(c);
|
||||
console.log('\t' + c);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (i === 0) {
|
||||
console.log('Available Certificate Authorities:');
|
||||
}
|
||||
certs.push(c);
|
||||
console.log('\t' + c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
// Converts name to file path friendly name
|
||||
function normalizeName(name) {
|
||||
return name.replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
function removeCerts(type, name, caName, basePath = 'pki/', callback = (err, data) => {}) {
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
const certDir = path.join(basePath, normalizeName(caName), type, normalizeName(name));
|
||||
|
||||
if (fs.existsSync(certDir)) {
|
||||
fs.removeSync(certDir);
|
||||
const success = {
|
||||
message: `${name} was removed from ${caName}.`,
|
||||
};
|
||||
console.log(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const error = {
|
||||
message: `${name} does not exist for ${caName}.`,
|
||||
};
|
||||
console.error(error.message);
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
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 not expired, set cert to be valid starting yesterday
|
||||
expired
|
||||
? cert.validity.notBefore.setFullYear(cert.validity.notBefore.getFullYear() - 5)
|
||||
: 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
|
||||
expired
|
||||
? cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() - 1)
|
||||
: cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 5);
|
||||
}
|
||||
|
||||
function setValidFrom(cert, date) {}
|
||||
|
||||
function setValidTo(cert, date) {}
|
||||
|
||||
function createMessageDigest() {
|
||||
return forge.md.sha384.create();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createMessageDigest,
|
||||
getSerial,
|
||||
listCerts,
|
||||
normalizeName,
|
||||
removeCerts,
|
||||
setExpirationDate,
|
||||
};
|
||||
const forge = require('node-forge');
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
|
||||
forge.options.usePureJavaScript = true;
|
||||
|
||||
function getSerial() {
|
||||
return new Date().getTime().toString(); // Current time formatted in milliseconds since the epoch.
|
||||
}
|
||||
|
||||
function listCerts(type, basePath = 'pki/') {
|
||||
const certs = [];
|
||||
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
if (fs.existsSync(basePath)) {
|
||||
const files = fs.readdirSync(basePath);
|
||||
|
||||
_.forEach(files, (c, i) => {
|
||||
if(fs.lstatSync(basePath + c).isDirectory()){
|
||||
if (type !== 'ca') {
|
||||
if (type === 'users') {
|
||||
console.log('User certificates for ' + c + ':');
|
||||
} else {
|
||||
console.log('Server certificates for ' + c + ':');
|
||||
}
|
||||
const tempBase = basePath + c + '/' + type + '/';
|
||||
if (fs.existsSync(tempBase)) {
|
||||
const files = fs.readdirSync(tempBase);
|
||||
_.forEach(files, c => {
|
||||
certs.push(c);
|
||||
console.log('\t' + c);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (i === 0) {
|
||||
console.log('Available Certificate Authorities:');
|
||||
}
|
||||
certs.push(c);
|
||||
console.log('\t' + c);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
// Converts name to file path friendly name
|
||||
function normalizeName(name) {
|
||||
return name.replace(/\s+/g, '-').toLowerCase();
|
||||
}
|
||||
|
||||
function removeCerts(type, name, caName, basePath = 'pki/', callback = (err, data) => {}) {
|
||||
if (basePath.substr(-1) !== '/') basePath += '/';
|
||||
|
||||
const certDir = path.join(basePath, normalizeName(caName), type, normalizeName(name));
|
||||
|
||||
if (fs.existsSync(certDir)) {
|
||||
fs.removeSync(certDir);
|
||||
const success = {
|
||||
message: `${name} was removed from ${caName}.`,
|
||||
};
|
||||
console.log(success.message);
|
||||
callback(null, success);
|
||||
} else {
|
||||
const error = {
|
||||
message: `${name} does not exist for ${caName}.`,
|
||||
};
|
||||
console.error(error.message);
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
function setExpirationDate(cert, expired) {
|
||||
cert.validity.notBefore = new Date();
|
||||
cert.validity.notAfter = new Date();
|
||||
|
||||
// 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() - 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 1 year from today
|
||||
expired
|
||||
? cert.validity.notAfter.setDate(cert.validity.notAfter.getDate() - 1)
|
||||
: cert.validity.notAfter.setFullYear(cert.validity.notAfter.getFullYear() + 1);
|
||||
}
|
||||
|
||||
function setValidFrom(cert, date) {}
|
||||
|
||||
function setValidTo(cert, date) {}
|
||||
|
||||
function createMessageDigest() {
|
||||
return forge.md.sha384.create();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createMessageDigest,
|
||||
getSerial,
|
||||
listCerts,
|
||||
normalizeName,
|
||||
removeCerts,
|
||||
setExpirationDate,
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
const fs = require('fs-extra');
|
||||
|
||||
after('Remove PKI directory', function() {
|
||||
fs.removeSync(global.pkiPath);
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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)
|
||||
});
|
||||
});
|
||||
+325
-297
@@ -1,297 +1,325 @@
|
||||
const _ = require('lodash');
|
||||
const certs = require('..');
|
||||
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'));
|
||||
expect(created).to.be.true;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
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'));
|
||||
expect(created).to.be.true;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('List CA Certs', function() {
|
||||
certs.listCACerts(pkiPath);
|
||||
});
|
||||
|
||||
it('Remove CA Cert', function() {
|
||||
certs.createCACert('CA Cert Test Removal', { basePath: pkiPath });
|
||||
certs.removeCACert('CA Cert Test Removal', pkiPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Server Tests', function() {
|
||||
it('Create Server Cert including Root CA in bundle', function() {
|
||||
certs.createServerCert('Server', 'ca-cert-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
});
|
||||
|
||||
it('Create Server Cert excluding Root CA in bundle', function() {
|
||||
certs.createServerCert('Server 2', 'ca-cert-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
rootCaName: null,
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(pkiPath, 'ca-cert-2/servers/server-valid-from-and-to/crt.pem')
|
||||
);
|
||||
expect(created).to.be.true;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Server Cert with expiration before start should not be created', function(done) {
|
||||
try {
|
||||
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,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(pkiPath, 'ca-cert-2/servers/server-valid-to-before-from/crt.pem')
|
||||
);
|
||||
expect(created).to.be.false;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Expired Server Cert', function() {
|
||||
certs.createServerCert('Server 3', 'ca-cert-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: true,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
});
|
||||
|
||||
it('Create Expired Server Cert for existing server', function() {
|
||||
certs.createServerCert('Server 2', 'ca-cert-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: true,
|
||||
rootCaName: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('List Server Certs', function() {
|
||||
certs.listServerCerts(pkiPath);
|
||||
});
|
||||
|
||||
it('Remove Server Cert', function() {
|
||||
certs.createServerCert('Server Test Removal', 'ca-cert-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
certs.removeServerCert('Server Test Removal', 'ca-cert-2', pkiPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('User Tests', function() {
|
||||
const caName = 'User Tests CA';
|
||||
const caDir = 'user-tests-ca';
|
||||
|
||||
before(function(done) {
|
||||
certs.createCACert(caName, { basePath: pkiPath });
|
||||
done();
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
certs.removeCACert(caName, pkiPath);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Create User Cert', function(done) {
|
||||
certs.createUserCert('User 1', caName, { basePath: pkiPath, expired: false });
|
||||
const userCerts = certs.listUserCerts(pkiPath);
|
||||
expect(userCerts).to.contain('user-1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('Create User Cert with start and expiration dates', function(done) {
|
||||
try {
|
||||
certs.createUserCert('User Valid From and To', caName, {
|
||||
validFrom: '2050-01-01T00:00:00',
|
||||
validTo: '2051-01-01T00:00:00',
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(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 expiration before start should not be created', function(done) {
|
||||
try {
|
||||
certs.createUserCert('User Valid To before From', caName, {
|
||||
validFrom: '2051-01-01T00:00:00',
|
||||
validTo: '2050-01-01T00:00:00',
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(pkiPath, caDir, 'users/user-valid-to-before-from/crt.pem')
|
||||
);
|
||||
expect(created).to.be.false;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Expired User Cert', function(done) {
|
||||
certs.createUserCert('User 2', caName, { basePath: pkiPath, expired: true });
|
||||
const userCerts = certs.listUserCerts(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 });
|
||||
});
|
||||
|
||||
it('List User Certs', function(done) {
|
||||
const userCerts = certs.listUserCerts(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,
|
||||
expired: false,
|
||||
subjectAltEmailNames: ['[email protected]'],
|
||||
});
|
||||
const userCerts = certs.listUserCerts(pkiPath);
|
||||
expect(userCerts).to.contain('user-3');
|
||||
done();
|
||||
});
|
||||
|
||||
it('Remove User Cert', function(done) {
|
||||
certs.createUserCert('User Test Removal', caName, {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
let userCerts = certs.listUserCerts(pkiPath);
|
||||
expect(userCerts).to.contain('user-test-removal');
|
||||
certs.removeUserCert('User Test Removal', caName, pkiPath);
|
||||
userCerts = certs.listUserCerts(pkiPath);
|
||||
expect(userCerts).not.to.contain('user-test-removal');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('List Certs', function() {
|
||||
const originalTimeout = this.timeout;
|
||||
const newTimeout = originalTimeout * 4;
|
||||
|
||||
after(function() {
|
||||
this.timeout(originalTimeout);
|
||||
});
|
||||
|
||||
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.createServerCert('Server List', 'ca-cert-list-2', false, {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert List',
|
||||
});
|
||||
certs.createUserCert('User List 1', 'ca-cert-list-2', {
|
||||
basePath: pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
|
||||
const list = certs.listCerts(pkiPath);
|
||||
expect(list.ca).to.not.be.null;
|
||||
|
||||
const caCert = _.find(list.ca, { name: 'ca-cert-list' });
|
||||
expect(caCert).to.not.be.null;
|
||||
expect(caCert.servers).to.be.empty;
|
||||
expect(caCert.users).to.be.empty;
|
||||
|
||||
const caCert2 = _.find(list.ca, { name: 'ca-cert-list-2' });
|
||||
expect(caCert2).to.not.be.null;
|
||||
expect(caCert2.servers).to.not.be.empty;
|
||||
expect(caCert2.users).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('when none exist', function() {
|
||||
fs.removeSync(pkiPath);
|
||||
|
||||
const list = certs.listCerts(pkiPath);
|
||||
expect(list.ca).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cert Database Tests', function() {
|
||||
before('', function() {
|
||||
certs.createCACert('ca cert db', { basePath: pkiPath });
|
||||
});
|
||||
|
||||
it('Create Cert DB', function() {
|
||||
certs.createCertDatabase('ca cert db', { basePath: pkiPath });
|
||||
});
|
||||
});
|
||||
|
||||
it('Normalize Name', function() {
|
||||
const name = certs.normalizeName('Test Name 3');
|
||||
expect(name).to.not.include(' ');
|
||||
expect(name).to.not.include('T');
|
||||
expect(name).to.not.include('N');
|
||||
expect(name).to.include('-');
|
||||
expect(name).to.equal('test-name-3');
|
||||
});
|
||||
});
|
||||
const _ = require('lodash');
|
||||
const certs = require('..');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const expect = require('chai').expect;
|
||||
|
||||
describe('Testing MADCert', function() {
|
||||
describe('CA Tests', function() {
|
||||
it('Create CA Cert', function(done) {
|
||||
try {
|
||||
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) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create intermediate CA Cert', function(done) {
|
||||
try {
|
||||
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) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('List CA Certs', function() {
|
||||
certs.listCACerts(global.pkiPath);
|
||||
});
|
||||
|
||||
it('Remove CA Cert', function() {
|
||||
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: global.pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
});
|
||||
|
||||
it('Create Server Cert excluding Root CA in bundle', function() {
|
||||
certs.createServerCert('Server 2', 'ca-cert-2', false, {
|
||||
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: global.pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(global.pkiPath, 'ca-cert-2/servers/server-valid-from-and-to/crt.pem')
|
||||
);
|
||||
expect(created).to.be.true;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Server Cert with expiration before start should not be created', function(done) {
|
||||
try {
|
||||
certs.createServerCert('Server Valid To before From', 'ca-cert-2', false, {
|
||||
validFrom: '2051-01-01T00:00:00',
|
||||
validTo: '2050-01-01T00:00:00',
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(global.pkiPath, 'ca-cert-2/servers/server-valid-to-before-from/crt.pem')
|
||||
);
|
||||
expect(created).to.be.false;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Expired Server Cert', function() {
|
||||
certs.createServerCert('Server 3', 'ca-cert-2', false, {
|
||||
basePath: global.pkiPath,
|
||||
expired: true,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
});
|
||||
|
||||
it('Create Expired Server Cert for existing server', function() {
|
||||
certs.createServerCert('Server 2', 'ca-cert-2', false, {
|
||||
basePath: global.pkiPath,
|
||||
expired: true,
|
||||
rootCaName: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('List Server Certs', function() {
|
||||
certs.listServerCerts(global.pkiPath);
|
||||
});
|
||||
|
||||
it('Remove Server Cert', function() {
|
||||
certs.createServerCert('Server Test Removal', 'ca-cert-2', false, {
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert',
|
||||
});
|
||||
certs.removeServerCert('Server Test Removal', 'ca-cert-2', global.pkiPath);
|
||||
});
|
||||
});
|
||||
|
||||
describe('User Tests', function() {
|
||||
const caName = 'User Tests CA';
|
||||
const caDir = 'user-tests-ca';
|
||||
|
||||
before(function(done) {
|
||||
certs.createCACert(caName, { basePath: global.pkiPath });
|
||||
done();
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
certs.removeCACert(caName, global.pkiPath);
|
||||
done();
|
||||
});
|
||||
|
||||
it('Create User Cert', function(done) {
|
||||
certs.createUserCert('User 1', caName, { basePath: global.pkiPath, expired: false });
|
||||
const userCerts = certs.listUserCerts(global.pkiPath);
|
||||
expect(userCerts).to.contain('user-1');
|
||||
done();
|
||||
});
|
||||
|
||||
it('Create User Cert with start and expiration dates', function(done) {
|
||||
try {
|
||||
certs.createUserCert('User Valid From and To', caName, {
|
||||
validFrom: '2050-01-01T00:00:00',
|
||||
validTo: '2051-01-01T00:00:00',
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
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();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create User Cert with expiration before start should not be created', function(done) {
|
||||
try {
|
||||
certs.createUserCert('User Valid To before From', caName, {
|
||||
validFrom: '2051-01-01T00:00:00',
|
||||
validTo: '2050-01-01T00:00:00',
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
const created = fs.pathExistsSync(
|
||||
path.join(global.pkiPath, caDir, 'users/user-valid-to-before-from/crt.pem')
|
||||
);
|
||||
expect(created).to.be.false;
|
||||
done();
|
||||
} catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
it('Create Expired User Cert', function(done) {
|
||||
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: global.pkiPath, expired: true });
|
||||
});
|
||||
|
||||
it('List User Certs', function(done) {
|
||||
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: global.pkiPath,
|
||||
expired: false,
|
||||
subjectAltEmailNames: ['[email protected]'],
|
||||
});
|
||||
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: global.pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
let userCerts = certs.listUserCerts(global.pkiPath);
|
||||
expect(userCerts).to.contain('user-test-removal');
|
||||
certs.removeUserCert('User Test Removal', caName, global.pkiPath);
|
||||
userCerts = certs.listUserCerts(global.pkiPath);
|
||||
expect(userCerts).not.to.contain('user-test-removal');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('List Certs', function() {
|
||||
const originalTimeout = this.timeout;
|
||||
const newTimeout = originalTimeout * 4;
|
||||
|
||||
after(function() {
|
||||
this.timeout(originalTimeout);
|
||||
});
|
||||
|
||||
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: global.pkiPath });
|
||||
certs.createIntermediateCACert('CA Cert List 2', 'CA Cert List', { basePath: global.pkiPath });
|
||||
certs.createServerCert('Server List', 'ca-cert-list-2', false, {
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
rootCaName: 'CA Cert List',
|
||||
});
|
||||
certs.createUserCert('User List 1', 'ca-cert-list-2', {
|
||||
basePath: global.pkiPath,
|
||||
expired: false,
|
||||
});
|
||||
|
||||
const list = certs.listCerts(global.pkiPath);
|
||||
expect(list.ca).to.not.be.null;
|
||||
|
||||
const caCert = _.find(list.ca, { name: 'ca-cert-list' });
|
||||
expect(caCert).to.not.be.null;
|
||||
expect(caCert.servers).to.be.empty;
|
||||
expect(caCert.users).to.be.empty;
|
||||
|
||||
const caCert2 = _.find(list.ca, { name: 'ca-cert-list-2' });
|
||||
expect(caCert2).to.not.be.null;
|
||||
expect(caCert2.servers).to.not.be.empty;
|
||||
expect(caCert2.users).to.not.be.empty;
|
||||
});
|
||||
|
||||
it('when none exist', function() {
|
||||
fs.removeSync(global.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: global.pkiPath });
|
||||
});
|
||||
|
||||
it('Create Cert DB', function() {
|
||||
certs.createCertDatabase('ca cert db', { basePath: global.pkiPath });
|
||||
});
|
||||
});
|
||||
|
||||
it('Normalize Name', function() {
|
||||
const name = certs.normalizeName('Test Name 3');
|
||||
expect(name).to.not.include(' ');
|
||||
expect(name).to.not.include('T');
|
||||
expect(name).to.not.include('N');
|
||||
expect(name).to.include('-');
|
||||
expect(name).to.equal('test-name-3');
|
||||
});
|
||||
});
|
||||
@@ -1,873 +0,0 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/runtime-corejs3@^7.8.3":
|
||||
version "7.10.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.4.tgz#f29fc1990307c4c57b10dbd6ce667b27159d9e0d"
|
||||
integrity sha512-BFlgP2SoLO9HJX9WBwN67gHWMBhDX/eDz64Jajd6mR/UAUzqrNMm99d4qHnVaKscAElZoFiPv+JpR/Siud5lXw==
|
||||
dependencies:
|
||||
core-js-pure "^3.0.0"
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
|
||||
"@ungap/[email protected]":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
|
||||
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
|
||||
|
||||
[email protected]:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||
|
||||
ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||
|
||||
ansi-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
||||
|
||||
ansi-styles@^3.2.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.0.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
|
||||
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
|
||||
dependencies:
|
||||
"@types/color-name" "^1.1.1"
|
||||
color-convert "^2.0.1"
|
||||
|
||||
ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
anymatch@~3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
|
||||
integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
|
||||
|
||||
at-least-node@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
|
||||
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
[email protected]:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
|
||||
|
||||
camelcase@^5.0.0:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
camelcase@^6.0.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809"
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
chai@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
|
||||
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
|
||||
dependencies:
|
||||
assertion-error "^1.1.0"
|
||||
check-error "^1.0.2"
|
||||
deep-eql "^3.0.1"
|
||||
get-func-name "^2.0.0"
|
||||
pathval "^1.1.0"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
|
||||
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
check-error@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
|
||||
|
||||
[email protected]:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
|
||||
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
|
||||
dependencies:
|
||||
anymatch "~3.1.1"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.0"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.5.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.1.2"
|
||||
|
||||
cliui@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
|
||||
integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
|
||||
dependencies:
|
||||
string-width "^3.1.0"
|
||||
strip-ansi "^5.2.0"
|
||||
wrap-ansi "^5.1.0"
|
||||
|
||||
cliui@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
|
||||
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
[email protected]:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
|
||||
|
||||
color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
[email protected]:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
core-js-pure@^3.0.0:
|
||||
version "3.6.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
|
||||
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
|
||||
|
||||
[email protected]:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
|
||||
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
decamelize@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
decamelize@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-3.2.0.tgz#84b8e8f4f8c579f938e35e2cc7024907e0090851"
|
||||
integrity sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==
|
||||
dependencies:
|
||||
xregexp "^4.2.4"
|
||||
|
||||
decamelize@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
|
||||
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
|
||||
|
||||
deep-eql@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
|
||||
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
[email protected]:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
[email protected]:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
[email protected]:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
|
||||
integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
|
||||
dependencies:
|
||||
locate-path "^6.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
find-up@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||
integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
|
||||
dependencies:
|
||||
locate-path "^3.0.0"
|
||||
|
||||
find-up@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
flat@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241"
|
||||
integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==
|
||||
|
||||
fs-extra@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.1.tgz#910da0062437ba4c39fedd863f1675ccfefcb9fc"
|
||||
integrity sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==
|
||||
dependencies:
|
||||
at-least-node "^1.0.0"
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^1.0.0"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fsevents@~2.1.2:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
||||
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
||||
|
||||
get-caller-file@^2.0.1:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
|
||||
|
||||
glob-parent@~5.1.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
[email protected]:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
||||
version "4.2.4"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||
|
||||
[email protected]:
|
||||
version "1.10.5"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
[email protected]:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
|
||||
|
||||
is-fullwidth-code-point@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
|
||||
|
||||
is-fullwidth-code-point@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
||||
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
|
||||
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
is-plain-obj@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
|
||||
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
||||
|
||||
[email protected]:
|
||||
version "3.14.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.0.1.tgz#98966cba214378c8c84b82e085907b40bf614179"
|
||||
integrity sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==
|
||||
dependencies:
|
||||
universalify "^1.0.0"
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
locate-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||
integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
|
||||
dependencies:
|
||||
p-locate "^3.0.0"
|
||||
path-exists "^3.0.0"
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
locate-path@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||
integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
|
||||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
lodash@^4.17.11:
|
||||
version "4.17.19"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
|
||||
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
|
||||
|
||||
[email protected]:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
|
||||
integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
|
||||
[email protected], minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
mocha@^8.2.1:
|
||||
version "8.2.1"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39"
|
||||
integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==
|
||||
dependencies:
|
||||
"@ungap/promise-all-settled" "1.1.2"
|
||||
ansi-colors "4.1.1"
|
||||
browser-stdout "1.3.1"
|
||||
chokidar "3.4.3"
|
||||
debug "4.2.0"
|
||||
diff "4.0.2"
|
||||
escape-string-regexp "4.0.0"
|
||||
find-up "5.0.0"
|
||||
glob "7.1.6"
|
||||
growl "1.10.5"
|
||||
he "1.2.0"
|
||||
js-yaml "3.14.0"
|
||||
log-symbols "4.0.0"
|
||||
minimatch "3.0.4"
|
||||
ms "2.1.2"
|
||||
nanoid "3.1.12"
|
||||
serialize-javascript "5.0.1"
|
||||
strip-json-comments "3.1.1"
|
||||
supports-color "7.2.0"
|
||||
which "2.0.2"
|
||||
wide-align "1.1.3"
|
||||
workerpool "6.0.2"
|
||||
yargs "13.3.2"
|
||||
yargs-parser "13.1.2"
|
||||
yargs-unparser "2.0.0"
|
||||
|
||||
moment@^2.27.0:
|
||||
version "2.27.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
||||
|
||||
[email protected]:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
[email protected]:
|
||||
version "3.1.12"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
|
||||
integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
|
||||
|
||||
node-forge@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
|
||||
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
p-limit@^2.0.0, p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-limit@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
|
||||
dependencies:
|
||||
yocto-queue "^0.1.0"
|
||||
|
||||
p-locate@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
|
||||
integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
|
||||
dependencies:
|
||||
p-limit "^2.0.0"
|
||||
|
||||
p-locate@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||
dependencies:
|
||||
p-limit "^2.2.0"
|
||||
|
||||
p-locate@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
|
||||
integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
|
||||
dependencies:
|
||||
p-limit "^3.0.2"
|
||||
|
||||
p-try@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
path-exists@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
|
||||
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
pathval@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
|
||||
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
|
||||
randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
readdirp@~3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
|
||||
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
regenerator-runtime@^0.13.4:
|
||||
version "0.13.5"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
|
||||
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
require-main-filename@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
|
||||
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
|
||||
|
||||
safe-buffer@^5.1.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
[email protected]:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
|
||||
integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||
|
||||
"string-width@^1.0.2 || 2":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
|
||||
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
|
||||
dependencies:
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^4.0.0"
|
||||
|
||||
string-width@^3.0.0, string-width@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
|
||||
integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
|
||||
dependencies:
|
||||
emoji-regex "^7.0.1"
|
||||
is-fullwidth-code-point "^2.0.0"
|
||||
strip-ansi "^5.1.0"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
|
||||
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
strip-ansi@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
|
||||
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
|
||||
dependencies:
|
||||
ansi-regex "^3.0.0"
|
||||
|
||||
strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
|
||||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-ansi@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
|
||||
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.0"
|
||||
|
||||
[email protected]:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
[email protected], supports-color@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
universalify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-1.0.0.tgz#b61a1da173e8435b2fe3c67d29b9adf8594bd16d"
|
||||
integrity sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==
|
||||
|
||||
which-module@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
|
||||
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
|
||||
|
||||
[email protected]:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
[email protected]:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
|
||||
integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
|
||||
dependencies:
|
||||
string-width "^1.0.2 || 2"
|
||||
|
||||
[email protected]:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438"
|
||||
integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==
|
||||
|
||||
wrap-ansi@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
|
||||
integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.0"
|
||||
string-width "^3.0.0"
|
||||
strip-ansi "^5.0.0"
|
||||
|
||||
wrap-ansi@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
|
||||
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
xregexp@^4.2.4:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50"
|
||||
integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.8.3"
|
||||
|
||||
y18n@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
|
||||
integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
|
||||
|
||||
[email protected], yargs-parser@^13.1.2:
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
|
||||
integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||
dependencies:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
[email protected]:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
|
||||
integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
|
||||
dependencies:
|
||||
camelcase "^6.0.0"
|
||||
decamelize "^4.0.0"
|
||||
flat "^5.0.2"
|
||||
is-plain-obj "^2.1.0"
|
||||
|
||||
[email protected]:
|
||||
version "13.3.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
|
||||
integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
|
||||
dependencies:
|
||||
cliui "^5.0.0"
|
||||
find-up "^3.0.0"
|
||||
get-caller-file "^2.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^2.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^3.0.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^13.1.2"
|
||||
|
||||
yargs@^15.4.0:
|
||||
version "15.4.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.0.tgz#53949fb768309bac1843de9b17b80051e9805ec2"
|
||||
integrity sha512-D3fRFnZwLWp8jVAAhPZBsmeIHY8tTsb8ItV9KaAaopmC6wde2u6Yw29JBIZHXw14kgkRnYmDgmQU4FVMDlIsWw==
|
||||
dependencies:
|
||||
cliui "^6.0.0"
|
||||
decamelize "^3.2.0"
|
||||
find-up "^4.1.0"
|
||||
get-caller-file "^2.0.1"
|
||||
require-directory "^2.1.1"
|
||||
require-main-filename "^2.0.0"
|
||||
set-blocking "^2.0.0"
|
||||
string-width "^4.2.0"
|
||||
which-module "^2.0.0"
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^18.1.2"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
Reference in new issue
Block a user