func (c *Client) solveChallenges(challenges []authorizationResource) map[string]error {
	failures := make(map[string]error)
	for _, authz := range challenges {
		if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil {
			for i, solver := range solvers {
				err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
				if err != nil {
					failures[authz.Domain] = err
				}
			}
		} else {
			failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
		}
	}
	return failures
}
Looks through the challenge combinations to find a solvable match.
Then solves the challenges in series and returns.
package acme
import (
	"crypto"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"regexp"
	"strconv"
	"strings"
	"time"
)
var (
	Logger *log.Logger
)
func logf(format string, args ...interface{}) {
	if Logger != nil {
		Logger.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}
type User interface {
	GetEmail() string
	GetRegistration() *RegistrationResource
	GetPrivateKey() crypto.PrivateKey
}
type solver interface {
	Solve(challenge challenge, domain string) error
}
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
type Client struct {
	directory  directory
	user       User
	jws        *jws
	keyType    KeyType
	issuerCert []byte
	solvers    map[Challenge]solver
}
func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
	privKey := user.GetPrivateKey()
	if privKey == nil {
		return nil, errors.New("private key was nil")
	}
	var dir directory
	if _, err := getJSON(caDirURL, &dir); err != nil {
		return nil, fmt.Errorf("get directory at '%s': %v", caDirURL, err)
	}
	if dir.NewRegURL == "" {
		return nil, errors.New("directory missing new registration URL")
	}
	if dir.NewAuthzURL == "" {
		return nil, errors.New("directory missing new authz URL")
	}
	if dir.NewCertURL == "" {
		return nil, errors.New("directory missing new certificate URL")
	}
	if dir.RevokeCertURL == "" {
		return nil, errors.New("directory missing revoke certificate URL")
	}
	jws := &jws{privKey: privKey, directoryURL: caDirURL}
	solvers := make(map[Challenge]solver)
	solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
	solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
	return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
}
func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error {
	switch challenge {
	case HTTP01:
		c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
	case TLSSNI01:
		c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p}
	default:
		return fmt.Errorf("Unknown challenge %v", challenge)
	}
	return nil
}
func (c *Client) SetHTTPAddress(iface string) error {
	host, port, err := net.SplitHostPort(iface)
	if err != nil {
		return err
	}
	if chlng, ok := c.solvers[HTTP01]; ok {
		chlng.(*httpChallenge).provider = NewHTTPProviderServer(host, port)
	}
	return nil
}
func (c *Client) SetTLSAddress(iface string) error {
	host, port, err := net.SplitHostPort(iface)
	if err != nil {
		return err
	}
	if chlng, ok := c.solvers[TLSSNI01]; ok {
		chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
	}
	return nil
}
func (c *Client) ExcludeChallenges(challenges []Challenge) {
	for _, challenge := range challenges {
		delete(c.solvers, challenge)
	}
}
func (c *Client) Register() (*RegistrationResource, error) {
	if c == nil || c.user == nil {
		return nil, errors.New("acme: cannot register a nil client or user")
	}
	logf("[INFO] acme: Registering account for %s", c.user.GetEmail())
	regMsg := registrationMessage{
		Resource: "new-reg",
	}
	if c.user.GetEmail() != "" {
		regMsg.Contact = []string{"mailto:" + c.user.GetEmail()}
	} else {
		regMsg.Contact = []string{}
	}
	var serverReg Registration
	hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg)
	if err != nil {
		return nil, err
	}
	reg := &RegistrationResource{Body: serverReg}
	links := parseLinks(hdr["Link"])
	reg.URI = hdr.Get("Location")
	if links["terms-of-service"] != "" {
		reg.TosURL = links["terms-of-service"]
	}
	if links["next"] != "" {
		reg.NewAuthzURL = links["next"]
	} else {
		return nil, errors.New("acme: The server did not return 'next' link to proceed")
	}
	return reg, nil
}
func (c *Client) AgreeToTOS() error {
	reg := c.user.GetRegistration()
	reg.Body.Agreement = c.user.GetRegistration().TosURL
	reg.Body.Resource = "reg"
	_, err := postJSON(c.jws, c.user.GetRegistration().URI, c.user.GetRegistration().Body, nil)
	return err
}
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey) (CertificateResource, map[string]error) {
	if bundle {
		logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
	} else {
		logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
	}
	challenges, failures := c.getChallenges(domains)
	if len(failures) > 0 {
		return CertificateResource{}, failures
	}
	errs := c.solveChallenges(challenges)
	if len(errs) > 0 {
		return CertificateResource{}, errs
	}
	logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
	cert, err := c.requestCertificate(challenges, bundle, privKey)
	if err != nil {
		for _, chln := range challenges {
			failures[chln.Domain] = err
		}
	}
	return cert, failures
}
func (c *Client) RevokeCertificate(certificate []byte) error {
	certificates, err := parsePEMBundle(certificate)
	if err != nil {
		return err
	}
	x509Cert := certificates[0]
	if x509Cert.IsCA {
		return fmt.Errorf("Certificate bundle starts with a CA certificate")
	}
	encodedCert := base64.URLEncoding.EncodeToString(x509Cert.Raw)
	_, err = postJSON(c.jws, c.directory.RevokeCertURL, revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert}, nil)
	return err
}
func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) {
	certificates, err := parsePEMBundle(cert.Certificate)
	if err != nil {
		return CertificateResource{}, err
	}
	x509Cert := certificates[0]
	if x509Cert.IsCA {
		return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
	}
	timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
	logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
	resp, err := httpGet(cert.CertURL)
	if err != nil {
		return CertificateResource{}, err
	}
	defer resp.Body.Close()
	serverCertBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return CertificateResource{}, err
	}
	serverCert, err := x509.ParseCertificate(serverCertBytes)
	if err != nil {
		return CertificateResource{}, err
	}
	if !x509Cert.Equal(serverCert) {
		logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
		issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
		if bundle {
			links := parseLinks(resp.Header["Link"])
			issuerCert, err := c.getIssuerCertificate(links["up"])
			if err != nil {
				logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
			} else {
				issuerCert = pemEncode(derCertificateBytes(issuerCert))
				issuedCert = append(issuedCert, issuerCert...)
			}
		}
		cert.Certificate = issuedCert
		return cert, nil
	}
	var privKey crypto.PrivateKey
	if cert.PrivateKey != nil {
		privKey, err = parsePEMPrivateKey(cert.PrivateKey)
		if err != nil {
			return CertificateResource{}, err
		}
	}
	var domains []string
	var failures map[string]error
	if len(x509Cert.DNSNames) > 1 {
		domains = append(domains, x509Cert.Subject.CommonName)
		for _, sanDomain := range x509Cert.DNSNames {
			if sanDomain == x509Cert.Subject.CommonName {
				continue
			}
			domains = append(domains, sanDomain)
		}
	} else {
		domains = append(domains, x509Cert.Subject.CommonName)
	}
	newCert, failures := c.ObtainCertificate(domains, bundle, privKey)
	return newCert, failures[cert.Domain]
}