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,66 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const selfsign = require('../');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
// Get mkcert's CAROOT path
|
|
9
|
+
let caroot;
|
|
10
|
+
try {
|
|
11
|
+
caroot = execSync('mkcert -CAROOT', { encoding: 'utf8' }).trim();
|
|
12
|
+
} catch (err) {
|
|
13
|
+
console.error('Error: mkcert is not installed or not in PATH');
|
|
14
|
+
console.error('Install mkcert: https://github.com/FiloSottile/mkcert');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const caKeyPath = path.join(caroot, 'rootCA-key.pem');
|
|
19
|
+
const caCertPath = path.join(caroot, 'rootCA.pem');
|
|
20
|
+
|
|
21
|
+
// Check if CA files exist
|
|
22
|
+
if (!fs.existsSync(caKeyPath) || !fs.existsSync(caCertPath)) {
|
|
23
|
+
console.error('Error: mkcert CA files not found');
|
|
24
|
+
console.error('Run "mkcert -install" first to create the local CA');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log('Using mkcert CA from:', caroot);
|
|
29
|
+
|
|
30
|
+
// Read CA certificate and key
|
|
31
|
+
const caKey = fs.readFileSync(caKeyPath, 'utf8');
|
|
32
|
+
const caCert = fs.readFileSync(caCertPath, 'utf8');
|
|
33
|
+
|
|
34
|
+
// Generate a certificate signed by mkcert's CA
|
|
35
|
+
const pems = await selfsign.generate([
|
|
36
|
+
{ name: 'commonName', value: 'localhost' }
|
|
37
|
+
], {
|
|
38
|
+
days: 365,
|
|
39
|
+
keySize: 2048,
|
|
40
|
+
algorithm: 'sha256',
|
|
41
|
+
ca: {
|
|
42
|
+
key: caKey,
|
|
43
|
+
cert: caCert
|
|
44
|
+
}
|
|
45
|
+
},null);
|
|
46
|
+
|
|
47
|
+
// Create HTTPS server with the generated certificate
|
|
48
|
+
const server = https.createServer({
|
|
49
|
+
key: pems.private,
|
|
50
|
+
cert: pems.cert
|
|
51
|
+
}, (req, res) => {
|
|
52
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
53
|
+
res.end('Hello from HTTPS server with mkcert CA!\n');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const port = 3443;
|
|
57
|
+
server.listen(port, () => {
|
|
58
|
+
console.log(`HTTPS server running at https://localhost:${port}/`);
|
|
59
|
+
console.log('Certificate fingerprint:', pems.fingerprint);
|
|
60
|
+
console.log('\nSince this certificate is signed by mkcert\'s CA,');
|
|
61
|
+
console.log('your browser should trust it automatically (if mkcert -install was run).');
|
|
62
|
+
console.log('\nTest with: curl https://localhost:' + port);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const selfsign = require('../');
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
// Generate a self-signed certificate
|
|
6
|
+
const pems = await selfsign.generate([
|
|
7
|
+
{ name: 'commonName', value: 'localhost' }
|
|
8
|
+
], {
|
|
9
|
+
days: 365,
|
|
10
|
+
keySize: 2048,
|
|
11
|
+
algorithm: 'sha256'
|
|
12
|
+
},null);
|
|
13
|
+
|
|
14
|
+
// Create HTTPS server with the generated certificate
|
|
15
|
+
const server = https.createServer({
|
|
16
|
+
key: pems.private,
|
|
17
|
+
cert: pems.cert
|
|
18
|
+
}, (req, res) => {
|
|
19
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
20
|
+
res.end('Hello from self-signed HTTPS server!\n');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const port = 3443;
|
|
24
|
+
server.listen(port, () => {
|
|
25
|
+
console.log(`HTTPS server running at https://localhost:${port}/`);
|
|
26
|
+
console.log('Certificate fingerprint:', pems.fingerprint);
|
|
27
|
+
console.log('\nNote: Your browser will warn about the self-signed certificate.');
|
|
28
|
+
console.log('Test with: curl -k https://localhost:' + port);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
main().catch(console.error);
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
declare enum ASN1Class {
|
|
2
|
+
UNIVERSAL = 0x00,
|
|
3
|
+
APPLICATION = 0x40,
|
|
4
|
+
CONTEXT_SPECIFIC = 0x80,
|
|
5
|
+
PRIVATE = 0xc0,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface CertificateFieldOptions {
|
|
9
|
+
name?: string | undefined;
|
|
10
|
+
type?: string | undefined;
|
|
11
|
+
shortName?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface CertificateField extends CertificateFieldOptions {
|
|
15
|
+
valueConstructed?: boolean | undefined;
|
|
16
|
+
valueTagClass?: ASN1Class | undefined;
|
|
17
|
+
value?: any[] | string | undefined;
|
|
18
|
+
extensions?: any[] | undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Subject Alternative Name entry types:
|
|
23
|
+
* - 1: email (rfc822Name)
|
|
24
|
+
* - 2: DNS name
|
|
25
|
+
* - 6: URI
|
|
26
|
+
* - 7: IP address
|
|
27
|
+
*/
|
|
28
|
+
declare interface SubjectAltNameEntry {
|
|
29
|
+
/**
|
|
30
|
+
* Type of the alternative name:
|
|
31
|
+
* - 1: email (rfc822Name)
|
|
32
|
+
* - 2: DNS name
|
|
33
|
+
* - 6: URI
|
|
34
|
+
* - 7: IP address
|
|
35
|
+
*/
|
|
36
|
+
type: 1 | 2 | 6 | 7;
|
|
37
|
+
/** Value for types 1, 2, 6 (email, DNS, URI) */
|
|
38
|
+
value?: string;
|
|
39
|
+
/** IP address for type 7 (IPv4 or IPv6) */
|
|
40
|
+
ip?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare interface BasicConstraintsExtension {
|
|
44
|
+
name: 'basicConstraints';
|
|
45
|
+
/** Is this a CA certificate? */
|
|
46
|
+
cA?: boolean;
|
|
47
|
+
/** Maximum depth of valid certificate chain */
|
|
48
|
+
pathLenConstraint?: number;
|
|
49
|
+
/** Mark extension as critical */
|
|
50
|
+
critical?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare interface KeyUsageExtension {
|
|
54
|
+
name: 'keyUsage';
|
|
55
|
+
digitalSignature?: boolean;
|
|
56
|
+
nonRepudiation?: boolean;
|
|
57
|
+
/** Also known as contentCommitment */
|
|
58
|
+
contentCommitment?: boolean;
|
|
59
|
+
keyEncipherment?: boolean;
|
|
60
|
+
dataEncipherment?: boolean;
|
|
61
|
+
keyAgreement?: boolean;
|
|
62
|
+
/** For CA certificates */
|
|
63
|
+
keyCertSign?: boolean;
|
|
64
|
+
/** For CA certificates */
|
|
65
|
+
cRLSign?: boolean;
|
|
66
|
+
encipherOnly?: boolean;
|
|
67
|
+
decipherOnly?: boolean;
|
|
68
|
+
/** Mark extension as critical */
|
|
69
|
+
critical?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare interface ExtKeyUsageExtension {
|
|
73
|
+
name: 'extKeyUsage';
|
|
74
|
+
/** TLS server authentication */
|
|
75
|
+
serverAuth?: boolean;
|
|
76
|
+
/** TLS client authentication */
|
|
77
|
+
clientAuth?: boolean;
|
|
78
|
+
codeSigning?: boolean;
|
|
79
|
+
emailProtection?: boolean;
|
|
80
|
+
timeStamping?: boolean;
|
|
81
|
+
/** Mark extension as critical */
|
|
82
|
+
critical?: boolean;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare interface SubjectAltNameExtension {
|
|
86
|
+
name: 'subjectAltName';
|
|
87
|
+
altNames: SubjectAltNameEntry[];
|
|
88
|
+
/** Mark extension as critical */
|
|
89
|
+
critical?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
declare type CertificateExtension =
|
|
93
|
+
| BasicConstraintsExtension
|
|
94
|
+
| KeyUsageExtension
|
|
95
|
+
| ExtKeyUsageExtension
|
|
96
|
+
| SubjectAltNameExtension;
|
|
97
|
+
|
|
98
|
+
declare interface ClientCertificateOptions {
|
|
99
|
+
/**
|
|
100
|
+
* Key size for the client certificate in bits (RSA only)
|
|
101
|
+
* @default 2048
|
|
102
|
+
*/
|
|
103
|
+
keySize?: number
|
|
104
|
+
/**
|
|
105
|
+
* Key type for client certificate
|
|
106
|
+
* @default inherits from main keyType
|
|
107
|
+
*/
|
|
108
|
+
keyType?: 'rsa' | 'ec'
|
|
109
|
+
/**
|
|
110
|
+
* Elliptic curve for client certificate (EC only)
|
|
111
|
+
* @default "P-256"
|
|
112
|
+
*/
|
|
113
|
+
curve?: 'P-256' | 'P-384' | 'P-521'
|
|
114
|
+
/**
|
|
115
|
+
* Signature algorithm for client certificate
|
|
116
|
+
* @default inherits from main algorithm or "sha1"
|
|
117
|
+
*/
|
|
118
|
+
algorithm?: string
|
|
119
|
+
/**
|
|
120
|
+
* Client certificate's common name
|
|
121
|
+
* @default "John Doe jdoe123"
|
|
122
|
+
*/
|
|
123
|
+
cn?: string
|
|
124
|
+
/**
|
|
125
|
+
* The date before which the client certificate should not be valid
|
|
126
|
+
* @default now
|
|
127
|
+
*/
|
|
128
|
+
notBeforeDate?: Date
|
|
129
|
+
/**
|
|
130
|
+
* The date after which the client certificate should not be valid
|
|
131
|
+
* @default notBeforeDate + 1 year
|
|
132
|
+
*/
|
|
133
|
+
notAfterDate?: Date
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
declare interface SelfsignedOptions {
|
|
137
|
+
/**
|
|
138
|
+
* The date before which the certificate should not be valid
|
|
139
|
+
*
|
|
140
|
+
* @default now */
|
|
141
|
+
notBeforeDate?: Date
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The date after which the certificate should not be valid
|
|
145
|
+
*
|
|
146
|
+
* @default notBeforeDate + 365 days */
|
|
147
|
+
notAfterDate?: Date
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Key type: "rsa" or "ec" (elliptic curve)
|
|
151
|
+
* @default "rsa"
|
|
152
|
+
*/
|
|
153
|
+
keyType?: 'rsa' | 'ec'
|
|
154
|
+
/**
|
|
155
|
+
* the size for the private key in bits (RSA only)
|
|
156
|
+
* @default 2048
|
|
157
|
+
*/
|
|
158
|
+
keySize?: number
|
|
159
|
+
/**
|
|
160
|
+
* The elliptic curve to use (EC only): "P-256", "P-384", or "P-521"
|
|
161
|
+
* @default "P-256"
|
|
162
|
+
*/
|
|
163
|
+
curve?: 'P-256' | 'P-384' | 'P-521'
|
|
164
|
+
/**
|
|
165
|
+
* Certificate extensions. Supports basicConstraints, keyUsage, extKeyUsage, and subjectAltName.
|
|
166
|
+
* If not provided, defaults are used including DNS SAN matching commonName.
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* extensions: [
|
|
170
|
+
* { name: 'basicConstraints', cA: false },
|
|
171
|
+
* { name: 'keyUsage', digitalSignature: true, keyEncipherment: true },
|
|
172
|
+
* { name: 'subjectAltName', altNames: [
|
|
173
|
+
* { type: 2, value: 'localhost' },
|
|
174
|
+
* { type: 7, ip: '127.0.0.1' },
|
|
175
|
+
* { type: 7, ip: '::1' }
|
|
176
|
+
* ]}
|
|
177
|
+
* ]
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
extensions?: CertificateExtension[];
|
|
181
|
+
/**
|
|
182
|
+
* The signature algorithm: sha256, sha384, sha512 or sha1
|
|
183
|
+
* @default "sha1"
|
|
184
|
+
*/
|
|
185
|
+
algorithm?: string
|
|
186
|
+
/**
|
|
187
|
+
* include PKCS#7 as part of the output
|
|
188
|
+
* @default false
|
|
189
|
+
*/
|
|
190
|
+
pkcs7?: boolean
|
|
191
|
+
/**
|
|
192
|
+
* generate client cert signed by the original key
|
|
193
|
+
* Can be `true` for defaults or an options object
|
|
194
|
+
* @default false
|
|
195
|
+
*/
|
|
196
|
+
clientCertificate?: boolean | ClientCertificateOptions
|
|
197
|
+
/**
|
|
198
|
+
* client certificate's common name
|
|
199
|
+
* @default "John Doe jdoe123"
|
|
200
|
+
* @deprecated Use clientCertificate.cn instead
|
|
201
|
+
*/
|
|
202
|
+
clientCertificateCN?: string
|
|
203
|
+
/**
|
|
204
|
+
* the size for the client private key in bits
|
|
205
|
+
* @default 2048
|
|
206
|
+
* @deprecated Use clientCertificate.keySize instead
|
|
207
|
+
*/
|
|
208
|
+
clientCertificateKeySize?: number
|
|
209
|
+
/**
|
|
210
|
+
* existing key pair to use instead of generating new keys
|
|
211
|
+
*/
|
|
212
|
+
keyPair?: {
|
|
213
|
+
privateKey: string
|
|
214
|
+
publicKey: string
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* CA certificate and key for signing (if not provided, generates self-signed)
|
|
218
|
+
*/
|
|
219
|
+
ca?: {
|
|
220
|
+
/** CA private key in PEM format */
|
|
221
|
+
key: string
|
|
222
|
+
/** CA certificate in PEM format */
|
|
223
|
+
cert: string
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Passphrase to encrypt the private key (PKCS#8 encrypted format)
|
|
227
|
+
* When provided, the private key will be encrypted using AES-256-CBC
|
|
228
|
+
*/
|
|
229
|
+
passphrase?: string
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
declare interface GenerateResult {
|
|
233
|
+
private: string
|
|
234
|
+
public: string
|
|
235
|
+
cert: string
|
|
236
|
+
fingerprint: string
|
|
237
|
+
pkcs7?: string
|
|
238
|
+
clientprivate?: string
|
|
239
|
+
clientpublic?: string
|
|
240
|
+
clientcert?: string
|
|
241
|
+
clientpkcs7?: string
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Generate a certificate (async only)
|
|
246
|
+
*
|
|
247
|
+
* @param attrs Certificate attributes
|
|
248
|
+
* @param opts Generation options
|
|
249
|
+
* @returns Promise that resolves with certificate data
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```typescript
|
|
253
|
+
* // Self-signed certificate
|
|
254
|
+
* const pems = await generate();
|
|
255
|
+
*
|
|
256
|
+
* const pems = await generate([{ name: 'commonName', value: 'example.com' }]);
|
|
257
|
+
*
|
|
258
|
+
* const pems = await generate(null, {
|
|
259
|
+
* keySize: 2048,
|
|
260
|
+
* algorithm: 'sha256'
|
|
261
|
+
* });
|
|
262
|
+
*
|
|
263
|
+
* // CA-signed certificate
|
|
264
|
+
* const pems = await generate([{ name: 'commonName', value: 'localhost' }], {
|
|
265
|
+
* algorithm: 'sha256',
|
|
266
|
+
* ca: {
|
|
267
|
+
* key: fs.readFileSync('/path/to/ca.key', 'utf8'),
|
|
268
|
+
* cert: fs.readFileSync('/path/to/ca.crt', 'utf8')
|
|
269
|
+
* }
|
|
270
|
+
* });
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
export declare function generate(
|
|
274
|
+
attrs?: CertificateField[],
|
|
275
|
+
opts?: SelfsignedOptions
|
|
276
|
+
): Promise<GenerateResult>
|