From 998ffc79cb51298868ee0fbf7c3bc7668bdb6ede Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 9 Sep 2015 00:22:10 -0400 Subject: [PATCH 1/2] Do GoodKey checking in WFE. --- ca/certificate-authority.go | 3 +- ca/certificate-authority_test.go | 7 -- cmd/boulder-ca/main.go | 1 - cmd/boulder-ra/main.go | 1 - cmd/shell.go | 1 - core/good_key.go | 17 ++--- core/good_key_test.go | 28 ++++---- core/util.go | 16 +++++ ra/registration-authority.go | 5 +- ra/registration-authority_test.go | 2 - test/boulder-config.json | 1 - test/js/test.js | 20 +++--- wfe/test/not-an-example.com.crt | 29 +++++++++ wfe/web-front-end.go | 30 +++++++-- wfe/web-front-end_test.go | 103 ++++++++++++++---------------- 15 files changed, 152 insertions(+), 112 deletions(-) create mode 100644 wfe/test/not-an-example.com.crt diff --git a/ca/certificate-authority.go b/ca/certificate-authority.go index d34d1bbfe..339369425 100644 --- a/ca/certificate-authority.go +++ b/ca/certificate-authority.go @@ -59,7 +59,6 @@ type CertificateAuthorityImpl struct { ValidityPeriod time.Duration NotAfter time.Time MaxNames int - MaxKeySize int } // 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) 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()) // AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c ca.log.AuditErr(err) diff --git a/ca/certificate-authority_test.go b/ca/certificate-authority_test.go index c59dea19b..9e3dc10fb 100644 --- a/ca/certificate-authority_test.go +++ b/ca/certificate-authority_test.go @@ -212,7 +212,6 @@ func TestRevoke(t *testing.T) { ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 csr, _ := x509.ParseCertificateRequest(CNandSANCSR) 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") ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 /* // Uncomment to test with a local signer @@ -330,7 +328,6 @@ func TestRejectNoName(t *testing.T) { test.AssertNotError(t, err, "Failed to create CA") ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 // Test that the CA rejects CSRs with no names csr, _ := x509.ParseCertificateRequest(NoNameCSR) @@ -361,7 +358,6 @@ func TestDeduplication(t *testing.T) { test.AssertNotError(t, err, "Failed to create CA") ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 // Test that the CA collapses duplicate names csr, _ := x509.ParseCertificateRequest(DupeNameCSR) @@ -391,7 +387,6 @@ func TestRejectValidityTooLong(t *testing.T) { test.AssertNotError(t, err, "Failed to create CA") ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 // Test that the CA rejects CSRs that would expire after the intermediate cert csr, _ := x509.ParseCertificateRequest(NoCNCSR) @@ -411,7 +406,6 @@ func TestShortKey(t *testing.T) { ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile) ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 // Test that the CA rejects CSRs that would expire after the intermediate cert csr, _ := x509.ParseCertificateRequest(ShortKeyCSR) @@ -425,7 +419,6 @@ func TestRejectBadAlgorithm(t *testing.T) { ca, err := NewCertificateAuthorityImpl(ctx.caDB, ctx.caConfig, ctx.fc, caCertFile) ca.PA = ctx.pa ca.SA = ctx.sa - ca.MaxKeySize = 4096 // Test that the CA rejects CSRs that would expire after the intermediate cert csr, _ := x509.ParseCertificateRequest(BadAlgorithmCSR) diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index 735acbf9b..01d275033 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -46,7 +46,6 @@ func main() { cai, err := ca.NewCertificateAuthorityImpl(cadb, c.CA, clock.Default(), c.Common.IssuerCert) cmd.FailOnError(err, "Failed to create CA impl") - cai.MaxKeySize = c.Common.MaxKeySize cai.PA = pa go cmd.ProfileCmd("CA", stats) diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go index da77da9c0..c0d6cd535 100644 --- a/cmd/boulder-ra/main.go +++ b/cmd/boulder-ra/main.go @@ -45,7 +45,6 @@ func main() { rai := ra.NewRegistrationAuthorityImpl(clock.Default(), auditlogger) rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath - rai.MaxKeySize = c.Common.MaxKeySize rai.PA = pa raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout) cmd.FailOnError(err, "Couldn't parse RA DNS timeout") diff --git a/cmd/shell.go b/cmd/shell.go index 340998b76..68912b578 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -177,7 +177,6 @@ type Config struct { BaseURL string // Path to a PEM-encoded copy of the issuer certificate. IssuerCert string - MaxKeySize int DNSResolver string DNSTimeout string diff --git a/core/good_key.go b/core/good_key.go index 23daacdab..6d65ffe9f 100644 --- a/core/good_key.go +++ b/core/good_key.go @@ -42,17 +42,17 @@ var ( // key use (our requirements are the same for either one), according to basic // strength and algorithm checking. // 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() switch t := key.(type) { case rsa.PublicKey: - return GoodKeyRSA(t, maxKeySize) + return GoodKeyRSA(t) case *rsa.PublicKey: - return GoodKeyRSA(*t, maxKeySize) + return GoodKeyRSA(*t) case ecdsa.PublicKey: - return GoodKeyECDSA(t, maxKeySize) + return GoodKeyECDSA(t) case *ecdsa.PublicKey: - return GoodKeyECDSA(*t, maxKeySize) + return GoodKeyECDSA(*t) default: err := MalformedRequestError(fmt.Sprintf("Unknown key type %s", reflect.TypeOf(key))) 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 -func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) { +func GoodKeyECDSA(key ecdsa.PublicKey) (err error) { log := blog.GetAuditLogger() err = NotSupportedError("ECDSA keys not yet supported") 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 -func GoodKeyRSA(key rsa.PublicKey, maxKeySize int) (err error) { +func GoodKeyRSA(key rsa.PublicKey) (err error) { log := blog.GetAuditLogger() // Baseline Requirements Appendix A - // Modulus must be >= 2048 bits and < maxKeySize + // Modulus must be >= 2048 bits and <= 4096 bits modulus := key.N modulusBitLen := modulus.BitLen() + const maxKeySize = 4096 if modulusBitLen < 2048 { err = MalformedRequestError(fmt.Sprintf("Key too small: %d", modulusBitLen)) log.Debug(err.Error()) diff --git a/core/good_key_test.go b/core/good_key_test.go index ea9a95870..33f6ded8a 100644 --- a/core/good_key_test.go +++ b/core/good_key_test.go @@ -15,31 +15,29 @@ import ( "github.com/letsencrypt/boulder/test" ) -var maxKeySize = 2048 - func TestUnknownKeyType(t *testing.T) { 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) { ecdsaKey := ecdsa.PublicKey{} - test.AssertError(t, GoodKey(&ecdsaKey, maxKeySize), "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.") + test.AssertError(t, GoodKey(ecdsaKey), "Should have rejected ECDSA key.") } func TestSmallModulus(t *testing.T) { private, err := rsa.GenerateKey(rand.Reader, 2040) 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, 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), "Should have rejected too-short key.") } 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.AssertError(t, GoodKey(&private.PublicKey, maxKeySize), "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.") + test.AssertError(t, GoodKey(private.PublicKey), "Should have rejected too-long key.") } func TestSmallExponent(t *testing.T) { @@ -48,7 +46,7 @@ func TestSmallExponent(t *testing.T) { N: bigOne.Lsh(bigOne, 2048), 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) { @@ -57,7 +55,7 @@ func TestEvenExponent(t *testing.T) { N: bigOne.Lsh(bigOne, 2048), 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) { @@ -66,7 +64,7 @@ func TestEvenModulus(t *testing.T) { N: bigOne.Lsh(bigOne, 2048), 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) { @@ -78,11 +76,11 @@ func TestModulusDivisibleBy752(t *testing.T) { N: N, 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) { private, err := rsa.GenerateKey(rand.Reader, 2048) 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.") } diff --git a/core/util.go b/core/util.go index e741358ae..ce77670cd 100644 --- a/core/util.go +++ b/core/util.go @@ -17,10 +17,12 @@ import ( "encoding/asn1" "encoding/base64" "encoding/json" + "encoding/pem" "errors" "fmt" "hash" "io" + "io/ioutil" "math/big" "net/url" "strings" @@ -347,3 +349,17 @@ func UniqueNames(names []string) (unique []string) { } 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 +} diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 1f55eb3bb..c618b66d0 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -32,8 +32,7 @@ type RegistrationAuthorityImpl struct { clk clock.Clock log *blog.AuditLogger - AuthzBase string - MaxKeySize int + AuthzBase string } // NewRegistrationAuthorityImpl constructs a new RA object. @@ -96,7 +95,7 @@ type certificateRequestEvent struct { // NewRegistration constructs a new Registration from a request. 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())) } reg = core.Registration{ diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index 81fbef207..bfca00aad 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -196,7 +196,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut DB: cadb, ValidityPeriod: time.Hour * 2190, NotAfter: time.Now().Add(time.Hour * 8761), - MaxKeySize: 4096, Clk: fc, } cleanUp := func() { @@ -216,7 +215,6 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, *sa.SQLStorageAut ra.CA = &ca ra.PA = pa ra.AuthzBase = "http://acme.invalid/authz/" - ra.MaxKeySize = 4096 ra.DNSResolver = &mocks.MockDNS{} AuthzInitial.RegistrationID = Registration.ID diff --git a/test/boulder-config.json b/test/boulder-config.json index ddaace8fd..81a207a37 100644 --- a/test/boulder-config.json +++ b/test/boulder-config.json @@ -170,7 +170,6 @@ "common": { "baseURL": "http://localhost:4000", "issuerCert": "test/test-ca.pem", - "maxKeySize": 4096, "dnsResolver": "127.0.0.1:8053", "dnsTimeout": "10s", "dnsAllowLoopbackAddresses": true diff --git a/test/js/test.js b/test/js/test.js index bfdd445a5..f9de82f0b 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -202,16 +202,18 @@ function main() { function makeKeyPair() { console.log("Generating cert key pair..."); - 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); + try { + fs.statSync(state.keyFile); + child_process.execSync("openssl req -new -key " + state.keyFile + " -days 3650 -subj /CN=foo -nodes -x509 -out temp-cert.pem"); + } catch (e) { + if (e.code == 'ENOENT') { + child_process.execSync("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=foo -nodes -x509 -out temp-cert.pem"); + } else { + throw(e); } - state.certPrivateKey = cryptoUtil.importPemPrivateKey(fs.readFileSync(state.keyFile)); - - console.log(); - makeAccountKeyPair() - }); + } + state.certPrivateKey = cryptoUtil.importPemPrivateKey(fs.readFileSync(state.keyFile)); + makeAccountKeyPair() } function makeAccountKeyPair(answers) { diff --git a/wfe/test/not-an-example.com.crt b/wfe/test/not-an-example.com.crt new file mode 100644 index 000000000..5c459194c --- /dev/null +++ b/wfe/test/not-an-example.com.crt @@ -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----- diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 2a49d647d..c502de3ae 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -336,6 +336,11 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool, res wfe.log.Debug(fmt.Sprintf("%v :: %v", puberr.Error(), err.Error())) 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 // i.e., Nonce is in protected header and @@ -705,16 +710,27 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request return } - var init core.CertificateRequest - if err = json.Unmarshal(body, &init); err != nil { + var certificateRequest core.CertificateRequest + if err = json.Unmarshal(body, &certificateRequest); err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest) return } - wfe.logCsr(request.RemoteAddr, init, reg) - logEvent.Extra["CSRDNSNames"] = init.CSR.DNSNames - logEvent.Extra["CSREmailAddresses"] = init.CSR.EmailAddresses - logEvent.Extra["CSRIPAddresses"] = init.CSR.IPAddresses + wfe.logCsr(request.RemoteAddr, certificateRequest, reg) + // Check that the key in the CSR is good. This will also be checked in the CA + // component, but we want to discard CSRs with bad keys as early as possible + // 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 // TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the @@ -722,7 +738,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request // 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, reg.ID) + cert, err := wfe.RA.NewCertificate(certificateRequest, reg.ID) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Error creating new cert", err, statusCodeFromError(err)) diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index 490f8c7ca..ce90b53e5 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -10,7 +10,6 @@ import ( "crypto/rsa" "crypto/x509" "database/sql" - "encoding/hex" "encoding/json" "encoding/pem" "errors" @@ -112,22 +111,6 @@ TkLlEeVOuQfxTadw05gzKX0jKkMC4igGxvEeilYc6NR6a4nvRulG84Q8VV9Sy9Ie wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA== -----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 { @@ -304,11 +287,15 @@ func (ra *MockRegistrationAuthority) OnValidationUpdate(authz core.Authorization 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 - randomCertDer, _ := hex.DecodeString(GoodTestCert) - cert.DER = randomCertDer - return + certPtr, err := core.LoadCert("test/not-an-example.com.crt") + if err != nil { + return core.Certificate{}, err + } + return core.Certificate{ + DER: certPtr.Raw, + }, nil } 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.log.SyslogWriter = mocks.NewSyslogWriter() + wfe.RA = &MockRegistrationAuthority{} + wfe.SA = &MockSA{} + wfe.Stats, _ = statsd.NewNoopClient() + wfe.SubscriberAgreementURL = agreementURL + return wfe } @@ -609,46 +601,28 @@ func TestIssueCertificate(t *testing.T) { `{"type":"urn:acme:error:malformed","detail":"Error unmarshaling certificate request"}`) // Valid, signed JWS body, payload has a invalid signature on CSR and no authorizations: - // { - // "csr": "MIICU...", - // "authorizations: [] - // } + // alias b64url="base64 -w0 | sed -e 's,+,-,g' -e 's,/,_,g'" + // openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=foo.com | \ + // sed 's/foo.com/fob.com/' | b64url responseWriter.Body.Reset() wfe.NewCertificate(responseWriter, makePostRequest(signRequest(t, `{ "resource":"new-cert", - "csr": "MIICUzCCATsCAQAwDjEMMAoGA1UEAwwDZm9vMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3UWce2PY9y8n4B7jOk3DXZnu2pUgLqs7a5DzRBxnPqL7axis6thjSBI2FO7w5CUjO-m8XjD-GYWgfXebZ3aQVlBiYqdxZ3UG6RDwEbBCeKo7v8W-UUfESNNCXF874ddhJmEw0RF0YWSAEctAYHEGoPFz69gCql6xXDPY1OlpMArkIIlq9EZWwT081ekyJv0GYRfQigCMK4b1gkFvKsHja9-Q5u1b0AZyA-mPTu6z5EWkB2onhAXwWXX90sfUe8DSet9r9GxMln3lgZWT1zh3RMZILp0Uhh3NbXnA8JInukha3HPO8WgmDd4K6uBzWso0A6fp5NpX28ZpKAwM5iQltQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAFGJV3OcghJEZvO_hGtIdaRnsu6eX3CeqS0bYcEEza8vizlj4x09ntMH3QooqPOj8suul0vD75HZTpz6FHE7SyLeNKQBGNGp1PMWmXsFqD6xURCyMHvCZoHynpCr7D5HtzIvu9fAV7XRK7qBKXfRxbv21q0ysMWnfwkbS2wrs1wAzPPg4iGJq8uVItrlcFL8buJLzxvKa3lu_OjxNXjzdEt3VVko-AKS1swkYEhsGwKd8ZzNbpF2IQ-okXgR_ZecyW8t83pV-w33GhDL9w6RLRMgSM5aojy8ri7YIoIvc3-9klbw2kwY5oM2lmhoIOGU10TkEyn18myy_5GUEGhNzPA=", - "authorizations": [] + "csr": "MIICVzCCAT8CAQAwEjEQMA4GA1UEAwwHZm9iLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKzHhqcMSTVjBu61vufGVmIYM4mMbWXgndHOUWnIqSKcNtFtPQ465tcZRT5ITIZWXGjsmgDrj31qvG3t5qLwyaF5hsTvFHK72nLMAQhdgM6481Qe9yaoaulWpkGr_9LVz4jQ9pGAaLVamXGpSxV-ipTOo79Sev4aZE8ksD9atEfWtcOD9w8_zj74vpWjTAHN49Q88chlChVqakn0zSfHPfS-jF8g0UTddBuF0Ti3sZChjxzbo6LwZ4182xX7XPnOLav3AGj0Su7j5XMl3OpenOrlWulWJeZIHq5itGW321j306XiGdbrdWH4K7JygICFds6oolwQRGBY6yinAtCgkTcCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQBxPiHOtKuBxtvecMNtLkTSuTyEkusQGnjoFDaKe5oqwGYQgy0YBii2-BbaPmqS4ZaDc-vDz_RLeKH5ZiH-NliYR1V_CRtpFLQi18g_2pLQnZLVO3ENs-SM37nU_nBGn9O93t2bkssoM3fZmtgp3R2W7I_wvx7Z8oWKa4boTeBAg_q9Gmi6QskZBddK7A4S_vOR0frU6QSPK_ksPhvovp9fwb6CVKrlJWf556UwRPWgbkW39hvTxK2KHhrUEg3oawNkWde2jZtnZ9e-9zpw8-_5O0X7-YN0ucbFTfQybce_ReuLlGepiHT5bvVavBZoIvqw1XOgSMvGgZFU8tAWMBlj" }`, &wfe.nonceService))) test.AssertEquals(t, responseWriter.Body.String(), `{"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: - // { - // "csr": "MIIBK...", - // "authorizations: [] - // } + // openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=meep.com | b64url mockLog.Clear() responseWriter.Body.Reset() wfe.NewCertificate(responseWriter, makePostRequest(signRequest(t, `{ - "resource":"new-cert", - "csr": "MIIBKzCB2AIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqvFEGBNrjAotPbcdTSyDpxsESN0-eYl4TqS0ZLYwLTV-FuPHTPjFiq2oH1BEgmRzjb8YiPVXFMnaOeHE7zuuXQIDAQABoCYwJAYJKoZIhvcNAQkOMRcwFTATBgNVHREEDDAKgghtZWVwLmNvbTALBgkqhkiG9w0BAQUDQQBSEcEq-lMUnzv1DO8jK0hJR8YKc0yV8zuWVfAWN0_dsPg5Ny-OHhtJcOTIrUrLTb_xCU7cjiKxU8i3j1kaT-rt" - }`, &wfe.nonceService))) + "resource":"new-cert", + "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))) test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:unauthorized","detail":"Error creating new cert :: Key not authorized for name meep.com"}`) @@ -656,20 +630,21 @@ func TestIssueCertificate(t *testing.T) { mockLog.Clear() responseWriter.Body.Reset() + // openssl req -outform der -new -nodes -key wfe/test/178.key -subj /CN=not-an-example.com | b64url wfe.NewCertificate(responseWriter, makePostRequest(signRequest(t, `{ - "resource":"new-cert", - "csr": "MIH1MIGiAgEAMA0xCzAJBgNVBAYTAlVTMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAOXRzB9hDSCRPYjlu6HzJ9MkUPplDG-o0IS3ENiD8zcgCM-XvEEsse06CyhRb6g5Bz9AsGH9thaxszGB0o2RpakCAwEAAaAwMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0RBBYwFIISbm90LWFuLWV4YW1wbGUuY29tMAsGCSqGSIb3DQEBCwNBAFpyURFqjVn-7zx73GKaBvPF_2RhBsdehqSjaJ0BpvPKmzpoIFADjttNzKkWaRRDrTeT-GGMV2Gky8S-E_dzoms=", - "authorizations": ["valid"] - }`, &wfe.nonceService))) + "resource":"new-cert", + "csr": "MIICYjCCAUoCAQAwHTEbMBkGA1UEAwwSbm90LWFuLWV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmqs7nue5oFxKBk2WaFZJAma2nm1oFyPIq19gYEAdQN4mWvaJ8RjzHFkDMYUrlIrGxCYuFJDHFUk9dh19Na1MIY-NVLgcSbyNcOML3bLbLEwGmvXPbbEOflBA9mxUS9TLMgXW5ghf_qbt4vmSGKloIim41QXt55QFW6O-84s8Kd2OE6df0wTsEwLhZB3j5pDU-t7j5vTMv4Tc7EptaPkOdfQn-68viUJjlYM_4yIBVRhWCdexFdylCKVLg0obsghQEwULKYCUjdg6F0VJUI115DU49tzscXU_3FS3CyY8rchunuYszBNkdmgpAwViHNWuP7ESdEd_emrj1xuioSe6PwIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAE_T1nWU38XVYL28hNVSXU0rW5IBUKtbvr0qAkD4kda4HmQRTYkt-LNSuvxoZCC9lxijjgtJi-OJe_DCTdZZpYzewlVvcKToWSYHYQ6Wm1-fxxD_XzphvZOujpmBySchdiz7QSVWJmVZu34XD5RJbIcrmj_cjRt42J1hiTFjNMzQu9U6_HwIMmliDL-soFY2RTvvZf-dAFvOUQ-Wbxt97eM1PbbmxJNWRhbAmgEpe9PWDPTpqV5AK56VAa991cQ1P8ZVmPss5hvwGWhOtpnpTZVHN3toGNYFKqxWPboirqushQlfKiFqT9rpRgM3-mFjOHidGqsKEkTdmfSVlVEk3oo=" + }`, &wfe.nonceService))) 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, responseWriter.Body.String(), - string(randomCertDer)) + string(cert.Raw)) test.AssertEquals( t, responseWriter.Header().Get("Location"), - "/acme/cert/0000000000000000") + "/acme/cert/ff0000000000000e") test.AssertEquals( t, responseWriter.Header().Get("Link"), `;rel="up"`) @@ -680,7 +655,7 @@ func TestIssueCertificate(t *testing.T) { test.AssertEquals(t, len(reqlogs), 1) test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_NOTICE) 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) { @@ -1290,3 +1265,21 @@ func TestLengthRequired(t *testing.T) { _, ok := err.(core.LengthRequiredError) 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"}`) +} From fa5b8e889103a5e81cedb698122b38239e79fd9a Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 10 Sep 2015 07:51:40 -0400 Subject: [PATCH 2/2] Revert test.js to master. The changes to use an existing key depended on Node 0.12, but Travis provides Node 0.10 (unless you specify language: node and a version, but we have language: go). --- test/js/test.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/js/test.js b/test/js/test.js index f9de82f0b..bfdd445a5 100644 --- a/test/js/test.js +++ b/test/js/test.js @@ -202,18 +202,16 @@ function main() { function makeKeyPair() { console.log("Generating cert key pair..."); - try { - fs.statSync(state.keyFile); - child_process.execSync("openssl req -new -key " + state.keyFile + " -days 3650 -subj /CN=foo -nodes -x509 -out temp-cert.pem"); - } catch (e) { - if (e.code == 'ENOENT') { - child_process.execSync("openssl req -newkey rsa:2048 -keyout " + state.keyFile + " -days 3650 -subj /CN=foo -nodes -x509 -out temp-cert.pem"); - } else { - throw(e); + 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.certPrivateKey = cryptoUtil.importPemPrivateKey(fs.readFileSync(state.keyFile)); - makeAccountKeyPair() + state.certPrivateKey = cryptoUtil.importPemPrivateKey(fs.readFileSync(state.keyFile)); + + console.log(); + makeAccountKeyPair() + }); } function makeAccountKeyPair(answers) {