Merge branch 'master' into index-fix

This commit is contained in:
Jeff Hodges 2015-09-10 12:37:03 -07:00
commit def66fc85d
14 changed files with 141 additions and 103 deletions

View File

@ -59,7 +59,6 @@ type CertificateAuthorityImpl struct {
ValidityPeriod time.Duration ValidityPeriod time.Duration
NotAfter time.Time NotAfter time.Time
MaxNames int MaxNames int
MaxKeySize int
} }
// NewCertificateAuthorityImpl creates a CA that talks to a remote CFSSL // NewCertificateAuthorityImpl creates a CA that talks to a remote CFSSL
@ -239,7 +238,7 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
ca.log.AuditErr(err) ca.log.AuditErr(err)
return emptyCert, err return emptyCert, err
} }
if err = core.GoodKey(key, ca.MaxKeySize); err != nil { if err = core.GoodKey(key); err != nil {
err = fmt.Errorf("Invalid public key in CSR: %s", err.Error()) err = fmt.Errorf("Invalid public key in CSR: %s", err.Error())
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
ca.log.AuditErr(err) ca.log.AuditErr(err)

View File

@ -212,7 +212,6 @@ func TestRevoke(t *testing.T) {
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
csr, _ := x509.ParseCertificateRequest(CNandSANCSR) csr, _ := x509.ParseCertificateRequest(CNandSANCSR)
certObj, err := ca.IssueCertificate(*csr, ctx.reg.ID, FarFuture) certObj, err := ca.IssueCertificate(*csr, ctx.reg.ID, FarFuture)
@ -253,7 +252,6 @@ func TestIssueCertificate(t *testing.T) {
test.AssertNotError(t, err, "Failed to create CA") test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
/* /*
// Uncomment to test with a local signer // Uncomment to test with a local signer
@ -330,7 +328,6 @@ func TestRejectNoName(t *testing.T) {
test.AssertNotError(t, err, "Failed to create CA") test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
// Test that the CA rejects CSRs with no names // Test that the CA rejects CSRs with no names
csr, _ := x509.ParseCertificateRequest(NoNameCSR) csr, _ := x509.ParseCertificateRequest(NoNameCSR)
@ -361,7 +358,6 @@ func TestDeduplication(t *testing.T) {
test.AssertNotError(t, err, "Failed to create CA") test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
// Test that the CA collapses duplicate names // Test that the CA collapses duplicate names
csr, _ := x509.ParseCertificateRequest(DupeNameCSR) csr, _ := x509.ParseCertificateRequest(DupeNameCSR)
@ -391,7 +387,6 @@ func TestRejectValidityTooLong(t *testing.T) {
test.AssertNotError(t, err, "Failed to create CA") test.AssertNotError(t, err, "Failed to create CA")
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
// Test that the CA rejects CSRs that would expire after the intermediate cert // Test that the CA rejects CSRs that would expire after the intermediate cert
csr, _ := x509.ParseCertificateRequest(NoCNCSR) csr, _ := x509.ParseCertificateRequest(NoCNCSR)
@ -411,7 +406,6 @@ func TestShortKey(t *testing.T) {
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile) ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
// Test that the CA rejects CSRs that would expire after the intermediate cert // Test that the CA rejects CSRs that would expire after the intermediate cert
csr, _ := x509.ParseCertificateRequest(ShortKeyCSR) csr, _ := x509.ParseCertificateRequest(ShortKeyCSR)
@ -425,7 +419,6 @@ func TestRejectBadAlgorithm(t *testing.T) {
ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile) ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile)
ca.PA = ctx.pa ca.PA = ctx.pa
ca.SA = ctx.sa ca.SA = ctx.sa
ca.MaxKeySize = 4096
// Test that the CA rejects CSRs that would expire after the intermediate cert // Test that the CA rejects CSRs that would expire after the intermediate cert
csr, _ := x509.ParseCertificateRequest(BadAlgorithmCSR) csr, _ := x509.ParseCertificateRequest(BadAlgorithmCSR)

View File

@ -46,7 +46,6 @@ func main() {
cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, clock.Default(), c.Common.IssuerCert) cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, clock.Default(), c.Common.IssuerCert)
cmd.FailOnError(err, "Failed to create CA impl") cmd.FailOnError(err, "Failed to create CA impl")
cai.MaxKeySize = c.Common.MaxKeySize
cai.PA = pa cai.PA = pa
go cmd.ProfileCmd("CA", stats) go cmd.ProfileCmd("CA", stats)

View File

@ -45,7 +45,6 @@ func main() {
rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger) rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger)
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
rai.MaxKeySize = c.Common.MaxKeySize
rai.PA = pa rai.PA = pa
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout) raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse RA DNS timeout") cmd.FailOnError(err, "Couldn't parse RA DNS timeout")

View File

@ -177,7 +177,6 @@ type Config struct {
BaseURL string BaseURL string
// Path to a PEM-encoded copy of the issuer certificate. // Path to a PEM-encoded copy of the issuer certificate.
IssuerCert string IssuerCert string
MaxKeySize int
DNSResolver string DNSResolver string
DNSTimeout string DNSTimeout string

View File

@ -42,17 +42,17 @@ var (
// key use (our requirements are the same for either one), according to basic // key use (our requirements are the same for either one), according to basic
// strength and algorithm checking. // strength and algorithm checking.
// TODO: Support JsonWebKeys once go-jose migration is done. // TODO: Support JsonWebKeys once go-jose migration is done.
func GoodKey(key crypto.PublicKey, maxKeySize int) error { func GoodKey(key crypto.PublicKey) error {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
switch t := key.(type) { switch t := key.(type) {
case rsa.PublicKey: case rsa.PublicKey:
return GoodKeyRSA(t, maxKeySize) return GoodKeyRSA(t)
case *rsa.PublicKey: case *rsa.PublicKey:
return GoodKeyRSA(*t, maxKeySize) return GoodKeyRSA(*t)
case ecdsa.PublicKey: case ecdsa.PublicKey:
return GoodKeyECDSA(t, maxKeySize) return GoodKeyECDSA(t)
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
return GoodKeyECDSA(*t, maxKeySize) return GoodKeyECDSA(*t)
default: default:
err := MalformedRequestError(fmt.Sprintf("Unknown key type %s", reflect.TypeOf(key))) err := MalformedRequestError(fmt.Sprintf("Unknown key type %s", reflect.TypeOf(key)))
log.Debug(err.Error()) log.Debug(err.Error())
@ -61,7 +61,7 @@ func GoodKey(key crypto.PublicKey, maxKeySize int) error {
} }
// GoodKeyECDSA determines if an ECDSA pubkey meets our requirements // GoodKeyECDSA determines if an ECDSA pubkey meets our requirements
func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) { func GoodKeyECDSA(key ecdsa.PublicKey) (err error) {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
err = NotSupportedError("ECDSA keys not yet supported") err = NotSupportedError("ECDSA keys not yet supported")
log.Debug(err.Error()) log.Debug(err.Error())
@ -69,12 +69,13 @@ func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) {
} }
// GoodKeyRSA determines if a RSA pubkey meets our requirements // GoodKeyRSA determines if a RSA pubkey meets our requirements
func GoodKeyRSA(key rsa.PublicKey, maxKeySize int) (err error) { func GoodKeyRSA(key rsa.PublicKey) (err error) {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
// Baseline Requirements Appendix A // Baseline Requirements Appendix A
// Modulus must be >= 2048 bits and < maxKeySize // Modulus must be >= 2048 bits and <= 4096 bits
modulus := key.N modulus := key.N
modulusBitLen := modulus.BitLen() modulusBitLen := modulus.BitLen()
const maxKeySize = 4096
if modulusBitLen < 2048 { if modulusBitLen < 2048 {
err = MalformedRequestError(fmt.Sprintf("Key too small: %d", modulusBitLen)) err = MalformedRequestError(fmt.Sprintf("Key too small: %d", modulusBitLen))
log.Debug(err.Error()) log.Debug(err.Error())

View File

@ -15,31 +15,29 @@ import (
"github.com/letsencrypt/boulder/test" "github.com/letsencrypt/boulder/test"
) )
var maxKeySize = 2048
func TestUnknownKeyType(t *testing.T) { func TestUnknownKeyType(t *testing.T) {
notAKey := struct{}{} notAKey := struct{}{}
test.AssertError(t, GoodKey(notAKey, maxKeySize), "Should have rejected a key of unknown type") test.AssertError(t, GoodKey(notAKey), "Should have rejected a key of unknown type")
} }
func TestWrongKeyType(t *testing.T) { func TestWrongKeyType(t *testing.T) {
ecdsaKey := ecdsa.PublicKey{} ecdsaKey := ecdsa.PublicKey{}
test.AssertError(t, GoodKey(&ecdsaKey, maxKeySize), "Should have rejected ECDSA key.") test.AssertError(t, GoodKey(&ecdsaKey), "Should have rejected ECDSA key.")
test.AssertError(t, GoodKey(ecdsaKey, maxKeySize), "Should have rejected ECDSA key.") test.AssertError(t, GoodKey(ecdsaKey), "Should have rejected ECDSA key.")
} }
func TestSmallModulus(t *testing.T) { func TestSmallModulus(t *testing.T) {
private, err := rsa.GenerateKey(rand.Reader, 2040) private, err := rsa.GenerateKey(rand.Reader, 2040)
test.AssertNotError(t, err, "Error generating key") test.AssertNotError(t, err, "Error generating key")
test.AssertError(t, GoodKey(&private.PublicKey, maxKeySize), "Should have rejected too-short key.") test.AssertError(t, GoodKey(&private.PublicKey), "Should have rejected too-short key.")
test.AssertError(t, GoodKey(private.PublicKey, maxKeySize), "Should have rejected too-short key.") test.AssertError(t, GoodKey(private.PublicKey), "Should have rejected too-short key.")
} }
func TestLargeModulus(t *testing.T) { func TestLargeModulus(t *testing.T) {
private, err := rsa.GenerateKey(rand.Reader, maxKeySize+1) private, err := rsa.GenerateKey(rand.Reader, 4097)
test.AssertNotError(t, err, "Error generating key") test.AssertNotError(t, err, "Error generating key")
test.AssertError(t, GoodKey(&private.PublicKey, maxKeySize), "Should have rejected too-long key.") test.AssertError(t, GoodKey(&private.PublicKey), "Should have rejected too-long key.")
test.AssertError(t, GoodKey(private.PublicKey, maxKeySize), "Should have rejected too-long key.") test.AssertError(t, GoodKey(private.PublicKey), "Should have rejected too-long key.")
} }
func TestSmallExponent(t *testing.T) { func TestSmallExponent(t *testing.T) {
@ -48,7 +46,7 @@ func TestSmallExponent(t *testing.T) {
N: bigOne.Lsh(bigOne, 2048), N: bigOne.Lsh(bigOne, 2048),
E: 5, E: 5,
} }
test.AssertError(t, GoodKey(&key, maxKeySize), "Should have rejected small exponent.") test.AssertError(t, GoodKey(&key), "Should have rejected small exponent.")
} }
func TestEvenExponent(t *testing.T) { func TestEvenExponent(t *testing.T) {
@ -57,7 +55,7 @@ func TestEvenExponent(t *testing.T) {
N: bigOne.Lsh(bigOne, 2048), N: bigOne.Lsh(bigOne, 2048),
E: 1 << 17, E: 1 << 17,
} }
test.AssertError(t, GoodKey(&key, maxKeySize), "Should have rejected even exponent.") test.AssertError(t, GoodKey(&key), "Should have rejected even exponent.")
} }
func TestEvenModulus(t *testing.T) { func TestEvenModulus(t *testing.T) {
@ -66,7 +64,7 @@ func TestEvenModulus(t *testing.T) {
N: bigOne.Lsh(bigOne, 2048), N: bigOne.Lsh(bigOne, 2048),
E: (1 << 17) + 1, E: (1 << 17) + 1,
} }
test.AssertError(t, GoodKey(&key, maxKeySize), "Should have rejected even modulus.") test.AssertError(t, GoodKey(&key), "Should have rejected even modulus.")
} }
func TestModulusDivisibleBy752(t *testing.T) { func TestModulusDivisibleBy752(t *testing.T) {
@ -78,11 +76,11 @@ func TestModulusDivisibleBy752(t *testing.T) {
N: N, N: N,
E: (1 << 17) + 1, E: (1 << 17) + 1,
} }
test.AssertError(t, GoodKey(&key, maxKeySize), "Should have rejected modulus divisible by 751.") test.AssertError(t, GoodKey(&key), "Should have rejected modulus divisible by 751.")
} }
func TestGoodKey(t *testing.T) { func TestGoodKey(t *testing.T) {
private, err := rsa.GenerateKey(rand.Reader, 2048) private, err := rsa.GenerateKey(rand.Reader, 2048)
test.AssertNotError(t, err, "Error generating key") test.AssertNotError(t, err, "Error generating key")
test.AssertNotError(t, GoodKey(&private.PublicKey, maxKeySize), "Should have accepted good key.") test.AssertNotError(t, GoodKey(&private.PublicKey), "Should have accepted good key.")
} }

View File

@ -17,10 +17,12 @@ import (
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"hash" "hash"
"io" "io"
"io/ioutil"
"math/big" "math/big"
"net/url" "net/url"
"strings" "strings"
@ -347,3 +349,17 @@ func UniqueNames(names []string) (unique []string) {
} }
return return
} }
// LoadCert loads a PEM certificate specified by filename or returns a error
func LoadCert(filename string) (cert *x509.Certificate, err error) {
certPEM, err := ioutil.ReadFile(filename)
if err != nil {
return
}
block, _ := pem.Decode(certPEM)
if block == nil {
return nil, fmt.Errorf("No data in cert PEM file %s", filename)
}
cert, err = x509.ParseCertificate(block.Bytes)
return
}

View File

@ -32,8 +32,7 @@ type RegistrationAuthorityImpl struct {
clk clock.Clock clk clock.Clock
log *blog.AuditLogger log *blog.AuditLogger
AuthzBase string AuthzBase string
MaxKeySize int
} }
// NewRegistrationAuthorityImpl constructs a new RA object. // NewRegistrationAuthorityImpl constructs a new RA object.
@ -96,7 +95,7 @@ type certificateRequestEvent struct {
// NewRegistration constructs a new Registration from a request. // NewRegistration constructs a new Registration from a request.
func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
if err = core.GoodKey(init.Key.Key, ra.MaxKeySize); err != nil { if err = core.GoodKey(init.Key.Key); err != nil {
return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error()))
} }
reg = core.Registration{ reg = core.Registration{

View File

@ -196,7 +196,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
DB: cadb, DB: cadb,
ValidityPeriod: time.Hour * 2190, ValidityPeriod: time.Hour * 2190,
NotAfter: time.Now().Add(time.Hour * 8761), NotAfter: time.Now().Add(time.Hour * 8761),
MaxKeySize: 4096,
Clk: fc, Clk: fc,
} }
cleanUp := func() { cleanUp := func() {
@ -216,7 +215,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut
ra.CA = &ca ra.CA = &ca
ra.PA = pa ra.PA = pa
ra.AuthzBase = "http://acme.invalid/authz/" ra.AuthzBase = "http://acme.invalid/authz/"
ra.MaxKeySize = 4096
ra.DNSResolver = &mocks.MockDNS{} ra.DNSResolver = &mocks.MockDNS{}
AuthzInitial.RegistrationID = Registration.ID AuthzInitial.RegistrationID = Registration.ID

View File

@ -170,7 +170,6 @@
"common": { "common": {
"baseURL": "http://localhost:4000", "baseURL": "http://localhost:4000",
"issuerCert": "test/test-ca.pem", "issuerCert": "test/test-ca.pem",
"maxKeySize": 4096,
"dnsResolver": "127.0.0.1:8053", "dnsResolver": "127.0.0.1:8053",
"dnsTimeout": "10s", "dnsTimeout": "10s",
"dnsAllowLoopbackAddresses": true "dnsAllowLoopbackAddresses": true

View File

@ -0,0 +1,29 @@
Produced by:
js test.js --agree --email jsha@newview.org --domains not-an-example.com --certFile cert.der --certKey ../../wfe/test/178.key
openssl x509 -text -inform der -in cert.der -outform pem -out ../../wfe/test/not-an-example.com.crt
-----BEGIN CERTIFICATE-----
MIIEYzCCA0ugAwIBAgIRAP8AAAAAAAAOS09n2G6BjEYwDQYJKoZIhvcNAQELBQAw
HzEdMBsGA1UEAwwUaGFwcHkgaGFja2VyIGZha2UgQ0EwHhcNMTUwOTA5MjI1NjAw
WhcNMTUxMjA4MjI1NjAwWjAdMRswGQYDVQQDExJub3QtYW4tZXhhbXBsZS5jb20w
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaqzue57mgXEoGTZZoVkkC
ZraebWgXI8irX2BgQB1A3iZa9onxGPMcWQMxhSuUisbEJi4UkMcVST12HX01rUwh
j41UuBxJvI1w4wvdstssTAaa9c9tsQ5+UED2bFRL1MsyBdbmCF/+pu3i+ZIYqWgi
KbjVBe3nlAVbo77zizwp3Y4Tp1/TBOwTAuFkHePmkNT63uPm9My/hNzsSm1o+Q51
9Cf7ry+JQmOVgz/jIgFVGFYJ17EV3KUIpUuDShuyCFATBQspgJSN2DoXRUlQjXXk
NTj23OxxdT/cVLcLJjytyG6e5izME2R2aCkDBWIc1a4/sRJ0R396auPXG6KhJ7o/
AgMBAAGjggGaMIIBljAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH
AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFBGkJtghSl97/bTR
dyy0TaneZhnDMB8GA1UdIwQYMBaAFPt4TxL5YBWDLJ8XfzQZsy426kGJMGoGCCsG
AQUFBwEBBF4wXDAmBggrBgEFBQcwAYYaaHR0cDovL2xvY2FsaG9zdDo0MDAyL29j
c3AwMgYIKwYBBQUHMAKGJmh0dHA6Ly9sb2NhbGhvc3Q6NDAwMC9hY21lL2lzc3Vl
ci1jZXJ0MB0GA1UdEQQWMBSCEm5vdC1hbi1leGFtcGxlLmNvbTAnBgNVHR8EIDAe
MBygGqAYhhZodHRwOi8vZXhhbXBsZS5jb20vY3JsMGMGA1UdIARcMFowCgYGZ4EM
AQIBMAAwTAYDKgMEMEUwIgYIKwYBBQUHAgEWFmh0dHA6Ly9leGFtcGxlLmNvbS9j
cHMwHwYIKwYBBQUHAgIwEwwRRG8gV2hhdCBUaG91IFdpbHQwDQYJKoZIhvcNAQEL
BQADggEBAJTSscrGO1ymwZ+rMF+mfVeHfplfyMzZ/6SZyvaYgO9DLr42KIETdHBg
Y9AZ6aOKboN/hY98kb9mQ0BpOCsSaCkgTsqCjw3szsRd/FMgUSVn36vFpbX2f5oD
gF40N/51EN5Efbe7aN4Oxmcgijh4IY2sczcskJixAd9T/hjVtv160LJ0xcHRrfji
u/Tc2E0q+E5k4V91D2HajwU6qcGbap02JI+pX/Oq4S36yfggIUyowmXQw4nm1cb0
cFXwrMzg+XtDHj+Ex+yBlauq+MP1rjXiHrNIO2hIiyRU9jdxfITAE4DmqEzEBZKY
NORfB6suv4wLnAlsLbPJEdsraq4/IiU=
-----END CERTIFICATE-----

View File

@ -344,6 +344,11 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res
wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error()))
return nil, nil, reg, puberr return nil, nil, reg, puberr
} }
if key == nil {
err = core.SignatureValidationError("No JWK in JWS header")
wfe.log.Debug(err.Error())
return nil, nil, reg, err
}
// Check that the request has a known anti-replay nonce // Check that the request has a known anti-replay nonce
// i.e., Nonce is in protected header and // i.e., Nonce is in protected header and
@ -713,16 +718,27 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
return return
} }
var init core.CertificateRequest var certificateRequest core.CertificateRequest
if err = json.Unmarshal(body, &init); err != nil { if err = json.Unmarshal(body, &certificateRequest); err != nil {
logEvent.Error = err.Error() logEvent.Error = err.Error()
wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest) wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
return return
} }
wfe.logCsr(request.RemoteAddr, init, reg) wfe.logCsr(request.RemoteAddr, certificateRequest, reg)
logEvent.Extra["CSRDNSNames"] = init.CSR.DNSNames // Check that the key in the CSR is good. This will also be checked in the CA
logEvent.Extra["CSREmailAddresses"] = init.CSR.EmailAddresses // component, but we want to discard CSRs with bad keys as early as possible
logEvent.Extra["CSRIPAddresses"] = init.CSR.IPAddresses // because (a) it's an easy check and we can save unnecessary requests and
// bytes on the wire, and (b) the CA logs all rejections as audit events, but
// a bad key from the client is just a malformed request and doesn't need to
// be audited.
if err = core.GoodKey(certificateRequest.CSR.PublicKey); err != nil {
logEvent.Error = err.Error()
wfe.sendError(response, "Invalid key in certificate request", err, http.StatusBadRequest)
return
}
logEvent.Extra["CSRDNSNames"] = certificateRequest.CSR.DNSNames
logEvent.Extra["CSREmailAddresses"] = certificateRequest.CSR.EmailAddresses
logEvent.Extra["CSRIPAddresses"] = certificateRequest.CSR.IPAddresses
// Create new certificate and return // Create new certificate and return
// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the // TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
@ -730,7 +746,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
// authorized for target site, they could cause issuance for that site by // 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 // lying to the RA. We should probably pass a copy of the whole rquest to the
// RA for secondary validation. // RA for secondary validation.
cert, err := wfe.RA.NewCertificate(init, reg.ID) cert, err := wfe.RA.NewCertificate(certificateRequest, reg.ID)
if err != nil { if err != nil {
logEvent.Error = err.Error() logEvent.Error = err.Error()
wfe.sendError(response, "Error creating new cert", err, statusCodeFromError(err)) wfe.sendError(response, "Error creating new cert", err, statusCodeFromError(err))

View File

@ -10,7 +10,6 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"database/sql" "database/sql"
"encoding/hex"
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"errors" "errors"
@ -112,22 +111,6 @@ TkLlEeVOuQfxTadw05gzKX0jKkMC4igGxvEeilYc6NR6a4nvRulG84Q8VV9Sy9Ie
wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA== wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA==
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `
// Cert generated by Go:
// * Randomly generated key
// * CN = lets-encrypt
// * DNSNames = not-an-example.com
// Used for NewCertificate tests
GoodTestCert = "3082013e3081eba003020102020100300b06092a864886f70d01010b300030221" +
"80f32303539313131303233303030305a180f3230353931313130323330303030" +
"5a3000305c300d06092a864886f70d0101010500034b003048024100e5d1cc1f6" +
"10d20913d88e5bba1f327d32450fa650c6fa8d084b710d883f3372008cf97bc41" +
"2cb1ed3a0b28516fa839073f40b061fdb616b1b33181d28d91a5a90203010001a" +
"34e304c301d0603551d250416301406082b0601050507030106082b0601050507" +
"0302300c0603551d130101ff04023000301d0603551d110416301482126e6f742" +
"d616e2d6578616d706c652e636f6d300b06092a864886f70d01010b0341008cf8" +
"f349efa6d2fadbaf8ed9ba67e5a9b98c3d5a13c06297c4cf36dc76f494e8887e3" +
"5dd9c885526136d810fc7640f5ba56281e2b75fa3ff7c91a7d23bab7fd4"
) )
type MockSA struct { type MockSA struct {
@ -304,11 +287,15 @@ func (ra *MockRegistrationAuthority) OnValidationUpdate(authz core.Authorization
type MockCA struct{} type MockCA struct{}
func (ca *MockCA) IssueCertificate(csr x509.CertificateRequest, regID int64, earliestExpiry time.Time) (cert core.Certificate, err error) { func (ca *MockCA) IssueCertificate(csr x509.CertificateRequest, regID int64, earliestExpiry time.Time) (core.Certificate, error) {
// Return a basic certificate so NewCertificate can continue // Return a basic certificate so NewCertificate can continue
randomCertDer, _ := hex.DecodeString(GoodTestCert) certPtr, err := core.LoadCert("test/not-an-example.com.crt")
cert.DER = randomCertDer if err != nil {
return return core.Certificate{}, err
}
return core.Certificate{
DER: certPtr.Raw,
}, nil
} }
func (ca *MockCA) GenerateOCSP(xferObj core.OCSPSigningRequest) (ocsp []byte, err error) { func (ca *MockCA) GenerateOCSP(xferObj core.OCSPSigningRequest) (ocsp []byte, err error) {
@ -361,6 +348,11 @@ func setupWFE(t *testing.T) WebFrontEndImpl {
wfe.SubscriberAgreementURL = agreementURL wfe.SubscriberAgreementURL = agreementURL
wfe.log.SyslogWriter = mocks.NewSyslogWriter() wfe.log.SyslogWriter = mocks.NewSyslogWriter()
wfe.RA = &MockRegistrationAuthority{}
wfe.SA = &MockSA{}
wfe.Stats, _ = statsd.NewNoopClient()
wfe.SubscriberAgreementURL = agreementURL
return wfe return wfe
} }
@ -630,46 +622,28 @@ func TestIssueCertificate(t *testing.T) {
`{"type":"urn:acme:error:malformed","detail":"Error unmarshaling certificate request"}`) `{"type":"urn:acme:error:malformed","detail":"Error unmarshaling certificate request"}`)
// Valid, signed JWS body, payload has a invalid signature on CSR and no authorizations: // Valid, signed JWS body, payload has a invalid signature on CSR and no authorizations:
// { // alias b64url="base64 -w0 | sed -e 's,+,-,g' -e 's,/,_,g'"
// "csr": "MIICU...", // openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=foo.com | \
// "authorizations: [] // sed 's/foo.com/fob.com/' | b64url
// }
responseWriter.Body.Reset() responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, wfe.NewCertificate(responseWriter,
makePostRequest(signRequest(t, `{ makePostRequest(signRequest(t, `{
"resource":"new-cert", "resource":"new-cert",
"csr": "MIICUzCCATsCAQAwDjEMMAoGA1UEAwwDZm9vMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UWce2PY9y8n4B7jOk3DXZnu2pUgLqs7a5DzRBxnPqL7axis6thjSBI2FO7w5CUjO-m8XjD-GYWgfXebZ3aQVlBiYqdxZ3UG6RDwEbBCeKo7v8W-UUfESNNCXF874ddhJmEw0RF0YWSAEctAYHEGoPFz69gCql6xXDPY1OlpMArkIIlq9EZWwT081ekyJv0GYRfQigCMK4b1gkFvKsHja9-Q5u1b0AZyA-mPTu6z5EWkB2onhAXwWXX90sfUe8DSet9r9GxMln3lgZWT1zh3RMZILp0Uhh3NbXnA8JInukha3HPO8WgmDd4K6uBzWso0A6fp5NpX28ZpKAwM5iQltQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAFGJV3OcghJEZvO_hGtIdaRnsu6eX3CeqS0bYcEEza8vizlj4x09ntMH3QooqPOj8suul0vD75HZTpz6FHE7SyLeNKQBGNGp1PMWmXsFqD6xURCyMHvCZoHynpCr7D5HtzIvu9fAV7XRK7qBKXfRxbv21q0ysMWnfwkbS2wrs1wAzPPg4iGJq8uVItrlcFL8buJLzxvKa3lu_OjxNXjzdEt3VVko-AKS1swkYEhsGwKd8ZzNbpF2IQ-okXgR_ZecyW8t83pV-w33GhDL9w6RLRMgSM5aojy8ri7YIoIvc3-9klbw2kwY5oM2lmhoIOGU10TkEyn18myy_5GUEGhNzPA=", "csr": "MIICVzCCAT8CAQAwEjEQMA4GA1UEAwwHZm9iLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKzHhqcMSTVjBu61vufGVmIYM4mMbWXgndHOUWnIqSKcNtFtPQ465tcZRT5ITIZWXGjsmgDrj31qvG3t5qLwyaF5hsTvFHK72nLMAQhdgM6481Qe9yaoaulWpkGr_9LVz4jQ9pGAaLVamXGpSxV-ipTOo79Sev4aZE8ksD9atEfWtcOD9w8_zj74vpWjTAHN49Q88chlChVqakn0zSfHPfS-jF8g0UTddBuF0Ti3sZChjxzbo6LwZ4182xX7XPnOLav3AGj0Su7j5XMl3OpenOrlWulWJeZIHq5itGW321j306XiGdbrdWH4K7JygICFds6oolwQRGBY6yinAtCgkTcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBxPiHOtKuBxtvecMNtLkTSuTyEkusQGnjoFDaKe5oqwGYQgy0YBii2-BbaPmqS4ZaDc-vDz_RLeKH5ZiH-NliYR1V_CRtpFLQi18g_2pLQnZLVO3ENs-SM37nU_nBGn9O93t2bkssoM3fZmtgp3R2W7I_wvx7Z8oWKa4boTeBAg_q9Gmi6QskZBddK7A4S_vOR0frU6QSPK_ksPhvovp9fwb6CVKrlJWf556UwRPWgbkW39hvTxK2KHhrUEg3oawNkWde2jZtnZ9e-9zpw8-_5O0X7-YN0ucbFTfQybce_ReuLlGepiHT5bvVavBZoIvqw1XOgSMvGgZFU8tAWMBlj"
"authorizations": []
}`, &wfe.nonceService))) }`, &wfe.nonceService)))
test.AssertEquals(t, test.AssertEquals(t,
responseWriter.Body.String(), responseWriter.Body.String(),
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Invalid signature on CSR"}`) `{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Invalid signature on CSR"}`)
// Valid, signed JWS body, payload has a CSR with no DNS names
mockLog.Clear()
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter,
makePostRequest((signRequest(t, `{
"resource":"new-cert",
"csr": "MIIBBTCBsgIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAsr76ZkU2RTqi41eHfmpE5htDvkr202yjRS8x2M5yzT52ooT2WEVtnSuim0YfOEw6f-fHmbqsasqKmqlsJdgz2QIDAQABoAAwCwYJKoZIhvcNAQEFA0EAHkCv4kVPJa53ltOGrhpdH0mT04qHUqiTllJPPjxXxn6iwiVYL8nQuhs4Q2758ENoODBuM2F8gH19TIoXlcm3LQ=="
}`, &wfe.nonceService))))
test.AssertEquals(t,
responseWriter.Body.String(),
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Key not authorized for name Oh hi"}`)
assertCsrLogged(t, mockLog)
// Valid, signed JWS body, payload has a valid CSR but no authorizations: // Valid, signed JWS body, payload has a valid CSR but no authorizations:
// { // openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=meep.com | b64url
// "csr": "MIIBK...",
// "authorizations: []
// }
mockLog.Clear() mockLog.Clear()
responseWriter.Body.Reset() responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, wfe.NewCertificate(responseWriter,
makePostRequest(signRequest(t, `{ makePostRequest(signRequest(t, `{
"resource":"new-cert", "resource":"new-cert",
"csr": "MIIBKzCB2AIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqvFEGBNrjAotPbcdTSyDpxsESN0-eYl4TqS0ZLYwLTV-FuPHTPjFiq2oH1BEgmRzjb8YiPVXFMnaOeHE7zuuXQIDAQABoCYwJAYJKoZIhvcNAQkOMRcwFTATBgNVHREEDDAKgghtZWVwLmNvbTALBgkqhkiG9w0BAQUDQQBSEcEq-lMUnzv1DO8jK0hJR8YKc0yV8zuWVfAWN0_dsPg5Ny-OHhtJcOTIrUrLTb_xCU7cjiKxU8i3j1kaT-rt" "csr": "MIICWDCCAUACAQAwEzERMA8GA1UEAwwIbWVlcC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaqzue57mgXEoGTZZoVkkCZraebWgXI8irX2BgQB1A3iZa9onxGPMcWQMxhSuUisbEJi4UkMcVST12HX01rUwhj41UuBxJvI1w4wvdstssTAaa9c9tsQ5-UED2bFRL1MsyBdbmCF_-pu3i-ZIYqWgiKbjVBe3nlAVbo77zizwp3Y4Tp1_TBOwTAuFkHePmkNT63uPm9My_hNzsSm1o-Q519Cf7ry-JQmOVgz_jIgFVGFYJ17EV3KUIpUuDShuyCFATBQspgJSN2DoXRUlQjXXkNTj23OxxdT_cVLcLJjytyG6e5izME2R2aCkDBWIc1a4_sRJ0R396auPXG6KhJ7o_AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEALu046p76aKgvoAEHFINkMTgKokPXf9mZ4IZx_BKz-qs1MPMxVtPIrQDVweBH6tYT7Hfj2naLry6SpZ3vUNP_FYeTFWgW1V03LiqacX-QQgbEYtn99Dt3ScGyzb7EH833ztb3vDJ_-ha_CJplIrg-kHBBrlLFWXhh-I9K1qLRTNpbhZ18ooFde4Sbhkw9o9fKivGhx9aYr7ZbjRsNtKit_DsG1nwEXz53TMJ2vB9IQY29coJv_n5NFLkvBfzbG5faRNiFcimPYBO2jFdaA2mWzfxltLtwMF_dBwzTXDpMo3TVT9zEdV8YpsWqr63igqGDZVpKenlkqvRTeGJVayVuMA"
}`, &wfe.nonceService))) }`, &wfe.nonceService)))
test.AssertEquals(t, test.AssertEquals(t,
responseWriter.Body.String(), responseWriter.Body.String(),
`{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Key not authorized for name meep.com"}`) `{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Key not authorized for name meep.com"}`)
@ -677,20 +651,21 @@ func TestIssueCertificate(t *testing.T) {
mockLog.Clear() mockLog.Clear()
responseWriter.Body.Reset() responseWriter.Body.Reset()
// openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=not-an-example.com | b64url
wfe.NewCertificate(responseWriter, wfe.NewCertificate(responseWriter,
makePostRequest(signRequest(t, `{ makePostRequest(signRequest(t, `{
"resource":"new-cert", "resource":"new-cert",
"csr": "MIH1MIGiAgEAMA0xCzAJBgNVBAYTAlVTMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOXRzB9hDSCRPYjlu6HzJ9MkUPplDG-o0IS3ENiD8zcgCM-XvEEsse06CyhRb6g5Bz9AsGH9thaxszGB0o2RpakCAwEAAaAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0RBBYwFIISbm90LWFuLWV4YW1wbGUuY29tMAsGCSqGSIb3DQEBCwNBAFpyURFqjVn-7zx73GKaBvPF_2RhBsdehqSjaJ0BpvPKmzpoIFADjttNzKkWaRRDrTeT-GGMV2Gky8S-E_dzoms=", "csr": "MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSbm90LWFuLWV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmqs7nue5oFxKBk2WaFZJAma2nm1oFyPIq19gYEAdQN4mWvaJ8RjzHFkDMYUrlIrGxCYuFJDHFUk9dh19Na1MIY-NVLgcSbyNcOML3bLbLEwGmvXPbbEOflBA9mxUS9TLMgXW5ghf_qbt4vmSGKloIim41QXt55QFW6O-84s8Kd2OE6df0wTsEwLhZB3j5pDU-t7j5vTMv4Tc7EptaPkOdfQn-68viUJjlYM_4yIBVRhWCdexFdylCKVLg0obsghQEwULKYCUjdg6F0VJUI115DU49tzscXU_3FS3CyY8rchunuYszBNkdmgpAwViHNWuP7ESdEd_emrj1xuioSe6PwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAE_T1nWU38XVYL28hNVSXU0rW5IBUKtbvr0qAkD4kda4HmQRTYkt-LNSuvxoZCC9lxijjgtJi-OJe_DCTdZZpYzewlVvcKToWSYHYQ6Wm1-fxxD_XzphvZOujpmBySchdiz7QSVWJmVZu34XD5RJbIcrmj_cjRt42J1hiTFjNMzQu9U6_HwIMmliDL-soFY2RTvvZf-dAFvOUQ-Wbxt97eM1PbbmxJNWRhbAmgEpe9PWDPTpqV5AK56VAa991cQ1P8ZVmPss5hvwGWhOtpnpTZVHN3toGNYFKqxWPboirqushQlfKiFqT9rpRgM3-mFjOHidGqsKEkTdmfSVlVEk3oo="
"authorizations": ["valid"] }`, &wfe.nonceService)))
}`, &wfe.nonceService)))
assertCsrLogged(t, mockLog) assertCsrLogged(t, mockLog)
randomCertDer, _ := hex.DecodeString(GoodTestCert) cert, err := core.LoadCert("test/not-an-example.com.crt")
test.AssertNotError(t, err, "Could not load cert")
test.AssertEquals(t, test.AssertEquals(t,
responseWriter.Body.String(), responseWriter.Body.String(),
string(randomCertDer)) string(cert.Raw))
test.AssertEquals( test.AssertEquals(
t, responseWriter.Header().Get("Location"), t, responseWriter.Header().Get("Location"),
"/acme/cert/0000000000000000") "/acme/cert/ff0000000000000e")
test.AssertEquals( test.AssertEquals(
t, responseWriter.Header().Get("Link"), t, responseWriter.Header().Get("Link"),
`</acme/issuer-cert>;rel="up"`) `</acme/issuer-cert>;rel="up"`)
@ -701,7 +676,7 @@ func TestIssueCertificate(t *testing.T) {
test.AssertEquals(t, len(reqlogs), 1) test.AssertEquals(t, len(reqlogs), 1)
test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_NOTICE) test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_NOTICE)
test.AssertContains(t, reqlogs[0].Message, `[AUDIT] `) test.AssertContains(t, reqlogs[0].Message, `[AUDIT] `)
test.AssertContains(t, reqlogs[0].Message, `"Names":["not-an-example.com"]`) test.AssertContains(t, reqlogs[0].Message, `"CommonName":"not-an-example.com",`)
} }
func TestChallenge(t *testing.T) { func TestChallenge(t *testing.T) {
@ -1311,3 +1286,21 @@ func TestLengthRequired(t *testing.T) {
_, ok := err.(core.LengthRequiredError) _, ok := err.(core.LengthRequiredError)
test.Assert(t, ok, "Error code for missing content-length wasn't 411.") test.Assert(t, ok, "Error code for missing content-length wasn't 411.")
} }
func TestBadKeyCSR(t *testing.T) {
wfe := setupWFE(t)
responseWriter := httptest.NewRecorder()
// CSR with a bad (512 bit RSA) key.
// openssl req -outform der -new -newkey rsa:512 -nodes -keyout foo.com.key
// -subj /CN=foo.com | base64 -w0 | sed -e 's,+,-,g' -e 's,/,_,g'
wfe.NewCertificate(responseWriter,
makePostRequest(signRequest(t, `{
"resource":"new-cert",
"csr": "MIHLMHcCAQAwEjEQMA4GA1UEAwwHZm9vLmNvbTBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDCZftp4x4owgjBnwOKfzihIPedT-BUmV2fuQPMqaUlc8yJUp13vcO5uxUlaBm8leM7Dj_sgTDP_JgykorlYo73AgMBAAGgADANBgkqhkiG9w0BAQsFAANBAEaQ2QBhweK-kp1ejQCedUhMit_wG-uTBtKnc3M82f6_fztLkhg1vWQ782nmhbEI5orXp6QtNHgJYnBpqA9Ut00"
}`, &wfe.nonceService)))
test.AssertEquals(t,
responseWriter.Body.String(),
`{"type":"urn:acme:error:malformed","detail":"Invalid key in certificate request :: Key too small: 512"}`)
}