1package cmd
2
3import (
4	"crypto/x509"
5	"encoding/pem"
6	"fmt"
7	"os"
8	"strings"
9	"time"
10
11	"github.com/go-acme/lego/v4/certcrypto"
12	"github.com/go-acme/lego/v4/lego"
13	"github.com/go-acme/lego/v4/log"
14	"github.com/go-acme/lego/v4/registration"
15	"github.com/urfave/cli"
16)
17
18const filePerm os.FileMode = 0o600
19
20func setup(ctx *cli.Context, accountsStorage *AccountsStorage) (*Account, *lego.Client) {
21	keyType := getKeyType(ctx)
22	privateKey := accountsStorage.GetPrivateKey(keyType)
23
24	var account *Account
25	if accountsStorage.ExistsAccountFilePath() {
26		account = accountsStorage.LoadAccount(privateKey)
27	} else {
28		account = &Account{Email: accountsStorage.GetUserID(), key: privateKey}
29	}
30
31	client := newClient(ctx, account, keyType)
32
33	return account, client
34}
35
36func newClient(ctx *cli.Context, acc registration.User, keyType certcrypto.KeyType) *lego.Client {
37	config := lego.NewConfig(acc)
38	config.CADirURL = ctx.GlobalString("server")
39
40	config.Certificate = lego.CertificateConfig{
41		KeyType: keyType,
42		Timeout: time.Duration(ctx.GlobalInt("cert.timeout")) * time.Second,
43	}
44	config.UserAgent = fmt.Sprintf("lego-cli/%s", ctx.App.Version)
45
46	if ctx.GlobalIsSet("http-timeout") {
47		config.HTTPClient.Timeout = time.Duration(ctx.GlobalInt("http-timeout")) * time.Second
48	}
49
50	client, err := lego.NewClient(config)
51	if err != nil {
52		log.Fatalf("Could not create client: %v", err)
53	}
54
55	if client.GetExternalAccountRequired() && !ctx.GlobalIsSet("eab") {
56		log.Fatal("Server requires External Account Binding. Use --eab with --kid and --hmac.")
57	}
58
59	return client
60}
61
62// getKeyType the type from which private keys should be generated.
63func getKeyType(ctx *cli.Context) certcrypto.KeyType {
64	keyType := ctx.GlobalString("key-type")
65	switch strings.ToUpper(keyType) {
66	case "RSA2048":
67		return certcrypto.RSA2048
68	case "RSA4096":
69		return certcrypto.RSA4096
70	case "RSA8192":
71		return certcrypto.RSA8192
72	case "EC256":
73		return certcrypto.EC256
74	case "EC384":
75		return certcrypto.EC384
76	}
77
78	log.Fatalf("Unsupported KeyType: %s", keyType)
79	return ""
80}
81
82func getEmail(ctx *cli.Context) string {
83	email := ctx.GlobalString("email")
84	if email == "" {
85		log.Fatal("You have to pass an account (email address) to the program using --email or -m")
86	}
87	return email
88}
89
90func createNonExistingFolder(path string) error {
91	if _, err := os.Stat(path); os.IsNotExist(err) {
92		return os.MkdirAll(path, 0o700)
93	} else if err != nil {
94		return err
95	}
96	return nil
97}
98
99func readCSRFile(filename string) (*x509.CertificateRequest, error) {
100	bytes, err := os.ReadFile(filename)
101	if err != nil {
102		return nil, err
103	}
104	raw := bytes
105
106	// see if we can find a PEM-encoded CSR
107	var p *pem.Block
108	rest := bytes
109	for {
110		// decode a PEM block
111		p, rest = pem.Decode(rest)
112
113		// did we fail?
114		if p == nil {
115			break
116		}
117
118		// did we get a CSR?
119		if p.Type == "CERTIFICATE REQUEST" || p.Type == "NEW CERTIFICATE REQUEST" {
120			raw = p.Bytes
121		}
122	}
123
124	// no PEM-encoded CSR
125	// assume we were given a DER-encoded ASN.1 CSR
126	// (if this assumption is wrong, parsing these bytes will fail)
127	return x509.ParseCertificateRequest(raw)
128}
129