🔱: [acme] sync upgrade with 21 commits [trident-sync]

Bump v5.0.0
This commit is contained in:
xiaojunnuo
2023-01-29 15:27:11 +08:00
parent 62e3945d30
commit f64ea78c44
54 changed files with 8136 additions and 2 deletions

View File

@@ -0,0 +1,518 @@
## Classes
<dl>
<dt><a href="#AcmeClient">AcmeClient</a></dt>
<dd><p>AcmeClient</p>
</dd>
</dl>
## Objects
<dl>
<dt><a href="#Client">Client</a> : <code>object</code></dt>
<dd><p>ACME client</p>
</dd>
</dl>
<a name="AcmeClient"></a>
## AcmeClient
AcmeClient
**Kind**: global class
* [AcmeClient](#AcmeClient)
* [new AcmeClient(opts)](#new_AcmeClient_new)
* [.getTermsOfServiceUrl()](#AcmeClient+getTermsOfServiceUrl) ⇒ <code>Promise.&lt;(string\|null)&gt;</code>
* [.getAccountUrl()](#AcmeClient+getAccountUrl) ⇒ <code>string</code>
* [.createAccount([data])](#AcmeClient+createAccount) ⇒ <code>Promise.&lt;object&gt;</code>
* [.updateAccount([data])](#AcmeClient+updateAccount) ⇒ <code>Promise.&lt;object&gt;</code>
* [.updateAccountKey(newAccountKey, [data])](#AcmeClient+updateAccountKey) ⇒ <code>Promise.&lt;object&gt;</code>
* [.createOrder(data)](#AcmeClient+createOrder) ⇒ <code>Promise.&lt;object&gt;</code>
* [.getOrder(order)](#AcmeClient+getOrder) ⇒ <code>Promise.&lt;object&gt;</code>
* [.finalizeOrder(order, csr)](#AcmeClient+finalizeOrder) ⇒ <code>Promise.&lt;object&gt;</code>
* [.getAuthorizations(order)](#AcmeClient+getAuthorizations) ⇒ <code>Promise.&lt;Array.&lt;object&gt;&gt;</code>
* [.deactivateAuthorization(authz)](#AcmeClient+deactivateAuthorization) ⇒ <code>Promise.&lt;object&gt;</code>
* [.getChallengeKeyAuthorization(challenge)](#AcmeClient+getChallengeKeyAuthorization) ⇒ <code>Promise.&lt;string&gt;</code>
* [.verifyChallenge(authz, challenge)](#AcmeClient+verifyChallenge) ⇒ <code>Promise</code>
* [.completeChallenge(challenge)](#AcmeClient+completeChallenge) ⇒ <code>Promise.&lt;object&gt;</code>
* [.waitForValidStatus(item)](#AcmeClient+waitForValidStatus) ⇒ <code>Promise.&lt;object&gt;</code>
* [.getCertificate(order, [preferredChain])](#AcmeClient+getCertificate) ⇒ <code>Promise.&lt;string&gt;</code>
* [.revokeCertificate(cert, [data])](#AcmeClient+revokeCertificate) ⇒ <code>Promise</code>
* [.auto(opts)](#AcmeClient+auto) ⇒ <code>Promise.&lt;string&gt;</code>
<a name="new_AcmeClient_new"></a>
### new AcmeClient(opts)
| Param | Type | Description |
| --- | --- | --- |
| opts | <code>object</code> | |
| opts.directoryUrl | <code>string</code> | ACME directory URL |
| opts.accountKey | <code>buffer</code> \| <code>string</code> | PEM encoded account private key |
| [opts.accountUrl] | <code>string</code> | Account URL, default: `null` |
| [opts.externalAccountBinding] | <code>object</code> | |
| [opts.externalAccountBinding.kid] | <code>string</code> | External account binding KID |
| [opts.externalAccountBinding.hmacKey] | <code>string</code> | External account binding HMAC key |
| [opts.backoffAttempts] | <code>number</code> | Maximum number of backoff attempts, default: `10` |
| [opts.backoffMin] | <code>number</code> | Minimum backoff attempt delay in milliseconds, default: `5000` |
| [opts.backoffMax] | <code>number</code> | Maximum backoff attempt delay in milliseconds, default: `30000` |
**Example**
Create ACME client instance
```js
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: 'Private key goes here'
});
```
**Example**
Create ACME client instance
```js
const client = new acme.Client({
directoryUrl: acme.directory.letsencrypt.staging,
accountKey: 'Private key goes here',
accountUrl: 'Optional account URL goes here',
backoffAttempts: 10,
backoffMin: 5000,
backoffMax: 30000
});
```
**Example**
Create ACME client with external account binding
```js
const client = new acme.Client({
directoryUrl: 'https://acme-provider.example.com/directory-url',
accountKey: 'Private key goes here',
externalAccountBinding: {
kid: 'YOUR-EAB-KID',
hmacKey: 'YOUR-EAB-HMAC-KEY'
}
});
```
<a name="AcmeClient+getTermsOfServiceUrl"></a>
### acmeClient.getTermsOfServiceUrl() ⇒ <code>Promise.&lt;(string\|null)&gt;</code>
Get Terms of Service URL if available
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;(string\|null)&gt;</code> - ToS URL
**Example**
Get Terms of Service URL
```js
const termsOfService = client.getTermsOfServiceUrl();
if (!termsOfService) {
// CA did not provide Terms of Service
}
```
<a name="AcmeClient+getAccountUrl"></a>
### acmeClient.getAccountUrl() ⇒ <code>string</code>
Get current account URL
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>string</code> - Account URL
**Throws**:
- <code>Error</code> No account URL found
**Example**
Get current account URL
```js
try {
const accountUrl = client.getAccountUrl();
}
catch (e) {
// No account URL exists, need to create account first
}
```
<a name="AcmeClient+createAccount"></a>
### acmeClient.createAccount([data]) ⇒ <code>Promise.&lt;object&gt;</code>
Create a new account
https://tools.ietf.org/html/rfc8555#section-7.3
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Account
| Param | Type | Description |
| --- | --- | --- |
| [data] | <code>object</code> | Request data |
**Example**
Create a new account
```js
const account = await client.createAccount({
termsOfServiceAgreed: true
});
```
**Example**
Create a new account with contact info
```js
const account = await client.createAccount({
termsOfServiceAgreed: true,
contact: ['mailto:test@example.com']
});
```
<a name="AcmeClient+updateAccount"></a>
### acmeClient.updateAccount([data]) ⇒ <code>Promise.&lt;object&gt;</code>
Update existing account
https://tools.ietf.org/html/rfc8555#section-7.3.2
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Account
| Param | Type | Description |
| --- | --- | --- |
| [data] | <code>object</code> | Request data |
**Example**
Update existing account
```js
const account = await client.updateAccount({
contact: ['mailto:foo@example.com']
});
```
<a name="AcmeClient+updateAccountKey"></a>
### acmeClient.updateAccountKey(newAccountKey, [data]) ⇒ <code>Promise.&lt;object&gt;</code>
Update account private key
https://tools.ietf.org/html/rfc8555#section-7.3.5
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Account
| Param | Type | Description |
| --- | --- | --- |
| newAccountKey | <code>buffer</code> \| <code>string</code> | New PEM encoded private key |
| [data] | <code>object</code> | Additional request data |
**Example**
Update account private key
```js
const newAccountKey = 'New private key goes here';
const result = await client.updateAccountKey(newAccountKey);
```
<a name="AcmeClient+createOrder"></a>
### acmeClient.createOrder(data) ⇒ <code>Promise.&lt;object&gt;</code>
Create a new order
https://tools.ietf.org/html/rfc8555#section-7.4
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Order
| Param | Type | Description |
| --- | --- | --- |
| data | <code>object</code> | Request data |
**Example**
Create a new order
```js
const order = await client.createOrder({
identifiers: [
{ type: 'dns', value: 'example.com' },
{ type: 'dns', value: 'test.example.com' }
]
});
```
<a name="AcmeClient+getOrder"></a>
### acmeClient.getOrder(order) ⇒ <code>Promise.&lt;object&gt;</code>
Refresh order object from CA
https://tools.ietf.org/html/rfc8555#section-7.4
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Order
| Param | Type | Description |
| --- | --- | --- |
| order | <code>object</code> | Order object |
**Example**
```js
const order = { ... }; // Previously created order object
const result = await client.getOrder(order);
```
<a name="AcmeClient+finalizeOrder"></a>
### acmeClient.finalizeOrder(order, csr) ⇒ <code>Promise.&lt;object&gt;</code>
Finalize order
https://tools.ietf.org/html/rfc8555#section-7.4
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Order
| Param | Type | Description |
| --- | --- | --- |
| order | <code>object</code> | Order object |
| csr | <code>buffer</code> \| <code>string</code> | PEM encoded Certificate Signing Request |
**Example**
Finalize order
```js
const order = { ... }; // Previously created order object
const csr = { ... }; // Previously created Certificate Signing Request
const result = await client.finalizeOrder(order, csr);
```
<a name="AcmeClient+getAuthorizations"></a>
### acmeClient.getAuthorizations(order) ⇒ <code>Promise.&lt;Array.&lt;object&gt;&gt;</code>
Get identifier authorizations from order
https://tools.ietf.org/html/rfc8555#section-7.5
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;Array.&lt;object&gt;&gt;</code> - Authorizations
| Param | Type | Description |
| --- | --- | --- |
| order | <code>object</code> | Order |
**Example**
Get identifier authorizations
```js
const order = { ... }; // Previously created order object
const authorizations = await client.getAuthorizations(order);
authorizations.forEach((authz) => {
const { challenges } = authz;
});
```
<a name="AcmeClient+deactivateAuthorization"></a>
### acmeClient.deactivateAuthorization(authz) ⇒ <code>Promise.&lt;object&gt;</code>
Deactivate identifier authorization
https://tools.ietf.org/html/rfc8555#section-7.5.2
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Authorization
| Param | Type | Description |
| --- | --- | --- |
| authz | <code>object</code> | Identifier authorization |
**Example**
Deactivate identifier authorization
```js
const authz = { ... }; // Identifier authorization resolved from previously created order
const result = await client.deactivateAuthorization(authz);
```
<a name="AcmeClient+getChallengeKeyAuthorization"></a>
### acmeClient.getChallengeKeyAuthorization(challenge) ⇒ <code>Promise.&lt;string&gt;</code>
Get key authorization for ACME challenge
https://tools.ietf.org/html/rfc8555#section-8.1
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;string&gt;</code> - Key authorization
| Param | Type | Description |
| --- | --- | --- |
| challenge | <code>object</code> | Challenge object returned by API |
**Example**
Get challenge key authorization
```js
const challenge = { ... }; // Challenge from previously resolved identifier authorization
const key = await client.getChallengeKeyAuthorization(challenge);
// Write key somewhere to satisfy challenge
```
<a name="AcmeClient+verifyChallenge"></a>
### acmeClient.verifyChallenge(authz, challenge) ⇒ <code>Promise</code>
Verify that ACME challenge is satisfied
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
| Param | Type | Description |
| --- | --- | --- |
| authz | <code>object</code> | Identifier authorization |
| challenge | <code>object</code> | Authorization challenge |
**Example**
Verify satisfied ACME challenge
```js
const authz = { ... }; // Identifier authorization
const challenge = { ... }; // Satisfied challenge
await client.verifyChallenge(authz, challenge);
```
<a name="AcmeClient+completeChallenge"></a>
### acmeClient.completeChallenge(challenge) ⇒ <code>Promise.&lt;object&gt;</code>
Notify CA that challenge has been completed
https://tools.ietf.org/html/rfc8555#section-7.5.1
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Challenge
| Param | Type | Description |
| --- | --- | --- |
| challenge | <code>object</code> | Challenge object returned by API |
**Example**
Notify CA that challenge has been completed
```js
const challenge = { ... }; // Satisfied challenge
const result = await client.completeChallenge(challenge);
```
<a name="AcmeClient+waitForValidStatus"></a>
### acmeClient.waitForValidStatus(item) ⇒ <code>Promise.&lt;object&gt;</code>
Wait for ACME provider to verify status on a order, authorization or challenge
https://tools.ietf.org/html/rfc8555#section-7.5.1
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;object&gt;</code> - Valid order, authorization or challenge
| Param | Type | Description |
| --- | --- | --- |
| item | <code>object</code> | An order, authorization or challenge object |
**Example**
Wait for valid challenge status
```js
const challenge = { ... };
await client.waitForValidStatus(challenge);
```
**Example**
Wait for valid authoriation status
```js
const authz = { ... };
await client.waitForValidStatus(authz);
```
**Example**
Wait for valid order status
```js
const order = { ... };
await client.waitForValidStatus(order);
```
<a name="AcmeClient+getCertificate"></a>
### acmeClient.getCertificate(order, [preferredChain]) ⇒ <code>Promise.&lt;string&gt;</code>
Get certificate from ACME order
https://tools.ietf.org/html/rfc8555#section-7.4.2
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;string&gt;</code> - Certificate
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| order | <code>object</code> | | Order object |
| [preferredChain] | <code>string</code> | <code>null</code> | Indicate which certificate chain is preferred if a CA offers multiple, by exact issuer common name, default: `null` |
**Example**
Get certificate
```js
const order = { ... }; // Previously created order
const certificate = await client.getCertificate(order);
```
**Example**
Get certificate with preferred chain
```js
const order = { ... }; // Previously created order
const certificate = await client.getCertificate(order, 'DST Root CA X3');
```
<a name="AcmeClient+revokeCertificate"></a>
### acmeClient.revokeCertificate(cert, [data]) ⇒ <code>Promise</code>
Revoke certificate
https://tools.ietf.org/html/rfc8555#section-7.6
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
| Param | Type | Description |
| --- | --- | --- |
| cert | <code>buffer</code> \| <code>string</code> | PEM encoded certificate |
| [data] | <code>object</code> | Additional request data |
**Example**
Revoke certificate
```js
const certificate = { ... }; // Previously created certificate
const result = await client.revokeCertificate(certificate);
```
**Example**
Revoke certificate with reason
```js
const certificate = { ... }; // Previously created certificate
const result = await client.revokeCertificate(certificate, {
reason: 4
});
```
<a name="AcmeClient+auto"></a>
### acmeClient.auto(opts) ⇒ <code>Promise.&lt;string&gt;</code>
Auto mode
**Kind**: instance method of [<code>AcmeClient</code>](#AcmeClient)
**Returns**: <code>Promise.&lt;string&gt;</code> - Certificate
| Param | Type | Description |
| --- | --- | --- |
| opts | <code>object</code> | |
| opts.csr | <code>buffer</code> \| <code>string</code> | Certificate Signing Request |
| opts.challengeCreateFn | <code>function</code> | Function returning Promise triggered before completing ACME challenge |
| opts.challengeRemoveFn | <code>function</code> | Function returning Promise triggered after completing ACME challenge |
| [opts.email] | <code>string</code> | Account email address |
| [opts.termsOfServiceAgreed] | <code>boolean</code> | Agree to Terms of Service, default: `false` |
| [opts.skipChallengeVerification] | <code>boolean</code> | Skip internal challenge verification before notifying ACME provider, default: `false` |
| [opts.challengePriority] | <code>Array.&lt;string&gt;</code> | Array defining challenge type priority, default: `['http-01', 'dns-01']` |
| [opts.preferredChain] | <code>string</code> | Indicate which certificate chain is preferred if a CA offers multiple, by exact issuer common name, default: `null` |
**Example**
Order a certificate using auto mode
```js
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
commonName: 'test.example.com'
});
const certificate = await client.auto({
csr: certificateRequest,
email: 'test@example.com',
termsOfServiceAgreed: true,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {
// Satisfy challenge here
},
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'
});
const certificate = await client.auto({
csr: certificateRequest,
email: 'test@example.com',
termsOfServiceAgreed: true,
preferredChain: 'DST Root CA X3',
challengeCreateFn: async () => {},
challengeRemoveFn: async () => {}
});
```
<a name="Client"></a>
## Client : <code>object</code>
ACME client
**Kind**: global namespace

View File

@@ -0,0 +1,267 @@
## Objects
<dl>
<dt><a href="#crypto">crypto</a> : <code>object</code></dt>
<dd><p>Native Node.js crypto interface</p>
</dd>
</dl>
## Functions
<dl>
<dt><a href="#createPrivateRsaKey">createPrivateRsaKey([modulusLength])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private RSA key</p>
</dd>
<dt><a href="#createPrivateKey">createPrivateKey()</a></dt>
<dd><p>Alias of <code>createPrivateRsaKey()</code></p>
</dd>
<dt><a href="#createPrivateEcdsaKey">createPrivateEcdsaKey([namedCurve])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private ECDSA key</p>
</dd>
<dt><a href="#getPublicKey">getPublicKey(keyPem)</a> ⇒ <code>buffer</code></dt>
<dd><p>Get a public key derived from a RSA or ECDSA key</p>
</dd>
<dt><a href="#getJwk">getJwk(keyPem)</a> ⇒ <code>object</code></dt>
<dd><p>Get a JSON Web Key derived from a RSA or ECDSA key</p>
<p><a href="https://datatracker.ietf.org/doc/html/rfc7517">https://datatracker.ietf.org/doc/html/rfc7517</a></p>
</dd>
<dt><a href="#splitPemChain">splitPemChain(chainPem)</a> ⇒ <code>array</code></dt>
<dd><p>Split chain of PEM encoded objects from string into array</p>
</dd>
<dt><a href="#getPemBodyAsB64u">getPemBodyAsB64u(pem)</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object and return a Base64URL string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains(csrPem)</a> ⇒ <code>object</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo(certPem)</a> ⇒ <code>object</code></dt>
<dd><p>Read information from a certificate
If multiple certificates are chained, the first will be read</p>
</dd>
<dt><a href="#createCsr">createCsr(data, [keyPem])</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
</dl>
<a name="crypto"></a>
## crypto : <code>object</code>
Native Node.js crypto interface
**Kind**: global namespace
<a name="createPrivateRsaKey"></a>
## createPrivateRsaKey([modulusLength]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [modulusLength] | <code>number</code> | <code>2048</code> | Size of the keys modulus in bits, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.crypto.createPrivateRsaKey();
```
**Example**
Private RSA key with modulus size 4096
```js
const privateKey = await acme.crypto.createPrivateRsaKey(4096);
```
<a name="createPrivateKey"></a>
## createPrivateKey()
Alias of `createPrivateRsaKey()`
**Kind**: global function
<a name="createPrivateEcdsaKey"></a>
## createPrivateEcdsaKey([namedCurve]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private ECDSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private ECDSA key
| Param | Type | Description |
| --- | --- | --- |
| [namedCurve] | <code>string</code> | ECDSA curve name (P-256, P-384 or P-521), default `P-256` |
**Example**
Generate private ECDSA key
```js
const privateKey = await acme.crypto.createPrivateEcdsaKey();
```
**Example**
Private ECDSA key using P-384 curve
```js
const privateKey = await acme.crypto.createPrivateEcdsaKey('P-384');
```
<a name="getPublicKey"></a>
## getPublicKey(keyPem) ⇒ <code>buffer</code>
Get a public key derived from a RSA or ECDSA key
**Kind**: global function
**Returns**: <code>buffer</code> - PEM encoded public key
| Param | Type | Description |
| --- | --- | --- |
| keyPem | <code>buffer</code> \| <code>string</code> | PEM encoded private or public key |
**Example**
Get public key
```js
const publicKey = acme.crypto.getPublicKey(privateKey);
```
<a name="getJwk"></a>
## getJwk(keyPem) ⇒ <code>object</code>
Get a JSON Web Key derived from a RSA or ECDSA key
https://datatracker.ietf.org/doc/html/rfc7517
**Kind**: global function
**Returns**: <code>object</code> - JSON Web Key
| Param | Type | Description |
| --- | --- | --- |
| keyPem | <code>buffer</code> \| <code>string</code> | PEM encoded private or public key |
**Example**
Get JWK
```js
const jwk = acme.crypto.getJwk(privateKey);
```
<a name="splitPemChain"></a>
## splitPemChain(chainPem) ⇒ <code>array</code>
Split chain of PEM encoded objects from string into array
**Kind**: global function
**Returns**: <code>array</code> - Array of PEM objects including headers
| Param | Type | Description |
| --- | --- | --- |
| chainPem | <code>buffer</code> \| <code>string</code> | PEM encoded object chain |
<a name="getPemBodyAsB64u"></a>
## getPemBodyAsB64u(pem) ⇒ <code>string</code>
Parse body of PEM encoded object and return a Base64URL string
If multiple objects are chained, the first body will be returned
**Kind**: global function
**Returns**: <code>string</code> - Base64URL-encoded body
| Param | Type | Description |
| --- | --- | --- |
| pem | <code>buffer</code> \| <code>string</code> | PEM encoded chain or object |
<a name="readCsrDomains"></a>
## readCsrDomains(csrPem) ⇒ <code>object</code>
Read domains from a Certificate Signing Request
**Kind**: global function
**Returns**: <code>object</code> - {commonName, altNames}
| Param | Type | Description |
| --- | --- | --- |
| csrPem | <code>buffer</code> \| <code>string</code> | PEM encoded Certificate Signing Request |
**Example**
Read Certificate Signing Request domains
```js
const { commonName, altNames } = acme.crypto.readCsrDomains(certificateRequest);
console.log(`Common name: ${commonName}`);
console.log(`Alt names: ${altNames.join(', ')}`);
```
<a name="readCertificateInfo"></a>
## readCertificateInfo(certPem) ⇒ <code>object</code>
Read information from a certificate
If multiple certificates are chained, the first will be read
**Kind**: global function
**Returns**: <code>object</code> - Certificate info
| Param | Type | Description |
| --- | --- | --- |
| certPem | <code>buffer</code> \| <code>string</code> | PEM encoded certificate or chain |
**Example**
Read certificate information
```js
const info = acme.crypto.readCertificateInfo(certificate);
const { commonName, altNames } = info.domains;
console.log(`Not after: ${info.notAfter}`);
console.log(`Not before: ${info.notBefore}`);
console.log(`Common name: ${commonName}`);
console.log(`Alt names: ${altNames.join(', ')}`);
```
<a name="createCsr"></a>
## createCsr(data, [keyPem]) ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code>
Create a Certificate Signing Request
**Kind**: global function
**Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest]
| Param | Type | Description |
| --- | --- | --- |
| data | <code>object</code> | |
| [data.keySize] | <code>number</code> | Size of newly created RSA private key modulus in bits, default: `2048` |
| [data.commonName] | <code>string</code> | FQDN of your server |
| [data.altNames] | <code>array</code> | SAN (Subject Alternative Names), default: `[]` |
| [data.country] | <code>string</code> | 2 letter country code |
| [data.state] | <code>string</code> | State or province |
| [data.locality] | <code>string</code> | City |
| [data.organization] | <code>string</code> | Organization name |
| [data.organizationUnit] | <code>string</code> | Organizational unit name |
| [data.emailAddress] | <code>string</code> | Email address |
| [keyPem] | <code>string</code> | PEM encoded CSR private key |
**Example**
Create a Certificate Signing Request
```js
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
commonName: 'test.example.com'
});
```
**Example**
Certificate Signing Request with both common and alternative names
```js
const [certificateKey, certificateRequest] = await acme.crypto.createCsr({
keySize: 4096,
commonName: 'test.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',
country: 'US',
state: 'California',
locality: 'Los Angeles',
organization: 'The Company Inc.',
organizationUnit: 'IT Department',
emailAddress: 'contact@example.com'
});
```
**Example**
Certificate Signing Request with ECDSA private key
```js
const certificateKey = await acme.crypto.createPrivateEcdsaKey();
const [, certificateRequest] = await acme.crypto.createCsr({
commonName: 'test.example.com'
}, certificateKey);

View File

@@ -0,0 +1,257 @@
## Objects
<dl>
<dt><a href="#forge">forge</a> : <code>object</code></dt>
<dd><p>Legacy node-forge crypto interface</p>
<p>DEPRECATION WARNING: This crypto interface is deprecated and will be removed from acme-client in a future
major release. Please migrate to the new <code>acme.crypto</code> interface at your earliest convenience.</p>
</dd>
</dl>
## Functions
<dl>
<dt><a href="#createPrivateKey">createPrivateKey([size])</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Generate a private RSA key</p>
</dd>
<dt><a href="#createPublicKey">createPublicKey(key)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Create public key from a private RSA key</p>
</dd>
<dt><a href="#getPemBody">getPemBody(str)</a> ⇒ <code>string</code></dt>
<dd><p>Parse body of PEM encoded object from buffer or string
If multiple objects are chained, the first body will be returned</p>
</dd>
<dt><a href="#splitPemChain">splitPemChain(str)</a> ⇒ <code>Array.&lt;string&gt;</code></dt>
<dd><p>Split chain of PEM encoded objects from buffer or string into array</p>
</dd>
<dt><a href="#getModulus">getModulus(input)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get modulus</p>
</dd>
<dt><a href="#getPublicExponent">getPublicExponent(input)</a> ⇒ <code>Promise.&lt;buffer&gt;</code></dt>
<dd><p>Get public exponent</p>
</dd>
<dt><a href="#readCsrDomains">readCsrDomains(csr)</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read domains from a Certificate Signing Request</p>
</dd>
<dt><a href="#readCertificateInfo">readCertificateInfo(cert)</a> ⇒ <code>Promise.&lt;object&gt;</code></dt>
<dd><p>Read information from a certificate</p>
</dd>
<dt><a href="#createCsr">createCsr(data, [key])</a> ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code></dt>
<dd><p>Create a Certificate Signing Request</p>
</dd>
</dl>
<a name="forge"></a>
## forge : <code>object</code>
Legacy node-forge crypto interface
DEPRECATION WARNING: This crypto interface is deprecated and will be removed from acme-client in a future
major release. Please migrate to the new `acme.crypto` interface at your earliest convenience.
**Kind**: global namespace
<a name="createPrivateKey"></a>
## createPrivateKey([size]) ⇒ <code>Promise.&lt;buffer&gt;</code>
Generate a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded private RSA key
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [size] | <code>number</code> | <code>2048</code> | Size of the key, default: `2048` |
**Example**
Generate private RSA key
```js
const privateKey = await acme.forge.createPrivateKey();
```
**Example**
Private RSA key with defined size
```js
const privateKey = await acme.forge.createPrivateKey(4096);
```
<a name="createPublicKey"></a>
## createPublicKey(key) ⇒ <code>Promise.&lt;buffer&gt;</code>
Create public key from a private RSA key
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - PEM encoded public RSA key
| Param | Type | Description |
| --- | --- | --- |
| key | <code>buffer</code> \| <code>string</code> | PEM encoded private RSA key |
**Example**
Create public key
```js
const publicKey = await acme.forge.createPublicKey(privateKey);
```
<a name="getPemBody"></a>
## getPemBody(str) ⇒ <code>string</code>
Parse body of PEM encoded object from buffer or string
If multiple objects are chained, the first body will be returned
**Kind**: global function
**Returns**: <code>string</code> - PEM body
| Param | Type | Description |
| --- | --- | --- |
| str | <code>buffer</code> \| <code>string</code> | PEM encoded buffer or string |
<a name="splitPemChain"></a>
## splitPemChain(str) ⇒ <code>Array.&lt;string&gt;</code>
Split chain of PEM encoded objects from buffer or string into array
**Kind**: global function
**Returns**: <code>Array.&lt;string&gt;</code> - Array of PEM bodies
| Param | Type | Description |
| --- | --- | --- |
| str | <code>buffer</code> \| <code>string</code> | PEM encoded buffer or string |
<a name="getModulus"></a>
## getModulus(input) ⇒ <code>Promise.&lt;buffer&gt;</code>
Get modulus
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - Modulus
| Param | Type | Description |
| --- | --- | --- |
| input | <code>buffer</code> \| <code>string</code> | PEM encoded private key, certificate or CSR |
**Example**
Get modulus
```js
const m1 = await acme.forge.getModulus(privateKey);
const m2 = await acme.forge.getModulus(certificate);
const m3 = await acme.forge.getModulus(certificateRequest);
```
<a name="getPublicExponent"></a>
## getPublicExponent(input) ⇒ <code>Promise.&lt;buffer&gt;</code>
Get public exponent
**Kind**: global function
**Returns**: <code>Promise.&lt;buffer&gt;</code> - Exponent
| Param | Type | Description |
| --- | --- | --- |
| input | <code>buffer</code> \| <code>string</code> | PEM encoded private key, certificate or CSR |
**Example**
Get public exponent
```js
const e1 = await acme.forge.getPublicExponent(privateKey);
const e2 = await acme.forge.getPublicExponent(certificate);
const e3 = await acme.forge.getPublicExponent(certificateRequest);
```
<a name="readCsrDomains"></a>
## readCsrDomains(csr) ⇒ <code>Promise.&lt;object&gt;</code>
Read domains from a Certificate Signing Request
**Kind**: global function
**Returns**: <code>Promise.&lt;object&gt;</code> - {commonName, altNames}
| Param | Type | Description |
| --- | --- | --- |
| csr | <code>buffer</code> \| <code>string</code> | PEM encoded Certificate Signing Request |
**Example**
Read Certificate Signing Request domains
```js
const { commonName, altNames } = await acme.forge.readCsrDomains(certificateRequest);
console.log(`Common name: ${commonName}`);
console.log(`Alt names: ${altNames.join(', ')}`);
```
<a name="readCertificateInfo"></a>
## readCertificateInfo(cert) ⇒ <code>Promise.&lt;object&gt;</code>
Read information from a certificate
**Kind**: global function
**Returns**: <code>Promise.&lt;object&gt;</code> - Certificate info
| Param | Type | Description |
| --- | --- | --- |
| cert | <code>buffer</code> \| <code>string</code> | PEM encoded certificate |
**Example**
Read certificate information
```js
const info = await acme.forge.readCertificateInfo(certificate);
const { commonName, altNames } = info.domains;
console.log(`Not after: ${info.notAfter}`);
console.log(`Not before: ${info.notBefore}`);
console.log(`Common name: ${commonName}`);
console.log(`Alt names: ${altNames.join(', ')}`);
```
<a name="createCsr"></a>
## createCsr(data, [key]) ⇒ <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code>
Create a Certificate Signing Request
**Kind**: global function
**Returns**: <code>Promise.&lt;Array.&lt;buffer&gt;&gt;</code> - [privateKey, certificateSigningRequest]
| Param | Type | Description |
| --- | --- | --- |
| data | <code>object</code> | |
| [data.keySize] | <code>number</code> | Size of newly created private key, default: `2048` |
| [data.commonName] | <code>string</code> | |
| [data.altNames] | <code>array</code> | default: `[]` |
| [data.country] | <code>string</code> | |
| [data.state] | <code>string</code> | |
| [data.locality] | <code>string</code> | |
| [data.organization] | <code>string</code> | |
| [data.organizationUnit] | <code>string</code> | |
| [data.emailAddress] | <code>string</code> | |
| [key] | <code>buffer</code> \| <code>string</code> | CSR private key |
**Example**
Create a Certificate Signing Request
```js
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
commonName: 'test.example.com'
});
```
**Example**
Certificate Signing Request with both common and alternative names
```js
const [certificateKey, certificateRequest] = await acme.forge.createCsr({
keySize: 4096,
commonName: 'test.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',
country: 'US',
state: 'California',
locality: 'Los Angeles',
organization: 'The Company Inc.',
organizationUnit: 'IT Department',
emailAddress: 'contact@example.com'
});
```
**Example**
Certificate Signing Request with predefined private key
```js
const certificateKey = await acme.forge.createPrivateKey();
const [, certificateRequest] = await acme.forge.createCsr({
commonName: 'test.example.com'
}, certificateKey);

View File

@@ -0,0 +1,98 @@
# Upgrading to v5 of `acme-client`
This document outlines the breaking changes introduced in v5 of `acme-client`, why they were introduced and what you should look out for when upgrading your application.
First off this release drops support for Node LTS v10, v12 and v14, and the reason for that is a new native crypto interface - more on that below. Since Node v14 is still currently in maintenance mode, `acme-client` v4 will continue to receive security updates and bugfixes until (at least) Node v14 reaches its end-of-line.
## New native crypto interface
A new crypto interface has been introduced with v5, which you can find under `acme.crypto`. It uses native Node.js cryptography APIs to generate private keys, JSON Web Keys and signatures, and finally enables support for ECC/ECDSA (P-256, P384 and P521), both for account private keys and certificates. The [jsrsasign](https://www.npmjs.com/package/jsrsasign) module is used to handle generation and parsing of Certificate Signing Requests.
Full documentation of `acme.crypto` can be [found here](crypto.md).
Since the release of `acme-client` v1.0.0 the crypto interface API has remained mostly unaltered. Back then an OpenSSL CLI wrapper was used to generate keys, and very much has changed since. This has naturally resulted in a buildup of technical debt and slight API inconsistencies over time. The introduction of a new interface was a good opportunity to finally clean up these APIs.
Below you will find a table summarizing the current `acme.forge` methods, and their new `acme.crypto` replacements. A summary of the changes for each method, including examples on how to migrate, can be found following the table.
*Note: The now deprecated `acme.forge` interface is still available for use in v5, and will not be removed until a future major version, most likely v6. Should you not wish to change to the new interface right away, the following breaking changes will not immediately affect you.*
- :green_circle: = API functionality unchanged between `acme.forge` and `acme.crypto`
- :orange_circle: = Slight API changes, like depromising or renaming, action may be required
- :red_circle: = Breaking API changes or removal, action required if using these methods
| Deprecated `.forge` API | New `.crypto` API | State |
| ----------------------------- | ----------------------------- | --------------------- |
| `await createPrivateKey()` | `await createPrivateKey()` | :green_circle: |
| `await createPublicKey()` | `getPublicKey()` | :orange_circle: (1) |
| `getPemBody()` | `getPemBodyAsB64u()` | :red_circle: (2) |
| `splitPemChain()` | `splitPemChain()` | :green_circle: |
| `await getModulus()` | `getJwk()` | :red_circle: (3) |
| `await getPublicExponent()` | `getJwk()` | :red_circle: (3) |
| `await readCsrDomains()` | `readCsrDomains()` | :orange_circle: (4) |
| `await readCertificateInfo()` | `readCertificateInfo()` | :orange_circle: (4) |
| `await createCsr()` | `await createCsr()` | :green_circle: |
### 1. `createPublicKey` renamed and depromised
* The method `createPublicKey()` has been renamed to `getPublicKey()`
* No longer returns a promise, but the resulting public key directly
* This is non-breaking if called with `await`, since `await` does not require its operand to be a promise
* :orange_circle: **This is a breaking change if used with `.then()` or `.catch()`**
```js
// Before
const publicKey = await acme.forge.createPublicKey(privateKey);
// After
const publicKey = acme.crypto.getPublicKey(privateKey);
```
### 2. `getPemBody` renamed, now returns Base64URL
* Method `getPemBody()` has been renamed to `getPemBodyAsB64u()`
* Instead of a Base64-encoded PEM body, now returns a Base64URL-encoded PEM body
* :red_circle: **This is a breaking change**
```js
// Before
const body = acme.forge.getPemBody(pem);
// After
const body = acme.crypto.getPemBodyAsB64u(pem);
```
### 3. `getModulus` and `getPublicExponent` merged into `getJwk`
* Methods `getModulus()` and `getPublicExponent()` have been removed
* Replaced by new method `getJwk()`
* :red_circle: **This is a breaking change**
```js
// Before
const mod = await acme.forge.getModulus(key);
const exp = await acme.forge.getPublicExponent(key);
// After
const { e, n } = acme.crypto.getJwk(key);
```
### 4. `readCsrDomains` and `readCertificateInfo` depromised
* Methods `readCsrDomains()` and `readCertificateInfo()` no longer return promises, but their resulting payloads directly
* This is non-breaking if called with `await`, since `await` does not require its operand to be a promise
* :orange_circle: **This is a breaking change if used with `.then()` or `.catch()`**
```js
// Before
const domains = await acme.forge.readCsrDomains(csr);
const info = await acme.forge.readCertificateInfo(certificate);
// After
const domains = acme.crypto.readCsrDomains(csr);
const info = acme.crypto.readCertificateInfo(certificate);
```