From e9c307849235a093040b62f2f295accadb3b470f Mon Sep 17 00:00:00 2001 From: Joe Shaw Date: Tue, 13 Dec 2016 18:22:48 -0500 Subject: [PATCH] add issuer certificate to CertificateResource (#325) * add issuer certificate to CertificateResource Also write it out to the file system when running "lego run" Removed caching of the issuer certificate inside the acme client, since it didn't appear to be used. * only append issuerCert to issuedCert in case of success Effectively a no-op since issuerCert will be nil on error, but it seems more correct to only do it if fetching the issuer succeeds. --- acme/client.go | 47 +++++++++++++++++++++-------------------------- acme/messages.go | 23 ++++++++++++----------- cli_handlers.go | 8 ++++++++ 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/acme/client.go b/acme/client.go index 9f837af3..e758d24f 100644 --- a/acme/client.go +++ b/acme/client.go @@ -49,12 +49,11 @@ type validateFunc func(j *jws, domain, uri string, chlng challenge) error // Client is the user-friendy way to ACME type Client struct { - directory directory - user User - jws *jws - keyType KeyType - issuerCert []byte - solvers map[Challenge]solver + directory directory + user User + jws *jws + keyType KeyType + solvers map[Challenge]solver } // NewClient creates a new ACME client on behalf of the user. The client will depend on @@ -634,24 +633,26 @@ func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle cerRes.AccountRef = c.user.GetRegistration().URI issuedCert := pemEncode(derCertificateBytes(cert)) - // 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("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err) - } else { - // Success - append the issuer cert to the issued cert. - issuerCert = pemEncode(derCertificateBytes(issuerCert)) + + // 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("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err) + } else { + issuerCert = pemEncode(derCertificateBytes(issuerCert)) + + // If bundle is true, we want to return a certificate bundle. + // To do this, we append the issuer cert to the issued cert. + if bundle { issuedCert = append(issuedCert, issuerCert...) } } cerRes.Certificate = issuedCert + cerRes.IssuerCertificate = issuerCert logf("[INFO][%s] Server responded with a certificate.", commonName.Domain) return cerRes, nil } @@ -679,14 +680,9 @@ func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle } } -// getIssuerCertificate requests the issuer certificate and caches it for -// subsequent requests. +// getIssuerCertificate requests the issuer certificate func (c *Client) getIssuerCertificate(url string) ([]byte, error) { logf("[INFO] acme: Requesting issuer cert from %s", url) - if c.issuerCert != nil { - return c.issuerCert, nil - } - resp, err := httpGet(url) if err != nil { return nil, err @@ -703,7 +699,6 @@ func (c *Client) getIssuerCertificate(url string) ([]byte, error) { return nil, err } - c.issuerCert = issuerBytes return issuerBytes, err } diff --git a/acme/messages.go b/acme/messages.go index 0f6514c3..36db3b21 100644 --- a/acme/messages.go +++ b/acme/messages.go @@ -94,16 +94,17 @@ type revokeCertMessage struct { } // CertificateResource represents a CA issued certificate. -// PrivateKey and Certificate are both already PEM encoded -// and can be directly written to disk. Certificate may -// be a certificate bundle, depending on the options supplied -// to create it. +// PrivateKey, Certificate and IssuerCertificate are all +// already PEM encoded and can be directly written to disk. +// Certificate may be a certificate bundle, depending on the +// options supplied to create it. type CertificateResource struct { - Domain string `json:"domain"` - CertURL string `json:"certUrl"` - CertStableURL string `json:"certStableUrl"` - AccountRef string `json:"accountRef,omitempty"` - PrivateKey []byte `json:"-"` - Certificate []byte `json:"-"` - CSR []byte `json:"-"` + Domain string `json:"domain"` + CertURL string `json:"certUrl"` + CertStableURL string `json:"certStableUrl"` + AccountRef string `json:"accountRef,omitempty"` + PrivateKey []byte `json:"-"` + Certificate []byte `json:"-"` + IssuerCertificate []byte `json:"-"` + CSR []byte `json:"-"` } diff --git a/cli_handlers.go b/cli_handlers.go index 858d7100..dad19a14 100644 --- a/cli_handlers.go +++ b/cli_handlers.go @@ -136,12 +136,20 @@ func saveCertRes(certRes acme.CertificateResource, conf *Configuration) { privOut := path.Join(conf.CertPath(), certRes.Domain+".key") pemOut := path.Join(conf.CertPath(), certRes.Domain+".pem") metaOut := path.Join(conf.CertPath(), certRes.Domain+".json") + issuerOut := path.Join(conf.CertPath(), certRes.Domain+".issuer.crt") err := ioutil.WriteFile(certOut, certRes.Certificate, 0600) if err != nil { logger().Fatalf("Unable to save Certificate for domain %s\n\t%s", certRes.Domain, err.Error()) } + if certRes.IssuerCertificate != nil { + err = ioutil.WriteFile(issuerOut, certRes.IssuerCertificate, 0600) + if err != nil { + logger().Fatalf("Unable to save IssuerCertificate for domain %s\n\t%s", certRes.Domain, err.Error()) + } + } + if certRes.PrivateKey != nil { // if we were given a CSR, we don't know the private key err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600)