-
Notifications
You must be signed in to change notification settings - Fork 6
/
CertificateMaker.js
130 lines (100 loc) · 3.7 KB
/
CertificateMaker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const fs = require("fs")
const path = require("path")
const ACME = require("acme-client")
ACME.setLogger(message => {
console.log(message)
})
class CertificateMaker {
constructor(app) {
this.app = app
this.challengeTokens = {}
// Set up the HTTP challenge handler
this.setupChallengeHandler()
}
setupChallengeHandler() {
this.app.get("/.well-known/acme-challenge/:token", (req, res) => {
const token = req.params.token
const keyAuthorization = this.challengeTokens[token]
if (keyAuthorization) {
res.set("Content-Type", "text/plain")
res.send(keyAuthorization)
} else {
res.status(404).send("Not Found")
}
})
return this
}
log(domain, message) {
console.log(`Make cert for: ${domain}. ${message}`)
}
async makeCertificate(domain, email, directory) {
try {
// Create account and domain private keys
this.log(domain, "Creating private keys")
const [accountKey, domainPrivateKey] = await Promise.all([ACME.crypto.createPrivateKey(), ACME.crypto.createPrivateKey()])
this.log(domain, "Private keys created")
// Create CSR
const [certificateKey, certificateRequest] = await ACME.crypto.createCsr(
{
altNames: [domain]
},
domainPrivateKey
)
this.log(domain, "CSR created")
// Initialize ACME client
const client = new ACME.Client({
directoryUrl: ACME.directory.letsencrypt.production, // ACME.directory.letsencrypt.staging, //
accountKey
})
this.log(domain, "client created")
// Create a new account
await client.createAccount({
termsOfServiceAgreed: true,
contact: [`mailto:${email}`]
})
this.log(domain, "account created")
// Create a new order
const order = await client.createOrder({
identifiers: [{ type: "dns", value: domain }]
})
this.log(domain, "order created")
// Get authorizations
const authorizations = await client.getAuthorizations(order)
this.log(domain, "got auths")
// Handle challenges
for (const authz of authorizations) {
const challenge = authz.challenges.find(c => c.type === "http-01")
// Get key authorization
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge)
this.log(domain, "got key")
// Store the key authorization for the token
this.challengeTokens[challenge.token] = keyAuthorization
// Notify ACME server that the challenge is ready
await client.verifyChallenge(authz, challenge)
this.log(domain, "challenge verified")
await client.completeChallenge(challenge)
this.log(domain, "challenge completed")
// Wait for the challenge to be validated
await client.waitForValidStatus(challenge)
this.log(domain, "challenge valid")
// Remove the token after validation
delete this.challengeTokens[challenge.token]
}
// Finalize the order
await client.finalizeOrder(order, certificateRequest)
this.log(domain, "order finalized")
// Get the certificate
const certificate = await client.getCertificate(order)
this.log(domain, "got cert")
// Ensure the directory exists
fs.mkdirSync(directory, { recursive: true })
// Save the certificate and private key
fs.writeFileSync(path.join(directory, `${domain}.crt`), certificate)
fs.writeFileSync(path.join(directory, `${domain}.key`), domainPrivateKey)
this.log(domain, "wrote cert. SUCCESS!")
} catch (error) {
console.error(`An error occurred making cert for ${domain}`, error)
}
}
}
module.exports = { CertificateMaker }