ericchiang / go-acme

A Let's Encrypt client for Go
Apache License 2.0
219 stars 14 forks source link

I have a question about renew certificate #25

Open zhenweiwang1990 opened 8 years ago

zhenweiwang1990 commented 8 years ago

when I call RenewCertificate , Letsencrypt will return a same cert or not. Which case is successful?

there is a comment here, in https://github.com/ericchiang/letsencrypt/blob/master/acme.go#L327 :

// RenewCertificate attempts to renew an existing certificate.
// Let's Encrypt may return the same certificate. You should load your
// current x509.Certificate and use the Equal method to compare to the "new"
// certificate. If it's identical, you'll need to run NewCertificate and/or
// start a new certificate flow.
func (c *Client) RenewCertificate(certURI string) (*CertificateResponse, error) {
    resp, err := c.client.Get(certURI)
    if err != nil {
        return nil, fmt.Errorf("renew certificate http error: %s", err)
    }

    if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
        return nil, errors.New("certificate not available. Start a new certificate flow")
    }

    certResp, err := handleCertificateResponse(resp)
    if err == nil {
        if certResp.URI == "" {
            certResp.URI = certURI
        }
    }

    return certResp, err
}

but there is also a comment here in https://github.com/xenolf/lego/blob/master/acme/client.go#L398 :

// RenewCertificate takes a CertificateResource and tries to renew the certificate.
// If the renewal process succeeds, the new certificate will ge returned in a new CertResource.
// Please be aware that this function will return a new certificate in ANY case that is not an error.
// If the server does not provide us with a new cert on a GET request to the CertURL
// this function will start a new-cert flow where a new certificate gets generated.
// If bundle is true, the []byte contains both the issuer certificate and
// your issued certificate as a bundle.
// For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil.
func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) {
    // Input certificate is PEM encoded. Decode it here as we may need the decoded
    // cert later on in the renewal process. The input may be a bundle or a single certificate.
    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)
    }

    // This is just meant to be informal for the user.
    timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
    logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))

    // The first step of renewal is to check if we get a renewed cert
    // directly from the cert URL.
    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 the server responds with a different certificate we are effectively renewed.
    // TODO: Further test if we can actually use the new certificate (Our private key works)
    if !x509Cert.Equal(serverCert) {
        logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
        issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
        // If bundle is true, we want to return a certificate bundle.
        // To do this, we need the issuer certificate.
        if bundle {
            // The issuer certificate link is always supplied via an "up" link
            // in the response headers of a new certificate.
            links := parseLinks(resp.Header["Link"])
            issuerCert, err := c.getIssuerCertificate(links["up"])
            if err != nil {
                // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
                logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
            } else {
                // Success - append the issuer cert to the issued cert.
                issuerCert = pemEncode(derCertificateBytes(issuerCert))
                issuedCert = append(issuedCert, issuerCert...)
            }
        }

        cert.Certificate = issuedCert
        return cert, nil
    }

    // If the certificate is the same, then we need to request a new certificate.
    // Start by checking to see if the certificate was based off a CSR, and
    // use that if it's defined.
    if len(cert.CSR) > 0 {
        csr, err := pemDecodeTox509CSR(cert.CSR)
        if err != nil {
            return CertificateResource{}, err
        }
        newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
        return newCert, failures[cert.Domain]
    }

    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
    // check for SAN certificate
    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]
}

My problem is , when I try to renew, I always get the same certificate, but the cert's expire date never changed, so I think this is a result of failing renew?

ericchiang commented 8 years ago

Sorry for not responding sooner. This could easily be a bug in the current implementation. I'm seeing the following description in the ACME spec which matches the problem you're seeing

A certificate resource always represents the most recent certificate issued for the name/key binding expressed in the CSR. If the CA allows a certificate to be renewed, then it publishes renewed versions of the certificate through the same certificate URI.

https://letsencrypt.github.io/acme-spec/#certificate-issuance

I suppose RenewCertificate might be a bad name for that function's current implementation.