improve upon keyCreation

This commit is contained in:
Philipp Kunz 2016-11-07 18:41:52 +01:00
parent e1f24823c6
commit 11f12d7c0e
16 changed files with 116 additions and 87 deletions

View File

@ -11,7 +11,6 @@ import { IReqResArg } from './smartacme.classes.jwebclient';
export declare class AcmeClient { export declare class AcmeClient {
clientProfilePubKey: any; clientProfilePubKey: any;
daysValid: number; daysValid: number;
defaultRsaKeySize: number;
directory: any; directory: any;
directoryUrl: string; directoryUrl: string;
emailDefaultPrefix: string; emailDefaultPrefix: string;
@ -80,7 +79,7 @@ export declare class AcmeClient {
* @param {string} domain - expected to be already sanitized * @param {string} domain - expected to be already sanitized
* @param {function} callback - first argument will be the answer object * @param {function} callback - first argument will be the answer object
*/ */
requestSigning(domain: any, callback: any): void; requestSigning(commonName: any, callback: any): q.Promise<{}>;
/** /**
* retrieves profile of user (will make directory lookup and registration check) * retrieves profile of user (will make directory lookup and registration check)
* @param {function} callback - first argument will be the answer object * @param {function} callback - first argument will be the answer object
@ -102,13 +101,8 @@ export declare class AcmeClient {
agreeTos(tosLink: any, callback: any): void; agreeTos(tosLink: any, callback: any): void;
/** /**
* Entry-Point: Request certificate * Entry-Point: Request certificate
* @param {string} domain
* @param {string} organization
* @param {string} country
* @param {function} callback
* @returns Promise
*/ */
requestCertificate(domain: string, organization: string, country: string): q.Promise<{}>; requestCertificate(domainArg: string, organizationArg: string, countryCodeArg: string): q.Promise<{}>;
/** /**
* External: Create key pair * External: Create key pair
* @param {number} bit - key strength, expected to be already sanitized * @param {number} bit - key strength, expected to be already sanitized
@ -118,7 +112,13 @@ export declare class AcmeClient {
* @param {string} e - email address, expected to be already sanitized * @param {string} e - email address, expected to be already sanitized
* @param {function} callback * @param {function} callback
*/ */
createKeyPair(bit: any, c: any, o: any, cn: any, e: any): q.Promise<{}>; createKeyPair(optionsArg: {
keyBitSize: number;
countryCode: string;
organization: string;
commonName: string;
emailAddress: string;
}): q.Promise<{}>;
/** /**
* Helper: Empty callback * Helper: Empty callback
*/ */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,14 @@ export declare class SmartAcme {
acmeAccount: AcmeAccount; acmeAccount: AcmeAccount;
acmeClient: acmeclient.AcmeClient; acmeClient: acmeclient.AcmeClient;
constructor(directoryUrlArg?: string); constructor(directoryUrlArg?: string);
/**
* creates an account
*/
createAccount(): void; createAccount(): void;
/**
* returns the openssl key pair for
*/
getKeyPair(): any;
} }
export declare class AcmeAccount { export declare class AcmeAccount {
} }

View File

@ -4,14 +4,23 @@ class SmartAcme {
constructor(directoryUrlArg = 'https://acme-staging.api.letsencrypt.org/directory') { constructor(directoryUrlArg = 'https://acme-staging.api.letsencrypt.org/directory') {
this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg); this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg);
} }
/**
* creates an account
*/
createAccount() { createAccount() {
this.acmeClient.createAccount('test@bleu.de', (answer) => { this.acmeClient.createAccount('test@bleu.de', (answer) => {
console.log(answer); console.log(answer);
}); });
} }
/**
* returns the openssl key pair for
*/
getKeyPair() {
return this.acmeClient.getKeyPair();
}
} }
exports.SmartAcme = SmartAcme; exports.SmartAcme = SmartAcme;
class AcmeAccount { class AcmeAccount {
} }
exports.AcmeAccount = AcmeAccount; exports.AcmeAccount = AcmeAccount;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSw2REFBNEQ7QUFFNUQ7SUFHSSxZQUFZLGtCQUEwQixvREFBb0Q7UUFDdEYsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDaEUsQ0FBQztJQUVELGFBQWE7UUFDVCxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxjQUFjLEVBQUMsQ0FBQyxNQUFNO1lBQ2hELE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUE7UUFDdkIsQ0FBQyxDQUFDLENBQUE7SUFDTixDQUFDO0NBQ0o7QUFaRCw4QkFZQztBQUVEO0NBRUM7QUFGRCxrQ0FFQyJ9 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRhY21lLmNsYXNzZXMuc21hcnRhY21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSw2REFBNEQ7QUFFNUQ7SUFHSSxZQUFZLGtCQUEwQixvREFBb0Q7UUFDdEYsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLFVBQVUsQ0FBQyxVQUFVLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDaEUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsYUFBYTtRQUNULElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLGNBQWMsRUFBQyxDQUFDLE1BQU07WUFDaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUN2QixDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUM7SUFFRDs7T0FFRztJQUNILFVBQVU7UUFDTixNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLEVBQUUsQ0FBQTtJQUN2QyxDQUFDO0NBQ0o7QUF0QkQsOEJBc0JDO0FBRUQ7Q0FFQztBQUZELGtDQUVDIn0=

2
dist/smartacme.paths.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export declare let packageDir: string;
export declare let assetDir: string;

5
dist/smartacme.paths.js vendored Normal file
View File

@ -0,0 +1,5 @@
"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==

View File

@ -1,5 +1,6 @@
import 'typings-global'; import 'typings-global';
import * as path from 'path'; import * as path from 'path';
import * as smartfile from 'smartfile';
import * as smartstring from 'smartstring'; import * as smartstring from 'smartstring';
import * as shelljs from 'shelljs'; import * as shelljs from 'shelljs';
export { path, smartstring, shelljs }; export { path, smartfile, smartstring, shelljs };

View File

@ -2,8 +2,10 @@
require("typings-global"); require("typings-global");
const path = require("path"); const path = require("path");
exports.path = path; exports.path = path;
const smartfile = require("smartfile");
exports.smartfile = smartfile;
const smartstring = require("smartstring"); const smartstring = require("smartstring");
exports.smartstring = smartstring; exports.smartstring = smartstring;
const shelljs = require("shelljs"); const shelljs = require("shelljs");
exports.shelljs = shelljs; exports.shelljs = shelljs;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsMEJBQXVCO0FBQ3ZCLDZCQUE0QjtBQUt4QixvQkFBSTtBQUpSLDJDQUEwQztBQUt0QyxrQ0FBVztBQUpmLG1DQUFrQztBQUs5QiwwQkFBTyJ9 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhY21lLnBsdWdpbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydGFjbWUucGx1Z2lucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsMEJBQXVCO0FBQ3ZCLDZCQUE0QjtBQU14QixvQkFBSTtBQUxSLHVDQUFzQztBQU1sQyw4QkFBUztBQUxiLDJDQUEwQztBQU10QyxrQ0FBVztBQUxmLG1DQUFrQztBQU05QiwwQkFBTyJ9

View File

@ -28,6 +28,7 @@
"q": "^1.4.1", "q": "^1.4.1",
"rsa-pem-to-jwk": "^1.1.3", "rsa-pem-to-jwk": "^1.1.3",
"shelljs": "^0.7.5", "shelljs": "^0.7.5",
"smartfile": "^4.1.0",
"smartstring": "^2.0.20", "smartstring": "^2.0.20",
"typings-global": "^1.0.14" "typings-global": "^1.0.14"
}, },

View File

@ -9,8 +9,10 @@ describe('smartacme', function () {
testAcme = new smartacme.SmartAcme(); testAcme = new smartacme.SmartAcme();
should(testAcme).be.instanceOf(smartacme.SmartAcme); should(testAcme).be.instanceOf(smartacme.SmartAcme);
}); });
it('should have created keyPair', function () {
});
it('should register a new account', function () { it('should register a new account', function () {
testAcme.createAccount(); testAcme.createAccount();
}); });
}); });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUFxQjtBQUNyQixpQ0FBZ0M7QUFFaEMsNEJBQTRCO0FBQzVCLDJDQUEwQztBQUUxQyxRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ2xCLElBQUksUUFBNkIsQ0FBQTtJQUNqQyxFQUFFLENBQUMsZ0NBQWdDLEVBQUU7UUFDakMsUUFBUSxHQUFHLElBQUksU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFBO1FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUN2RCxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQywrQkFBK0IsRUFBRTtRQUNoQyxRQUFRLENBQUMsYUFBYSxFQUFFLENBQUE7SUFDNUIsQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUMsQ0FBQSJ9 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHdCQUFxQjtBQUNyQixpQ0FBZ0M7QUFFaEMsNEJBQTRCO0FBQzVCLDJDQUEwQztBQUUxQyxRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ2xCLElBQUksUUFBNkIsQ0FBQTtJQUNqQyxFQUFFLENBQUMsZ0NBQWdDLEVBQUU7UUFDakMsUUFBUSxHQUFHLElBQUksU0FBUyxDQUFDLFNBQVMsRUFBRSxDQUFBO1FBQ3BDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQTtJQUN2RCxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQyw2QkFBNkIsRUFBRTtJQUVsQyxDQUFDLENBQUMsQ0FBQTtJQUNGLEVBQUUsQ0FBQywrQkFBK0IsRUFBRTtRQUNoQyxRQUFRLENBQUMsYUFBYSxFQUFFLENBQUE7SUFDNUIsQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDLENBQUMsQ0FBQSJ9

View File

@ -9,6 +9,9 @@ describe('smartacme', function(){
it('should create a valid instance', function(){ it('should create a valid instance', function(){
testAcme = new smartacme.SmartAcme() testAcme = new smartacme.SmartAcme()
should(testAcme).be.instanceOf(smartacme.SmartAcme) should(testAcme).be.instanceOf(smartacme.SmartAcme)
})
it('should have created keyPair', function() {
}) })
it('should register a new account', function() { it('should register a new account', function() {
testAcme.createAccount() testAcme.createAccount()

View File

@ -28,7 +28,6 @@ let json_to_utf8buffer = (obj) => {
export class AcmeClient { export class AcmeClient {
clientProfilePubKey: any clientProfilePubKey: any
daysValid: number daysValid: number
defaultRsaKeySize: number
directory: any directory: any
directoryUrl: string directoryUrl: string
emailDefaultPrefix: string emailDefaultPrefix: string
@ -51,12 +50,7 @@ export class AcmeClient {
* @default 1 * @default 1
*/ */
this.daysValid = 1 this.daysValid = 1
/**
* @member {number} module:AcmeClient~AcmeClient#defaultRsaKeySize
* @desc Key strength in bits
* @default 4096
*/
this.defaultRsaKeySize = 4096
/** /**
* @member {Object} module:AcmeClient~AcmeClient#directory * @member {Object} module:AcmeClient~AcmeClient#directory
* @desc Hash map of REST URIs * @desc Hash map of REST URIs
@ -336,12 +330,9 @@ export class AcmeClient {
* @param {string} domain - expected to be already sanitized * @param {string} domain - expected to be already sanitized
* @param {function} callback - first argument will be the answer object * @param {function} callback - first argument will be the answer object
*/ */
requestSigning(domain, callback) { requestSigning(commonName, callback) {
/*jshint -W069 */ let done = q.defer()
if (typeof callback !== 'function') { fs.readFile(commonName + '.csr', (err, csrBuffer: Buffer) => {
callback = this.emptyCallback // ensure callback is function
}
fs.readFile(domain + '.csr', (err, csrBuffer: Buffer) => {
if (err instanceof Object) { // file system error if (err instanceof Object) { // file system error
if (this.jWebClient.verbose) { if (this.jWebClient.verbose) {
console.error('Error : File system error', err['code'], 'while reading key from file') console.error('Error : File system error', err['code'], 'while reading key from file')
@ -372,6 +363,7 @@ export class AcmeClient {
}) })
} }
}) })
return done.promise
} }
/** /**
@ -457,30 +449,28 @@ export class AcmeClient {
/** /**
* Entry-Point: Request certificate * Entry-Point: Request certificate
* @param {string} domain
* @param {string} organization
* @param {string} country
* @param {function} callback
* @returns Promise
*/ */
requestCertificate(domain: string, organization: string, country: string) { requestCertificate(domainArg: string, organizationArg: string, countryCodeArg: string) {
let done = q.defer() let done = q.defer()
this.getProfile() this.getProfile()
.then((profile) => { .then((profile) => {
let email = this.extractEmail(profile) // try to determine email address from profile let email = this.extractEmail(profile) // try to determine email address from profile
let bit = this.defaultRsaKeySize countryCodeArg = this.makeSafeFileName(countryCodeArg)
// sanitize domainArg = this.makeSafeFileName(domainArg)
bit = Number(bit)
country = this.makeSafeFileName(country)
domain = this.makeSafeFileName(domain)
email = this.makeSafeFileName(email) email = this.makeSafeFileName(email)
organization = this.makeSafeFileName(organization) organizationArg = this.makeSafeFileName(organizationArg)
// create key pair // create key pair
this.createKeyPair(bit, country, organization, domain, email) this.createKeyPair({
.then(() => { // create key pair keyBitSize: 4096,
this.requestSigning(domain, (cert) => { // send CSR countryCode: countryCodeArg,
organization: organizationArg,
commonName: domainArg,
emailAddress: email
})
.then(() => {
this.requestSigning(domainArg, (cert) => { // send CSR
if ((cert instanceof Buffer) || (typeof cert === 'string')) { // valid certificate data if ((cert instanceof Buffer) || (typeof cert === 'string')) { // valid certificate data
fs.writeFile(domain + '.der', cert, (err) => { // sanitize domain name for file path fs.writeFile(domainArg + '.der', cert, (err) => { // sanitize domain name for file path
if (err instanceof Object) { // file system error if (err instanceof Object) { // file system error
if (this.jWebClient.verbose) { if (this.jWebClient.verbose) {
console.error('Error : File system error', err['code'], 'while writing certificate to file') console.error('Error : File system error', err['code'], 'while writing certificate to file')
@ -509,9 +499,18 @@ export class AcmeClient {
* @param {string} e - email address, expected to be already sanitized * @param {string} e - email address, expected to be already sanitized
* @param {function} callback * @param {function} callback
*/ */
createKeyPair(bit, c, o, cn, e) { createKeyPair(optionsArg: {
keyBitSize: number,
countryCode: string,
organization: string,
commonName: string,
emailAddress: string
}) {
let done = q.defer() let done = q.defer()
let openssl = `openssl req -new -nodes -newkey rsa:${bit} -sha256 -subj "/C=${c}/O=${o}/CN=${cn}/emailAddress=${e}" -keyout \"${cn}.key\" -outform der -out \"${cn}.csr\"` 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') console.error('Action : Creating key pair')
if (this.jWebClient.verbose) { if (this.jWebClient.verbose) {
console.error('Running:', openssl) console.error('Running:', openssl)
@ -646,12 +645,6 @@ export class AcmeClient {
* @return {string} * @return {string}
*/ */
extractEmail(profile) { extractEmail(profile) {
/*jshint -W069 */
if (!(profile instanceof Object) || !(profile['contact'] instanceof Array)) {
// dereference
profile = null
return void 0 // invalid profile
}
let prefix = 'mailto:' let prefix = 'mailto:'
let email = profile.contact.filter((entry) => { let email = profile.contact.filter((entry) => {
if (typeof entry !== 'string') { if (typeof entry !== 'string') {

View File

@ -8,11 +8,21 @@ export class SmartAcme {
this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg) this.acmeClient = new acmeclient.AcmeClient(directoryUrlArg)
} }
/**
* creates an account
*/
createAccount() { createAccount() {
this.acmeClient.createAccount('test@bleu.de',(answer) => { this.acmeClient.createAccount('test@bleu.de',(answer) => {
console.log(answer) console.log(answer)
}) })
} }
/**
* returns the openssl key pair for
*/
getKeyPair() {
return this.acmeClient.getKeyPair()
}
} }
export class AcmeAccount { export class AcmeAccount {

5
ts/smartacme.paths.ts Normal file
View File

@ -0,0 +1,5 @@
import * as plugins from './smartacme.plugins'
export let packageDir = plugins.path.join(__dirname,'../')
export let assetDir = plugins.path.join(packageDir,'assets/')

View File

@ -1,10 +1,12 @@
import 'typings-global' import 'typings-global'
import * as path from 'path' import * as path from 'path'
import * as smartfile from 'smartfile'
import * as smartstring from 'smartstring' import * as smartstring from 'smartstring'
import * as shelljs from 'shelljs' import * as shelljs from 'shelljs'
export { export {
path, path,
smartfile,
smartstring, smartstring,
shelljs shelljs
} }