diff --git a/LICENSE b/LICENSE
index 86a2642..82d47fb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,4 @@
Copyright (C) 2016, Lossless GmbH
-Copyright (C) 2016, Martin Springwald
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/assets/private.pem b/assets/private.pem
new file mode 100644
index 0000000..0801437
--- /dev/null
+++ b/assets/private.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAyocFq3vvbiRNCsEcXtsqimIi6UM1OmxiYVQ0NoLvBtpaWk+E
+TvNIjmUgh5QQaQfRyRGoWvhskp+E8p6go4GsbRtzx0TvL8uINTcC3SHHo6Qvl599
+4QUUPUrioHdh+lX1oj+zIPVUOaL4dl0US1Ebs5vrZVbCfNXSm86vBaPIj6IkWEkj
+4S5xGsYlVaQUI8Tvv2fbPziIivbkxS1v/EEMnfk6i5PWgCsnMupYxz58WaVp9xyu
++v/DMPB09mqo4DzchtUNF/b5eOWh3pDJoewYyRVMDDPJoQiTKkJn3kt64EaQuZK2
+nUXcihlmaKIx5ayxirsgfvIvxidHnkQcluvciQIDAQABAoIBAQCTPUKz/3B8pMuW
+C/syQyhUXzB+YawrA20q0Wr8Toi0dL7HdZP9SgXv8DmMF+suUM8F3V6GdKGKn4qq
+UQT8mmPfFtw/fTBfkRs/hPUCC3L214D6PKvpkiW6wdytSN3kf+YKxUDXr0RCeuck
+NltwvlDjbXHfxQm0dEefms3HzeEb+jwCyyLVLv+cDly7w7Qqq+67A6mduV/hb53p
+92VFm36r7njr+1CYHq+ixV+oyUrEue7yW7w1SjZRkii3AY8Tbvk1f0lVw+XkyYf7
+bQvmGSGJh1FmBi7Lytc2hKnqBLTn+iWx3S5pdPhcKTMwC/OD8p+r/DfyqThW/KVa
+aaXdoY/5AoGBAO4uAcmHOhR+M/Jnue4srZJ82EkNOQy+zaFlg9KCU9R4qZ59/klH
+fp0PkOw3bDFT4/1i12nm4XXqhI9Z7nsKdAoajOYpnifJVEAwQh9MlRBM7Lw+ZS0q
+IcH7dvvP1XQ7E2U4C0cWUMcpWNpnmwV67gtqy0KZwk5i+WlFuugQzmhbAoGBANmu
+JX6bPKUx0kBJLWhJeAxsk0OoHJ4uGihs1zxT6gl6s+AKQG4db9vU2w99lJ0nR3Aw
+MLA4evSMFa5Od96W4KnoiMNHS4c5QiiVKsRSU1losWfwq0jyg406oyTh8rd0eOQn
+LDOKP7nDTij8A6l0/t5a2MCu4bLQQXTedPrX+wPrAoGBAM/XO94Fb+xUGLaOR1SM
+jkaHRSGyNTdnBP+zGy5GZirBxJo2rgB6MAWUgM1wq6v73bbOWtXiEJqaNGT3gEDE
+ZXAvrQZoCMgFSszcj8bKSEW6Ktc1x4p6+oxRCIpC2aycpJcuKcE1uvWgohWsVT2a
+AUHbRlXu4P0QJz7zB1/c0pGDAoGAbIvSVpfCXf3CAhx7cA1yt39Mz+f8nUQP9yiP
+C54sjh2JpKZ4CnDTXqN9uPO+L79ueBsPrE/9wAQ6q3ilfXFvBkrWJ8pdd0iuHN6F
+PPBwb50tGc+BGhcUUlBzGekxxxllTx/ZgrnlnRQu3XENwmp8zRQwEaUjFq+SdFyZ
+qJwap5ECgYEA7UGxxRXAjfStTLnsrnr9svvr3QhwnZBg5JAjeR6FKC0cGFzdBrJ5
+rV/Zy4mGbTBBVh5oU3MplB3AUHejuFv+8eCik2mJug8k3G8KQAk9mB8oV97k0cp+
+bdlu9vlutIoCG9RXxCHdgRVLiLK+OkLv6p7hQOIY7fsIRaAuI+vPKSk=
+-----END RSA PRIVATE KEY-----
diff --git a/assets/public.pem b/assets/public.pem
new file mode 100644
index 0000000..604ae70
--- /dev/null
+++ b/assets/public.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyocFq3vvbiRNCsEcXtsq
+imIi6UM1OmxiYVQ0NoLvBtpaWk+ETvNIjmUgh5QQaQfRyRGoWvhskp+E8p6go4Gs
+bRtzx0TvL8uINTcC3SHHo6Qvl5994QUUPUrioHdh+lX1oj+zIPVUOaL4dl0US1Eb
+s5vrZVbCfNXSm86vBaPIj6IkWEkj4S5xGsYlVaQUI8Tvv2fbPziIivbkxS1v/EEM
+nfk6i5PWgCsnMupYxz58WaVp9xyu+v/DMPB09mqo4DzchtUNF/b5eOWh3pDJoewY
+yRVMDDPJoQiTKkJn3kt64EaQuZK2nUXcihlmaKIx5ayxirsgfvIvxidHnkQcluvc
+iQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/dist/smartacme.classes.acmeclient.d.ts b/dist/smartacme.classes.acmeclient.d.ts
deleted file mode 100644
index ad862a4..0000000
--- a/dist/smartacme.classes.acmeclient.d.ts
+++ /dev/null
@@ -1,199 +0,0 @@
-///
-import * as q from 'q';
-import { JWebClient } from './smartacme.classes.jwebclient';
-import { IReqResArg } from './smartacme.classes.jwebclient';
-/**
- * @class AcmeClient
- * @constructor
- * @description ACME protocol implementation from client perspective
- * @param {string} directory_url - Address of directory
- * @param {module:JWebClient~JWebClient} jWebClient - Reference to JSON-Web-Client
- */
-export declare class AcmeClient {
- clientProfilePubKey: any;
- daysValid: number;
- directory: any;
- directoryUrl: string;
- emailDefaultPrefix: string;
- emailOverride: string;
- jWebClient: JWebClient;
- regLink: string;
- tosLink: string;
- webroot: string;
- wellKnownPath: string;
- withInteraction: boolean;
- constructor(directoryUrlArg: any);
- /**
- * getDirectory
- * @description retrieve directory entries (directory url must be set prior to execution)
- * @param {function} callback - first argument will be the answer object
- */
- getDirectory(): q.Promise;
- /**
- * newRegistration
- * @description try to register (directory lookup must have occured prior to execution)
- * @param {Object} payload
- * @param {function} callback - first argument will be the answer object
- */
- newRegistration(payload: any): q.Promise<{}>;
- /**
- * getRegistration
- * @description get information about registration
- * @param {string} uri - will be exposed when trying to register
- * @param {Object} payload - update information
- * @param {function} callback - first argument will be the answer object
- */
- getRegistration(uri: any, payload: any): q.Promise;
- /**
- * authorizeDomain
- * @description authorize domain using challenge-response-method
- * @param {string} domain
- * @param {function} callback - first argument will be the answer object
- */
- authorizeDomain(domain: any): q.Promise<{}>;
- /**
- * acceptChallenge
- * @description tell server which challenge will be accepted
- * @param {Object} challenge
- * @param {function} callback - first argument will be the answer object
- */
- acceptChallenge(challenge?: {}): q.Promise<{}>;
- /**
- * pollUntilValid
- * @description periodically (with exponential back-off) check status of challenge
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilValid(uri: any, retry?: number): q.Promise<{}>;
- /**
- * pollUntilIssued
- * @description periodically (with exponential back-off) check status of CSR
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilIssued(uri: any, retry?: number): q.Promise<{}>;
- /**
- * requestSigning
- * @description send CSR
- * @param {string} domain - expected to be already sanitized
- * @param {function} callback - first argument will be the answer object
- */
- requestSigning(commonName: any): q.Promise<{}>;
- /**
- * retrieves profile of user (will make directory lookup and registration check)
- * @param {function} callback - first argument will be the answer object
- */
- getProfile(): q.Promise<{}>;
- /**
- * createAccount
- * @description create new account (assumes directory lookup has already occured)
- * @param {string} email
- * @param {function} callback - first argument will be the registration URI
- */
- createAccount(email: string): q.Promise<{}>;
- /**
- * agreeTos
- * @description agree with terms of service (update agreement status in profile)
- * @param {string} tosLink
- * @param {function} callback - first argument will be the answer object
- */
- agreeTos(tosLink: any): q.Promise<{}>;
- /**
- * Entry-Point: Request certificate
- */
- requestCertificate(domainArg: string, organizationArg: string, countryCodeArg: string): q.Promise<{}>;
- /**
- * External: Create key pair
- * @param {number} bit - key strength, expected to be already sanitized
- * @param {string} c - country code, expected to be already sanitized
- * @param {string} o - organization, expected to be already sanitized
- * @param {string} cn - common name (domain name), expected to be already sanitized
- * @param {string} e - email address, expected to be already sanitized
- * @param {function} callback
- */
- createKeyPair(optionsArg: {
- keyBitSize: number;
- countryCode: string;
- organization: string;
- commonName: string;
- emailAddress: string;
- }): q.Promise<{}>;
- /**
- * Helper: Empty callback
- */
- emptyCallback(): void;
- /**
- * Helper: Make safe file name or path from string
- * @param {string} name
- * @param {boolean} withPath - optional, default false
- * @return {string}
- */
- makeSafeFileName(name: any, withPath?: boolean): any;
- /**
- * Helper: Prepare challenge
- * @param {string} domain
- * @param {Object} challenge
- * @param {function} callback
- */
- prepareChallenge(domain: any, challenge: any, callback: any): void;
- /**
- * Helper: Extract TOS Link, e.g. from "<http://...>;rel="terms-of-service"
- * @param {string} linkStr
- * @return {string}
- */
- getTosLink(linkStr: any): string;
- /**
- * Helper: Select challenge by type
- * @param {Object} ans
- * @param {string} challenge_type
- * @return {Object}
- */
- selectChallenge(ans: any, challengeType: string): any;
- /**
- * Helper: Extract first found email from profile (without mailto prefix)
- * @param {Object} profile
- * @return {string}
- */
- extractEmail(profile: any): string;
- /**
- * Make ACME-Request: Domain-Authorization Request - Object: resource, identifier
- * @param {string} domain
- * @return {{resource: string, identifier: Object}}
- */
- makeDomainAuthorizationRequest(domain: any): {
- 'resource': string;
- 'identifier': {
- 'type': string;
- 'value': any;
- };
- };
- /**
- * Make ACME-Object: Key-Authorization (encoded) - String: Challenge-Token . Encoded-Account-Key-Hash
- * @param {Object} challenge
- * @return {string}
- */
- makeKeyAuthorization(challenge: any): string;
- /**
- * Make ACME-Request: Challenge-Response - Object: resource, keyAuthorization
- * @param {Object} challenge
- * @return {{resource: string, keyAuthorization: string}}
- */
- makeChallengeResponse(challenge: any): {
- 'resource': string;
- 'keyAuthorization': string;
- };
- /**
- * Make ACME-Request: CSR - Object: resource, csr, notBefore, notAfter
- * @param {string} csr
- * @param {number} days_valid
- * @return {{resource: string, csr: string, notBefore: string, notAfter: string}}
- */
- makeCertRequest(csr: string, DAYS_VALID: number): {
- 'resource': string;
- 'csr': any;
- 'notBefore': string;
- 'notAfter': string;
- };
-}
diff --git a/dist/smartacme.classes.acmeclient.js b/dist/smartacme.classes.acmeclient.js
deleted file mode 100644
index 4113b4a..0000000
--- a/dist/smartacme.classes.acmeclient.js
+++ /dev/null
@@ -1,717 +0,0 @@
-"use strict";
-const plugins = require("./smartacme.plugins");
-const q = require("q");
-const crypto = require("crypto");
-const fs = require("fs");
-const readline = require("readline");
-const smartacme_classes_jwebclient_1 = require("./smartacme.classes.jwebclient");
-/**
- * json_to_utf8buffer
- * @private
- * @description convert JSON to Buffer using UTF-8 encoding
- * @param {Object} obj
- * @return {Buffer}
- * @throws Exception if object cannot be stringified or contains cycle
- */
-let json_to_utf8buffer = (obj) => {
- return new Buffer(JSON.stringify(obj), 'utf8');
-};
-/**
- * @class AcmeClient
- * @constructor
- * @description ACME protocol implementation from client perspective
- * @param {string} directory_url - Address of directory
- * @param {module:JWebClient~JWebClient} jWebClient - Reference to JSON-Web-Client
- */
-class AcmeClient {
- constructor(directoryUrlArg) {
- this.jWebClient = new smartacme_classes_jwebclient_1.JWebClient();
- /**
- * @member {Object} module:AcmeClient~AcmeClient#clientProfilePubKey
- * @desc Cached public key obtained from profile
- */
- this.clientProfilePubKey = {};
- /**
- * @member {number} module:AcmeClient~AcmeClient#days_valid
- * @desc Validity period in days
- * @default 1
- */
- this.daysValid = 1;
- /**
- * @member {Object} module:AcmeClient~AcmeClient#directory
- * @desc Hash map of REST URIs
- */
- this.directory = {};
- /**
- * @member {string} module:AcmeClient~AcmeClient#directory_url
- * @desc Address of directory
- */
- this.directoryUrl = directoryUrlArg;
- /**
- * @member {string} module:AcmeClient~AcmeClient#emailDefaultPrefix
- * @desc Prefix of email address if constructed from domain name
- * @default "hostmaster"
- */
- this.emailDefaultPrefix = 'hostmaster'; // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#emailOverride
- * @desc Email address to use
- */
- this.emailOverride = null; // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#regLink
- * @desc Cached registration URI
- */
- this.regLink = null; // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#tosLink
- * @desc Cached terms of service URI
- */
- this.tosLink = null; // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#webroot
- * @desc Path to server web root (or path to store challenge data)
- * @default "."
- */
- this.webroot = '.'; // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#well_known_path
- * @desc Directory structure for challenge data
- * @default "/.well-known/acme-challenge/"
- */
- this.wellKnownPath = '/.well-known/acme-challenge/'; // {string}
- /**
- * @member {boolean} module:AcmeClient~AcmeClient#withInteraction
- * @desc Determines if interaction of user is required
- * @default true
- */
- this.withInteraction = true; // {boolean}
- }
- // *****************************************************************************
- // REQUEST-Section
- // *****************************************************************************
- /**
- * getDirectory
- * @description retrieve directory entries (directory url must be set prior to execution)
- * @param {function} callback - first argument will be the answer object
- */
- getDirectory() {
- let done = q.defer();
- this.jWebClient.get(this.directoryUrl)
- .then((reqResArg) => {
- done.resolve(reqResArg);
- });
- return done.promise;
- }
- /**
- * newRegistration
- * @description try to register (directory lookup must have occured prior to execution)
- * @param {Object} payload
- * @param {function} callback - first argument will be the answer object
- */
- newRegistration(payload) {
- let done = q.defer();
- if (!(payload instanceof Object)) {
- payload = {}; // ensure payload is object
- }
- payload.resource = 'new-reg';
- this.jWebClient.post(this.directory['new-reg'], payload);
- return done.promise;
- }
- /**
- * getRegistration
- * @description get information about registration
- * @param {string} uri - will be exposed when trying to register
- * @param {Object} payload - update information
- * @param {function} callback - first argument will be the answer object
- */
- getRegistration(uri, payload) {
- let done = q.defer();
- payload['resource'] = 'reg';
- this.jWebClient.post(uri, payload)
- .then((reqResArg) => {
- if (reqResArg.ans instanceof Object) {
- this.clientProfilePubKey = reqResArg.ans.key; // cache or reset returned public key
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- let linkStr = reqResArg.res.headers['link'];
- if (typeof linkStr === 'string') {
- let tosLink = this.getTosLink(linkStr);
- if (typeof tosLink === 'string') {
- this.tosLink = tosLink; // cache TOS link
- }
- else {
- this.tosLink = null; // reset TOS link
- }
- }
- else {
- this.tosLink = null; // reset TOS link
- }
- }
- else {
- this.tosLink = null; // reset TOS link
- }
- done.resolve({ ans: reqResArg.ans, res: reqResArg.res });
- }
- else {
- done.reject(new Error('some error'));
- }
- });
- return done.promise;
- }
- /**
- * authorizeDomain
- * @description authorize domain using challenge-response-method
- * @param {string} domain
- * @param {function} callback - first argument will be the answer object
- */
- authorizeDomain(domain) {
- let done = q.defer();
- this.getProfile()
- .then(profile => {
- if (!(profile instanceof Object)) {
- done.reject(new Error('no profile returned'));
- }
- else {
- this.jWebClient.post(this.directory['new-authz'], this.makeDomainAuthorizationRequest(domain))
- .then((reqResArg) => {
- if ((reqResArg.res instanceof Object) && (reqResArg.res['statusCode'] === 403)) {
- this.agreeTos(this.tosLink)
- .then((reqResArg2) => {
- if ((reqResArg.res instanceof Object)
- && (reqResArg2.res['statusCode'] >= 200)
- && (reqResArg2.res['statusCode'] <= 400)) {
- this.authorizeDomain(domain).then(() => {
- done.resolve();
- }); // try authorization again
- }
- else {
- done.reject(false); // agreement failed
- }
- });
- }
- else {
- if ((reqResArg.res instanceof Object)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')
- && (reqResArg.ans instanceof Object)) {
- let poll_uri = reqResArg.res.headers['location']; // status URI for polling
- let challenge = this.selectChallenge(reqResArg.ans, 'http-01'); // select simple http challenge
- if (challenge instanceof Object) {
- this.prepareChallenge(domain, challenge, () => {
- // reset
- reqResArg.ans = null;
- reqResArg.res = null;
- // accept challenge
- this.acceptChallenge(challenge)
- .then((reqResArg2) => {
- if ((reqResArg2.res instanceof Object)
- && (reqResArg2.res['statusCode'] < 400) // server confirms challenge acceptance
- ) {
- this.pollUntilValid(poll_uri)
- .then(() => {
- done.resolve();
- }); // poll status until server states success
- }
- else {
- done.reject(false); // server did not confirm challenge acceptance
- }
- });
- });
- }
- else {
- done.reject(false); // desired challenge is not in list
- }
- }
- else {
- done.reject(false); // server did not respond with status URI
- }
- }
- });
- }
- });
- return done.promise;
- }
- /**
- * acceptChallenge
- * @description tell server which challenge will be accepted
- * @param {Object} challenge
- * @param {function} callback - first argument will be the answer object
- */
- acceptChallenge(challenge = {}) {
- let done = q.defer();
- this.jWebClient.post(challenge['uri'], this.makeChallengeResponse(challenge))
- .then(() => {
- done.resolve();
- });
- return done.promise;
- }
- /**
- * pollUntilValid
- * @description periodically (with exponential back-off) check status of challenge
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilValid(uri, retry = 1) {
- let done = q.defer();
- if (retry > 128) {
- done.reject(false); // stop if retry value exceeds maximum
- }
- else {
- this.jWebClient.get(uri)
- .then((reqResArg) => {
- if (!(reqResArg.ans instanceof Object)) {
- done.reject(false); // invalid answer
- }
- else {
- if (reqResArg.ans['status'] === 'pending') {
- setTimeout(() => {
- this.pollUntilValid(uri, retry * 2); // retry
- }, retry * 500);
- }
- else {
- done.resolve(); // challenge complete
- }
- }
- });
- }
- return done.promise;
- }
- /**
- * pollUntilIssued
- * @description periodically (with exponential back-off) check status of CSR
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilIssued(uri, retry = 1) {
- let done = q.defer();
- if (retry > 128) {
- done.reject(false); // stop if retry value exceeds maximum
- }
- else {
- this.jWebClient.get(uri)
- .then((reqResArg) => {
- if ((reqResArg.ans instanceof Buffer) && (reqResArg.ans.length > 0)) {
- done.resolve(reqResArg.ans); // certificate was returned with answer
- }
- else {
- if ((reqResArg.res instanceof Object) && (reqResArg.res['statusCode'] < 400)) {
- setTimeout(() => {
- this.pollUntilIssued(uri, retry * 2); // retry
- }, retry * 500);
- }
- else {
- done.reject(false); // CSR complete
- }
- }
- });
- }
- return done.promise;
- }
- /**
- * requestSigning
- * @description send CSR
- * @param {string} domain - expected to be already sanitized
- * @param {function} callback - first argument will be the answer object
- */
- requestSigning(commonName) {
- let done = q.defer();
- fs.readFile(commonName + '.csr', (err, csrBuffer) => {
- if (err instanceof Object) {
- if (this.jWebClient.verbose) {
- console.error('Error : File system error', err['code'], 'while reading key from file');
- }
- done.reject(false);
- }
- else {
- let csr = csrBuffer.toString();
- this.jWebClient.post(this.directory['new-cert'], this.makeCertRequest(csr, this.daysValid))
- .then((reqResArg) => {
- if ((reqResArg.ans instanceof Buffer) && (reqResArg.ans.length > 0)) {
- done.resolve(reqResArg.ans); // certificate was returned with answer
- }
- else {
- if (reqResArg.res instanceof Object) {
- if ((reqResArg.res['statusCode'] < 400) && !reqResArg.ans) {
- let headers = reqResArg['headers'];
- if (!(headers instanceof Object)) {
- headers = {}; // ensure headers is object
- }
- this.pollUntilIssued(headers['location'])
- .then(x => { done.resolve(x); });
- }
- else {
- done.resolve((reqResArg.res['statusCode'] < 400) ? reqResArg.ans : false); // answer may be provided as string or object
- }
- }
- else {
- done.reject(false); // invalid response
- }
- }
- });
- }
- });
- return done.promise;
- }
- /**
- * retrieves profile of user (will make directory lookup and registration check)
- * @param {function} callback - first argument will be the answer object
- */
- getProfile() {
- let done = q.defer();
- this.getDirectory()
- .then((dir) => {
- if (!(dir instanceof Object)) {
- done.reject(new Error('server did not respond with directory'));
- }
- else {
- this.directory = dir; // cache directory
- this.newRegistration(null)
- .then((reqResArg) => {
- if ((reqResArg.res instanceof Object)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')) {
- this.regLink = reqResArg.res.headers['location'];
- this.getRegistration(this.regLink, null)
- .then((reqResArg) => {
- done.resolve();
- }); // get registration info from link
- }
- else {
- done.reject(new Error('registration failed'));
- }
- });
- }
- });
- return done.promise;
- }
- /**
- * createAccount
- * @description create new account (assumes directory lookup has already occured)
- * @param {string} email
- * @param {function} callback - first argument will be the registration URI
- */
- createAccount(email) {
- let done = q.defer();
- if (typeof email === 'string') {
- this.newRegistration({
- contact: [
- 'mailto:' + email
- ]
- })
- .then((reqResArg) => {
- if ((reqResArg.res instanceof Object)
- && (reqResArg.res['statusCode'] === 201)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')) {
- this.regLink = reqResArg.res.headers['location'];
- done.resolve(this.regLink); // registration URI
- }
- else {
- done.reject(new Error('could not register new account')); // registration failed
- }
- });
- }
- else {
- done.reject(new Error('no email address provided'));
- }
- return done.promise;
- }
- /**
- * agreeTos
- * @description agree with terms of service (update agreement status in profile)
- * @param {string} tosLink
- * @param {function} callback - first argument will be the answer object
- */
- agreeTos(tosLink) {
- let done = q.defer();
- this.getRegistration(this.regLink, {
- 'Agreement': tosLink // terms of service URI
- }).then(() => {
- done.resolve();
- });
- return done.promise;
- }
- /**
- * Entry-Point: Request certificate
- */
- requestCertificate(domainArg, organizationArg, countryCodeArg) {
- let done = q.defer();
- this.getProfile()
- .then((profile) => {
- let email = this.extractEmail(profile); // try to determine email address from profile
- countryCodeArg = this.makeSafeFileName(countryCodeArg);
- domainArg = this.makeSafeFileName(domainArg);
- email = this.makeSafeFileName(email);
- organizationArg = this.makeSafeFileName(organizationArg);
- // create key pair
- this.createKeyPair({
- keyBitSize: 4096,
- countryCode: countryCodeArg,
- organization: organizationArg,
- commonName: domainArg,
- emailAddress: email
- })
- .then(() => {
- this.requestSigning(domainArg)
- .then((cert) => {
- if ((cert instanceof Buffer) || (typeof cert === 'string')) {
- fs.writeFile(domainArg + '.der', cert, (err) => {
- if (err instanceof Object) {
- if (this.jWebClient.verbose) {
- console.error('Error : File system error', err['code'], 'while writing certificate to file');
- }
- done.reject(err);
- }
- else {
- done.resolve(); // CSR complete and certificate written to file system
- }
- });
- }
- else {
- done.reject('invalid certificate data');
- }
- });
- });
- });
- return done.promise;
- }
- /**
- * External: Create key pair
- * @param {number} bit - key strength, expected to be already sanitized
- * @param {string} c - country code, expected to be already sanitized
- * @param {string} o - organization, expected to be already sanitized
- * @param {string} cn - common name (domain name), expected to be already sanitized
- * @param {string} e - email address, expected to be already sanitized
- * @param {function} callback
- */
- createKeyPair(optionsArg) {
- let done = q.defer();
- let openssl = `openssl req -new -nodes -newkey rsa:${optionsArg.keyBitSize} `
- + `-sha256 `
- + `-subj "/C=${optionsArg.countryCode}/O=${optionsArg.organization}/CN=${optionsArg.commonName}/emailAddress=${optionsArg.emailAddress}" `
- + `-keyout \"${optionsArg.commonName}.key\" -outform der -out \"${optionsArg.commonName}.csr\"`;
- console.error('Action : Creating key pair');
- if (this.jWebClient.verbose) {
- console.error('Running:', openssl);
- }
- plugins.shelljs.exec(openssl, (codeArg, stdOutArg, stdErrorArg) => {
- if (!stdErrorArg) {
- done.resolve();
- }
- else {
- done.reject(stdErrorArg);
- }
- });
- return done.promise;
- }
- /**
- * Helper: Empty callback
- */
- emptyCallback() {
- // nop
- }
- /**
- * Helper: Make safe file name or path from string
- * @param {string} name
- * @param {boolean} withPath - optional, default false
- * @return {string}
- */
- makeSafeFileName(name, withPath = false) {
- if (typeof name !== 'string') {
- name = '';
- }
- // respects file name restrictions for ntfs and ext2
- let regexFile = '[<>:\"/\\\\\\|\\?\\*\\u0000-\\u001f\\u007f\\u0080-\\u009f]';
- let regexPath = '[<>:\"\\\\\\|\\?\\*\\u0000-\\u001f\\u007f\\u0080-\\u009f]';
- return name.replace(new RegExp(withPath ? regexPath : regexFile, 'g'), (charToReplace) => {
- if (typeof charToReplace === 'string') {
- return '%' + charToReplace.charCodeAt(0).toString(16).toLocaleUpperCase();
- }
- return '%00';
- });
- }
- /**
- * Helper: Prepare challenge
- * @param {string} domain
- * @param {Object} challenge
- * @param {function} callback
- */
- prepareChallenge(domain, challenge, callback) {
- /*jshint -W069, unused:false*/
- if (typeof callback !== 'function') {
- callback = this.emptyCallback; // ensure callback is function
- }
- if (challenge instanceof Object) {
- if (challenge['type'] === 'http-01') {
- let path = this.webroot + this.wellKnownPath + challenge['token']; // webroot and well_known_path are expected to be already sanitized
- fs.writeFile(path, this.makeKeyAuthorization(challenge), (err) => {
- if (err instanceof Object) {
- if (this.jWebClient.verbose) {
- console.error('Error : File system error', err['code'], 'while writing challenge data to file');
- }
- callback();
- }
- else {
- // let uri = "http://" + domain + this.well_known_path + challenge["token"]
- let rl = readline.createInterface(process.stdin, process.stdout);
- if (this.withInteraction) {
- rl.question('Press enter to proceed', (answer) => {
- rl.close();
- callback();
- });
- }
- else {
- rl.close();
- callback(); // skip interaction prompt if desired
- }
- }
- });
- }
- else {
- console.error('Error : Challenge not supported');
- callback();
- }
- }
- else {
- console.error('Error : Invalid challenge response');
- callback();
- }
- }
- /**
- * Helper: Extract TOS Link, e.g. from "<http://...>;rel="terms-of-service"
- * @param {string} linkStr
- * @return {string}
- */
- getTosLink(linkStr) {
- let match = /(<)([^>]+)(>;rel="terms-of-service")/g.exec(linkStr);
- if ((match instanceof Array) && (match.length > 2)) {
- let result = match[2];
- // dereference
- match = null;
- return result;
- }
- }
- /**
- * Helper: Select challenge by type
- * @param {Object} ans
- * @param {string} challenge_type
- * @return {Object}
- */
- selectChallenge(ans, challengeType) {
- /*jshint -W069 */
- if ((ans instanceof Object) && (ans['challenges'] instanceof Array)) {
- return ans.challenges.filter((entry) => {
- let type = entry['type'];
- // dereference
- entry = null;
- if (type === challengeType) {
- return true;
- }
- return false;
- }).pop();
- } // return first match or undefined
- // dereference
- ans = null;
- return void 0; // challenges not available or in expected format
- }
- /**
- * Helper: Extract first found email from profile (without mailto prefix)
- * @param {Object} profile
- * @return {string}
- */
- extractEmail(profile) {
- let prefix = 'mailto:';
- let email = profile.contact.filter((entry) => {
- if (typeof entry !== 'string') {
- return false;
- }
- else {
- return !entry.indexOf(prefix); // check for mail prefix
- }
- }).pop();
- // dereference
- profile = null;
- if (typeof email !== 'string') {
- return void 0;
- } // return default
- return email.substr(prefix.length); // only return email address without protocol prefix
- }
- /**
- * Make ACME-Request: Domain-Authorization Request - Object: resource, identifier
- * @param {string} domain
- * @return {{resource: string, identifier: Object}}
- */
- makeDomainAuthorizationRequest(domain) {
- return {
- 'resource': 'new-authz',
- 'identifier': {
- 'type': 'dns',
- 'value': domain
- }
- };
- }
- /**
- * Make ACME-Object: Key-Authorization (encoded) - String: Challenge-Token . Encoded-Account-Key-Hash
- * @param {Object} challenge
- * @return {string}
- */
- makeKeyAuthorization(challenge) {
- /*jshint -W069 */
- if (challenge instanceof Object) {
- if (this.clientProfilePubKey instanceof Object) {
- let jwk = json_to_utf8buffer({
- e: this.clientProfilePubKey['e'],
- kty: this.clientProfilePubKey['kty'],
- n: this.clientProfilePubKey['n']
- });
- let hash = crypto.createHash('sha256').update(jwk.toString('utf8'), 'utf8').digest();
- // create base64 encoded hash of account key
- let ACCOUNT_KEY = plugins.smartstring.base64.encodeUri(hash.toString());
- let token = challenge['token'];
- return token + '.' + ACCOUNT_KEY;
- }
- }
- else {
- return ''; // return default (for writing to file)
- }
- }
- /**
- * Make ACME-Request: Challenge-Response - Object: resource, keyAuthorization
- * @param {Object} challenge
- * @return {{resource: string, keyAuthorization: string}}
- */
- makeChallengeResponse(challenge) {
- return {
- 'resource': 'challenge',
- 'keyAuthorization': this.makeKeyAuthorization(challenge)
- };
- }
- /**
- * Make ACME-Request: CSR - Object: resource, csr, notBefore, notAfter
- * @param {string} csr
- * @param {number} days_valid
- * @return {{resource: string, csr: string, notBefore: string, notAfter: string}}
- */
- makeCertRequest(csr, DAYS_VALID) {
- if (typeof csr !== 'string' && !(csr instanceof Buffer)) {
- csr = ''; // default string for CSR
- }
- if ((typeof DAYS_VALID !== 'number') || (isNaN(DAYS_VALID)) || (DAYS_VALID === 0)) {
- DAYS_VALID = 1; // default validity duration (1 day)
- }
- let DOMAIN_CSR_DER = plugins.smartstring.base64.encodeUri(csr); // create base64 encoded CSR
- let CURRENT_DATE = (new Date()).toISOString(); // set start date to current date
- // set end date to current date + days_valid
- let NOTAFTER_DATE = (new Date((+new Date()) + 1000 * 60 * 60 * 24 * Math.abs(DAYS_VALID))).toISOString();
- return {
- 'resource': 'new-cert',
- 'csr': DOMAIN_CSR_DER,
- 'notBefore': CURRENT_DATE,
- 'notAfter': NOTAFTER_DATE
- };
- }
-}
-exports.AcmeClient = AcmeClient;
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuYWNtZWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0YWNtZS5jbGFzc2VzLmFjbWVjbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUE4QztBQUM5Qyx1QkFBc0I7QUFDdEIsaUNBQWdDO0FBQ2hDLHlCQUF3QjtBQUN4QixxQ0FBb0M7QUFDcEMsaUZBQTJEO0FBRzNEOzs7Ozs7O0dBT0c7QUFDSCxJQUFJLGtCQUFrQixHQUFHLENBQUMsR0FBRztJQUN6QixNQUFNLENBQUMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQTtBQUNsRCxDQUFDLENBQUE7QUFFRDs7Ozs7O0dBTUc7QUFDSDtJQWFJLFlBQVksZUFBZTtRQU4zQixlQUFVLEdBQUcsSUFBSSx5Q0FBVSxFQUFFLENBQUE7UUFPekI7OztXQUdHO1FBQ0gsSUFBSSxDQUFDLG1CQUFtQixHQUFHLEVBQUUsQ0FBQTtRQUM3Qjs7OztXQUlHO1FBQ0gsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUE7UUFFbEI7OztXQUdHO1FBQ0gsSUFBSSxDQUFDLFNBQVMsR0FBRyxFQUFFLENBQUE7UUFDbkI7OztXQUdHO1FBQ0gsSUFBSSxDQUFDLFlBQVksR0FBRyxlQUFlLENBQUE7UUFDbkM7Ozs7V0FJRztRQUNILElBQUksQ0FBQyxrQkFBa0IsR0FBRyxZQUFZLENBQUEsQ0FBQyxXQUFXO1FBQ2xEOzs7V0FHRztRQUNILElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFBLENBQUMsV0FBVztRQUNyQzs7O1dBR0c7UUFDSCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQSxDQUFDLFdBQVc7UUFDL0I7OztXQUdHO1FBQ0gsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUEsQ0FBQyxXQUFXO1FBQy9COzs7O1dBSUc7UUFDSCxJQUFJLENBQUMs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
\ No newline at end of file
diff --git a/dist/smartacme.classes.jwebclient.d.ts b/dist/smartacme.classes.jwebclient.d.ts
deleted file mode 100644
index b6fa06c..0000000
--- a/dist/smartacme.classes.jwebclient.d.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-///
-import * as q from 'q';
-export interface IReqResArg {
- ans: any;
- res: any;
-}
-/**
- * @class JWebClient
- * @constructor
- * @description Implementation of HTTPS-based JSON-Web-Client
- */
-export declare class JWebClient {
- /**
- * User account key pair
- */
- keyPair: any;
- /**
- * Cached nonce returned with last request
- */
- lastNonce: string;
- /**
- * @member {boolean} module:JWebClient~JWebClient#verbose
- * @desc Determines verbose mode
- */
- verbose: boolean;
- constructor();
- /**
- * createJWT
- * @description create JSON-Web-Token signed object
- * @param {string|undefined} nonce
- * @param {Object|string|number|boolean} payload
- * @param {string} alg
- * @param {Object|string} key
- * @param {Object} jwk
- * @return {string}
- */
- createJWT(nonce: any, payload: any, alg: any, key: any, jwk: any): string;
- /**
- * request
- * @description make GET or POST request over HTTPS and use JOSE as payload type
- * @param {string} query
- * @param {string} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- request(query: string, payload?: string): q.Promise<{}>;
- /**
- * get
- * @description make GET request
- * @param {string} uri
- * @param {function} callback
- * @param {function} errorCallback
- */
- get(uri: string): q.Promise;
- /**
- * make POST request
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- post(uri: string, payload: any): q.Promise;
- /**
- * checks if status is expected and log errors
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {Object|string} ans
- * @param {Object} res
- */
- evaluateStatus(uri: any, payload: any, ans: any, res: any): void;
-}
diff --git a/dist/smartacme.classes.jwebclient.js b/dist/smartacme.classes.jwebclient.js
deleted file mode 100644
index 4a2bb76..0000000
--- a/dist/smartacme.classes.jwebclient.js
+++ /dev/null
@@ -1,222 +0,0 @@
-"use strict";
-const plugins = require("./smartacme.plugins");
-const https = require("https");
-let jwa = require('jwa');
-const url = require("url");
-const q = require("q");
-/**
- * json_to_utf8base64url
- * @private
- * @description convert JSON to base64-url encoded string using UTF-8 encoding
- * @param {Object} obj
- * @return {string}
- * @throws Exception if object cannot be stringified or contains cycle
- */
-let json_to_utf8base64url = (obj) => {
- return plugins.smartstring.base64.encodeUri(JSON.stringify(obj));
-};
-/**
- * @class JWebClient
- * @constructor
- * @description Implementation of HTTPS-based JSON-Web-Client
- */
-class JWebClient {
- constructor() {
- /**
- * User account key pair
- */
- this.keyPair = {};
- /**
- * Cached nonce returned with last request
- */
- this.lastNonce = null;
- this.verbose = false;
- }
- /**
- * createJWT
- * @description create JSON-Web-Token signed object
- * @param {string|undefined} nonce
- * @param {Object|string|number|boolean} payload
- * @param {string} alg
- * @param {Object|string} key
- * @param {Object} jwk
- * @return {string}
- */
- createJWT(nonce, payload, alg, key, jwk) {
- /*jshint -W069 */
- // prepare key
- if (key instanceof Object) {
- key = new Buffer(plugins.smartstring.base64.decode(key['k']));
- }
- // prepare header
- let header = {
- typ: 'JWT',
- alg: alg,
- jwk: jwk,
- nonce: null
- };
- if (nonce !== void 0) {
- header.nonce = nonce;
- }
- // concatenate header and payload
- let input = [
- json_to_utf8base64url(header),
- json_to_utf8base64url(payload)
- ].join('.');
- // sign input
- let hmac = jwa(alg);
- let sig = hmac.sign(input, key);
- // concatenate input and signature
- let output = [
- input,
- sig
- ].join('.');
- // output
- return output;
- }
- /**
- * request
- * @description make GET or POST request over HTTPS and use JOSE as payload type
- * @param {string} query
- * @param {string} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- request(query, payload = null) {
- let done = q.defer();
- // prepare options
- let uri = url.parse(query);
- let options = {
- hostname: uri.hostname,
- port: parseInt(uri.port, 10),
- path: uri.path,
- method: null,
- headers: {}
- };
- if (!payload === null) {
- options.method = 'POST';
- options.headers = {
- 'Content-Type': 'application/jose',
- 'Content-Length': payload.length
- };
- }
- else {
- options.method = 'GET';
- }
- // prepare request
- let req = https.request(options, (res) => {
- // receive data
- let data = [];
- res.on('data', (block) => {
- if (block instanceof Buffer) {
- data.push(block);
- }
- });
- res.on('end', () => {
- let buf = Buffer.concat(data);
- let isJSON = ((res instanceof Object)
- && (res['headers'] instanceof Object)
- && (typeof res.headers['content-type'] === 'string')
- && (res.headers['content-type'].indexOf('json') > -1));
- if (isJSON && buf.length > 0) {
- try {
- // convert to JSON
- let json = JSON.parse(buf.toString('utf8'));
- done.resolve({ json: json, res: res });
- }
- catch (e) {
- // error (if empty or invalid JSON)
- done.reject(e);
- }
- }
- });
- }).on('error', (e) => {
- console.error('Error occured', e);
- // error
- done.reject(e);
- });
- // write POST body if payload was specified
- if (!payload === null) {
- req.write(payload);
- }
- // make request
- req.end();
- return done.promise;
- }
- /**
- * get
- * @description make GET request
- * @param {string} uri
- * @param {function} callback
- * @param {function} errorCallback
- */
- get(uri) {
- let done = q.defer();
- this.request(uri)
- .then((reqResArg) => {
- this.evaluateStatus(uri, null, reqResArg.ans, reqResArg.res);
- // save replay nonce for later requests
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- this.lastNonce = reqResArg.res.headers['replay-nonce'];
- }
- done.resolve(reqResArg);
- });
- return done.promise;
- }
- /**
- * make POST request
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- post(uri, payload) {
- let done = q.defer();
- let jwt = this.createJWT(this.lastNonce, payload, 'RS256', this.keyPair['private_pem'], this.keyPair['public_jwk']);
- this.request(uri, jwt)
- .then((reqResArg) => {
- this.evaluateStatus(uri, payload, reqResArg.ans, reqResArg.res);
- // save replay nonce for later requests
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- this.lastNonce = reqResArg.res.headers['replay-nonce'];
- }
- done.resolve(reqResArg);
- });
- return done.promise;
- }
- /**
- * checks if status is expected and log errors
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {Object|string} ans
- * @param {Object} res
- */
- evaluateStatus(uri, payload, ans, res) {
- if (this.verbose) {
- if ((payload instanceof Object)
- || (typeof payload === 'string')
- || (typeof payload === 'number')
- || (typeof payload === 'boolean')) {
- console.error('Send :', payload); // what has been sent
- }
- }
- let uri_parsed = url.parse(uri);
- if (res['statusCode'] >= 100 && res['statusCode'] < 400) {
- console.error('HTTP :', res['statusCode'], uri_parsed.path); // response code if successful
- }
- if (res['statusCode'] >= 400 && res['statusCode'] < 500) {
- console.error('HTTP :', res['statusCode'], uri_parsed.path); // response code if error
- if (ans instanceof Object) {
- if (typeof ans['detail'] === 'string') {
- console.error('Message:', ans.detail.split(' :: ').pop()); // error message if any
- }
- }
- }
- if (this.verbose) {
- console.error('Receive:', res['headers']); // received headers
- console.error('Receive:', ans); // received data
- }
- }
-}
-exports.JWebClient = JWebClient;
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuandlYmNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0YWNtZS5jbGFzc2VzLmp3ZWJjbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUE4QztBQUM5QywrQkFBOEI7QUFDOUIsSUFBSSxHQUFHLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFBO0FBQ3hCLDJCQUEwQjtBQUMxQix1QkFBc0I7QUFPdEI7Ozs7Ozs7R0FPRztBQUNILElBQUkscUJBQXFCLEdBQUcsQ0FBQyxHQUFHO0lBQzVCLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO0FBQ3BFLENBQUMsQ0FBQTtBQUVEOzs7O0dBSUc7QUFDSDtJQWdCSTtRQWZBOztXQUVHO1FBQ0gsWUFBTyxHQUFRLEVBQUUsQ0FBQTtRQUVqQjs7V0FFRztRQUNILGNBQVMsR0FBVyxJQUFJLENBQUE7UUFRcEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUE7SUFDeEIsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILFNBQVMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRztRQUNuQyxpQkFBaUI7UUFDakIsY0FBYztRQUNkLEVBQUUsQ0FBQyxDQUFDLEdBQUcsWUFBWSxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQ3hCLEdBQUcsR0FBRyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUNqRSxDQUFDO1FBQ0QsaUJBQWlCO1FBQ2pCLElBQUksTUFBTSxHQUFHO1lBQ1QsR0FBRyxFQUFFLEtBQUs7WUFDVixHQUFHLEVBQUUsR0FBRztZQUNSLEdBQUcs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
\ No newline at end of file
diff --git a/dist/smartacme.classes.smartacme.d.ts b/dist/smartacme.classes.smartacme.d.ts
index 04e7d4c..b287317 100644
--- a/dist/smartacme.classes.smartacme.d.ts
+++ b/dist/smartacme.classes.smartacme.d.ts
@@ -1,16 +1,29 @@
-import * as acmeclient from './smartacme.classes.acmeclient';
+///
+import 'typings-global';
+import * as q from 'q';
+/**
+ * class SmartAcme exports methods for maintaining SSL Certificates
+ */
export declare class SmartAcme {
- acmeAccount: AcmeAccount;
- acmeClient: acmeclient.AcmeClient;
- constructor(directoryUrlArg?: string);
+ preparedBool: boolean;
+ acmeUrls: any;
+ productionBool: boolean;
+ keyPair: any;
+ constructor(productionArg?: boolean);
/**
- * creates an account
+ * prepares the SmartAcme class
*/
- createAccount(): void;
+ prepareAcme(): q.Promise<{}>;
/**
- * returns the openssl key pair for
+ * creates an account if not currently present in module
*/
- getKeyPair(): any;
-}
-export declare class AcmeAccount {
+ createAccount(): q.Promise<{}>;
+ /**
+ * creates a keyPair
+ */
+ createKeyPair(): q.Promise<{}>;
+ /**
+ * gets the Acme Urls
+ */
+ getAcmeUrls(): q.Promise<{}>;
}
diff --git a/dist/smartacme.classes.smartacme.js b/dist/smartacme.classes.smartacme.js
index 5824955..bb5a8c8 100644
--- a/dist/smartacme.classes.smartacme.js
+++ b/dist/smartacme.classes.smartacme.js
@@ -1,26 +1,104 @@
"use strict";
-const acmeclient = require("./smartacme.classes.acmeclient");
+require("typings-global");
+const q = require("q");
+let ACME = require('le-acme-core').ACME.create();
+let RSA = require('rsa-compat').RSA;
+let bitlen = 1024;
+let exp = 65537;
+let options = {
+ public: true,
+ pem: true,
+ internal: true
+};
+/**
+ * class SmartAcme exports methods for maintaining SSL Certificates
+ */
class SmartAcme {
- constructor(directoryUrlArg = 'https://acme-staging.api.letsencrypt.org/directory') {
- this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg);
+ constructor(productionArg = false) {
+ this.preparedBool = false;
+ this.productionBool = productionArg;
}
/**
- * creates an account
+ * prepares the SmartAcme class
+ */
+ prepareAcme() {
+ let done = q.defer();
+ if (this.preparedBool === false) {
+ this.getAcmeUrls()
+ .then(() => {
+ return this.createKeyPair();
+ })
+ .then((x) => {
+ console.log('prepared smartacme instance');
+ done.resolve();
+ });
+ }
+ else {
+ done.resolve();
+ }
+ return done.promise;
+ }
+ /**
+ * creates an account if not currently present in module
*/
createAccount() {
- this.acmeClient.createAccount('test@bleu.de', (answer) => {
- console.log(answer);
- });
+ let done = q.defer();
+ this.prepareAcme()
+ .then(() => {
+ return this.createKeyPair();
+ })
+ .then(() => {
+ let options = {
+ newRegUrl: this.acmeUrls.newReg,
+ email: 'domains@lossless.org',
+ accountKeypair: {
+ privateKeyPem: this.keyPair
+ },
+ agreeToTerms: function (tosUrl, done) {
+ done(null, tosUrl);
+ }
+ };
+ ACME.registerNewAccount(options, (err, regr) => {
+ if (err) {
+ console.log(err);
+ done.reject(err);
+ }
+ done.resolve(regr);
+ }); // returns "regr" registration data
+ }).catch(err => { console.log(err); });
+ return done.promise;
}
/**
- * returns the openssl key pair for
+ * creates a keyPair
*/
- getKeyPair() {
- return this.acmeClient.getKeyPair();
+ createKeyPair() {
+ let done = q.defer();
+ RSA.generateKeypair(bitlen, exp, options, (err, keypair) => {
+ if (err) {
+ console.log(err);
+ done.reject(err);
+ }
+ console.log(keypair);
+ this.keyPair = keypair;
+ });
+ done.resolve();
+ return done.promise;
+ }
+ /**
+ * gets the Acme Urls
+ */
+ getAcmeUrls() {
+ let done = q.defer();
+ ACME.getAcmeUrls(ACME.stagingServerUrl, (err, urls) => {
+ if (err) {
+ throw err;
+ }
+ this.acmeUrls = urls;
+ console.log(this.acmeUrls);
+ done.resolve();
+ });
+ return done.promise;
}
}
exports.SmartAcme = SmartAcme;
-class AcmeAccount {
-}
-exports.AcmeAccount = AcmeAccount;
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSw2REFBNEQ7QUFFNUQ7SUFHSSxZQUFZLGtCQUEwQixvREFBb0Q7UUFDdEYsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDaEUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsYUFBYTtRQUNULElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLGNBQWMsRUFBQyxDQUFDLE1BQU07WUFDaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUN2QixDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUM7SUFFRDs7T0FFRztJQUNILFVBQVU7UUFDTixNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLEVBQUUsQ0FBQTtJQUN2QyxDQUFDO0NBQ0o7QUF0QkQsOEJBc0JDO0FBRUQ7Q0FFQztBQUZELGtDQUVDIn0=
\ No newline at end of file
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSwwQkFBdUI7QUFDdkIsdUJBQXNCO0FBTXRCLElBQUksSUFBSSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUE7QUFDaEQsSUFBSSxHQUFHLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDLEdBQUcsQ0FBQTtBQUVuQyxJQUFJLE1BQU0sR0FBRyxJQUFJLENBQUE7QUFDakIsSUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFBO0FBQ2YsSUFBSSxPQUFPLEdBQUc7SUFDVixNQUFNLEVBQUUsSUFBSTtJQUNaLEdBQUcsRUFBRSxJQUFJO0lBQ1QsUUFBUSxFQUFFLElBQUk7Q0FDakIsQ0FBQTtBQUNEOztHQUVHO0FBQ0g7SUFLSSxZQUFZLGdCQUF5QixLQUFLO1FBSjFDLGlCQUFZLEdBQVksS0FBSyxDQUFBO1FBS3pCLElBQUksQ0FBQyxjQUFjLEdBQUcsYUFBYSxDQUFBO0lBQ3ZDLENBQUM7SUFFRDs7T0FFRztJQUNILFdBQVc7UUFDUCxJQUFJLElBQUksR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUE7UUFDcEIsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksS0FBSyxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBQzlCLElBQUksQ0FBQyxXQUFXLEVBQUU7aUJBQ2IsSUFBSSxDQUFDO2dCQUNGLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUE7WUFDL0IsQ0FBQyxDQUFDO2lCQUNELElBQUksQ0FBQyxDQUFDLENBQUM7Z0JBQ0osT0FBTyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFBO2dCQUMxQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUE7WUFDbEIsQ0FBQyxD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
\ No newline at end of file
diff --git a/dist/smartacme.paths.js b/dist/smartacme.paths.js
index 3fd577f..00bbc68 100644
--- a/dist/smartacme.paths.js
+++ b/dist/smartacme.paths.js
@@ -1,5 +1,7 @@
"use strict";
-const plugins = require("./smartacme.plugins");
-exports.packageDir = plugins.path.join(__dirname, '../');
-exports.assetDir = plugins.path.join(exports.packageDir, 'assets/');
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBhdGhzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLnBhdGhzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSwrQ0FBOEM7QUFFbkMsUUFBQSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDLEtBQUssQ0FBQyxDQUFBO0FBQy9DLFFBQUEsUUFBUSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGtCQUFVLEVBQUMsU0FBUyxDQUFDLENBQUEifQ==
\ No newline at end of file
+const path = require("path");
+const smartfile = require("smartfile");
+exports.packageDir = path.join(__dirname, '../');
+exports.assetDir = path.join(exports.packageDir, 'assets/');
+smartfile.fs.ensureDirSync(exports.assetDir);
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBhdGhzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLnBhdGhzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSw2QkFBNEI7QUFDNUIsdUNBQXNDO0FBRTNCLFFBQUEsVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFDLEtBQUssQ0FBQyxDQUFBO0FBQ3ZDLFFBQUEsUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsa0JBQVUsRUFBQyxTQUFTLENBQUMsQ0FBQTtBQUNyRCxTQUFTLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxnQkFBUSxDQUFDLENBQUEifQ==
\ No newline at end of file
diff --git a/dist/smartacme.plugins.d.ts b/dist/smartacme.plugins.d.ts
deleted file mode 100644
index 52ad01f..0000000
--- a/dist/smartacme.plugins.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import 'typings-global';
-import * as path from 'path';
-import * as smartfile from 'smartfile';
-import * as smartstring from 'smartstring';
-import * as shelljs from 'shelljs';
-export { path, smartfile, smartstring, shelljs };
diff --git a/dist/smartacme.plugins.js b/dist/smartacme.plugins.js
deleted file mode 100644
index 0a53b08..0000000
--- a/dist/smartacme.plugins.js
+++ /dev/null
@@ -1,11 +0,0 @@
-"use strict";
-require("typings-global");
-const path = require("path");
-exports.path = path;
-const smartfile = require("smartfile");
-exports.smartfile = smartfile;
-const smartstring = require("smartstring");
-exports.smartstring = smartstring;
-const shelljs = require("shelljs");
-exports.shelljs = shelljs;
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsMEJBQXVCO0FBQ3ZCLDZCQUE0QjtBQU14QixvQkFBSTtBQUxSLHVDQUFzQztBQU1sQyw4QkFBUztBQUxiLDJDQUEwQztBQU10QyxrQ0FBVztBQUxmLG1DQUFrQztBQU05QiwwQkFBTyJ9
\ No newline at end of file
diff --git a/package.json b/package.json
index 74e7e72..34640bd 100644
--- a/package.json
+++ b/package.json
@@ -23,11 +23,10 @@
},
"homepage": "https://gitlab.com/pushrocks/smartacme#README",
"dependencies": {
- "@types/base64url": "^2.0.3",
- "jwa": "^1.1.3",
+ "@types/q": "0.x.x",
+ "le-acme-core": "^2.0.7",
"q": "^1.4.1",
- "rsa-pem-to-jwk": "^1.1.3",
- "shelljs": "^0.7.5",
+ "rsa-compat": "^1.2.7",
"smartfile": "^4.1.0",
"smartstring": "^2.0.20",
"typings-global": "^1.0.14"
diff --git a/test/test.js b/test/test.js
index f3b513e..56c73cc 100644
--- a/test/test.js
+++ b/test/test.js
@@ -6,13 +6,26 @@ const smartacme = require("../dist/index");
describe('smartacme', function () {
let testAcme;
it('should create a valid instance', function () {
+ this.timeout(10000);
testAcme = new smartacme.SmartAcme();
should(testAcme).be.instanceOf(smartacme.SmartAcme);
});
+ it('should get the ACME urls', function (done) {
+ testAcme.getAcmeUrls().then(() => { done(); });
+ });
+ it('should prepare the Instance', function (done) {
+ testAcme.prepareAcme().then(done);
+ });
it('should have created keyPair', function () {
});
- it('should register a new account', function () {
- testAcme.createAccount();
+ it('should register a new account', function (done) {
+ testAcme.createAccount().then(x => {
+ console.log(x);
+ done();
+ }).catch(err => {
+ console.log(err);
+ done(err);
+ });
});
});
-//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUFxQjtBQUNyQixpQ0FBZ0M7QUFFaEMsNEJBQTRCO0FBQzVCLDJDQUEwQztBQUUxQyxRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ2xCLElBQUksUUFBNkIsQ0FBQTtJQUNqQyxFQUFFLENBQUMsZ0NBQWdDLEVBQUU7UUFDakMsUUFBUSxHQUFHLElBQUksU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFBO1FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUN2RCxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRTtJQUVsQyxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQywrQkFBK0IsRUFBRTtRQUNoQyxRQUFRLENBQUMsYUFBYSxFQUFFLENBQUE7SUFDNUIsQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUMsQ0FBQSJ9
\ No newline at end of file
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUFxQjtBQUNyQixpQ0FBZ0M7QUFFaEMsNEJBQTRCO0FBQzVCLDJDQUEwQztBQUUxQyxRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ2xCLElBQUksUUFBNkIsQ0FBQTtJQUNqQyxFQUFFLENBQUMsZ0NBQWdDLEVBQUU7UUFDakMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUNuQixRQUFRLEdBQUcsSUFBSSxTQUFTLENBQUMsU0FBUyxFQUFFLENBQUE7UUFDcEMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxDQUFBO0lBQ3ZELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLDBCQUEwQixFQUFFLFVBQVUsSUFBSTtRQUN6QyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsSUFBSSxFQUFFLENBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUNqRCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRSxVQUFVLElBQUk7UUFDNUMsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNyQyxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRTtJQUVsQyxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQywrQkFBK0IsRUFBRSxVQUFVLElBQUk7UUFDOUMsUUFBUSxDQUFDLGFBQWEsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzNCLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDZCxJQUFJLEVBQUUsQ0FBQTtRQUNWLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHO1lBQ1IsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtZQUNoQixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDYixDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUMsQ0FBQyxDQUFBO0FBQ04sQ0FBQyxDQUFDLENBQUEifQ==
\ No newline at end of file
diff --git a/test/test.ts b/test/test.ts
index 67ece67..68d93cc 100644
--- a/test/test.ts
+++ b/test/test.ts
@@ -4,16 +4,31 @@ import * as should from 'should'
// import the module to test
import * as smartacme from '../dist/index'
-describe('smartacme', function(){
+describe('smartacme', function () {
let testAcme: smartacme.SmartAcme
- it('should create a valid instance', function(){
+ it('should create a valid instance', function () {
+ this.timeout(10000)
testAcme = new smartacme.SmartAcme()
should(testAcme).be.instanceOf(smartacme.SmartAcme)
})
- it('should have created keyPair', function() {
-
+
+ it('should get the ACME urls', function (done) {
+ testAcme.getAcmeUrls().then(() => { done() })
})
- it('should register a new account', function() {
- testAcme.createAccount().catch()
+
+ it('should prepare the Instance', function (done) {
+ testAcme.prepareAcme().then(done)
+ })
+ it('should have created keyPair', function () {
+
+ })
+ it('should register a new account', function (done) {
+ testAcme.createAccount().then(x => {
+ console.log(x)
+ done()
+ }).catch(err => {
+ console.log(err)
+ done(err)
+ })
})
})
diff --git a/ts/smartacme.classes.acmeclient.ts b/ts/smartacme.classes.acmeclient.ts
deleted file mode 100644
index dccbcb2..0000000
--- a/ts/smartacme.classes.acmeclient.ts
+++ /dev/null
@@ -1,743 +0,0 @@
-import * as plugins from './smartacme.plugins'
-import * as q from 'q'
-import * as crypto from 'crypto'
-import * as fs from 'fs'
-import * as readline from 'readline'
-import { JWebClient } from './smartacme.classes.jwebclient'
-import { IReqResArg } from './smartacme.classes.jwebclient'
-
-/**
- * json_to_utf8buffer
- * @private
- * @description convert JSON to Buffer using UTF-8 encoding
- * @param {Object} obj
- * @return {Buffer}
- * @throws Exception if object cannot be stringified or contains cycle
- */
-let json_to_utf8buffer = (obj) => {
- return new Buffer(JSON.stringify(obj), 'utf8')
-}
-
-/**
- * @class AcmeClient
- * @constructor
- * @description ACME protocol implementation from client perspective
- * @param {string} directory_url - Address of directory
- * @param {module:JWebClient~JWebClient} jWebClient - Reference to JSON-Web-Client
- */
-export class AcmeClient {
- clientProfilePubKey: any
- daysValid: number
- directory: any
- directoryUrl: string
- emailDefaultPrefix: string
- emailOverride: string
- jWebClient = new JWebClient()
- regLink: string
- tosLink: string
- webroot: string
- wellKnownPath: string
- withInteraction: boolean
- constructor(directoryUrlArg) {
- /**
- * @member {Object} module:AcmeClient~AcmeClient#clientProfilePubKey
- * @desc Cached public key obtained from profile
- */
- this.clientProfilePubKey = {}
- /**
- * @member {number} module:AcmeClient~AcmeClient#days_valid
- * @desc Validity period in days
- * @default 1
- */
- this.daysValid = 1
-
- /**
- * @member {Object} module:AcmeClient~AcmeClient#directory
- * @desc Hash map of REST URIs
- */
- this.directory = {}
- /**
- * @member {string} module:AcmeClient~AcmeClient#directory_url
- * @desc Address of directory
- */
- this.directoryUrl = directoryUrlArg
- /**
- * @member {string} module:AcmeClient~AcmeClient#emailDefaultPrefix
- * @desc Prefix of email address if constructed from domain name
- * @default "hostmaster"
- */
- this.emailDefaultPrefix = 'hostmaster' // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#emailOverride
- * @desc Email address to use
- */
- this.emailOverride = null // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#regLink
- * @desc Cached registration URI
- */
- this.regLink = null // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#tosLink
- * @desc Cached terms of service URI
- */
- this.tosLink = null // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#webroot
- * @desc Path to server web root (or path to store challenge data)
- * @default "."
- */
- this.webroot = '.' // {string}
- /**
- * @member {string} module:AcmeClient~AcmeClient#well_known_path
- * @desc Directory structure for challenge data
- * @default "/.well-known/acme-challenge/"
- */
- this.wellKnownPath = '/.well-known/acme-challenge/' // {string}
- /**
- * @member {boolean} module:AcmeClient~AcmeClient#withInteraction
- * @desc Determines if interaction of user is required
- * @default true
- */
- this.withInteraction = true // {boolean}
- }
-
- // *****************************************************************************
- // REQUEST-Section
- // *****************************************************************************
-
- /**
- * getDirectory
- * @description retrieve directory entries (directory url must be set prior to execution)
- * @param {function} callback - first argument will be the answer object
- */
- getDirectory() {
- let done = q.defer()
- this.jWebClient.get(this.directoryUrl)
- .then((reqResArg: IReqResArg) => {
- done.resolve(reqResArg)
- })
- return done.promise
- }
-
- /**
- * newRegistration
- * @description try to register (directory lookup must have occured prior to execution)
- * @param {Object} payload
- * @param {function} callback - first argument will be the answer object
- */
- newRegistration(payload) {
- let done = q.defer()
- if (!(payload instanceof Object)) {
- payload = {} // ensure payload is object
- }
- payload.resource = 'new-reg'
- this.jWebClient.post(this.directory['new-reg'], payload)
- return done.promise
- }
-
- /**
- * getRegistration
- * @description get information about registration
- * @param {string} uri - will be exposed when trying to register
- * @param {Object} payload - update information
- * @param {function} callback - first argument will be the answer object
- */
- getRegistration(uri, payload) {
- let done = q.defer()
- payload['resource'] = 'reg'
- this.jWebClient.post(uri, payload)
- .then((reqResArg: IReqResArg) => {
- if (reqResArg.ans instanceof Object) {
- this.clientProfilePubKey = reqResArg.ans.key // cache or reset returned public key
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- let linkStr = reqResArg.res.headers['link']
- if (typeof linkStr === 'string') {
- let tosLink = this.getTosLink(linkStr)
- if (typeof tosLink === 'string') {
- this.tosLink = tosLink // cache TOS link
- } else {
- this.tosLink = null // reset TOS link
- }
- } else {
- this.tosLink = null // reset TOS link
- }
- } else {
- this.tosLink = null // reset TOS link
- }
- done.resolve({ ans: reqResArg.ans, res: reqResArg.res })
- } else {
- done.reject(new Error('some error'))
- }
- })
- return done.promise
- }
-
- /**
- * authorizeDomain
- * @description authorize domain using challenge-response-method
- * @param {string} domain
- * @param {function} callback - first argument will be the answer object
- */
- authorizeDomain(domain) {
- let done = q.defer()
- this.getProfile()
- .then(profile => {
- if (!(profile instanceof Object)) {
- done.reject(new Error('no profile returned'))
- } else {
- this.jWebClient.post(this.directory['new-authz'], this.makeDomainAuthorizationRequest(domain))
- .then((reqResArg: IReqResArg) => {
- if ((reqResArg.res instanceof Object) && (reqResArg.res['statusCode'] === 403)) { // if unauthorized
- this.agreeTos(this.tosLink)
- .then((reqResArg2: IReqResArg) => { // agree to TOS
- if ( // if TOS were agreed successfully
- (reqResArg.res instanceof Object)
- && (reqResArg2.res['statusCode'] >= 200)
- && (reqResArg2.res['statusCode'] <= 400)
- ) {
- this.authorizeDomain(domain).then(() => {
- done.resolve()
- }) // try authorization again
- } else {
- done.reject(false) // agreement failed
- }
- })
- } else {
- if (
- (reqResArg.res instanceof Object)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')
- && (reqResArg.ans instanceof Object)
- ) {
- let poll_uri = reqResArg.res.headers['location'] // status URI for polling
- let challenge = this.selectChallenge(reqResArg.ans, 'http-01') // select simple http challenge
- if (challenge instanceof Object) { // desired challenge is in list
- this.prepareChallenge(domain, challenge, () => { // prepare all objects and files for challenge
- // reset
- reqResArg.ans = null
- reqResArg.res = null
- // accept challenge
- this.acceptChallenge(challenge)
- .then((reqResArg2: IReqResArg) => {
- if (
- (reqResArg2.res instanceof Object)
- && (reqResArg2.res['statusCode'] < 400) // server confirms challenge acceptance
- ) {
- this.pollUntilValid(poll_uri)
- .then(() => {
- done.resolve()
- }) // poll status until server states success
- } else {
- done.reject(false) // server did not confirm challenge acceptance
- }
- })
- })
- } else {
- done.reject(false) // desired challenge is not in list
- }
- } else {
- done.reject(false) // server did not respond with status URI
- }
- }
- })
- }
- })
- return done.promise
- }
-
- /**
- * acceptChallenge
- * @description tell server which challenge will be accepted
- * @param {Object} challenge
- * @param {function} callback - first argument will be the answer object
- */
- acceptChallenge(challenge = {}) {
- let done = q.defer()
- this.jWebClient.post(challenge['uri'], this.makeChallengeResponse(challenge))
- .then(() => {
- done.resolve()
- })
- return done.promise
- }
-
- /**
- * pollUntilValid
- * @description periodically (with exponential back-off) check status of challenge
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilValid(uri, retry = 1) {
- let done = q.defer()
- if (retry > 128) {
- done.reject(false) // stop if retry value exceeds maximum
- } else {
- this.jWebClient.get(uri)
- .then((reqResArg) => {
- if (!(reqResArg.ans instanceof Object)) {
- done.reject(false) // invalid answer
- } else {
- if (reqResArg.ans['status'] === 'pending') { // still pending
- setTimeout(() => {
- this.pollUntilValid(uri, retry * 2) // retry
- }, retry * 500)
- } else {
- done.resolve() // challenge complete
- }
- }
- })
- }
- return done.promise
- }
-
- /**
- * pollUntilIssued
- * @description periodically (with exponential back-off) check status of CSR
- * @param {string} uri
- * @param {function} callback - first argument will be the answer object
- * @param {number} retry - factor of delay
- */
- pollUntilIssued(uri, retry = 1) {
- let done = q.defer()
- if (retry > 128) {
- done.reject(false) // stop if retry value exceeds maximum
- } else {
- this.jWebClient.get(uri)
- .then((reqResArg: IReqResArg) => {
- if ((reqResArg.ans instanceof Buffer) && (reqResArg.ans.length > 0)) {
- done.resolve(reqResArg.ans) // certificate was returned with answer
- } else {
- if ((reqResArg.res instanceof Object) && (reqResArg.res['statusCode'] < 400)) { // still pending
- setTimeout(() => {
- this.pollUntilIssued(uri, retry * 2) // retry
- }, retry * 500)
- } else {
- done.reject(false) // CSR complete
- }
- }
- })
- }
- return done.promise
- }
-
- /**
- * requestSigning
- * @description send CSR
- * @param {string} domain - expected to be already sanitized
- * @param {function} callback - first argument will be the answer object
- */
- requestSigning(commonName) {
- let done = q.defer()
- fs.readFile(commonName + '.csr', (err, csrBuffer: Buffer) => {
- if (err instanceof Object) { // file system error
- if (this.jWebClient.verbose) {
- console.error('Error : File system error', err['code'], 'while reading key from file')
- }
- done.reject(false)
- } else {
- let csr = csrBuffer.toString()
- this.jWebClient.post(this.directory['new-cert'], this.makeCertRequest(csr, this.daysValid))
- .then((reqResArg: IReqResArg) => {
- if ((reqResArg.ans instanceof Buffer) && (reqResArg.ans.length > 0)) { // answer is buffer
- done.resolve(reqResArg.ans) // certificate was returned with answer
- } else {
- if (reqResArg.res instanceof Object) {
- if ((reqResArg.res['statusCode'] < 400) && !reqResArg.ans) { // success response, but no answer was provided
- let headers = reqResArg['headers']
- if (!(headers instanceof Object)) {
- headers = {} // ensure headers is object
- }
- this.pollUntilIssued(headers['location'])
- .then(x => { done.resolve(x) })
- } else {
- done.resolve((reqResArg.res['statusCode'] < 400) ? reqResArg.ans : false) // answer may be provided as string or object
- }
- } else {
- done.reject(false) // invalid response
- }
- }
- })
- }
- })
- return done.promise
- }
-
- /**
- * retrieves profile of user (will make directory lookup and registration check)
- * @param {function} callback - first argument will be the answer object
- */
- getProfile() {
- let done = q.defer()
- this.getDirectory()
- .then((dir) => {
- if (!(dir instanceof Object)) {
- done.reject(new Error('server did not respond with directory'))
- } else {
- this.directory = dir // cache directory
- this.newRegistration(null)
- .then((reqResArg: IReqResArg) => { // try new registration to get registration link
- if (
- (reqResArg.res instanceof Object)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')
- ) {
- this.regLink = reqResArg.res.headers['location']
- this.getRegistration(this.regLink, null)
- .then((reqResArg: IReqResArg) => {
- done.resolve()
- }) // get registration info from link
- } else {
- done.reject(new Error('registration failed'))
- }
- })
- }
- })
- return done.promise
- }
-
- /**
- * createAccount
- * @description create new account (assumes directory lookup has already occured)
- * @param {string} email
- * @param {function} callback - first argument will be the registration URI
- */
- createAccount(email: string) {
- let done = q.defer()
- if (typeof email === 'string') {
- this.newRegistration({
- contact: [
- 'mailto:' + email
- ]
- })
- .then((reqResArg: IReqResArg) => {
- if (
- (reqResArg.res instanceof Object)
- && (reqResArg.res['statusCode'] === 201)
- && (reqResArg.res['headers'] instanceof Object)
- && (typeof reqResArg.res.headers['location'] === 'string')
- ) {
- this.regLink = reqResArg.res.headers['location']
- done.resolve(this.regLink) // registration URI
- } else {
- done.reject(new Error('could not register new account')) // registration failed
- }
- })
-
- } else {
- done.reject(new Error('no email address provided'))
- }
- return done.promise
- }
-
- /**
- * agreeTos
- * @description agree with terms of service (update agreement status in profile)
- * @param {string} tosLink
- * @param {function} callback - first argument will be the answer object
- */
- agreeTos(tosLink) {
- let done = q.defer()
- this.getRegistration(this.regLink, {
- 'Agreement': tosLink // terms of service URI
- }).then(() => {
- done.resolve()
- })
- return done.promise
- }
-
- /**
- * Entry-Point: Request certificate
- */
- requestCertificate(domainArg: string, organizationArg: string, countryCodeArg: string) {
- let done = q.defer()
- this.getProfile()
- .then((profile) => {
- let email = this.extractEmail(profile) // try to determine email address from profile
- countryCodeArg = this.makeSafeFileName(countryCodeArg)
- domainArg = this.makeSafeFileName(domainArg)
- email = this.makeSafeFileName(email)
- organizationArg = this.makeSafeFileName(organizationArg)
- // create key pair
- this.createKeyPair({
- keyBitSize: 4096,
- countryCode: countryCodeArg,
- organization: organizationArg,
- commonName: domainArg,
- emailAddress: email
- })
- .then(() => {
- this.requestSigning(domainArg)
- .then((cert) => { // send CSR
- if ((cert instanceof Buffer) || (typeof cert === 'string')) { // valid certificate data
- fs.writeFile(domainArg + '.der', cert, (err) => { // sanitize domain name for file path
- if (err instanceof Object) { // file system error
- if (this.jWebClient.verbose) {
- console.error('Error : File system error', err['code'], 'while writing certificate to file')
- }
- done.reject(err)
- } else {
- done.resolve() // CSR complete and certificate written to file system
- }
- })
- } else {
- done.reject('invalid certificate data')
- }
- })
-
- })
- })
- return done.promise
- }
-
- /**
- * External: Create key pair
- * @param {number} bit - key strength, expected to be already sanitized
- * @param {string} c - country code, expected to be already sanitized
- * @param {string} o - organization, expected to be already sanitized
- * @param {string} cn - common name (domain name), expected to be already sanitized
- * @param {string} e - email address, expected to be already sanitized
- * @param {function} callback
- */
- createKeyPair(optionsArg: {
- keyBitSize: number,
- countryCode: string,
- organization: string,
- commonName: string,
- emailAddress: string
- }) {
- let done = q.defer()
- let openssl = `openssl req -new -nodes -newkey rsa:${optionsArg.keyBitSize} `
- + `-sha256 `
- + `-subj "/C=${optionsArg.countryCode}/O=${optionsArg.organization}/CN=${optionsArg.commonName}/emailAddress=${optionsArg.emailAddress}" `
- + `-keyout \"${optionsArg.commonName}.key\" -outform der -out \"${optionsArg.commonName}.csr\"`
- console.error('Action : Creating key pair')
- if (this.jWebClient.verbose) {
- console.error('Running:', openssl)
- }
- plugins.shelljs.exec(openssl, (codeArg, stdOutArg, stdErrorArg) => {
- if (!stdErrorArg) {
- done.resolve()
- } else {
- done.reject(stdErrorArg)
- }
- })
- return done.promise
- }
-
- /**
- * Helper: Empty callback
- */
- emptyCallback() {
- // nop
- }
-
- /**
- * Helper: Make safe file name or path from string
- * @param {string} name
- * @param {boolean} withPath - optional, default false
- * @return {string}
- */
- makeSafeFileName(name, withPath = false) {
- if (typeof name !== 'string') {
- name = ''
- }
- // respects file name restrictions for ntfs and ext2
- let regexFile = '[<>:\"/\\\\\\|\\?\\*\\u0000-\\u001f\\u007f\\u0080-\\u009f]'
- let regexPath = '[<>:\"\\\\\\|\\?\\*\\u0000-\\u001f\\u007f\\u0080-\\u009f]'
- return name.replace(new RegExp(withPath ? regexPath : regexFile, 'g'), (charToReplace) => {
- if (typeof charToReplace === 'string') {
- return '%' + charToReplace.charCodeAt(0).toString(16).toLocaleUpperCase()
- }
- return '%00'
- })
- }
-
- /**
- * Helper: Prepare challenge
- * @param {string} domain
- * @param {Object} challenge
- * @param {function} callback
- */
- prepareChallenge(domain, challenge, callback) {
- /*jshint -W069, unused:false*/
- if (typeof callback !== 'function') {
- callback = this.emptyCallback // ensure callback is function
- }
- if (challenge instanceof Object) {
- if (challenge['type'] === 'http-01') { // simple http challenge
- let path = this.webroot + this.wellKnownPath + challenge['token'] // webroot and well_known_path are expected to be already sanitized
- fs.writeFile(path, this.makeKeyAuthorization(challenge), (err) => { // create challenge file
- if (err instanceof Object) { // file system error
- if (this.jWebClient.verbose) {
- console.error(
- 'Error : File system error',
- err['code'], 'while writing challenge data to file'
- )
- }
- callback()
- } else {
- // let uri = "http://" + domain + this.well_known_path + challenge["token"]
- let rl = readline.createInterface(process.stdin, process.stdout)
- if (this.withInteraction) {
- rl.question('Press enter to proceed', (answer) => { // wait for user to proceed
- rl.close()
- callback()
- })
- } else {
- rl.close()
- callback() // skip interaction prompt if desired
- }
- }
- })
- } else { // no supported challenge
- console.error('Error : Challenge not supported')
- callback()
- }
- } else { // invalid challenge response
- console.error('Error : Invalid challenge response')
- callback()
- }
- }
-
- /**
- * Helper: Extract TOS Link, e.g. from "<http://...>;rel="terms-of-service"
- * @param {string} linkStr
- * @return {string}
- */
- getTosLink(linkStr) {
- let match = /(<)([^>]+)(>;rel="terms-of-service")/g.exec(linkStr)
- if ((match instanceof Array) && (match.length > 2)) {
- let result = match[2]
- // dereference
- match = null
- return result
- }
- }
-
- /**
- * Helper: Select challenge by type
- * @param {Object} ans
- * @param {string} challenge_type
- * @return {Object}
- */
- selectChallenge(ans, challengeType: string) {
- /*jshint -W069 */
- if ((ans instanceof Object) && (ans['challenges'] instanceof Array)) {
- return ans.challenges.filter((entry) => {
- let type = entry['type']
- // dereference
- entry = null
- if (type === challengeType) { // check for type match
- return true
- }
- return false
- }).pop()
- } // return first match or undefined
- // dereference
- ans = null
- return void 0 // challenges not available or in expected format
- }
-
- /**
- * Helper: Extract first found email from profile (without mailto prefix)
- * @param {Object} profile
- * @return {string}
- */
- extractEmail(profile) {
- let prefix = 'mailto:'
- let email = profile.contact.filter((entry) => {
- if (typeof entry !== 'string') {
- return false
- } else {
- return !entry.indexOf(prefix) // check for mail prefix
- }
- }
- ).pop()
- // dereference
- profile = null
- if (typeof email !== 'string') {
- return void 0
- } // return default
- return email.substr(prefix.length) // only return email address without protocol prefix
- }
-
- /**
- * Make ACME-Request: Domain-Authorization Request - Object: resource, identifier
- * @param {string} domain
- * @return {{resource: string, identifier: Object}}
- */
- makeDomainAuthorizationRequest(domain) {
- return {
- 'resource': 'new-authz',
- 'identifier': {
- 'type': 'dns',
- 'value': domain
- }
- }
- }
-
- /**
- * Make ACME-Object: Key-Authorization (encoded) - String: Challenge-Token . Encoded-Account-Key-Hash
- * @param {Object} challenge
- * @return {string}
- */
- makeKeyAuthorization(challenge) {
- /*jshint -W069 */
- if (challenge instanceof Object) {
- if (this.clientProfilePubKey instanceof Object) {
- let jwk = json_to_utf8buffer({
- e: this.clientProfilePubKey['e'],
- kty: this.clientProfilePubKey['kty'],
- n: this.clientProfilePubKey['n']
- }
- )
- let hash = crypto.createHash('sha256').update(jwk.toString('utf8'), 'utf8').digest()
- // create base64 encoded hash of account key
- let ACCOUNT_KEY = plugins.smartstring.base64.encodeUri(hash.toString())
- let token = challenge['token']
- return token + '.' + ACCOUNT_KEY
- }
- } else {
- return '' // return default (for writing to file)
- }
- }
-
- /**
- * Make ACME-Request: Challenge-Response - Object: resource, keyAuthorization
- * @param {Object} challenge
- * @return {{resource: string, keyAuthorization: string}}
- */
- makeChallengeResponse(challenge) {
- return {
- 'resource': 'challenge',
- 'keyAuthorization': this.makeKeyAuthorization(challenge)
- }
- }
-
- /**
- * Make ACME-Request: CSR - Object: resource, csr, notBefore, notAfter
- * @param {string} csr
- * @param {number} days_valid
- * @return {{resource: string, csr: string, notBefore: string, notAfter: string}}
- */
- makeCertRequest(csr: string, DAYS_VALID: number) {
- if (typeof csr !== 'string' && !(csr instanceof Buffer)) {
- csr = '' // default string for CSR
- }
- if ((typeof DAYS_VALID !== 'number') || (isNaN(DAYS_VALID)) || (DAYS_VALID === 0)) {
- DAYS_VALID = 1 // default validity duration (1 day)
- }
- let DOMAIN_CSR_DER = plugins.smartstring.base64.encodeUri(csr) // create base64 encoded CSR
- let CURRENT_DATE = (new Date()).toISOString() // set start date to current date
-
- // set end date to current date + days_valid
- let NOTAFTER_DATE = (new Date((+new Date()) + 1000 * 60 * 60 * 24 * Math.abs(DAYS_VALID))).toISOString()
- return {
- 'resource': 'new-cert',
- 'csr': DOMAIN_CSR_DER,
- 'notBefore': CURRENT_DATE,
- 'notAfter': NOTAFTER_DATE
- }
- }
-}
diff --git a/ts/smartacme.classes.jwebclient.ts b/ts/smartacme.classes.jwebclient.ts
deleted file mode 100644
index 610951e..0000000
--- a/ts/smartacme.classes.jwebclient.ts
+++ /dev/null
@@ -1,246 +0,0 @@
-import * as plugins from './smartacme.plugins'
-import * as https from 'https'
-let jwa = require('jwa')
-import * as url from 'url'
-import * as q from 'q'
-
-export interface IReqResArg {
- ans: any
- res: any
-}
-
-/**
- * json_to_utf8base64url
- * @private
- * @description convert JSON to base64-url encoded string using UTF-8 encoding
- * @param {Object} obj
- * @return {string}
- * @throws Exception if object cannot be stringified or contains cycle
- */
-let json_to_utf8base64url = (obj) => {
- return plugins.smartstring.base64.encodeUri(JSON.stringify(obj))
-}
-
-/**
- * @class JWebClient
- * @constructor
- * @description Implementation of HTTPS-based JSON-Web-Client
- */
-export class JWebClient {
- /**
- * User account key pair
- */
- keyPair: any = {}
-
- /**
- * Cached nonce returned with last request
- */
- lastNonce: string = null
-
- /**
- * @member {boolean} module:JWebClient~JWebClient#verbose
- * @desc Determines verbose mode
- */
- verbose: boolean
- constructor() {
- this.verbose = false
- }
-
- /**
- * createJWT
- * @description create JSON-Web-Token signed object
- * @param {string|undefined} nonce
- * @param {Object|string|number|boolean} payload
- * @param {string} alg
- * @param {Object|string} key
- * @param {Object} jwk
- * @return {string}
- */
- createJWT(nonce, payload, alg, key, jwk) {
- /*jshint -W069 */
- // prepare key
- if (key instanceof Object) {
- key = new Buffer(plugins.smartstring.base64.decode(key['k']))
- }
- // prepare header
- let header = {
- typ: 'JWT',
- alg: alg,
- jwk: jwk,
- nonce: null
- }
-
- if (nonce !== void 0) {
- header.nonce = nonce
- }
- // concatenate header and payload
- let input = [
- json_to_utf8base64url(header),
- json_to_utf8base64url(payload)
- ].join('.')
- // sign input
- let hmac = jwa(alg)
- let sig = hmac.sign(input, key)
- // concatenate input and signature
- let output = [
- input,
- sig
- ].join('.')
- // output
- return output
- }
-
- /**
- * request
- * @description make GET or POST request over HTTPS and use JOSE as payload type
- * @param {string} query
- * @param {string} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- request(query: string, payload: string = null) {
- let done = q.defer()
- // prepare options
- let uri = url.parse(query)
- let options = {
- hostname: uri.hostname,
- port: parseInt(uri.port, 10),
- path: uri.path,
- method: null,
- headers: {}
- }
- if (!payload === null) {
- options.method = 'POST'
- options.headers = {
- 'Content-Type': 'application/jose',
- 'Content-Length': payload.length
- }
- } else {
- options.method = 'GET'
- }
- // prepare request
- let req = https.request(options, (res) => {
- // receive data
- let data = []
- res.on('data', (block) => {
- if (block instanceof Buffer) {
- data.push(block)
- }
- })
- res.on('end', () => {
- let buf = Buffer.concat(data)
- let isJSON = (
- (res instanceof Object)
- && (res['headers'] instanceof Object)
- && (typeof res.headers['content-type'] === 'string')
- && (res.headers['content-type'].indexOf('json') > -1)
- )
- if (isJSON && buf.length > 0) {
- try {
- // convert to JSON
- let json = JSON.parse(buf.toString('utf8'))
- done.resolve({ json: json, res: res })
- } catch (e) {
- // error (if empty or invalid JSON)
- done.reject(e)
- }
- }
- })
- }).on('error', (e) => {
- console.error('Error occured', e)
- // error
- done.reject(e)
- })
- // write POST body if payload was specified
- if (!payload === null) {
- req.write(payload)
- }
- // make request
- req.end()
- return done.promise
- }
-
- /**
- * get
- * @description make GET request
- * @param {string} uri
- * @param {function} callback
- * @param {function} errorCallback
- */
- get(uri: string) {
- let done = q.defer()
- this.request(uri)
- .then((reqResArg: IReqResArg) => {
- this.evaluateStatus(uri, null, reqResArg.ans, reqResArg.res)
- // save replay nonce for later requests
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- this.lastNonce = reqResArg.res.headers['replay-nonce']
- }
- done.resolve(reqResArg)
- })
- return done.promise
- }
-
- /**
- * make POST request
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {function} callback
- * @param {function} errorCallback
- */
- post(uri: string, payload) {
- let done = q.defer()
- let jwt = this.createJWT(
- this.lastNonce,
- payload,
- 'RS256',
- this.keyPair['private_pem'],
- this.keyPair['public_jwk'])
- this.request(uri, jwt)
- .then((reqResArg: IReqResArg) => {
- this.evaluateStatus(uri, payload, reqResArg.ans, reqResArg.res)
- // save replay nonce for later requests
- if ((reqResArg.res instanceof Object) && (reqResArg.res['headers'] instanceof Object)) {
- this.lastNonce = reqResArg.res.headers['replay-nonce']
- }
- done.resolve(reqResArg)
- })
- return done.promise
- }
-
- /**
- * checks if status is expected and log errors
- * @param {string} uri
- * @param {Object|string|number|boolean} payload
- * @param {Object|string} ans
- * @param {Object} res
- */
- evaluateStatus(uri, payload, ans, res) {
- if (this.verbose) {
- if (
- (payload instanceof Object)
- || (typeof payload === 'string')
- || (typeof payload === 'number')
- || (typeof payload === 'boolean')
- ) {
- console.error('Send :', payload) // what has been sent
- }
- }
- let uri_parsed = url.parse(uri)
- if (res['statusCode'] >= 100 && res['statusCode'] < 400) {
- console.error('HTTP :', res['statusCode'], uri_parsed.path) // response code if successful
- }
- if (res['statusCode'] >= 400 && res['statusCode'] < 500) {
- console.error('HTTP :', res['statusCode'], uri_parsed.path) // response code if error
- if (ans instanceof Object) {
- if (typeof ans['detail'] === 'string') {
- console.error('Message:', ans.detail.split(' :: ').pop()) // error message if any
- }
- }
- }
- if (this.verbose) {
- console.error('Receive:', res['headers']) // received headers
- console.error('Receive:', ans) // received data
- }
- }
-}
diff --git a/ts/smartacme.classes.smartacme.ts b/ts/smartacme.classes.smartacme.ts
index 3b23c14..972373c 100644
--- a/ts/smartacme.classes.smartacme.ts
+++ b/ts/smartacme.classes.smartacme.ts
@@ -1,30 +1,114 @@
-import * as plugins from './smartacme.plugins'
-import * as acmeclient from './smartacme.classes.acmeclient'
+import 'typings-global'
+import * as q from 'q'
+import * as path from 'path'
+import * as smartfile from 'smartfile'
+import * as smartstring from 'smartstring'
+import * as paths from './smartacme.paths'
+let ACME = require('le-acme-core').ACME.create()
+let RSA = require('rsa-compat').RSA
+
+let bitlen = 1024
+let exp = 65537
+let options = {
+ public: true,
+ pem: true,
+ internal: true
+}
+/**
+ * class SmartAcme exports methods for maintaining SSL Certificates
+ */
export class SmartAcme {
- acmeAccount: AcmeAccount
- acmeClient: acmeclient.AcmeClient
- constructor(directoryUrlArg: string = 'https://acme-staging.api.letsencrypt.org/directory') {
- this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg)
+ preparedBool: boolean = false
+ acmeUrls: any
+ productionBool: boolean
+ keyPair: any
+ constructor(productionArg: boolean = false) {
+ this.productionBool = productionArg
}
/**
- * creates an account
+ * prepares the SmartAcme class
+ */
+ prepareAcme() {
+ let done = q.defer()
+ if (this.preparedBool === false) {
+ this.getAcmeUrls()
+ .then(() => {
+ return this.createKeyPair()
+ })
+ .then((x) => {
+ console.log('prepared smartacme instance')
+ done.resolve()
+ })
+ } else {
+ done.resolve()
+ }
+ return done.promise
+ }
+
+ /**
+ * creates an account if not currently present in module
*/
createAccount() {
- this.acmeClient.createAccount('test@bleu.de').then((answer) => {
- console.log(answer)
- }).catch(err => { console.log(err) })
+ let done = q.defer()
+ this.prepareAcme()
+ .then(() => {
+ return this.createKeyPair()
+ })
+ .then(() => {
+ let options = {
+ newRegUrl: this.acmeUrls.newReg,
+ email: 'domains@lossless.org', // valid email (server checks MX records)
+ accountKeypair: { // privateKeyPem or privateKeyJwt
+ privateKeyPem: this.keyPair
+ },
+ agreeToTerms: function (tosUrl, done) {
+ done(null, tosUrl)
+ }
+ }
+ ACME.registerNewAccount(options, (err, regr) => {
+ if(err) {
+ console.log(err)
+ done.reject(err)
+ }
+ done.resolve(regr)
+ }) // returns "regr" registration data
+ }).catch(err => { console.log(err) })
+
+ return done.promise
}
/**
- * returns the openssl key pair for
+ * creates a keyPair
*/
- getKeyPair() {
- return this.acmeClient.getKeyPair()
+ createKeyPair() {
+ let done = q.defer()
+ RSA.generateKeypair(bitlen, exp, options, (err, keypair) => {
+ if (err) {
+ console.log(err)
+ done.reject(err)
+ }
+ console.log(keypair)
+ this.keyPair = keypair
+ })
+ done.resolve()
+ return done.promise
+ }
+
+ /**
+ * gets the Acme Urls
+ */
+ getAcmeUrls() {
+ let done = q.defer()
+ ACME.getAcmeUrls(ACME.stagingServerUrl, (err, urls) => {
+ if (err) {
+ throw err
+ }
+ this.acmeUrls = urls
+ console.log(this.acmeUrls)
+ done.resolve()
+ })
+ return done.promise
}
}
-
-export class AcmeAccount {
-
-}
diff --git a/ts/smartacme.paths.ts b/ts/smartacme.paths.ts
index c675e44..91bfc39 100644
--- a/ts/smartacme.paths.ts
+++ b/ts/smartacme.paths.ts
@@ -1,5 +1,6 @@
-import * as plugins from './smartacme.plugins'
-
-export let packageDir = plugins.path.join(__dirname,'../')
-export let assetDir = plugins.path.join(packageDir,'assets/')
+import * as path from 'path'
+import * as smartfile from 'smartfile'
+export let packageDir = path.join(__dirname,'../')
+export let assetDir = path.join(packageDir,'assets/')
+smartfile.fs.ensureDirSync(assetDir)
diff --git a/ts/smartacme.plugins.ts b/ts/smartacme.plugins.ts
deleted file mode 100644
index 70dc773..0000000
--- a/ts/smartacme.plugins.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import 'typings-global'
-import * as path from 'path'
-import * as smartfile from 'smartfile'
-import * as smartstring from 'smartstring'
-import * as shelljs from 'shelljs'
-
-export {
- path,
- smartfile,
- smartstring,
- shelljs
-}