Skip to content

Commit

Permalink
Merge branch 'master' of github.com:letsencrypt/boulder into va-timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandshoemaker committed Jun 6, 2015
2 parents d145a3d + ef5f95d commit 99d7ed7
Show file tree
Hide file tree
Showing 31 changed files with 677 additions and 339 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pre:

# Compile each of the binaries
$(OBJECTS): pre
go build -o ./bin/$@ -ldflags "-X $(BUILD_ID_VAR) $(REVID)" cmd/$@/main.go
go build -tags pkcs11 -o ./bin/$@ -ldflags "-X $(BUILD_ID_VAR) $(REVID)" cmd/$@/main.go

clean:
rm -f $(OBJDIR)/*
Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ A quick-start method for running a Boulder instance is to use one of the example

```
> mkdir .boulder-config
> cp test/example-config.json .boulder-config/config.json
> cp test/boulder-config.json .boulder-config/config.json
> docker run --name=boulder --read-only=true --rm=true -v $(pwd)/.boulder-config:/boulder:ro -p 4000:4000 quay.io/letsencrypt/boulder:latest boulder
```

Expand All @@ -43,6 +43,18 @@ Quickstart
Install RabbitMQ from https://rabbitmq.com/download.html. It's required to run
tests.

Install libtool-ltdl dev libraries, which are required for Boulder's PKCS11
support.

Ubuntu:
`sudo apt-get install libltdl3-dev`

CentOS:
`sudo yum install libtool-ltdl-devel`

OS X:
`sudo port install libtool`

```
> go get github.com/letsencrypt/boulder # Ignore errors about no buildable files
> cd $GOPATH/src/github.com/letsencrypt/boulder
Expand Down
77 changes: 49 additions & 28 deletions ca/certificate-authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package ca
import (
"crypto"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
Expand All @@ -18,41 +19,48 @@ import (
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/policy"

"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
cfsslOCSP "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
)

// Config defines the JSON configuration file schema
type Config struct {
Server string
AuthKey string
Profile string
TestMode bool
DBDriver string
DBName string
SerialPrefix int
// This field is only allowed if TestMode is true, indicating that we are
// signing with a local key. In production we will use an HSM and this
// IssuerKey must be empty (and TestMode must be false). PEM-encoded private
// key used for signing certificates and OCSP responses.
IssuerKey string
Key KeyConfig
// How long issue certificates are valid for, should match expiry field
// in cfssl config.
Expiry string
// The maximum number of subjectAltNames in a single certificate
MaxNames int
CFSSL cfsslConfig.Config
}

type KeyConfig struct {
File string
PKCS11 PKCS11Config
}

type PKCS11Config struct {
Module string
Token string
PIN string
Label string
}

// CertificateAuthorityImpl represents a CA that signs certificates, CRLs, and
// OCSP responses.
type CertificateAuthorityImpl struct {
profile string
Signer signer.Signer
OCSPSigner cfsslOCSP.Signer
OCSPSigner ocsp.Signer
SA core.StorageAuthority
PA core.PolicyAuthority
DB core.CertificateAuthorityDatabase
Expand Down Expand Up @@ -80,20 +88,19 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
return nil, err
}

// Create the remote signer
localProfile := cfsslConfig.SigningProfile{
Expiry: time.Hour, // BOGUS: Required by CFSSL, but not used
RemoteName: config.Server, // BOGUS: Only used as a flag by CFSSL
RemoteServer: config.Server,
UseSerialSeq: true,
// CFSSL requires processing JSON configs through its own LoadConfig, so we
// serialize and then deserialize.
cfsslJSON, err := json.Marshal(config.CFSSL)
if err != nil {
return nil, err
}

localProfile.Provider, err = auth.New(config.AuthKey, nil)
cfsslConfigObj, err := cfsslConfig.LoadConfig(cfsslJSON)
if err != nil {
return nil, err
}

signer, err := remote.NewSigner(&cfsslConfig.Signing{Default: &localProfile})
// Load the private key, which can be a file or a PKCS#11 key.
priv, err := loadKey(config.Key)
if err != nil {
return nil, err
}
Expand All @@ -103,18 +110,14 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
return nil, err
}

// In test mode, load a private key from a file.
// TODO: This should rely on the CFSSL config, to make it easy to use a key
// from a file vs an HSM. https://github.com/letsencrypt/boulder/issues/163
issuerKey, err := loadIssuerKey(config.IssuerKey)
signer, err := local.NewSigner(priv, issuer, x509.SHA256WithRSA, cfsslConfigObj.Signing)
if err != nil {
return nil, err
}

// Set up our OCSP signer. Note this calls for both the issuer cert and the
// OCSP signing cert, which are the same in our case.
ocspSigner, err := cfsslOCSP.NewSigner(issuer, issuer, issuerKey,
time.Hour*24*4)
ocspSigner, err := ocsp.NewSigner(issuer, issuer, priv, time.Hour)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -145,6 +148,24 @@ func NewCertificateAuthorityImpl(cadb core.CertificateAuthorityDatabase, config
return ca, nil
}

func loadKey(keyConfig KeyConfig) (priv crypto.Signer, err error) {
if keyConfig.File != "" {
var keyBytes []byte
keyBytes, err = ioutil.ReadFile(keyConfig.File)
if err != nil {
return nil, fmt.Errorf("Could not read key file %s", keyConfig.File)
}

priv, err = helpers.ParsePrivateKeyPEM(keyBytes)
return
} else {
pkcs11Config := keyConfig.PKCS11
priv, err = pkcs11key.New(pkcs11Config.Module,
pkcs11Config.Token, pkcs11Config.PIN, pkcs11Config.Label)
return
}
}

func loadIssuer(filename string) (issuerCert *x509.Certificate, err error) {
if filename == "" {
err = errors.New("Issuer certificate was not provided in config.")
Expand Down Expand Up @@ -181,7 +202,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(xferObj core.OCSPSigningRequest
return nil, err
}

signRequest := cfsslOCSP.SignRequest{
signRequest := ocsp.SignRequest{
Certificate: cert,
Status: xferObj.Status,
Reason: xferObj.Reason,
Expand All @@ -207,7 +228,7 @@ func (ca *CertificateAuthorityImpl) RevokeCertificate(serial string, reasonCode
return err
}

signRequest := cfsslOCSP.SignRequest{
signRequest := ocsp.SignRequest{
Certificate: cert,
Status: string(core.OCSPStatusRevoked),
Reason: reasonCode,
Expand Down
101 changes: 42 additions & 59 deletions ca/certificate-authority_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,17 @@ package ca

import (
"bytes"
"crypto"
"crypto/x509"
"encoding/asn1"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
"os"
"testing"
"time"

apisign "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
ocspConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp/config"
_ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3"

"github.com/letsencrypt/boulder/core"
Expand Down Expand Up @@ -285,57 +280,11 @@ var FarFuture = time.Date(2100, 1, 1, 0, 0, 0, 0, time.UTC)
var FarPast = time.Date(1950, 1, 1, 0, 0, 0, 0, time.UTC)

// CFSSL config
const hostPort = "localhost:9000"
const authKey = "79999d86250c367a2b517a1ae7d409c1"
const profileName = "ee"
const caKeyFile = "../test/test-ca.key"
const caCertFile = "../test/test-ca.pem"

var cfsslSigner *local.Signer
var caKey crypto.PrivateKey
var caCert x509.Certificate

func TestMain(m *testing.M) {
caKeyPEM, _ := ioutil.ReadFile(caKeyFile)
caKey, _ := helpers.ParsePrivateKeyPEM(caKeyPEM)

caCertPEM, _ := ioutil.ReadFile(caCertFile)
caCert, _ := helpers.ParseCertificatePEM(caCertPEM)

// Create an online CFSSL instance
// This is designed to mimic what LE plans to do
authHandler, _ := auth.New(authKey, nil)
policy := &cfsslConfig.Signing{
Profiles: map[string]*cfsslConfig.SigningProfile{
profileName: &cfsslConfig.SigningProfile{
Usage: []string{"server auth"},
CA: false,
IssuerURL: []string{"http://not-example.com/issuer-url"},
OCSP: "http://not-example.com/ocsp",
CRL: "http://not-example.com/crl",

Policies: []asn1.ObjectIdentifier{
asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1},
},
Expiry: 8760 * time.Hour,
Backdate: time.Hour,
Provider: authHandler,
CSRWhitelist: &cfsslConfig.CSRWhitelist{
PublicKeyAlgorithm: true,
PublicKey: true,
SignatureAlgorithm: true,
},
},
},
Default: &cfsslConfig.SigningProfile{
Expiry: time.Hour,
},
}
cfsslSigner, _ = local.NewSigner(caKey, caCert, x509.SHA256WithRSA, policy)
signHandler, _ := apisign.NewAuthHandlerFromSigner(cfsslSigner)
http.Handle("/api/v1/cfssl/authsign", signHandler)
// This goroutine should get killed when main() return
go (func() { http.ListenAndServe(hostPort, nil) })()

os.Exit(m.Run())
}
Expand All @@ -350,16 +299,47 @@ func setup(t *testing.T) (cadb core.CertificateAuthorityDatabase, storageAuthori
cadb, _ = test.NewMockCertificateAuthorityDatabase()

// Create a CA
// Uncomment to test with a remote signer
caConfig = Config{
Server: hostPort,
AuthKey: authKey,
Profile: profileName,
SerialPrefix: 17,
IssuerKey: "../test/test-ca.key",
TestMode: true,
Expiry: "8760h",
MaxNames: 2,
Key: KeyConfig{
File: caKeyFile,
},
TestMode: true,
Expiry: "8760h",
MaxNames: 2,
CFSSL: cfsslConfig.Config{
Signing: &cfsslConfig.Signing{
Profiles: map[string]*cfsslConfig.SigningProfile{
profileName: &cfsslConfig.SigningProfile{
Usage: []string{"server auth"},
CA: false,
IssuerURL: []string{"http://not-example.com/issuer-url"},
OCSP: "http://not-example.com/ocsp",
CRL: "http://not-example.com/crl",

Policies: []asn1.ObjectIdentifier{
asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1},
},
ExpiryString: "8760h",
Backdate: time.Hour,
CSRWhitelist: &cfsslConfig.CSRWhitelist{
PublicKeyAlgorithm: true,
PublicKey: true,
SignatureAlgorithm: true,
},
},
},
Default: &cfsslConfig.SigningProfile{
ExpiryString: "8760h",
},
},
OCSP: &ocspConfig.Config{
CACertFile: caCertFile,
ResponderCertFile: caCertFile,
KeyFile: caKeyFile,
},
},
}
return cadb, storageAuthority, caConfig
}
Expand All @@ -375,6 +355,9 @@ func TestRevoke(t *testing.T) {
cadb, storageAuthority, caConfig := setup(t)
ca, err := NewCertificateAuthorityImpl(cadb, caConfig, caCertFile)
test.AssertNotError(t, err, "Failed to create CA")
if err != nil {
return
}
ca.SA = storageAuthority

csrDER, _ := hex.DecodeString(CN_AND_SAN_CSR_HEX)
Expand Down
5 changes: 4 additions & 1 deletion cmd/admin-revoker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ func setupContext(context *cli.Context) (rpc.CertificateAuthorityClient, *blog.A

ch := cmd.AmqpChannel(c.AMQP.Server)

cac, err := rpc.NewCertificateAuthorityClient("revoker->CA", c.AMQP.CA.Server, ch)
caRPC, err := rpc.NewAmqpRPCCLient("revoker->CA", c.AMQP.CA.Server, ch)
cmd.FailOnError(err, "Unable to create RPC client")

cac, err := rpc.NewCertificateAuthorityClient(caRPC)
cmd.FailOnError(err, "Unable to create CA client")

dbMap, err := sa.NewDbMap(c.Revoker.DBDriver, c.Revoker.DBName)
Expand Down
9 changes: 7 additions & 2 deletions cmd/boulder-ca/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,17 @@ func main() {
ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1))

sac, err := rpc.NewStorageAuthorityClient("CA->SA", c.AMQP.SA.Server, ch)
saRPC, err := rpc.NewAmqpRPCCLient("CA->SA", c.AMQP.SA.Server, ch)
cmd.FailOnError(err, "Unable to create RPC client")

sac, err := rpc.NewStorageAuthorityClient(saRPC)
cmd.FailOnError(err, "Failed to create SA client")

cai.SA = &sac

cas, err := rpc.NewCertificateAuthorityServer(c.AMQP.CA.Server, ch, cai)
cas := rpc.NewAmqpRPCServer(c.AMQP.CA.Server, ch)

err = rpc.NewCertificateAuthorityServer(cas, cai)
cmd.FailOnError(err, "Unable to create CA server")

auditlogger.Info(app.VersionString())
Expand Down
Loading

0 comments on commit 99d7ed7

Please sign in to comment.