self-sign 5.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of self-sign might be problematic. Click here for more details.
- package/.claude/settings.local.json +14 -0
- package/.github/workflows/pr-tests.yml +27 -0
- package/.jshintrc +39 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +97 -0
- package/LICENSE +22 -0
- package/README.md +474 -0
- package/examples/https-server-mkcert.js +66 -0
- package/examples/https-server.js +32 -0
- package/index.d.ts +276 -0
- package/index.js +583 -0
- package/package.json +36 -0
- package/pkcs7.js +70 -0
- package/test/ca-signing.js +242 -0
- package/test/ec-keys.js +142 -0
- package/test/tests.js +605 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: PR Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
strategy:
|
|
10
|
+
fail-fast: false
|
|
11
|
+
matrix:
|
|
12
|
+
node-version: [18.x, 20.x, 22.x, 24.x]
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout repository
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
18
|
+
uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: ${{ matrix.node-version }}
|
|
21
|
+
cache: npm
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: npm ci
|
|
25
|
+
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: npm test
|
package/.jshintrc
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"camelcase": false,
|
|
3
|
+
"curly": false,
|
|
4
|
+
|
|
5
|
+
"node": true,
|
|
6
|
+
"esnext": true,
|
|
7
|
+
"bitwise": true,
|
|
8
|
+
"eqeqeq": true,
|
|
9
|
+
"immed": true,
|
|
10
|
+
"indent": 2,
|
|
11
|
+
"latedef": true,
|
|
12
|
+
"newcap": true,
|
|
13
|
+
"noarg": true,
|
|
14
|
+
"regexp": true,
|
|
15
|
+
"undef": true,
|
|
16
|
+
"strict": false,
|
|
17
|
+
"smarttabs": true,
|
|
18
|
+
"expr": true,
|
|
19
|
+
|
|
20
|
+
"evil": true,
|
|
21
|
+
"browser": true,
|
|
22
|
+
"regexdash": true,
|
|
23
|
+
"wsh": true,
|
|
24
|
+
"trailing": true,
|
|
25
|
+
"sub": true,
|
|
26
|
+
"unused": true,
|
|
27
|
+
"laxcomma": true,
|
|
28
|
+
|
|
29
|
+
"globals": {
|
|
30
|
+
"after": false,
|
|
31
|
+
"before": false,
|
|
32
|
+
"afterEach": false,
|
|
33
|
+
"beforeEach": false,
|
|
34
|
+
"describe": false,
|
|
35
|
+
"it": false,
|
|
36
|
+
"DOMParser": true,
|
|
37
|
+
"XMLSerializer": true
|
|
38
|
+
}
|
|
39
|
+
}
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [5.0.0] - 2025-11-26
|
|
9
|
+
|
|
10
|
+
### 🚀 Major Rewrite
|
|
11
|
+
|
|
12
|
+
Complete rewrite replacing `node-forge` with modern `@peculiar/x509` and `pkijs` libraries.
|
|
13
|
+
|
|
14
|
+
### ✨ Added
|
|
15
|
+
|
|
16
|
+
- Native WebCrypto API support for better performance and security
|
|
17
|
+
- TypeScript examples in documentation
|
|
18
|
+
- Async/await support as the primary API
|
|
19
|
+
- Support for `keyPair` option to use existing keys
|
|
20
|
+
- Updated to use Node.js native crypto for all operations
|
|
21
|
+
- Separate `selfsign/pkcs7` module for tree-shakeable PKCS#7 support
|
|
22
|
+
|
|
23
|
+
### 💥 BREAKING CHANGES
|
|
24
|
+
|
|
25
|
+
1. **Async-only API**: The `generate()` function now returns a Promise. Synchronous generation has been removed.
|
|
26
|
+
```js
|
|
27
|
+
// Old (v4.x)
|
|
28
|
+
const pems = selfsign.generate(attrs, options);
|
|
29
|
+
|
|
30
|
+
// New (v5.x)
|
|
31
|
+
const pems = await selfsign.generate(attrs, options);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
2. **No callback support**: Callbacks have been completely removed in favor of Promises.
|
|
35
|
+
```js
|
|
36
|
+
// Old (v4.x)
|
|
37
|
+
selfsign.generate(attrs, options, function(err, pems) { ... });
|
|
38
|
+
|
|
39
|
+
// New (v5.x)
|
|
40
|
+
const pems = await selfsign.generate(attrs, options);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
3. **Minimum Node.js version**: Now requires Node.js >= 15.6.0 (was >= 10)
|
|
44
|
+
- Required for native WebCrypto support
|
|
45
|
+
|
|
46
|
+
4. **Dependencies changed**:
|
|
47
|
+
- ❌ Removed: `node-forge` (1.64 MB)
|
|
48
|
+
- ✅ Added: `@peculiar/x509` (551 KB) - 66% smaller!
|
|
49
|
+
- ✅ Added: `pkijs` (1.94 MB, only for PKCS#7 support)
|
|
50
|
+
- Bundle size reduced by 66% when not using PKCS#7
|
|
51
|
+
|
|
52
|
+
5. **PKCS#7 API changed**:
|
|
53
|
+
- Old: `const pems = await generate(attrs, { pkcs7: true }); pems.pkcs7`
|
|
54
|
+
- New: `const { createPkcs7 } = require('selfsign/pkcs7'); const pkcs7 = createPkcs7(pems.cert);`
|
|
55
|
+
- PKCS#7 is now a separate module for better tree-shaking
|
|
56
|
+
|
|
57
|
+
### 🔧 Changed
|
|
58
|
+
|
|
59
|
+
- Default key size remains 2048 bits (was incorrectly documented as 1024)
|
|
60
|
+
- PEM output uses `\n` line endings (was `\r\n`)
|
|
61
|
+
- Private keys now use PKCS#8 format (`BEGIN PRIVATE KEY` instead of `BEGIN RSA PRIVATE KEY`)
|
|
62
|
+
- Certificate generation is now fully async using native WebCrypto
|
|
63
|
+
- **PKCS#7 is now tree-shakeable**: Moved to separate `selfsign/pkcs7` module so bundlers can exclude it when not used
|
|
64
|
+
|
|
65
|
+
### 🐛 Fixed
|
|
66
|
+
|
|
67
|
+
- Default key size documentation corrected from 1024 to 2048 bits
|
|
68
|
+
- Improved error handling for certificate generation failures
|
|
69
|
+
|
|
70
|
+
### 📦 Dependencies
|
|
71
|
+
|
|
72
|
+
**Removed:**
|
|
73
|
+
- `node-forge@^1.3.1`
|
|
74
|
+
- `@types/node-forge@^1.3.0`
|
|
75
|
+
|
|
76
|
+
**Added:**
|
|
77
|
+
- `@peculiar/x509@^1.14.2` (required)
|
|
78
|
+
- `pkijs@^3.3.3` (required, but tree-shakeable via separate `selfsign/pkcs7` module)
|
|
79
|
+
|
|
80
|
+
### 🔒 Security
|
|
81
|
+
|
|
82
|
+
- Now uses Node.js native WebCrypto API instead of JavaScript implementation
|
|
83
|
+
- Better integration with platform security features
|
|
84
|
+
- More secure random number generation
|
|
85
|
+
|
|
86
|
+
### 📚 Documentation
|
|
87
|
+
|
|
88
|
+
- Complete README rewrite with async/await examples
|
|
89
|
+
- Added migration guide from v4.x to v5.x
|
|
90
|
+
- Updated all code examples to use async/await
|
|
91
|
+
- Added requirements section highlighting Node.js version requirement
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## [4.0.0] - Previous Release
|
|
96
|
+
|
|
97
|
+
See git history for changes in 4.x and earlier versions.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 José F. Romaniello
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# selfsign
|
|
2
|
+
|
|
3
|
+
Generate self-sign X.509 certificates using Node.js native crypto.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install selfsign
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- **Node.js >= 15.6.0** (for native WebCrypto support)
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
**Version 5.0 is async-only.** The `generate()` function now returns a Promise.
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const selfsign = require('self-sign');
|
|
21
|
+
|
|
22
|
+
const attrs = [{ name: 'commonName', value: 'contoso.com' }];
|
|
23
|
+
const pems = await selfsign.generate(attrs);
|
|
24
|
+
console.log(pems);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Output
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
{
|
|
31
|
+
private: '-----BEGIN PRIVATE KEY-----\n...',
|
|
32
|
+
public: '-----BEGIN PUBLIC KEY-----\n...',
|
|
33
|
+
cert: '-----BEGIN CERTIFICATE-----\n...',
|
|
34
|
+
fingerprint: 'XX:XX:XX:...'
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Options
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const pems = await selfsign.generate(null, {
|
|
42
|
+
keyType: 'rsa', // key type: 'rsa' or 'ec' (default: 'rsa')
|
|
43
|
+
keySize: 2048, // the size for the private key in bits (default: 2048, RSA only)
|
|
44
|
+
curve: 'P-256', // elliptic curve: 'P-256', 'P-384', or 'P-521' (default: 'P-256', EC only)
|
|
45
|
+
notBeforeDate: new Date(), // start of certificate validity (default: now)
|
|
46
|
+
notAfterDate: new Date('2026-01-01'), // end of certificate validity (default: notBeforeDate + 365 days)
|
|
47
|
+
algorithm: 'sha256', // sign the certificate with specified algorithm (default: 'sha1')
|
|
48
|
+
extensions: [{ name: 'basicConstraints', cA: true }], // certificate extensions array
|
|
49
|
+
clientCertificate: true, // generate client cert (default: false) - can also be an options object
|
|
50
|
+
ca: { key: '...', cert: '...' }, // CA key and cert for signing (default: self-sign)
|
|
51
|
+
passphrase: 'secret' // encrypt the private key with a passphrase (default: none)
|
|
52
|
+
},null);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Setting Custom Validity Period
|
|
56
|
+
|
|
57
|
+
Use `notBeforeDate` and `notAfterDate` to control certificate validity:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
// Using date-fns
|
|
61
|
+
const { addDays, addYears } = require('date-fns');
|
|
62
|
+
|
|
63
|
+
const pems = await selfsign.generate(null, {
|
|
64
|
+
notBeforeDate: new Date(),
|
|
65
|
+
notAfterDate: addDays(new Date(), 30) // Valid for 30 days
|
|
66
|
+
},null);
|
|
67
|
+
|
|
68
|
+
// Or with vanilla JS
|
|
69
|
+
const notBefore = new Date();
|
|
70
|
+
const notAfter = new Date(notBefore);
|
|
71
|
+
notAfter.setFullYear(notAfter.getFullYear() + 2); // Valid for 2 years
|
|
72
|
+
|
|
73
|
+
const pems = await selfsign.generate(null, {
|
|
74
|
+
notBeforeDate: notBefore,
|
|
75
|
+
notAfterDate: notAfter
|
|
76
|
+
},null);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Supported Algorithms
|
|
80
|
+
|
|
81
|
+
- `sha1` (default)
|
|
82
|
+
- `sha256`
|
|
83
|
+
- `sha384`
|
|
84
|
+
- `sha512`
|
|
85
|
+
|
|
86
|
+
### Custom Extensions
|
|
87
|
+
|
|
88
|
+
You can customize certificate extensions using the `extensions` option. This is useful for adding Subject Alternative Names (SANs) with IPv6 addresses, custom key usage, and more.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
const pems = await selfsign.generate(
|
|
92
|
+
[{ name: 'commonName', value: 'localhost' }],
|
|
93
|
+
{
|
|
94
|
+
extensions: [
|
|
95
|
+
{
|
|
96
|
+
name: 'basicConstraints',
|
|
97
|
+
cA: false
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'keyUsage',
|
|
101
|
+
digitalSignature: true,
|
|
102
|
+
keyEncipherment: true
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'subjectAltName',
|
|
106
|
+
altNames: [
|
|
107
|
+
{ type: 2, value: 'localhost' }, // DNS
|
|
108
|
+
{ type: 7, ip: '127.0.0.1' }, // IPv4
|
|
109
|
+
{ type: 7, ip: '::1' } // IPv6
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
},null
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Supported Extensions
|
|
118
|
+
|
|
119
|
+
**basicConstraints**
|
|
120
|
+
```js
|
|
121
|
+
{
|
|
122
|
+
name: 'basicConstraints',
|
|
123
|
+
cA: true, // is this a CA certificate?
|
|
124
|
+
pathLenConstraint: 0, // max depth of valid cert chain (optional)
|
|
125
|
+
critical: true // mark as critical extension
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**keyUsage**
|
|
130
|
+
```js
|
|
131
|
+
{
|
|
132
|
+
name: 'keyUsage',
|
|
133
|
+
digitalSignature: true,
|
|
134
|
+
nonRepudiation: true,
|
|
135
|
+
keyEncipherment: true,
|
|
136
|
+
dataEncipherment: true,
|
|
137
|
+
keyAgreement: true,
|
|
138
|
+
keyCertSign: true, // for CA certificates
|
|
139
|
+
cRLSign: true, // for CA certificates
|
|
140
|
+
encipherOnly: true,
|
|
141
|
+
decipherOnly: true,
|
|
142
|
+
critical: true
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**extKeyUsage** (Extended Key Usage)
|
|
147
|
+
```js
|
|
148
|
+
{
|
|
149
|
+
name: 'extKeyUsage',
|
|
150
|
+
serverAuth: true, // TLS server authentication
|
|
151
|
+
clientAuth: true, // TLS client authentication
|
|
152
|
+
codeSigning: true,
|
|
153
|
+
emailProtection: true,
|
|
154
|
+
timeStamping: true
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**subjectAltName** (Subject Alternative Name)
|
|
159
|
+
```js
|
|
160
|
+
{
|
|
161
|
+
name: 'subjectAltName',
|
|
162
|
+
altNames: [
|
|
163
|
+
{ type: 1, value: 'user@example.com' }, // email (rfc822Name)
|
|
164
|
+
{ type: 2, value: 'example.com' }, // DNS name
|
|
165
|
+
{ type: 2, value: '*.example.com' }, // wildcard DNS
|
|
166
|
+
{ type: 6, value: 'http://example.com/webid' }, // URI
|
|
167
|
+
{ type: 7, ip: '127.0.0.1' }, // IPv4 address
|
|
168
|
+
{ type: 7, ip: '::1' } // IPv6 address
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Default Extensions
|
|
174
|
+
|
|
175
|
+
When no `extensions` option is provided (or an empty array), the following defaults are used:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
[
|
|
179
|
+
{ name: 'basicConstraints', cA: false, critical: true },
|
|
180
|
+
{ name: 'keyUsage', digitalSignature: true, keyEncipherment: true, critical: true },
|
|
181
|
+
{ name: 'extKeyUsage', serverAuth: true, clientAuth: true },
|
|
182
|
+
{ name: 'subjectAltName', altNames: [
|
|
183
|
+
{ type: 2, value: commonName },
|
|
184
|
+
// For localhost, also includes: { type: 7, ip: '127.0.0.1' }
|
|
185
|
+
]}
|
|
186
|
+
]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Elliptic Curve (EC) Keys
|
|
190
|
+
|
|
191
|
+
By default, selfsign generates RSA keys. You can generate certificates using elliptic curve cryptography instead, which provides equivalent security with smaller key sizes and faster operations.
|
|
192
|
+
|
|
193
|
+
```js
|
|
194
|
+
// Generate EC certificate with P-256 curve (default)
|
|
195
|
+
const pems = await selfsign.generate(null, { keyType: 'ec' },null);
|
|
196
|
+
|
|
197
|
+
// Generate EC certificate with P-384 curve
|
|
198
|
+
const pems = await selfsign.generate(null, { keyType: 'ec', curve: 'P-384' },null);
|
|
199
|
+
|
|
200
|
+
// Generate EC certificate with P-521 curve and SHA-512
|
|
201
|
+
const pems = await selfsign.generate(null, {
|
|
202
|
+
keyType: 'ec',
|
|
203
|
+
curve: 'P-521',
|
|
204
|
+
algorithm: 'sha512'
|
|
205
|
+
},null);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Supported curves:**
|
|
209
|
+
- `P-256` (default) - 128-bit security, fastest
|
|
210
|
+
- `P-384` - 192-bit security
|
|
211
|
+
- `P-521` - 256-bit security, strongest
|
|
212
|
+
|
|
213
|
+
EC keys work with all other options including `clientCertificate`, `passphrase`, `ca`, and `keyPair`:
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
// EC certificate with encrypted private key
|
|
217
|
+
const pems = await selfsign.generate(null, {
|
|
218
|
+
keyType: 'ec',
|
|
219
|
+
passphrase: 'secret'
|
|
220
|
+
},null);
|
|
221
|
+
|
|
222
|
+
// EC certificate with client certificate
|
|
223
|
+
const pems = await selfsign.generate(null, {
|
|
224
|
+
keyType: 'ec',
|
|
225
|
+
clientCertificate: true
|
|
226
|
+
},null);
|
|
227
|
+
|
|
228
|
+
// Reuse existing EC key pair
|
|
229
|
+
const pems = await selfsign.generate(null, {
|
|
230
|
+
keyType: 'ec',
|
|
231
|
+
curve: 'P-256',
|
|
232
|
+
keyPair: {
|
|
233
|
+
publicKey: existingPublicKey,
|
|
234
|
+
privateKey: existingPrivateKey
|
|
235
|
+
}
|
|
236
|
+
},null);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Using Your Own Keys
|
|
240
|
+
|
|
241
|
+
You can avoid key pair generation by specifying your own keys:
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
const pems = await selfsign.generate(null, {
|
|
245
|
+
keyPair: {
|
|
246
|
+
publicKey: '-----BEGIN PUBLIC KEY-----...',
|
|
247
|
+
privateKey: '-----BEGIN PRIVATE KEY-----...'
|
|
248
|
+
}
|
|
249
|
+
},null);
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Encrypting the Private Key
|
|
253
|
+
|
|
254
|
+
You can encrypt the private key with a passphrase using AES-256-CBC:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const pems = await selfsign.generate(null, {
|
|
258
|
+
passphrase: 'my-secret-passphrase'
|
|
259
|
+
},null);
|
|
260
|
+
|
|
261
|
+
// The private key will be in encrypted PKCS#8 format:
|
|
262
|
+
// -----BEGIN ENCRYPTED PRIVATE KEY-----
|
|
263
|
+
// ...
|
|
264
|
+
// -----END ENCRYPTED PRIVATE KEY-----
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
To use the encrypted key, provide the passphrase:
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
const crypto = require('crypto');
|
|
271
|
+
|
|
272
|
+
// Decrypt the key
|
|
273
|
+
const privateKey = crypto.createPrivateKey({
|
|
274
|
+
key: pems.private,
|
|
275
|
+
passphrase: 'my-secret-passphrase'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Or use directly with HTTPS server
|
|
279
|
+
const https = require('https');
|
|
280
|
+
https.createServer({
|
|
281
|
+
key: pems.private,
|
|
282
|
+
passphrase: 'my-secret-passphrase',
|
|
283
|
+
cert: pems.cert
|
|
284
|
+
}, app).listen(443);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Signing with a CA
|
|
288
|
+
|
|
289
|
+
You can generate certificates signed by an existing Certificate Authority instead of self-sign certificates. This is useful for development environments where you want browsers to trust your certificates.
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
const fs = require('fs');
|
|
293
|
+
const selfsign = require('self-sign');
|
|
294
|
+
|
|
295
|
+
const pems = await selfsign.generate([
|
|
296
|
+
{ name: 'commonName', value: 'localhost' }
|
|
297
|
+
], {
|
|
298
|
+
algorithm: 'sha256',
|
|
299
|
+
ca: {
|
|
300
|
+
key: fs.readFileSync('/path/to/ca.key', 'utf8'),
|
|
301
|
+
cert: fs.readFileSync('/path/to/ca.crt', 'utf8')
|
|
302
|
+
}
|
|
303
|
+
},null);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The generated certificate will be signed by the provided CA and will include:
|
|
307
|
+
- Subject Alternative Name (SAN) extension with DNS name matching the commonName
|
|
308
|
+
- For `localhost`, an additional IP SAN for `127.0.0.1`
|
|
309
|
+
- Key Usage: digitalSignature, keyEncipherment
|
|
310
|
+
- Extended Key Usage: serverAuth, clientAuth
|
|
311
|
+
|
|
312
|
+
#### Using with mkcert
|
|
313
|
+
|
|
314
|
+
[mkcert](https://github.com/FiloSottile/mkcert) is a simple tool for making locally-trusted development certificates. Combining it with `selfsign` provides an excellent developer experience:
|
|
315
|
+
|
|
316
|
+
- **No certificate files to manage** - generate trusted certificates on-the-fly at server startup
|
|
317
|
+
- **No git-ignored cert files** - nothing to store, share, or accidentally commit
|
|
318
|
+
- **Browsers trust the certificates automatically** - no security warnings during development
|
|
319
|
+
|
|
320
|
+
```js
|
|
321
|
+
const https = require('https');
|
|
322
|
+
const fs = require('fs');
|
|
323
|
+
const path = require('path');
|
|
324
|
+
const { execSync } = require('child_process');
|
|
325
|
+
const selfsign = require('self-sign');
|
|
326
|
+
|
|
327
|
+
// Get mkcert's CA (requires: brew install mkcert && mkcert -install)
|
|
328
|
+
const caroot = execSync('mkcert -CAROOT', { encoding: 'utf8' }).trim();
|
|
329
|
+
|
|
330
|
+
const pems = await selfsign.generate([
|
|
331
|
+
{ name: 'commonName', value: 'localhost' }
|
|
332
|
+
], {
|
|
333
|
+
algorithm: 'sha256',
|
|
334
|
+
ca: {
|
|
335
|
+
key: fs.readFileSync(path.join(caroot, 'rootCA-key.pem'), 'utf8'),
|
|
336
|
+
cert: fs.readFileSync(path.join(caroot, 'rootCA.pem'), 'utf8')
|
|
337
|
+
}
|
|
338
|
+
},null);
|
|
339
|
+
|
|
340
|
+
// Start server with browser-trusted certificate - no files written to disk
|
|
341
|
+
https.createServer({ key: pems.private, cert: pems.cert }, app).listen(443);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
See [examples/https-server-mkcert.js](examples/https-server-mkcert.js) for a complete working example.
|
|
345
|
+
|
|
346
|
+
## Attributes
|
|
347
|
+
|
|
348
|
+
Attributes follow the X.509 standard:
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
const attrs = [
|
|
352
|
+
{ name: 'commonName', value: 'example.org' },
|
|
353
|
+
{ name: 'countryName', value: 'US' },
|
|
354
|
+
{ shortName: 'ST', value: 'Virginia' },
|
|
355
|
+
{ name: 'localityName', value: 'Blacksburg' },
|
|
356
|
+
{ name: 'organizationName', value: 'Test' },
|
|
357
|
+
{ shortName: 'OU', value: 'Test' }
|
|
358
|
+
];
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Generate Client Certificates
|
|
362
|
+
|
|
363
|
+
For environments where servers require client certificates, you can generate client keys signed by the original (server) key:
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
const pems = await selfsign.generate(null, { clientCertificate: true },null);
|
|
367
|
+
console.log(pems);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Output includes additional client certificate fields:
|
|
371
|
+
|
|
372
|
+
```js
|
|
373
|
+
{
|
|
374
|
+
private: '-----BEGIN PRIVATE KEY-----\n...',
|
|
375
|
+
public: '-----BEGIN PUBLIC KEY-----\n...',
|
|
376
|
+
cert: '-----BEGIN CERTIFICATE-----\n...',
|
|
377
|
+
fingerprint: 'XX:XX:XX:...',
|
|
378
|
+
clientprivate: '-----BEGIN PRIVATE KEY-----\n...',
|
|
379
|
+
clientpublic: '-----BEGIN PUBLIC KEY-----\n...',
|
|
380
|
+
clientcert: '-----BEGIN CERTIFICATE-----\n...'
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Client Certificate Options
|
|
385
|
+
|
|
386
|
+
The `clientCertificate` option can be `true` for defaults, or an options object for full control:
|
|
387
|
+
|
|
388
|
+
```js
|
|
389
|
+
const pems = await selfsign.generate(null, {
|
|
390
|
+
clientCertificate: {
|
|
391
|
+
cn: 'jdoe', // common name (default: 'John Doe jdoe123')
|
|
392
|
+
keyType: 'rsa', // key type: 'rsa' or 'ec' (default: inherits from parent)
|
|
393
|
+
keySize: 4096, // key size in bits (default: 2048, RSA only)
|
|
394
|
+
curve: 'P-256', // elliptic curve (default: 'P-256', EC only)
|
|
395
|
+
algorithm: 'sha256', // signature algorithm (default: inherits from parent or 'sha1')
|
|
396
|
+
notBeforeDate: new Date(), // validity start (default: now)
|
|
397
|
+
notAfterDate: new Date('2026-01-01') // validity end (default: notBeforeDate + 1 year)
|
|
398
|
+
}
|
|
399
|
+
},null);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Simple example with just a custom CN:
|
|
403
|
+
|
|
404
|
+
```js
|
|
405
|
+
const pems = await selfsign.generate(null, {
|
|
406
|
+
clientCertificate: { cn: 'FooBar' }
|
|
407
|
+
},null);
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## PKCS#7 Support
|
|
411
|
+
|
|
412
|
+
PKCS#7 formatting is available through a separate module for better tree-shaking:
|
|
413
|
+
|
|
414
|
+
```js
|
|
415
|
+
const selfsign = require('self-sign');
|
|
416
|
+
const { createPkcs7 } = require('self-sign/pkcs7');
|
|
417
|
+
|
|
418
|
+
const pems = await selfsign.generate(attrs);
|
|
419
|
+
const pkcs7 = createPkcs7(pems.cert);
|
|
420
|
+
console.log(pkcs7); // PKCS#7 formatted certificate
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
You can also create PKCS#7 for client certificates:
|
|
424
|
+
|
|
425
|
+
```js
|
|
426
|
+
const pems = await selfsign.generate(null, { clientCertificate: true },null);
|
|
427
|
+
const clientPkcs7 = createPkcs7(pems.clientcert);
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
## Migration from v4.x
|
|
431
|
+
|
|
432
|
+
Version 5.0 introduces breaking changes:
|
|
433
|
+
|
|
434
|
+
### Breaking Changes
|
|
435
|
+
|
|
436
|
+
1. **Async-only API**: The `generate()` function is now async and returns a Promise. Synchronous generation is no longer supported.
|
|
437
|
+
2. **No callback support**: Callbacks have been removed. Use `async`/`await` or `.then()`.
|
|
438
|
+
3. **Minimum Node.js version**: Now requires Node.js >= 15.6.0 (was >= 10).
|
|
439
|
+
4. **Dependencies**: Replaced `node-forge` with `@peculiar/x509` and `pkijs` (66% smaller bundle size).
|
|
440
|
+
5. **`days` option removed**: Use `notAfterDate` instead. Default validity is 365 days from `notBeforeDate`.
|
|
441
|
+
|
|
442
|
+
### Migration Examples
|
|
443
|
+
|
|
444
|
+
**Old (v4.x):**
|
|
445
|
+
```js
|
|
446
|
+
// Sync
|
|
447
|
+
const pems = selfsign.generate(attrs, { days: 365 });
|
|
448
|
+
|
|
449
|
+
// Callback
|
|
450
|
+
selfsign.generate(attrs, { days: 365 },null, function(err, pems) {
|
|
451
|
+
if (err) throw err;
|
|
452
|
+
console.log(pems);
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**New (v5.x):**
|
|
457
|
+
```js
|
|
458
|
+
// Async/await (default 365 days validity)
|
|
459
|
+
const pems = await selfsign.generate(attrs);
|
|
460
|
+
|
|
461
|
+
// Custom validity with notAfterDate
|
|
462
|
+
const notAfter = new Date();
|
|
463
|
+
notAfter.setDate(notAfter.getDate() + 30); // 30 days
|
|
464
|
+
const pems = await selfsign.generate(attrs, { notAfterDate: notAfter });
|
|
465
|
+
|
|
466
|
+
// Or with .then()
|
|
467
|
+
selfsign.generate(attrs)
|
|
468
|
+
.then(pems => console.log(pems))
|
|
469
|
+
.catch(err => console.error(err));
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## License
|
|
473
|
+
|
|
474
|
+
MIT
|