From e0ecd528cbe3848c9e84e928c1b466f72a124b30 Mon Sep 17 00:00:00 2001 From: OttoMao Date: Mon, 2 May 2016 00:18:35 +0800 Subject: [PATCH] invoke node-forge to generator certs --- cert/gen-cer | 48 -------------------- cert/gen-cer.cmd | 27 ----------- cert/gen-rootCA | 17 ------- cert/gen-rootCA.cmd | 12 ----- lib/certGenerator.js | 85 +++++++++++++++++++++++++++++++++++ lib/certMgr.js | 102 ++++++++++++++++++++++++------------------ lib/httpsServerMgr.js | 2 + 7 files changed, 146 insertions(+), 147 deletions(-) delete mode 100755 cert/gen-cer delete mode 100644 cert/gen-cer.cmd delete mode 100755 cert/gen-rootCA delete mode 100644 cert/gen-rootCA.cmd create mode 100644 lib/certGenerator.js diff --git a/cert/gen-cer b/cert/gen-cer deleted file mode 100755 index 4deb0b4..0000000 --- a/cert/gen-cer +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -#Required -domain=$1 -outputPath=$2 -commonname=$domain - -#Change to your company details -country=ZH -state=Shanghai -locality=Shanghai -organization=a.com -organizationalunit=IT -email=a@b.com - -#Optional -password=a - -if [ -z "$domain" ] -then - echo "Argument not present." - echo "Useage $0 [domain] [outputPath]" - - exit 99 -fi - -echo "Generating key request for $outputPath$domain" - -#Generate a key -# openssl genrsa -out host.key 2048 -# openssl genrsa -des3 -out $outputPath$domain.key 2048 -noout -openssl genrsa -passout pass:$password -out $outputPath$domain.key 2048 - - -#Remove passphrase from the key. Comment the line out to keep the passphrase -echo "Removing passphrase from key" -openssl rsa -in $outputPath$domain.key -passin pass:$password -out $outputPath$domain.key - -#Create the request -echo "Creating CSR" -openssl req -sha256 -new -key $outputPath$domain.key -out $outputPath$domain.csr -passin pass:$password \ - -subj "/C=$country/ST=$state/L=$locality/O=$organization/OU=$organizationalunit/CN=$commonname/emailAddress=$email" - -#Generating a Self-Signed Certificate -openssl x509 -req -sha256 -days 3650 -in $outputPath$domain.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out $outputPath$domain.crt -# -signkey $outputPath$domain.key -#openssl x509 -req -in host.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out host.crt -days 365 -echo "Finished" diff --git a/cert/gen-cer.cmd b/cert/gen-cer.cmd deleted file mode 100644 index 722f1e8..0000000 --- a/cert/gen-cer.cmd +++ /dev/null @@ -1,27 +0,0 @@ -@echo off - -set domain=%1 -set outputPath=%2 -set commonname=%domain% - -set country=ZH -set state=Shanghai -set locality=Shanghai -set organization=a.com -set organizationalunit=IT -set email=a@b.com -set password=a - -echo Generating key request for %domain% - -openssl genrsa -passout pass:%password% -out %domain%.key 2048 - - -echo Removing passphrase from key -openssl rsa -in %domain%.key -passin pass:%password% -out %domain%.key - -echo Creating CSR -openssl req -sha256 -new -key %domain%.key -out %domain%.csr -passin pass:%password% -subj /C=%country%/ST=%state%/L=%locality%/O=%organization%/OU=%organizationalunit%/CN=%commonname%/emailAddress=%email% - -openssl x509 -req -sha256 -days 3650 -in %domain%.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial -out %domain%.crt -echo Finished diff --git a/cert/gen-rootCA b/cert/gen-rootCA deleted file mode 100755 index fda566b..0000000 --- a/cert/gen-rootCA +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -outputPath=$1 -cd $outputPath -openssl genrsa -out rootCA.key 2048 -openssl req -sha256 -x509 -new -nodes -key rootCA.key -days 36500 -out rootCA.crt \ - -subj "/C=CN/ST=SH/L=SH/O=AnyProxy/OU=Section/CN=Anyproxy SSL Proxying/emailAddress=AnyProxy@AnyProxy" -echo "=============" -echo "rootCA generated at :" -pwd -echo "=============" - -chmod 666 rootCA.* - -open . - -exit 0 \ No newline at end of file diff --git a/cert/gen-rootCA.cmd b/cert/gen-rootCA.cmd deleted file mode 100644 index 170adc0..0000000 --- a/cert/gen-rootCA.cmd +++ /dev/null @@ -1,12 +0,0 @@ -@echo off - -openssl genrsa -out rootCA.key 2048 -openssl req -x509 -new -nodes -key rootCA.key -days 3650 -out rootCA.crt -subj "/C=CN/ST=SH/L=SH/O=AnyProxy/OU=Section/CN=Anyproxy SSL Proxying/emailAddress=AnyProxy@AnyProxy" -echo ============= -echo rootCA generated at : -echo %cd% -echo ============= - -start . - -rem exit 0 diff --git a/lib/certGenerator.js b/lib/certGenerator.js new file mode 100644 index 0000000..4ceea3a --- /dev/null +++ b/lib/certGenerator.js @@ -0,0 +1,85 @@ +var forge = require('node-forge'); + +var defaultAttrs = [ + { name: 'countryName', value: 'CN' }, + { name: 'organizationName', value: 'AnyProxy' }, + { shortName: 'ST', value: 'SH' }, + { shortName: 'OU', value: 'AnyProxy SSL Proxy'} +]; + +function getKeysAndCert(){ + var keys = forge.pki.rsa.generateKeyPair(1024); + var cert = forge.pki.createCertificate(); + cert.publicKey = keys.publicKey; + cert.serialNumber = '01'; + cert.validity.notBefore = new Date(); + cert.validity.notAfter = new Date(); + cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10); // 10 years + return { + keys: keys, + cert: cert + }; +} + +function generateRootCA(){ + var keysAndCert = getKeysAndCert(); + keys = keysAndCert.keys; + cert = keysAndCert.cert; + + var attrs = defaultAttrs.concat([ + { + name: 'commonName', + value: 'AnyProxy' + } + ]); + cert.setSubject(attrs); + cert.setIssuer(attrs); + cert.setExtensions([ + { name: 'basicConstraints', cA: true }, + { name: 'keyUsage', keyCertSign: true, digitalSignature: true, nonRepudiation: true, keyEncipherment: true, dataEncipherment: true }, + { name: 'extKeyUsage', serverAuth: true, clientAuth: true, codeSigning: true, emailProtection: true, timeStamping: true }, + { name: 'nsCertType', client: true, server: true, email: true, objsign: true, sslCA: true, emailCA: true, objCA: true }, + { name: 'subjectAltName', altNames: [ { type: 6, /* URI */ value: 'http://example.org/webid#me' }, { type: 7, /* IP */ ip: '127.0.0.1' } ] }, + { name: 'subjectKeyIdentifier' } + ]); + + cert.sign(keys.privateKey, forge.md.sha256.create()); + + return { + privateKey: forge.pki.privateKeyToPem(keys.privateKey), + publicKey: forge.pki.publicKeyToPem(keys.publicKey), + certificate: forge.pki.certificateToPem(cert) + }; + + return pem; +} + +function generateCertsForHostname(domain, rootCAConfig){ + var keysAndCert = getKeysAndCert(); + keys = keysAndCert.keys; + cert = keysAndCert.cert; + + var caCert = forge.pki.certificateFromPem(rootCAConfig.cert) + var caKey = forge.pki.privateKeyFromPem(rootCAConfig.key) + + // issuer from CA + cert.setIssuer(caCert.subject.attributes) + + var attrs = defaultAttrs.concat([ + { + name: 'commonName', + value: domain + } + ]); + cert.setSubject(attrs); + cert.sign(caKey, forge.md.sha256.create()); + + return { + privateKey: forge.pki.privateKeyToPem(keys.privateKey), + publicKey: forge.pki.publicKeyToPem(keys.publicKey), + certificate: forge.pki.certificateToPem(cert) + }; +} + +module.exports.generateRootCA = generateRootCA; +module.exports.generateCertsForHostname = generateCertsForHostname; \ No newline at end of file diff --git a/lib/certMgr.js b/lib/certMgr.js index 267859e..50b12f1 100644 --- a/lib/certMgr.js +++ b/lib/certMgr.js @@ -1,19 +1,19 @@ var exec = require('child_process').exec, - spawn = require('child_process').spawn, - path = require("path"), - fs = require("fs"), - os = require("os"), - color = require('colorful'), - readline = require('readline'), - util = require('./util'), - logUtil = require("./log"), - asyncTask = require("async-task-mgr"); + spawn = require('child_process').spawn, + path = require("path"), + fs = require("fs"), + os = require("os"), + color = require('colorful'), + readline = require('readline'), + util = require('./util'), + logUtil = require("./log"), + certGenerator = require("./certGenerator"), + asyncTask = require("async-task-mgr"); -var isWin = /^win/.test(process.platform); +var isWin = /^win/.test(process.platform), certDir = path.join(util.getUserHome(),"/.anyproxy_certs/"), - cmdDir = path.join(__dirname,"..","./cert/"), - cmd_genRoot = isWin ? path.join(cmdDir,"./gen-rootCA.cmd") : path.join(cmdDir,"./gen-rootCA"), - cmd_genCert = isWin ? path.join(cmdDir,"./gen-cer.cmd") : path.join(cmdDir,"./gen-cer"), + rootCAcrtFilePath = path.join(certDir,"rootCA.crt"), + rootCAkeyFilePath = path.join(certDir,"rootCA.key"), createCertTaskMgr = new asyncTask(); if(!fs.existsSync(certDir)){ @@ -27,20 +27,31 @@ if(!fs.existsSync(certDir)){ } } +var cache_rootCACrtFileContent, cache_rootCAKeyFileContent; function getCertificate(hostname,certCallback){ - + checkRootCA(); var keyFile = path.join(certDir , "__hostname.key".replace(/__hostname/,hostname) ), crtFile = path.join(certDir , "__hostname.crt".replace(/__hostname/,hostname) ); + if(!cache_rootCACrtFileContent || !cache_rootCAKeyFileContent){ + cache_rootCACrtFileContent = fs.readFileSync(rootCAcrtFilePath, {encoding: 'utf8'}); + cache_rootCAKeyFileContent = fs.readFileSync(rootCAkeyFilePath, {encoding: 'utf8'}); + } + createCertTaskMgr.addTask(hostname,function(callback){ if(!fs.existsSync(keyFile) || !fs.existsSync(crtFile)){ - createCert(hostname,function(err){ - if(err){ - callback(err); - }else{ - callback(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile)); - } - }); + try{ + var result = certGenerator.generateCertsForHostname(hostname, { + cert: cache_rootCACrtFileContent, + key: cache_rootCAKeyFileContent + }); + fs.writeFileSync(keyFile, result.privateKey); + fs.writeFileSync(crtFile, result.certificate); + callback(null, result.privateKey, result.certificate); + + }catch(e){ + callback(e); + } }else{ callback(null , fs.readFileSync(keyFile) , fs.readFileSync(crtFile)); } @@ -66,37 +77,38 @@ function createCert(hostname,callback){ logUtil.printLog(color.yellow(color.bold("[internal https]")) + color.yellow(tipText)) ; callback(null); } - }); + }); } function clearCerts(cb){ if(isWin){ exec("del * /q",{cwd : certDir},cb); }else{ - exec("rm *.key *.csr *.crt",{cwd : certDir},cb); + exec("rm *.key *.csr *.crt *.srl",{cwd : certDir},cb); } } - function isRootCAFileExists(){ - var crtFile = path.join(certDir,"rootCA.crt"), - keyFile = path.join(certDir,"rootCA.key"); - - return (fs.existsSync(crtFile) && fs.existsSync(keyFile)); + return (fs.existsSync(rootCAcrtFilePath) && fs.existsSync(rootCAkeyFilePath)); } +var rootCAExists = false; function checkRootCA(){ + if(rootCAExists) return; if(!isRootCAFileExists()){ logUtil.printLog(color.red("can not find rootCA.crt or rootCA.key"), logUtil.T_ERR); logUtil.printLog(color.red("you may generate one by the following methods"), logUtil.T_ERR); logUtil.printLog(color.red("\twhen using globally : anyproxy --root"), logUtil.T_ERR); logUtil.printLog(color.red("\twhen using as a module : require(\"anyproxy\").generateRootCA();"), logUtil.T_ERR); logUtil.printLog(color.red("\tmore info : https://github.com/alibaba/anyproxy/wiki/How-to-config-https-proxy"), logUtil.T_ERR); - process.exit(0); + process.exit(0); + } else{ + rootCAExists = true; } } function generateRootCA(){ + if(isRootCAFileExists()){ logUtil.printLog(color.yellow("rootCA exists at " + certDir)); var rl = readline.createInterface({ @@ -122,29 +134,33 @@ function generateRootCA(){ //clear old certs clearCerts(function(){ logUtil.printLog(color.green("temp certs cleared")); + try{ + var result = certGenerator.generateRootCA(); + fs.writeFileSync(rootCAkeyFilePath, result.privateKey); + fs.writeFileSync(rootCAcrtFilePath, result.certificate); - var spawnSteam = spawn(cmd_genRoot,['.'],{cwd:certDir,stdio: 'inherit'}); + logUtil.printLog(color.green("rootCA generated")); + logUtil.printLog(color.green(color.bold("please trust the rootCA.crt in " + certDir))); + logUtil.printLog(color.green(color.bold("or you may get it via anyproxy webinterface"))); - spawnSteam.on('close', function (code) { - - if(code == 0){ - logUtil.printLog(color.green("rootCA generated")); - logUtil.printLog(color.green(color.bold("please trust the rootCA.crt in " + certDir))); - logUtil.printLog(color.green(color.bold("or you may get it via anyproxy webinterface"))); - }else{ - logUtil.printLog(color.red("fail to generate root CA"),logUtil.T_ERR); - } - process.exit(0); - }); + if(isWin){ + exec("start .",{cwd : certDir}); + }else{ + exec("open .",{cwd : certDir}); + } + }catch(e){ + logUtil.printLog(color.red(e)); + logUtil.printLog(color.red(e.stack)); + logUtil.printLog(color.red("fail to generate root CA"),logUtil.T_ERR); + } }); } } - function getRootCAFilePath(){ if(isRootCAFileExists()){ - return path.join(certDir,"rootCA.crt"); + return rootCAcrtFilePath; }else{ return ""; } diff --git a/lib/httpsServerMgr.js b/lib/httpsServerMgr.js index 3e7bcca..e016bcf 100644 --- a/lib/httpsServerMgr.js +++ b/lib/httpsServerMgr.js @@ -3,6 +3,7 @@ var getPort = require('./getPort'), async = require("async"), http = require('http'), https = require('https'), + Buffer = require('buffer').Buffer, fs = require('fs'), net = require('net'), tls = require('tls'), @@ -48,6 +49,7 @@ function SNIPrepareCert(serverName,SNICallback){ SNICallback(null,ctx); }else{ logUtil.printLog("err occurred when prepare certs for SNI - " + err, logUtil.T_ERR); + logUtil.printLog("err occurred when prepare certs for SNI - " + err.stack, logUtil.T_ERR); logUtil.printLog("you may upgrade your Node.js to >= v0.12", logUtil.T_ERR); } });