Closed lukecyca closed 3 years ago
@zenhack : the corresponding commit to certbot is https://github.com/certbot/certbot/commit/f743dbec3a04349533735a161b650f02844b2294
The relevant bits are this new function:
def find_chain_with_issuer(fullchains, issuer_cn, warn_on_no_match=False):
"""Chooses the first certificate chain from fullchains which contains an
Issuer Subject Common Name matching issuer_cn.
:param fullchains: The list of fullchains in PEM chain format.
:type fullchains: `list` of `str`
:param `str` issuer_cn: The exact Subject Common Name to match against any
issuer in the certificate chain.
:returns: The best-matching fullchain, PEM-encoded, or the first if none match.
:rtype: `str`
"""
for chain in fullchains:
certs = [x509.load_pem_x509_certificate(cert, default_backend()) \
for cert in CERT_PEM_REGEX.findall(chain.encode())]
# Iterate the fullchain beginning from the leaf. For each certificate encountered,
# match against Issuer Subject CN.
for cert in certs:
cert_issuer_cn = cert.issuer.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
if cert_issuer_cn and cert_issuer_cn[0].value == issuer_cn:
return chain
# Nothing matched, return whatever was first in the list.
if warn_on_no_match:
logger.info("Certbot has been configured to prefer certificate chains with "
"issuer '%s', but no chain from the CA matched this issuer. Using "
"the default certificate chain instead.", issuer_cn)
return fullchains[0]
and the following change to the obtain_certificate_from_csr()
function:
def obtain_certificate_from_csr(self, csr, orderr=None):
"""Obtain certificate.
:param .util.CSR csr: PEM-encoded Certificate Signing
Request. The key used to generate this CSR can be different
than `authkey`.
:param acme.messages.OrderResource orderr: contains authzrs
:returns: certificate and chain as PEM byte strings
:rtype: tuple
"""
if self.auth_handler is None:
msg = ("Unable to obtain certificate because authenticator is "
"not set.")
logger.warning(msg)
raise errors.Error(msg)
if self.account.regr is None:
raise errors.Error("Please register with the ACME server first.")
logger.debug("CSR: %s", csr)
if orderr is None:
orderr = self._get_order_and_authorizations(csr.data, best_effort=False)
deadline = datetime.datetime.now() + datetime.timedelta(seconds=90)
- orderr = self.acme.finalize_order(orderr, deadline)
- cert, chain = crypto_util.cert_and_chain_from_fullchain(orderr.fullchain_pem)
+ get_alt_chains = self.config.preferred_chain is not None
+ orderr = self.acme.finalize_order(orderr, deadline,
+ fetch_alternative_chains=get_alt_chains)
+ fullchain = orderr.fullchain_pem
+ if get_alt_chains and orderr.alternative_fullchains_pem:
+ fullchain = crypto_util.find_chain_with_issuer([fullchain] + \
+ orderr.alternative_fullchains_pem,
+ self.config.preferred_chain,
+ not self.config.dry_run)
+ cert, chain = crypto_util.cert_and_chain_from_fullchain(fullchain)
return cert.encode(), chain.encode()
def obtain_certificate_from_csr(self, csr, orderr=None):
"""Obtain certificate.
:param .util.CSR csr: PEM-encoded Certificate Signing
Request. The key used to generate this CSR can be different
than `authkey`.
:param acme.messages.OrderResource orderr: contains authzrs
:returns: certificate and chain as PEM byte strings
:rtype: tuple
"""
if self.auth_handler is None:
msg = ("Unable to obtain certificate because authenticator is "
"not set.")
logger.warning(msg)
raise errors.Error(msg)
if self.account.regr is None:
raise errors.Error("Please register with the ACME server first.")
logger.debug("CSR: %s", csr)
if orderr is None:
orderr = self._get_order_and_authorizations(csr.data, best_effort=False)
deadline = datetime.datetime.now() + datetime.timedelta(seconds=90)
get_alt_chains = self.config.preferred_chain is not None
orderr = self.acme.finalize_order(orderr, deadline,
fetch_alternative_chains=get_alt_chains)
fullchain = orderr.fullchain_pem
if get_alt_chains and orderr.alternative_fullchains_pem:
fullchain = crypto_util.find_chain_with_issuer([fullchain] + \
orderr.alternative_fullchains_pem,
self.config.preferred_chain,
not self.config.dry_run)
cert, chain = crypto_util.cert_and_chain_from_fullchain(fullchain)
return cert.encode(), chain.encode()
plus handling of the new flag.
Everything else is done by the acme
module starting with version 1.6.0
I'll try to tackle this in the next few days.
The actual deadline is January 11, so there is no rush yet. Let me know if you need help.
I certainly wouldn't say no, if you wanted to tackle it.
Quoting Nicolas Duchon (2020-09-26 03:04:34)
The actual deadline is January 11, so there is no rush yet. Let me know if you need help.
-- You are receiving this because you were mentioned. Reply to this email directly, [1]view it on GitHub, or [2]unsubscribe.
Verweise
@zenhack sorry for the lack of answer, things shifted a bit since then and letsencrypt-nginx-proxy-companion
will be moving away from simp_le
soonish. I probably won't have the spare time needed to work on this after all. 😞
Ok, thanks for letting me know.
Now that DST Root CA X3
has expired, I am getting certificate failures from an old python 3.4 application (using OpenSSL<1.1.0) requesting data from a let's encrypt signed API I host.
It would be great if I could sign the API endpoint with the alternate chain to give the developers of the old python application more time to migrate.
I think it is unlikely that I'll find time to work on this any time soon, but I will review patches if someone else is interested in doing the work.
I've created a pull request for adding a new option (https://github.com/zenhack/simp_le/pull/150):
--use_alt_chain CHAIN_NO
If non-zero, then use alternative certificate chain
number. (default: 0)
Then by using --use_alt_chain=1
I was able to renew my Let's Encrypt certificate with the following chain:
Certificate chain
0 s:CN = …
i:C = US, O = Let's Encrypt, CN = R3
1 s:C = US, O = Let's Encrypt, CN = R3
i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
It's not perfect, as it would be better to use a CN instead of a number. But it is very simple change and at least it might allow to temporarily work-around current problems with expired "DST Root CA X3" certificate in the chain. Apparently some buggy TLS implementations will reject the cert even if "ISRG Root X1" is trusted, just because it is signed by an expired cert.
Closing, since #150 is merged.
I had initially opened this on kuba's repo. Cross-posting it here now after being advised this is where
simp_le
development is happening now.From nginx-proxy/docker-letsencrypt-nginx-proxy-companion#695:
Can simp_le support this option?