acme-client
Advanced tools
Comparing version 4.2.5 to 5.0.0
@@ -5,3 +5,3 @@ { | ||
"author": "nmorsman", | ||
"version": "4.2.5", | ||
"version": "5.0.0", | ||
"main": "src/index.js", | ||
@@ -12,3 +12,3 @@ "types": "types", | ||
"engines": { | ||
"node": ">= 10" | ||
"node": ">= 16" | ||
}, | ||
@@ -20,24 +20,23 @@ "files": [ | ||
"dependencies": { | ||
"axios": "0.26.1", | ||
"backo2": "^1.0.0", | ||
"bluebird": "^3.5.0", | ||
"axios": "0.27.2", | ||
"debug": "^4.1.1", | ||
"node-forge": "^1.3.0" | ||
"jsrsasign": "^10.5.26", | ||
"node-forge": "^1.3.1" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^14.0.5", | ||
"chai": "^4.1.0", | ||
"chai-as-promised": "^7.0.0", | ||
"dtslint": "^4.0.0", | ||
"eslint": "^7.1.0", | ||
"eslint-config-airbnb-base": "^14.0.0", | ||
"eslint-plugin-import": "^2.10.0", | ||
"jsdoc-to-markdown": "^6.0.1", | ||
"mocha": "^8.1.3", | ||
"nock": "^13.0.4", | ||
"typescript": "^4.0.2", | ||
"uuid": "^8.1.0" | ||
"@types/node": "^18.6.1", | ||
"chai": "^4.3.6", | ||
"chai-as-promised": "^7.1.1", | ||
"dtslint": "^4.2.1", | ||
"eslint": "^8.11.0", | ||
"eslint-config-airbnb-base": "^15.0.0", | ||
"eslint-plugin-import": "^2.25.4", | ||
"jsdoc-to-markdown": "^7.1.1", | ||
"mocha": "^10.0.0", | ||
"nock": "^13.2.4", | ||
"typescript": "^4.6.2", | ||
"uuid": "^8.3.2" | ||
}, | ||
"scripts": { | ||
"build-docs": "jsdoc2md src/client.js > docs/client.md && jsdoc2md src/crypto/forge.js > docs/forge.md", | ||
"build-docs": "jsdoc2md src/client.js > docs/client.md && jsdoc2md src/crypto/index.js > docs/crypto.md && jsdoc2md src/crypto/forge.js > docs/forge.md", | ||
"lint": "eslint .", | ||
@@ -44,0 +43,0 @@ "lint-types": "dtslint types", |
@@ -11,10 +11,18 @@ # acme-client [![CircleCI](https://circleci.com/gh/publishlab/node-acme-client.svg?style=svg)](https://circleci.com/gh/publishlab/node-acme-client) | ||
## Important upgrade notice | ||
On September 15, 2022, Let's Encrypt will stop accepting Certificate Signing Requests signed using the obsolete SHA-1 hash. This change affects all `acme-client` versions lower than `3.3.2` and `4.2.4`. Please upgrade ASAP to ensure that your certificates can still be issued following this date. | ||
A more detailed explanation can be found [at the Let's Encrypt forums](https://community.letsencrypt.org/t/rejecting-sha-1-csrs-and-validation-using-tls-1-0-1-1-urls/175144). | ||
### Compatibility | ||
| acme-client | API | Style | Node.js | | ||
| ------------- | --------- | --------- | ------- | | ||
| v4.x | ACMEv2 | Promise | >= v10 | | ||
| v3.x | ACMEv2 | Promise | >= v8 | | ||
| v2.x | ACMEv2 | Promise | >= v4 | | ||
| v1.x | ACMEv1 | callback | >= v4 | | ||
| 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) | | ||
@@ -30,2 +38,3 @@ | ||
* [Cryptography](#cryptography) | ||
* [Legacy .forge interface](#legacy-forge-interface) | ||
* [Auto mode](#auto-mode) | ||
@@ -113,15 +122,13 @@ * [Challenge priority](#challenge-priority) | ||
For key pair generation and Certificate Signing Requests, `acme-client` uses [node-forge](https://www.npmjs.com/package/node-forge), a pure JavaScript implementation of the TLS protocol. | ||
For key pairs `acme-client` utilizes native Node.js cryptography APIs, supporting signing and generation of both RSA and ECDSA keys. The module [jsrsasign](https://www.npmjs.com/package/jsrsasign) is used to generate and parse Certificate Signing Requests. | ||
These utility methods are exposed through `.forge`. | ||
These utility methods are exposed through `.crypto`. | ||
__API documentation: [docs/forge.md](docs/forge.md)__ | ||
* __Documentation: [docs/crypto.md](docs/crypto.md)__ | ||
#### Example | ||
```js | ||
const privateKey = await acme.forge.createPrivateKey(); | ||
const privateRsaKey = await acme.crypto.createPrivateRsaKey(); | ||
const privateEcdsaKey = await acme.crypto.createPrivateEcdsaKey(); | ||
const [certificateKey, certificateCsr] = await acme.forge.createCsr({ | ||
const [certificateKey, certificateCsr] = await acme.crypto.createCsr({ | ||
commonName: '*.example.com', | ||
@@ -133,13 +140,18 @@ altNames: ['example.com'] | ||
## Auto mode | ||
### Legacy `.forge` interface | ||
For convenience an `auto()` method is included in the client that takes a single config object. This method will handle the entire process of getting a certificate for one or multiple domains. | ||
The legacy `node-forge` crypto interface is still available for backward compatibility, however this interface is now considered deprecated and will be removed in a future major version of `acme-client`. | ||
A full example can be found at [examples/auto.js](examples/auto.js). | ||
You should consider migrating to the new `.crypto` API at your earliest convenience. More details can be found in the [acme-client v5 upgrade guide](docs/upgrade-v5.md). | ||
__Documentation: [docs/client.md#AcmeClient+auto](docs/client.md#AcmeClient+auto)__ | ||
* __Documentation: [docs/forge.md](docs/forge.md)__ | ||
#### Example | ||
## Auto mode | ||
For convenience an `auto()` method is included in the client that takes a single config object. This method will handle the entire process of getting a certificate for one or multiple domains. | ||
* __Documentation: [docs/client.md#AcmeClient+auto](docs/client.md#AcmeClient+auto)__ | ||
* __Full example: [examples/auto.js](examples/auto.js)__ | ||
```js | ||
@@ -194,9 +206,5 @@ const autoOpts = { | ||
A full example can be found at [examples/api.js](examples/api.js). | ||
* __Documentation: [docs/client.md](docs/client.md)__ | ||
* __Full example: [examples/api.js](examples/api.js)__ | ||
__API documentation: [docs/client.md](docs/client.md)__ | ||
#### Example | ||
```js | ||
@@ -203,0 +211,0 @@ const account = await client.createAccount({ |
@@ -5,5 +5,4 @@ /** | ||
const Promise = require('bluebird'); | ||
const { readCsrDomains } = require('./crypto'); | ||
const { log } = require('./logger'); | ||
const forge = require('./crypto/forge'); | ||
@@ -64,3 +63,3 @@ const defaultOpts = { | ||
log('[auto] Parsing domains from Certificate Signing Request'); | ||
const csrDomains = await forge.readCsrDomains(opts.csr); | ||
const csrDomains = readCsrDomains(opts.csr); | ||
const domains = [csrDomains.commonName].concat(csrDomains.altNames); | ||
@@ -67,0 +66,0 @@ const uniqueDomains = Array.from(new Set(domains)); |
@@ -7,4 +7,4 @@ /** | ||
const crypto = require('crypto'); | ||
const Promise = require('bluebird'); | ||
const { createHash } = require('crypto'); | ||
const { getPemBodyAsB64u } = require('./crypto'); | ||
const { log } = require('./logger'); | ||
@@ -16,3 +16,2 @@ const HttpClient = require('./http'); | ||
const auto = require('./auto'); | ||
const forge = require('./crypto/forge'); | ||
@@ -270,12 +269,9 @@ | ||
/* Get new JWK */ | ||
/* Get old JWK */ | ||
data.account = accountUrl; | ||
data.oldKey = await this.http.getJwk(); | ||
data.oldKey = this.http.getJwk(); | ||
/* TODO: Backward-compatibility with draft-ietf-acme-12, remove this in a later release */ | ||
data.newKey = await newHttpClient.getJwk(); | ||
/* Get signed request body from new client */ | ||
const url = await newHttpClient.getResourceUrl('keyChange'); | ||
const body = await newHttpClient.createSignedRsaBody(url, data); | ||
const body = newHttpClient.createSignedBody(url, data); | ||
@@ -378,5 +374,3 @@ /* Change key using old client */ | ||
const body = forge.getPemBody(csr); | ||
const data = { csr: util.b64escape(body) }; | ||
const data = { csr: getPemBodyAsB64u(csr) }; | ||
const resp = await this.api.finalizeOrder(order.finalize, data); | ||
@@ -410,3 +404,3 @@ | ||
async getAuthorizations(order) { | ||
return Promise.map((order.authorizations || []), async (url) => { | ||
return Promise.all((order.authorizations || []).map(async (url) => { | ||
const resp = await this.api.getAuthorization(url); | ||
@@ -417,3 +411,3 @@ | ||
return resp.data; | ||
}); | ||
})); | ||
} | ||
@@ -472,5 +466,5 @@ | ||
async getChallengeKeyAuthorization(challenge) { | ||
const jwk = await this.http.getJwk(); | ||
const keysum = crypto.createHash('sha256').update(JSON.stringify(jwk)); | ||
const thumbprint = util.b64escape(keysum.digest('base64')); | ||
const jwk = this.http.getJwk(); | ||
const keysum = createHash('sha256').update(JSON.stringify(jwk)); | ||
const thumbprint = keysum.digest('base64url'); | ||
const result = `${challenge.token}.${thumbprint}`; | ||
@@ -492,4 +486,4 @@ | ||
if ((challenge.type === 'dns-01') || (challenge.type === 'tls-alpn-01')) { | ||
const shasum = crypto.createHash('sha256').update(result); | ||
return util.b64escape(shasum.digest('base64')); | ||
const shasum = createHash('sha256').update(result); | ||
return shasum.digest('base64url'); | ||
} | ||
@@ -650,3 +644,3 @@ | ||
const alternateLinks = util.parseLinkHeader(resp.headers.link); | ||
const alternates = await Promise.map(alternateLinks, async (link) => this.api.apiRequest(link, null, [200])); | ||
const alternates = await Promise.all(alternateLinks.map(async (link) => this.api.apiRequest(link, null, [200]))); | ||
const certificates = [resp].concat(alternates).map((c) => c.data); | ||
@@ -687,5 +681,3 @@ | ||
async revokeCertificate(cert, data = {}) { | ||
const body = forge.getPemBody(cert); | ||
data.certificate = util.b64escape(body); | ||
data.certificate = getPemBodyAsB64u(cert); | ||
const resp = await this.api.revokeCert(data); | ||
@@ -712,3 +704,3 @@ return resp.data; | ||
* ```js | ||
* const [certificateKey, certificateRequest] = await acme.forge.createCsr({ | ||
* const [certificateKey, certificateRequest] = await acme.crypto.createCsr({ | ||
* commonName: 'test.example.com' | ||
@@ -732,3 +724,3 @@ * }); | ||
* ```js | ||
* const [certificateKey, certificateRequest] = await acme.forge.createCsr({ | ||
* const [certificateKey, certificateRequest] = await acme.crypto.createCsr({ | ||
* commonName: 'test.example.com' | ||
@@ -735,0 +727,0 @@ * }); |
/** | ||
* node-forge crypto engine | ||
* 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. | ||
* | ||
* @namespace forge | ||
@@ -8,6 +11,6 @@ */ | ||
const net = require('net'); | ||
const Promise = require('bluebird'); | ||
const { promisify } = require('util'); | ||
const forge = require('node-forge'); | ||
const generateKeyPair = Promise.promisify(forge.pki.rsa.generateKeyPair); | ||
const generateKeyPair = promisify(forge.pki.rsa.generateKeyPair); | ||
@@ -146,3 +149,3 @@ | ||
/** | ||
* Parse body of PEM encoded object form buffer or string | ||
* Parse body of PEM encoded object from buffer or string | ||
* If multiple objects are chained, the first body will be returned | ||
@@ -149,0 +152,0 @@ * |
@@ -5,7 +5,6 @@ /** | ||
const crypto = require('crypto'); | ||
const { createHmac, createSign, constants: { RSA_PKCS1_PADDING } } = require('crypto'); | ||
const { getJwk } = require('./crypto'); | ||
const { log } = require('./logger'); | ||
const axios = require('./axios'); | ||
const util = require('./util'); | ||
const forge = require('./crypto/forge'); | ||
@@ -94,19 +93,10 @@ | ||
* | ||
* @returns {Promise<object>} {e, kty, n} | ||
* @returns {object} JSON Web Key | ||
*/ | ||
async getJwk() { | ||
if (this.jwk) { | ||
return this.jwk; | ||
getJwk() { | ||
if (!this.jwk) { | ||
this.jwk = getJwk(this.accountKey); | ||
} | ||
const exponent = await forge.getPublicExponent(this.accountKey); | ||
const modulus = await forge.getModulus(this.accountKey); | ||
this.jwk = { | ||
e: util.b64encode(exponent), | ||
kty: 'RSA', | ||
n: util.b64encode(modulus) | ||
}; | ||
return this.jwk; | ||
@@ -181,6 +171,6 @@ } | ||
* @param {string} [opts.kid] JWS KID | ||
* @returns {Promise<object>} Signed HTTP request body | ||
* @returns {object} Signed HTTP request body | ||
*/ | ||
async prepareSignedBody(alg, url, payload = null, { nonce = null, kid = null } = {}) { | ||
prepareSignedBody(alg, url, payload = null, { nonce = null, kid = null } = {}) { | ||
const header = { alg, url }; | ||
@@ -199,3 +189,3 @@ | ||
else { | ||
header.jwk = await this.getJwk(); | ||
header.jwk = this.getJwk(); | ||
} | ||
@@ -205,4 +195,4 @@ | ||
return { | ||
payload: payload ? util.b64encode(JSON.stringify(payload)) : '', | ||
protected: util.b64encode(JSON.stringify(header)) | ||
payload: payload ? Buffer.from(JSON.stringify(payload)).toString('base64url') : '', | ||
protected: Buffer.from(JSON.stringify(header)).toString('base64url') | ||
}; | ||
@@ -213,3 +203,3 @@ } | ||
/** | ||
* Create signed HMAC HTTP request body | ||
* Create JWS HTTP request body using HMAC | ||
* | ||
@@ -222,11 +212,11 @@ * @param {string} hmacKey HMAC key | ||
* @param {string} [opts.kid] JWS KID | ||
* @returns {Promise<object>} Signed HMAC request body | ||
* @returns {object} Signed HMAC request body | ||
*/ | ||
async createSignedHmacBody(hmacKey, url, payload = null, { nonce = null, kid = null } = {}) { | ||
const result = await this.prepareSignedBody('HS256', url, payload, { nonce, kid }); | ||
createSignedHmacBody(hmacKey, url, payload = null, { nonce = null, kid = null } = {}) { | ||
const result = this.prepareSignedBody('HS256', url, payload, { nonce, kid }); | ||
/* Signature */ | ||
const signer = crypto.createHmac('SHA256', Buffer.from(hmacKey, 'base64')).update(`${result.protected}.${result.payload}`, 'utf8'); | ||
result.signature = util.b64encode(signer.digest()); | ||
const signer = createHmac('SHA256', Buffer.from(hmacKey, 'base64')).update(`${result.protected}.${result.payload}`, 'utf8'); | ||
result.signature = signer.digest().toString('base64url'); | ||
@@ -238,4 +228,6 @@ return result; | ||
/** | ||
* Create signed RSA HTTP request body | ||
* Create JWS HTTP request body using RSA or ECC | ||
* | ||
* https://datatracker.ietf.org/doc/html/rfc7515 | ||
* | ||
* @param {string} url Request URL | ||
@@ -246,12 +238,35 @@ * @param {object} [payload] Request payload | ||
* @param {string} [opts.kid] JWS KID | ||
* @returns {Promise<object>} Signed RSA request body | ||
* @returns {object} JWS request body | ||
*/ | ||
async createSignedRsaBody(url, payload = null, { nonce = null, kid = null } = {}) { | ||
const result = await this.prepareSignedBody('RS256', url, payload, { nonce, kid }); | ||
createSignedBody(url, payload = null, { nonce = null, kid = null } = {}) { | ||
const jwk = this.getJwk(); | ||
let headerAlg = 'RS256'; | ||
let signerAlg = 'SHA256'; | ||
/* Signature */ | ||
const signer = crypto.createSign('RSA-SHA256').update(`${result.protected}.${result.payload}`, 'utf8'); | ||
result.signature = util.b64escape(signer.sign(this.accountKey, 'base64')); | ||
/* https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 */ | ||
if (jwk.crv && (jwk.kty === 'EC')) { | ||
headerAlg = 'ES256'; | ||
if (jwk.crv === 'P-384') { | ||
headerAlg = 'ES384'; | ||
signerAlg = 'SHA384'; | ||
} | ||
else if (jwk.crv === 'P-521') { | ||
headerAlg = 'ES512'; | ||
signerAlg = 'SHA512'; | ||
} | ||
} | ||
/* Prepare body and signer */ | ||
const result = this.prepareSignedBody(headerAlg, url, payload, { nonce, kid }); | ||
const signer = createSign(signerAlg).update(`${result.protected}.${result.payload}`, 'utf8'); | ||
/* Signature - https://stackoverflow.com/questions/39554165 */ | ||
result.signature = signer.sign({ | ||
key: this.accountKey, | ||
padding: RSA_PKCS1_PADDING, | ||
dsaEncoding: 'ieee-p1363' | ||
}, 'base64url'); | ||
return result; | ||
@@ -284,7 +299,7 @@ } | ||
if (this.externalAccountBinding.kid && this.externalAccountBinding.hmacKey) { | ||
const jwk = await this.getJwk(); | ||
const jwk = this.getJwk(); | ||
const eabKid = this.externalAccountBinding.kid; | ||
const eabHmacKey = this.externalAccountBinding.hmacKey; | ||
payload.externalAccountBinding = await this.createSignedHmacBody(eabHmacKey, url, jwk, { kid: eabKid }); | ||
payload.externalAccountBinding = this.createSignedHmacBody(eabHmacKey, url, jwk, { kid: eabKid }); | ||
} | ||
@@ -294,3 +309,3 @@ } | ||
/* Sign body and send request */ | ||
const data = await this.createSignedRsaBody(url, payload, { nonce, kid }); | ||
const data = this.createSignedBody(url, payload, { nonce, kid }); | ||
const resp = await this.request(url, 'post', { data }); | ||
@@ -297,0 +312,0 @@ |
@@ -31,2 +31,3 @@ /** | ||
exports.crypto = require('./crypto'); | ||
exports.forge = require('./crypto/forge'); | ||
@@ -33,0 +34,0 @@ |
136
src/util.js
@@ -5,10 +5,41 @@ /** | ||
const Promise = require('bluebird'); | ||
const dns = Promise.promisifyAll(require('dns')); | ||
const Backoff = require('backo2'); | ||
const dns = require('dns').promises; | ||
const { readCertificateInfo, splitPemChain } = require('./crypto'); | ||
const { log } = require('./logger'); | ||
const forge = require('./crypto/forge'); | ||
/** | ||
* Exponential backoff | ||
* | ||
* https://github.com/mokesmokes/backo | ||
* | ||
* @class | ||
* @param {object} [opts] | ||
* @param {number} [opts.min] Minimum backoff duration in ms | ||
* @param {number} [opts.max] Maximum backoff duration in ms | ||
*/ | ||
class Backoff { | ||
constructor({ min = 100, max = 10000 } = {}) { | ||
this.min = min; | ||
this.max = max; | ||
this.attempts = 0; | ||
} | ||
/** | ||
* Get backoff duration | ||
* | ||
* @returns {number} Backoff duration in ms | ||
*/ | ||
duration() { | ||
const ms = this.min * (2 ** this.attempts); | ||
this.attempts += 1; | ||
return Math.min(ms, this.max); | ||
} | ||
} | ||
/** | ||
* Retry promise | ||
@@ -37,3 +68,3 @@ * | ||
await Promise.delay(duration); | ||
await new Promise((resolve) => { setTimeout(resolve, duration); }); | ||
return retryPromise(fn, attempts, backoff); | ||
@@ -62,29 +93,2 @@ } | ||
/** | ||
* Escape base64 encoded string | ||
* | ||
* @param {string} str Base64 encoded string | ||
* @returns {string} Escaped string | ||
*/ | ||
function b64escape(str) { | ||
return str.replace(/\+/g, '-') | ||
.replace(/\//g, '_') | ||
.replace(/=/g, ''); | ||
} | ||
/** | ||
* Base64 encode and escape buffer or string | ||
* | ||
* @param {buffer|string} str Buffer or string to be encoded | ||
* @returns {string} Escaped base64 encoded string | ||
*/ | ||
function b64encode(str) { | ||
const buf = Buffer.isBuffer(str) ? str : Buffer.from(str); | ||
return b64escape(buf.toString('base64')); | ||
} | ||
/** | ||
* Parse URLs from link header | ||
@@ -110,34 +114,48 @@ * | ||
/** | ||
* Find certificate chain with preferred issuer | ||
* If issuer can not be located, the first certificate will be returned | ||
* Find certificate chain with preferred issuer common name | ||
* - If issuer is found in multiple chains, the closest to root wins | ||
* - If issuer can not be located, the first chain will be returned | ||
* | ||
* @param {array} certificates Array of PEM encoded certificate chains | ||
* @param {string} issuer Preferred certificate issuer | ||
* @returns {Promise<string>} PEM encoded certificate chain | ||
* @returns {string} PEM encoded certificate chain | ||
*/ | ||
async function findCertificateChainForIssuer(chains, issuer) { | ||
try { | ||
return await Promise.any(chains.map(async (chain) => { | ||
/* Look up all issuers */ | ||
const certs = forge.splitPemChain(chain); | ||
const infoCollection = await Promise.map(certs, forge.readCertificateInfo); | ||
const issuerCollection = infoCollection.map((i) => i.issuer.commonName); | ||
function findCertificateChainForIssuer(chains, issuer) { | ||
log(`Attempting to find match for issuer="${issuer}" in ${chains.length} certificate chains`); | ||
let bestMatch = null; | ||
let bestDistance = null; | ||
/* Found match, return it */ | ||
if (issuerCollection.includes(issuer)) { | ||
log(`Found matching certificate for preferred issuer="${issuer}", issuers=${JSON.stringify(issuerCollection)}`); | ||
return chain; | ||
chains.forEach((chain) => { | ||
/* Look up all issuers */ | ||
const certs = splitPemChain(chain); | ||
const infoCollection = certs.map((c) => readCertificateInfo(c)); | ||
const issuerCollection = infoCollection.map((i) => i.issuer.commonName); | ||
/* Found issuer match, get distance from root - lower is better */ | ||
if (issuerCollection.includes(issuer)) { | ||
const distance = (issuerCollection.length - issuerCollection.indexOf(issuer)); | ||
log(`Found matching chain for preferred issuer="${issuer}" distance=${distance} issuers=${JSON.stringify(issuerCollection)}`); | ||
/* Chain wins, use it */ | ||
if (!bestDistance || (distance < bestDistance)) { | ||
log(`Issuer is closer to root than previous match, using it (${distance} < ${bestDistance || 'undefined'})`); | ||
bestMatch = chain; | ||
bestDistance = distance; | ||
} | ||
} | ||
else { | ||
/* No match */ | ||
log(`Unable to match certificate for preferred issuer="${issuer}", issuers=${JSON.stringify(issuerCollection)}`); | ||
} | ||
}); | ||
/* No match, throw error */ | ||
log(`Unable to match certificate for preferred issuer="${issuer}", issuers=${JSON.stringify(issuerCollection)}`); | ||
throw new Error('Certificate issuer mismatch'); | ||
})); | ||
/* Return found match */ | ||
if (bestMatch) { | ||
return bestMatch; | ||
} | ||
catch (e) { | ||
/* No certificates matched, return default */ | ||
log(`Found no match in ${chains.length} certificate chains for preferred issuer="${issuer}", returning default certificate chain`); | ||
return chains[0]; | ||
} | ||
/* No chains matched, return default */ | ||
log(`Found no match in ${chains.length} certificate chains for preferred issuer="${issuer}", returning default certificate chain`); | ||
return chains[0]; | ||
} | ||
@@ -176,3 +194,3 @@ | ||
try { | ||
await dns.resolveSoaAsync(recordName); | ||
await dns.resolveSoa(recordName); | ||
log(`Found SOA record, considering domain to be: ${recordName}`); | ||
@@ -211,4 +229,4 @@ return recordName; | ||
log(`Looking up authoritative NS records for domain: ${domain}`); | ||
const nsRecords = await dns.resolveNsAsync(domain); | ||
const nsAddrArray = await Promise.map(nsRecords, async (r) => dns.resolve4Async(r)); | ||
const nsRecords = await dns.resolveNs(domain); | ||
const nsAddrArray = await Promise.all(nsRecords.map(async (r) => dns.resolve4(r))); | ||
const nsAddresses = [].concat(...nsAddrArray).filter((a) => a); | ||
@@ -242,4 +260,2 @@ | ||
retry, | ||
b64escape, | ||
b64encode, | ||
parseLinkHeader, | ||
@@ -246,0 +262,0 @@ findCertificateChainForIssuer, |
@@ -5,4 +5,3 @@ /** | ||
const Promise = require('bluebird'); | ||
const dns = Promise.promisifyAll(require('dns')); | ||
const dns = require('dns').promises; | ||
const { log } = require('./logger'); | ||
@@ -52,3 +51,3 @@ const axios = require('./axios'); | ||
log(`Checking name for CNAME records: ${recordName}`); | ||
const cnameRecords = await resolver.resolveCnameAsync(recordName); | ||
const cnameRecords = await resolver.resolveCname(recordName); | ||
@@ -67,3 +66,3 @@ if (cnameRecords.length) { | ||
log(`Checking name for TXT records: ${recordName}`); | ||
const txtRecords = await resolver.resolveTxtAsync(recordName); | ||
const txtRecords = await resolver.resolveTxt(recordName); | ||
@@ -70,0 +69,0 @@ if (txtRecords.length) { |
@@ -78,3 +78,3 @@ /** | ||
waitForValidStatus<T = Order | Authorization | rfc8555.Challenge>(item: T): Promise<T>; | ||
getCertificate(order: Order, preferredChain?: string | null): Promise<string>; | ||
getCertificate(order: Order, preferredChain?: string): Promise<string>; | ||
revokeCertificate(cert: CertificateBuffer | CertificateString, data?: rfc8555.CertificateRevocationRequest): Promise<void>; | ||
@@ -136,3 +136,32 @@ auto(opts: ClientAutoOptions): Promise<string>; | ||
export interface RsaPublicJwk { | ||
e: string; | ||
kty: string; | ||
n: string; | ||
} | ||
export interface EcdsaPublicJwk { | ||
crv: string; | ||
kty: string; | ||
x: string; | ||
y: string; | ||
} | ||
export interface CryptoInterface { | ||
createPrivateKey(keySize?: number): Promise<PrivateKeyBuffer>; | ||
createPrivateRsaKey(keySize?: number): Promise<PrivateKeyBuffer>; | ||
createPrivateEcdsaKey(namedCurve?: 'P-256' | 'P-384' | 'P-521'): Promise<PrivateKeyBuffer>; | ||
getPublicKey(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): PublicKeyBuffer; | ||
getJwk(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): RsaPublicJwk | EcdsaPublicJwk; | ||
splitPemChain(chainPem: CertificateBuffer | CertificateString): string[]; | ||
getPemBodyAsB64u(pem: CertificateBuffer | CertificateString): string; | ||
readCsrDomains(csrPem: CsrBuffer | CsrString): CertificateDomains; | ||
readCertificateInfo(certPem: CertificateBuffer | CertificateString): CertificateInfo; | ||
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CsrBuffer]>; | ||
} | ||
export const crypto: CryptoInterface; | ||
/* TODO: LEGACY */ | ||
export interface CryptoLegacyInterface { | ||
createPrivateKey(size?: number): Promise<PrivateKeyBuffer>; | ||
@@ -149,3 +178,3 @@ createPublicKey(key: PrivateKeyBuffer | PrivateKeyString): Promise<PublicKeyBuffer>; | ||
export const forge: CryptoInterface; | ||
export const forge: CryptoLegacyInterface; | ||
@@ -152,0 +181,0 @@ |
@@ -10,3 +10,3 @@ /** | ||
/* Client */ | ||
const accountKey = await acme.forge.createPrivateKey(); | ||
const accountKey = await acme.crypto.createPrivateKey(); | ||
@@ -45,3 +45,3 @@ const client = new acme.Client({ | ||
/* Finalize */ | ||
const [certKey, certCsr] = await acme.forge.createCsr({ | ||
const [certKey, certCsr] = await acme.crypto.createCsr({ | ||
commonName: 'example.com', | ||
@@ -48,0 +48,0 @@ altNames: ['example.com', '*.example.com'] |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
105908
4
2762
263
4
+ Addedjsrsasign@^10.5.26
+ Addedasynckit@0.4.0(transitive)
+ Addedaxios@0.27.2(transitive)
+ Addedcombined-stream@1.0.8(transitive)
+ Addeddelayed-stream@1.0.0(transitive)
+ Addedform-data@4.0.0(transitive)
+ Addedjsrsasign@10.9.0(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
- Removedbacko2@^1.0.0
- Removedbluebird@^3.5.0
- Removedaxios@0.26.1(transitive)
- Removedbacko2@1.0.2(transitive)
- Removedbluebird@3.7.2(transitive)
Updatedaxios@0.27.2
Updatednode-forge@^1.3.1