github.com/StackExchange/DNSControl@v0.2.8/pkg/acme/directoryStorage.go (about)

     1  package acme
     2  
     3  import (
     4  	"crypto/x509"
     5  	"encoding/json"
     6  	"encoding/pem"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/xenolf/lego/acme"
    13  )
    14  
    15  // directoryStorage implements storage in a local file directory
    16  type directoryStorage string
    17  
    18  // filename for certificate / key / json file
    19  func (d directoryStorage) certFile(name, ext string) string {
    20  	return filepath.Join(d.certDir(name), name+"."+ext)
    21  }
    22  func (d directoryStorage) certDir(name string) string {
    23  	return filepath.Join(string(d), "certificates", name)
    24  }
    25  
    26  func (d directoryStorage) accountDirectory(acmeHost string) string {
    27  	return filepath.Join(string(d), ".letsencrypt", acmeHost)
    28  }
    29  
    30  func (d directoryStorage) accountFile(acmeHost string) string {
    31  	return filepath.Join(d.accountDirectory(acmeHost), "account.json")
    32  }
    33  func (d directoryStorage) accountKeyFile(acmeHost string) string {
    34  	return filepath.Join(d.accountDirectory(acmeHost), "account.key")
    35  }
    36  
    37  // TODO: probably lock these down more
    38  const perms os.FileMode = 0644
    39  const dirPerms os.FileMode = 0700
    40  
    41  func (d directoryStorage) GetCertificate(name string) (*acme.CertificateResource, error) {
    42  	f, err := os.Open(d.certFile(name, "json"))
    43  	if err != nil && os.IsNotExist(err) {
    44  		// if json does not exist, nothing does
    45  		return nil, nil
    46  	}
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	defer f.Close()
    51  	dec := json.NewDecoder(f)
    52  	cr := &acme.CertificateResource{}
    53  	if err = dec.Decode(cr); err != nil {
    54  		return nil, err
    55  	}
    56  	// load cert
    57  	crtBytes, err := ioutil.ReadFile(d.certFile(name, "crt"))
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	cr.Certificate = crtBytes
    62  	return cr, nil
    63  }
    64  
    65  func (d directoryStorage) StoreCertificate(name string, cert *acme.CertificateResource) error {
    66  	// make sure actual cert data never gets into metadata json
    67  	if err := os.MkdirAll(d.certDir(name), dirPerms); err != nil {
    68  		return err
    69  	}
    70  	pub := cert.Certificate
    71  	cert.Certificate = nil
    72  	priv := cert.PrivateKey
    73  	cert.PrivateKey = nil
    74  	jDAt, err := json.MarshalIndent(cert, "", "  ")
    75  	if err != nil {
    76  		return err
    77  	}
    78  	if err = ioutil.WriteFile(d.certFile(name, "json"), jDAt, perms); err != nil {
    79  		return err
    80  	}
    81  	if err = ioutil.WriteFile(d.certFile(name, "crt"), pub, perms); err != nil {
    82  		return err
    83  	}
    84  	return ioutil.WriteFile(d.certFile(name, "key"), priv, perms)
    85  }
    86  
    87  func (d directoryStorage) GetAccount(acmeHost string) (*Account, error) {
    88  	f, err := os.Open(d.accountFile(acmeHost))
    89  	if err != nil && os.IsNotExist(err) {
    90  		return nil, nil
    91  	}
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	defer f.Close()
    96  	dec := json.NewDecoder(f)
    97  	acct := &Account{}
    98  	if err = dec.Decode(acct); err != nil {
    99  		return nil, err
   100  	}
   101  	keyBytes, err := ioutil.ReadFile(d.accountKeyFile(acmeHost))
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	keyBlock, _ := pem.Decode(keyBytes)
   106  	if keyBlock == nil {
   107  		return nil, fmt.Errorf("Error decoding account private key")
   108  	}
   109  	acct.key, err = x509.ParseECPrivateKey(keyBlock.Bytes)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	return acct, nil
   114  }
   115  
   116  func (d directoryStorage) StoreAccount(acmeHost string, account *Account) error {
   117  	if err := os.MkdirAll(d.accountDirectory(acmeHost), dirPerms); err != nil {
   118  		return err
   119  	}
   120  	acctBytes, err := json.MarshalIndent(account, "", "  ")
   121  	if err != nil {
   122  		return err
   123  	}
   124  	if err = ioutil.WriteFile(d.accountFile(acmeHost), acctBytes, perms); err != nil {
   125  		return err
   126  	}
   127  	keyBytes, err := x509.MarshalECPrivateKey(account.key)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	pemKey := &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
   132  	pemBytes := pem.EncodeToMemory(pemKey)
   133  	return ioutil.WriteFile(d.accountKeyFile(acmeHost), pemBytes, perms)
   134  }