Example #1
0
// Using the given client and interactor (or interaction.Auto if nil), register
// the client account if it does not already exist.
//
// The interactor is used to prompt for terms of service agreement, if
// agreement has not already been obtained. An e. mail address is prompted for.
func AssistedUpsertRegistration(cl *acmeapi.Client, interactor interaction.Interactor, ctx context.Context) error {
	interactor = defaultInteraction(interactor)

	email := ""

	for {
		err := cl.AgreeRegistration(ctx)
		if err != nil {
			if e, ok := err.(*acmeapi.AgreementError); ok {
				res, err := interactor.Prompt(&interaction.Challenge{
					Title:        "Terms of Service Agreement Required",
					YesLabel:     "I Agree",
					NoLabel:      "Cancel",
					ResponseType: interaction.RTYesNo,
					UniqueID:     "acme-agreement:" + e.URI,
					Prompt:       "Do you agree to the Terms of Service?",
					Body: fmt.Sprintf(`You must agree to the terms of service at the following URL to continue:

%s

Do you agree to the terms of service set out in the above document?`, e.URI),
				})
				if err != nil {
					return err
				}
				if !res.Cancelled {
					if email == "" {
						email, err = getEmail(interactor)
						if err != nil {
							return err
						}
						if email == "-" {
							return fmt.Errorf("e. mail input cancelled")
						}
					}

					if cl.AccountInfo.AgreementURIs == nil {
						cl.AccountInfo.AgreementURIs = map[string]struct{}{}
					}
					cl.AccountInfo.AgreementURIs[e.URI] = struct{}{}
					if email != "" {
						cl.AccountInfo.ContactURIs = []string{"mailto:" + email}
					}
					continue
				}
			}
		}

		return err
	}
}
Example #2
0
func (r *reconcile) revokeCertificateInner(c *storage.Certificate) error {
	if len(c.Certificates) == 0 {
		return fmt.Errorf("no certificates in certificate to revoke: %v", c)
	}

	endCertificate := c.Certificates[0]

	crt, err := x509.ParseCertificate(endCertificate)
	if err != nil {
		return err
	}

	// Get the endpoint which issued the certificate.
	endpoint, err := acmeendpoints.CertificateToEndpoint(r.getGenericClient(), crt, context.TODO())
	if err != nil {
		return fmt.Errorf("could not map certificate %v to endpoint: %v", c, err)
	}

	// In order to revoke a certificate, one needs either the private key of the
	// certificate, or the account key with authorizations for all names on the
	// certificate. Try and find the private key first.
	var client *acmeapi.Client
	var revocationKey crypto.PrivateKey
	if c.Key != nil {
		revocationKey = c.Key.PrivateKey
		client = r.getClientForDirectoryURL(endpoint.DirectoryURL)
	}

	if revocationKey == nil {
		acct, err := r.getAccountByDirectoryURL(endpoint.DirectoryURL)
		if err != nil {
			return err
		}

		client = r.getClientForAccount(acct)

		// If we have no private key for the certificate, obtain all necessary
		// authorizations.
		err = r.getRevocationAuthorizations(acct, crt)
		if err != nil {
			return err
		}
	}

	return client.Revoke(endCertificate, revocationKey, context.TODO())
}
Example #3
0
// The Let's Encrypt state directory format keeps certificates but not their
// URLs. Since boulder uses the serial number to form the URL, we can
// reconstruct the URL. But since not even the provider association is stored,
// we have to guess.
func determineLECertificateURL(certFilename string) (string, error) {
	b, err := ioutil.ReadFile(certFilename)
	if err != nil {
		return "", err
	}

	certs, err := acmeutils.LoadCertificates(b)
	if err != nil {
		return "", err
	}

	if len(certs) == 0 {
		return "", fmt.Errorf("no certs")
	}

	c, err := x509.ParseCertificate(certs[0])
	if err != nil {
		return "", err
	}

	sn := fmt.Sprintf("%036x", c.SerialNumber)
	for u := range knownProviderURLs {
		certURL, err := convertBoulderProviderURLToCertificateURL(u, sn)
		if err != nil {
			continue
		}

		cl := acmeapi.Client{
			DirectoryURL: u,
		}

		crt := acmeapi.Certificate{
			URI: certURL,
		}
		err = cl.LoadCertificate(&crt, context.TODO())
		if err != nil {
			continue
		}

		return certURL, nil
	}

	return "", fmt.Errorf("cannot find certificate URL for %#v (serial %#v)", certFilename, sn)
}
Example #4
0
// Given a certificate, tries to determine the certificate URL and definite endpoint.
func CertificateToEndpointURL(cl *acmeapi.Client, cert *x509.Certificate, ctx context.Context) (*Endpoint, string, error) {
	es, certain, err := CertificateToEndpoints(cert)
	if err != nil {
		return nil, "", err
	}

	for _, e := range es {
		if e.certificateURLTemplate == nil {
			continue
		}

		var b bytes.Buffer
		err = e.certificateURLTemplate.Execute(&b, map[string]interface{}{
			"Certificate": cert,
		})
		if err != nil {
			return nil, "", err
		}

		u := b.String()
		if !certain {
			// Check that this is the right endpoint via an HTTP request.
			acrt := acmeapi.Certificate{
				URI: u,
			}

			err := cl.LoadCertificate(&acrt, ctx)
			if err != nil {
				continue
			}

			// check that the certificate DER matches
			if !bytes.Equal(acrt.Certificate, cert.Raw) {
				continue
			}
		}

		return e, u, nil
	}

	return nil, "", ErrNotFound
}
Example #5
0
// Completes a given challenge, polling it until it is complete. Can be
// cancelled using ctx.
//
// dnsName is the hostname which is being authorized. webPaths and priorKeyFunc
// are passed to responders.
//
// The return value indicates whether the whole authorization has been invalidated
// (set to "failed" status) as a result of an error. In this case a new authorization
// must be created.
func CompleteChallenge(c *acmeapi.Client, ch *acmeapi.Challenge, dnsName string, ccfg responder.ChallengeConfig, ctx context.Context) (invalidated bool, err error) {
	log.Debugf("attempting challenge type %s", ch.Type)

	var certs [][]byte
	for _, c := range ch.Certs {
		certs = append(certs, c)
	}

	r, err := responder.New(responder.Config{
		Type:                   ch.Type,
		Token:                  ch.Token,
		AccountKey:             c.AccountKey,
		Hostname:               dnsName,
		AcceptableCertificates: certs,
		ChallengeConfig:        ccfg,
	})

	if err != nil {
		log.Debuge(err, "challenge instantiation failed")
		return false, err
	}

	err = r.Start()
	if err != nil {
		log.Debuge(err, "challenge start failed")
		return false, err
	}

	defer r.Stop()

	err = c.RespondToChallenge(ch, r.Validation(), r.ValidationSigningKey(), ctx)
	if err != nil {
		return false /* ??? */, err
	}

	b := denet.Backoff{
		InitialDelay: 5 * time.Second,
		MaxDelay:     30 * time.Second,
	}

	for {
		log.Debug("waiting to poll challenge")
		select {
		case <-ctx.Done():
			return true, ctx.Err()
		case <-r.RequestDetectedChan():
			log.Debug("request detected")
		case <-time.After(b.NextDelay()):
		}

		log.Debug("querying challenge status")
		err := c.WaitLoadChallenge(ch, ctx)
		if err != nil {
			return false, err
		}

		if ch.Status.Final() {
			log.Debug("challenge now in final state")
			break
		}
	}

	if ch.Status != "valid" {
		return true, fmt.Errorf("challenge failed with status %#v", ch.Status)
	}

	return false, nil
}
Example #6
0
func (s *Store) downloadCertificate(c *Certificate) error {
	log.Debugf("downloading certificate %v", c)

	col := s.db.Collection("certs/" + c.ID())
	if col == nil {
		return fmt.Errorf("cannot get collection")
	}

	cl := acmeapi.Client{}

	crt := acmeapi.Certificate{
		URI: c.URL,
	}

	err := cl.WaitForCertificate(&crt, context.TODO())
	if err != nil {
		return err
	}

	if len(crt.Certificate) == 0 {
		return fmt.Errorf("nil certificate?")
	}

	fcert, err := col.Create("cert")
	if err != nil {
		return err
	}
	defer fcert.CloseAbort()

	fchain, err := col.Create("chain")
	if err != nil {
		return err
	}
	defer fchain.CloseAbort()

	ffullchain, err := col.Create("fullchain")
	if err != nil {
		return err
	}
	defer ffullchain.CloseAbort()

	err = pem.Encode(io.MultiWriter(fcert, ffullchain), &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: crt.Certificate,
	})
	if err != nil {
		return err
	}

	for _, ec := range crt.ExtraCertificates {
		err = pem.Encode(io.MultiWriter(fchain, ffullchain), &pem.Block{
			Type:  "CERTIFICATE",
			Bytes: ec,
		})
		if err != nil {
			return err
		}
	}

	fcert.Close()
	fchain.Close()
	ffullchain.Close()

	c.Certificates = nil
	c.Certificates = append(c.Certificates, crt.Certificate)
	c.Certificates = append(c.Certificates, crt.ExtraCertificates...)
	c.Cached = true

	return nil
}