@certd/acme-client 1.39.12 → 1.39.13

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,3 +1,4 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * ACME HTTP client
3
4
  */
@@ -6,7 +7,6 @@ const { RSA_PKCS1_PADDING } = constants;
6
7
  import axios from './axios.js';
7
8
  import { log } from './logger.js';
8
9
  import { getJwk } from './crypto/index.js';
9
-
10
10
  /**
11
11
  * ACME HTTP client
12
12
  *
@@ -17,16 +17,13 @@ import { getJwk } from './crypto/index.js';
17
17
  * @param {string} [opts.externalAccountBinding.kid] External account binding KID
18
18
  * @param {string} [opts.externalAccountBinding.hmacKey] External account binding HMAC key
19
19
  */
20
-
21
20
  class HttpClient {
22
- constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {}, logger, cacheNonce= false) {
21
+ constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {}, logger, cacheNonce = false) {
23
22
  this.directoryUrl = directoryUrl;
24
23
  this.accountKey = accountKey;
25
24
  this.externalAccountBinding = externalAccountBinding;
26
-
27
25
  this.maxBadNonceRetries = 5;
28
26
  this.jwk = null;
29
-
30
27
  this.directoryCache = null;
31
28
  this.directoryMaxAge = 86400;
32
29
  this.directoryTimestamp = 0;
@@ -35,14 +32,13 @@ class HttpClient {
35
32
  this.nonces = [];
36
33
  this.cacheNonce = cacheNonce;
37
34
  }
38
-
39
35
  pushNonce(nonce) {
40
36
  if (!this.cacheNonce || !nonce) {
41
37
  return;
42
38
  }
43
39
  this.nonces.push({
44
40
  nonce,
45
- expires: Date.now() + 30*1000,
41
+ expires: Date.now() + 30 * 1000,
46
42
  });
47
43
  }
48
44
  popNonce() {
@@ -60,7 +56,6 @@ class HttpClient {
60
56
  return item.nonce;
61
57
  }
62
58
  }
63
-
64
59
  /**
65
60
  * HTTP request
66
61
  *
@@ -69,7 +64,6 @@ class HttpClient {
69
64
  * @param {object} [opts] Request options
70
65
  * @returns {Promise<object>} HTTP response
71
66
  */
72
-
73
67
  async request(url, method, opts = {}) {
74
68
  if (this.urlMapping && this.urlMapping.enabled && this.urlMapping.mappings) {
75
69
  // eslint-disable-next-line no-restricted-syntax
@@ -85,29 +79,22 @@ class HttpClient {
85
79
  opts.url = url;
86
80
  opts.method = method;
87
81
  opts.validateStatus = null;
88
-
89
82
  /* Headers */
90
83
  if (typeof opts.headers === 'undefined') {
91
84
  opts.headers = {};
92
85
  }
93
-
94
86
  opts.headers['Content-Type'] = 'application/jose+json';
95
-
96
87
  /* Request */
97
88
  this.log(`HTTP request: ${method} ${url}`);
98
89
  const resp = await axios.request(opts);
99
-
100
90
  this.log(`RESP ${resp.status} ${method} ${url}`);
101
-
102
91
  const nonce = resp.headers['replay-nonce'];
103
92
  if (nonce) {
104
93
  //如果有nonce
105
94
  this.pushNonce(nonce);
106
95
  }
107
-
108
96
  return resp;
109
97
  }
110
-
111
98
  /**
112
99
  * Get ACME provider directory
113
100
  *
@@ -115,44 +102,34 @@ class HttpClient {
115
102
  *
116
103
  * @returns {Promise<object>} ACME directory contents
117
104
  */
118
-
119
105
  async getDirectory() {
120
106
  const now = Math.floor(Date.now() / 1000);
121
107
  const age = (now - this.directoryTimestamp);
122
-
123
108
  if (!this.directoryCache || (age > this.directoryMaxAge)) {
124
109
  this.log(`Refreshing ACME directory, age: ${age}`);
125
110
  const resp = await this.request(this.directoryUrl, 'get');
126
-
127
111
  if (resp.status >= 400) {
128
112
  throw new Error(`Attempting to read ACME directory returned error ${resp.status}: ${this.directoryUrl}`);
129
113
  }
130
-
131
114
  if (!resp.data) {
132
115
  throw new Error('Attempting to read ACME directory returned no data');
133
116
  }
134
-
135
117
  this.directoryCache = resp.data;
136
118
  this.directoryTimestamp = now;
137
119
  }
138
-
139
120
  return this.directoryCache;
140
121
  }
141
-
142
122
  /**
143
123
  * Get JSON Web Key
144
124
  *
145
125
  * @returns {object} JSON Web Key
146
126
  */
147
-
148
127
  getJwk() {
149
128
  if (!this.jwk) {
150
129
  this.jwk = getJwk(this.accountKey);
151
130
  }
152
-
153
131
  return this.jwk;
154
132
  }
155
-
156
133
  /**
157
134
  * Get nonce from directory API endpoint
158
135
  *
@@ -160,63 +137,48 @@ class HttpClient {
160
137
  *
161
138
  * @returns {Promise<string>} Nonce
162
139
  */
163
-
164
140
  async getNonce() {
165
-
166
141
  //尝试从队列中pop一个nonce
167
142
  const nonce = this.popNonce();
168
143
  if (nonce) {
169
144
  return nonce;
170
145
  }
171
-
172
146
  const url = await this.getResourceUrl('newNonce');
173
147
  const resp = await this.request(url, 'head');
174
-
175
148
  if (!resp.headers['replay-nonce']) {
176
149
  throw new Error('Failed to get nonce from ACME provider');
177
150
  }
178
-
179
151
  if (this.cacheNonce) {
180
152
  return this.popNonce();
181
153
  }
182
154
  return resp.headers['replay-nonce'];
183
-
184
155
  }
185
-
186
156
  /**
187
157
  * Get URL for a directory resource
188
158
  *
189
159
  * @param {string} resource API resource name
190
160
  * @returns {Promise<string>} URL
191
161
  */
192
-
193
162
  async getResourceUrl(resource) {
194
163
  const dir = await this.getDirectory();
195
-
196
164
  if (!dir[resource]) {
197
165
  throw new Error(`Unable to locate API resource URL in ACME directory: "${resource}",获取ACME接口地址信息失败,可能网络不稳定或该证书颁发机构服务器崩溃,目录地址:${this.directoryUrl},请测试地址是否可以正常访问并显示json格式的URL地址列表`);
198
166
  }
199
-
200
167
  return dir[resource];
201
168
  }
202
-
203
169
  /**
204
170
  * Get directory meta field
205
171
  *
206
172
  * @param {string} field Meta field name
207
173
  * @returns {Promise<string|null>} Meta field value
208
174
  */
209
-
210
175
  async getMetaField(field) {
211
176
  const dir = await this.getDirectory();
212
-
213
177
  if (('meta' in dir) && (field in dir.meta)) {
214
178
  return dir.meta[field];
215
179
  }
216
-
217
180
  return null;
218
181
  }
219
-
220
182
  /**
221
183
  * Prepare HTTP request body for signature
222
184
  *
@@ -228,16 +190,13 @@ class HttpClient {
228
190
  * @param {string} [opts.kid] JWS KID
229
191
  * @returns {object} Signed HTTP request body
230
192
  */
231
-
232
193
  prepareSignedBody(alg, url, payload = null, { nonce = null, kid = null } = {}) {
233
194
  const header = { alg, url };
234
-
235
195
  /* Nonce */
236
196
  if (nonce) {
237
197
  this.log(`Using nonce: ${nonce}`);
238
198
  header.nonce = nonce;
239
199
  }
240
-
241
200
  /* KID or JWK */
242
201
  if (kid) {
243
202
  header.kid = kid;
@@ -245,14 +204,12 @@ class HttpClient {
245
204
  else {
246
205
  header.jwk = this.getJwk();
247
206
  }
248
-
249
207
  /* Body */
250
208
  return {
251
209
  payload: payload ? Buffer.from(JSON.stringify(payload)).toString('base64url') : '',
252
210
  protected: Buffer.from(JSON.stringify(header)).toString('base64url'),
253
211
  };
254
212
  }
255
-
256
213
  /**
257
214
  * Create JWS HTTP request body using HMAC
258
215
  *
@@ -264,17 +221,13 @@ class HttpClient {
264
221
  * @param {string} [opts.kid] JWS KID
265
222
  * @returns {object} Signed HMAC request body
266
223
  */
267
-
268
224
  createSignedHmacBody(hmacKey, url, payload = null, { nonce = null, kid = null } = {}) {
269
225
  const result = this.prepareSignedBody('HS256', url, payload, { nonce, kid });
270
-
271
226
  /* Signature */
272
227
  const signer = createHmac('SHA256', Buffer.from(hmacKey, 'base64')).update(`${result.protected}.${result.payload}`, 'utf8');
273
228
  result.signature = signer.digest().toString('base64url');
274
-
275
229
  return result;
276
230
  }
277
-
278
231
  /**
279
232
  * Create JWS HTTP request body using RSA or ECC
280
233
  *
@@ -287,16 +240,13 @@ class HttpClient {
287
240
  * @param {string} [opts.kid] JWS KID
288
241
  * @returns {object} JWS request body
289
242
  */
290
-
291
243
  createSignedBody(url, payload = null, { nonce = null, kid = null } = {}) {
292
244
  const jwk = this.getJwk();
293
245
  let headerAlg = 'RS256';
294
246
  let signerAlg = 'SHA256';
295
-
296
247
  /* https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 */
297
248
  if (jwk.crv && (jwk.kty === 'EC')) {
298
249
  headerAlg = 'ES256';
299
-
300
250
  if (jwk.crv === 'P-384') {
301
251
  headerAlg = 'ES384';
302
252
  signerAlg = 'SHA384';
@@ -306,21 +256,17 @@ class HttpClient {
306
256
  signerAlg = 'SHA512';
307
257
  }
308
258
  }
309
-
310
259
  /* Prepare body and signer */
311
260
  const result = this.prepareSignedBody(headerAlg, url, payload, { nonce, kid });
312
261
  const signer = createSign(signerAlg).update(`${result.protected}.${result.payload}`, 'utf8');
313
-
314
262
  /* Signature - https://stackoverflow.com/questions/39554165 */
315
263
  result.signature = signer.sign({
316
264
  key: this.accountKey,
317
265
  padding: RSA_PKCS1_PADDING,
318
266
  dsaEncoding: 'ieee-p1363',
319
267
  }, 'base64url');
320
-
321
268
  return result;
322
269
  }
323
-
324
270
  /**
325
271
  * Signed HTTP request
326
272
  *
@@ -335,40 +281,32 @@ class HttpClient {
335
281
  * @param {number} [attempts] Request attempt counter
336
282
  * @returns {Promise<object>} HTTP response
337
283
  */
338
-
339
284
  async signedRequest(url, payload, { kid = null, nonce = null, includeExternalAccountBinding = false } = {}, attempts = 0) {
340
285
  if (!nonce) {
341
286
  nonce = await this.getNonce();
342
287
  }
343
-
344
288
  /* External account binding */
345
289
  if (includeExternalAccountBinding && this.externalAccountBinding) {
346
290
  if (this.externalAccountBinding.kid && this.externalAccountBinding.hmacKey) {
347
291
  const jwk = this.getJwk();
348
292
  const eabKid = this.externalAccountBinding.kid;
349
293
  const eabHmacKey = this.externalAccountBinding.hmacKey;
350
-
351
294
  payload.externalAccountBinding = this.createSignedHmacBody(eabHmacKey, url, jwk, { kid: eabKid });
352
295
  }
353
296
  }
354
-
355
297
  /* Sign body and send request */
356
298
  const data = this.createSignedBody(url, payload, { nonce, kid });
357
299
  const resp = await this.request(url, 'post', { data });
358
-
359
300
  /* Retry on bad nonce - https://datatracker.ietf.org/doc/html/rfc8555#section-6.5 */
360
301
  if (resp.data && resp.data.type && (resp.status === 400) && (resp.data.type === 'urn:ietf:params:acme:error:badNonce') && (attempts < this.maxBadNonceRetries)) {
361
302
  nonce = resp.headers['replay-nonce'] || null;
362
303
  attempts += 1;
363
-
364
304
  this.log(`Caught invalid nonce error, retrying (${attempts}/${this.maxBadNonceRetries}) signed request to: ${url}`);
365
305
  return this.signedRequest(url, payload, { kid, nonce, includeExternalAccountBinding }, attempts);
366
306
  }
367
-
368
307
  /* Return response */
369
308
  return resp;
370
309
  }
371
310
  }
372
-
373
311
  /* Export client */
374
312
  export default HttpClient;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * acme-client
3
+ */
4
+ export { default as Client } from './client.js';
5
+ export type * from './types.js';
6
+ /**
7
+ * Directory URLs
8
+ */
9
+ export declare const directory: {
10
+ buypass: {
11
+ staging: string;
12
+ production: string;
13
+ };
14
+ google: {
15
+ staging: string;
16
+ production: string;
17
+ };
18
+ letsencrypt: {
19
+ staging: string;
20
+ production: string;
21
+ };
22
+ letsencrypt_staging: {
23
+ production: string;
24
+ };
25
+ zerossl: {
26
+ staging: string;
27
+ production: string;
28
+ };
29
+ sslcom: {
30
+ staging: string;
31
+ production: string;
32
+ ec: string;
33
+ };
34
+ litessl: {
35
+ staging: string;
36
+ production: string;
37
+ };
38
+ };
39
+ export declare function getDirectoryUrl(opts: any): any;
40
+ export declare function getAllSslProviderDomains(): string[];
41
+ export declare function getSslProviderReverseProxies(): {};
42
+ export declare function setSslProviderReverseProxies(reverseProxies: any): void;
43
+ /**
44
+ * Crypto
45
+ */
46
+ export * as crypto from './crypto/index.js';
47
+ export * as forge from './crypto/forge.js';
48
+ /**
49
+ * Axios
50
+ */
51
+ export * from './axios.js';
52
+ /**
53
+ * Logger
54
+ */
55
+ export * from './logger.js';
56
+ export * from './verify.js';
57
+ export * from './error.js';
58
+ export * from './util.js';
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * acme-client
4
+ */
5
+ export { default as Client } from './client.js';
6
+ /**
7
+ * Directory URLs
8
+ */
9
+ export const directory = {
10
+ buypass: {
11
+ staging: 'https://api.test4.buypass.no/acme/directory',
12
+ production: 'https://api.buypass.com/acme/directory',
13
+ },
14
+ google: {
15
+ staging: 'https://dv.acme-v02.test-api.pki.goog/directory',
16
+ production: 'https://dv.acme-v02.api.pki.goog/directory',
17
+ },
18
+ letsencrypt: {
19
+ staging: 'https://acme-staging-v02.api.letsencrypt.org/directory',
20
+ production: 'https://acme-v02.api.letsencrypt.org/directory',
21
+ },
22
+ letsencrypt_staging: {
23
+ production: 'https://acme-staging-v02.api.letsencrypt.org/directory',
24
+ },
25
+ zerossl: {
26
+ staging: 'https://acme.zerossl.com/v2/DV90',
27
+ production: 'https://acme.zerossl.com/v2/DV90',
28
+ },
29
+ sslcom: {
30
+ staging: 'https://acme.ssl.com/sslcom-dv-rsa',
31
+ production: 'https://acme.ssl.com/sslcom-dv-rsa',
32
+ ec: 'https://acme.ssl.com/sslcom-dv-ecc',
33
+ },
34
+ litessl: {
35
+ staging: 'https://acme.litessl.com/acme/v2/directory',
36
+ production: 'https://acme.litessl.com/acme/v2/directory',
37
+ },
38
+ };
39
+ export function getDirectoryUrl(opts) {
40
+ const { sslProvider, pkType } = opts;
41
+ const list = directory[sslProvider];
42
+ if (!list) {
43
+ throw new Error(`sslProvider ${sslProvider} not found`);
44
+ }
45
+ let pkTypePrefix = pkType || 'rsa';
46
+ if (pkType) {
47
+ pkTypePrefix = pkType.toLowerCase().split("_")[0];
48
+ }
49
+ if (pkTypePrefix && list[pkTypePrefix]) {
50
+ return list[pkTypePrefix];
51
+ }
52
+ return list.production;
53
+ }
54
+ export function getAllSslProviderDomains() {
55
+ const list = Object.values(directory).map((item) => {
56
+ let url = item.production.replace('https://', '');
57
+ url = url.substring(0, url.indexOf('/'));
58
+ return url;
59
+ });
60
+ return list;
61
+ }
62
+ let sslProviderReverseProxies = {};
63
+ function initSslProviderReverseProxies() {
64
+ for (const sslProvider of getAllSslProviderDomains()) {
65
+ sslProviderReverseProxies[sslProvider] = "";
66
+ }
67
+ }
68
+ initSslProviderReverseProxies();
69
+ export function getSslProviderReverseProxies() {
70
+ return sslProviderReverseProxies;
71
+ }
72
+ export function setSslProviderReverseProxies(reverseProxies) {
73
+ Object.assign(sslProviderReverseProxies, reverseProxies);
74
+ }
75
+ /**
76
+ * Crypto
77
+ */
78
+ export * as crypto from './crypto/index.js';
79
+ export * as forge from './crypto/forge.js';
80
+ /**
81
+ * Axios
82
+ */
83
+ export * from './axios.js';
84
+ /**
85
+ * Logger
86
+ */
87
+ export * from './logger.js';
88
+ export * from './verify.js';
89
+ export * from './error.js';
90
+ export * from './util.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * ACME logger
3
+ */
4
+ /**
5
+ * Set logger function
6
+ *
7
+ * @param {function} fn Logger function
8
+ */
9
+ export declare const setLogger: (fn: any) => void;
10
+ /**
11
+ * Log message
12
+ *
13
+ * @param {string} msg Message
14
+ */
15
+ export declare const log: (...msg: any[]) => void;
@@ -1,29 +1,24 @@
1
+ // @ts-nocheck
1
2
  /**
2
3
  * ACME logger
3
4
  */
4
-
5
- import debugg from 'debug'
5
+ import debugg from 'debug';
6
6
  const debug = debugg('acme-client');
7
-
8
- let logger = () => {};
9
-
7
+ let logger = () => { };
10
8
  /**
11
9
  * Set logger function
12
10
  *
13
11
  * @param {function} fn Logger function
14
12
  */
15
-
16
13
  export const setLogger = (fn) => {
17
14
  logger = fn;
18
15
  };
19
-
20
16
  /**
21
17
  * Log message
22
18
  *
23
19
  * @param {string} msg Message
24
20
  */
25
-
26
- export const log = (...msg) => {
21
+ export const log = (...msg) => {
27
22
  debug(...msg);
28
23
  logger(...msg);
29
24
  };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Account
3
+ *
4
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.2
5
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.3
6
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2
7
+ */
8
+ export interface Account {
9
+ status: "valid" | "deactivated" | "revoked";
10
+ orders: string;
11
+ contact?: string[];
12
+ termsOfServiceAgreed?: boolean;
13
+ externalAccountBinding?: object;
14
+ }
15
+ export interface AccountCreateRequest {
16
+ contact?: string[];
17
+ termsOfServiceAgreed?: boolean;
18
+ onlyReturnExisting?: boolean;
19
+ externalAccountBinding?: object;
20
+ }
21
+ export interface AccountUpdateRequest {
22
+ status?: string;
23
+ contact?: string[];
24
+ termsOfServiceAgreed?: boolean;
25
+ }
26
+ /**
27
+ * Order
28
+ *
29
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.3
30
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
31
+ */
32
+ export interface Order {
33
+ status: "pending" | "ready" | "processing" | "valid" | "invalid";
34
+ identifiers: Identifier[];
35
+ authorizations: string[];
36
+ finalize: string;
37
+ expires?: string;
38
+ notBefore?: string;
39
+ notAfter?: string;
40
+ error?: object;
41
+ certificate?: string;
42
+ }
43
+ export interface OrderCreateRequest {
44
+ identifiers: Identifier[];
45
+ notBefore?: string;
46
+ notAfter?: string;
47
+ }
48
+ /**
49
+ * Authorization
50
+ *
51
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
52
+ */
53
+ export interface Authorization {
54
+ identifier: Identifier;
55
+ status: "pending" | "valid" | "invalid" | "deactivated" | "expired" | "revoked";
56
+ challenges: Challenge[];
57
+ expires?: string;
58
+ wildcard?: boolean;
59
+ }
60
+ export interface Identifier {
61
+ type: string;
62
+ value: string;
63
+ }
64
+ /**
65
+ * Challenge
66
+ *
67
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-8
68
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-8.3
69
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-8.4
70
+ */
71
+ export interface ChallengeAbstract {
72
+ type: string;
73
+ url: string;
74
+ status: "pending" | "processing" | "valid" | "invalid";
75
+ validated?: string;
76
+ error?: object;
77
+ }
78
+ export interface HttpChallenge extends ChallengeAbstract {
79
+ type: "http-01";
80
+ token: string;
81
+ }
82
+ export interface DnsChallenge extends ChallengeAbstract {
83
+ type: "dns-01";
84
+ token: string;
85
+ }
86
+ export type Challenge = HttpChallenge | DnsChallenge;
87
+ /**
88
+ * Certificate
89
+ *
90
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.6
91
+ */
92
+ export declare enum CertificateRevocationReason {
93
+ Unspecified = 0,
94
+ KeyCompromise = 1,
95
+ CACompromise = 2,
96
+ AffiliationChanged = 3,
97
+ Superseded = 4,
98
+ CessationOfOperation = 5,
99
+ CertificateHold = 6,
100
+ RemoveFromCRL = 8,
101
+ PrivilegeWithdrawn = 9,
102
+ AACompromise = 10
103
+ }
104
+ export interface CertificateRevocationRequest {
105
+ reason?: CertificateRevocationReason;
106
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Account
3
+ *
4
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.2
5
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.3
6
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2
7
+ */
8
+ /**
9
+ * Certificate
10
+ *
11
+ * https://datatracker.ietf.org/doc/html/rfc8555#section-7.6
12
+ */
13
+ export var CertificateRevocationReason;
14
+ (function (CertificateRevocationReason) {
15
+ CertificateRevocationReason[CertificateRevocationReason["Unspecified"] = 0] = "Unspecified";
16
+ CertificateRevocationReason[CertificateRevocationReason["KeyCompromise"] = 1] = "KeyCompromise";
17
+ CertificateRevocationReason[CertificateRevocationReason["CACompromise"] = 2] = "CACompromise";
18
+ CertificateRevocationReason[CertificateRevocationReason["AffiliationChanged"] = 3] = "AffiliationChanged";
19
+ CertificateRevocationReason[CertificateRevocationReason["Superseded"] = 4] = "Superseded";
20
+ CertificateRevocationReason[CertificateRevocationReason["CessationOfOperation"] = 5] = "CessationOfOperation";
21
+ CertificateRevocationReason[CertificateRevocationReason["CertificateHold"] = 6] = "CertificateHold";
22
+ CertificateRevocationReason[CertificateRevocationReason["RemoveFromCRL"] = 8] = "RemoveFromCRL";
23
+ CertificateRevocationReason[CertificateRevocationReason["PrivilegeWithdrawn"] = 9] = "PrivilegeWithdrawn";
24
+ CertificateRevocationReason[CertificateRevocationReason["AACompromise"] = 10] = "AACompromise";
25
+ })(CertificateRevocationReason || (CertificateRevocationReason = {}));