Reformat code
This commit is contained in:
parent
34d9abec19
commit
5a6e557913
4 changed files with 58 additions and 41 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { promisify } from "util";
|
||||
import {promisify} from "util";
|
||||
import fromKeyLike from "jose/jwk/from_key_like";
|
||||
import generateKeyPair from "jose/util/generate_key_pair";
|
||||
import calculateThumbprint from "jose/jwk/thumbprint";
|
||||
|
|
@ -9,6 +9,7 @@ import CompactSign from "jose/jws/compact/sign";
|
|||
import pem from "pem";
|
||||
import * as common from "./common.js";
|
||||
import request from "./request.js";
|
||||
|
||||
const createCsr = promisify(pem.createCSR);
|
||||
|
||||
/**
|
||||
|
|
@ -37,6 +38,7 @@ class Client {
|
|||
this.replayNonce = '';
|
||||
this.thumbprint = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Export account public and private keys to a directory.
|
||||
*
|
||||
|
|
@ -56,19 +58,21 @@ class Client {
|
|||
common.writeKeyToFile(publicKeyFile, this.accountPublicKey)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate new account public and private keys.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
async generateAccountKeyPair() {
|
||||
const { privateKey, publicKey } = await generateKeyPair(common.ACCOUNT_KEY_ALGORITHM);
|
||||
const {privateKey, publicKey} = await generateKeyPair(common.ACCOUNT_KEY_ALGORITHM);
|
||||
// @ts-ignore
|
||||
this.accountPrivateKey = privateKey;
|
||||
// @ts-ignore
|
||||
this.accountPublicKey = publicKey;
|
||||
await this.initAccountJwks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a certificate from Let's Encrypt for your domain.
|
||||
*
|
||||
|
|
@ -81,16 +85,17 @@ class Client {
|
|||
await this.newNonce();
|
||||
if (!this.myAccountUrl)
|
||||
await this.newAccount();
|
||||
const { authzUrls, finalizeUrl } = await this.newOrder(domain);
|
||||
const { challenge } = await this.authz(authzUrls[0]);
|
||||
const {authzUrls, finalizeUrl} = await this.newOrder(domain);
|
||||
const {challenge} = await this.authz(authzUrls[0]);
|
||||
await this.completeChallenge(challenge, domain);
|
||||
await this.pollAuthz(authzUrls[0]);
|
||||
const { certificate, privateKeyData } = await this.finalizeOrder(finalizeUrl, domain);
|
||||
const {certificate, privateKeyData} = await this.finalizeOrder(finalizeUrl, domain);
|
||||
return {
|
||||
certificate,
|
||||
privateKeyData
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Import account public and private keys from a directory.
|
||||
*
|
||||
|
|
@ -108,6 +113,7 @@ class Client {
|
|||
this.accountPublicKey = common.importPublicKey(publicKeyData);
|
||||
await this.initAccountJwks();
|
||||
}
|
||||
|
||||
async authz(authzUrl) {
|
||||
const data = await this.sign({
|
||||
kid: this.myAccountUrl,
|
||||
|
|
@ -125,18 +131,20 @@ class Client {
|
|||
if (res.statusCode !== 200) {
|
||||
throw new Error(`authz() Status Code: ${res.statusCode} Data: ${res.data}`);
|
||||
}
|
||||
const { challenges, identifier, ...rest } = res.data;
|
||||
const challenge = challenges.find(({ type }) => type === 'http-01');
|
||||
const {challenges, identifier, ...rest} = res.data;
|
||||
const challenge = challenges.find(({type}) => type === 'http-01');
|
||||
return {
|
||||
challenge,
|
||||
domain: identifier.value,
|
||||
...rest
|
||||
};
|
||||
}
|
||||
|
||||
async completeChallenge(challenge, domain) {
|
||||
await this.readyChallenge(challenge);
|
||||
await this.receiveServerRequest(challenge, domain);
|
||||
}
|
||||
|
||||
async directory() {
|
||||
if (this.hasDirectory)
|
||||
return false;
|
||||
|
|
@ -150,6 +158,7 @@ class Client {
|
|||
this.newOrderUrl = res.data.newOrder;
|
||||
return true;
|
||||
}
|
||||
|
||||
async fetchCertificate(certificateUrl) {
|
||||
const data = await this.sign({
|
||||
kid: this.myAccountUrl,
|
||||
|
|
@ -170,11 +179,13 @@ class Client {
|
|||
}
|
||||
return res.data;
|
||||
}
|
||||
|
||||
async finalizeOrder(finalizeUrl, domain) {
|
||||
const { privateKey } = await generateKeyPair(common.CERTIFICATE_KEY_ALGORITHM);
|
||||
const {privateKey} = await generateKeyPair(common.CERTIFICATE_KEY_ALGORITHM);
|
||||
// @ts-ignore
|
||||
const clientKey = common.exportPrivateKey(privateKey);
|
||||
let { csr
|
||||
let {
|
||||
csr
|
||||
// @ts-ignore
|
||||
} = await createCsr({
|
||||
clientKey,
|
||||
|
|
@ -215,6 +226,7 @@ class Client {
|
|||
privateKeyData: clientKey
|
||||
};
|
||||
}
|
||||
|
||||
async initAccountJwks() {
|
||||
if (this.accountPrivateKey == null || this.accountPublicKey == null) {
|
||||
return Promise.reject(new Error('Account key pair not generated'));
|
||||
|
|
@ -227,6 +239,7 @@ class Client {
|
|||
this.accountPrivateJwk = accountPrivateJwk;
|
||||
this.thumbprint = await calculateThumbprint(publicJwk);
|
||||
}
|
||||
|
||||
async newAccount(...emails) {
|
||||
const data = await this.sign({
|
||||
jwk: this.accountPublicJwk,
|
||||
|
|
@ -249,6 +262,7 @@ class Client {
|
|||
this.myAccountUrl = res.headers.location;
|
||||
return res.statusCode === 201;
|
||||
}
|
||||
|
||||
async newNonce() {
|
||||
if (this.replayNonce)
|
||||
return false;
|
||||
|
|
@ -261,6 +275,7 @@ class Client {
|
|||
this.setReplayNonce(res);
|
||||
return true;
|
||||
}
|
||||
|
||||
async newOrder(...domains) {
|
||||
const identifiers = domains.map(domain => ({
|
||||
type: 'dns',
|
||||
|
|
@ -285,7 +300,7 @@ class Client {
|
|||
throw new Error(`newOrder() Status Code: ${res.statusCode} Data: ${res.data}`);
|
||||
}
|
||||
const orderUrl = res.headers.location;
|
||||
const { authorizations: authzUrls, finalize: finalizeUrl } = res.data;
|
||||
const {authorizations: authzUrls, finalize: finalizeUrl} = res.data;
|
||||
return {
|
||||
authzUrls,
|
||||
domains,
|
||||
|
|
@ -293,6 +308,7 @@ class Client {
|
|||
orderUrl
|
||||
};
|
||||
}
|
||||
|
||||
async pollAuthz(authzUrl) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const result = await this.authz(authzUrl);
|
||||
|
|
@ -307,6 +323,7 @@ class Client {
|
|||
}
|
||||
throw new Error('pollAuthz() timed out');
|
||||
}
|
||||
|
||||
async readyChallenge(challenge) {
|
||||
const data = await this.sign({
|
||||
kid: this.myAccountUrl,
|
||||
|
|
@ -325,6 +342,7 @@ class Client {
|
|||
throw new Error(`readyChallenge() Status Code: ${res.statusCode} Data: ${res.data}`);
|
||||
}
|
||||
}
|
||||
|
||||
receiveServerRequest(challenge, domain) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const time = setTimeout(() => {
|
||||
|
|
@ -344,6 +362,7 @@ class Client {
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
setReplayNonce(res) {
|
||||
const replayNonce = (res.headers['replay-nonce'] || '').trim();
|
||||
if (!replayNonce) {
|
||||
|
|
@ -351,6 +370,7 @@ class Client {
|
|||
}
|
||||
this.replayNonce = replayNonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("jose/types.js").JWSHeaderParameters} header
|
||||
* @param {import("jose/types.js").JWTPayload | undefined} [payload]
|
||||
|
|
@ -367,8 +387,7 @@ class Client {
|
|||
...header
|
||||
})
|
||||
.sign(this.accountPrivateKey);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// SignJWT constructor only accepts object but RFC8555 requires empty payload
|
||||
// Workaround: manually pass empty Uint8Array to CompactSign constructor
|
||||
const sig = new CompactSign(new Uint8Array());
|
||||
|
|
@ -386,4 +405,5 @@ class Client {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Client;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import crypto from "crypto";
|
||||
import fs from "fs";
|
||||
|
||||
export const ACCOUNT_KEY_ALGORITHM = 'ES256';
|
||||
export const CERTIFICATE_KEY_ALGORITHM = 'RS256';
|
||||
const env = (process.env.NODE_ENV || '').trim().toLowerCase();
|
||||
|
|
@ -61,8 +62,7 @@ export const importPrivateKey = (privateKeyData, passphrase) => {
|
|||
}
|
||||
try {
|
||||
return crypto.createPrivateKey(privateKeyOpts);
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
throw new Error('Failed to import private key');
|
||||
}
|
||||
};
|
||||
|
|
@ -79,8 +79,7 @@ export const importPublicKey = publicKeyData => {
|
|||
format: PUBLIC_KEY_FORMAT,
|
||||
type: PUBLIC_KEY_TYPE
|
||||
});
|
||||
}
|
||||
catch {
|
||||
} catch {
|
||||
throw new Error('Failed to import public key');
|
||||
}
|
||||
};
|
||||
|
|
@ -97,8 +96,7 @@ export const writeKeyToFile = async (filename, key, passphrase) => {
|
|||
key = key.includes('PRIVATE KEY')
|
||||
? importPrivateKey(key, passphrase)
|
||||
: importPublicKey(key);
|
||||
}
|
||||
else if (!(key instanceof crypto.KeyObject)) {
|
||||
} else if (!(key instanceof crypto.KeyObject)) {
|
||||
throw new Error('Expected "key" to be crypto.KeyObject or string');
|
||||
}
|
||||
const isPrivateKey = key.type === 'private';
|
||||
|
|
@ -106,6 +104,6 @@ export const writeKeyToFile = async (filename, key, passphrase) => {
|
|||
? exportPrivateKey(key, passphrase)
|
||||
: exportPublicKey(key);
|
||||
const mode = isPrivateKey ? PRIVATE_KEY_PERMISSIONS : PUBLIC_KEY_PERMISSIONS;
|
||||
await fs.promises.writeFile(filename, keyData, { mode });
|
||||
await fs.promises.writeFile(filename, keyData, {mode});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import Client from './client.js';
|
||||
|
||||
export * from './common.js';
|
||||
export { Client };
|
||||
export {Client};
|
||||
|
|
@ -6,16 +6,15 @@ import https from "https";
|
|||
* @param {() => void} [cb]
|
||||
* @return {Promise<{data: any, headers: import('http').IncomingHttpHeaders, statusCode: number}>}
|
||||
*/
|
||||
const request = (url, { data = '', ...options } = {}, cb) => {
|
||||
const request = (url, {data = '', ...options} = {}, cb) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
url = new URL(url);
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
return reject(err);
|
||||
}
|
||||
https.request(url, options, res => {
|
||||
const { statusCode, headers } = res;
|
||||
const {statusCode, headers} = res;
|
||||
let data = '';
|
||||
res
|
||||
.on('data', chunk => {
|
||||
|
|
@ -25,13 +24,12 @@ const request = (url, { data = '', ...options } = {}, cb) => {
|
|||
if (headers['content-type']?.includes('application/json')) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve({ data, headers, statusCode: statusCode || 0 });
|
||||
resolve({data, headers, statusCode: statusCode || 0});
|
||||
})
|
||||
.once('error', reject);
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
editor.link_modal.header
Reference in a new issue