From 2edb8690868eba0fa7a79b028e8095d017614e40 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 12:40:37 -0700 Subject: [PATCH 01/15] Improve test.js. Use request instead of http so it works with either HTTP or HTTPS URLs. Write DER certificate rather than PEM certificate. I was getting some bytewise mismatches when checking the output PEM against a downloaded DER. --- test/js/package.json | 3 +- test/js/test.js | 209 ++++++++++++++++++------------------------- 2 files changed, 87 insertions(+), 125 deletions(-) diff --git a/test/js/package.json b/test/js/package.json index 0e499937b..a85289a7b 100644 --- a/test/js/package.json +++ b/test/js/package.json @@ -6,6 +6,7 @@ "dependencies": { "cli": "^0.6.5", "inquirer": "^0.8.2", - "node-forge": "^0.6.21" + "node-forge": "^0.6.21", + "request": "^2.55.0" } } diff --git a/test/js/test.js b/test/js/test.js index bc04cdc45..c60583062 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -9,6 +9,7 @@ var inquirer = require("inquirer"); var cli = require("cli"); var http = require('http'); var fs = require('fs'); +var request = require('request'); var url = require('url'); var util = require("./acme-util"); var crypto = require("./crypto-util"); @@ -32,7 +33,7 @@ var questions = { type: "confirm", name: "terms", message: "Do you agree to these terms?", - default: false, + default: true, }], domain: [{ @@ -64,7 +65,7 @@ var questions = { type: "input", name: "certFile", message: "Name for certificate file", - default: "cert.pem" + default: "cert.der" }], }; @@ -72,7 +73,7 @@ var state = { keyPairBits: 512, keyPair: null, - newRegistrationURL: "http://localhost:4000/acme/new-reg", + newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg", registrationURL: "", termsRequired: false, @@ -169,14 +170,12 @@ function register(answers) { var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage)); var payload = JSON.stringify(jws); - var options = url.parse(state.newRegistrationURL); - options.method = "POST"; - var req = http.request(options, getTerms); + var req = request.post(state.newRegistrationURL, {}, getTerms); req.write(payload) req.end(); } -function getTerms(resp) { +function getTerms(err, resp) { if (Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Registration request failed with code " + resp.statusCode); @@ -196,28 +195,20 @@ function getTerms(resp) { if (state.termsRequired) { state.termsURL = links["terms-of-service"]; console.log(state.termsURL); - http.get(state.termsURL, getAgreement) + request.get(state.termsURL, getAgreement) } else { inquirer.prompt(questions.domain, getChallenges); } } -function getAgreement(resp) { - var body = ""; - resp.on("data", function(chunk) { - body += chunk; - }); - resp.on("end", function(chunk) { - if (chunk) { body += chunk; } +function getAgreement(err, resp, body) { + // TODO: Check content-type + console.log("The CA requires your agreement to terms (not supported)."); + console.log(); + console.log(body); + console.log(); - // TODO: Check content-type - console.log("The CA requires your agreement to terms (not supported)."); - console.log(); - console.log(body); - console.log(); - - inquirer.prompt(questions.terms, sendAgreement); - }); + inquirer.prompt(questions.terms, sendAgreement); } function sendAgreement(answers) { @@ -234,20 +225,14 @@ function sendAgreement(answers) { var payload = JSON.stringify(jws); console.log("Posting agreement to: " + state.registrationURL) - var options = url.parse(state.registrationURL); - options.method = "POST"; - var req = http.request(options, function(resp) { - var body = ""; - resp.on("data", function(chunk) { body += chunk; }); - resp.on("end", function() { - if (Math.floor(resp.statusCode / 100) != 2) { - // Non-2XX response - console.log("Couldn't POST agreement back to server, aborting."); - console.log("Code: "+ resp.statusCode); - console.log(body); - process.exit(1); - } - }); + var req = request(state.registrationURL, {}, function(err, resp, body) { + if (Math.floor(resp.statusCode / 100) != 2) { + // Non-2XX response + console.log("Couldn't POST agreement back to server, aborting."); + console.log("Code: "+ resp.statusCode); + console.log(body); + process.exit(1); + } inquirer.prompt(questions.domain, getChallenges); }); @@ -268,14 +253,12 @@ function getChallenges(answers) { var jws = crypto.generateSignature(state.keyPair, new Buffer(authzMessage)); var payload = JSON.stringify(jws); - var options = url.parse(state.newAuthorizationURL); - options.method = "POST"; - var req = http.request(options, getReadyToValidate); + var req = request.post(state.newAuthorizationURL, {}, getReadyToValidate); req.write(payload) req.end(); } -function getReadyToValidate(resp) { +function getReadyToValidate(err, resp, body) { if (Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Authorization request failed with code " + resp.statusCode) @@ -291,42 +274,34 @@ function getReadyToValidate(resp) { state.authorizationURL = resp.headers["location"]; state.newCertificateURL = links["next"]; - var body = "" - resp.on('data', function(chunk) { - body += chunk; - }); - resp.on('end', function(chunk) { - if (chunk) { body += chunk; } + var authz = JSON.parse(body); - var authz = JSON.parse(body); + var simpleHttps = authz.challenges.filter(function(x) { return x.type == "simpleHttps"; }); + if (simpleHttps.length == 0) { + console.log("The server didn't offer any challenges we can handle."); + return; + } - var simpleHttps = authz.challenges.filter(function(x) { return x.type == "simpleHttps"; }); - if (simpleHttps.length == 0) { - console.log("The server didn't offer any challenges we can handle."); - return; - } + var challenge = simpleHttps[0]; + var path = crypto.randomString(8) + ".txt"; + fs.writeFileSync(".well-known/acme-challenge/" + path, challenge.token); + state.responseURL = challenge["uri"]; + state.path = path; - var challenge = simpleHttps[0]; - var path = crypto.randomString(8) + ".txt"; - fs.writeFileSync(".well-known/acme-challenge/" + path, challenge.token); - state.responseURL = challenge["uri"]; - state.path = path; + console.log(); + console.log("To validate that you own "+ state.domain +", the CA has\n" + + "asked you to provision a file on your server. I've saved\n" + + "the file here for you.\n"); + console.log(" File: " + path); + console.log(" URL: http://"+ state.domain +"/.well-known/acme-challenge/"+ path); + console.log(); - console.log(); - console.log("To validate that you own "+ state.domain +", the CA has\n" + - "asked you to provision a file on your server. I've saved\n" + - "the file here for you.\n"); - console.log(" File: " + path); - console.log(" URL: http://"+ state.domain +"/.well-known/acme-challenge/"+ path); - console.log(); + // To do this locally (boulder connects to port 5001) + // > mkdir -p .well-known/acme-challenge/ + // > mv $CHALLENGE_FILE ./well-known/acme-challenge/ + // > python -m SimpleHTTPServer 5001 - // To do this locally (boulder connects to port 5001) - // > mkdir -p .well-known/acme-challenge/ - // > mv $CHALLENGE_FILE ./well-known/acme-challenge/ - // > python -m SimpleHTTPServer 5001 - - inquirer.prompt(questions.readyToValidate, sendResponse); - }); + inquirer.prompt(questions.readyToValidate, sendResponse); } function sendResponse() { @@ -340,44 +315,36 @@ function sendResponse() { var options = url.parse(state.responseURL); options.method = "POST"; - var req = http.request(options, ensureValidation); + var req = request.post(state.responseURL, {}, ensureValidation); req.write(payload) req.end(); } -function ensureValidation(resp) { +function ensureValidation(err, resp, body) { if (Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Authorization status request failed with code " + resp.statusCode) return; } - var body = ""; - resp.on('data', function(chunk) { - body += chunk; - }); - resp.on('end', function(chunk) { - if (chunk) { body += chunk; } + var authz = JSON.parse(body); - var authz = JSON.parse(body); - - if (authz.status == "pending") { - setTimeout(function() { - http.get(state.authorizationURL, ensureValidation); - }, state.retryDelay); - } else if (authz.status == "valid") { - cli.spinner("Validating domain ... done", true); - console.log(); - getCertificate(); - } else if (authz.status == "invalid") { - console.log("The CA was unable to validate the file you provisioned:" + authz); - return; - } else { - console.log("The CA returned an authorization in an unexpected state"); - console.log(JSON.stringify(authz, null, " ")); - return; - } - }); + if (authz.status == "pending") { + setTimeout(function() { + request.get(state.authorizationURL, {}, ensureValidation); + }, state.retryDelay); + } else if (authz.status == "valid") { + cli.spinner("Validating domain ... done", true); + console.log(); + getCertificate(); + } else if (authz.status == "invalid") { + console.log("The CA was unable to validate the file you provisioned:" + authz); + return; + } else { + console.log("The CA returned an authorization in an unexpected state"); + console.log(JSON.stringify(authz, null, " ")); + return; + } } function getCertificate() { @@ -392,44 +359,38 @@ function getCertificate() { cli.spinner("Requesting certificate"); - var options = url.parse(state.newCertificateURL); - options.method = "POST"; - var req = http.request(options, downloadCertificate); + var req = request.post(state.newCertificateURL, {}, downloadCertificate); req.write(payload) req.end(); } -function downloadCertificate(resp) { - var chunks = []; - resp.on('data', function(chunk) { - chunks.push(chunk); - }); - resp.on('end', function(chunk) { - if (chunk) { chunks.push(chunk); } - var body = Buffer.concat(chunks); +function downloadCertificate(err, resp, body) { + if (Math.floor(resp.statusCode / 100) != 2) { + // Non-2XX response + console.log("Certificate request failed with code " + resp.statusCode); + console.log(body.toString()); + return; + } - if (Math.floor(resp.statusCode / 100) != 2) { - // Non-2XX response - console.log("Certificate request failed with code " + resp.statusCode); - console.log(body.toString()); - return; + cli.spinner("Requesting certificate ... done", true); + console.log(); + + state.certificate = body; + console.log() + var certURL = resp.headers['location']; + request.get(certURL, function(err, res, body) { + if (body !== state.certificate) { + console.log("ERROR! Cert at", certURL, "did not match returned cert."); } - - cli.spinner("Requesting certificate ... done", true); - console.log(); - var certB64 = util.b64enc(body); - - state.certificate = certB64; - inquirer.prompt(questions.files, saveFiles); }); + inquirer.prompt(questions.files, saveFiles); } function saveFiles(answers) { var keyPEM = crypto.privateKeyToPem(state.keyPair.privateKey); fs.writeFileSync(answers.keyFile, keyPEM); - var certPEM = crypto.certificateToPem(state.certificate); - fs.writeFileSync(answers.certFile, certPEM); + fs.writeFileSync(answers.certFile, state.certificate); console.log("Done!") console.log("To try it out:"); From 4e2d71f40da94ba6fea5a3678754cdb02d011f22 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 18:39:20 -0700 Subject: [PATCH 02/15] Add stdout logging for Notice level. --- log/audit-logger.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/log/audit-logger.go b/log/audit-logger.go index 221885bcc..cdd25a6ba 100644 --- a/log/audit-logger.go +++ b/log/audit-logger.go @@ -116,3 +116,9 @@ func (log *AuditLogger) Warning(msg string) (err error) { log.Stats.Inc("Logging.Warning", 1, 1.0) return log.Writer.Warning(msg) } + +func (log *AuditLogger) Notice(msg string) (err error) { + fmt.Println(msg) + log.Stats.Inc("Logging.Warning", 1, 1.0) + return log.Writer.Notice(msg) +} From 14511361e49437a3094c401d8c42796ceb78a195 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 18:39:47 -0700 Subject: [PATCH 03/15] Handle challenges within test.js --- test/js/test.js | 103 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/test/js/test.js b/test/js/test.js index c60583062..b4ffc8fec 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -5,14 +5,16 @@ "use strict"; -var inquirer = require("inquirer"); var cli = require("cli"); -var http = require('http'); +var crypto = require("./crypto-util"); +var child_process = require('child_process'); var fs = require('fs'); +var http = require('http'); +var https = require('https'); +var inquirer = require("inquirer"); var request = require('request'); var url = require('url'); var util = require("./acme-util"); -var crypto = require("./crypto-util"); var questions = { email: [{ @@ -73,7 +75,8 @@ var state = { keyPairBits: 512, keyPair: null, - newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg", + //newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg", + newRegistrationURL: "http://localhost:4000/acme/new-reg", registrationURL: "", termsRequired: false, @@ -90,6 +93,8 @@ var state = { newCertificateURL: "", certificateURL: "", + certFile: "", + keyFile: "" }; function parseLink(link) { @@ -153,8 +158,18 @@ saveFiles */ function main() { + inquirer.prompt(questions.files, makeKeyPair); +} + +function makeKeyPair(answers) { + state.certFile = answers.certFile; + state.keyFile = answers.keyFile; console.log("Generating key pair..."); state.keyPair = crypto.generateKeyPair(state.keyPairBits); + var keyPEM = crypto.privateKeyToPem(state.keyPair.privateKey); + fs.writeFileSync(answers.keyFile, keyPEM); + child_process.exec("openssl req -key key.pem -x509 -days 3650 -subj /CN=blah -nodes -out temp-cert.pem -new"); + console.log(); inquirer.prompt(questions.email, register) } @@ -176,7 +191,7 @@ function register(answers) { } function getTerms(err, resp) { - if (Math.floor(resp.statusCode / 100) != 2) { + if (err || Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Registration request failed with code " + resp.statusCode); return; @@ -202,8 +217,12 @@ function getTerms(err, resp) { } function getAgreement(err, resp, body) { + if (err) { + console.log("getAgreement error:", err); + process.exit(1); + } // TODO: Check content-type - console.log("The CA requires your agreement to terms (not supported)."); + console.log("The CA requires your agreement to terms."); console.log(); console.log(body); console.log(); @@ -226,8 +245,7 @@ function sendAgreement(answers) { console.log("Posting agreement to: " + state.registrationURL) var req = request(state.registrationURL, {}, function(err, resp, body) { - if (Math.floor(resp.statusCode / 100) != 2) { - // Non-2XX response + if (err) { console.log("Couldn't POST agreement back to server, aborting."); console.log("Code: "+ resp.statusCode); console.log(body); @@ -259,7 +277,7 @@ function getChallenges(answers) { } function getReadyToValidate(err, resp, body) { - if (Math.floor(resp.statusCode / 100) != 2) { + if (err || Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Authorization request failed with code " + resp.statusCode) return; @@ -284,22 +302,36 @@ function getReadyToValidate(err, resp, body) { var challenge = simpleHttps[0]; var path = crypto.randomString(8) + ".txt"; - fs.writeFileSync(".well-known/acme-challenge/" + path, challenge.token); + var challengePath = ".well-known/acme-challenge/" + path; + fs.writeFileSync(challengePath, challenge.token); state.responseURL = challenge["uri"]; state.path = path; - console.log(); - console.log("To validate that you own "+ state.domain +", the CA has\n" + - "asked you to provision a file on your server. I've saved\n" + - "the file here for you.\n"); - console.log(" File: " + path); - console.log(" URL: http://"+ state.domain +"/.well-known/acme-challenge/"+ path); - console.log(); - - // To do this locally (boulder connects to port 5001) - // > mkdir -p .well-known/acme-challenge/ - // > mv $CHALLENGE_FILE ./well-known/acme-challenge/ - // > python -m SimpleHTTPServer 5001 + // For local, test-mode validation + function httpResponder(request, response) { + console.log("Got request for", request.url); + var host = request.headers["host"]; + if (host === state.domain && + request.method === "GET" && + request.url == "/" + challengePath) { + response.writeHead(200, {"Content-Type": "text/plain"}); + response.end(challenge.token); + } else { + console.log("Got invalid request for", request.method, host, request.url); + response.writeHead(404, {"Content-Type": "text/plain"}); + response.end(""); + } + }; + if (/localhost/.test(state.newRegistrationURL)) { + var httpServer = http.createServer(httpResponder) + httpServer.listen(5001) + } else { + var httpServer = https.createServer({ + cert: fs.readFileSync("temp-cert.pem"), + key: fs.readFileSync(state.keyFile) + }) + httpServer.listen(443) + } inquirer.prompt(questions.readyToValidate, sendResponse); } @@ -359,13 +391,16 @@ function getCertificate() { cli.spinner("Requesting certificate"); - var req = request.post(state.newCertificateURL, {}, downloadCertificate); + var req = request.post({ + url: state.newCertificateURL, + encoding: null // Return body as buffer. + }, downloadCertificate); req.write(payload) req.end(); } function downloadCertificate(err, resp, body) { - if (Math.floor(resp.statusCode / 100) != 2) { + if (err || Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response console.log("Certificate request failed with code " + resp.statusCode); console.log(body.toString()); @@ -378,24 +413,26 @@ function downloadCertificate(err, resp, body) { state.certificate = body; console.log() var certURL = resp.headers['location']; - request.get(certURL, function(err, res, body) { - if (body !== state.certificate) { + request.get({ + url: certURL, + encoding: null // Return body as buffer. + }, function(err, res, body) { + if (body.toString() !== state.certificate.toString()) { console.log("ERROR! Cert at", certURL, "did not match returned cert."); + } else { + console.log("Successfully verified cert at", certURL); + saveFiles() } }); - inquirer.prompt(questions.files, saveFiles); } function saveFiles(answers) { - var keyPEM = crypto.privateKeyToPem(state.keyPair.privateKey); - fs.writeFileSync(answers.keyFile, keyPEM); - - fs.writeFileSync(answers.certFile, state.certificate); + fs.writeFileSync(state.certFile, state.certificate); console.log("Done!") console.log("To try it out:"); - console.log("openssl s_server -accept 8080 -www -key "+ - answers.keyFile +" -cert "+ answers.certFile); + console.log("openssl s_server -accept 8080 -www -certform der -key "+ + state.keyFile +" -cert "+ state.certFile); // XXX: Explicitly exit, since something's tenacious here process.exit(0); From 1065b14c9c4895545984bebf47f4d04ea5949420 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 18:39:50 -0700 Subject: [PATCH 04/15] Add more logging to boulder. --- va/validation-authority.go | 3 ++ wfe/web-front-end.go | 81 +++++++++++++++++++------------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/va/validation-authority.go b/va/validation-authority.go index 5b427f0d9..06d4f6039 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -46,6 +46,7 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti url = fmt.Sprintf("https://%s/.well-known/acme-challenge/%s", identifier, challenge.Path) } + va.log.Notice(fmt.Sprintf("Attempting to validate SimpleHTTPS for %s %s", identifier, url)) httpRequest, err := http.NewRequest("GET", url, nil) if err != nil { challenge.Status = core.StatusInvalid @@ -107,6 +108,8 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, } hostPort = identifier.Value + ":443" } + va.log.Notice(fmt.Sprintf("Attempting to validate DVSNI for %s %s %s", + identifier, hostPort, zName)) conn, err := tls.Dial("tcp", hostPort, &tls.Config{ ServerName: nonceName, InsecureSkipVerify: true, diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index a155118f9..ef38a3294 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -125,12 +125,13 @@ type problem struct { Instance string `json:"instance,omitempty"` } -func sendError(response http.ResponseWriter, message string, code int) { +func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, message string, code int) { problem := problem{Detail: message} problemDoc, err := json.Marshal(problem) if err != nil { - return + problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}") } + wfe.log.Debug("Sending error to client: " + string(problemDoc)) // Paraphrased from // https://golang.org/src/net/http/server.go#L1272 response.Header().Set("Content-Type", "application/problem+json") @@ -144,26 +145,26 @@ func link(url, relation string) string { func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return } body, key, err := verifyPOST(request) if err != nil { - sendError(response, fmt.Sprintf("Unable to read/verify body: %v", err), http.StatusBadRequest) + wfe.sendError(response, fmt.Sprintf("Unable to read/verify body: %v", err), http.StatusBadRequest) return } var init core.Registration err = json.Unmarshal(body, &init) if err != nil { - sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) return } reg, err := wfe.RA.NewRegistration(init, key) if err != nil { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Error creating new registration: %+v", err), http.StatusInternalServerError) return @@ -173,7 +174,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques reg.ID = "" responseBody, err := json.Marshal(reg) if err != nil { - sendError(response, "Error marshaling authz", http.StatusInternalServerError) + wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError) return } @@ -193,26 +194,26 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return } body, key, err := verifyPOST(request) if err != nil { - sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) return } var init core.Authorization if err = json.Unmarshal(body, &init); err != nil { - sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest) return } // Create new authz and return authz, err := wfe.RA.NewAuthorization(init, key) if err != nil { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Error creating new authz: %+v", err), http.StatusInternalServerError) return @@ -223,7 +224,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque authz.ID = "" responseBody, err := json.Marshal(authz) if err != nil { - sendError(response, "Error marshaling authz", http.StatusInternalServerError) + wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError) return } @@ -240,20 +241,20 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) { if request.Method != "POST" { - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return } body, key, err := verifyPOST(request) if err != nil { - sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) return } var init core.CertificateRequest if err = json.Unmarshal(body, &init); err != nil { fmt.Println(err) - sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest) return } @@ -268,7 +269,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request // RA for secondary validation. cert, err := wfe.RA.NewCertificate(init, key) if err != nil { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Error creating new cert: %+v", err), http.StatusBadRequest) return @@ -307,7 +308,7 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re } if !found { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Unable to find challenge"), http.StatusNotFound) return @@ -315,38 +316,38 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re switch request.Method { default: - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return case "POST": body, key, err := verifyPOST(request) if err != nil { - sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) return } var challengeResponse core.Challenge if err = json.Unmarshal(body, &challengeResponse); err != nil { - sendError(response, "Error unmarshaling authorization", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling authorization", http.StatusBadRequest) return } // Check that the signing key is the right key if !key.Equals(authz.Key) { - sendError(response, "Signing key does not match key in authorization", http.StatusForbidden) + wfe.sendError(response, "Signing key does not match key in authorization", http.StatusForbidden) return } // Ask the RA to update this authorization updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse) if err != nil { - sendError(response, "Unable to update authorization", http.StatusInternalServerError) + wfe.sendError(response, "Unable to update authorization", http.StatusInternalServerError) return } jsonReply, err := json.Marshal(updatedAuthz) if err != nil { - sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -363,7 +364,7 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * id := parseIDFromPath(request.URL.Path) reg, err := wfe.SA.GetRegistration(id) if err != nil { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Unable to find registration: %+v", err), http.StatusNotFound) return @@ -372,13 +373,13 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * switch request.Method { default: - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return case "GET": jsonReply, err := json.Marshal(reg) if err != nil { - sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -388,20 +389,20 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * case "POST": body, key, err := verifyPOST(request) if err != nil { - sendError(response, "Unable to read/verify body", http.StatusBadRequest) + wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest) return } var update core.Registration err = json.Unmarshal(body, &update) if err != nil { - sendError(response, "Error unmarshaling registration", http.StatusBadRequest) + wfe.sendError(response, "Error unmarshaling registration", http.StatusBadRequest) return } // Check that the signing key is the right key if !key.Equals(reg.Key) { - sendError(response, "Signing key does not match key in registration", http.StatusForbidden) + wfe.sendError(response, "Signing key does not match key in registration", http.StatusForbidden) return } @@ -409,13 +410,13 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * updatedReg, err := wfe.RA.UpdateRegistration(reg, update) if err != nil { fmt.Println(err) - sendError(response, "Unable to update registration", http.StatusInternalServerError) + wfe.sendError(response, "Unable to update registration", http.StatusInternalServerError) return } jsonReply, err := json.Marshal(updatedReg) if err != nil { - sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -430,7 +431,7 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request id := parseIDFromPath(request.URL.Path) authz, err := wfe.SA.GetAuthorization(id) if err != nil { - sendError(response, + wfe.sendError(response, fmt.Sprintf("Unable to find authorization: %+v", err), http.StatusNotFound) return @@ -444,13 +445,13 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request switch request.Method { default: - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return case "GET": jsonReply, err := json.Marshal(authz) if err != nil { - sendError(response, "Failed to marshal authz", http.StatusInternalServerError) + wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") @@ -463,34 +464,34 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request var allHex = regexp.MustCompile("^[0-9a-f]+$") -func notFound(response http.ResponseWriter) { - sendError(response, "Not found", http.StatusNotFound) +func (wfe *WebFrontEndImpl) notFound(response http.ResponseWriter) { + wfe.sendError(response, "Not found", http.StatusNotFound) } func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) { path := request.URL.Path switch request.Method { default: - sendError(response, "Method not allowed", http.StatusMethodNotAllowed) + wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed) return case "GET": // Certificate paths consist of the CertBase path, plus exactly sixteen hex // digits. if !strings.HasPrefix(path, wfe.CertPath) { - notFound(response) + wfe.notFound(response) return } serial := path[len(wfe.CertPath):] if len(serial) != 16 || !allHex.Match([]byte(serial)) { - notFound(response) + wfe.notFound(response) return } wfe.log.Notice(fmt.Sprintf("Requested certificate ID %s", serial)) cert, err := wfe.SA.GetCertificate(serial) if err != nil { - notFound(response) + wfe.notFound(response) return } From 6fc9ed5b80efc93e352b89f2dfbaee3fecd36e9f Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 19:11:51 -0700 Subject: [PATCH 05/15] Use openssl to generate test.js key, for speed. --- test/js/test.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/js/test.js b/test/js/test.js index b4ffc8fec..d95f8d02b 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -72,7 +72,6 @@ var questions = { }; var state = { - keyPairBits: 512, keyPair: null, //newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg", @@ -165,10 +164,8 @@ function makeKeyPair(answers) { state.certFile = answers.certFile; state.keyFile = answers.keyFile; console.log("Generating key pair..."); - state.keyPair = crypto.generateKeyPair(state.keyPairBits); - var keyPEM = crypto.privateKeyToPem(state.keyPair.privateKey); - fs.writeFileSync(answers.keyFile, keyPEM); - child_process.exec("openssl req -key key.pem -x509 -days 3650 -subj /CN=blah -nodes -out temp-cert.pem -new"); + child_process.exec("openssl req -newkey", state.keyFile, "-days 3650 -subj /CN=blah -nodes -out temp-cert.pem"); + state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile)); console.log(); inquirer.prompt(questions.email, register) @@ -193,7 +190,7 @@ function register(answers) { function getTerms(err, resp) { if (err || Math.floor(resp.statusCode / 100) != 2) { // Non-2XX response - console.log("Registration request failed with code " + resp.statusCode); + console.log("Registration request failed:" + err); return; } @@ -247,7 +244,7 @@ function sendAgreement(answers) { var req = request(state.registrationURL, {}, function(err, resp, body) { if (err) { console.log("Couldn't POST agreement back to server, aborting."); - console.log("Code: "+ resp.statusCode); + console.log("error: " + err); console.log(body); process.exit(1); } @@ -370,7 +367,7 @@ function ensureValidation(err, resp, body) { console.log(); getCertificate(); } else if (authz.status == "invalid") { - console.log("The CA was unable to validate the file you provisioned:" + authz); + console.log("The CA was unable to validate the file you provisioned:" + body); return; } else { console.log("The CA returned an authorization in an unexpected state"); From e210f2c623e4e495061cfa39d5e6a5f18b97e48d Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Fri, 24 Apr 2015 19:20:58 -0700 Subject: [PATCH 06/15] Fix live validation for SimpleHTTPS. --- va/validation-authority.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/va/validation-authority.go b/va/validation-authority.go index 06d4f6039..ab7ae93e0 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -39,16 +39,21 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti return } - url := "" + if identifier.Type != IdentifierDNS { + challenge.Status = core.StatusInvalid + return + } + hostName := identifier.Value if va.TestMode { - url = fmt.Sprintf("http://localhost:5001/.well-known/acme-challenge/%s", challenge.Path) - } else { - url = fmt.Sprintf("https://%s/.well-known/acme-challenge/%s", identifier, challenge.Path) + hostName = "localhost:5001" } - va.log.Notice(fmt.Sprintf("Attempting to validate SimpleHTTPS for %s %s", identifier, url)) + url = fmt.Sprintf("https://%s/.well-known/acme-challenge/%s", hostName, challenge.Path) + + va.log.Notice(fmt.Sprintf("Attempting to validate SimpleHTTPS for %s %s", hostName, url)) httpRequest, err := http.NewRequest("GET", url, nil) if err != nil { + va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err)) challenge.Status = core.StatusInvalid return } @@ -61,6 +66,7 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti // Read body & test body, err := ioutil.ReadAll(httpResponse.Body) if err != nil { + va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err)) challenge.Status = core.StatusInvalid return } @@ -68,10 +74,16 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti if subtle.ConstantTimeCompare(body, []byte(challenge.Token)) == 1 { challenge.Status = core.StatusValid return + } else { + va.log.Notice(fmt.Sprintf("Incorrect token validating SimpleHTTPS for %s %s: %s", hostName, url)) } + } else if err != nil { + va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err)) + challenge.Status = core.StatusInvalid + } else { + va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, httpResponse.StatusCode)) + challenge.Status = core.StatusInvalid } - - challenge.Status = core.StatusInvalid return } From a59323bfaa21c09e6b7989593df1ab891376a323 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 11:43:28 -0700 Subject: [PATCH 07/15] Fix key generation in test.js. Also be more lenient about validating inbound challenge requests when talking to a localhost Boulder. --- test/js/test.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/js/test.js b/test/js/test.js index d95f8d02b..c6e878ac8 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -164,11 +164,16 @@ function makeKeyPair(answers) { state.certFile = answers.certFile; state.keyFile = answers.keyFile; console.log("Generating key pair..."); - child_process.exec("openssl req -newkey", state.keyFile, "-days 3650 -subj /CN=blah -nodes -out temp-cert.pem"); - state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile)); + child_process.exec("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=example.com -nodes -x509 -out temp-cert.pem", function (error, stdout, stderr) { + if (error) { + console.log(error); + process.exit(1); + } + state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile)); - console.log(); - inquirer.prompt(questions.email, register) + console.log(); + inquirer.prompt(questions.email, register) + }); } function register(answers) { @@ -308,7 +313,7 @@ function getReadyToValidate(err, resp, body) { function httpResponder(request, response) { console.log("Got request for", request.url); var host = request.headers["host"]; - if (host === state.domain && + if ((host === state.domain || /localhost/.test(state.newRegistrationURL)) && request.method === "GET" && request.url == "/" + challengePath) { response.writeHead(200, {"Content-Type": "text/plain"}); From 9124b34b315b3ebc28ba68bfd46d7b8abb48fa0a Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 13:18:38 -0700 Subject: [PATCH 08/15] Improve Validation Authority SimpleHTTPS works in both local test mode and live mode. Don't keep alive SimpleHTTPS connections after verifying challenge. --- va/validation-authority.go | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/va/validation-authority.go b/va/validation-authority.go index ab7ae93e0..b98f62329 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -39,16 +39,18 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti return } - if identifier.Type != IdentifierDNS { + if identifier.Type != core.IdentifierDNS { challenge.Status = core.StatusInvalid return } hostName := identifier.Value + protocol := "https" if va.TestMode { hostName = "localhost:5001" + protocol = "http" } - url = fmt.Sprintf("https://%s/.well-known/acme-challenge/%s", hostName, challenge.Path) + url := fmt.Sprintf("%s://%s/.well-known/acme-challenge/%s", protocol, hostName, challenge.Path) va.log.Notice(fmt.Sprintf("Attempting to validate SimpleHTTPS for %s %s", hostName, url)) httpRequest, err := http.NewRequest("GET", url, nil) @@ -58,8 +60,20 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti return } - httpRequest.Host = identifier.Value - client := http.Client{Timeout: 5 * time.Second} + httpRequest.Host = hostName + tr := &http.Transport{ + // We are talking to a client that does not yet have a certificate, + // so we accept a temporary, invalid one. TODO: We may want to change this + // to just be over HTTP. + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + // We don't expect to make multiple requests to a client, so close + // connection immediately. + DisableKeepAlives: true, + } + client := http.Client{ + Transport: tr, + Timeout: 5 * time.Second, + } httpResponse, err := client.Do(httpRequest) if err == nil && httpResponse.StatusCode == 200 { @@ -75,19 +89,24 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti challenge.Status = core.StatusValid return } else { - va.log.Notice(fmt.Sprintf("Incorrect token validating SimpleHTTPS for %s %s: %s", hostName, url)) + va.log.Notice(fmt.Sprintf("Incorrect token validating SimpleHTTPS for %s %s", hostName, url)) } } else if err != nil { va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err)) challenge.Status = core.StatusInvalid } else { - va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, httpResponse.StatusCode)) + va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %d", hostName, url, httpResponse.StatusCode)) challenge.Status = core.StatusInvalid } return } func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) (challenge core.Challenge) { + if identifier.Type != "dns" { + challenge.Status = core.StatusInvalid + return + } + challenge = input const DVSNI_SUFFIX = ".acme.invalid" @@ -110,15 +129,9 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, // Make a connection with SNI = nonceName - hostPort := "" + hostPort := identifier.Value + ":443" if va.TestMode { hostPort = "localhost:5001" - } else { - if identifier.Type != "dns" { - challenge.Status = core.StatusInvalid - return - } - hostPort = identifier.Value + ":443" } va.log.Notice(fmt.Sprintf("Attempting to validate DVSNI for %s %s %s", identifier, hostPort, zName)) From 66cb0fcefe2179839fe7e03c47ee5926d4a43ba6 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 14:46:50 -0700 Subject: [PATCH 09/15] Fix format strings for serials and DVSNI. --- sa/storage-authority.go | 2 +- va/validation-authority.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sa/storage-authority.go b/sa/storage-authority.go index d718cb9ee..cc6c3a608 100644 --- a/sa/storage-authority.go +++ b/sa/storage-authority.go @@ -374,7 +374,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte) (digest string, e if err != nil { return } - serial := fmt.Sprintf("%016x", parsedCertificate.SerialNumber) + serial := fmt.Sprintf("%032x", parsedCertificate.SerialNumber) tx, err := ssa.db.Begin() if err != nil { diff --git a/va/validation-authority.go b/va/validation-authority.go index b98f62329..7da43c39f 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -125,7 +125,7 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, RS := append(R, S...) z := sha256.Sum256(RS) - zName := fmt.Sprintf("%x.acme.invalid", z) + zName := fmt.Sprintf("%064x.acme.invalid", z) // Make a connection with SNI = nonceName From f81b531abbb7528a3d37d5e0707d26891b863f6c Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 14:47:34 -0700 Subject: [PATCH 10/15] Fix incorrect request.post invocation. Also tidy up some error cases and variable names. --- test/js/test.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/test/js/test.js b/test/js/test.js index c6e878ac8..ce7fde2cb 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -246,7 +246,7 @@ function sendAgreement(answers) { var payload = JSON.stringify(jws); console.log("Posting agreement to: " + state.registrationURL) - var req = request(state.registrationURL, {}, function(err, resp, body) { + var req = request.post(state.registrationURL, function(err, resp, body) { if (err) { console.log("Couldn't POST agreement back to server, aborting."); console.log("error: " + err); @@ -310,16 +310,16 @@ function getReadyToValidate(err, resp, body) { state.path = path; // For local, test-mode validation - function httpResponder(request, response) { - console.log("Got request for", request.url); - var host = request.headers["host"]; + function httpResponder(req, response) { + console.log("Got request for", req.url); + var host = req.headers["host"]; if ((host === state.domain || /localhost/.test(state.newRegistrationURL)) && - request.method === "GET" && - request.url == "/" + challengePath) { + req.method === "GET" && + req.url == "/" + challengePath) { response.writeHead(200, {"Content-Type": "text/plain"}); response.end(challenge.token); } else { - console.log("Got invalid request for", request.method, host, request.url); + console.log("Got invalid request for", req.method, host, req.url); response.writeHead(404, {"Content-Type": "text/plain"}); response.end(""); } @@ -331,7 +331,7 @@ function getReadyToValidate(err, resp, body) { var httpServer = https.createServer({ cert: fs.readFileSync("temp-cert.pem"), key: fs.readFileSync(state.keyFile) - }) + }, httpResponder) httpServer.listen(443) } @@ -419,8 +419,17 @@ function downloadCertificate(err, resp, body) { url: certURL, encoding: null // Return body as buffer. }, function(err, res, body) { + if (err) { + console.log("Error: Failed to fetch certificate from", certURL, ":", err); + process.exit(1); + } + if (res.statusCode !== 200) { + console.log("Error: Failed to fetch certificate from", certURL, ":", res.statusCode, res.body.toString()); + fs.writeFileSync(state.certFile, state.certificate); + process.exit(1); + } if (body.toString() !== state.certificate.toString()) { - console.log("ERROR! Cert at", certURL, "did not match returned cert."); + console.log("Error: cert at", certURL, "did not match returned cert."); } else { console.log("Successfully verified cert at", certURL); saveFiles() From d4aa8c6c78379fd86c3e09edf43059b47b1c8a51 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 15:50:18 -0700 Subject: [PATCH 11/15] Close connection after DVSNI. --- va/validation-authority.go | 1 + 1 file changed, 1 insertion(+) diff --git a/va/validation-authority.go b/va/validation-authority.go index 7da43c39f..c0d9024b7 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -144,6 +144,7 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, challenge.Status = core.StatusInvalid return } + defer conn.Close() // Check that zName is a dNSName SAN in the server's certificate certs := conn.ConnectionState().PeerCertificates From 1743fb2edbad50ca1a637fb120650b523a5ed2d8 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 15:50:31 -0700 Subject: [PATCH 12/15] Fix stats for log.Notice. --- log/audit-logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log/audit-logger.go b/log/audit-logger.go index cdd25a6ba..aa231e7cf 100644 --- a/log/audit-logger.go +++ b/log/audit-logger.go @@ -119,6 +119,6 @@ func (log *AuditLogger) Warning(msg string) (err error) { func (log *AuditLogger) Notice(msg string) (err error) { fmt.Println(msg) - log.Stats.Inc("Logging.Warning", 1, 1.0) + log.Stats.Inc("Logging.Notice", 1, 1.0) return log.Writer.Notice(msg) } From eecf4b305e005d7a7b8916b58f519059f8df132e Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 15:50:44 -0700 Subject: [PATCH 13/15] Add instructions and clarify openssl command line. --- test/js/test.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/js/test.js b/test/js/test.js index ce7fde2cb..67dac282f 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -2,6 +2,15 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// To test against a Boulder running on localhost in test mode: +// cd boulder/test/js +// npm install +// js test.js +// +// To test against a live or demo Boulder, edit this file to change +// newRegistrationURL, then run: +// sudo js test.js. "use strict"; @@ -164,7 +173,7 @@ function makeKeyPair(answers) { state.certFile = answers.certFile; state.keyFile = answers.keyFile; console.log("Generating key pair..."); - child_process.exec("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=example.com -nodes -x509 -out temp-cert.pem", function (error, stdout, stderr) { + child_process.exec("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=foo -nodes -x509 -out temp-cert.pem", function (error, stdout, stderr) { if (error) { console.log(error); process.exit(1); From 9121719a49099c5dab5b36c581e7bec39c4eac35 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 15:54:23 -0700 Subject: [PATCH 14/15] Ignore goveralls submission failures. --- test.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index 3102d6b07..0967a92c7 100755 --- a/test.sh +++ b/test.sh @@ -44,7 +44,10 @@ doTest wfe [ -e $GOBIN/gover ] && run $GOBIN/gover if [ "${TRAVIS}" == "true" ] ; then - run $GOBIN/goveralls -coverprofile=gover.coverprofile -service=travis-ci + # We don't use the run function here because sometimes goveralls fails to + # contact the server and exits with non-zero status, but we don't want to + # treat that as a failure. + $GOBIN/goveralls -coverprofile=gover.coverprofile -service=travis-ci fi exit ${FAILURE} From 0bd39daab50a8f553cadbca6ee54450ad9cfc093 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 27 Apr 2015 17:00:53 -0700 Subject: [PATCH 15/15] Fix tests that relied on mis-formatted serial. --- ca/certificate-authority_test.go | 4 ++-- ra/registration-authority_test.go | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ca/certificate-authority_test.go b/ca/certificate-authority_test.go index 106f8730e..c7985cf8a 100644 --- a/ca/certificate-authority_test.go +++ b/ca/certificate-authority_test.go @@ -353,10 +353,10 @@ func TestIssueCertificate(t *testing.T) { } // Verify that the cert got stored in the DB - shortSerial := fmt.Sprintf("%016x", cert.SerialNumber)[0:16] + shortSerial := fmt.Sprintf("%032x", cert.SerialNumber)[0:16] _, err = sa.GetCertificate(shortSerial) test.AssertNotError(t, err, - fmt.Sprintf("Certificate %016x not found in database", shortSerial)) + fmt.Sprintf("Certificate %032x not found in database", cert.SerialNumber)) } // Test that the CA rejects CSRs with no names diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index 51eaa48af..79657c376 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -244,11 +244,12 @@ func TestNewCertificate(t *testing.T) { test.AssertNotError(t, err, "Failed to issue certificate") parsedCert, err := x509.ParseCertificate(cert.DER) test.AssertNotError(t, err, "Failed to parse certificate") - shortSerial := fmt.Sprintf("%016x", parsedCert.SerialNumber)[0:16] + shortSerial := fmt.Sprintf("%032x", parsedCert.SerialNumber)[0:16] // Verify that cert shows up and is as expected dbCert, err := sa.GetCertificate(shortSerial) - test.AssertNotError(t, err, "Could not fetch certificate from database") + test.AssertNotError(t, err, fmt.Sprintf("Could not fetch certificate %032x from database", + parsedCert.SerialNumber)) test.Assert(t, bytes.Compare(cert.DER, dbCert) == 0, "Certificates differ") // TODO Test failure cases