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.
This commit is contained in:
Joe Shaw 2016-12-13 18:22:48 -05:00 committed by xenolf
parent d149f14b6b
commit e9c3078492
3 changed files with 41 additions and 37 deletions

View file

@ -49,12 +49,11 @@ type validateFunc func(j *jws, domain, uri string, chlng challenge) error
// Client is the user-friendy way to ACME // Client is the user-friendy way to ACME
type Client struct { type Client struct {
directory directory directory directory
user User user User
jws *jws jws *jws
keyType KeyType keyType KeyType
issuerCert []byte solvers map[Challenge]solver
solvers map[Challenge]solver
} }
// NewClient creates a new ACME client on behalf of the user. The client will depend on // 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 cerRes.AccountRef = c.user.GetRegistration().URI
issuedCert := pemEncode(derCertificateBytes(cert)) issuedCert := pemEncode(derCertificateBytes(cert))
// If bundle is true, we want to return a certificate bundle.
// To do this, we need the issuer certificate. // The issuer certificate link is always supplied via an "up" link
if bundle { // in the response headers of a new certificate.
// The issuer certificate link is always supplied via an "up" link links := parseLinks(resp.Header["Link"])
// in the response headers of a new certificate. issuerCert, err := c.getIssuerCertificate(links["up"])
links := parseLinks(resp.Header["Link"]) if err != nil {
issuerCert, err := c.getIssuerCertificate(links["up"]) // If we fail to acquire the issuer cert, return the issued certificate - do not fail.
if err != nil { logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
// If we fail to acquire the issuer cert, return the issued certificate - do not fail. } else {
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err) issuerCert = pemEncode(derCertificateBytes(issuerCert))
} else {
// Success - append the issuer cert to the issued cert. // If bundle is true, we want to return a certificate bundle.
issuerCert = pemEncode(derCertificateBytes(issuerCert)) // To do this, we append the issuer cert to the issued cert.
if bundle {
issuedCert = append(issuedCert, issuerCert...) issuedCert = append(issuedCert, issuerCert...)
} }
} }
cerRes.Certificate = issuedCert cerRes.Certificate = issuedCert
cerRes.IssuerCertificate = issuerCert
logf("[INFO][%s] Server responded with a certificate.", commonName.Domain) logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
return cerRes, nil return cerRes, nil
} }
@ -679,14 +680,9 @@ func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle
} }
} }
// getIssuerCertificate requests the issuer certificate and caches it for // getIssuerCertificate requests the issuer certificate
// subsequent requests.
func (c *Client) getIssuerCertificate(url string) ([]byte, error) { func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
logf("[INFO] acme: Requesting issuer cert from %s", url) logf("[INFO] acme: Requesting issuer cert from %s", url)
if c.issuerCert != nil {
return c.issuerCert, nil
}
resp, err := httpGet(url) resp, err := httpGet(url)
if err != nil { if err != nil {
return nil, err return nil, err
@ -703,7 +699,6 @@ func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
return nil, err return nil, err
} }
c.issuerCert = issuerBytes
return issuerBytes, err return issuerBytes, err
} }

View file

@ -94,16 +94,17 @@ type revokeCertMessage struct {
} }
// CertificateResource represents a CA issued certificate. // CertificateResource represents a CA issued certificate.
// PrivateKey and Certificate are both already PEM encoded // PrivateKey, Certificate and IssuerCertificate are all
// and can be directly written to disk. Certificate may // already PEM encoded and can be directly written to disk.
// be a certificate bundle, depending on the options supplied // Certificate may be a certificate bundle, depending on the
// to create it. // options supplied to create it.
type CertificateResource struct { type CertificateResource struct {
Domain string `json:"domain"` Domain string `json:"domain"`
CertURL string `json:"certUrl"` CertURL string `json:"certUrl"`
CertStableURL string `json:"certStableUrl"` CertStableURL string `json:"certStableUrl"`
AccountRef string `json:"accountRef,omitempty"` AccountRef string `json:"accountRef,omitempty"`
PrivateKey []byte `json:"-"` PrivateKey []byte `json:"-"`
Certificate []byte `json:"-"` Certificate []byte `json:"-"`
CSR []byte `json:"-"` IssuerCertificate []byte `json:"-"`
CSR []byte `json:"-"`
} }

View file

@ -136,12 +136,20 @@ func saveCertRes(certRes acme.CertificateResource, conf *Configuration) {
privOut := path.Join(conf.CertPath(), certRes.Domain+".key") privOut := path.Join(conf.CertPath(), certRes.Domain+".key")
pemOut := path.Join(conf.CertPath(), certRes.Domain+".pem") pemOut := path.Join(conf.CertPath(), certRes.Domain+".pem")
metaOut := path.Join(conf.CertPath(), certRes.Domain+".json") metaOut := path.Join(conf.CertPath(), certRes.Domain+".json")
issuerOut := path.Join(conf.CertPath(), certRes.Domain+".issuer.crt")
err := ioutil.WriteFile(certOut, certRes.Certificate, 0600) err := ioutil.WriteFile(certOut, certRes.Certificate, 0600)
if err != nil { if err != nil {
logger().Fatalf("Unable to save Certificate for domain %s\n\t%s", certRes.Domain, err.Error()) 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 certRes.PrivateKey != nil {
// if we were given a CSR, we don't know the private key // if we were given a CSR, we don't know the private key
err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600) err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600)