Merge pull request #67 from letsencrypt/fix-error-encoding

Fix encoding of errors in WFE.
This commit is contained in:
jsha 2015-03-31 15:06:15 -07:00
commit d3c47b7d62
4 changed files with 188 additions and 4 deletions

View File

@ -4,7 +4,7 @@ The node.js scripts in this directory provide a simple end-to-end test of Boulde
```
# Install dependencies
> npm install inquirer cli node-forge
> npm install
# Start cfssl with signing parameters
# (These are the default parameters to use a Yubikey.)

11
test/js/package.json Normal file
View File

@ -0,0 +1,11 @@
{
"author": "ISRG",
"name": "Boulder-test",
"repository": "https://github.com/letsencrypt/boulder",
"version": "0.0.1",
"dependencies": {
"cli": "^0.6.5",
"inquirer": "^0.8.2",
"node-forge": "^0.6.21"
}
}

View File

@ -7,6 +7,7 @@ package wfe
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
@ -45,6 +46,10 @@ func verifyPOST(request *http.Request) ([]byte, jose.JsonWebKey, error) {
zeroKey := jose.JsonWebKey{}
// Read body
if request.Body == nil {
return nil, zeroKey, errors.New("No body on POST")
}
body, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil, zeroKey, err
@ -95,7 +100,7 @@ func sendError(response http.ResponseWriter, message string, code int) {
// https://golang.org/src/net/http/server.go#L1272
response.Header().Set("Content-Type", "application/problem+json")
response.WriteHeader(code)
fmt.Fprintln(response, problemDoc)
response.Write(problemDoc)
}
func link(url, relation string) string {
@ -193,7 +198,6 @@ 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)
return
@ -207,13 +211,20 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
var init core.CertificateRequest
if err = json.Unmarshal(body, &init); err != nil {
fmt.Println(err)
sendError(response, "Error unmarshaling certificate request", http.StatusBadRequest)
return
}
wfe.log.Notice(fmt.Sprintf("Asked to create new certificate: %v %v", init, key))
wfe.log.Notice(fmt.Sprintf("Client requested new certificate: %v %v %v",
request.RemoteAddr, init, key))
// Create new certificate and return
// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
// WFE is compromised, *and* the attacker knows the public key of an account
// authorized for target site, they could cause issuance for that site by
// lying to the RA. We should probably pass a copy of the whole rquest to the
// RA for secondary validation.
cert, err := wfe.RA.NewCertificate(init, key)
if err != nil {
sendError(response,

162
wfe/web-front-end_test.go Normal file
View File

@ -0,0 +1,162 @@
// Copyright 2014 ISRG. All rights reserved
// 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/.
package wfe
import (
"log/syslog"
"testing"
"io/ioutil"
"io"
"strings"
"net/http"
"net/http/httptest"
"github.com/letsencrypt/boulder/ra"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/test"
)
func makeBody(s string) (io.ReadCloser) {
return ioutil.NopCloser(strings.NewReader(s))
}
// TODO: Write additional test cases for:
// - RA returns with a cert success
// - RA returns with a failure
func TestIssueCertificate(t *testing.T) {
syslogger, _ := syslog.New(syslog.LOG_DEBUG, "test")
log := blog.AuditLogger{syslogger}
// TODO: Use a mock RA so we can test various conditions of authorized, not authorized, etc.
ra := ra.NewRegistrationAuthorityImpl(&log)
wfe := NewWebFrontEndImpl(&log)
wfe.RA = &ra
responseWriter := httptest.NewRecorder()
// GET instead of POST should be rejected
wfe.NewCertificate(responseWriter, &http.Request{
Method: "GET",
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Method not allowed\"}")
// POST, but no body.
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Unable to read/verify body\"}")
// POST, but body that isn't valid JWS
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Body: makeBody("hi"),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Unable to read/verify body\"}")
// POST, Properly JWS-signed, but payload is "foo", not base64-encoded JSON.
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "Zm9vCg",
"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Error unmarshaling certificate request\"}")
// Same signed body, but payload modified by one byte, breaking signature.
// should fail JWS verification.
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
}
},
"payload": "xm9vCg",
"signature": "RjUQ679fxJgeAJlxqgvDP_sfGZnJ-1RgWF2qmcbnBWljs6h1qp63pLnJOl13u81bP_bCSjaWkelGG8Ymx_X-aQ"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Unable to read/verify body\"}")
// Valid, signed JWS body, payload is '{}'
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "e30K",
"signature": "JXYA_pin91Bc5oz5I6dqCNNWDrBaYTB31EnWorrj4JEFRaidafC9mpLDLLA9jR9kX_Vy2bA5b6pPpXVKm0w146a0L551OdL8JrrLka9q6LypQdDLLQa76XD03hSBOFcC-Oo5FLPa3WRWS1fQ37hYAoLxtS3isWXMIq_4Onx5bq8bwKyu-3E3fRb_lzIZ8hTIWwcblCTOfufUe6AoK4m6MfBjz0NGhyyk4lEZZw6Sttm2VuZo3xmWoRTJEyJG5AOJ6fkNJ9iQQ1kVhMr0ZZ7NVCaOZAnxrwv2sCjY6R3f4HuEVe1yzT75Mq2IuXq-tadGyFujvUxF6BWHCulbEnss7g"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"detail\":\"Error unmarshaling certificate request\"}")
// Valid, signed JWS body, payload has a legit CSR but no authorizations:
// {
// "csr": "MIICU...",
// "authorizations: []
// }
// Payload was created by: openssl req -new -nodes -subj /CN=foo
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Body: makeBody(`
{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "ICAgIHsKICAgICAgImNzciI6ICJNSUlDVXpDQ0FUc0NBUUF3RGpFTU1Bb0dBMVVFQXd3RFptOXZNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTNVV2NlMlBZOXk4bjRCN2pPazNEWFpudTJwVWdMcXM3YTVEelJCeG5QcUw3YXhpczZ0aGpTQkkyRk83dzVDVWpPLW04WGpELUdZV2dmWGViWjNhUVZsQmlZcWR4WjNVRzZSRHdFYkJDZUtvN3Y4Vy1VVWZFU05OQ1hGODc0ZGRoSm1FdzBSRjBZV1NBRWN0QVlIRUdvUEZ6NjlnQ3FsNnhYRFBZMU9scE1BcmtJSWxxOUVaV3dUMDgxZWt5SnYwR1lSZlFpZ0NNSzRiMWdrRnZLc0hqYTktUTV1MWIwQVp5QS1tUFR1Nno1RVdrQjJvbmhBWHdXWFg5MHNmVWU4RFNldDlyOUd4TWxuM2xnWldUMXpoM1JNWklMcDBVaGgzTmJYbkE4SkludWtoYTNIUE84V2dtRGQ0SzZ1QnpXc28wQTZmcDVOcFgyOFpwS0F3TTVpUWx0UUlEQVFBQm9BQXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRkdKVjNPY2doSkVadk9faEd0SWRhUm5zdTZlWDNDZXFTMGJZY0VFemE4dml6bGo0eDA5bnRNSDNRb29xUE9qOHN1dWwwdkQ3NUhaVHB6NkZIRTdTeUxlTktRQkdOR3AxUE1XbVhzRnFENnhVUkN5TUh2Q1pvSHlucENyN0Q1SHR6SXZ1OWZBVjdYUks3cUJLWGZSeGJ2MjFxMHlzTVduZndrYlMyd3JzMXdBelBQZzRpR0pxOHVWSXRybGNGTDhidUpMenh2S2EzbHVfT2p4TlhqemRFdDNWVmtvLUFLUzFzd2tZRWhzR3dLZDhaek5icEYySVEtb2tYZ1JfWmVjeVc4dDgzcFYtdzMzR2hETDl3NlJMUk1nU001YW9qeThyaTdZSW9JdmMzLTlrbGJ3Mmt3WTVvTTJsbWhvSU9HVTEwVGtFeW4xOG15eV81R1VFR2hOelBBPSIsCiAgICAgICJhdXRob3JpemF0aW9ucyI6IFtdCiAgICB9Cg",
"signature": "PxtFtDXR74ZDgZUWsNaMFpFAhJrYtCYpl3-vr9SCwuWIxB9hZCnLWB5JFwNuC9CtTSYXqDJhzPs4-Bzh345HdwO-ifu1EIVxmc3bAszYS-cxA0lDzr8wJ0ldX0WvADshRWaeFYWJja7ggW03k5JZiNa9AigKIvkGBS2YWpEpCo954cdCEmIL3UOdVjN9aXRT7zzC9wczv4-hYDR-6uP_8J6ATUXJ-UJaTnMi3R0cwtHIcTBZgtgGspoCbtgv-3KaAGNkm5AY062xO5_GbefWwuD2hd8AjKyoTLdfQtwadu6Q3Zl6ZzW_eAfQVDnoblgSt19Gtm4HP4Rf_GosGjRMog"
}
`),
})
test.AssertEquals(t,
responseWriter.Body.String(),
// TODO: I think this is wrong. The CSR in the payload above was created by openssl and should be valid.
"{\"detail\":\"Error creating new cert: Invalid signature on CSR\"}")
}