-
Notifications
You must be signed in to change notification settings - Fork 54
/
api.js
148 lines (123 loc) · 4.25 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:test@example.com'],
});
/* 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()}`);
};