// 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) }
// 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 }