mirror of https://github.com/certd/certd
				
				
				
			Merge remote-tracking branch 'origin/acme_sync' into v2
# Conflicts: # packages/core/acme-client/package.json # packages/core/acme-client/src/auto.js # packages/core/acme-client/src/axios.js # packages/core/acme-client/src/http.jspull/78/head
						commit
						1b7debc6a4
					
				| 
						 | 
				
			
			@ -9,15 +9,8 @@ env:
 | 
			
		|||
rules:
 | 
			
		||||
  indent: [2, 4, { SwitchCase: 1, VariableDeclarator: 1 }]
 | 
			
		||||
  brace-style: [2, 'stroustrup', { allowSingleLine: true }]
 | 
			
		||||
  space-before-function-paren: [2, { anonymous: 'never', named: 'never' }]
 | 
			
		||||
  func-names: 0
 | 
			
		||||
  prefer-destructuring: 0
 | 
			
		||||
  object-curly-newline: 0
 | 
			
		||||
  class-methods-use-this: 0
 | 
			
		||||
  wrap-iife: [2, 'inside']
 | 
			
		||||
  no-param-reassign: 0
 | 
			
		||||
  comma-dangle: [2, 'never']
 | 
			
		||||
  max-len: [1, 200, 2, { ignoreUrls: true, ignoreComments: false }]
 | 
			
		||||
  no-multiple-empty-lines: [2, { max: 2, maxBOF: 0, maxEOF: 0 }]
 | 
			
		||||
  prefer-object-spread: 0
 | 
			
		||||
  import/no-useless-path-segments: 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
---
 | 
			
		||||
name: test
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +11,6 @@ jobs:
 | 
			
		|||
        node: [16, 18, 20, 22]
 | 
			
		||||
        eab: [0, 1]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # Environment
 | 
			
		||||
    #
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +39,6 @@ jobs:
 | 
			
		|||
      ACME_HTTP_PORT: 5002
 | 
			
		||||
      ACME_HTTPS_PORT: 5003
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
    # Pipeline
 | 
			
		||||
    #
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
 | 
			
		|||
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## v5.3.1
 | 
			
		||||
## v5.3.1 (2024-05-22)
 | 
			
		||||
 | 
			
		||||
* `fixed` Allow `client.auto()` being called with an empty CSR common name
 | 
			
		||||
* `fixed` Bug when calling `updateAccountKey()` with external account binding
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +92,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
 | 
			
		|||
* `fixed` Upgrade `axios@0.26.1`
 | 
			
		||||
* `fixed` Upgrade `node-forge@1.3.0` - [CVE-2022-24771](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24771), [CVE-2022-24772](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24772), [CVE-2022-24773](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24773)
 | 
			
		||||
 | 
			
		||||
## 4.2.4 (2022-03-19)
 | 
			
		||||
## v4.2.4 (2022-03-19)
 | 
			
		||||
 | 
			
		||||
* `fixed` Use SHA-256 when signing CSRs
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,13 +9,13 @@ This module is written to handle communication with a Boulder/Let's Encrypt-styl
 | 
			
		|||
 | 
			
		||||
## Compatibility
 | 
			
		||||
 | 
			
		||||
| acme-client   | Node.js   |                                           |
 | 
			
		||||
| ------------- | --------- | ----------------------------------------- |
 | 
			
		||||
| v5.x          | >= v16    | [Upgrade guide](docs/upgrade-v5.md)       |
 | 
			
		||||
| v4.x          | >= v10    | [Changelog](CHANGELOG.md#v400-2020-05-29) |
 | 
			
		||||
| v3.x          | >= v8     | [Changelog](CHANGELOG.md#v300-2019-07-13) |
 | 
			
		||||
| v2.x          | >= v4     | [Changelog](CHANGELOG.md#v200-2018-04-02) |
 | 
			
		||||
| v1.x          | >= v4     | [Changelog](CHANGELOG.md#v100-2017-10-20) |
 | 
			
		||||
| acme-client | Node.js |                                           |
 | 
			
		||||
| ----------- | ------- | ----------------------------------------- |
 | 
			
		||||
| v5.x        | >= v16  | [Upgrade guide](docs/upgrade-v5.md)       |
 | 
			
		||||
| v4.x        | >= v10  | [Changelog](CHANGELOG.md#v400-2020-05-29) |
 | 
			
		||||
| v3.x        | >= v8   | [Changelog](CHANGELOG.md#v300-2019-07-13) |
 | 
			
		||||
| v2.x        | >= v4   | [Changelog](CHANGELOG.md#v200-2018-04-02) |
 | 
			
		||||
| v1.x        | >= v4   | [Changelog](CHANGELOG.md#v100-2017-10-20) |
 | 
			
		||||
 | 
			
		||||
## Table of contents
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ const accountPrivateKey = '<PEM encoded private key>';
 | 
			
		|||
 | 
			
		||||
const client = new acme.Client({
 | 
			
		||||
    directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
    accountKey: accountPrivateKey
 | 
			
		||||
    accountKey: accountPrivateKey,
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -75,8 +75,8 @@ const client = new acme.Client({
 | 
			
		|||
    accountKey: accountPrivateKey,
 | 
			
		||||
    externalAccountBinding: {
 | 
			
		||||
        kid: 'YOUR-EAB-KID',
 | 
			
		||||
        hmacKey: 'YOUR-EAB-HMAC-KEY'
 | 
			
		||||
    }
 | 
			
		||||
        hmacKey: 'YOUR-EAB-HMAC-KEY',
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +90,7 @@ In some cases, for example with some EAB providers, this account creation step m
 | 
			
		|||
const client = new acme.Client({
 | 
			
		||||
    directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
    accountKey: accountPrivateKey,
 | 
			
		||||
    accountUrl: 'https://acme-v02.api.letsencrypt.org/acme/acct/12345678'
 | 
			
		||||
    accountUrl: 'https://acme-v02.api.letsencrypt.org/acme/acct/12345678',
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -113,8 +113,7 @@ const privateRsaKey = await acme.crypto.createPrivateRsaKey();
 | 
			
		|||
const privateEcdsaKey = await acme.crypto.createPrivateEcdsaKey();
 | 
			
		||||
 | 
			
		||||
const [certificateKey, certificateCsr] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: '*.example.com',
 | 
			
		||||
    altNames: ['example.com']
 | 
			
		||||
    altNames: ['example.com', '*.example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +138,7 @@ const autoOpts = {
 | 
			
		|||
    email: 'test@example.com',
 | 
			
		||||
    termsOfServiceAgreed: true,
 | 
			
		||||
    challengeCreateFn: async (authz, challenge, keyAuthorization) => {},
 | 
			
		||||
    challengeRemoveFn: async (authz, challenge, keyAuthorization) => {}
 | 
			
		||||
    challengeRemoveFn: async (authz, challenge, keyAuthorization) => {},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const certificate = await client.auto(autoOpts);
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +155,7 @@ To modify challenge priority, provide a list of challenge types in `challengePri
 | 
			
		|||
```js
 | 
			
		||||
await client.auto({
 | 
			
		||||
    ...,
 | 
			
		||||
    challengePriority: ['http-01', 'dns-01']
 | 
			
		||||
    challengePriority: ['http-01', 'dns-01'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +170,7 @@ To completely disable `acme-client`s internal challenge verification, enable `sk
 | 
			
		|||
```js
 | 
			
		||||
await client.auto({
 | 
			
		||||
    ...,
 | 
			
		||||
    skipChallengeVerification: true
 | 
			
		||||
    skipChallengeVerification: true,
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,14 +184,14 @@ For more fine-grained control you can interact with the ACME API using the metho
 | 
			
		|||
```js
 | 
			
		||||
const account = await client.createAccount({
 | 
			
		||||
    termsOfServiceAgreed: true,
 | 
			
		||||
    contact: ['mailto:test@example.com']
 | 
			
		||||
    contact: ['mailto:test@example.com'],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const order = await client.createOrder({
 | 
			
		||||
    identifiers: [
 | 
			
		||||
        { type: 'dns', value: 'example.com' },
 | 
			
		||||
        { type: 'dns', value: '*.example.com' }
 | 
			
		||||
    ]
 | 
			
		||||
        { type: 'dns', value: '*.example.com' },
 | 
			
		||||
    ],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -207,7 +206,7 @@ const acme = require('acme-client');
 | 
			
		|||
 | 
			
		||||
acme.axios.defaults.proxy = {
 | 
			
		||||
    host: '127.0.0.1',
 | 
			
		||||
    port: 9000
 | 
			
		||||
    port: 9000,
 | 
			
		||||
};
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ Create ACME client instance
 | 
			
		|||
```js
 | 
			
		||||
const client = new acme.Client({
 | 
			
		||||
    directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
    accountKey: 'Private key goes here'
 | 
			
		||||
    accountKey: 'Private key goes here',
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ const client = new acme.Client({
 | 
			
		|||
    accountUrl: 'Optional account URL goes here',
 | 
			
		||||
    backoffAttempts: 10,
 | 
			
		||||
    backoffMin: 5000,
 | 
			
		||||
    backoffMax: 30000
 | 
			
		||||
    backoffMax: 30000,
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
| 
						 | 
				
			
			@ -86,8 +86,8 @@ const client = new acme.Client({
 | 
			
		|||
    accountKey: 'Private key goes here',
 | 
			
		||||
    externalAccountBinding: {
 | 
			
		||||
        kid: 'YOUR-EAB-KID',
 | 
			
		||||
        hmacKey: 'YOUR-EAB-HMAC-KEY'
 | 
			
		||||
    }
 | 
			
		||||
        hmacKey: 'YOUR-EAB-HMAC-KEY',
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="AcmeClient+getTermsOfServiceUrl"></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +145,7 @@ https://datatracker.ietf.org/doc/html/rfc8555#section-7.3
 | 
			
		|||
Create a new account
 | 
			
		||||
```js
 | 
			
		||||
const account = await client.createAccount({
 | 
			
		||||
    termsOfServiceAgreed: true
 | 
			
		||||
    termsOfServiceAgreed: true,
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ Create a new account with contact info
 | 
			
		|||
```js
 | 
			
		||||
const account = await client.createAccount({
 | 
			
		||||
    termsOfServiceAgreed: true,
 | 
			
		||||
    contact: ['mailto:test@example.com']
 | 
			
		||||
    contact: ['mailto:test@example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="AcmeClient+updateAccount"></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +174,7 @@ https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2
 | 
			
		|||
Update existing account
 | 
			
		||||
```js
 | 
			
		||||
const account = await client.updateAccount({
 | 
			
		||||
    contact: ['mailto:foo@example.com']
 | 
			
		||||
    contact: ['mailto:foo@example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="AcmeClient+updateAccountKey"></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -218,8 +218,8 @@ Create a new order
 | 
			
		|||
const order = await client.createOrder({
 | 
			
		||||
    identifiers: [
 | 
			
		||||
        { type: 'dns', value: 'example.com' },
 | 
			
		||||
        { type: 'dns', value: 'test.example.com' }
 | 
			
		||||
    ]
 | 
			
		||||
        { type: 'dns', value: 'test.example.com' },
 | 
			
		||||
    ],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="AcmeClient+getOrder"></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -452,7 +452,7 @@ Revoke certificate with reason
 | 
			
		|||
```js
 | 
			
		||||
const certificate = { ... }; // Previously created certificate
 | 
			
		||||
const result = await client.revokeCertificate(certificate, {
 | 
			
		||||
    reason: 4
 | 
			
		||||
    reason: 4,
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="AcmeClient+auto"></a>
 | 
			
		||||
| 
						 | 
				
			
			@ -479,7 +479,7 @@ Auto mode
 | 
			
		|||
Order a certificate using auto mode
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const certificate = await client.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -491,14 +491,14 @@ const certificate = await client.auto({
 | 
			
		|||
    },
 | 
			
		||||
    challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
 | 
			
		||||
        // Clean up challenge here
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
Order a certificate using auto mode with preferred chain
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const certificate = await client.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +507,7 @@ const certificate = await client.auto({
 | 
			
		|||
    termsOfServiceAgreed: true,
 | 
			
		||||
    preferredChain: 'DST Root CA X3',
 | 
			
		||||
    challengeCreateFn: async () => {},
 | 
			
		||||
    challengeRemoveFn: async () => {}
 | 
			
		||||
    challengeRemoveFn: async () => {},
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
<a name="Client"></a>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -239,29 +239,30 @@ Create a Certificate Signing Request
 | 
			
		|||
Create a Certificate Signing Request
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
Certificate Signing Request with both common and alternative names
 | 
			
		||||
> *Warning*: Certificate subject common name has been [deprecated](https://letsencrypt.org/docs/glossary/#def-CN) and its use is [discouraged](https://cabforum.org/uploads/BRv1.2.3.pdf).
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    keySize: 4096,
 | 
			
		||||
    commonName: 'test.example.com',
 | 
			
		||||
    altNames: ['foo.example.com', 'bar.example.com']
 | 
			
		||||
    altNames: ['foo.example.com', 'bar.example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
Certificate Signing Request with additional information
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: 'test.example.com',
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
    country: 'US',
 | 
			
		||||
    state: 'California',
 | 
			
		||||
    locality: 'Los Angeles',
 | 
			
		||||
    organization: 'The Company Inc.',
 | 
			
		||||
    organizationUnit: 'IT Department',
 | 
			
		||||
    emailAddress: 'contact@example.com'
 | 
			
		||||
    emailAddress: 'contact@example.com',
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
| 
						 | 
				
			
			@ -270,8 +271,9 @@ Certificate Signing Request with ECDSA private key
 | 
			
		|||
const certificateKey = await acme.crypto.createPrivateEcdsaKey();
 | 
			
		||||
 | 
			
		||||
const [, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
}, certificateKey);
 | 
			
		||||
```
 | 
			
		||||
<a name="createAlpnCertificate"></a>
 | 
			
		||||
 | 
			
		||||
## createAlpnCertificate(authz, keyAuthorization, [keyPem]) ⇒ <code>Promise.<Array.<buffer>></code>
 | 
			
		||||
| 
						 | 
				
			
			@ -298,6 +300,7 @@ Create a ALPN certificate with ECDSA private key
 | 
			
		|||
```js
 | 
			
		||||
const alpnKey = await acme.crypto.createPrivateEcdsaKey();
 | 
			
		||||
const [, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization, alpnKey);
 | 
			
		||||
```
 | 
			
		||||
<a name="isAlpnCertificateAuthorizationValid"></a>
 | 
			
		||||
 | 
			
		||||
## isAlpnCertificateAuthorizationValid(certPem, keyAuthorization) ⇒ <code>boolean</code>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -222,29 +222,30 @@ Create a Certificate Signing Request
 | 
			
		|||
Create a Certificate Signing Request
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
Certificate Signing Request with both common and alternative names
 | 
			
		||||
> *Warning*: Certificate subject common name has been [deprecated](https://letsencrypt.org/docs/glossary/#def-CN) and its use is [discouraged](https://cabforum.org/uploads/BRv1.2.3.pdf).
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
    keySize: 4096,
 | 
			
		||||
    commonName: 'test.example.com',
 | 
			
		||||
    altNames: ['foo.example.com', 'bar.example.com']
 | 
			
		||||
    altNames: ['foo.example.com', 'bar.example.com'],
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
Certificate Signing Request with additional information
 | 
			
		||||
```js
 | 
			
		||||
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
    commonName: 'test.example.com',
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
    country: 'US',
 | 
			
		||||
    state: 'California',
 | 
			
		||||
    locality: 'Los Angeles',
 | 
			
		||||
    organization: 'The Company Inc.',
 | 
			
		||||
    organizationUnit: 'IT Department',
 | 
			
		||||
    emailAddress: 'contact@example.com'
 | 
			
		||||
    emailAddress: 'contact@example.com',
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
**Example**  
 | 
			
		||||
| 
						 | 
				
			
			@ -253,5 +254,5 @@ Certificate Signing Request with predefined private key
 | 
			
		|||
const certificateKey = await acme.forge.createPrivateKey();
 | 
			
		||||
 | 
			
		||||
const [, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
    commonName: 'test.example.com'
 | 
			
		||||
    altNames: ['test.example.com'],
 | 
			
		||||
}, certificateKey);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ function log(m) {
 | 
			
		|||
    process.stdout.write(`${m}\n`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function used to satisfy an ACME challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +24,6 @@ async function challengeCreateFn(authz, challenge, keyAuthorization) {
 | 
			
		|||
    log(keyAuthorization);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function used to remove an ACME challenge response
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -41,30 +39,29 @@ async function challengeRemoveFn(authz, challenge, keyAuthorization) {
 | 
			
		|||
    log(keyAuthorization);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
module.exports = async function() {
 | 
			
		||||
module.exports = async () => {
 | 
			
		||||
    /* Init client */
 | 
			
		||||
    const client = new acme.Client({
 | 
			
		||||
        directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
        accountKey: await acme.crypto.createPrivateKey()
 | 
			
		||||
        accountKey: await acme.crypto.createPrivateKey(),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Register account */
 | 
			
		||||
    await client.createAccount({
 | 
			
		||||
        termsOfServiceAgreed: true,
 | 
			
		||||
        contact: ['mailto:test@example.com']
 | 
			
		||||
        contact: ['mailto:test@example.com'],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Place new order */
 | 
			
		||||
    const order = await client.createOrder({
 | 
			
		||||
        identifiers: [
 | 
			
		||||
            { type: 'dns', value: 'example.com' },
 | 
			
		||||
            { type: 'dns', value: '*.example.com' }
 | 
			
		||||
        ]
 | 
			
		||||
            { type: 'dns', value: '*.example.com' },
 | 
			
		||||
        ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			@ -138,8 +135,7 @@ module.exports = async function() {
 | 
			
		|||
 | 
			
		||||
    /* Finalize order */
 | 
			
		||||
    const [key, csr] = await acme.crypto.createCsr({
 | 
			
		||||
        commonName: '*.example.com',
 | 
			
		||||
        altNames: ['example.com']
 | 
			
		||||
        altNames: ['example.com', '*.example.com'],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const finalized = await client.finalizeOrder(order, csr);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ function log(m) {
 | 
			
		|||
    process.stdout.write(`${m}\n`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function used to satisfy an ACME challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +46,6 @@ async function challengeCreateFn(authz, challenge, keyAuthorization) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Function used to remove an ACME challenge response
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -80,25 +78,24 @@ async function challengeRemoveFn(authz, challenge, keyAuthorization) {
 | 
			
		|||
 | 
			
		||||
        /* Replace this */
 | 
			
		||||
        log(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
 | 
			
		||||
        // await dnsProvider.removeRecord(dnsRecord, 'TXT');
 | 
			
		||||
        // await dnsProvider.removeRecord(dnsRecord, 'TXT', recordValue);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
module.exports = async function() {
 | 
			
		||||
module.exports = async () => {
 | 
			
		||||
    /* Init client */
 | 
			
		||||
    const client = new acme.Client({
 | 
			
		||||
        directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
        accountKey: await acme.crypto.createPrivateKey()
 | 
			
		||||
        accountKey: await acme.crypto.createPrivateKey(),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Create CSR */
 | 
			
		||||
    const [key, csr] = await acme.crypto.createCsr({
 | 
			
		||||
        commonName: 'example.com'
 | 
			
		||||
        altNames: ['example.com'],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Certificate */
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +104,7 @@ module.exports = async function() {
 | 
			
		|||
        email: 'test@example.com',
 | 
			
		||||
        termsOfServiceAgreed: true,
 | 
			
		||||
        challengeCreateFn,
 | 
			
		||||
        challengeRemoveFn
 | 
			
		||||
        challengeRemoveFn,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Done */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,6 @@ function log(m) {
 | 
			
		|||
    process.stdout.write(`${(new Date()).toISOString()} ${m}\n`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -33,18 +32,16 @@ function log(m) {
 | 
			
		|||
        log('Initializing ACME client');
 | 
			
		||||
        const client = new acme.Client({
 | 
			
		||||
            directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey()
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey(),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Order wildcard certificate
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        log(`Creating CSR for ${WILDCARD_DOMAIN}`);
 | 
			
		||||
        const [key, csr] = await acme.crypto.createCsr({
 | 
			
		||||
            commonName: WILDCARD_DOMAIN,
 | 
			
		||||
            altNames: [`*.${WILDCARD_DOMAIN}`]
 | 
			
		||||
            altNames: [WILDCARD_DOMAIN, `*.${WILDCARD_DOMAIN}`],
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        log(`Ordering certificate for ${WILDCARD_DOMAIN}`);
 | 
			
		||||
| 
						 | 
				
			
			@ -60,12 +57,11 @@ function log(m) {
 | 
			
		|||
            challengeRemoveFn: (authz, challenge, keyAuthorization) => {
 | 
			
		||||
                /* TODO: Implement this */
 | 
			
		||||
                log(`[TODO] Remove TXT record key=_acme-challenge.${authz.identifier.value} value=${keyAuthorization}`);
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        log(`Certificate for ${WILDCARD_DOMAIN} created successfully`);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * HTTPS server
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +74,7 @@ function log(m) {
 | 
			
		|||
 | 
			
		||||
        const httpsServer = https.createServer({
 | 
			
		||||
            key,
 | 
			
		||||
            cert
 | 
			
		||||
            cert,
 | 
			
		||||
        }, requestListener);
 | 
			
		||||
 | 
			
		||||
        httpsServer.listen(HTTPS_SERVER_PORT, () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@ function log(m) {
 | 
			
		|||
    process.stdout.write(`${(new Date()).toISOString()} ${m}\n`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * On-demand certificate generation using http-01
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +51,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
    /* Create CSR */
 | 
			
		||||
    log(`Creating CSR for ${servername}`);
 | 
			
		||||
    const [key, csr] = await acme.crypto.createCsr({
 | 
			
		||||
        commonName: servername
 | 
			
		||||
        altNames: [servername],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Order certificate */
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +66,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
        },
 | 
			
		||||
        challengeRemoveFn: (authz, challenge) => {
 | 
			
		||||
            delete challengeResponses[challenge.token];
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Done, store certificate */
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +76,6 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
    return certificateStore[servername];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -91,10 +89,9 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
        log('Initializing ACME client');
 | 
			
		||||
        const client = new acme.Client({
 | 
			
		||||
            directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey()
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey(),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * HTTP server
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +126,6 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
            log(`HTTP server listening on port ${HTTP_SERVER_PORT}`);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * HTTPS server
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -158,7 +154,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
                    log(`[ERROR] ${e.message}`);
 | 
			
		||||
                    cb(e.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
        }, requestListener);
 | 
			
		||||
 | 
			
		||||
        httpsServer.listen(HTTPS_SERVER_PORT, () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@ function log(m) {
 | 
			
		|||
    process.stdout.write(`${(new Date()).toISOString()} ${m}\n`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * On-demand certificate generation using tls-alpn-01
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +50,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
    /* Create CSR */
 | 
			
		||||
    log(`Creating CSR for ${servername}`);
 | 
			
		||||
    const [key, csr] = await acme.crypto.createCsr({
 | 
			
		||||
        commonName: servername
 | 
			
		||||
        altNames: [servername],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Order certificate */
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +65,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
        },
 | 
			
		||||
        challengeRemoveFn: (authz) => {
 | 
			
		||||
            delete alpnResponses[authz.identifier.value];
 | 
			
		||||
        }
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Done, store certificate */
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +75,6 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
    return certificateStore[servername];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -90,10 +88,9 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
        log('Initializing ACME client');
 | 
			
		||||
        const client = new acme.Client({
 | 
			
		||||
            directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey()
 | 
			
		||||
            accountKey: await acme.crypto.createPrivateKey(),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * ALPN responder
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -118,14 +115,14 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
                    log(`Found ALPN certificate for ${servername}, serving secure context`);
 | 
			
		||||
                    cb(null, tls.createSecureContext({
 | 
			
		||||
                        key: alpnResponses[servername][0],
 | 
			
		||||
                        cert: alpnResponses[servername][1]
 | 
			
		||||
                        cert: alpnResponses[servername][1],
 | 
			
		||||
                    }));
 | 
			
		||||
                }
 | 
			
		||||
                catch (e) {
 | 
			
		||||
                    log(`[ERROR] ${e.message}`);
 | 
			
		||||
                    cb(e.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /* Terminate once TLS handshake has been established */
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +134,6 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
            log(`ALPN responder listening on port ${ALPN_RESPONDER_PORT}`);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * HTTPS server
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -166,7 +162,7 @@ async function getCertOnDemand(client, servername, attempt = 0) {
 | 
			
		|||
                    log(`[ERROR] ${e.message}`);
 | 
			
		||||
                    cb(e.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
        }, requestListener);
 | 
			
		||||
 | 
			
		||||
        httpsServer.listen(HTTPS_SERVER_PORT, () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,24 +16,24 @@
 | 
			
		|||
        "types"
 | 
			
		||||
    ],
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@peculiar/x509": "^1.9.7",
 | 
			
		||||
        "@peculiar/x509": "^1.10.0",
 | 
			
		||||
        "asn1js": "^3.0.5",
 | 
			
		||||
        "axios": "^1.6.5",
 | 
			
		||||
        "axios": "^1.7.2",
 | 
			
		||||
        "debug": "^4.1.1",
 | 
			
		||||
        "https-proxy-agent": "^7.0.4",
 | 
			
		||||
        "node-forge": "^1.3.1"
 | 
			
		||||
    },
 | 
			
		||||
    "devDependencies": {
 | 
			
		||||
        "@types/node": "^20.11.5",
 | 
			
		||||
        "@types/node": "^20.12.12",
 | 
			
		||||
        "chai": "^4.4.1",
 | 
			
		||||
        "chai-as-promised": "^7.1.1",
 | 
			
		||||
        "eslint": "^8.56.0",
 | 
			
		||||
        "chai-as-promised": "^7.1.2",
 | 
			
		||||
        "eslint": "^8.57.0",
 | 
			
		||||
        "eslint-config-airbnb-base": "^15.0.0",
 | 
			
		||||
        "eslint-plugin-import": "^2.29.1",
 | 
			
		||||
        "jsdoc-to-markdown": "^8.0.0",
 | 
			
		||||
        "mocha": "^10.2.0",
 | 
			
		||||
        "nock": "^13.5.0",
 | 
			
		||||
        "tsd": "^0.30.4",
 | 
			
		||||
        "jsdoc-to-markdown": "^8.0.1",
 | 
			
		||||
        "mocha": "^10.4.0",
 | 
			
		||||
        "nock": "^13.5.4",
 | 
			
		||||
        "tsd": "^0.31.0",
 | 
			
		||||
        "typescript": "^4.8.4",
 | 
			
		||||
        "uuid": "^8.3.2"
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@
 | 
			
		|||
 | 
			
		||||
const util = require('./util');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * AcmeApi
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +17,6 @@ class AcmeApi {
 | 
			
		|||
        this.accountUrl = accountUrl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get account URL
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +32,6 @@ class AcmeApi {
 | 
			
		|||
        return this.accountUrl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ACME API request
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +56,6 @@ class AcmeApi {
 | 
			
		|||
        return resp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ACME API request by resource name helper
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +74,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(resourceUrl, payload, validStatusCodes, { includeJwsKid, includeExternalAccountBinding });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get Terms of Service URL if available
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -91,7 +86,6 @@ class AcmeApi {
 | 
			
		|||
        return this.http.getMetaField('termsOfService');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create new account
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +98,7 @@ class AcmeApi {
 | 
			
		|||
    async createAccount(data) {
 | 
			
		||||
        const resp = await this.apiResourceRequest('newAccount', data, [200, 201], {
 | 
			
		||||
            includeJwsKid: false,
 | 
			
		||||
            includeExternalAccountBinding: (data.onlyReturnExisting !== true)
 | 
			
		||||
            includeExternalAccountBinding: (data.onlyReturnExisting !== true),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        /* Set account URL */
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +109,6 @@ class AcmeApi {
 | 
			
		|||
        return resp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update account
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +122,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(this.getAccountUrl(), data, [200, 202]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update account key
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +135,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiResourceRequest('keyChange', data, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create new order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +148,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiResourceRequest('newOrder', data, [201]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +161,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(url, null, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Finalize order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +175,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(url, data, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get identifier authorization
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +188,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(url, null, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update identifier authorization
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +202,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(url, data, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Complete challenge
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +216,6 @@ class AcmeApi {
 | 
			
		|||
        return this.apiRequest(url, data, [200]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Revoke certificate
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -245,6 +230,5 @@ class AcmeApi {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Export API */
 | 
			
		||||
module.exports = AcmeApi;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,10 +14,9 @@ const defaultOpts = {
 | 
			
		|||
    skipChallengeVerification: false,
 | 
			
		||||
    challengePriority: ['http-01', 'dns-01'],
 | 
			
		||||
    challengeCreateFn: async () => { throw new Error('Missing challengeCreateFn()'); },
 | 
			
		||||
    challengeRemoveFn: async () => { throw new Error('Missing challengeRemoveFn()'); }
 | 
			
		||||
    challengeRemoveFn: async () => { throw new Error('Missing challengeRemoveFn()'); },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ACME client auto mode
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -26,8 +25,8 @@ const defaultOpts = {
 | 
			
		|||
 * @returns {Promise<buffer>} Certificate
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
module.exports = async function(client, userOpts) {
 | 
			
		||||
    const opts = Object.assign({}, defaultOpts, userOpts);
 | 
			
		||||
module.exports = async (client, userOpts) => {
 | 
			
		||||
    const opts = { ...defaultOpts, ...userOpts };
 | 
			
		||||
    const accountPayload = { termsOfServiceAgreed: opts.termsOfServiceAgreed };
 | 
			
		||||
 | 
			
		||||
    if (!Buffer.isBuffer(opts.csr)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +37,6 @@ module.exports = async function(client, userOpts) {
 | 
			
		|||
        accountPayload.contact = [`mailto:${opts.email}`];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Register account
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +52,6 @@ module.exports = async function(client, userOpts) {
 | 
			
		|||
        await client.createAccount(accountPayload);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parse domains from CSR
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +62,6 @@ module.exports = async function(client, userOpts) {
 | 
			
		|||
 | 
			
		||||
    log(`[auto] Resolved ${uniqueDomains.length} unique domains from parsing the Certificate Signing Request`);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Place order
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +73,6 @@ module.exports = async function(client, userOpts) {
 | 
			
		|||
 | 
			
		||||
    log(`[auto] Placed certificate order successfully, received ${authorizations.length} identity authorizations`);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolve and satisfy challenges
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +191,6 @@ module.exports = async function(client, userOpts) {
 | 
			
		|||
        return Promise.all(results);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        log('开始challenge');
 | 
			
		||||
        await runPromisePa(challengePromises);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@
 | 
			
		|||
const axios = require('axios');
 | 
			
		||||
const pkg = require('./../package.json');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Instance
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -19,9 +18,8 @@ instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version
 | 
			
		|||
instance.defaults.acmeSettings = {
 | 
			
		||||
    httpChallengePort: 80,
 | 
			
		||||
    httpsChallengePort: 443,
 | 
			
		||||
    tlsAlpnChallengePort: 443
 | 
			
		||||
    tlsAlpnChallengePort: 443,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// instance.defaults.proxy = {
 | 
			
		||||
//     host: '192.168.34.139',
 | 
			
		||||
//     port: 10811
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +33,6 @@ instance.defaults.acmeSettings = {
 | 
			
		|||
 | 
			
		||||
instance.defaults.adapter = 'http';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Export instance
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,6 @@ const verify = require('./verify');
 | 
			
		|||
const util = require('./util');
 | 
			
		||||
const auto = require('./auto');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ACME states
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +23,6 @@ const validStates = ['ready', 'valid'];
 | 
			
		|||
const pendingStates = ['pending', 'processing'];
 | 
			
		||||
const invalidStates = ['invalid'];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Default options
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -38,10 +36,9 @@ const defaultOpts = {
 | 
			
		|||
    externalAccountBinding: {},
 | 
			
		||||
    backoffAttempts: 10,
 | 
			
		||||
    backoffMin: 5000,
 | 
			
		||||
    backoffMax: 30000
 | 
			
		||||
    backoffMax: 30000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * AcmeClient
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +58,7 @@ const defaultOpts = {
 | 
			
		|||
 * ```js
 | 
			
		||||
 * const client = new acme.Client({
 | 
			
		||||
 *     directoryUrl: acme.directory.letsencrypt.staging,
 | 
			
		||||
 *     accountKey: 'Private key goes here'
 | 
			
		||||
 *     accountKey: 'Private key goes here',
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +70,7 @@ const defaultOpts = {
 | 
			
		|||
 *     accountUrl: 'Optional account URL goes here',
 | 
			
		||||
 *     backoffAttempts: 10,
 | 
			
		||||
 *     backoffMin: 5000,
 | 
			
		||||
 *     backoffMax: 30000
 | 
			
		||||
 *     backoffMax: 30000,
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +81,8 @@ const defaultOpts = {
 | 
			
		|||
 *     accountKey: 'Private key goes here',
 | 
			
		||||
 *     externalAccountBinding: {
 | 
			
		||||
 *         kid: 'YOUR-EAB-KID',
 | 
			
		||||
 *         hmacKey: 'YOUR-EAB-HMAC-KEY'
 | 
			
		||||
 *     }
 | 
			
		||||
 *         hmacKey: 'YOUR-EAB-HMAC-KEY',
 | 
			
		||||
 *     },
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -96,19 +93,17 @@ class AcmeClient {
 | 
			
		|||
            opts.accountKey = Buffer.from(opts.accountKey);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.opts = Object.assign({}, defaultOpts, opts);
 | 
			
		||||
 | 
			
		||||
        this.opts = { ...defaultOpts, ...opts };
 | 
			
		||||
        this.backoffOpts = {
 | 
			
		||||
            attempts: this.opts.backoffAttempts,
 | 
			
		||||
            min: this.opts.backoffMin,
 | 
			
		||||
            max: this.opts.backoffMax
 | 
			
		||||
            max: this.opts.backoffMax,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding);
 | 
			
		||||
        this.api = new AcmeApi(this.http, this.opts.accountUrl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get Terms of Service URL if available
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +123,6 @@ class AcmeClient {
 | 
			
		|||
        return this.api.getTermsOfServiceUrl();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get current account URL
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +144,6 @@ class AcmeClient {
 | 
			
		|||
        return this.api.getAccountUrl();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new account
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +155,7 @@ class AcmeClient {
 | 
			
		|||
     * @example Create a new account
 | 
			
		||||
     * ```js
 | 
			
		||||
     * const account = await client.createAccount({
 | 
			
		||||
     *     termsOfServiceAgreed: true
 | 
			
		||||
     *     termsOfServiceAgreed: true,
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +163,7 @@ class AcmeClient {
 | 
			
		|||
     * ```js
 | 
			
		||||
     * const account = await client.createAccount({
 | 
			
		||||
     *     termsOfServiceAgreed: true,
 | 
			
		||||
     *     contact: ['mailto:test@example.com']
 | 
			
		||||
     *     contact: ['mailto:test@example.com'],
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +189,6 @@ class AcmeClient {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update existing account
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -208,7 +200,7 @@ class AcmeClient {
 | 
			
		|||
     * @example Update existing account
 | 
			
		||||
     * ```js
 | 
			
		||||
     * const account = await client.updateAccount({
 | 
			
		||||
     *     contact: ['mailto:foo@example.com']
 | 
			
		||||
     *     contact: ['mailto:foo@example.com'],
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -236,7 +228,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update account private key
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +273,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a new order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -296,8 +286,8 @@ class AcmeClient {
 | 
			
		|||
     * const order = await client.createOrder({
 | 
			
		||||
     *     identifiers: [
 | 
			
		||||
     *         { type: 'dns', value: 'example.com' },
 | 
			
		||||
     *         { type: 'dns', value: 'test.example.com' }
 | 
			
		||||
     *     ]
 | 
			
		||||
     *         { type: 'dns', value: 'test.example.com' },
 | 
			
		||||
     *     ],
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +304,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh order object from CA
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -376,7 +365,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get identifier authorizations from order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -406,7 +394,6 @@ class AcmeClient {
 | 
			
		|||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Deactivate identifier authorization
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -427,10 +414,7 @@ class AcmeClient {
 | 
			
		|||
            throw new Error('Unable to deactivate identifier authorization, URL not found');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const data = {
 | 
			
		||||
            status: 'deactivated'
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const data = { status: 'deactivated' };
 | 
			
		||||
        const resp = await this.api.updateAuthorization(authz.url, data);
 | 
			
		||||
 | 
			
		||||
        /* Add URL to response */
 | 
			
		||||
| 
						 | 
				
			
			@ -438,7 +422,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get key authorization for ACME challenge
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -480,7 +463,6 @@ class AcmeClient {
 | 
			
		|||
        throw new Error(`Unable to produce key authorization, unknown challenge type: ${challenge.type}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Verify that ACME challenge is satisfied
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -515,7 +497,6 @@ class AcmeClient {
 | 
			
		|||
        return util.retry(verifyFn, this.backoffOpts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify CA that challenge has been completed
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -536,7 +517,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Wait for ACME provider to verify status on a order, authorization or challenge
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +573,6 @@ class AcmeClient {
 | 
			
		|||
        return util.retry(verifyFn, this.backoffOpts);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get certificate from ACME order
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -640,7 +619,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Revoke certificate
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -660,7 +638,7 @@ class AcmeClient {
 | 
			
		|||
     * ```js
 | 
			
		||||
     * const certificate = { ... }; // Previously created certificate
 | 
			
		||||
     * const result = await client.revokeCertificate(certificate, {
 | 
			
		||||
     *     reason: 4
 | 
			
		||||
     *     reason: 4,
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -671,7 +649,6 @@ class AcmeClient {
 | 
			
		|||
        return resp.data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Auto mode
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -689,7 +666,7 @@ class AcmeClient {
 | 
			
		|||
     * @example Order a certificate using auto mode
 | 
			
		||||
     * ```js
 | 
			
		||||
     * const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
     *     commonName: 'test.example.com'
 | 
			
		||||
     *     altNames: ['test.example.com'],
 | 
			
		||||
     * });
 | 
			
		||||
     *
 | 
			
		||||
     * const certificate = await client.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -701,14 +678,14 @@ class AcmeClient {
 | 
			
		|||
     *     },
 | 
			
		||||
     *     challengeRemoveFn: async (authz, challenge, keyAuthorization) => {
 | 
			
		||||
     *         // Clean up challenge here
 | 
			
		||||
     *     }
 | 
			
		||||
     *     },
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     *
 | 
			
		||||
     * @example Order a certificate using auto mode with preferred chain
 | 
			
		||||
     * ```js
 | 
			
		||||
     * const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
     *     commonName: 'test.example.com'
 | 
			
		||||
     *     altNames: ['test.example.com'],
 | 
			
		||||
     * });
 | 
			
		||||
     *
 | 
			
		||||
     * const certificate = await client.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -717,7 +694,7 @@ class AcmeClient {
 | 
			
		|||
     *     termsOfServiceAgreed: true,
 | 
			
		||||
     *     preferredChain: 'DST Root CA X3',
 | 
			
		||||
     *     challengeCreateFn: async () => {},
 | 
			
		||||
     *     challengeRemoveFn: async () => {}
 | 
			
		||||
     *     challengeRemoveFn: async () => {},
 | 
			
		||||
     * });
 | 
			
		||||
     * ```
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -727,6 +704,5 @@ class AcmeClient {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Export client */
 | 
			
		||||
module.exports = AcmeClient;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,6 @@ const forge = require('node-forge');
 | 
			
		|||
 | 
			
		||||
const generateKeyPair = promisify(forge.pki.rsa.generateKeyPair);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Attempt to parse forge object from PEM encoded string
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +53,6 @@ function forgeObjectFromPem(input) {
 | 
			
		|||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse domain names from a certificate or CSR
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -93,11 +91,10 @@ function parseDomains(obj) {
 | 
			
		|||
 | 
			
		||||
    return {
 | 
			
		||||
        commonName,
 | 
			
		||||
        altNames
 | 
			
		||||
        altNames,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a private RSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +120,6 @@ async function createPrivateKey(size = 2048) {
 | 
			
		|||
 | 
			
		||||
exports.createPrivateKey = createPrivateKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create public key from a private RSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -136,14 +132,13 @@ exports.createPrivateKey = createPrivateKey;
 | 
			
		|||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.createPublicKey = async function(key) {
 | 
			
		||||
exports.createPublicKey = async (key) => {
 | 
			
		||||
    const privateKey = forge.pki.privateKeyFromPem(key);
 | 
			
		||||
    const publicKey = forge.pki.rsa.setPublicKey(privateKey.n, privateKey.e);
 | 
			
		||||
    const pemKey = forge.pki.publicKeyToPem(publicKey);
 | 
			
		||||
    return Buffer.from(pemKey);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse body of PEM encoded object from buffer or string
 | 
			
		||||
 * If multiple objects are chained, the first body will be returned
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +152,6 @@ exports.getPemBody = (str) => {
 | 
			
		|||
    return forge.util.encode64(msg.body);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Split chain of PEM encoded objects from buffer or string into array
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +161,6 @@ exports.getPemBody = (str) => {
 | 
			
		|||
 | 
			
		||||
exports.splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get modulus
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -182,7 +175,7 @@ exports.splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode);
 | 
			
		|||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.getModulus = async function(input) {
 | 
			
		||||
exports.getModulus = async (input) => {
 | 
			
		||||
    if (!Buffer.isBuffer(input)) {
 | 
			
		||||
        input = Buffer.from(input);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +184,6 @@ exports.getModulus = async function(input) {
 | 
			
		|||
    return Buffer.from(forge.util.hexToBytes(obj.n.toString(16)), 'binary');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get public exponent
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -206,7 +198,7 @@ exports.getModulus = async function(input) {
 | 
			
		|||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.getPublicExponent = async function(input) {
 | 
			
		||||
exports.getPublicExponent = async (input) => {
 | 
			
		||||
    if (!Buffer.isBuffer(input)) {
 | 
			
		||||
        input = Buffer.from(input);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +207,6 @@ exports.getPublicExponent = async function(input) {
 | 
			
		|||
    return Buffer.from(forge.util.hexToBytes(obj.e.toString(16)), 'binary');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read domains from a Certificate Signing Request
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -231,7 +222,7 @@ exports.getPublicExponent = async function(input) {
 | 
			
		|||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.readCsrDomains = async function(csr) {
 | 
			
		||||
exports.readCsrDomains = async (csr) => {
 | 
			
		||||
    if (!Buffer.isBuffer(csr)) {
 | 
			
		||||
        csr = Buffer.from(csr);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -240,7 +231,6 @@ exports.readCsrDomains = async function(csr) {
 | 
			
		|||
    return parseDomains(obj);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read information from a certificate
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +250,7 @@ exports.readCsrDomains = async function(csr) {
 | 
			
		|||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.readCertificateInfo = async function(cert) {
 | 
			
		||||
exports.readCertificateInfo = async (cert) => {
 | 
			
		||||
    if (!Buffer.isBuffer(cert)) {
 | 
			
		||||
        cert = Buffer.from(cert);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -270,15 +260,14 @@ exports.readCertificateInfo = async function(cert) {
 | 
			
		|||
 | 
			
		||||
    return {
 | 
			
		||||
        issuer: {
 | 
			
		||||
            commonName: issuerCn ? issuerCn.value : null
 | 
			
		||||
            commonName: issuerCn ? issuerCn.value : null,
 | 
			
		||||
        },
 | 
			
		||||
        domains: parseDomains(obj),
 | 
			
		||||
        notAfter: obj.validity.notAfter,
 | 
			
		||||
        notBefore: obj.validity.notBefore
 | 
			
		||||
        notBefore: obj.validity.notBefore,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determine ASN.1 type for CSR subject short name
 | 
			
		||||
 * Note: https://datatracker.ietf.org/doc/html/rfc5280
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +288,6 @@ function getCsrValueTagClass(shortName) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create array of short names and values for Certificate Signing Request subjects
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +307,6 @@ function createCsrSubject(subjectObj) {
 | 
			
		|||
    }, []);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create array of alt names for Certificate Signing Requests
 | 
			
		||||
 * Note: https://github.com/digitalbazaar/forge/blob/dfdde475677a8a25c851e33e8f81dca60d90cfb9/lib/x509.js#L1444-L1454
 | 
			
		||||
| 
						 | 
				
			
			@ -336,7 +323,6 @@ function formatCsrAltNames(altNames) {
 | 
			
		|||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a Certificate Signing Request
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -356,29 +342,30 @@ function formatCsrAltNames(altNames) {
 | 
			
		|||
 * @example Create a Certificate Signing Request
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com'
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * @example Certificate Signing Request with both common and alternative names
 | 
			
		||||
 * > *Warning*: Certificate subject common name has been [deprecated](https://letsencrypt.org/docs/glossary/#def-CN) and its use is [discouraged](https://cabforum.org/uploads/BRv1.2.3.pdf).
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
 *     keySize: 4096,
 | 
			
		||||
 *     commonName: 'test.example.com',
 | 
			
		||||
 *     altNames: ['foo.example.com', 'bar.example.com']
 | 
			
		||||
 *     altNames: ['foo.example.com', 'bar.example.com'],
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * @example Certificate Signing Request with additional information
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com',
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 *     country: 'US',
 | 
			
		||||
 *     state: 'California',
 | 
			
		||||
 *     locality: 'Los Angeles',
 | 
			
		||||
 *     organization: 'The Company Inc.',
 | 
			
		||||
 *     organizationUnit: 'IT Department',
 | 
			
		||||
 *     emailAddress: 'contact@example.com'
 | 
			
		||||
 *     emailAddress: 'contact@example.com',
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -387,11 +374,11 @@ function formatCsrAltNames(altNames) {
 | 
			
		|||
 * const certificateKey = await acme.forge.createPrivateKey();
 | 
			
		||||
 *
 | 
			
		||||
 * const [, certificateRequest] = await acme.forge.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com'
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 * }, certificateKey);
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.createCsr = async function(data, key = null) {
 | 
			
		||||
exports.createCsr = async (data, key = null) => {
 | 
			
		||||
    if (!key) {
 | 
			
		||||
        key = await createPrivateKey(data.keySize);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -423,7 +410,7 @@ exports.createCsr = async function(data, key = null) {
 | 
			
		|||
        L: data.locality,
 | 
			
		||||
        O: data.organization,
 | 
			
		||||
        OU: data.organizationUnit,
 | 
			
		||||
        E: data.emailAddress
 | 
			
		||||
        E: data.emailAddress,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    csr.setSubject(subject);
 | 
			
		||||
| 
						 | 
				
			
			@ -434,8 +421,8 @@ exports.createCsr = async function(data, key = null) {
 | 
			
		|||
            name: 'extensionRequest',
 | 
			
		||||
            extensions: [{
 | 
			
		||||
                name: 'subjectAltName',
 | 
			
		||||
                altNames: formatCsrAltNames(data.altNames)
 | 
			
		||||
            }]
 | 
			
		||||
                altNames: formatCsrAltNames(data.altNames),
 | 
			
		||||
            }],
 | 
			
		||||
        }]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,6 @@ const subjectAltNameOID = '2.5.29.17';
 | 
			
		|||
/* id-pe-acmeIdentifier - https://datatracker.ietf.org/doc/html/rfc8737#section-6.1 */
 | 
			
		||||
const alpnAcmeIdentifierOID = '1.3.6.1.5.5.7.1.31';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determine key type and info by attempting to derive public key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +34,7 @@ function getKeyInfo(keyPem) {
 | 
			
		|||
    const result = {
 | 
			
		||||
        isRSA: false,
 | 
			
		||||
        isECDSA: false,
 | 
			
		||||
        publicKey: crypto.createPublicKey(keyPem)
 | 
			
		||||
        publicKey: crypto.createPublicKey(keyPem),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (result.publicKey.asymmetricKeyType === 'rsa') {
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +50,6 @@ function getKeyInfo(keyPem) {
 | 
			
		|||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a private RSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -74,8 +72,8 @@ async function createPrivateRsaKey(modulusLength = 2048) {
 | 
			
		|||
        modulusLength,
 | 
			
		||||
        privateKeyEncoding: {
 | 
			
		||||
            type: 'pkcs8',
 | 
			
		||||
            format: 'pem'
 | 
			
		||||
        }
 | 
			
		||||
            format: 'pem',
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return Buffer.from(pair.privateKey);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +81,6 @@ async function createPrivateRsaKey(modulusLength = 2048) {
 | 
			
		|||
 | 
			
		||||
exports.createPrivateRsaKey = createPrivateRsaKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Alias of `createPrivateRsaKey()`
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +89,6 @@ exports.createPrivateRsaKey = createPrivateRsaKey;
 | 
			
		|||
 | 
			
		||||
exports.createPrivateKey = createPrivateRsaKey;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a private ECDSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -115,14 +111,13 @@ exports.createPrivateEcdsaKey = async (namedCurve = 'P-256') => {
 | 
			
		|||
        namedCurve,
 | 
			
		||||
        privateKeyEncoding: {
 | 
			
		||||
            type: 'pkcs8',
 | 
			
		||||
            format: 'pem'
 | 
			
		||||
        }
 | 
			
		||||
            format: 'pem',
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return Buffer.from(pair.privateKey);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get a public key derived from a RSA or ECDSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -140,13 +135,12 @@ exports.getPublicKey = (keyPem) => {
 | 
			
		|||
 | 
			
		||||
    const publicKey = info.publicKey.export({
 | 
			
		||||
        type: info.isECDSA ? 'spki' : 'pkcs1',
 | 
			
		||||
        format: 'pem'
 | 
			
		||||
        format: 'pem',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return Buffer.from(publicKey);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get a JSON Web Key derived from a RSA or ECDSA key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +157,7 @@ exports.getPublicKey = (keyPem) => {
 | 
			
		|||
 | 
			
		||||
function getJwk(keyPem) {
 | 
			
		||||
    const jwk = crypto.createPublicKey(keyPem).export({
 | 
			
		||||
        format: 'jwk'
 | 
			
		||||
        format: 'jwk',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Sort keys */
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +169,6 @@ function getJwk(keyPem) {
 | 
			
		|||
 | 
			
		||||
exports.getJwk = getJwk;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Produce CryptoKeyPair and signing algorithm from a PEM encoded private key
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +184,7 @@ async function getWebCryptoKeyPair(keyPem) {
 | 
			
		|||
    /* Signing algorithm */
 | 
			
		||||
    const sigalg = {
 | 
			
		||||
        name: 'RSASSA-PKCS1-v1_5',
 | 
			
		||||
        hash: { name: 'SHA-256' }
 | 
			
		||||
        hash: { name: 'SHA-256' },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (info.isECDSA) {
 | 
			
		||||
| 
						 | 
				
			
			@ -215,7 +208,6 @@ async function getWebCryptoKeyPair(keyPem) {
 | 
			
		|||
    return [{ privateKey, publicKey }, sigalg];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Split chain of PEM encoded objects from string into array
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +227,6 @@ function splitPemChain(chainPem) {
 | 
			
		|||
 | 
			
		||||
exports.splitPemChain = splitPemChain;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse body of PEM encoded object and return a Base64URL string
 | 
			
		||||
 * If multiple objects are chained, the first body will be returned
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +247,6 @@ exports.getPemBodyAsB64u = (pem) => {
 | 
			
		|||
    return Buffer.from(dec).toString('base64url');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse domains from a certificate or CSR
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -277,11 +267,10 @@ function parseDomains(input) {
 | 
			
		|||
 | 
			
		||||
    return {
 | 
			
		||||
        commonName,
 | 
			
		||||
        altNames
 | 
			
		||||
        altNames,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read domains from a Certificate Signing Request
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -307,7 +296,6 @@ exports.readCsrDomains = (csrPem) => {
 | 
			
		|||
    return parseDomains(csr);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Read information from a certificate
 | 
			
		||||
 * If multiple certificates are chained, the first will be read
 | 
			
		||||
| 
						 | 
				
			
			@ -338,15 +326,14 @@ exports.readCertificateInfo = (certPem) => {
 | 
			
		|||
 | 
			
		||||
    return {
 | 
			
		||||
        issuer: {
 | 
			
		||||
            commonName: cert.issuerName.getField('CN').pop() || null
 | 
			
		||||
            commonName: cert.issuerName.getField('CN').pop() || null,
 | 
			
		||||
        },
 | 
			
		||||
        domains: parseDomains(cert),
 | 
			
		||||
        notBefore: cert.notBefore,
 | 
			
		||||
        notAfter: cert.notAfter
 | 
			
		||||
        notAfter: cert.notAfter,
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Determine ASN.1 character string type for CSR subject field name
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -369,7 +356,6 @@ function getCsrAsn1CharStringType(field) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create array of subject fields for a Certificate Signing Request
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -391,7 +377,6 @@ function createCsrSubject(input) {
 | 
			
		|||
    }, []);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create x509 subject alternate name extension
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -409,7 +394,6 @@ function createSubjectAltNameExtension(altNames) {
 | 
			
		|||
    }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a Certificate Signing Request
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -429,29 +413,30 @@ function createSubjectAltNameExtension(altNames) {
 | 
			
		|||
 * @example Create a Certificate Signing Request
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com'
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * @example Certificate Signing Request with both common and alternative names
 | 
			
		||||
 * > *Warning*: Certificate subject common name has been [deprecated](https://letsencrypt.org/docs/glossary/#def-CN) and its use is [discouraged](https://cabforum.org/uploads/BRv1.2.3.pdf).
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
 *     keySize: 4096,
 | 
			
		||||
 *     commonName: 'test.example.com',
 | 
			
		||||
 *     altNames: ['foo.example.com', 'bar.example.com']
 | 
			
		||||
 *     altNames: ['foo.example.com', 'bar.example.com'],
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
 * @example Certificate Signing Request with additional information
 | 
			
		||||
 * ```js
 | 
			
		||||
 * const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com',
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 *     country: 'US',
 | 
			
		||||
 *     state: 'California',
 | 
			
		||||
 *     locality: 'Los Angeles',
 | 
			
		||||
 *     organization: 'The Company Inc.',
 | 
			
		||||
 *     organizationUnit: 'IT Department',
 | 
			
		||||
 *     emailAddress: 'contact@example.com'
 | 
			
		||||
 *     emailAddress: 'contact@example.com',
 | 
			
		||||
 * });
 | 
			
		||||
 * ```
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -460,8 +445,9 @@ function createSubjectAltNameExtension(altNames) {
 | 
			
		|||
 * const certificateKey = await acme.crypto.createPrivateEcdsaKey();
 | 
			
		||||
 *
 | 
			
		||||
 * const [, certificateRequest] = await acme.crypto.createCsr({
 | 
			
		||||
 *     commonName: 'test.example.com'
 | 
			
		||||
 *     altNames: ['test.example.com'],
 | 
			
		||||
 * }, certificateKey);
 | 
			
		||||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.createCsr = async (data, keyPem = null) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -489,7 +475,7 @@ exports.createCsr = async (data, keyPem = null) => {
 | 
			
		|||
        new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature | x509.KeyUsageFlags.keyEncipherment), // eslint-disable-line no-bitwise
 | 
			
		||||
 | 
			
		||||
        /* https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6 */
 | 
			
		||||
        createSubjectAltNameExtension(data.altNames)
 | 
			
		||||
        createSubjectAltNameExtension(data.altNames),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /* Create CSR */
 | 
			
		||||
| 
						 | 
				
			
			@ -504,8 +490,8 @@ exports.createCsr = async (data, keyPem = null) => {
 | 
			
		|||
            L: data.locality,
 | 
			
		||||
            O: data.organization,
 | 
			
		||||
            OU: data.organizationUnit,
 | 
			
		||||
            E: data.emailAddress
 | 
			
		||||
        })
 | 
			
		||||
            E: data.emailAddress,
 | 
			
		||||
        }),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Done */
 | 
			
		||||
| 
						 | 
				
			
			@ -513,7 +499,6 @@ exports.createCsr = async (data, keyPem = null) => {
 | 
			
		|||
    return [keyPem, Buffer.from(pem)];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a self-signed ALPN certificate for TLS-ALPN-01 challenges
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -533,6 +518,7 @@ exports.createCsr = async (data, keyPem = null) => {
 | 
			
		|||
 * ```js
 | 
			
		||||
 * const alpnKey = await acme.crypto.createPrivateEcdsaKey();
 | 
			
		||||
 * const [, alpnCertificate] = await acme.crypto.createAlpnCertificate(authz, keyAuthorization, alpnKey);
 | 
			
		||||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -564,7 +550,7 @@ exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) =
 | 
			
		|||
        await x509.SubjectKeyIdentifierExtension.create(keys.publicKey),
 | 
			
		||||
 | 
			
		||||
        /* https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6 */
 | 
			
		||||
        createSubjectAltNameExtension([commonName])
 | 
			
		||||
        createSubjectAltNameExtension([commonName]),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /* ALPN extension */
 | 
			
		||||
| 
						 | 
				
			
			@ -581,8 +567,8 @@ exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) =
 | 
			
		|||
        notBefore: now,
 | 
			
		||||
        notAfter: now,
 | 
			
		||||
        name: createCsrSubject({
 | 
			
		||||
            CN: commonName
 | 
			
		||||
        })
 | 
			
		||||
            CN: commonName,
 | 
			
		||||
        }),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    /* Done */
 | 
			
		||||
| 
						 | 
				
			
			@ -590,7 +576,6 @@ exports.createAlpnCertificate = async (authz, keyAuthorization, keyPem = null) =
 | 
			
		|||
    return [keyPem, Buffer.from(pem)];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Validate that a ALPN certificate contains the expected key authorization
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,6 @@ class HttpClient {
 | 
			
		|||
        this.jwk = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * HTTP request
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +69,6 @@ class HttpClient {
 | 
			
		|||
        return resp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Ensure provider directory exists
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +93,6 @@ class HttpClient {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get JSON Web Key
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +107,6 @@ class HttpClient {
 | 
			
		|||
        return this.jwk;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get nonce from directory API endpoint
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +126,6 @@ class HttpClient {
 | 
			
		|||
        return resp.headers['replay-nonce'];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get URL for a directory resource
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +143,6 @@ class HttpClient {
 | 
			
		|||
        return this.directory[resource];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get directory meta field
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -166,7 +160,6 @@ class HttpClient {
 | 
			
		|||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepare HTTP request body for signature
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -199,11 +192,10 @@ class HttpClient {
 | 
			
		|||
        /* Body */
 | 
			
		||||
        return {
 | 
			
		||||
            payload: payload ? Buffer.from(JSON.stringify(payload)).toString('base64url') : '',
 | 
			
		||||
            protected: Buffer.from(JSON.stringify(header)).toString('base64url')
 | 
			
		||||
            protected: Buffer.from(JSON.stringify(header)).toString('base64url'),
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create JWS HTTP request body using HMAC
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +218,6 @@ class HttpClient {
 | 
			
		|||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create JWS HTTP request body using RSA or ECC
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -267,13 +258,12 @@ class HttpClient {
 | 
			
		|||
        result.signature = signer.sign({
 | 
			
		||||
            key: this.accountKey,
 | 
			
		||||
            padding: RSA_PKCS1_PADDING,
 | 
			
		||||
            dsaEncoding: 'ieee-p1363'
 | 
			
		||||
            dsaEncoding: 'ieee-p1363',
 | 
			
		||||
        }, 'base64url');
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Signed HTTP request
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -309,7 +299,7 @@ class HttpClient {
 | 
			
		|||
        const data = this.createSignedBody(url, payload, { nonce, kid });
 | 
			
		||||
        const resp = await this.request(url, 'post', { data });
 | 
			
		||||
 | 
			
		||||
        /* Retry on bad nonce - https://datatracker.ietf.org/doc/html/draft-ietf-acme-acme-10#section-6.4 */
 | 
			
		||||
        /* Retry on bad nonce - https://datatracker.ietf.org/doc/html/rfc8555#section-6.5 */
 | 
			
		||||
        if (resp.data && resp.data.type && (resp.status === 400) && (resp.data.type === 'urn:ietf:params:acme:error:badNonce') && (attempts < this.maxBadNonceRetries)) {
 | 
			
		||||
            nonce = resp.headers['replay-nonce'] || null;
 | 
			
		||||
            attempts += 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -323,6 +313,5 @@ class HttpClient {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Export client */
 | 
			
		||||
module.exports = HttpClient;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@
 | 
			
		|||
 | 
			
		||||
exports.Client = require('./client');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directory URLs
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -12,18 +11,17 @@ exports.Client = require('./client');
 | 
			
		|||
exports.directory = {
 | 
			
		||||
    buypass: {
 | 
			
		||||
        staging: 'https://api.test4.buypass.no/acme/directory',
 | 
			
		||||
        production: 'https://api.buypass.com/acme/directory'
 | 
			
		||||
        production: 'https://api.buypass.com/acme/directory',
 | 
			
		||||
    },
 | 
			
		||||
    letsencrypt: {
 | 
			
		||||
        staging: 'https://acme-staging-v02.api.letsencrypt.org/directory',
 | 
			
		||||
        production: 'https://acme-v02.api.letsencrypt.org/directory'
 | 
			
		||||
        production: 'https://acme-v02.api.letsencrypt.org/directory',
 | 
			
		||||
    },
 | 
			
		||||
    zerossl: {
 | 
			
		||||
        production: 'https://acme.zerossl.com/v2/DV90'
 | 
			
		||||
    }
 | 
			
		||||
        production: 'https://acme.zerossl.com/v2/DV90',
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Crypto
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -31,14 +29,12 @@ exports.directory = {
 | 
			
		|||
exports.crypto = require('./crypto');
 | 
			
		||||
exports.forge = require('./crypto/forge');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Axios
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.axios = require('./axios');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Logger
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@ const debug = require('debug')('acme-client');
 | 
			
		|||
 | 
			
		||||
let logger = () => {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set logger function
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -17,11 +16,10 @@ exports.setLogger = (fn) => {
 | 
			
		|||
    logger = fn;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Log message
 | 
			
		||||
 *
 | 
			
		||||
 * @param {string} Message
 | 
			
		||||
 * @param {string} msg Message
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.log = (msg) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ const dns = require('dns').promises;
 | 
			
		|||
const { readCertificateInfo, splitPemChain } = require('./crypto');
 | 
			
		||||
const { log } = require('./logger');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Exponential backoff
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +25,6 @@ class Backoff {
 | 
			
		|||
        this.attempts = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get backoff duration
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +38,6 @@ class Backoff {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retry promise
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +67,6 @@ async function retryPromise(fn, attempts, backoff) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Retry promise
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +83,6 @@ function retry(fn, { attempts = 5, min = 5000, max = 30000 } = {}) {
 | 
			
		|||
    return retryPromise(fn, attempts, backoff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Parse URLs from link header
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -107,7 +102,6 @@ function parseLinkHeader(header, rel = 'alternate') {
 | 
			
		|||
    return results.filter((r) => r);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find certificate chain with preferred issuer common name
 | 
			
		||||
 *  - If issuer is found in multiple chains, the closest to root wins
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +151,6 @@ function findCertificateChainForIssuer(chains, issuer) {
 | 
			
		|||
    return chains[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Find and format error in response object
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +171,6 @@ function formatResponseError(resp) {
 | 
			
		|||
    return result.replace(/\n/g, '');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Resolve root domain name by looking for SOA record
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +196,6 @@ async function resolveDomainBySoaRecord(recordName) {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get DNS resolver using domains authoritative NS records
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +236,6 @@ async function getAuthoritativeDnsResolver(recordName) {
 | 
			
		|||
    return resolver;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Attempt to retrieve TLS ALPN certificate from peer
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +257,7 @@ async function retrieveTlsAlpnCertificate(host, port, timeout = 30000) {
 | 
			
		|||
            port,
 | 
			
		||||
            servername: host,
 | 
			
		||||
            rejectUnauthorized: false,
 | 
			
		||||
            ALPNProtocols: ['acme-tls/1']
 | 
			
		||||
            ALPNProtocols: ['acme-tls/1'],
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        socket.setTimeout(timeout);
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +289,6 @@ async function retrieveTlsAlpnCertificate(host, port, timeout = 30000) {
 | 
			
		|||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Export utils
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -310,5 +299,5 @@ module.exports = {
 | 
			
		|||
    findCertificateChainForIssuer,
 | 
			
		||||
    formatResponseError,
 | 
			
		||||
    getAuthoritativeDnsResolver,
 | 
			
		||||
    retrieveTlsAlpnCertificate
 | 
			
		||||
    retrieveTlsAlpnCertificate,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ const axios = require('./axios');
 | 
			
		|||
const util = require('./util');
 | 
			
		||||
const { isAlpnCertificateAuthorizationValid } = require('./crypto');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Verify ACME HTTP challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +42,6 @@ async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix =
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Walk DNS until TXT records are found
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +79,6 @@ async function walkDnsChallengeRecord(recordName, resolver = dns) {
 | 
			
		|||
    throw new Error(`No TXT records found for name: ${recordName}`);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Verify ACME DNS challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +118,6 @@ async function verifyDnsChallenge(authz, challenge, keyAuthorization, prefix = '
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Verify ACME TLS ALPN challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +145,6 @@ async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Export API
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -157,5 +152,5 @@ async function verifyTlsAlpnChallenge(authz, challenge, keyAuthorization) {
 | 
			
		|||
module.exports = {
 | 
			
		||||
    'http-01': verifyHttpChallenge,
 | 
			
		||||
    'dns-01': verifyDnsChallenge,
 | 
			
		||||
    'tls-alpn-01': verifyTlsAlpnChallenge
 | 
			
		||||
    'tls-alpn-01': verifyTlsAlpnChallenge,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,6 @@ const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
 | 
			
		|||
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
 | 
			
		||||
const tlsAlpnPort = axios.defaults.acmeSettings.tlsAlpnChallengePort || 443;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('pebble', () => {
 | 
			
		||||
    const httpsAgent = new https.Agent({ rejectUnauthorized: false });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,18 +38,16 @@ describe('pebble', () => {
 | 
			
		|||
    const testTlsAlpn01ChallengeHost = `${uuid()}.${domainName}`;
 | 
			
		||||
    const testTlsAlpn01ChallengeValue = uuid();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pebble CTS required
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    before(function() {
 | 
			
		||||
    before(function () {
 | 
			
		||||
        if (!cts.isEnabled()) {
 | 
			
		||||
            this.skip();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * DNS mocking
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +89,6 @@ describe('pebble', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * HTTP-01 challenge response
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +114,6 @@ describe('pebble', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * HTTPS-01 challenge response
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -143,7 +138,7 @@ describe('pebble', () => {
 | 
			
		|||
            /* Assert HTTP 302 */
 | 
			
		||||
            const resp = await axios.get(`http://${testHttps01ChallengeHost}:${httpPort}/.well-known/acme-challenge/${testHttps01ChallengeToken}`, {
 | 
			
		||||
                maxRedirects: 0,
 | 
			
		||||
                validateStatus: null
 | 
			
		||||
                validateStatus: null,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            assert.strictEqual(resp.status, 302);
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +160,6 @@ describe('pebble', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * DNS-01 challenge response
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +182,6 @@ describe('pebble', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * TLS-ALPN-01 challenge response
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ const axios = require('./../src/axios');
 | 
			
		|||
const HttpClient = require('./../src/http');
 | 
			
		||||
const pkg = require('./../package.json');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('http', () => {
 | 
			
		||||
    let testClient;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +19,6 @@ describe('http', () => {
 | 
			
		|||
    const defaultUaEndpoint = `http://${uuid()}.example.com`;
 | 
			
		||||
    const customUaEndpoint = `http://${uuid()}.example.com`;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * HTTP mocking
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +41,6 @@ describe('http', () => {
 | 
			
		|||
        axios.defaults.headers.common['User-Agent'] = defaultUserAgent;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialize
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +49,6 @@ describe('http', () => {
 | 
			
		|||
        testClient = new HttpClient();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * HTTP verbs
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +61,6 @@ describe('http', () => {
 | 
			
		|||
        assert.strictEqual(resp.data, 'ok');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * User-Agent
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@
 | 
			
		|||
const { assert } = require('chai');
 | 
			
		||||
const logger = require('./../src/logger');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('logger', () => {
 | 
			
		||||
    let lastLogMessage = null;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +12,6 @@ describe('logger', () => {
 | 
			
		|||
        lastLogMessage = msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Logger
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +21,6 @@ describe('logger', () => {
 | 
			
		|||
        assert.isNull(lastLogMessage);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    it('should log with custom logger', () => {
 | 
			
		||||
        logger.setLogger(customLoggerFn);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,6 @@ const verify = require('./../src/verify');
 | 
			
		|||
 | 
			
		||||
const domainName = process.env.ACME_DOMAIN_NAME || 'example.com';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('verify', () => {
 | 
			
		||||
    const challengeTypes = ['http-01', 'dns-01'];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -30,18 +29,16 @@ describe('verify', () => {
 | 
			
		|||
    const testTlsAlpn01Challenge = { type: 'dns-01', status: 'pending', token: uuid() };
 | 
			
		||||
    const testTlsAlpn01Key = uuid();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pebble CTS required
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    before(function() {
 | 
			
		||||
    before(function () {
 | 
			
		||||
        if (!cts.isEnabled()) {
 | 
			
		||||
            this.skip();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * API
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +47,6 @@ describe('verify', () => {
 | 
			
		|||
        assert.containsAllKeys(verify, challengeTypes);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * http-01
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +77,6 @@ describe('verify', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * https-01
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +97,6 @@ describe('verify', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * dns-01
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +127,6 @@ describe('verify', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * tls-alpn-01
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,10 +9,9 @@ const spec = require('./spec');
 | 
			
		|||
const forge = require('./../src/crypto/forge');
 | 
			
		||||
 | 
			
		||||
const cryptoEngines = {
 | 
			
		||||
    forge
 | 
			
		||||
    forge,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('crypto-legacy', () => {
 | 
			
		||||
    let testPemKey;
 | 
			
		||||
    let testCert;
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +27,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
    const testCertPath = path.join(__dirname, 'fixtures', 'certificate.crt');
 | 
			
		||||
    const testSanCertPath = path.join(__dirname, 'fixtures', 'san-certificate.crt');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fixtures
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +48,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Engines
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +59,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
            let testNonCnCsr;
 | 
			
		||||
            let testNonAsciiCsr;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Key generation
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -83,14 +79,13 @@ describe('crypto-legacy', () => {
 | 
			
		|||
                publicKeyStore.push(key.toString().replace(/[\r\n]/gm, ''));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Certificate Signing Request
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should generate a csr', async () => {
 | 
			
		||||
                const [key, csr] = await engine.createCsr({
 | 
			
		||||
                    commonName: testCsrDomain
 | 
			
		||||
                    commonName: testCsrDomain,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +97,7 @@ describe('crypto-legacy', () => {
 | 
			
		|||
            it('should generate a san csr', async () => {
 | 
			
		||||
                const [key, csr] = await engine.createCsr({
 | 
			
		||||
                    commonName: testSanCsrDomains[0],
 | 
			
		||||
                    altNames: testSanCsrDomains.slice(1, testSanCsrDomains.length)
 | 
			
		||||
                    altNames: testSanCsrDomains.slice(1, testSanCsrDomains.length),
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +108,7 @@ describe('crypto-legacy', () => {
 | 
			
		|||
 | 
			
		||||
            it('should generate a csr without common name', async () => {
 | 
			
		||||
                const [key, csr] = await engine.createCsr({
 | 
			
		||||
                    altNames: testSanCsrDomains
 | 
			
		||||
                    altNames: testSanCsrDomains,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +121,7 @@ describe('crypto-legacy', () => {
 | 
			
		|||
                const [key, csr] = await engine.createCsr({
 | 
			
		||||
                    commonName: testCsrDomain,
 | 
			
		||||
                    organization: '大安區',
 | 
			
		||||
                    organizationUnit: '中文部門'
 | 
			
		||||
                    organizationUnit: '中文部門',
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +162,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
                assert.deepStrictEqual(result.altNames, [testCsrDomain]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Certificate
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +182,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
                assert.deepEqual(info.domains.altNames, testSanCsrDomains.slice(1, testSanCsrDomains.length));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * PEM utils
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -214,7 +207,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Modulus and exponent
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -246,7 +238,6 @@ describe('crypto-legacy', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Verify identical results
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,6 @@ dGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0aW5ndGVzdGluZ3Rlc3Rpbmd0ZXN0
 | 
			
		|||
-----END TEST-----
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('crypto', () => {
 | 
			
		||||
    const testCsrDomain = 'example.com';
 | 
			
		||||
    const testSanCsrDomains = ['example.com', 'test.example.com', 'abc.example.com'];
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +57,6 @@ describe('crypto', () => {
 | 
			
		|||
    const testCertPath = path.join(__dirname, 'fixtures', 'certificate.crt');
 | 
			
		||||
    const testSanCertPath = path.join(__dirname, 'fixtures', 'san-certificate.crt');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Key types
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -68,24 +66,23 @@ describe('crypto', () => {
 | 
			
		|||
            createKeyFns: {
 | 
			
		||||
                s1024: () => crypto.createPrivateRsaKey(1024),
 | 
			
		||||
                s2048: () => crypto.createPrivateRsaKey(),
 | 
			
		||||
                s4096: () => crypto.createPrivateRsaKey(4096)
 | 
			
		||||
                s4096: () => crypto.createPrivateRsaKey(4096),
 | 
			
		||||
            },
 | 
			
		||||
            jwkSpecFn: spec.jwk.rsa
 | 
			
		||||
            jwkSpecFn: spec.jwk.rsa,
 | 
			
		||||
        },
 | 
			
		||||
        ecdsa: {
 | 
			
		||||
            createKeyFns: {
 | 
			
		||||
                p256: () => crypto.createPrivateEcdsaKey(),
 | 
			
		||||
                p384: () => crypto.createPrivateEcdsaKey('P-384'),
 | 
			
		||||
                p521: () => crypto.createPrivateEcdsaKey('P-521')
 | 
			
		||||
                p521: () => crypto.createPrivateEcdsaKey('P-521'),
 | 
			
		||||
            },
 | 
			
		||||
            jwkSpecFn: spec.jwk.ecdsa
 | 
			
		||||
        }
 | 
			
		||||
            jwkSpecFn: spec.jwk.ecdsa,
 | 
			
		||||
        },
 | 
			
		||||
    }).forEach(([name, { createKeyFns, jwkSpecFn }]) => {
 | 
			
		||||
        describe(name, () => {
 | 
			
		||||
            const testPrivateKeys = {};
 | 
			
		||||
            const testPublicKeys = {};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Iterate through all generator variations
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +94,6 @@ describe('crypto', () => {
 | 
			
		|||
                let testNonAsciiCsr;
 | 
			
		||||
                let testAlpnCertificate;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Keys and JWK
 | 
			
		||||
                 */
 | 
			
		||||
| 
						 | 
				
			
			@ -132,14 +128,13 @@ describe('crypto', () => {
 | 
			
		|||
                    jwkSpecFn(jwk);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Certificate Signing Request
 | 
			
		||||
                 */
 | 
			
		||||
 | 
			
		||||
                it(`${n}/should generate a csr`, async () => {
 | 
			
		||||
                    const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                        commonName: testCsrDomain
 | 
			
		||||
                        commonName: testCsrDomain,
 | 
			
		||||
                    }, testPrivateKeys[n]);
 | 
			
		||||
 | 
			
		||||
                    assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +146,7 @@ describe('crypto', () => {
 | 
			
		|||
                it(`${n}/should generate a san csr`, async () => {
 | 
			
		||||
                    const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                        commonName: testSanCsrDomains[0],
 | 
			
		||||
                        altNames: testSanCsrDomains.slice(1, testSanCsrDomains.length)
 | 
			
		||||
                        altNames: testSanCsrDomains.slice(1, testSanCsrDomains.length),
 | 
			
		||||
                    }, testPrivateKeys[n]);
 | 
			
		||||
 | 
			
		||||
                    assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +157,7 @@ describe('crypto', () => {
 | 
			
		|||
 | 
			
		||||
                it(`${n}/should generate a csr without common name`, async () => {
 | 
			
		||||
                    const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                        altNames: testSanCsrDomains
 | 
			
		||||
                        altNames: testSanCsrDomains,
 | 
			
		||||
                    }, testPrivateKeys[n]);
 | 
			
		||||
 | 
			
		||||
                    assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +170,7 @@ describe('crypto', () => {
 | 
			
		|||
                    const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                        commonName: testCsrDomain,
 | 
			
		||||
                        organization: '大安區',
 | 
			
		||||
                        organizationUnit: '中文部門'
 | 
			
		||||
                        organizationUnit: '中文部門',
 | 
			
		||||
                    }, testPrivateKeys[n]);
 | 
			
		||||
 | 
			
		||||
                    assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -186,7 +181,7 @@ describe('crypto', () => {
 | 
			
		|||
 | 
			
		||||
                it(`${n}/should generate a csr with key as string`, async () => {
 | 
			
		||||
                    const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                        commonName: testCsrDomain
 | 
			
		||||
                        commonName: testCsrDomain,
 | 
			
		||||
                    }, testPrivateKeys[n].toString());
 | 
			
		||||
 | 
			
		||||
                    assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
| 
						 | 
				
			
			@ -195,11 +190,10 @@ describe('crypto', () => {
 | 
			
		|||
 | 
			
		||||
                it(`${n}/should throw with invalid key`, async () => {
 | 
			
		||||
                    await assert.isRejected(crypto.createCsr({
 | 
			
		||||
                        commonName: testCsrDomain
 | 
			
		||||
                        commonName: testCsrDomain,
 | 
			
		||||
                    }, testPublicKeys[n]));
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * Domain and info resolver
 | 
			
		||||
                 */
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +237,6 @@ describe('crypto', () => {
 | 
			
		|||
                    });
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                /**
 | 
			
		||||
                 * ALPN
 | 
			
		||||
                 */
 | 
			
		||||
| 
						 | 
				
			
			@ -284,7 +277,6 @@ describe('crypto', () => {
 | 
			
		|||
        });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Common functionality
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -294,7 +286,6 @@ describe('crypto', () => {
 | 
			
		|||
        let testCert;
 | 
			
		||||
        let testSanCert;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        it('should read private key fixture', async () => {
 | 
			
		||||
            testPemKey = await fs.readFile(testKeyPath);
 | 
			
		||||
            assert.isTrue(Buffer.isBuffer(testPemKey));
 | 
			
		||||
| 
						 | 
				
			
			@ -310,21 +301,19 @@ describe('crypto', () => {
 | 
			
		|||
            assert.isTrue(Buffer.isBuffer(testSanCert));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * CSR with auto-generated key
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        it('should generate a csr with default key', async () => {
 | 
			
		||||
            const [key, csr] = await crypto.createCsr({
 | 
			
		||||
                commonName: testCsrDomain
 | 
			
		||||
                commonName: testCsrDomain,
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            assert.isTrue(Buffer.isBuffer(key));
 | 
			
		||||
            assert.isTrue(Buffer.isBuffer(csr));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Certificate
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +341,6 @@ describe('crypto', () => {
 | 
			
		|||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * ALPN
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +353,6 @@ describe('crypto', () => {
 | 
			
		|||
            assert.isTrue(Buffer.isBuffer(cert));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * PEM utils
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,35 +20,32 @@ const clientOpts = {
 | 
			
		|||
    directoryUrl,
 | 
			
		||||
    backoffAttempts: 5,
 | 
			
		||||
    backoffMin: 1000,
 | 
			
		||||
    backoffMax: 5000
 | 
			
		||||
    backoffMax: 5000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (capEabEnabled && process.env.ACME_EAB_KID && process.env.ACME_EAB_HMAC_KEY) {
 | 
			
		||||
    clientOpts.externalAccountBinding = {
 | 
			
		||||
        kid: process.env.ACME_EAB_KID,
 | 
			
		||||
        hmacKey: process.env.ACME_EAB_HMAC_KEY
 | 
			
		||||
        hmacKey: process.env.ACME_EAB_HMAC_KEY,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('client', () => {
 | 
			
		||||
    const testDomain = `${uuid()}.${domainName}`;
 | 
			
		||||
    const testDomainAlpn = `${uuid()}.${domainName}`;
 | 
			
		||||
    const testDomainWildcard = `*.${testDomain}`;
 | 
			
		||||
    const testContact = `mailto:test-${uuid()}@nope.com`;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pebble CTS required
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    before(function() {
 | 
			
		||||
    before(function () {
 | 
			
		||||
        if (!cts.isEnabled()) {
 | 
			
		||||
            this.skip();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Key types
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -58,18 +55,18 @@ describe('client', () => {
 | 
			
		|||
            createKeyFn: () => acme.crypto.createPrivateRsaKey(),
 | 
			
		||||
            createKeyAltFns: {
 | 
			
		||||
                s1024: () => acme.crypto.createPrivateRsaKey(1024),
 | 
			
		||||
                s4096: () => acme.crypto.createPrivateRsaKey(4096)
 | 
			
		||||
                s4096: () => acme.crypto.createPrivateRsaKey(4096),
 | 
			
		||||
            },
 | 
			
		||||
            jwkSpecFn: spec.jwk.rsa
 | 
			
		||||
            jwkSpecFn: spec.jwk.rsa,
 | 
			
		||||
        },
 | 
			
		||||
        ecdsa: {
 | 
			
		||||
            createKeyFn: () => acme.crypto.createPrivateEcdsaKey(),
 | 
			
		||||
            createKeyAltFns: {
 | 
			
		||||
                p384: () => acme.crypto.createPrivateEcdsaKey('P-384'),
 | 
			
		||||
                p521: () => acme.crypto.createPrivateEcdsaKey('P-521')
 | 
			
		||||
                p521: () => acme.crypto.createPrivateEcdsaKey('P-521'),
 | 
			
		||||
            },
 | 
			
		||||
            jwkSpecFn: spec.jwk.ecdsa
 | 
			
		||||
        }
 | 
			
		||||
            jwkSpecFn: spec.jwk.ecdsa,
 | 
			
		||||
        },
 | 
			
		||||
    }).forEach(([name, { createKeyFn, createKeyAltFns, jwkSpecFn }]) => {
 | 
			
		||||
        describe(name, () => {
 | 
			
		||||
            let testIssuers;
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +94,6 @@ describe('client', () => {
 | 
			
		|||
            let testCertificateAlpn;
 | 
			
		||||
            let testCertificateWildcard;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Fixtures
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -114,11 +110,11 @@ describe('client', () => {
 | 
			
		|||
 | 
			
		||||
            it('should generate certificate signing request', async () => {
 | 
			
		||||
                [, testCsr] = await acme.crypto.createCsr({ commonName: testDomain }, await createKeyFn());
 | 
			
		||||
                [, testCsrAlpn] = await acme.crypto.createCsr({ commonName: testDomainAlpn }, await createKeyFn());
 | 
			
		||||
                [, testCsrWildcard] = await acme.crypto.createCsr({ commonName: testDomainWildcard }, await createKeyFn());
 | 
			
		||||
                [, testCsrAlpn] = await acme.crypto.createCsr({ altNames: [testDomainAlpn] }, await createKeyFn());
 | 
			
		||||
                [, testCsrWildcard] = await acme.crypto.createCsr({ altNames: [testDomainWildcard] }, await createKeyFn());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should resolve certificate issuers [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should resolve certificate issuers [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -134,7 +130,6 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Initialize clients
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -142,7 +137,7 @@ describe('client', () => {
 | 
			
		|||
            it('should initialize client', () => {
 | 
			
		||||
                testClient = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountKey
 | 
			
		||||
                    accountKey: testAccountKey,
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,12 +146,11 @@ describe('client', () => {
 | 
			
		|||
                jwkSpecFn(jwk);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Terms of Service
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should produce tos url [ACME_CAP_META_TOS_FIELD]', async function() {
 | 
			
		||||
            it('should produce tos url [ACME_CAP_META_TOS_FIELD]', async function () {
 | 
			
		||||
                if (!capMetaTosField) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +159,7 @@ describe('client', () => {
 | 
			
		|||
                assert.isString(tos);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should not produce tos url [!ACME_CAP_META_TOS_FIELD]', async function() {
 | 
			
		||||
            it('should not produce tos url [!ACME_CAP_META_TOS_FIELD]', async function () {
 | 
			
		||||
                if (capMetaTosField) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -174,12 +168,11 @@ describe('client', () => {
 | 
			
		|||
                assert.isNull(tos);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Create account
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should refuse account creation without tos [ACME_CAP_META_TOS_FIELD]', async function() {
 | 
			
		||||
            it('should refuse account creation without tos [ACME_CAP_META_TOS_FIELD]', async function () {
 | 
			
		||||
                if (!capMetaTosField) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +180,7 @@ describe('client', () => {
 | 
			
		|||
                await assert.isRejected(testClient.createAccount());
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should refuse account creation without eab [ACME_CAP_EAB_ENABLED]', async function() {
 | 
			
		||||
            it('should refuse account creation without eab [ACME_CAP_EAB_ENABLED]', async function () {
 | 
			
		||||
                if (!capEabEnabled) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -195,17 +188,17 @@ describe('client', () => {
 | 
			
		|||
                const client = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountKey,
 | 
			
		||||
                    externalAccountBinding: null
 | 
			
		||||
                    externalAccountBinding: null,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(client.createAccount({
 | 
			
		||||
                    termsOfServiceAgreed: true
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should create an account', async () => {
 | 
			
		||||
                testAccount = await testClient.createAccount({
 | 
			
		||||
                    termsOfServiceAgreed: true
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                spec.rfc8555.account(testAccount);
 | 
			
		||||
| 
						 | 
				
			
			@ -217,7 +210,6 @@ describe('client', () => {
 | 
			
		|||
                assert.isString(testAccountUrl);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Create account with alternate key sizes
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -226,11 +218,11 @@ describe('client', () => {
 | 
			
		|||
                it(`should create account with key=${k}`, async () => {
 | 
			
		||||
                    const client = new acme.Client({
 | 
			
		||||
                        ...clientOpts,
 | 
			
		||||
                        accountKey: await altKeyFn()
 | 
			
		||||
                        accountKey: await altKeyFn(),
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    const account = await client.createAccount({
 | 
			
		||||
                        termsOfServiceAgreed: true
 | 
			
		||||
                        termsOfServiceAgreed: true,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    spec.rfc8555.account(account);
 | 
			
		||||
| 
						 | 
				
			
			@ -238,7 +230,6 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Find existing account using secondary client
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -246,22 +237,22 @@ describe('client', () => {
 | 
			
		|||
            it('should throw when trying to find account using invalid account key', async () => {
 | 
			
		||||
                const client = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountSecondaryKey
 | 
			
		||||
                    accountKey: testAccountSecondaryKey,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(client.createAccount({
 | 
			
		||||
                    onlyReturnExisting: true
 | 
			
		||||
                    onlyReturnExisting: true,
 | 
			
		||||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should find existing account using account key', async () => {
 | 
			
		||||
                const client = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountKey
 | 
			
		||||
                    accountKey: testAccountKey,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const account = await client.createAccount({
 | 
			
		||||
                    onlyReturnExisting: true
 | 
			
		||||
                    onlyReturnExisting: true,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                spec.rfc8555.account(account);
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +260,6 @@ describe('client', () => {
 | 
			
		|||
                assert.deepStrictEqual(account.key, testAccount.key);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Account URL
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +268,7 @@ describe('client', () => {
 | 
			
		|||
                const client = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountKey,
 | 
			
		||||
                    accountUrl: 'https://acme-staging-v02.api.letsencrypt.org/acme/acct/1'
 | 
			
		||||
                    accountUrl: 'https://acme-staging-v02.api.letsencrypt.org/acme/acct/1',
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(client.updateAccount());
 | 
			
		||||
| 
						 | 
				
			
			@ -288,11 +278,11 @@ describe('client', () => {
 | 
			
		|||
                const client = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: testAccountKey,
 | 
			
		||||
                    accountUrl: testAccountUrl
 | 
			
		||||
                    accountUrl: testAccountUrl,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const account = await client.createAccount({
 | 
			
		||||
                    onlyReturnExisting: true
 | 
			
		||||
                    onlyReturnExisting: true,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                spec.rfc8555.account(account);
 | 
			
		||||
| 
						 | 
				
			
			@ -300,7 +290,6 @@ describe('client', () => {
 | 
			
		|||
                assert.deepStrictEqual(account.key, testAccount.key);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Update account contact info
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -316,12 +305,11 @@ describe('client', () => {
 | 
			
		|||
                assert.include(account.contact, testContact);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Change account private key
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should change account private key [ACME_CAP_UPDATE_ACCOUNT_KEY]', async function() {
 | 
			
		||||
            it('should change account private key [ACME_CAP_UPDATE_ACCOUNT_KEY]', async function () {
 | 
			
		||||
                if (!capUpdateAccountKey) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -329,7 +317,7 @@ describe('client', () => {
 | 
			
		|||
                await testClient.updateAccountKey(testAccountSecondaryKey);
 | 
			
		||||
 | 
			
		||||
                const account = await testClient.createAccount({
 | 
			
		||||
                    onlyReturnExisting: true
 | 
			
		||||
                    onlyReturnExisting: true,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                spec.rfc8555.account(account);
 | 
			
		||||
| 
						 | 
				
			
			@ -337,7 +325,6 @@ describe('client', () => {
 | 
			
		|||
                assert.notDeepEqual(account.key, testAccount.key);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Create new certificate order
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -357,7 +344,6 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get status of existing certificate order
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +357,6 @@ describe('client', () => {
 | 
			
		|||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get identifier authorization
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -401,7 +386,6 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Generate challenge key authorization
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -418,7 +402,6 @@ describe('client', () => {
 | 
			
		|||
                [testKeyAuthorization, testKeyAuthorizationAlpn, testKeyAuthorizationWildcard].forEach((k) => assert.isString(k));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Deactivate identifier authorization
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -427,8 +410,8 @@ describe('client', () => {
 | 
			
		|||
                const order = await testClient.createOrder({
 | 
			
		||||
                    identifiers: [
 | 
			
		||||
                        { type: 'dns', value: `${uuid()}.${domainName}` },
 | 
			
		||||
                        { type: 'dns', value: `${uuid()}.${domainName}` }
 | 
			
		||||
                    ]
 | 
			
		||||
                        { type: 'dns', value: `${uuid()}.${domainName}` },
 | 
			
		||||
                    ],
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const authzCollection = await testClient.getAuthorizations(order);
 | 
			
		||||
| 
						 | 
				
			
			@ -445,7 +428,6 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Verify satisfied challenge
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -460,7 +442,6 @@ describe('client', () => {
 | 
			
		|||
                await testClient.verifyChallenge(testAuthzWildcard, testChallengeWildcard);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Complete challenge
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -474,7 +455,6 @@ describe('client', () => {
 | 
			
		|||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Wait for valid challenge
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -483,7 +463,6 @@ describe('client', () => {
 | 
			
		|||
                await Promise.all([testChallenge, testChallengeAlpn, testChallengeWildcard].map(async (c) => testClient.waitForValidStatus(c)));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Finalize order
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -500,7 +479,6 @@ describe('client', () => {
 | 
			
		|||
                assert.strictEqual(testOrderWildcard.url, finalizeWildcard.url);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Wait for valid order
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -509,7 +487,6 @@ describe('client', () => {
 | 
			
		|||
                await Promise.all([testOrder, testOrderAlpn, testOrderWildcard].map(async (o) => testClient.waitForValidStatus(o)));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Get certificate
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -525,7 +502,7 @@ describe('client', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should get alternate certificate chain [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should get alternate certificate chain [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -539,7 +516,7 @@ describe('client', () => {
 | 
			
		|||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should get default chain with invalid preference [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should get default chain with invalid preference [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -551,7 +528,6 @@ describe('client', () => {
 | 
			
		|||
                assert.strictEqual(testIssuers[0], info.issuer.commonName);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Revoke certificate
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -568,7 +544,6 @@ describe('client', () => {
 | 
			
		|||
                await assert.isRejected(testClient.getCertificate(testOrderWildcard));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Deactivate account
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -581,7 +556,6 @@ describe('client', () => {
 | 
			
		|||
                assert.strictEqual(account.status, 'deactivated');
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Verify that no new orders can be made
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,17 +18,16 @@ const clientOpts = {
 | 
			
		|||
    directoryUrl,
 | 
			
		||||
    backoffAttempts: 5,
 | 
			
		||||
    backoffMin: 1000,
 | 
			
		||||
    backoffMax: 5000
 | 
			
		||||
    backoffMax: 5000,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (capEabEnabled && process.env.ACME_EAB_KID && process.env.ACME_EAB_HMAC_KEY) {
 | 
			
		||||
    clientOpts.externalAccountBinding = {
 | 
			
		||||
        kid: process.env.ACME_EAB_KID,
 | 
			
		||||
        hmacKey: process.env.ACME_EAB_HMAC_KEY
 | 
			
		||||
        hmacKey: process.env.ACME_EAB_HMAC_KEY,
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('client.auto', () => {
 | 
			
		||||
    const testDomain = `${uuid()}.${domainName}`;
 | 
			
		||||
    const testHttpDomain = `${uuid()}.${domainName}`;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,21 +39,19 @@ describe('client.auto', () => {
 | 
			
		|||
    const testSanDomains = [
 | 
			
		||||
        `${uuid()}.${domainName}`,
 | 
			
		||||
        `${uuid()}.${domainName}`,
 | 
			
		||||
        `${uuid()}.${domainName}`
 | 
			
		||||
        `${uuid()}.${domainName}`,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pebble CTS required
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    before(function() {
 | 
			
		||||
    before(function () {
 | 
			
		||||
        if (!cts.isEnabled()) {
 | 
			
		||||
            this.skip();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Key types
 | 
			
		||||
     */
 | 
			
		||||
| 
						 | 
				
			
			@ -64,16 +61,16 @@ describe('client.auto', () => {
 | 
			
		|||
            createKeyFn: () => acme.crypto.createPrivateRsaKey(),
 | 
			
		||||
            createKeyAltFns: {
 | 
			
		||||
                s1024: () => acme.crypto.createPrivateRsaKey(1024),
 | 
			
		||||
                s4096: () => acme.crypto.createPrivateRsaKey(4096)
 | 
			
		||||
            }
 | 
			
		||||
                s4096: () => acme.crypto.createPrivateRsaKey(4096),
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
        ecdsa: {
 | 
			
		||||
            createKeyFn: () => acme.crypto.createPrivateEcdsaKey(),
 | 
			
		||||
            createKeyAltFns: {
 | 
			
		||||
                p384: () => acme.crypto.createPrivateEcdsaKey('P-384'),
 | 
			
		||||
                p521: () => acme.crypto.createPrivateEcdsaKey('P-521')
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
                p521: () => acme.crypto.createPrivateEcdsaKey('P-521'),
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    }).forEach(([name, { createKeyFn, createKeyAltFns }]) => {
 | 
			
		||||
        describe(name, () => {
 | 
			
		||||
            let testIssuers;
 | 
			
		||||
| 
						 | 
				
			
			@ -82,12 +79,11 @@ describe('client.auto', () => {
 | 
			
		|||
            let testSanCertificate;
 | 
			
		||||
            let testWildcardCertificate;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Fixtures
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should resolve certificate issuers [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should resolve certificate issuers [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +99,6 @@ describe('client.auto', () => {
 | 
			
		|||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Initialize client
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -111,31 +106,30 @@ describe('client.auto', () => {
 | 
			
		|||
            it('should initialize client', async () => {
 | 
			
		||||
                testClient = new acme.Client({
 | 
			
		||||
                    ...clientOpts,
 | 
			
		||||
                    accountKey: await createKeyFn()
 | 
			
		||||
                    accountKey: await createKeyFn(),
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Invalid challenge response
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should throw on invalid challenge response', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeNoopFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn,
 | 
			
		||||
                }), /^authorization not found/i);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should throw on invalid challenge response with opts.skipChallengeVerification=true', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -143,38 +137,37 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    skipChallengeVerification: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeNoopFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn,
 | 
			
		||||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Challenge function exceptions
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should throw on challengeCreate exception', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeThrowFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeNoopFn,
 | 
			
		||||
                }), /^oops$/);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should not throw on challengeRemove exception', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeThrowFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeThrowFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -188,8 +181,8 @@ describe('client.auto', () => {
 | 
			
		|||
                        `${uuid()}.${domainName}`,
 | 
			
		||||
                        `${uuid()}.${domainName}`,
 | 
			
		||||
                        `${uuid()}.${domainName}`,
 | 
			
		||||
                        `${uuid()}.${domainName}`
 | 
			
		||||
                    ]
 | 
			
		||||
                        `${uuid()}.${domainName}`,
 | 
			
		||||
                    ],
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                await assert.isRejected(testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -205,28 +198,27 @@ describe('client.auto', () => {
 | 
			
		|||
                        results.push(true);
 | 
			
		||||
                        return cts.challengeCreateFn(...args);
 | 
			
		||||
                    },
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                }));
 | 
			
		||||
 | 
			
		||||
                assert.strictEqual(results.length, 5);
 | 
			
		||||
                assert.deepStrictEqual(results, [false, false, false, true, true]);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Order certificates
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            it('should order certificate', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testDomain
 | 
			
		||||
                    commonName: testDomain,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +227,7 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order certificate using http-01', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testHttpDomain
 | 
			
		||||
                    commonName: testHttpDomain,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -243,7 +235,7 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.assertHttpChallengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    challengePriority: ['http-01']
 | 
			
		||||
                    challengePriority: ['http-01'],
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +243,7 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order certificate using https-01', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testHttpsDomain
 | 
			
		||||
                    commonName: testHttpsDomain,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -259,7 +251,7 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.assertHttpsChallengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    challengePriority: ['http-01']
 | 
			
		||||
                    challengePriority: ['http-01'],
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +259,7 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order certificate using dns-01', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testDnsDomain
 | 
			
		||||
                    commonName: testDnsDomain,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -275,7 +267,7 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.assertDnsChallengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    challengePriority: ['dns-01']
 | 
			
		||||
                    challengePriority: ['dns-01'],
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -283,7 +275,7 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order certificate using tls-alpn-01', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testAlpnDomain
 | 
			
		||||
                    commonName: testAlpnDomain,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +283,7 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.assertTlsAlpnChallengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    challengePriority: ['tls-alpn-01']
 | 
			
		||||
                    challengePriority: ['tls-alpn-01'],
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -299,14 +291,14 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order san certificate', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    altNames: testSanDomains
 | 
			
		||||
                    altNames: testSanDomains,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -315,15 +307,14 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order wildcard certificate', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: testWildcardDomain,
 | 
			
		||||
                    altNames: [`*.${testWildcardDomain}`]
 | 
			
		||||
                    altNames: [testWildcardDomain, `*.${testWildcardDomain}`],
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
                    csr,
 | 
			
		||||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +323,7 @@ describe('client.auto', () => {
 | 
			
		|||
 | 
			
		||||
            it('should order certificate with opts.skipChallengeVerification=true', async () => {
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -340,20 +331,20 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    skipChallengeVerification: true,
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                assert.isString(cert);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should order alternate certificate chain [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should order alternate certificate chain [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                await Promise.all(testIssuers.map(async (issuer) => {
 | 
			
		||||
                    const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                        commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                        commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                    }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                    const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -361,7 +352,7 @@ describe('client.auto', () => {
 | 
			
		|||
                        termsOfServiceAgreed: true,
 | 
			
		||||
                        preferredChain: issuer,
 | 
			
		||||
                        challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                        challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                        challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    const rootCert = acme.crypto.splitPemChain(cert).pop();
 | 
			
		||||
| 
						 | 
				
			
			@ -371,13 +362,13 @@ describe('client.auto', () => {
 | 
			
		|||
                }));
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            it('should get default chain with invalid preference [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function() {
 | 
			
		||||
            it('should get default chain with invalid preference [ACME_CAP_ALTERNATE_CERT_ROOTS]', async function () {
 | 
			
		||||
                if (!capAlternateCertRoots) {
 | 
			
		||||
                    this.skip();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`
 | 
			
		||||
                    commonName: `${uuid()}.${domainName}`,
 | 
			
		||||
                }, await createKeyFn());
 | 
			
		||||
 | 
			
		||||
                const cert = await testClient.auto({
 | 
			
		||||
| 
						 | 
				
			
			@ -385,7 +376,7 @@ describe('client.auto', () => {
 | 
			
		|||
                    termsOfServiceAgreed: true,
 | 
			
		||||
                    preferredChain: uuid(),
 | 
			
		||||
                    challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                    challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
                const rootCert = acme.crypto.splitPemChain(cert).pop();
 | 
			
		||||
| 
						 | 
				
			
			@ -394,7 +385,6 @@ describe('client.auto', () => {
 | 
			
		|||
                assert.strictEqual(testIssuers[0], info.issuer.commonName);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Order certificate with alternate key sizes
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			@ -402,21 +392,20 @@ describe('client.auto', () => {
 | 
			
		|||
            Object.entries(createKeyAltFns).forEach(([k, altKeyFn]) => {
 | 
			
		||||
                it(`should order certificate with key=${k}`, async () => {
 | 
			
		||||
                    const [, csr] = await acme.crypto.createCsr({
 | 
			
		||||
                        commonName: testDomain
 | 
			
		||||
                        commonName: testDomain,
 | 
			
		||||
                    }, await altKeyFn());
 | 
			
		||||
 | 
			
		||||
                    const cert = await testClient.auto({
 | 
			
		||||
                        csr,
 | 
			
		||||
                        termsOfServiceAgreed: true,
 | 
			
		||||
                        challengeCreateFn: cts.challengeCreateFn,
 | 
			
		||||
                        challengeRemoveFn: cts.challengeRemoveFn
 | 
			
		||||
                        challengeRemoveFn: cts.challengeRemoveFn,
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
                    assert.isString(cert);
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * Read certificates
 | 
			
		||||
             */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ const axios = require('./../src/axios');
 | 
			
		|||
const apiBaseUrl = process.env.ACME_CHALLTESTSRV_URL || null;
 | 
			
		||||
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Send request
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -21,20 +20,18 @@ async function request(apiPath, data = {}) {
 | 
			
		|||
    await axios.request({
 | 
			
		||||
        url: `${apiBaseUrl}/${apiPath}`,
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        data
 | 
			
		||||
        data,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * State
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
exports.isEnabled = () => !!apiBaseUrl;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * DNS
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +39,6 @@ exports.isEnabled = () => !!apiBaseUrl;
 | 
			
		|||
exports.addDnsARecord = async (host, addresses) => request('add-a', { host, addresses });
 | 
			
		||||
exports.setDnsCnameRecord = async (host, target) => request('set-cname', { host, target });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Challenge response
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +51,7 @@ async function addHttps01ChallengeResponse(token, content, targetHostname) {
 | 
			
		|||
    await addHttp01ChallengeResponse(token, content);
 | 
			
		||||
    return request('add-redirect', {
 | 
			
		||||
        path: `/.well-known/acme-challenge/${token}`,
 | 
			
		||||
        targetURL: `https://${targetHostname}:${httpsPort}/.well-known/acme-challenge/${token}`
 | 
			
		||||
        targetURL: `https://${targetHostname}:${httpsPort}/.well-known/acme-challenge/${token}`,
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +68,6 @@ exports.addHttps01ChallengeResponse = addHttps01ChallengeResponse;
 | 
			
		|||
exports.addDns01ChallengeResponse = addDns01ChallengeResponse;
 | 
			
		||||
exports.addTlsAlpn01ChallengeResponse = addTlsAlpn01ChallengeResponse;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Challenge response mock functions
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ const util = require('./../src/util');
 | 
			
		|||
 | 
			
		||||
const pebbleManagementUrl = process.env.ACME_PEBBLE_MANAGEMENT_URL || null;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Pebble
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +25,6 @@ async function getPebbleCertIssuers() {
 | 
			
		|||
    return info.map((i) => i.issuer.commonName);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get certificate issuers
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,14 +7,12 @@ const chai = require('chai');
 | 
			
		|||
const chaiAsPromised = require('chai-as-promised');
 | 
			
		||||
const axios = require('./../src/axios');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add promise support to Chai
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
chai.use(chaiAsPromised);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Challenge test server ports
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +29,6 @@ if (process.env.ACME_TLSALPN_PORT) {
 | 
			
		|||
    axios.defaults.acmeSettings.tlsAlpnChallengePort = process.env.ACME_TLSALPN_PORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * External account binding
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ const { assert } = require('chai');
 | 
			
		|||
const spec = {};
 | 
			
		||||
module.exports = spec;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ACME
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +119,6 @@ spec.rfc8555.challenge = (obj) => {
 | 
			
		|||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Crypto
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -150,7 +148,6 @@ spec.crypto.certificateInfo = (obj) => {
 | 
			
		|||
    assert.strictEqual(Object.prototype.toString.call(obj.notAfter), '[object Date]');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * JWK
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,6 @@ export type PublicKeyString = string;
 | 
			
		|||
export type CertificateString = string;
 | 
			
		||||
export type CsrString = string;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Augmented ACME interfaces
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +27,6 @@ export interface Authorization extends rfc8555.Authorization {
 | 
			
		|||
    url: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Client
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +78,6 @@ export class Client {
 | 
			
		|||
    auto(opts: ClientAutoOptions): Promise<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Directory URLs
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +96,6 @@ export const directory: {
 | 
			
		|||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Crypto
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -177,14 +173,12 @@ export interface CryptoLegacyInterface {
 | 
			
		|||
 | 
			
		||||
export const forge: CryptoLegacyInterface;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Axios
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export const axios: AxiosInstance;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Logger
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,6 @@
 | 
			
		|||
 | 
			
		||||
import * as acme from 'acme-client';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(async () => {
 | 
			
		||||
    /* Client */
 | 
			
		||||
    const accountKey = await acme.crypto.createPrivateKey();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,6 @@ export interface AccountUpdateRequest {
 | 
			
		|||
    termsOfServiceAgreed?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Order
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +52,6 @@ export interface OrderCreateRequest {
 | 
			
		|||
    notAfter?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Authorization
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +71,6 @@ export interface Identifier {
 | 
			
		|||
    value: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Challenge
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -102,7 +99,6 @@ export interface DnsChallenge extends ChallengeAbstract {
 | 
			
		|||
 | 
			
		||||
export type Challenge = HttpChallenge | DnsChallenge;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Certificate
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue