2015-06-08 00:36:07 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-01-27 01:01:39 +00:00
|
|
|
"crypto"
|
2015-06-08 00:36:07 +00:00
|
|
|
"encoding/json"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
|
2018-05-27 21:55:19 +00:00
|
|
|
"github.com/xenolf/lego/acme"
|
2018-05-27 22:08:23 +00:00
|
|
|
"github.com/xenolf/lego/log"
|
2015-06-08 00:36:07 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Account represents a users local saved credentials
|
|
|
|
type Account struct {
|
|
|
|
Email string `json:"email"`
|
2016-01-27 01:01:39 +00:00
|
|
|
key crypto.PrivateKey
|
2015-06-08 00:36:07 +00:00
|
|
|
Registration *acme.RegistrationResource `json:"registration"`
|
|
|
|
|
|
|
|
conf *Configuration
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAccount creates a new account for an email address
|
|
|
|
func NewAccount(email string, conf *Configuration) *Account {
|
|
|
|
accKeysPath := conf.AccountKeysPath(email)
|
|
|
|
// TODO: move to function in configuration?
|
|
|
|
accKeyPath := accKeysPath + string(os.PathSeparator) + email + ".key"
|
|
|
|
if err := checkFolder(accKeysPath); err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not check/create directory for account %s: %v", email, err)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|
|
|
|
|
2016-01-27 01:01:39 +00:00
|
|
|
var privKey crypto.PrivateKey
|
2015-06-08 00:36:07 +00:00
|
|
|
if _, err := os.Stat(accKeyPath); os.IsNotExist(err) {
|
2016-01-27 01:01:39 +00:00
|
|
|
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Printf("No key found for account %s. Generating a curve P384 EC key.", email)
|
2016-01-27 01:01:39 +00:00
|
|
|
privKey, err = generatePrivateKey(accKeyPath)
|
2015-06-08 00:36:07 +00:00
|
|
|
if err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not generate RSA private account key for account %s: %v", email, err)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|
2016-01-27 01:01:39 +00:00
|
|
|
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Printf("Saved key to %s", accKeyPath)
|
2015-06-08 00:36:07 +00:00
|
|
|
} else {
|
2016-01-27 01:01:39 +00:00
|
|
|
privKey, err = loadPrivateKey(accKeyPath)
|
2015-06-08 00:36:07 +00:00
|
|
|
if err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not load RSA private key from file %s: %v", accKeyPath, err)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
accountFile := path.Join(conf.AccountPath(email), "account.json")
|
|
|
|
if _, err := os.Stat(accountFile); os.IsNotExist(err) {
|
|
|
|
return &Account{Email: email, key: privKey, conf: conf}
|
|
|
|
}
|
|
|
|
|
|
|
|
fileBytes, err := ioutil.ReadFile(accountFile)
|
|
|
|
if err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not load file for account %s -> %v", email, err)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var acc Account
|
|
|
|
err = json.Unmarshal(fileBytes, &acc)
|
|
|
|
if err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not parse file for account %s -> %v", email, err)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
acc.key = privKey
|
|
|
|
acc.conf = conf
|
|
|
|
|
2018-03-14 01:04:09 +00:00
|
|
|
if acc.Registration == nil || acc.Registration.Body.Status == "" {
|
|
|
|
reg, err := tryRecoverAccount(privKey, conf)
|
|
|
|
if err != nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not load account for %s. Registration is nil -> %#v", email, err)
|
2018-03-14 01:04:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
acc.Registration = reg
|
2018-05-28 16:31:28 +00:00
|
|
|
err = acc.Save()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Could not save account for %s. Registration is nil -> %#v", email, err)
|
|
|
|
}
|
2016-04-21 23:53:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if acc.conf == nil {
|
2018-05-27 22:08:23 +00:00
|
|
|
log.Fatalf("Could not load account for %s. Configuration is nil.", email)
|
2016-04-21 23:53:50 +00:00
|
|
|
}
|
|
|
|
|
2015-06-08 00:36:07 +00:00
|
|
|
return &acc
|
|
|
|
}
|
|
|
|
|
2018-03-14 01:04:09 +00:00
|
|
|
func tryRecoverAccount(privKey crypto.PrivateKey, conf *Configuration) (*acme.RegistrationResource, error) {
|
|
|
|
// couldn't load account but got a key. Try to look the account up.
|
|
|
|
serverURL := conf.context.GlobalString("server")
|
|
|
|
client, err := acme.NewClient(serverURL, &Account{key: privKey, conf: conf}, acme.RSA2048)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
reg, err := client.ResolveAccountByKey()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return reg, nil
|
|
|
|
}
|
|
|
|
|
2015-06-08 00:36:07 +00:00
|
|
|
/** Implementation of the acme.User interface **/
|
|
|
|
|
|
|
|
// GetEmail returns the email address for the account
|
|
|
|
func (a *Account) GetEmail() string {
|
|
|
|
return a.Email
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetPrivateKey returns the private RSA account key.
|
2016-01-27 01:01:39 +00:00
|
|
|
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
2015-06-08 00:36:07 +00:00
|
|
|
return a.key
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetRegistration returns the server registration
|
|
|
|
func (a *Account) GetRegistration() *acme.RegistrationResource {
|
|
|
|
return a.Registration
|
|
|
|
}
|
|
|
|
|
|
|
|
/** End **/
|
|
|
|
|
|
|
|
// Save the account to disk
|
|
|
|
func (a *Account) Save() error {
|
2015-06-08 21:52:41 +00:00
|
|
|
jsonBytes, err := json.MarshalIndent(a, "", "\t")
|
2015-06-08 00:36:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-06-12 06:16:30 +00:00
|
|
|
return ioutil.WriteFile(
|
|
|
|
path.Join(a.conf.AccountPath(a.Email), "account.json"),
|
|
|
|
jsonBytes,
|
|
|
|
0600,
|
|
|
|
)
|
2015-06-08 00:36:07 +00:00
|
|
|
}
|