github.com/philhug/dnscontrol@v0.2.4-0.20180625181521-921fa9849001/pkg/acme/registration.go (about)

     1  package acme
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/ecdsa"
     6  	"crypto/elliptic"
     7  	"crypto/rand"
     8  	"crypto/x509"
     9  	"encoding/json"
    10  	"encoding/pem"
    11  	"io/ioutil"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  
    16  	"github.com/xenolf/lego/acmev2"
    17  )
    18  
    19  func (c *certManager) loadOrCreateAccount() error {
    20  	f, err := os.Open(c.accountFile())
    21  	if err != nil && os.IsNotExist(err) {
    22  		return c.createAccount()
    23  	}
    24  	if err != nil {
    25  		return err
    26  	}
    27  	defer f.Close()
    28  	dec := json.NewDecoder(f)
    29  	acct := &account{}
    30  	if err = dec.Decode(acct); err != nil {
    31  		return err
    32  	}
    33  	c.account = acct
    34  	keyBytes, err := ioutil.ReadFile(c.accountKeyFile())
    35  	if err != nil {
    36  		return err
    37  	}
    38  	keyBlock, _ := pem.Decode(keyBytes)
    39  	if keyBlock == nil {
    40  		log.Fatal("WTF", keyBytes)
    41  	}
    42  	c.account.key, err = x509.ParseECPrivateKey(keyBlock.Bytes)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	c.client, err = acme.NewClient(c.acmeDirectory, c.account, acme.RSA2048) // TODO: possibly make configurable on a cert-by cert basis
    47  	if err != nil {
    48  		return err
    49  	}
    50  	return nil
    51  }
    52  
    53  func (c *certManager) accountDirectory() string {
    54  	return filepath.Join(c.directory, ".letsencrypt", c.acmeHost)
    55  }
    56  
    57  func (c *certManager) accountFile() string {
    58  	return filepath.Join(c.accountDirectory(), "account.json")
    59  }
    60  func (c *certManager) accountKeyFile() string {
    61  	return filepath.Join(c.accountDirectory(), "account.key")
    62  }
    63  
    64  const perms os.FileMode = 0644 // TODO: probably lock this down more
    65  
    66  func (c *certManager) createAccount() error {
    67  	if err := os.MkdirAll(c.accountDirectory(), perms); err != nil {
    68  		return err
    69  	}
    70  	privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	acct := &account{
    75  		key:   privateKey,
    76  		Email: c.email,
    77  	}
    78  	c.account = acct
    79  	c.client, err = acme.NewClient(c.acmeDirectory, c.account, acme.EC384)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	reg, err := c.client.Register(true)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	c.account.Registration = reg
    88  	acctBytes, err := json.MarshalIndent(c.account, "", "  ")
    89  	if err != nil {
    90  		return err
    91  	}
    92  	if err = ioutil.WriteFile(c.accountFile(), acctBytes, perms); err != nil {
    93  		return err
    94  	}
    95  	keyBytes, err := x509.MarshalECPrivateKey(privateKey)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	pemKey := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
   100  	pemBytes := pem.EncodeToMemory(pemKey)
   101  	if err = ioutil.WriteFile(c.accountKeyFile(), pemBytes, perms); err != nil {
   102  		return err
   103  	}
   104  	return nil
   105  }
   106  
   107  type account struct {
   108  	Email        string `json:"email"`
   109  	key          crypto.PrivateKey
   110  	Registration *acme.RegistrationResource `json:"registration"`
   111  }
   112  
   113  func (a *account) GetEmail() string {
   114  	return a.Email
   115  }
   116  func (a *account) GetPrivateKey() crypto.PrivateKey {
   117  	return a.key
   118  }
   119  func (a *account) GetRegistration() *acme.RegistrationResource {
   120  	return a.Registration
   121  }