maven-proxy 1.0.1 → 1.0.2

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.
@@ -1,194 +1,194 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import forge from "node-forge";
4
-
5
- const CERT_VALID_YEARS = 30;
6
-
7
- function randomSerial() {
8
- return forge.util.bytesToHex(forge.random.getBytesSync(9));
9
- }
10
-
11
- function sanitizeHost(hostname) {
12
- return hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
13
- }
14
-
15
- async function ensureDir(dirPath) {
16
- await fs.promises.mkdir(dirPath, { recursive: true });
17
- }
18
-
19
- async function readIfExists(filePath) {
20
- try {
21
- return await fs.promises.readFile(filePath, "utf8");
22
- } catch (error) {
23
- if (error.code === "ENOENT") {
24
- return null;
25
- }
26
- throw error;
27
- }
28
- }
29
-
30
- function createRootCertificate() {
31
- const keys = forge.pki.rsa.generateKeyPair(2048);
32
- const cert = forge.pki.createCertificate();
33
-
34
- cert.publicKey = keys.publicKey;
35
- cert.serialNumber = randomSerial();
36
-
37
- const now = new Date();
38
- const rootNotAfter = new Date(now);
39
- rootNotAfter.setFullYear(rootNotAfter.getFullYear() + CERT_VALID_YEARS);
40
- cert.validity.notBefore = new Date(now.getTime() - 24 * 60 * 60 * 1000);
41
- cert.validity.notAfter = rootNotAfter;
42
-
43
- const attrs = [
44
- { name: "commonName", value: "Maven Proxy Root CA" },
45
- { name: "organizationName", value: "maven-proxy" },
46
- { shortName: "OU", value: "Development" },
47
- ];
48
-
49
- cert.setSubject(attrs);
50
- cert.setIssuer(attrs);
51
- cert.setExtensions([
52
- { name: "basicConstraints", cA: true, critical: true },
53
- {
54
- name: "keyUsage",
55
- keyCertSign: true,
56
- cRLSign: true,
57
- digitalSignature: true,
58
- critical: true,
59
- },
60
- { name: "subjectKeyIdentifier" },
61
- ]);
62
-
63
- cert.sign(keys.privateKey, forge.md.sha256.create());
64
-
65
- return {
66
- keyPem: forge.pki.privateKeyToPem(keys.privateKey),
67
- certPem: forge.pki.certificateToPem(cert),
68
- privateKey: keys.privateKey,
69
- certificate: cert,
70
- };
71
- }
72
-
73
- function createLeafCertificate(hostname, rootPrivateKey, rootCertificate) {
74
- const keys = forge.pki.rsa.generateKeyPair(2048);
75
- const cert = forge.pki.createCertificate();
76
-
77
- cert.publicKey = keys.publicKey;
78
- cert.serialNumber = randomSerial();
79
-
80
- const now = new Date();
81
- const leafNotAfter = new Date(now);
82
- leafNotAfter.setFullYear(leafNotAfter.getFullYear() + CERT_VALID_YEARS);
83
- cert.validity.notBefore = new Date(now.getTime() - 60 * 60 * 1000);
84
- cert.validity.notAfter = leafNotAfter;
85
-
86
- cert.setSubject([
87
- { name: "commonName", value: hostname },
88
- { name: "organizationName", value: "maven-proxy-leaf" },
89
- ]);
90
-
91
- cert.setIssuer(rootCertificate.subject.attributes);
92
- cert.setExtensions([
93
- { name: "basicConstraints", cA: false, critical: true },
94
- {
95
- name: "keyUsage",
96
- digitalSignature: true,
97
- keyEncipherment: true,
98
- dataEncipherment: true,
99
- critical: true,
100
- },
101
- { name: "extKeyUsage", serverAuth: true },
102
- {
103
- name: "subjectAltName",
104
- altNames: [{ type: 2, value: hostname }],
105
- },
106
- { name: "subjectKeyIdentifier" },
107
- ]);
108
-
109
- cert.sign(rootPrivateKey, forge.md.sha256.create());
110
-
111
- return {
112
- keyPem: forge.pki.privateKeyToPem(keys.privateKey),
113
- certPem: forge.pki.certificateToPem(cert),
114
- };
115
- }
116
-
117
- export class CertManager {
118
- constructor(config) {
119
- this.config = config;
120
- this.rootPrivateKey = null;
121
- this.rootCertificate = null;
122
- this.leafCache = new Map();
123
- this.leafPromiseCache = new Map();
124
- }
125
-
126
- async init() {
127
- await ensureDir(path.dirname(this.config.rootCertPath));
128
- await ensureDir(path.dirname(this.config.rootKeyPath));
129
- await ensureDir(this.config.leafCertDir);
130
-
131
- const certPem = await readIfExists(this.config.rootCertPath);
132
- const keyPem = await readIfExists(this.config.rootKeyPath);
133
-
134
- if (certPem && keyPem) {
135
- this.rootCertificate = forge.pki.certificateFromPem(certPem);
136
- this.rootPrivateKey = forge.pki.privateKeyFromPem(keyPem);
137
- return;
138
- }
139
-
140
- const root = createRootCertificate();
141
- this.rootCertificate = root.certificate;
142
- this.rootPrivateKey = root.privateKey;
143
-
144
- await fs.promises.writeFile(this.config.rootCertPath, root.certPem, "utf8");
145
- await fs.promises.writeFile(this.config.rootKeyPath, root.keyPem, "utf8");
146
- }
147
-
148
- async getRootCertPem() {
149
- return fs.promises.readFile(this.config.rootCertPath, "utf8");
150
- }
151
-
152
- async getOrCreateLeaf(hostname) {
153
- const normalizedHost = String(hostname).toLowerCase();
154
-
155
- if (this.leafCache.has(normalizedHost)) {
156
- return this.leafCache.get(normalizedHost);
157
- }
158
-
159
- if (this.leafPromiseCache.has(normalizedHost)) {
160
- return this.leafPromiseCache.get(normalizedHost);
161
- }
162
-
163
- const promise = this.#loadOrCreateLeaf(normalizedHost)
164
- .then((leaf) => {
165
- this.leafCache.set(normalizedHost, leaf);
166
- return leaf;
167
- })
168
- .finally(() => {
169
- this.leafPromiseCache.delete(normalizedHost);
170
- });
171
-
172
- this.leafPromiseCache.set(normalizedHost, promise);
173
- return promise;
174
- }
175
-
176
- async #loadOrCreateLeaf(hostname) {
177
- const safeHost = sanitizeHost(hostname);
178
- const certPath = path.join(this.config.leafCertDir, `${safeHost}.crt`);
179
- const keyPath = path.join(this.config.leafCertDir, `${safeHost}.key.pem`);
180
-
181
- const certPem = await readIfExists(certPath);
182
- const keyPem = await readIfExists(keyPath);
183
-
184
- if (certPem && keyPem) {
185
- return { certPem, keyPem };
186
- }
187
-
188
- const leaf = createLeafCertificate(hostname, this.rootPrivateKey, this.rootCertificate);
189
- await fs.promises.writeFile(certPath, leaf.certPem, "utf8");
190
- await fs.promises.writeFile(keyPath, leaf.keyPem, "utf8");
191
-
192
- return leaf;
193
- }
194
- }
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import forge from "node-forge";
4
+
5
+ const CERT_VALID_YEARS = 30;
6
+
7
+ function randomSerial() {
8
+ return forge.util.bytesToHex(forge.random.getBytesSync(9));
9
+ }
10
+
11
+ function sanitizeHost(hostname) {
12
+ return hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
13
+ }
14
+
15
+ async function ensureDir(dirPath) {
16
+ await fs.promises.mkdir(dirPath, { recursive: true });
17
+ }
18
+
19
+ async function readIfExists(filePath) {
20
+ try {
21
+ return await fs.promises.readFile(filePath, "utf8");
22
+ } catch (error) {
23
+ if (error.code === "ENOENT") {
24
+ return null;
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+
30
+ function createRootCertificate() {
31
+ const keys = forge.pki.rsa.generateKeyPair(2048);
32
+ const cert = forge.pki.createCertificate();
33
+
34
+ cert.publicKey = keys.publicKey;
35
+ cert.serialNumber = randomSerial();
36
+
37
+ const now = new Date();
38
+ const rootNotAfter = new Date(now);
39
+ rootNotAfter.setFullYear(rootNotAfter.getFullYear() + CERT_VALID_YEARS);
40
+ cert.validity.notBefore = new Date(now.getTime() - 24 * 60 * 60 * 1000);
41
+ cert.validity.notAfter = rootNotAfter;
42
+
43
+ const attrs = [
44
+ { name: "commonName", value: "Maven Proxy Root CA" },
45
+ { name: "organizationName", value: "maven-proxy" },
46
+ { shortName: "OU", value: "Development" },
47
+ ];
48
+
49
+ cert.setSubject(attrs);
50
+ cert.setIssuer(attrs);
51
+ cert.setExtensions([
52
+ { name: "basicConstraints", cA: true, critical: true },
53
+ {
54
+ name: "keyUsage",
55
+ keyCertSign: true,
56
+ cRLSign: true,
57
+ digitalSignature: true,
58
+ critical: true,
59
+ },
60
+ { name: "subjectKeyIdentifier" },
61
+ ]);
62
+
63
+ cert.sign(keys.privateKey, forge.md.sha256.create());
64
+
65
+ return {
66
+ keyPem: forge.pki.privateKeyToPem(keys.privateKey),
67
+ certPem: forge.pki.certificateToPem(cert),
68
+ privateKey: keys.privateKey,
69
+ certificate: cert,
70
+ };
71
+ }
72
+
73
+ function createLeafCertificate(hostname, rootPrivateKey, rootCertificate) {
74
+ const keys = forge.pki.rsa.generateKeyPair(2048);
75
+ const cert = forge.pki.createCertificate();
76
+
77
+ cert.publicKey = keys.publicKey;
78
+ cert.serialNumber = randomSerial();
79
+
80
+ const now = new Date();
81
+ const leafNotAfter = new Date(now);
82
+ leafNotAfter.setFullYear(leafNotAfter.getFullYear() + CERT_VALID_YEARS);
83
+ cert.validity.notBefore = new Date(now.getTime() - 60 * 60 * 1000);
84
+ cert.validity.notAfter = leafNotAfter;
85
+
86
+ cert.setSubject([
87
+ { name: "commonName", value: hostname },
88
+ { name: "organizationName", value: "maven-proxy-leaf" },
89
+ ]);
90
+
91
+ cert.setIssuer(rootCertificate.subject.attributes);
92
+ cert.setExtensions([
93
+ { name: "basicConstraints", cA: false, critical: true },
94
+ {
95
+ name: "keyUsage",
96
+ digitalSignature: true,
97
+ keyEncipherment: true,
98
+ dataEncipherment: true,
99
+ critical: true,
100
+ },
101
+ { name: "extKeyUsage", serverAuth: true },
102
+ {
103
+ name: "subjectAltName",
104
+ altNames: [{ type: 2, value: hostname }],
105
+ },
106
+ { name: "subjectKeyIdentifier" },
107
+ ]);
108
+
109
+ cert.sign(rootPrivateKey, forge.md.sha256.create());
110
+
111
+ return {
112
+ keyPem: forge.pki.privateKeyToPem(keys.privateKey),
113
+ certPem: forge.pki.certificateToPem(cert),
114
+ };
115
+ }
116
+
117
+ export class CertManager {
118
+ constructor(config) {
119
+ this.config = config;
120
+ this.rootPrivateKey = null;
121
+ this.rootCertificate = null;
122
+ this.leafCache = new Map();
123
+ this.leafPromiseCache = new Map();
124
+ }
125
+
126
+ async init() {
127
+ await ensureDir(path.dirname(this.config.rootCertPath));
128
+ await ensureDir(path.dirname(this.config.rootKeyPath));
129
+ await ensureDir(this.config.leafCertDir);
130
+
131
+ const certPem = await readIfExists(this.config.rootCertPath);
132
+ const keyPem = await readIfExists(this.config.rootKeyPath);
133
+
134
+ if (certPem && keyPem) {
135
+ this.rootCertificate = forge.pki.certificateFromPem(certPem);
136
+ this.rootPrivateKey = forge.pki.privateKeyFromPem(keyPem);
137
+ return;
138
+ }
139
+
140
+ const root = createRootCertificate();
141
+ this.rootCertificate = root.certificate;
142
+ this.rootPrivateKey = root.privateKey;
143
+
144
+ await fs.promises.writeFile(this.config.rootCertPath, root.certPem, "utf8");
145
+ await fs.promises.writeFile(this.config.rootKeyPath, root.keyPem, "utf8");
146
+ }
147
+
148
+ async getRootCertPem() {
149
+ return fs.promises.readFile(this.config.rootCertPath, "utf8");
150
+ }
151
+
152
+ async getOrCreateLeaf(hostname) {
153
+ const normalizedHost = String(hostname).toLowerCase();
154
+
155
+ if (this.leafCache.has(normalizedHost)) {
156
+ return this.leafCache.get(normalizedHost);
157
+ }
158
+
159
+ if (this.leafPromiseCache.has(normalizedHost)) {
160
+ return this.leafPromiseCache.get(normalizedHost);
161
+ }
162
+
163
+ const promise = this.#loadOrCreateLeaf(normalizedHost)
164
+ .then((leaf) => {
165
+ this.leafCache.set(normalizedHost, leaf);
166
+ return leaf;
167
+ })
168
+ .finally(() => {
169
+ this.leafPromiseCache.delete(normalizedHost);
170
+ });
171
+
172
+ this.leafPromiseCache.set(normalizedHost, promise);
173
+ return promise;
174
+ }
175
+
176
+ async #loadOrCreateLeaf(hostname) {
177
+ const safeHost = sanitizeHost(hostname);
178
+ const certPath = path.join(this.config.leafCertDir, `${safeHost}.crt`);
179
+ const keyPath = path.join(this.config.leafCertDir, `${safeHost}.key.pem`);
180
+
181
+ const certPem = await readIfExists(certPath);
182
+ const keyPem = await readIfExists(keyPath);
183
+
184
+ if (certPem && keyPem) {
185
+ return { certPem, keyPem };
186
+ }
187
+
188
+ const leaf = createLeafCertificate(hostname, this.rootPrivateKey, this.rootCertificate);
189
+ await fs.promises.writeFile(certPath, leaf.certPem, "utf8");
190
+ await fs.promises.writeFile(keyPath, leaf.keyPem, "utf8");
191
+
192
+ return leaf;
193
+ }
194
+ }