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
|
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.
|
// 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
|
// 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
|
// 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!")
|
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
|
||||||
}
|
}
|
||||||
|
|
||||||
commonName := authz[0]
|
|
||||||
var err error
|
var err error
|
||||||
if privKey == nil {
|
if privKey == nil {
|
||||||
privKey, err = generatePrivateKey(c.keyType)
|
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 san []string
|
||||||
var authURLs []string
|
|
||||||
for _, auth := range authz[1:] {
|
for _, auth := range authz[1:] {
|
||||||
san = append(san, auth.Domain)
|
san = append(san, auth.Domain)
|
||||||
authURLs = append(authURLs, auth.AuthURL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should the CSR be customizable?
|
// TODO: should the CSR be customizable?
|
||||||
|
@ -550,6 +612,17 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
||||||
return CertificateResource{}, err
|
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)
|
csrString := base64.URLEncoding.EncodeToString(csr)
|
||||||
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -561,7 +634,6 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
|
||||||
return CertificateResource{}, err
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
privateKeyPem := pemEncode(privKey)
|
|
||||||
cerRes := CertificateResource{
|
cerRes := CertificateResource{
|
||||||
Domain: commonName.Domain,
|
Domain: commonName.Domain,
|
||||||
CertURL: resp.Header.Get("Location"),
|
CertURL: resp.Header.Get("Location"),
|
||||||
|
|
Loading…
Reference in a new issue