forked from TrueCloudLab/lego
Add ObtainCertificateForCSR()
This commit also breaks requestCertificate() into two parts, the first of which generates a CSR, the second of which became requestCertificateForCsr() which does what the name implies.
This commit is contained in:
parent
c570b320ae
commit
8d7afd02b9
1 changed files with 76 additions and 4 deletions
|
@ -278,6 +278,69 @@ func (c *Client) AgreeToTOS() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// ObtainCertificateForCSR tries to obtain a certificate matching the CSR passed into it.
|
||||
// The domains are inferred from the CommonName and SubjectAltNames, if any. The private key
|
||||
// for this CSR is not required.
|
||||
// If bundle is true, the []byte contains both the issuer certificate and
|
||||
// your issued certificate as a bundle.
|
||||
// This function will never return a partial certificate. If one domain in the list fails,
|
||||
// the whole certificate will fail.
|
||||
func (c *Client) ObtainCertificateForCSR(csr []byte, bundle bool) (CertificateResource, map[string]error) {
|
||||
// parse the CSR
|
||||
parsedCsr, err := x509.ParseCertificateRequest(csr)
|
||||
if err != nil {
|
||||
return CertificateResource{}, map[string]error{"csr": err}
|
||||
}
|
||||
|
||||
// figure out what domains it concerns
|
||||
// start with the common name
|
||||
domains := []string{parsedCsr.Subject.CommonName}
|
||||
|
||||
// loop over the SubjectAltName DNS names
|
||||
DNSNames:
|
||||
for _, sanName := range parsedCsr.DNSNames {
|
||||
// /
|
||||
for _, existingName := range domains {
|
||||
if existingName == sanName {
|
||||
// duplicate; skip this name
|
||||
continue DNSNames
|
||||
}
|
||||
}
|
||||
|
||||
// name is unique
|
||||
domains = append(domains, sanName)
|
||||
}
|
||||
|
||||
if bundle {
|
||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
} else {
|
||||
logf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
challenges, failures := c.getChallenges(domains)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(failures) > 0 {
|
||||
return CertificateResource{}, failures
|
||||
}
|
||||
|
||||
errs := c.solveChallenges(challenges)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(errs) > 0 {
|
||||
return CertificateResource{}, errs
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
cert, err := c.requestCertificateForCsr(challenges, bundle, csr, nil)
|
||||
if err != nil {
|
||||
for _, chln := range challenges {
|
||||
failures[chln.Domain] = err
|
||||
}
|
||||
}
|
||||
|
||||
return cert, failures
|
||||
}
|
||||
|
||||
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
||||
// The first domain in domains is used for the CommonName field of the certificate, all other
|
||||
// domains are added using the Subject Alternate Names extension. A new private key is generated
|
||||
|
@ -528,7 +591,6 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
|||
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
|
||||
}
|
||||
|
||||
commonName := authz[0]
|
||||
var err error
|
||||
if privKey == nil {
|
||||
privKey, err = generatePrivateKey(c.keyType)
|
||||
|
@ -537,11 +599,11 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
|||
}
|
||||
}
|
||||
|
||||
// determine certificate name(s) based on the authorization resources
|
||||
commonName := authz[0]
|
||||
var san []string
|
||||
var authURLs []string
|
||||
for _, auth := range authz[1:] {
|
||||
san = append(san, auth.Domain)
|
||||
authURLs = append(authURLs, auth.AuthURL)
|
||||
}
|
||||
|
||||
// TODO: should the CSR be customizable?
|
||||
|
@ -550,6 +612,17 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
|||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
return c.requestCertificateForCsr(authz, bundle, csr, pemEncode(privKey))
|
||||
}
|
||||
|
||||
func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle bool, csr []byte, privateKeyPem []byte) (CertificateResource, error) {
|
||||
commonName := authz[0]
|
||||
|
||||
var authURLs []string
|
||||
for _, auth := range authz[1:] {
|
||||
authURLs = append(authURLs, auth.AuthURL)
|
||||
}
|
||||
|
||||
csrString := base64.URLEncoding.EncodeToString(csr)
|
||||
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
||||
if err != nil {
|
||||
|
@ -561,7 +634,6 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
|||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
privateKeyPem := pemEncode(privKey)
|
||||
cerRes := CertificateResource{
|
||||
Domain: commonName.Domain,
|
||||
CertURL: resp.Header.Get("Location"),
|
||||
|
|
Loading…
Reference in a new issue