Skip to content

Commit

Permalink
Challenge certs PEM encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
emilevauge committed Sep 23, 2016
1 parent 8140e44 commit 36b539d
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 33 deletions.
19 changes: 14 additions & 5 deletions acme/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ type Account struct {
Registration *acme.RegistrationResource
PrivateKey []byte
DomainsCertificate DomainsCertificates
ChallengeCerts map[string][]byte
ChallengeCerts map[string]ChallengeCert
}

// ChallengeCert stores a challenge certificate
type ChallengeCert struct {
Certificate []byte
PrivateKey []byte
certificate *tls.Certificate
}

// Init inits acccount struct
Expand All @@ -32,6 +39,7 @@ func (a *Account) Init() error {
return nil
}

// NewAccount creates an account
func NewAccount(email string) (*Account, error) {
// Create a user. New accounts need an email and private key to start
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
Expand All @@ -44,21 +52,21 @@ func NewAccount(email string) (*Account, error) {
Email: email,
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
DomainsCertificate: domainsCerts,
ChallengeCerts: map[string][]byte{}}, nil
ChallengeCerts: map[string]ChallengeCert{}}, nil
}

// GetEmail returns email
func (a Account) GetEmail() string {
func (a *Account) GetEmail() string {
return a.Email
}

// GetRegistration returns lets encrypt registration resource
func (a Account) GetRegistration() *acme.RegistrationResource {
func (a *Account) GetRegistration() *acme.RegistrationResource {
return a.Registration
}

// GetPrivateKey returns private key
func (a Account) GetPrivateKey() crypto.PrivateKey {
func (a *Account) GetPrivateKey() crypto.PrivateKey {
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
return privateKey
}
Expand All @@ -81,6 +89,7 @@ type DomainsCertificates struct {
lock sync.RWMutex
}

// Init inits DomainsCertificates
func (dc *DomainsCertificates) Init() error {
dc.lock.Lock()
defer dc.lock.Unlock()
Expand Down
38 changes: 10 additions & 28 deletions acme/challengeProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,13 @@ import (
"crypto/tls"
"sync"

"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/gob"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/xenolf/lego/acme"
"time"
)

func init() {
gob.Register(rsa.PrivateKey{})
gob.Register(rsa.PublicKey{})
}

var _ acme.ChallengeProviderTimeout = (*challengeProvider)(nil)

type challengeProvider struct {
Expand All @@ -40,28 +32,24 @@ func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate
if account.ChallengeCerts == nil {
return nil, false
}
if certBinary, ok := account.ChallengeCerts[domain]; ok {
cert := &tls.Certificate{}
var buffer bytes.Buffer
buffer.Write(certBinary)
dec := gob.NewDecoder(&buffer)
err := dec.Decode(cert)
if challenge, ok := account.ChallengeCerts[domain]; ok {
cert, err := tls.X509KeyPair(challenge.Certificate, challenge.PrivateKey)
if err != nil {
log.Errorf("Error unmarshaling challenge cert %s", err.Error())
log.Errorf("Error loading challenge cert %s", err.Error())
return nil, false
}
return cert, true
return &cert, true
}
return nil, false
}

func (c *challengeProvider) Present(domain, token, keyAuth string) error {
log.Debugf("Challenge Present %s", domain)
cert, _, err := acme.TLSSNI01ChallengeCert(keyAuth)
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
if err != nil {
return err
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
if err != nil {
return err
}
Expand All @@ -74,17 +62,11 @@ func (c *challengeProvider) Present(domain, token, keyAuth string) error {
}
account := object.(*Account)
if account.ChallengeCerts == nil {
account.ChallengeCerts = map[string][]byte{}
account.ChallengeCerts = map[string]ChallengeCert{}
}
for i := range cert.Leaf.DNSNames {
var buffer bytes.Buffer
enc := gob.NewEncoder(&buffer)
err := enc.Encode(cert)
if err != nil {
return err
}
account.ChallengeCerts[cert.Leaf.DNSNames[i]] = buffer.Bytes()
log.Debugf("Challenge Present cert: %s", cert.Leaf.DNSNames[i])
for i := range leaf.DNSNames {
account.ChallengeCerts[leaf.DNSNames[i]] = cert
log.Debugf("Challenge Present cert: %s", leaf.DNSNames[i])
}
return transaction.Commit(account)
}
Expand Down
47 changes: 47 additions & 0 deletions acme/crypto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package acme

import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
Expand Down Expand Up @@ -76,3 +78,48 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin

return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
}

// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
func TLSSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
// generate a new RSA key for the certificates
var tempPrivKey crypto.PrivateKey
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return ChallengeCert{}, "", err
}
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
rsaPrivPEM := pemEncode(rsaPrivKey)

zBytes := sha256.Sum256([]byte(keyAuth))
z := hex.EncodeToString(zBytes[:sha256.Size])
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
if err != nil {
return ChallengeCert{}, "", err
}

certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
if err != nil {
return ChallengeCert{}, "", err
}

return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
}
func pemEncode(data interface{}) []byte {
var pemBlock *pem.Block
switch key := data.(type) {
case *ecdsa.PrivateKey:
keyBytes, _ := x509.MarshalECPrivateKey(key)
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
case *rsa.PrivateKey:
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
break
case *x509.CertificateRequest:
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
break
case []byte:
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
}

return pem.EncodeToMemory(pemBlock)
}

0 comments on commit 36b539d

Please sign in to comment.