Partly done implementation of revoke.
This commit is contained in:
parent
1d2c6a5d7c
commit
8e30ff81fb
|
@ -6,10 +6,12 @@
|
|||
package ca
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
@ -17,21 +19,40 @@ import (
|
|||
"github.com/letsencrypt/boulder/policy"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||
cfsslConfig "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||
"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/ocsp"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||
)
|
||||
type Config struct {
|
||||
Server string
|
||||
AuthKey string
|
||||
Profile string
|
||||
TestMode bool
|
||||
DBDriver string
|
||||
DBName string
|
||||
SerialPrefix int
|
||||
// A PEM-encoded copy of the issuer certificate.
|
||||
IssuerCert string
|
||||
// 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
|
||||
}
|
||||
|
||||
// CertificateAuthorityImpl represents a CA that signs certificates, CRLs, and
|
||||
// OCSP responses.
|
||||
type CertificateAuthorityImpl struct {
|
||||
profile string
|
||||
Signer signer.Signer
|
||||
SA core.StorageAuthority
|
||||
PA core.PolicyAuthority
|
||||
DB core.CertificateAuthorityDatabase
|
||||
log *blog.AuditLogger
|
||||
Prefix int // Prepended to the serial number
|
||||
profile string
|
||||
Signer signer.Signer
|
||||
OCSPSigner ocsp.Signer
|
||||
SA core.StorageAuthority
|
||||
PA core.PolicyAuthority
|
||||
DB core.CertificateAuthorityDatabase
|
||||
log *blog.AuditLogger
|
||||
Prefix int // Prepended to the serial number
|
||||
}
|
||||
|
||||
// NewCertificateAuthorityImpl creates a CA that talks to a remote CFSSL
|
||||
|
@ -40,44 +61,120 @@ type CertificateAuthorityImpl struct {
|
|||
// 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(logger *blog.AuditLogger, hostport string, authKey string, profile string, serialPrefix int, cadb core.CertificateAuthorityDatabase) (ca *CertificateAuthorityImpl, err error) {
|
||||
func NewCertificateAuthorityImpl(logger *blog.AuditLogger,
|
||||
cadb core.CertificateAuthorityDatabase, config Config) (ca *CertificateAuthorityImpl, err error) {
|
||||
logger.Notice("Certificate Authority Starting")
|
||||
|
||||
if config.SerialPrefix == 0 {
|
||||
err = errors.New("Must have non-zero serial prefix for CA.")
|
||||
}
|
||||
|
||||
// Create the remote signer
|
||||
localProfile := config.SigningProfile{
|
||||
localProfile := cfsslConfig.SigningProfile{
|
||||
Expiry: time.Hour, // BOGUS: Required by CFSSL, but not used
|
||||
RemoteName: hostport, // BOGUS: Only used as a flag by CFSSL
|
||||
RemoteServer: hostport,
|
||||
RemoteName: config.Server, // BOGUS: Only used as a flag by CFSSL
|
||||
RemoteServer: config.Server,
|
||||
UseSerialSeq: true,
|
||||
}
|
||||
|
||||
localProfile.Provider, err = auth.New(authKey, nil)
|
||||
localProfile.Provider, err = auth.New(config.AuthKey, nil)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signer, err := remote.NewSigner(&config.Signing{Default: &localProfile})
|
||||
signer, err := remote.NewSigner(&cfsslConfig.Signing{Default: &localProfile})
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
|
||||
issuer, err := loadIssuer(config.IssuerCert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// In test mode, load a private key from a file. In production, use an HSM.
|
||||
if !config.TestMode {
|
||||
err = errors.New("OCSP signing with a PKCS#11 key not yet implemented.")
|
||||
return nil, err
|
||||
}
|
||||
issuerKey, err := loadIssuerKey(config.IssuerKey)
|
||||
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 := ocsp.NewSigner(issuer, issuer, issuerKey,
|
||||
time.Hour * 24 * 4)
|
||||
|
||||
pa := policy.NewPolicyAuthorityImpl(logger)
|
||||
|
||||
ca = &CertificateAuthorityImpl{
|
||||
Signer: signer,
|
||||
profile: profile,
|
||||
OCSPSigner: ocspSigner,
|
||||
profile: config.Profile,
|
||||
PA: pa,
|
||||
DB: cadb,
|
||||
Prefix: serialPrefix,
|
||||
Prefix: config.SerialPrefix,
|
||||
log: logger,
|
||||
}
|
||||
return ca, err
|
||||
}
|
||||
|
||||
func loadIssuer(filename string) (issuerCert *x509.Certificate, err error) {
|
||||
if filename == "" {
|
||||
err = errors.New("Issuer certificate was not provided in config.")
|
||||
return
|
||||
}
|
||||
issuerCertPEM, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
issuerCert, err = helpers.ParseCertificatePEM(issuerCertPEM)
|
||||
return
|
||||
}
|
||||
|
||||
func (ca *CertificateAuthorityImpl) RevokeCertificate(serial string) (cert core.Certificate, err error) {
|
||||
func loadIssuerKey(filename string) (issuerKey crypto.Signer, err error) {
|
||||
if filename == "" {
|
||||
err = errors.New("IssuerKey must be provided in test mode.")
|
||||
return
|
||||
}
|
||||
|
||||
pem, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
issuerKey, err = helpers.ParsePrivateKeyPEM(pem)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (ca *CertificateAuthorityImpl) RevokeCertificate(serial string) (err error) {
|
||||
certDER, err := ca.SA.GetCertificate(serial)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signRequest := ocsp.SignRequest{
|
||||
Certificate: cert,
|
||||
Status: string(core.OCSPStatusRevoked),
|
||||
// Per https://tools.ietf.org/html/rfc5280, CRLReason 0 is "unspecified."
|
||||
// TODO: Add support for specifying reason.
|
||||
Reason: 0,
|
||||
RevokedAt: time.Now(),
|
||||
}
|
||||
ocspResponse, err := ca.OCSPSigner.Sign(signRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ca.SA.MarkCertificateRevoked(serial, ocspResponse)
|
||||
return err
|
||||
}
|
||||
|
||||
// IssueCertificate attempts to convert a CSR into a signed Certificate, while
|
||||
// enforcing all policies.
|
||||
func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest) (cert core.Certificate, err error) {
|
||||
|
|
|
@ -306,7 +306,16 @@ func TestIssueCertificate(t *testing.T) {
|
|||
|
||||
// Create a CA
|
||||
// Uncomment to test with a remote signer
|
||||
ca, err := NewCertificateAuthorityImpl(blog.TestLogger(), hostPort, authKey, profileName, 17, cadb)
|
||||
caConfig := Config{
|
||||
Server: hostPort,
|
||||
AuthKey: authKey,
|
||||
Profile: profileName,
|
||||
SerialPrefix: 17,
|
||||
IssuerCert: "../test/test-ca.pem",
|
||||
IssuerKey: "../test/test-ca.key",
|
||||
TestMode: true,
|
||||
}
|
||||
ca, err := NewCertificateAuthorityImpl(blog.TestLogger(), cadb, caConfig)
|
||||
test.AssertNotError(t, err, "Failed to create CA")
|
||||
ca.SA = sa
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ func main() {
|
|||
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(auditlogger, c.CA.DBDriver, c.CA.DBName)
|
||||
cmd.FailOnError(err, "Failed to create CA database")
|
||||
|
||||
cai, err := ca.NewCertificateAuthorityImpl(auditlogger, c.CA.Server, c.CA.AuthKey, c.CA.Profile, c.CA.SerialPrefix, cadb)
|
||||
cai, err := ca.NewCertificateAuthorityImpl(auditlogger, cadb, c.CA)
|
||||
cmd.FailOnError(err, "Failed to create CA impl")
|
||||
|
||||
go cmd.ProfileCmd("CA", stats)
|
||||
|
|
11
cmd/shell.go
11
cmd/shell.go
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/rpc"
|
||||
"github.com/letsencrypt/boulder/ca"
|
||||
)
|
||||
|
||||
// Config stores configuration parameters that applications
|
||||
|
@ -59,15 +60,7 @@ type Config struct {
|
|||
ListenAddress string
|
||||
}
|
||||
|
||||
CA struct {
|
||||
Server string
|
||||
AuthKey string
|
||||
Profile string
|
||||
TestMode bool
|
||||
DBDriver string
|
||||
DBName string
|
||||
SerialPrefix int
|
||||
}
|
||||
CA ca.Config
|
||||
|
||||
SA struct {
|
||||
DBDriver string
|
||||
|
|
|
@ -100,6 +100,7 @@ type StorageAdder interface {
|
|||
NewPendingAuthorization() (string, error)
|
||||
UpdatePendingAuthorization(Authorization) error
|
||||
FinalizeAuthorization(Authorization) error
|
||||
MarkCertificateRevoked(serial string, ocspResponse []byte) error
|
||||
|
||||
AddCertificate([]byte) (string, error)
|
||||
}
|
||||
|
|
|
@ -271,9 +271,9 @@ func (ssa *SQLStorageAuthority) GetCertificate(serial string) (cert []byte, err
|
|||
// GetCertificateStatus takes a hexadecimal string representing the full 128-bit serial
|
||||
// number of a certificate and returns data about that certificate's current
|
||||
// validity.
|
||||
func (ssa *SQLStorageAuthority) GetCertificateStatus(id string) (status core.CertificateStatus, err error) {
|
||||
if len(id) != 32 {
|
||||
err = errors.New("Invalid certificate serial " + id)
|
||||
func (ssa *SQLStorageAuthority) GetCertificateStatus(serial string) (status core.CertificateStatus, err error) {
|
||||
if len(serial) != 32 {
|
||||
err = errors.New("Invalid certificate serial " + serial)
|
||||
return
|
||||
}
|
||||
var statusString string;
|
||||
|
@ -281,7 +281,7 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(id string) (status core.Cer
|
|||
`SELECT subscriberApproved, status, ocspLastUpdated
|
||||
FROM certificateStatus
|
||||
WHERE serial = ?
|
||||
LIMIT 1;`, id).Scan(&status.SubscriberApproved, &statusString, &status.OCSPLastUpdated)
|
||||
LIMIT 1;`, serial).Scan(&status.SubscriberApproved, &statusString, &status.OCSPLastUpdated)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -316,15 +316,30 @@ func (ssa *SQLStorageAuthority) NewRegistration() (id string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string,
|
||||
ocspResponse []byte) (err error) {
|
||||
func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspResponse []byte) (err error) {
|
||||
if _, err = ssa.GetCertificate(serial); err != nil {
|
||||
return errors.New(fmt.Sprintf(
|
||||
"Unable to mark certificate %s revoked: cert not found.", serial))
|
||||
}
|
||||
|
||||
if _, err = ssa.GetCertificateStatus(serial); err != nil {
|
||||
return errors.New(fmt.Sprintf(
|
||||
"Unable to mark certificate %s revoked: cert status not found.", serial))
|
||||
}
|
||||
|
||||
tx, err := ssa.db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//TODO ...
|
||||
|
||||
_, err = tx.Exec("UPDATE certificateStatus SET status=?, revokedDate=?",
|
||||
string(core.OCSPStatusRevoked), time.Now())
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
err = tx.Commit()
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err error) {
|
||||
|
|
|
@ -42,7 +42,10 @@
|
|||
"profile": "ee",
|
||||
"dbDriver": "sqlite3",
|
||||
"dbName": "boulder-ca.sqlite3",
|
||||
"testMode": true
|
||||
"testMode": true,
|
||||
"issuerCert": "test/test-ca.pem",
|
||||
"_comment": "This should only be present in testMode. In prod use an HSM."
|
||||
"issuerKey": "test/test-ca.key"
|
||||
},
|
||||
|
||||
"sa": {
|
||||
|
|
Loading…
Reference in New Issue