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 -}