package acme import ( "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "errors" "log" "math/big" "net/http" "sync" "time" ) var errNoCert = errors.New("no cert") type tlsSolver struct { http.Server sni map[string]*tls.Certificate sync.Once } func (s *tlsSolver) getCert(h *tls.ClientHelloInfo) (*tls.Certificate, error) { if crt, ok := s.sni[h.ServerName]; ok { return crt, nil } return nil, errNoCert } func NewTLSSolver(addr string) (Solver, error) { if addr == "" { return nil, ErrSkipped } s := &tlsSolver{ Server: http.Server{Addr: addr}, sni: make(map[string]*tls.Certificate), } s.Server.TLSConfig = &tls.Config{ GetCertificate: s.getCert, } return s, nil } func newCert(domain string) (tls.Certificate, error) { fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err } key, err := rsa.GenerateKey(rand.Reader, 2048) // NewKey(2048) if err != nil { return fail(err) } serialLimit := new(big.Int).Lsh(big.NewInt(1), 128) serial, err := rand.Int(rand.Reader, serialLimit) if err != nil { return fail(err) } tmpl := x509.Certificate{ SerialNumber: serial, NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour), KeyUsage: x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, BasicConstraintsValid: true, SignatureAlgorithm: x509.SHA256WithRSA, DNSNames: []string{domain}, } crt, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key) if err != nil { return fail(err) } return tls.Certificate{ Certificate: [][]byte{crt}, PrivateKey: key, }, nil } func (s *tlsSolver) Solve(ch Challenge) error { go s.Do(func() { log.Fatal(s.ListenAndServeTLS("", "")) }) name := ch.SNIName() log.Println("Solve tls", name) crt, err := newCert(name) if err != nil { return err } s.sni[name] = &crt return nil } func (*tlsSolver) Solved() error { return nil } func (*tlsSolver) Type() ChalType { return ChallengeTLS }