Skip to content

Commit

Permalink
Merge 0a3732f into d2c6035
Browse files Browse the repository at this point in the history
  • Loading branch information
bifurcation committed Mar 4, 2015
2 parents d2c6035 + 0a3732f commit b49020c
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 143 deletions.
82 changes: 62 additions & 20 deletions boulder-start/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"github.com/streadway/amqp"
"net/http"
"os"

_ "github.com/mattn/go-sqlite3"
)

// Exit and print error message if we encountered a problem
Expand Down Expand Up @@ -47,21 +49,30 @@ func main() {
app.Usage = "Command-line utility to start Boulder's servers in stand-alone mode"
app.Version = "0.0.0"


// Specify AMQP Server
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "amqp",
Value: "amqp://guest:guest@localhost:5672",
EnvVar: "AMQP_SERVER",
Usage: "AMQP Broker URI",
},
Name: "amqp",
Value: "amqp://guest:guest@localhost:5672",
EnvVar: "AMQP_SERVER",
Usage: "AMQP Broker URI",
},
cli.StringFlag{
Name: "cfssl",
Value: "tcp://localhost:8888",
EnvVar: "CFSSL_PORT",
Usage: "CFSSL Server URI",
},
Name: "cfssl",
Value: "localhost:8888",
EnvVar: "CFSSL_SERVER",
Usage: "CFSSL Server URI",
},
cli.StringFlag{
Name: "cfsslAuthKey",
EnvVar: "CFSSL_AUTH_KEY",
Usage: "CFSSL authentication key",
},
cli.StringFlag{
Name: "cfsslProfile",
EnvVar: "CFSSL_PROFILE",
Usage: "CFSSL signing profile",
},
}

// One command per element of the system
Expand All @@ -79,19 +90,32 @@ func main() {
Name: "monolithic",
Usage: "Start the CA in monolithic mode, without using AMQP",
Action: func(c *cli.Context) {

// XXX Print the config
fmt.Println(c.GlobalString("amqp"))
fmt.Println(c.GlobalString("cfssl"))
fmt.Println(c.GlobalString("cfsslAuthKey"))
fmt.Println(c.GlobalString("cfsslProfile"))

// Grab parameters
cfsslServer := c.GlobalString("cfssl")
authKey := c.GlobalString("cfsslAuthKey")
profile := c.GlobalString("cfsslProfile")

// Create the components
wfe := boulder.NewWebFrontEndImpl()
sa := boulder.NewSimpleStorageAuthorityImpl()
sa, err := boulder.NewSQLStorageAuthority("sqlite3", ":memory:")
failOnError(err, "Unable to create SA")
ra := boulder.NewRegistrationAuthorityImpl()
va := boulder.NewValidationAuthorityImpl()
ca, err := boulder.NewCertificateAuthorityImpl()
ca, err := boulder.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
failOnError(err, "Unable to create CA")

// Wire them up
wfe.RA = &ra
wfe.SA = &sa
ra.CA = &ca
ra.SA = &sa
wfe.SA = sa
ra.CA = ca
ra.SA = sa
ra.VA = &va
va.RA = &ra

Expand All @@ -115,6 +139,11 @@ func main() {
Name: "monolithic-amqp",
Usage: "Start the CA in monolithic mode, using AMQP",
Action: func(c *cli.Context) {
// Grab parameters
cfsslServer := c.GlobalString("cfssl")
authKey := c.GlobalString("cfsslAuthKey")
profile := c.GlobalString("cfsslProfile")

// Create an AMQP channel
ch := amqpChannel(c.GlobalString("amqp"))

Expand All @@ -131,13 +160,17 @@ func main() {
// ... and corresponding servers
// (We need this order so that we can give the servers
// references to the clients)
cas, err := boulder.NewCertificateAuthorityServer("CA.server", ch)
cai, err := boulder.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
failOnError(err, "Failed to create CA impl")
cas, err := boulder.NewCertificateAuthorityServer("CA.server", ch, cai)
failOnError(err, "Failed to create CA server")
vas, err := boulder.NewValidationAuthorityServer("VA.server", ch, &rac)
failOnError(err, "Failed to create VA server")
ras, err := boulder.NewRegistrationAuthorityServer("RA.server", ch, &vac, &cac, &sac)
failOnError(err, "Failed to create RA server")
sas := boulder.NewStorageAuthorityServer("SA.server", ch)
sai, err := boulder.NewSQLStorageAuthority("sqlite3", ":memory:")
failOnError(err, "Failed to create SA impl")
sas := boulder.NewStorageAuthorityServer("SA.server", ch, sai)

// Start the servers
cas.Start()
Expand Down Expand Up @@ -203,9 +236,16 @@ func main() {
Name: "ca",
Usage: "Start the CertificateAuthority",
Action: func(c *cli.Context) {
// Grab parameters
cfsslServer := c.GlobalString("cfssl")
authKey := c.GlobalString("cfsslAuthKey")
profile := c.GlobalString("cfsslProfile")

ch := amqpChannel(c.GlobalString("amqp"))

cas, err := boulder.NewCertificateAuthorityServer("CA.server", ch)
cai, err := boulder.NewCertificateAuthorityImpl(cfsslServer, authKey, profile)
failOnError(err, "Failed to create CA impl")
cas, err := boulder.NewCertificateAuthorityServer("CA.server", ch, cai)
failOnError(err, "Unable to create CA server")
runForever(cas)
},
Expand All @@ -216,7 +256,9 @@ func main() {
Action: func(c *cli.Context) {
ch := amqpChannel(c.GlobalString("amqp"))

sas := boulder.NewStorageAuthorityServer("SA.server", ch)
sai, err := boulder.NewSQLStorageAuthority("sqlite3", ":memory:")
failOnError(err, "Failed to create SA impl")
sas := boulder.NewStorageAuthorityServer("SA.server", ch, sai)
runForever(sas)
},
},
Expand Down
156 changes: 69 additions & 87 deletions certificate-authority.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,117 +6,99 @@
package boulder

import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"math/big"
"time"

"github.com/cloudflare/cfssl/auth"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/remote"
)

type CertificateAuthorityImpl struct {
privateKey interface{}
certificate x509.Certificate
derCertificate []byte
signer signer.Signer
profile string
SA StorageAuthority
}

var (
serialNumberBits = uint(64)
oneYear = 365 * 24 * time.Hour
rootCertificateTemplate = x509.Certificate{
SignatureAlgorithm: x509.SHA256WithRSA,
Subject: pkix.Name{Organization: []string{"ACME CA"}},
IsCA: true,
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature |
x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
eeCertificateTemplate = x509.Certificate{
SignatureAlgorithm: x509.SHA256WithRSA,
IsCA: false,
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
// NewCertificateAuthorityImpl creates a CA that talks to a remote CFSSL
// instance. (To use a local signer, simply instantiate CertificateAuthorityImpl
// directly.) Communications with the CA are authenticated with MACs,
// using CFSSL's authenticated signature scheme. A CA created in this way
// issues for a single profile on the remote signer, which is indicated
// by name in this constructor.
func NewCertificateAuthorityImpl(hostport string, authKey string, profile string) (ca *CertificateAuthorityImpl, err error) {
// Create the remote signer
localProfile := config.SigningProfile{
Expiry: 60 * time.Minute, // BOGUS: Required by CFSSL, but not used
RemoteName: hostport,
}
)

func newSerialNumber() (*big.Int, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), serialNumberBits)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
localProfile.Provider, err = auth.New(authKey, nil)
if err != nil {
return nil, err
return
}
return serialNumber, nil
}

func NewCertificateAuthorityImpl() (CertificateAuthorityImpl, error) {
zero := CertificateAuthorityImpl{}

// Generate a key pair
priv, err := rsa.GenerateKey(rand.Reader, 2048)
signer, err := remote.NewSigner(&config.Signing{Default: &localProfile})
if err != nil {
return zero, err
return
}

// Sign the certificate
template := rootCertificateTemplate
template.SerialNumber, err = newSerialNumber()
if err != nil {
return zero, err
ca = &CertificateAuthorityImpl{signer: signer, profile: profile}
return
}

func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest) (certID string, cert []byte, err error) {
// XXX Take in authorizations and verify that union covers CSR?
// Pull hostnames from CSR
hostNames := csr.DNSNames // DNSNames + CN from CSR
if len(hostNames) < 1 {
err = errors.New("Cannot issue a certificate without a hostname.")
return
}
template.NotBefore = time.Now()
template.NotAfter = template.NotBefore.Add(oneYear)
der, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return zero, err
var commonName string
if len(csr.Subject.CommonName) > 0 {
commonName = csr.Subject.CommonName
} else {
commonName = hostNames[0]
}

// Parse the certificate
certs, err := x509.ParseCertificates(der)
if err != nil || len(certs) == 0 {
return zero, err
// Convert the CSR to PEM
csrPEM := string(pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr.Raw,
}))

// Send the cert off for signing
req := signer.SignRequest{
Request: csrPEM,
Profile: ca.profile,
Hostname: commonName,
Subject: &signer.Subject{
CN: commonName,
Hosts: hostNames,
},
}

return CertificateAuthorityImpl{
privateKey: priv,
certificate: *certs[0],
derCertificate: der,
}, nil
}

func (ca *CertificateAuthorityImpl) CACertificate() []byte {
return ca.derCertificate
}

func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest) ([]byte, error) {
template := eeCertificateTemplate

// Set serial
serialNumber, err := newSerialNumber()
certPEM, err := ca.signer.Sign(req)
if err != nil {
return nil, err
return
}
template.SerialNumber = serialNumber

// Set validity
template.NotBefore = time.Now()
template.NotAfter = template.NotBefore.Add(oneYear)

// Set hostnames
domains := csr.DNSNames
if len(csr.Subject.CommonName) > 0 {
domains = append(domains, csr.Subject.CommonName)
if len(certPEM) == 0 {
err = errors.New("No certificate returned by server")
return
}
if len(domains) == 0 {
return []byte{}, errors.New("No names provided for certificate")

block, _ := pem.Decode(certPEM)
if block == nil || block.Type != "CERTIFICATE" {
err = errors.New("Invalid certificate value returned")
return
}
template.Subject = pkix.Name{CommonName: domains[0]}
template.DNSNames = domains
cert = block.Bytes

// Sign
return x509.CreateCertificate(rand.Reader, &template, &ca.certificate, csr.PublicKey, ca.privateKey)
// Store the cert with the certificate authority, if provided
certID, err = ca.SA.AddCertificate(cert)
return
}

0 comments on commit b49020c

Please sign in to comment.