Merge pull request #113 from letsencrypt/improve-testjs
Improve test.js and misc changes to improve live deployment.
This commit is contained in:
commit
6fe1a8be3b
|
|
@ -353,10 +353,10 @@ func TestIssueCertificate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the cert got stored in the DB
|
// 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)
|
_, err = sa.GetCertificate(shortSerial)
|
||||||
test.AssertNotError(t, err,
|
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
|
// Test that the CA rejects CSRs with no names
|
||||||
|
|
|
||||||
|
|
@ -116,3 +116,9 @@ func (log *AuditLogger) Warning(msg string) (err error) {
|
||||||
log.Stats.Inc("Logging.Warning", 1, 1.0)
|
log.Stats.Inc("Logging.Warning", 1, 1.0)
|
||||||
return log.Writer.Warning(msg)
|
return log.Writer.Warning(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *AuditLogger) Notice(msg string) (err error) {
|
||||||
|
fmt.Println(msg)
|
||||||
|
log.Stats.Inc("Logging.Notice", 1, 1.0)
|
||||||
|
return log.Writer.Notice(msg)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -244,11 +244,12 @@ func TestNewCertificate(t *testing.T) {
|
||||||
test.AssertNotError(t, err, "Failed to issue certificate")
|
test.AssertNotError(t, err, "Failed to issue certificate")
|
||||||
parsedCert, err := x509.ParseCertificate(cert.DER)
|
parsedCert, err := x509.ParseCertificate(cert.DER)
|
||||||
test.AssertNotError(t, err, "Failed to parse certificate")
|
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
|
// Verify that cert shows up and is as expected
|
||||||
dbCert, err := sa.GetCertificate(shortSerial)
|
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")
|
test.Assert(t, bytes.Compare(cert.DER, dbCert) == 0, "Certificates differ")
|
||||||
|
|
||||||
// TODO Test failure cases
|
// TODO Test failure cases
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte) (digest string, e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serial := fmt.Sprintf("%016x", parsedCertificate.SerialNumber)
|
serial := fmt.Sprintf("%032x", parsedCertificate.SerialNumber)
|
||||||
|
|
||||||
tx, err := ssa.db.Begin()
|
tx, err := ssa.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
5
test.sh
5
test.sh
|
|
@ -44,7 +44,10 @@ doTest wfe
|
||||||
[ -e $GOBIN/gover ] && run $GOBIN/gover
|
[ -e $GOBIN/gover ] && run $GOBIN/gover
|
||||||
|
|
||||||
if [ "${TRAVIS}" == "true" ] ; then
|
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
|
fi
|
||||||
|
|
||||||
exit ${FAILURE}
|
exit ${FAILURE}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cli": "^0.6.5",
|
"cli": "^0.6.5",
|
||||||
"inquirer": "^0.8.2",
|
"inquirer": "^0.8.2",
|
||||||
"node-forge": "^0.6.21"
|
"node-forge": "^0.6.21",
|
||||||
|
"request": "^2.55.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
208
test/js/test.js
208
test/js/test.js
|
|
@ -2,16 +2,28 @@
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// 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/.
|
// 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";
|
"use strict";
|
||||||
|
|
||||||
var inquirer = require("inquirer");
|
|
||||||
var cli = require("cli");
|
var cli = require("cli");
|
||||||
var http = require('http');
|
var crypto = require("./crypto-util");
|
||||||
|
var child_process = require('child_process');
|
||||||
var fs = require('fs');
|
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 url = require('url');
|
||||||
var util = require("./acme-util");
|
var util = require("./acme-util");
|
||||||
var crypto = require("./crypto-util");
|
|
||||||
|
|
||||||
var questions = {
|
var questions = {
|
||||||
email: [{
|
email: [{
|
||||||
|
|
@ -32,7 +44,7 @@ var questions = {
|
||||||
type: "confirm",
|
type: "confirm",
|
||||||
name: "terms",
|
name: "terms",
|
||||||
message: "Do you agree to these terms?",
|
message: "Do you agree to these terms?",
|
||||||
default: false,
|
default: true,
|
||||||
}],
|
}],
|
||||||
|
|
||||||
domain: [{
|
domain: [{
|
||||||
|
|
@ -64,14 +76,14 @@ var questions = {
|
||||||
type: "input",
|
type: "input",
|
||||||
name: "certFile",
|
name: "certFile",
|
||||||
message: "Name for certificate file",
|
message: "Name for certificate file",
|
||||||
default: "cert.pem"
|
default: "cert.der"
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
var state = {
|
var state = {
|
||||||
keyPairBits: 512,
|
|
||||||
keyPair: null,
|
keyPair: null,
|
||||||
|
|
||||||
|
//newRegistrationURL: "https://www.letsencrypt-demo.org/acme/new-reg",
|
||||||
newRegistrationURL: "http://localhost:4000/acme/new-reg",
|
newRegistrationURL: "http://localhost:4000/acme/new-reg",
|
||||||
registrationURL: "",
|
registrationURL: "",
|
||||||
|
|
||||||
|
|
@ -89,6 +101,8 @@ var state = {
|
||||||
|
|
||||||
newCertificateURL: "",
|
newCertificateURL: "",
|
||||||
certificateURL: "",
|
certificateURL: "",
|
||||||
|
certFile: "",
|
||||||
|
keyFile: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseLink(link) {
|
function parseLink(link) {
|
||||||
|
|
@ -152,10 +166,23 @@ saveFiles
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
inquirer.prompt(questions.files, makeKeyPair);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeKeyPair(answers) {
|
||||||
|
state.certFile = answers.certFile;
|
||||||
|
state.keyFile = answers.keyFile;
|
||||||
console.log("Generating key pair...");
|
console.log("Generating key pair...");
|
||||||
state.keyPair = crypto.generateKeyPair(state.keyPairBits);
|
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);
|
||||||
|
}
|
||||||
|
state.keyPair = crypto.importPemPrivateKey(fs.readFileSync(state.keyFile));
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
inquirer.prompt(questions.email, register)
|
inquirer.prompt(questions.email, register)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function register(answers) {
|
function register(answers) {
|
||||||
|
|
@ -169,17 +196,15 @@ function register(answers) {
|
||||||
var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
|
var jws = crypto.generateSignature(state.keyPair, new Buffer(registerMessage));
|
||||||
var payload = JSON.stringify(jws);
|
var payload = JSON.stringify(jws);
|
||||||
|
|
||||||
var options = url.parse(state.newRegistrationURL);
|
var req = request.post(state.newRegistrationURL, {}, getTerms);
|
||||||
options.method = "POST";
|
|
||||||
var req = http.request(options, getTerms);
|
|
||||||
req.write(payload)
|
req.write(payload)
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTerms(resp) {
|
function getTerms(err, resp) {
|
||||||
if (Math.floor(resp.statusCode / 100) != 2) {
|
if (err || Math.floor(resp.statusCode / 100) != 2) {
|
||||||
// Non-2XX response
|
// Non-2XX response
|
||||||
console.log("Registration request failed with code " + resp.statusCode);
|
console.log("Registration request failed:" + err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,28 +221,24 @@ function getTerms(resp) {
|
||||||
if (state.termsRequired) {
|
if (state.termsRequired) {
|
||||||
state.termsURL = links["terms-of-service"];
|
state.termsURL = links["terms-of-service"];
|
||||||
console.log(state.termsURL);
|
console.log(state.termsURL);
|
||||||
http.get(state.termsURL, getAgreement)
|
request.get(state.termsURL, getAgreement)
|
||||||
} else {
|
} else {
|
||||||
inquirer.prompt(questions.domain, getChallenges);
|
inquirer.prompt(questions.domain, getChallenges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAgreement(resp) {
|
function getAgreement(err, resp, body) {
|
||||||
var body = "";
|
if (err) {
|
||||||
resp.on("data", function(chunk) {
|
console.log("getAgreement error:", err);
|
||||||
body += chunk;
|
process.exit(1);
|
||||||
});
|
}
|
||||||
resp.on("end", function(chunk) {
|
|
||||||
if (chunk) { body += chunk; }
|
|
||||||
|
|
||||||
// TODO: Check content-type
|
// 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();
|
||||||
console.log(body);
|
console.log(body);
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
inquirer.prompt(questions.terms, sendAgreement);
|
inquirer.prompt(questions.terms, sendAgreement);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendAgreement(answers) {
|
function sendAgreement(answers) {
|
||||||
|
|
@ -234,20 +255,13 @@ function sendAgreement(answers) {
|
||||||
var payload = JSON.stringify(jws);
|
var payload = JSON.stringify(jws);
|
||||||
|
|
||||||
console.log("Posting agreement to: " + state.registrationURL)
|
console.log("Posting agreement to: " + state.registrationURL)
|
||||||
var options = url.parse(state.registrationURL);
|
var req = request.post(state.registrationURL, function(err, resp, body) {
|
||||||
options.method = "POST";
|
if (err) {
|
||||||
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("Couldn't POST agreement back to server, aborting.");
|
||||||
console.log("Code: "+ resp.statusCode);
|
console.log("error: " + err);
|
||||||
console.log(body);
|
console.log(body);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
inquirer.prompt(questions.domain, getChallenges);
|
inquirer.prompt(questions.domain, getChallenges);
|
||||||
});
|
});
|
||||||
|
|
@ -268,15 +282,13 @@ function getChallenges(answers) {
|
||||||
var jws = crypto.generateSignature(state.keyPair, new Buffer(authzMessage));
|
var jws = crypto.generateSignature(state.keyPair, new Buffer(authzMessage));
|
||||||
var payload = JSON.stringify(jws);
|
var payload = JSON.stringify(jws);
|
||||||
|
|
||||||
var options = url.parse(state.newAuthorizationURL);
|
var req = request.post(state.newAuthorizationURL, {}, getReadyToValidate);
|
||||||
options.method = "POST";
|
|
||||||
var req = http.request(options, getReadyToValidate);
|
|
||||||
req.write(payload)
|
req.write(payload)
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReadyToValidate(resp) {
|
function getReadyToValidate(err, resp, body) {
|
||||||
if (Math.floor(resp.statusCode / 100) != 2) {
|
if (err || Math.floor(resp.statusCode / 100) != 2) {
|
||||||
// Non-2XX response
|
// Non-2XX response
|
||||||
console.log("Authorization request failed with code " + resp.statusCode)
|
console.log("Authorization request failed with code " + resp.statusCode)
|
||||||
return;
|
return;
|
||||||
|
|
@ -291,13 +303,6 @@ function getReadyToValidate(resp) {
|
||||||
state.authorizationURL = resp.headers["location"];
|
state.authorizationURL = resp.headers["location"];
|
||||||
state.newCertificateURL = links["next"];
|
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"; });
|
var simpleHttps = authz.challenges.filter(function(x) { return x.type == "simpleHttps"; });
|
||||||
|
|
@ -308,25 +313,38 @@ function getReadyToValidate(resp) {
|
||||||
|
|
||||||
var challenge = simpleHttps[0];
|
var challenge = simpleHttps[0];
|
||||||
var path = crypto.randomString(8) + ".txt";
|
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.responseURL = challenge["uri"];
|
||||||
state.path = path;
|
state.path = path;
|
||||||
|
|
||||||
console.log();
|
// For local, test-mode validation
|
||||||
console.log("To validate that you own "+ state.domain +", the CA has\n" +
|
function httpResponder(req, response) {
|
||||||
"asked you to provision a file on your server. I've saved\n" +
|
console.log("Got request for", req.url);
|
||||||
"the file here for you.\n");
|
var host = req.headers["host"];
|
||||||
console.log(" File: " + path);
|
if ((host === state.domain || /localhost/.test(state.newRegistrationURL)) &&
|
||||||
console.log(" URL: http://"+ state.domain +"/.well-known/acme-challenge/"+ path);
|
req.method === "GET" &&
|
||||||
console.log();
|
req.url == "/" + challengePath) {
|
||||||
|
response.writeHead(200, {"Content-Type": "text/plain"});
|
||||||
// To do this locally (boulder connects to port 5001)
|
response.end(challenge.token);
|
||||||
// > mkdir -p .well-known/acme-challenge/
|
} else {
|
||||||
// > mv $CHALLENGE_FILE ./well-known/acme-challenge/
|
console.log("Got invalid request for", req.method, host, req.url);
|
||||||
// > python -m SimpleHTTPServer 5001
|
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)
|
||||||
|
}, httpResponder)
|
||||||
|
httpServer.listen(443)
|
||||||
|
}
|
||||||
|
|
||||||
inquirer.prompt(questions.readyToValidate, sendResponse);
|
inquirer.prompt(questions.readyToValidate, sendResponse);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendResponse() {
|
function sendResponse() {
|
||||||
|
|
@ -340,44 +358,36 @@ function sendResponse() {
|
||||||
|
|
||||||
var options = url.parse(state.responseURL);
|
var options = url.parse(state.responseURL);
|
||||||
options.method = "POST";
|
options.method = "POST";
|
||||||
var req = http.request(options, ensureValidation);
|
var req = request.post(state.responseURL, {}, ensureValidation);
|
||||||
req.write(payload)
|
req.write(payload)
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureValidation(resp) {
|
function ensureValidation(err, resp, body) {
|
||||||
if (Math.floor(resp.statusCode / 100) != 2) {
|
if (Math.floor(resp.statusCode / 100) != 2) {
|
||||||
// Non-2XX response
|
// Non-2XX response
|
||||||
console.log("Authorization status request failed with code " + resp.statusCode)
|
console.log("Authorization status request failed with code " + resp.statusCode)
|
||||||
return;
|
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") {
|
if (authz.status == "pending") {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
http.get(state.authorizationURL, ensureValidation);
|
request.get(state.authorizationURL, {}, ensureValidation);
|
||||||
}, state.retryDelay);
|
}, state.retryDelay);
|
||||||
} else if (authz.status == "valid") {
|
} else if (authz.status == "valid") {
|
||||||
cli.spinner("Validating domain ... done", true);
|
cli.spinner("Validating domain ... done", true);
|
||||||
console.log();
|
console.log();
|
||||||
getCertificate();
|
getCertificate();
|
||||||
} else if (authz.status == "invalid") {
|
} 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;
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.log("The CA returned an authorization in an unexpected state");
|
console.log("The CA returned an authorization in an unexpected state");
|
||||||
console.log(JSON.stringify(authz, null, " "));
|
console.log(JSON.stringify(authz, null, " "));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCertificate() {
|
function getCertificate() {
|
||||||
|
|
@ -392,23 +402,16 @@ function getCertificate() {
|
||||||
|
|
||||||
cli.spinner("Requesting certificate");
|
cli.spinner("Requesting certificate");
|
||||||
|
|
||||||
var options = url.parse(state.newCertificateURL);
|
var req = request.post({
|
||||||
options.method = "POST";
|
url: state.newCertificateURL,
|
||||||
var req = http.request(options, downloadCertificate);
|
encoding: null // Return body as buffer.
|
||||||
|
}, downloadCertificate);
|
||||||
req.write(payload)
|
req.write(payload)
|
||||||
req.end();
|
req.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadCertificate(resp) {
|
function downloadCertificate(err, resp, body) {
|
||||||
var chunks = [];
|
if (err || Math.floor(resp.statusCode / 100) != 2) {
|
||||||
resp.on('data', function(chunk) {
|
|
||||||
chunks.push(chunk);
|
|
||||||
});
|
|
||||||
resp.on('end', function(chunk) {
|
|
||||||
if (chunk) { chunks.push(chunk); }
|
|
||||||
var body = Buffer.concat(chunks);
|
|
||||||
|
|
||||||
if (Math.floor(resp.statusCode / 100) != 2) {
|
|
||||||
// Non-2XX response
|
// Non-2XX response
|
||||||
console.log("Certificate request failed with code " + resp.statusCode);
|
console.log("Certificate request failed with code " + resp.statusCode);
|
||||||
console.log(body.toString());
|
console.log(body.toString());
|
||||||
|
|
@ -417,24 +420,39 @@ function downloadCertificate(resp) {
|
||||||
|
|
||||||
cli.spinner("Requesting certificate ... done", true);
|
cli.spinner("Requesting certificate ... done", true);
|
||||||
console.log();
|
console.log();
|
||||||
var certB64 = util.b64enc(body);
|
|
||||||
|
|
||||||
state.certificate = certB64;
|
state.certificate = body;
|
||||||
inquirer.prompt(questions.files, saveFiles);
|
console.log()
|
||||||
|
var certURL = resp.headers['location'];
|
||||||
|
request.get({
|
||||||
|
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.");
|
||||||
|
} else {
|
||||||
|
console.log("Successfully verified cert at", certURL);
|
||||||
|
saveFiles()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveFiles(answers) {
|
function saveFiles(answers) {
|
||||||
var keyPEM = crypto.privateKeyToPem(state.keyPair.privateKey);
|
fs.writeFileSync(state.certFile, state.certificate);
|
||||||
fs.writeFileSync(answers.keyFile, keyPEM);
|
|
||||||
|
|
||||||
var certPEM = crypto.certificateToPem(state.certificate);
|
|
||||||
fs.writeFileSync(answers.certFile, certPEM);
|
|
||||||
|
|
||||||
console.log("Done!")
|
console.log("Done!")
|
||||||
console.log("To try it out:");
|
console.log("To try it out:");
|
||||||
console.log("openssl s_server -accept 8080 -www -key "+
|
console.log("openssl s_server -accept 8080 -www -certform der -key "+
|
||||||
answers.keyFile +" -cert "+ answers.certFile);
|
state.keyFile +" -cert "+ state.certFile);
|
||||||
|
|
||||||
// XXX: Explicitly exit, since something's tenacious here
|
// XXX: Explicitly exit, since something's tenacious here
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|
|
||||||
|
|
@ -39,27 +39,48 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
url := ""
|
if identifier.Type != core.IdentifierDNS {
|
||||||
|
challenge.Status = core.StatusInvalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hostName := identifier.Value
|
||||||
|
protocol := "https"
|
||||||
if va.TestMode {
|
if va.TestMode {
|
||||||
url = fmt.Sprintf("http://localhost:5001/.well-known/acme-challenge/%s", challenge.Path)
|
hostName = "localhost:5001"
|
||||||
} else {
|
protocol = "http"
|
||||||
url = fmt.Sprintf("https://%s/.well-known/acme-challenge/%s", identifier, 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)
|
httpRequest, err := http.NewRequest("GET", url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err))
|
||||||
challenge.Status = core.StatusInvalid
|
challenge.Status = core.StatusInvalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
httpRequest.Host = identifier.Value
|
httpRequest.Host = hostName
|
||||||
client := http.Client{Timeout: 5 * time.Second}
|
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)
|
httpResponse, err := client.Do(httpRequest)
|
||||||
|
|
||||||
if err == nil && httpResponse.StatusCode == 200 {
|
if err == nil && httpResponse.StatusCode == 200 {
|
||||||
// Read body & test
|
// Read body & test
|
||||||
body, err := ioutil.ReadAll(httpResponse.Body)
|
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %s", hostName, url, err))
|
||||||
challenge.Status = core.StatusInvalid
|
challenge.Status = core.StatusInvalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -67,14 +88,25 @@ func (va ValidationAuthorityImpl) validateSimpleHTTPS(identifier core.AcmeIdenti
|
||||||
if subtle.ConstantTimeCompare(body, []byte(challenge.Token)) == 1 {
|
if subtle.ConstantTimeCompare(body, []byte(challenge.Token)) == 1 {
|
||||||
challenge.Status = core.StatusValid
|
challenge.Status = core.StatusValid
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
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
|
challenge.Status = core.StatusInvalid
|
||||||
|
} else {
|
||||||
|
va.log.Notice(fmt.Sprintf("Error validating SimpleHTTPS for %s %s: %d", hostName, url, httpResponse.StatusCode))
|
||||||
|
challenge.Status = core.StatusInvalid
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) (challenge core.Challenge) {
|
func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) (challenge core.Challenge) {
|
||||||
|
if identifier.Type != "dns" {
|
||||||
|
challenge.Status = core.StatusInvalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
challenge = input
|
challenge = input
|
||||||
|
|
||||||
const DVSNI_SUFFIX = ".acme.invalid"
|
const DVSNI_SUFFIX = ".acme.invalid"
|
||||||
|
|
@ -93,20 +125,16 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
|
||||||
RS := append(R, S...)
|
RS := append(R, S...)
|
||||||
|
|
||||||
z := sha256.Sum256(RS)
|
z := sha256.Sum256(RS)
|
||||||
zName := fmt.Sprintf("%x.acme.invalid", z)
|
zName := fmt.Sprintf("%064x.acme.invalid", z)
|
||||||
|
|
||||||
// Make a connection with SNI = nonceName
|
// Make a connection with SNI = nonceName
|
||||||
|
|
||||||
hostPort := ""
|
hostPort := identifier.Value + ":443"
|
||||||
if va.TestMode {
|
if va.TestMode {
|
||||||
hostPort = "localhost:5001"
|
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))
|
||||||
conn, err := tls.Dial("tcp", hostPort, &tls.Config{
|
conn, err := tls.Dial("tcp", hostPort, &tls.Config{
|
||||||
ServerName: nonceName,
|
ServerName: nonceName,
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
|
|
@ -116,6 +144,7 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
|
||||||
challenge.Status = core.StatusInvalid
|
challenge.Status = core.StatusInvalid
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
// Check that zName is a dNSName SAN in the server's certificate
|
// Check that zName is a dNSName SAN in the server's certificate
|
||||||
certs := conn.ConnectionState().PeerCertificates
|
certs := conn.ConnectionState().PeerCertificates
|
||||||
|
|
|
||||||
|
|
@ -125,12 +125,13 @@ type problem struct {
|
||||||
Instance string `json:"instance,omitempty"`
|
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}
|
problem := problem{Detail: message}
|
||||||
problemDoc, err := json.Marshal(problem)
|
problemDoc, err := json.Marshal(problem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}")
|
||||||
}
|
}
|
||||||
|
wfe.log.Debug("Sending error to client: " + string(problemDoc))
|
||||||
// Paraphrased from
|
// Paraphrased from
|
||||||
// https://golang.org/src/net/http/server.go#L1272
|
// https://golang.org/src/net/http/server.go#L1272
|
||||||
response.Header().Set("Content-Type", "application/problem+json")
|
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) {
|
func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) {
|
||||||
if request.Method != "POST" {
|
if request.Method != "POST" {
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, key, err := verifyPOST(request)
|
body, key, err := verifyPOST(request)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var init core.Registration
|
var init core.Registration
|
||||||
err = json.Unmarshal(body, &init)
|
err = json.Unmarshal(body, &init)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
|
wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reg, err := wfe.RA.NewRegistration(init, key)
|
reg, err := wfe.RA.NewRegistration(init, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Error creating new registration: %+v", err),
|
fmt.Sprintf("Error creating new registration: %+v", err),
|
||||||
http.StatusInternalServerError)
|
http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
@ -173,7 +174,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
|
||||||
reg.ID = ""
|
reg.ID = ""
|
||||||
responseBody, err := json.Marshal(reg)
|
responseBody, err := json.Marshal(reg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Error marshaling authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,26 +194,26 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
|
||||||
|
|
||||||
func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
|
func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
|
||||||
if request.Method != "POST" {
|
if request.Method != "POST" {
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, key, err := verifyPOST(request)
|
body, key, err := verifyPOST(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var init core.Authorization
|
var init core.Authorization
|
||||||
if err = json.Unmarshal(body, &init); err != nil {
|
if err = json.Unmarshal(body, &init); err != nil {
|
||||||
sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
|
wfe.sendError(response, "Error unmarshaling JSON", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new authz and return
|
// Create new authz and return
|
||||||
authz, err := wfe.RA.NewAuthorization(init, key)
|
authz, err := wfe.RA.NewAuthorization(init, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Error creating new authz: %+v", err),
|
fmt.Sprintf("Error creating new authz: %+v", err),
|
||||||
http.StatusInternalServerError)
|
http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
|
@ -223,7 +224,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
||||||
authz.ID = ""
|
authz.ID = ""
|
||||||
responseBody, err := json.Marshal(authz)
|
responseBody, err := json.Marshal(authz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Error marshaling authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Error marshaling authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,20 +241,20 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
|
||||||
|
|
||||||
func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
|
func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
|
||||||
if request.Method != "POST" {
|
if request.Method != "POST" {
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
body, key, err := verifyPOST(request)
|
body, key, err := verifyPOST(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var init core.CertificateRequest
|
var init core.CertificateRequest
|
||||||
if err = json.Unmarshal(body, &init); err != nil {
|
if err = json.Unmarshal(body, &init); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest)
|
wfe.sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,7 +269,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
|
||||||
// RA for secondary validation.
|
// RA for secondary validation.
|
||||||
cert, err := wfe.RA.NewCertificate(init, key)
|
cert, err := wfe.RA.NewCertificate(init, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Error creating new cert: %+v", err),
|
fmt.Sprintf("Error creating new cert: %+v", err),
|
||||||
http.StatusBadRequest)
|
http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
|
@ -307,7 +308,7 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Unable to find challenge"),
|
fmt.Sprintf("Unable to find challenge"),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
|
@ -315,38 +316,38 @@ func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.Re
|
||||||
|
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
default:
|
default:
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
|
||||||
case "POST":
|
case "POST":
|
||||||
body, key, err := verifyPOST(request)
|
body, key, err := verifyPOST(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var challengeResponse core.Challenge
|
var challengeResponse core.Challenge
|
||||||
if err = json.Unmarshal(body, &challengeResponse); err != nil {
|
if err = json.Unmarshal(body, &challengeResponse); err != nil {
|
||||||
sendError(response, "Error unmarshaling authorization", http.StatusBadRequest)
|
wfe.sendError(response, "Error unmarshaling authorization", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the signing key is the right key
|
// Check that the signing key is the right key
|
||||||
if !key.Equals(authz.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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the RA to update this authorization
|
// Ask the RA to update this authorization
|
||||||
updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse)
|
updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Unable to update authorization", http.StatusInternalServerError)
|
wfe.sendError(response, "Unable to update authorization", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonReply, err := json.Marshal(updatedAuthz)
|
jsonReply, err := json.Marshal(updatedAuthz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Header().Set("Content-Type", "application/json")
|
response.Header().Set("Content-Type", "application/json")
|
||||||
|
|
@ -363,7 +364,7 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
||||||
id := parseIDFromPath(request.URL.Path)
|
id := parseIDFromPath(request.URL.Path)
|
||||||
reg, err := wfe.SA.GetRegistration(id)
|
reg, err := wfe.SA.GetRegistration(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Unable to find registration: %+v", err),
|
fmt.Sprintf("Unable to find registration: %+v", err),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
|
@ -372,13 +373,13 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
||||||
|
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
default:
|
default:
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
|
||||||
case "GET":
|
case "GET":
|
||||||
jsonReply, err := json.Marshal(reg)
|
jsonReply, err := json.Marshal(reg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Header().Set("Content-Type", "application/json")
|
response.Header().Set("Content-Type", "application/json")
|
||||||
|
|
@ -388,20 +389,20 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
||||||
case "POST":
|
case "POST":
|
||||||
body, key, err := verifyPOST(request)
|
body, key, err := verifyPOST(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
wfe.sendError(response, "Unable to read/verify body", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var update core.Registration
|
var update core.Registration
|
||||||
err = json.Unmarshal(body, &update)
|
err = json.Unmarshal(body, &update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Error unmarshaling registration", http.StatusBadRequest)
|
wfe.sendError(response, "Error unmarshaling registration", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the signing key is the right key
|
// Check that the signing key is the right key
|
||||||
if !key.Equals(reg.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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,13 +410,13 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
||||||
updatedReg, err := wfe.RA.UpdateRegistration(reg, update)
|
updatedReg, err := wfe.RA.UpdateRegistration(reg, update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
sendError(response, "Unable to update registration", http.StatusInternalServerError)
|
wfe.sendError(response, "Unable to update registration", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonReply, err := json.Marshal(updatedReg)
|
jsonReply, err := json.Marshal(updatedReg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Header().Set("Content-Type", "application/json")
|
response.Header().Set("Content-Type", "application/json")
|
||||||
|
|
@ -430,7 +431,7 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
|
||||||
id := parseIDFromPath(request.URL.Path)
|
id := parseIDFromPath(request.URL.Path)
|
||||||
authz, err := wfe.SA.GetAuthorization(id)
|
authz, err := wfe.SA.GetAuthorization(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response,
|
wfe.sendError(response,
|
||||||
fmt.Sprintf("Unable to find authorization: %+v", err),
|
fmt.Sprintf("Unable to find authorization: %+v", err),
|
||||||
http.StatusNotFound)
|
http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
|
@ -444,13 +445,13 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
|
||||||
|
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
default:
|
default:
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
|
||||||
case "GET":
|
case "GET":
|
||||||
jsonReply, err := json.Marshal(authz)
|
jsonReply, err := json.Marshal(authz)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
wfe.sendError(response, "Failed to marshal authz", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
response.Header().Set("Content-Type", "application/json")
|
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]+$")
|
var allHex = regexp.MustCompile("^[0-9a-f]+$")
|
||||||
|
|
||||||
func notFound(response http.ResponseWriter) {
|
func (wfe *WebFrontEndImpl) notFound(response http.ResponseWriter) {
|
||||||
sendError(response, "Not found", http.StatusNotFound)
|
wfe.sendError(response, "Not found", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) {
|
func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) {
|
||||||
path := request.URL.Path
|
path := request.URL.Path
|
||||||
switch request.Method {
|
switch request.Method {
|
||||||
default:
|
default:
|
||||||
sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
wfe.sendError(response, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
|
|
||||||
case "GET":
|
case "GET":
|
||||||
// Certificate paths consist of the CertBase path, plus exactly sixteen hex
|
// Certificate paths consist of the CertBase path, plus exactly sixteen hex
|
||||||
// digits.
|
// digits.
|
||||||
if !strings.HasPrefix(path, wfe.CertPath) {
|
if !strings.HasPrefix(path, wfe.CertPath) {
|
||||||
notFound(response)
|
wfe.notFound(response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serial := path[len(wfe.CertPath):]
|
serial := path[len(wfe.CertPath):]
|
||||||
if len(serial) != 16 || !allHex.Match([]byte(serial)) {
|
if len(serial) != 16 || !allHex.Match([]byte(serial)) {
|
||||||
notFound(response)
|
wfe.notFound(response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wfe.log.Notice(fmt.Sprintf("Requested certificate ID %s", serial))
|
wfe.log.Notice(fmt.Sprintf("Requested certificate ID %s", serial))
|
||||||
|
|
||||||
cert, err := wfe.SA.GetCertificate(serial)
|
cert, err := wfe.SA.GetCertificate(serial)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
notFound(response)
|
wfe.notFound(response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue