1
2
3 package integration
4
5 import (
6 "bytes"
7 "crypto/ecdsa"
8 "crypto/elliptic"
9 "crypto/rand"
10 "crypto/x509"
11 "crypto/x509/pkix"
12 "encoding/asn1"
13 "encoding/hex"
14 "fmt"
15 "net/http"
16 "os"
17
18 "github.com/eggsampler/acme/v3"
19 )
20
21 func init() {
22
23
24
25
26 os.Chdir("../../")
27 }
28
29 var (
30 OIDExtensionCTPoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
31 )
32
33 func random_domain() string {
34 var bytes [3]byte
35 rand.Read(bytes[:])
36 return hex.EncodeToString(bytes[:]) + ".com"
37 }
38
39 type client struct {
40 acme.Account
41 acme.Client
42 }
43
44 func makeClient(contacts ...string) (*client, error) {
45 c, err := acme.NewClient("http://boulder.service.consul:4001/directory")
46 if err != nil {
47 return nil, fmt.Errorf("Error connecting to acme directory: %v", err)
48 }
49 privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
50 if err != nil {
51 return nil, fmt.Errorf("error creating private key: %v", err)
52 }
53 account, err := c.NewAccount(privKey, false, true, contacts...)
54 if err != nil {
55 return nil, err
56 }
57 return &client{account, c}, nil
58 }
59
60 func addHTTP01Response(token, keyAuthorization string) error {
61 resp, err := http.Post("http://boulder.service.consul:8055/add-http01", "",
62 bytes.NewBufferString(fmt.Sprintf(`{
63 "token": "%s",
64 "content": "%s"
65 }`, token, keyAuthorization)))
66 if err != nil {
67 return fmt.Errorf("adding http-01 response: %s", err)
68 }
69 if resp.StatusCode != http.StatusOK {
70 return fmt.Errorf("adding http-01 response: status %d", resp.StatusCode)
71 }
72 resp.Body.Close()
73 return nil
74 }
75
76 func delHTTP01Response(token string) error {
77 resp, err := http.Post("http://boulder.service.consul:8055/del-http01", "",
78 bytes.NewBufferString(fmt.Sprintf(`{
79 "token": "%s"
80 }`, token)))
81 if err != nil {
82 return fmt.Errorf("deleting http-01 response: %s", err)
83 }
84 defer resp.Body.Close()
85
86 if resp.StatusCode != http.StatusOK {
87 return fmt.Errorf("deleting http-01 response: status %d", resp.StatusCode)
88 }
89 return nil
90 }
91
92 func makeClientAndOrder(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*client, *acme.Order, error) {
93 var err error
94 if c == nil {
95 c, err = makeClient()
96 if err != nil {
97 return nil, nil, err
98 }
99 }
100
101 var ids []acme.Identifier
102 for _, domain := range domains {
103 ids = append(ids, acme.Identifier{Type: "dns", Value: domain})
104 }
105 order, err := c.Client.NewOrder(c.Account, ids)
106 if err != nil {
107 return nil, nil, err
108 }
109
110 for _, authUrl := range order.Authorizations {
111 auth, err := c.Client.FetchAuthorization(c.Account, authUrl)
112 if err != nil {
113 return nil, nil, fmt.Errorf("fetching authorization at %s: %s", authUrl, err)
114 }
115
116 chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
117 if !ok {
118 return nil, nil, fmt.Errorf("no HTTP challenge at %s", authUrl)
119 }
120
121 err = addHTTP01Response(chal.Token, chal.KeyAuthorization)
122 if err != nil {
123 return nil, nil, fmt.Errorf("adding HTTP-01 response: %s", err)
124 }
125 chal, err = c.Client.UpdateChallenge(c.Account, chal)
126 if err != nil {
127 delHTTP01Response(chal.Token)
128 return nil, nil, fmt.Errorf("updating challenge: %s", err)
129 }
130 delHTTP01Response(chal.Token)
131 }
132
133 csr, err := makeCSR(csrKey, domains, cn)
134 if err != nil {
135 return nil, nil, err
136 }
137
138 order, err = c.Client.FinalizeOrder(c.Account, order, csr)
139 if err != nil {
140 return nil, nil, fmt.Errorf("finalizing order: %s", err)
141 }
142
143 return c, &order, nil
144 }
145
146 type issuanceResult struct {
147 acme.Order
148 certs []*x509.Certificate
149 }
150
151 func authAndIssue(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*issuanceResult, error) {
152 var err error
153
154 c, order, err := makeClientAndOrder(c, csrKey, domains, cn)
155 if err != nil {
156 return nil, err
157 }
158
159 certs, err := c.Client.FetchCertificates(c.Account, order.Certificate)
160 if err != nil {
161 return nil, fmt.Errorf("fetching certificates: %s", err)
162 }
163 return &issuanceResult{*order, certs}, nil
164 }
165
166 type issuanceResultAllChains struct {
167 acme.Order
168 certs map[string][]*x509.Certificate
169 }
170
171 func authAndIssueFetchAllChains(c *client, csrKey *ecdsa.PrivateKey, domains []string, cn bool) (*issuanceResultAllChains, error) {
172 c, order, err := makeClientAndOrder(c, csrKey, domains, cn)
173 if err != nil {
174 return nil, err
175 }
176
177
178 certs, err := c.Client.FetchAllCertificates(c.Account, order.Certificate)
179 if err != nil {
180 return nil, fmt.Errorf("fetching certificates: %s", err)
181 }
182
183 return &issuanceResultAllChains{*order, certs}, nil
184 }
185
186 func makeCSR(k *ecdsa.PrivateKey, domains []string, cn bool) (*x509.CertificateRequest, error) {
187 var err error
188 if k == nil {
189 k, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
190 if err != nil {
191 return nil, fmt.Errorf("generating certificate key: %s", err)
192 }
193 }
194
195 tmpl := &x509.CertificateRequest{
196 SignatureAlgorithm: x509.ECDSAWithSHA256,
197 PublicKeyAlgorithm: x509.ECDSA,
198 PublicKey: k.Public(),
199 DNSNames: domains,
200 }
201 if cn {
202 tmpl.Subject = pkix.Name{CommonName: domains[0]}
203 }
204
205 csrDer, err := x509.CreateCertificateRequest(rand.Reader, tmpl, k)
206 if err != nil {
207 return nil, fmt.Errorf("making csr: %s", err)
208 }
209 csr, err := x509.ParseCertificateRequest(csrDer)
210 if err != nil {
211 return nil, fmt.Errorf("parsing csr: %s", err)
212 }
213 return csr, nil
214 }
215
View as plain text