Address most of Armon's initial feedback.

Commit contents (C)2015 Akamai Technologies, Inc. <opensource@akamai.com>
This commit is contained in:
Jeff Mitchell 2015-06-11 21:57:05 -04:00
parent db5354823f
commit 018c0ec7f5
4 changed files with 77 additions and 53 deletions

View file

@ -23,7 +23,7 @@ func Backend() *framework.Backend {
"config/*",
"revoked/*",
"revoke/*",
"crl/build",
"crl/rotate",
},
Unauthenticated: []string{
"cert/*",

View file

@ -5,7 +5,7 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
crand "crypto/rand"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
@ -13,7 +13,6 @@ import (
"encoding/base64"
"fmt"
"math/big"
mrand "math/rand"
"net"
"regexp"
"strings"
@ -281,16 +280,19 @@ func validateCommonNames(req *logical.Request, commonNames []string, role *roleE
}
func createCertificate(creationInfo *certCreationBundle) (rawBundle *rawCertBundle, userErr, intErr error) {
rawBundle = &rawCertBundle{
SerialNumber: (&big.Int{}).Rand(mrand.New(mrand.NewSource(time.Now().UnixNano())), (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil)),
}
var clientPrivKey crypto.Signer
var err error
rawBundle = &rawCertBundle{}
rawBundle.SerialNumber, err = rand.Int(rand.Reader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
if err != nil {
return nil, nil, fmt.Errorf("Error getting random serial number")
}
switch creationInfo.KeyType {
case "rsa":
rawBundle.PrivateKeyType = RSAPrivateKeyType
clientPrivKey, err = rsa.GenerateKey(crand.Reader, creationInfo.KeyBits)
clientPrivKey, err = rsa.GenerateKey(rand.Reader, creationInfo.KeyBits)
if err != nil {
return nil, nil, fmt.Errorf("Error generating RSA private key")
}
@ -310,7 +312,7 @@ func createCertificate(creationInfo *certCreationBundle) (rawBundle *rawCertBund
default:
return nil, fmt.Errorf("Unsupported bit length for EC key: %d", creationInfo.KeyBits), nil
}
clientPrivKey, err = ecdsa.GenerateKey(curve, crand.Reader)
clientPrivKey, err = ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("Error generating EC private key")
}
@ -371,7 +373,7 @@ func createCertificate(creationInfo *certCreationBundle) (rawBundle *rawCertBund
return nil, nil, fmt.Errorf("Unable to get signing private key: %s", err)
}
cert, err := x509.CreateCertificate(crand.Reader, certTemplate, creationInfo.CACert, clientPrivKey.Public(), signingPrivKey)
cert, err := x509.CreateCertificate(rand.Reader, certTemplate, creationInfo.CACert, clientPrivKey.Public(), signingPrivKey)
if err != nil {
return nil, nil, fmt.Errorf("Unable to create certificate: %s", err)
}

View file

@ -15,55 +15,71 @@ type revocationInfo struct {
RevocationTime int64 `json:"unix_time"`
}
var (
crlLifetime = time.Hour * 72
)
func revokeCert(req *logical.Request, serial string) (*logical.Response, error) {
alreadyRevoked := false
var err error
revInfo := revocationInfo{}
certEntry, userErr, intErr := fetchCertBySerial(req, "revoked/", serial)
if certEntry != nil {
return nil, nil
// Verify that it is also deleted from certs/
// in case of partial failure from an earlier run.
certEntry, _, _ = fetchCertBySerial(req, "certs/", serial)
if certEntry != nil {
alreadyRevoked = true
revEntry, err := req.Storage.Get("revoked/" + serial)
if err != nil {
return nil, fmt.Errorf("Error getting existing revocation info")
}
err = revEntry.DecodeJSON(&revInfo)
if err != nil {
return nil, fmt.Errorf("Error decoding existing revocation info")
}
} else {
return nil, nil
}
}
certEntry, userErr, intErr = fetchCertBySerial(req, "certs/", serial)
switch {
case userErr != nil:
return logical.ErrorResponse(userErr.Error()), nil
case intErr != nil:
return nil, intErr
}
if !alreadyRevoked {
certEntry, userErr, intErr = fetchCertBySerial(req, "certs/", serial)
switch {
case userErr != nil:
return logical.ErrorResponse(userErr.Error()), nil
case intErr != nil:
return nil, intErr
}
// Possible TODO: use some kind of transaction log in case of an
// error anywhere along here (so we've validated that we got a
// value back, but want to make sure it not only is deleted from
// certs/ but also shows up in revoked/ and a CRL is generated)
err := req.Storage.Delete("certs/" + serial)
cert, err := x509.ParseCertificate(certEntry.Value)
if err != nil {
return nil, fmt.Errorf("Error parsing certificate")
}
if cert == nil {
return nil, fmt.Errorf("Got a nil certificate")
}
if err != nil {
return nil, fmt.Errorf("Error deleting cert from valid-certs location")
}
if cert.NotAfter.Before(time.Now()) {
return nil, nil
}
cert, err := x509.ParseCertificate(certEntry.Value)
if err != nil {
return nil, fmt.Errorf("Error parsing certificate")
}
if cert == nil {
return nil, fmt.Errorf("Got a nil certificate")
}
revInfo.CertificateBytes = certEntry.Value
revInfo.RevocationTime = time.Now().Unix()
if cert.NotAfter.Before(time.Now()) {
return nil, nil
}
certEntry, err = logical.StorageEntryJSON("revoked/"+serial, revInfo)
if err != nil {
return nil, fmt.Errorf("Error creating revocation entry")
}
revInfo := revocationInfo{
CertificateBytes: certEntry.Value,
RevocationTime: time.Now().Unix(),
}
err = req.Storage.Put(certEntry)
if err != nil {
return nil, fmt.Errorf("Error saving revoked certificate to new location")
}
certEntry, err = logical.StorageEntryJSON("revoked/"+serial, revInfo)
if err != nil {
return nil, fmt.Errorf("Error creating revocation entry")
}
err = req.Storage.Put(certEntry)
if err != nil {
return nil, fmt.Errorf("Error saving revoked certificate to new location")
}
err = buildCRL(req)
@ -71,6 +87,12 @@ func revokeCert(req *logical.Request, serial string) (*logical.Response, error)
return nil, fmt.Errorf("Error encountered during CRL building: %s", err)
}
err = req.Storage.Delete("certs/" + serial)
if err != nil {
return nil, fmt.Errorf("Error deleting cert from valid-certs location")
}
return &logical.Response{
Data: map[string]interface{}{
"revocation_time": revInfo.RevocationTime,
@ -136,7 +158,7 @@ func buildCRL(req *logical.Request) error {
}
// TODO: Make expiry configurable
crlBytes, err := caCert.CreateCRL(rand.Reader, signingPrivKey, revokedCerts, time.Now(), time.Now().Add(time.Hour*72))
crlBytes, err := caCert.CreateCRL(rand.Reader, signingPrivKey, revokedCerts, time.Now(), time.Now().Add(crlLifetime))
if err != nil {
return fmt.Errorf("Error creating new CRL: %s", err)
}

View file

@ -107,7 +107,7 @@ func (b *backend) pathIssueCert(
"Invalid lease: %s", err)), nil
}
if time.Now().Add(lease).After(time.Now().Add(leaseMax)) {
if lease > leaseMax {
return logical.ErrorResponse("Lease expires after maximum allowed by this role"), nil
}
@ -159,12 +159,12 @@ func (b *backend) pathIssueCert(
serial := strings.ToLower(getOctalFormatted(rawBundle.SerialNumber.Bytes(), ":"))
resp := b.Secret(SecretCertsType).Response(map[string]interface{}{}, map[string]interface{}{
resp := b.Secret(SecretCertsType).Response(map[string]interface{}{
"serial": serial,
}, map[string]interface{}{
"serial": serial,
})
resp.Data["serial"] = serial
block := pem.Block{
Type: "CERTIFICATE",
Bytes: rawBundle.CertificateBytes,