forked from publishlab/node-acme-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.js
148 lines (123 loc) · 4.2 KB
/
api.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/**
* Example of acme.Client API
*/
const acme = require('./../');
function log(m) {
process.stdout.write(`${m}\n`);
}
/**
* Function used to satisfy an ACME challenge
*
* @param {object} authz Authorization object
* @param {object} challenge Selected challenge
* @param {string} keyAuthorization Authorization key
* @returns {Promise}
*/
async function challengeCreateFn(authz, challenge, keyAuthorization) {
// Do something here
log(JSON.stringify(authz));
log(JSON.stringify(challenge));
log(keyAuthorization);
}
/**
* Function used to remove an ACME challenge response
*
* @param {object} authz Authorization object
* @param {object} challenge Selected challenge
* @returns {Promise}
*/
async function challengeRemoveFn(authz, challenge, keyAuthorization) {
// Do something here
log(JSON.stringify(authz));
log(JSON.stringify(challenge));
log(keyAuthorization);
}
/**
* Main
*/
module.exports = async () => {
// Init client
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: await acme.crypto.createPrivateKey(),
});
// Register account
await client.createAccount({
termsOfServiceAgreed: true,
contact: ['mailto:[email protected]'],
});
// Place new order
const order = await client.createOrder({
identifiers: [
{ type: 'dns', value: 'example.com' },
{ type: 'dns', value: '*.example.com' },
],
});
/**
* authorizations / client.getAuthorizations(order);
* An array with one item per DNS name in the certificate order.
* All items require at least one satisfied challenge before order can be completed.
*/
const authorizations = await client.getAuthorizations(order);
const promises = authorizations.map(async (authz) => {
let challengeCompleted = false;
try {
/**
* challenges / authz.challenges
* An array of all available challenge types for a single DNS name.
* One of these challenges needs to be satisfied.
*/
const { challenges } = authz;
// Just select any challenge
const challenge = challenges.pop();
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge);
try {
// Satisfy challenge
await challengeCreateFn(authz, challenge, keyAuthorization);
// Verify that challenge is satisfied
await client.verifyChallenge(authz, challenge);
// Notify ACME provider that challenge is satisfied
await client.completeChallenge(challenge);
challengeCompleted = true;
// Wait for ACME provider to respond with valid status
await client.waitForValidStatus(challenge);
}
finally {
// Clean up challenge response
try {
await challengeRemoveFn(authz, challenge, keyAuthorization);
}
catch (e) {
/**
* Catch errors thrown by challengeRemoveFn() so the order can
* be finalized, even though something went wrong during cleanup
*/
}
}
}
catch (e) {
// Deactivate pending authz when unable to complete challenge
if (!challengeCompleted) {
try {
await client.deactivateAuthorization(authz);
}
catch (f) {
// Catch and suppress deactivateAuthorization() errors
}
}
throw e;
}
});
// Wait for challenges to complete
await Promise.all(promises);
// Finalize order
const [key, csr] = await acme.crypto.createCsr({
altNames: ['example.com', '*.example.com'],
});
const finalized = await client.finalizeOrder(order, csr);
const cert = await client.getCertificate(finalized);
// Done
log(`CSR:\n${csr.toString()}`);
log(`Private key:\n${key.toString()}`);
log(`Certificate:\n${cert.toString()}`);
};