ca.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. // Copyright 2018 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // Package acmetest provides types for testing acme and autocert packages.
  5. //
  6. // TODO: Consider moving this to x/crypto/acme/internal/acmetest for acme tests as well.
  7. package acmetest
  8. import (
  9. "crypto"
  10. "crypto/ecdsa"
  11. "crypto/elliptic"
  12. "crypto/rand"
  13. "crypto/tls"
  14. "crypto/x509"
  15. "crypto/x509/pkix"
  16. "encoding/base64"
  17. "encoding/json"
  18. "encoding/pem"
  19. "fmt"
  20. "io"
  21. "log"
  22. "math/big"
  23. "net/http"
  24. "net/http/httptest"
  25. "path"
  26. "sort"
  27. "strconv"
  28. "strings"
  29. "sync"
  30. "time"
  31. "golang.org/x/crypto/acme"
  32. )
  33. // CAServer is a simple test server which implements ACME spec bits needed for testing.
  34. type CAServer struct {
  35. URL string // server URL after it has been started
  36. Roots *x509.CertPool // CA root certificates; initialized in NewCAServer
  37. rootKey crypto.Signer
  38. rootCert []byte // DER encoding
  39. rootTemplate *x509.Certificate
  40. server *httptest.Server
  41. challengeTypes []string // supported challenge types
  42. domainsWhitelist []string // only these domains are valid for issuing, unless empty
  43. mu sync.Mutex
  44. certCount int // number of issued certs
  45. domainAddr map[string]string // domain name to addr:port resolution
  46. authorizations map[string]*authorization // keyed by domain name
  47. orders []*order // index is used as order ID
  48. errors []error // encountered client errors
  49. }
  50. // NewCAServer creates a new ACME test server and starts serving requests.
  51. // The returned CAServer issues certs signed with the CA roots
  52. // available in the Roots field.
  53. //
  54. // The challengeTypes argument defines the supported ACME challenge types
  55. // sent to a client in a response for a domain authorization.
  56. // If domainsWhitelist is non-empty, the certs will be issued only for the specified
  57. // list of domains. Otherwise, any domain name is allowed.
  58. func NewCAServer(challengeTypes []string, domainsWhitelist []string) *CAServer {
  59. var whitelist []string
  60. for _, name := range domainsWhitelist {
  61. whitelist = append(whitelist, name)
  62. }
  63. sort.Strings(whitelist)
  64. ca := &CAServer{
  65. challengeTypes: challengeTypes,
  66. domainsWhitelist: whitelist,
  67. domainAddr: make(map[string]string),
  68. authorizations: make(map[string]*authorization),
  69. }
  70. key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  71. if err != nil {
  72. panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
  73. }
  74. tmpl := &x509.Certificate{
  75. SerialNumber: big.NewInt(1),
  76. Subject: pkix.Name{
  77. Organization: []string{"Test Acme Co"},
  78. CommonName: "Root CA",
  79. },
  80. NotBefore: time.Now(),
  81. NotAfter: time.Now().Add(365 * 24 * time.Hour),
  82. KeyUsage: x509.KeyUsageCertSign,
  83. BasicConstraintsValid: true,
  84. IsCA: true,
  85. }
  86. der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
  87. if err != nil {
  88. panic(fmt.Sprintf("x509.CreateCertificate: %v", err))
  89. }
  90. cert, err := x509.ParseCertificate(der)
  91. if err != nil {
  92. panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
  93. }
  94. ca.Roots = x509.NewCertPool()
  95. ca.Roots.AddCert(cert)
  96. ca.rootKey = key
  97. ca.rootCert = der
  98. ca.rootTemplate = tmpl
  99. ca.server = httptest.NewServer(http.HandlerFunc(ca.handle))
  100. ca.URL = ca.server.URL
  101. return ca
  102. }
  103. // Close shuts down the server and blocks until all outstanding
  104. // requests on this server have completed.
  105. func (ca *CAServer) Close() {
  106. ca.server.Close()
  107. }
  108. func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
  109. return ca.server.URL + fmt.Sprintf(format, arg...)
  110. }
  111. func (ca *CAServer) addr(domain string) (string, error) {
  112. ca.mu.Lock()
  113. defer ca.mu.Unlock()
  114. addr, ok := ca.domainAddr[domain]
  115. if !ok {
  116. return "", fmt.Errorf("CAServer: no addr resolution for %q", domain)
  117. }
  118. return addr, nil
  119. }
  120. func (ca *CAServer) httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
  121. s := fmt.Sprintf(format, a...)
  122. log.Println(s)
  123. http.Error(w, s, code)
  124. }
  125. // Resolve adds a domain to address resolution for the ca to dial to
  126. // when validating challenges for the domain authorization.
  127. func (ca *CAServer) Resolve(domain, addr string) {
  128. ca.mu.Lock()
  129. defer ca.mu.Unlock()
  130. ca.domainAddr[domain] = addr
  131. }
  132. type discovery struct {
  133. NewNonce string `json:"newNonce"`
  134. NewReg string `json:"newAccount"`
  135. NewOrder string `json:"newOrder"`
  136. NewAuthz string `json:"newAuthz"`
  137. }
  138. type challenge struct {
  139. URI string `json:"uri"`
  140. Type string `json:"type"`
  141. Token string `json:"token"`
  142. }
  143. type authorization struct {
  144. Status string `json:"status"`
  145. Challenges []challenge `json:"challenges"`
  146. domain string
  147. }
  148. type order struct {
  149. Status string `json:"status"`
  150. AuthzURLs []string `json:"authorizations"`
  151. FinalizeURL string `json:"finalize"` // CSR submit URL
  152. CertURL string `json:"certificate"` // already issued cert
  153. leaf []byte // issued cert in DER format
  154. }
  155. func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
  156. log.Printf("%s %s", r.Method, r.URL)
  157. w.Header().Set("Replay-Nonce", "nonce")
  158. // TODO: Verify nonce header for all POST requests.
  159. switch {
  160. default:
  161. ca.httpErrorf(w, http.StatusBadRequest, "unrecognized r.URL.Path: %s", r.URL.Path)
  162. // Discovery request.
  163. case r.URL.Path == "/":
  164. resp := &discovery{
  165. NewNonce: ca.serverURL("/new-nonce"),
  166. NewReg: ca.serverURL("/new-reg"),
  167. NewOrder: ca.serverURL("/new-order"),
  168. NewAuthz: ca.serverURL("/new-authz"),
  169. }
  170. if err := json.NewEncoder(w).Encode(resp); err != nil {
  171. panic(fmt.Sprintf("discovery response: %v", err))
  172. }
  173. // Nonce requests.
  174. case r.URL.Path == "/new-nonce":
  175. // Nonce values are always set. Nothing else to do.
  176. return
  177. // Client key registration request.
  178. case r.URL.Path == "/new-reg":
  179. // TODO: Check the user account key against a ca.accountKeys?
  180. w.Header().Set("Location", ca.serverURL("/accounts/1"))
  181. w.WriteHeader(http.StatusCreated)
  182. w.Write([]byte("{}"))
  183. // New order request.
  184. case r.URL.Path == "/new-order":
  185. var req struct {
  186. Identifiers []struct{ Value string }
  187. }
  188. if err := decodePayload(&req, r.Body); err != nil {
  189. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  190. return
  191. }
  192. ca.mu.Lock()
  193. defer ca.mu.Unlock()
  194. o := &order{Status: acme.StatusPending}
  195. for _, id := range req.Identifiers {
  196. z := ca.authz(id.Value)
  197. o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%s", z.domain))
  198. }
  199. orderID := len(ca.orders)
  200. ca.orders = append(ca.orders, o)
  201. w.Header().Set("Location", ca.serverURL("/orders/%d", orderID))
  202. w.WriteHeader(http.StatusCreated)
  203. if err := json.NewEncoder(w).Encode(o); err != nil {
  204. panic(err)
  205. }
  206. // Existing order status requests.
  207. case strings.HasPrefix(r.URL.Path, "/orders/"):
  208. ca.mu.Lock()
  209. defer ca.mu.Unlock()
  210. o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/orders/"))
  211. if err != nil {
  212. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  213. return
  214. }
  215. if err := json.NewEncoder(w).Encode(o); err != nil {
  216. panic(err)
  217. }
  218. // Identifier authorization request.
  219. case r.URL.Path == "/new-authz":
  220. var req struct {
  221. Identifier struct{ Value string }
  222. }
  223. if err := decodePayload(&req, r.Body); err != nil {
  224. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  225. return
  226. }
  227. ca.mu.Lock()
  228. defer ca.mu.Unlock()
  229. z := ca.authz(req.Identifier.Value)
  230. w.Header().Set("Location", ca.serverURL("/authz/%s", z.domain))
  231. w.WriteHeader(http.StatusCreated)
  232. if err := json.NewEncoder(w).Encode(z); err != nil {
  233. panic(fmt.Sprintf("new authz response: %v", err))
  234. }
  235. // Accept tls-alpn-01 challenge type requests.
  236. case strings.HasPrefix(r.URL.Path, "/challenge/tls-alpn-01/"):
  237. domain := strings.TrimPrefix(r.URL.Path, "/challenge/tls-alpn-01/")
  238. ca.mu.Lock()
  239. _, exist := ca.authorizations[domain]
  240. ca.mu.Unlock()
  241. if !exist {
  242. ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: no authz for %q", domain)
  243. return
  244. }
  245. go ca.validateChallenge("tls-alpn-01", domain)
  246. w.Write([]byte("{}"))
  247. // Get authorization status requests.
  248. case strings.HasPrefix(r.URL.Path, "/authz/"):
  249. domain := strings.TrimPrefix(r.URL.Path, "/authz/")
  250. ca.mu.Lock()
  251. defer ca.mu.Unlock()
  252. authz, ok := ca.authorizations[domain]
  253. if !ok {
  254. ca.httpErrorf(w, http.StatusNotFound, "no authz for %q", domain)
  255. return
  256. }
  257. if err := json.NewEncoder(w).Encode(authz); err != nil {
  258. panic(fmt.Sprintf("get authz for %q response: %v", domain, err))
  259. }
  260. // Certificate issuance request.
  261. case strings.HasPrefix(r.URL.Path, "/new-cert/"):
  262. ca.mu.Lock()
  263. defer ca.mu.Unlock()
  264. orderID := strings.TrimPrefix(r.URL.Path, "/new-cert/")
  265. o, err := ca.storedOrder(orderID)
  266. if err != nil {
  267. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  268. return
  269. }
  270. if o.Status != acme.StatusReady {
  271. ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
  272. return
  273. }
  274. // Validate CSR request.
  275. var req struct {
  276. CSR string `json:"csr"`
  277. }
  278. decodePayload(&req, r.Body)
  279. b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
  280. csr, err := x509.ParseCertificateRequest(b)
  281. if err != nil {
  282. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  283. return
  284. }
  285. names := unique(append(csr.DNSNames, csr.Subject.CommonName))
  286. if err := ca.matchWhitelist(names); err != nil {
  287. ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
  288. return
  289. }
  290. if err := ca.authorized(names); err != nil {
  291. ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
  292. return
  293. }
  294. // Issue the certificate.
  295. der, err := ca.leafCert(csr)
  296. if err != nil {
  297. ca.httpErrorf(w, http.StatusBadRequest, "new-cert response: ca.leafCert: %v", err)
  298. return
  299. }
  300. o.leaf = der
  301. o.CertURL = ca.serverURL("/issued-cert/%s", orderID)
  302. o.Status = acme.StatusValid
  303. if err := json.NewEncoder(w).Encode(o); err != nil {
  304. panic(err)
  305. }
  306. // Already issued cert download requests.
  307. case strings.HasPrefix(r.URL.Path, "/issued-cert/"):
  308. ca.mu.Lock()
  309. defer ca.mu.Unlock()
  310. o, err := ca.storedOrder(strings.TrimPrefix(r.URL.Path, "/issued-cert/"))
  311. if err != nil {
  312. ca.httpErrorf(w, http.StatusBadRequest, err.Error())
  313. return
  314. }
  315. if o.Status != acme.StatusValid {
  316. ca.httpErrorf(w, http.StatusForbidden, "order status: %s", o.Status)
  317. return
  318. }
  319. w.Header().Set("Content-Type", "application/pem-certificate-chain")
  320. pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: o.leaf})
  321. pem.Encode(w, &pem.Block{Type: "CERTIFICATE", Bytes: ca.rootCert})
  322. }
  323. }
  324. // matchWhitelist reports whether all dnsNames are whitelisted.
  325. // The whitelist is provided in NewCAServer.
  326. func (ca *CAServer) matchWhitelist(dnsNames []string) error {
  327. if len(ca.domainsWhitelist) == 0 {
  328. return nil
  329. }
  330. var nomatch []string
  331. for _, name := range dnsNames {
  332. i := sort.SearchStrings(ca.domainsWhitelist, name)
  333. if i == len(ca.domainsWhitelist) || ca.domainsWhitelist[i] != name {
  334. nomatch = append(nomatch, name)
  335. }
  336. }
  337. if len(nomatch) > 0 {
  338. return fmt.Errorf("matchWhitelist: some domains don't match: %q", nomatch)
  339. }
  340. return nil
  341. }
  342. // storedOrder retrieves a previously created order at index i.
  343. // It requires ca.mu to be locked.
  344. func (ca *CAServer) storedOrder(i string) (*order, error) {
  345. idx, err := strconv.Atoi(i)
  346. if err != nil {
  347. return nil, fmt.Errorf("storedOrder: %v", err)
  348. }
  349. if idx < 0 {
  350. return nil, fmt.Errorf("storedOrder: invalid order index %d", idx)
  351. }
  352. if idx > len(ca.orders)-1 {
  353. return nil, fmt.Errorf("storedOrder: no such order %d", idx)
  354. }
  355. return ca.orders[idx], nil
  356. }
  357. // authz returns an existing authorization for the identifier or creates a new one.
  358. // It requires ca.mu to be locked.
  359. func (ca *CAServer) authz(identifier string) *authorization {
  360. authz, ok := ca.authorizations[identifier]
  361. if !ok {
  362. authz = &authorization{
  363. domain: identifier,
  364. Status: acme.StatusPending,
  365. }
  366. for _, typ := range ca.challengeTypes {
  367. authz.Challenges = append(authz.Challenges, challenge{
  368. Type: typ,
  369. URI: ca.serverURL("/challenge/%s/%s", typ, authz.domain),
  370. Token: challengeToken(authz.domain, typ),
  371. })
  372. }
  373. ca.authorizations[authz.domain] = authz
  374. }
  375. return authz
  376. }
  377. // authorized reports whether all authorizations for dnsNames have been satisfied.
  378. // It requires ca.mu to be locked.
  379. func (ca *CAServer) authorized(dnsNames []string) error {
  380. var noauthz []string
  381. for _, name := range dnsNames {
  382. authz, ok := ca.authorizations[name]
  383. if !ok || authz.Status != acme.StatusValid {
  384. noauthz = append(noauthz, name)
  385. }
  386. }
  387. if len(noauthz) > 0 {
  388. return fmt.Errorf("CAServer: no authz for %q", noauthz)
  389. }
  390. return nil
  391. }
  392. // leafCert issues a new certificate.
  393. // It requires ca.mu to be locked.
  394. func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
  395. ca.certCount++ // next leaf cert serial number
  396. leaf := &x509.Certificate{
  397. SerialNumber: big.NewInt(int64(ca.certCount)),
  398. Subject: pkix.Name{Organization: []string{"Test Acme Co"}},
  399. NotBefore: time.Now(),
  400. NotAfter: time.Now().Add(90 * 24 * time.Hour),
  401. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  402. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  403. DNSNames: csr.DNSNames,
  404. BasicConstraintsValid: true,
  405. }
  406. if len(csr.DNSNames) == 0 {
  407. leaf.DNSNames = []string{csr.Subject.CommonName}
  408. }
  409. return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
  410. }
  411. // TODO: Only tls-alpn-01 is currently supported: implement http-01 and dns-01.
  412. func (ca *CAServer) validateChallenge(typ, identifier string) {
  413. var err error
  414. switch typ {
  415. case "tls-alpn-01":
  416. err = ca.verifyALPNChallenge(identifier)
  417. default:
  418. panic(fmt.Sprintf("validation of %q is not implemented", typ))
  419. }
  420. ca.mu.Lock()
  421. defer ca.mu.Unlock()
  422. authz := ca.authorizations[identifier]
  423. if err != nil {
  424. authz.Status = "invalid"
  425. } else {
  426. authz.Status = "valid"
  427. }
  428. log.Printf("validated %q for %q; authz status is now: %s", typ, identifier, authz.Status)
  429. // Update all pending orders.
  430. // An order becomes "ready" if all authorizations are "valid".
  431. // An order becomes "invalid" if any authorization is "invalid".
  432. // Status changes: https://tools.ietf.org/html/rfc8555#section-7.1.6
  433. OrdersLoop:
  434. for i, o := range ca.orders {
  435. if o.Status != acme.StatusPending {
  436. continue
  437. }
  438. var countValid int
  439. for _, zurl := range o.AuthzURLs {
  440. z, ok := ca.authorizations[path.Base(zurl)]
  441. if !ok {
  442. log.Printf("no authz %q for order %d", zurl, i)
  443. continue OrdersLoop
  444. }
  445. if z.Status == acme.StatusInvalid {
  446. o.Status = acme.StatusInvalid
  447. log.Printf("order %d is now invalid", i)
  448. continue OrdersLoop
  449. }
  450. if z.Status == acme.StatusValid {
  451. countValid++
  452. }
  453. }
  454. if countValid == len(o.AuthzURLs) {
  455. o.Status = acme.StatusReady
  456. o.FinalizeURL = ca.serverURL("/new-cert/%d", i)
  457. log.Printf("order %d is now ready", i)
  458. }
  459. }
  460. }
  461. func (ca *CAServer) verifyALPNChallenge(domain string) error {
  462. const acmeALPNProto = "acme-tls/1"
  463. addr, err := ca.addr(domain)
  464. if err != nil {
  465. return err
  466. }
  467. conn, err := tls.Dial("tcp", addr, &tls.Config{
  468. ServerName: domain,
  469. InsecureSkipVerify: true,
  470. NextProtos: []string{acmeALPNProto},
  471. })
  472. if err != nil {
  473. return err
  474. }
  475. if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
  476. return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
  477. }
  478. if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
  479. return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
  480. }
  481. // TODO: verify conn.ConnectionState().PeerCertificates[0]
  482. return nil
  483. }
  484. func decodePayload(v interface{}, r io.Reader) error {
  485. var req struct{ Payload string }
  486. if err := json.NewDecoder(r).Decode(&req); err != nil {
  487. return err
  488. }
  489. payload, err := base64.RawURLEncoding.DecodeString(req.Payload)
  490. if err != nil {
  491. return err
  492. }
  493. return json.Unmarshal(payload, v)
  494. }
  495. func challengeToken(domain, challType string) string {
  496. return fmt.Sprintf("token-%s-%s", domain, challType)
  497. }
  498. func unique(a []string) []string {
  499. seen := make(map[string]bool)
  500. var res []string
  501. for _, s := range a {
  502. if s != "" && !seen[s] {
  503. seen[s] = true
  504. res = append(res, s)
  505. }
  506. }
  507. return res
  508. }