2019-01-06 19:41:21 +00:00
|
|
|
import * as plugins from './smartacme.plugins';
|
2019-01-08 19:45:35 +00:00
|
|
|
import { CertManager } from './smartacme.classes.certmanager';
|
|
|
|
import { CertRemoteHandler } from './smartacme.classes.certremotehandler';
|
2017-01-14 13:14:50 +00:00
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
*/
|
2019-01-08 19:45:35 +00:00
|
|
|
export interface ISmartAcmeOptions {
|
|
|
|
accountPrivateKey?: string;
|
|
|
|
accountEmail: string;
|
|
|
|
setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
|
|
|
|
removeChallenge: (domainName: string) => Promise<any>;
|
|
|
|
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
|
|
|
|
}
|
2017-01-01 23:18:51 +00:00
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
export class SmartAcme {
|
2019-01-08 19:45:35 +00:00
|
|
|
private options: ISmartAcmeOptions;
|
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
// the acme client
|
|
|
|
private client: any;
|
2019-01-06 22:54:46 +00:00
|
|
|
private smartdns = new plugins.smartdns.Smartdns();
|
2016-11-01 19:16:43 +00:00
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
// the account private key
|
|
|
|
private privateKey: string;
|
2016-11-07 17:41:52 +00:00
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
// challenge fullfillment
|
2019-01-06 22:30:38 +00:00
|
|
|
private setChallenge: (domainName: string, keyAuthorization: string) => Promise<any>;
|
|
|
|
private removeChallenge: (domainName: string) => Promise<any>;
|
2016-11-01 19:16:43 +00:00
|
|
|
|
2019-01-08 19:45:35 +00:00
|
|
|
// certmanager
|
|
|
|
private certmanager: CertManager;
|
|
|
|
private certremoteHandler: CertRemoteHandler;
|
|
|
|
|
|
|
|
constructor(optionsArg: ISmartAcmeOptions) {
|
|
|
|
this.options = optionsArg;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async init() {
|
|
|
|
this.privateKey = this.options.accountPrivateKey || (await plugins.acme.forge.createPrivateKey());
|
|
|
|
this.setChallenge = this.options.setChallenge;
|
|
|
|
this.removeChallenge = this.options.removeChallenge;
|
|
|
|
|
|
|
|
this.certmanager = new CertManager({
|
|
|
|
mongoDescriptor: this.options.mongoDescriptor
|
|
|
|
});
|
|
|
|
await this.certmanager.init();
|
|
|
|
this.certremoteHandler = new CertRemoteHandler();
|
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
this.client = new plugins.acme.Client({
|
|
|
|
directoryUrl: plugins.acme.directory.letsencrypt.staging,
|
|
|
|
accountKey: this.privateKey
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Register account */
|
|
|
|
await this.client.createAccount({
|
|
|
|
termsOfServiceAgreed: true,
|
2019-01-08 19:45:35 +00:00
|
|
|
contact: [`mailto:${this.options.accountEmail}`]
|
2019-01-06 19:41:21 +00:00
|
|
|
});
|
2018-08-11 23:35:14 +00:00
|
|
|
}
|
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
public async getCertificateForDomain(domainArg: string) {
|
|
|
|
const domain = domainArg;
|
|
|
|
|
2019-01-08 19:45:35 +00:00
|
|
|
const retrievedCertificate = await this.certmanager.retrieveCertificate(domain);
|
|
|
|
|
|
|
|
if(retrievedCertificate) {
|
|
|
|
return retrievedCertificate;
|
|
|
|
}
|
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
/* Place new order */
|
|
|
|
const order = await this.client.createOrder({
|
|
|
|
identifiers: [{ type: 'dns', value: domain }, { type: 'dns', value: `*.${domain}` }]
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Get authorizations and select challenges */
|
|
|
|
const authorizations = await this.client.getAuthorizations(order);
|
|
|
|
|
2019-01-06 22:54:46 +00:00
|
|
|
for (const authz of authorizations) {
|
2019-01-06 22:30:38 +00:00
|
|
|
console.log(authz);
|
|
|
|
const domainDnsName: string = `_acme-challenge.${authz.identifier.value}`;
|
|
|
|
const dnsChallenge: string = authz.challenges.find(challengeArg => {
|
|
|
|
return challengeArg.type === 'dns-01';
|
|
|
|
});
|
|
|
|
// process.exit(1);
|
|
|
|
const keyAuthorization: string = await this.client.getChallengeKeyAuthorization(dnsChallenge);
|
2019-01-06 19:41:21 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
/* Satisfy challenge */
|
2019-01-06 22:30:38 +00:00
|
|
|
await this.setChallenge(domainDnsName, keyAuthorization);
|
2019-01-07 00:08:50 +00:00
|
|
|
await this.smartdns.checkUntilAvailable(domainDnsName, 'TXT', keyAuthorization, 100, 5000);
|
2019-01-06 22:54:46 +00:00
|
|
|
|
2019-01-06 19:41:21 +00:00
|
|
|
/* Verify that challenge is satisfied */
|
2019-01-06 22:30:38 +00:00
|
|
|
await this.client.verifyChallenge(authz, dnsChallenge);
|
2019-01-06 19:41:21 +00:00
|
|
|
|
|
|
|
/* Notify ACME provider that challenge is satisfied */
|
2019-01-06 22:30:38 +00:00
|
|
|
await this.client.completeChallenge(dnsChallenge);
|
2019-01-06 19:41:21 +00:00
|
|
|
|
|
|
|
/* Wait for ACME provider to respond with valid status */
|
2019-01-06 22:30:38 +00:00
|
|
|
await this.client.waitForValidStatus(dnsChallenge);
|
2019-01-06 19:41:21 +00:00
|
|
|
} finally {
|
|
|
|
/* Clean up challenge response */
|
|
|
|
try {
|
2019-01-06 22:30:38 +00:00
|
|
|
await this.removeChallenge(domainDnsName);
|
2019-01-06 19:41:21 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
}
|
2019-01-06 22:54:46 +00:00
|
|
|
}
|
2019-01-06 19:41:21 +00:00
|
|
|
|
|
|
|
/* Finalize order */
|
|
|
|
const [key, csr] = await plugins.acme.forge.createCsr({
|
|
|
|
commonName: `*.${domain}`,
|
|
|
|
altNames: [domain]
|
|
|
|
});
|
|
|
|
|
|
|
|
await this.client.finalizeOrder(order, csr);
|
|
|
|
const cert = await this.client.getCertificate(order);
|
|
|
|
|
|
|
|
/* Done */
|
|
|
|
console.log(`CSR:\n${csr.toString()}`);
|
|
|
|
console.log(`Private key:\n${key.toString()}`);
|
|
|
|
console.log(`Certificate:\n${cert.toString()}`);
|
2019-01-08 19:45:35 +00:00
|
|
|
|
|
|
|
this.certmanager.storeCertificate(key.toString(), cert.toString(), csr.toString());
|
2017-04-28 16:56:55 +00:00
|
|
|
}
|
2019-01-06 19:41:21 +00:00
|
|
|
|
2019-01-06 22:30:38 +00:00
|
|
|
toStorageObject() {}
|
2016-11-01 17:27:57 +00:00
|
|
|
}
|