Fix deprecate pyopenssl (#10186)

Uploading for tests;

These deprecations are a precursor to #10174 

In addition to the previously discussed `acme` functions, the `certbot`
functions were deprecated as they are primarily used for testing and
support. Marking them deprecated now will allow them to be removed in
the next major release, as they will no-longer be used.
This commit is contained in:
Jonathan Vanasco 2025-02-24 13:54:17 -05:00 committed by GitHub
parent d3aceba188
commit cda56361ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 125 additions and 30 deletions

View file

@ -104,7 +104,12 @@ class PyOpenSSLCertOrReqAllNamesTest(unittest.TestCase):
def _call(cls, loader, name):
# pylint: disable=protected-access
from acme.crypto_util import _pyopenssl_cert_or_req_all_names
return _pyopenssl_cert_or_req_all_names(loader(name))
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='acme.crypto_util._pyopenssl_cert_or_req_all_names is deprecated *'
)
return _pyopenssl_cert_or_req_all_names(loader(name))
def _call_cert(self, name):
return self._call(test_util.load_cert, name)
@ -128,7 +133,12 @@ class PyOpenSSLCertOrReqSANTest(unittest.TestCase):
def _call(cls, loader, name):
# pylint: disable=protected-access
from acme.crypto_util import _pyopenssl_cert_or_req_san
return _pyopenssl_cert_or_req_san(loader(name))
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='acme.crypto_util._pyopenssl_cert_or_req_san is deprecated *'
)
return _pyopenssl_cert_or_req_san(loader(name))
@classmethod
def _get_idn_names(cls):
@ -392,7 +402,12 @@ class DumpPyopensslChainTest(unittest.TestCase):
def _call(cls, loaded):
# pylint: disable=protected-access
from acme.crypto_util import dump_pyopenssl_chain
return dump_pyopenssl_chain(loaded)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='acme.crypto_util.dump_pyopenssl_chain is *'
)
return dump_pyopenssl_chain(loaded)
def test_dump_pyopenssl_chain(self):
names = ['cert.pem', 'cert-san.pem', 'cert-idnsans.pem']
@ -406,10 +421,15 @@ class DumpPyopensslChainTest(unittest.TestCase):
names = ['cert.pem', 'cert-san.pem', 'cert-idnsans.pem']
loaded = [test_util.load_cert(name) for name in names]
wrap_func = jose.ComparableX509
wrapped = [wrap_func(cert) for cert in loaded]
dump_func = OpenSSL.crypto.dump_certificate
length = sum(len(dump_func(OpenSSL.crypto.FILETYPE_PEM, cert)) for cert in loaded)
assert len(self._call(wrapped)) == length
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='The next major version of josepy *'
)
wrapped = [wrap_func(cert) for cert in loaded]
dump_func = OpenSSL.crypto.dump_certificate
length = sum(len(dump_func(OpenSSL.crypto.FILETYPE_PEM, cert)) for cert in loaded)
assert len(self._call(wrapped)) == length
if __name__ == '__main__':

View file

@ -373,6 +373,16 @@ def get_names_from_subject_and_extensions(
def _pyopenssl_cert_or_req_all_names(loaded_cert_or_req: Union[crypto.X509, crypto.X509Req]
) -> List[str]:
"""
Deprecated
.. deprecated: 3.2.1
"""
warnings.warn(
"acme.crypto_util._pyopenssl_cert_or_req_all_names is deprecated and "
"will be removed in the next major release of Certbot.",
DeprecationWarning,
stacklevel=2
)
cert_or_req = loaded_cert_or_req.to_cryptography()
return get_names_from_subject_and_extensions(
cert_or_req.subject, cert_or_req.extensions
@ -391,7 +401,15 @@ def _pyopenssl_cert_or_req_san(cert_or_req: Union[crypto.X509, crypto.X509Req])
:returns: A list of Subject Alternative Names that is DNS.
:rtype: `list` of `str`
Deprecated
.. deprecated: 3.2.1
"""
warnings.warn(
"acme.crypto_util._pyopenssl_cert_or_req_san is deprecated and "
"will be removed in the next major release of Certbot.",
DeprecationWarning,
stacklevel=2
)
exts = cert_or_req.to_cryptography().extensions
try:
san_ext = exts.get_extension_for_class(x509.SubjectAlternativeName)
@ -563,7 +581,15 @@ def dump_pyopenssl_chain(chain: Union[List[jose.ComparableX509], List[crypto.X50
:returns: certificate chain bundle
:rtype: bytes
Deprecated
.. deprecated: 3.2.1
"""
warnings.warn(
"acme.crypto_util.dump_pyopenssl_chain is deprecated and "
"will be removed in the next major release of Certbot.",
DeprecationWarning,
stacklevel=2
)
# XXX: returns empty string when no chain is available, which
# shuts up RenewableCert, but might not be the best solution...

View file

@ -10,7 +10,11 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
### Changed
*
* deprecated `acme.crypto_util.dump_pyopenssl_chain`
* deprecated `acme.crypto_util._pyopenssl_cert_or_req_all_names`
* deprecated `acme.crypto_util._pyopenssl_cert_or_req_san`
* deprecated `certbot.crypto_util.dump_pyopenssl_chain`
* deprecated `certbot.crypto_util.pyopenssl_load_certificate`
### Fixed

View file

@ -5,6 +5,7 @@ import re
import sys
import unittest
from unittest import mock
import warnings
import OpenSSL
import pytest
@ -405,7 +406,13 @@ class CertLoaderTest(unittest.TestCase):
def test_load_valid_cert(self):
from certbot.crypto_util import pyopenssl_load_certificate
cert, file_type = pyopenssl_load_certificate(CERT)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='certbot.crypto_util.pyopenssl_load_certificate is *'
)
cert, file_type = pyopenssl_load_certificate(CERT)
assert file_type == OpenSSL.crypto.FILETYPE_PEM
assert binascii.unhexlify(
cert.digest("sha256").replace(b":", b"")
@ -415,7 +422,12 @@ class CertLoaderTest(unittest.TestCase):
from certbot.crypto_util import pyopenssl_load_certificate
bad_cert_data = CERT.replace(b"BEGIN CERTIFICATE", b"ASDFASDFASDF!!!")
with pytest.raises(errors.Error):
pyopenssl_load_certificate(bad_cert_data)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='certbot.crypto_util.pyopenssl_load_certificate is *'
)
pyopenssl_load_certificate(bad_cert_data)
class NotBeforeTest(unittest.TestCase):

View file

@ -14,6 +14,7 @@ import traceback
from typing import List
import unittest
from unittest import mock
import warnings
import configobj
import josepy as jose
@ -435,7 +436,12 @@ class RevokeTest(test_util.TempDirTestCase):
config = cli.prepare_and_parse_args(plugins, args)
from certbot._internal.main import revoke
revoke(config, plugins)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='certbot.crypto_util.pyopenssl_load_certificate is *'
)
revoke(config, plugins)
@mock.patch('certbot._internal.main._delete_if_appropriate')
@mock.patch('certbot._internal.main.client.acme_client')
@ -1797,18 +1803,23 @@ class MainTest(test_util.ConfigTestCase):
mock_delete_if_appropriate):
mock_delete_if_appropriate.return_value = False
server = 'foo.bar'
self._call_no_clientmock(['--cert-path', SS_CERT_PATH, '--key-path', RSA2048_KEY_PATH,
'--server', server, 'revoke'])
with open(RSA2048_KEY_PATH, 'rb') as f:
assert mock_acme_client.ClientV2.call_count == 1
assert mock_acme_client.ClientNetwork.call_args[0][0] == \
jose.JWK.load(f.read())
with open(SS_CERT_PATH, 'rb') as f:
cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
mock_revoke = mock_acme_client.ClientV2().revoke
mock_revoke.assert_called_once_with(
jose.ComparableX509(cert),
mock.ANY)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='certbot.crypto_util.pyopenssl_load_certificate is *'
)
self._call_no_clientmock(['--cert-path', SS_CERT_PATH, '--key-path', RSA2048_KEY_PATH,
'--server', server, 'revoke'])
with open(RSA2048_KEY_PATH, 'rb') as f:
assert mock_acme_client.ClientV2.call_count == 1
assert mock_acme_client.ClientNetwork.call_args[0][0] == \
jose.JWK.load(f.read())
with open(SS_CERT_PATH, 'rb') as f:
cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
mock_revoke = mock_acme_client.ClientV2().revoke
mock_revoke.assert_called_once_with(
jose.ComparableX509(cert),
mock.ANY)
def test_revoke_with_key_mismatch(self):
server = 'foo.bar'
@ -1822,13 +1833,18 @@ class MainTest(test_util.ConfigTestCase):
mock_delete_if_appropriate):
mock_delete_if_appropriate.return_value = False
mock_determine_account.return_value = (mock.MagicMock(), None)
_, _, _, client = self._call(['--cert-path', CERT, 'revoke'])
with open(CERT) as f:
cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
mock_revoke = client.acme_from_config_key().revoke
mock_revoke.assert_called_once_with(
jose.ComparableX509(cert),
mock.ANY)
with warnings.catch_warnings():
warnings.filterwarnings(
'ignore',
message='certbot.crypto_util.pyopenssl_load_certificate is *'
)
_, _, _, client = self._call(['--cert-path', CERT, 'revoke'])
with open(CERT) as f:
cert = crypto_util.pyopenssl_load_certificate(f.read())[0]
mock_revoke = client.acme_from_config_key().revoke
mock_revoke.assert_called_once_with(
jose.ComparableX509(cert),
mock.ANY)
@mock.patch('certbot._internal.log.post_arg_parse_setup')
def test_register(self, _):

View file

@ -14,6 +14,7 @@ from typing import Set
from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import warnings
from cryptography import x509
from cryptography.exceptions import InvalidSignature
@ -395,7 +396,15 @@ def pyopenssl_load_certificate(data: bytes) -> Tuple[crypto.X509, int]:
:raises errors.Error:
Deprecated
.. deprecated: 3.2.1
"""
warnings.warn(
"certbot.crypto_util.pyopenssl_load_certificate is deprecated and "
"will be removed in the next major release of Certbot.",
DeprecationWarning,
stacklevel=2
)
openssl_errors = []
@ -491,7 +500,15 @@ def dump_pyopenssl_chain(
:param list chain: List of `crypto.X509` (or wrapped in
:class:`josepy.util.ComparableX509`).
Deprecated
.. deprecated: 3.2.1
"""
warnings.warn(
"certbot.crypto_util.dump_pyopenssl_chain is deprecated and "
"will be removed in the next major release of Certbot.",
DeprecationWarning,
stacklevel=2
)
# XXX: returns empty string when no chain is available, which
# shuts up RenewableCert, but might not be the best solution...
return acme_crypto_util.dump_pyopenssl_chain(chain, filetype)