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 }