Merge pull request #67 from letsencrypt/fix-error-encoding
Fix encoding of errors in WFE.
This commit is contained in:
commit
d3c47b7d62
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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\"}")
|
||||
}
|
||||
Loading…
Reference in New Issue