[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1106577: marked as done (unblock: python-josepy/2.0.0-1)



Your message dated Mon, 26 May 2025 17:40:21 +0000
with message-id <E1uJboD-002oCr-2f@respighi.debian.org>
and subject line unblock python-josepy
has caused the Debian Bug report #1106577,
regarding unblock: python-josepy/2.0.0-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1106577: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1106577
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: python-josepy@packages.debian.org, hlieberman@debian.org
Control: affects -1 + src:python-josepy
User: release.debian.org@packages.debian.org
Usertags: unblock
User: hlieberman@debian.org
Usertags: trixie-certbot

Please unblock package python-josepy

[ Reason ]

The certbot 2.x series is end of life and will not receive further updates or
backports of changes.
(https://github.com/certbot/certbot/wiki/Architectural-Decision-Records-2025#-update-to-certbots-version-policy-and-end-of-life-support-on-previous-major-versions)
By far and away, the primary purpose of certbot is to receive certificates from
Let's Encrypt, and the Let's Encrypt team are planning API changes in 2025 which
will break the issuance of TLS certificates for people using the Certbot 2.x
series.

[ Impact ]

If the unblock is not granted, certbot will suddenly stop working at
some point in the next year and users' TLS certificates will expire. Because
certbot tends to be used as a set-it-and-forget-it system, and Let's Encrypt has
recently disabled their email notifications, users' websites and applications
may suddenly be unavailable to users and/or vulnerable to MitM.

[ Tests ]

Certbot's two primary plugins (python-certbot-apache, python-certbot-nginx) and
the main utility (python-certbot) have a test harness which exercises the entire
process of getting a certificate against a test environment. This provides very
high confidence that those packages are still working, and that the libraries
which they depend on (python-josepy, python-acme) are in good health. These
tests pass cleanly on ci.d.n for all three invocations.

The dns plugin packages (python-certbot-dns-*) are substantially less
complicated than the other certbot packages and primarily handle communication
with various companies' API layers. Those are unlikely to have broken because of
the changes to certbot's internals; the primary way in which those packages
break are due to API changes on the providers' ends.

[ Risks ]

Upgrading the packages across major versions comes with risks, certainly, but
there is little in the way of alternative. The changes are too complex for me to
be willing to attempt to backport, and in a security critical application, I am
even more reticent than I normally would be. I recognize the late application
introduces even more risk --- and rightfully, I'm sure no small amount of
annoyance --- but it is where we've ended up.

[ Checklist ]
  [X] all changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in testing

[ Other info ]

This is one of a series of identical unblock requests with only
package names and debdiffs differing.

unblock python-josepy/2.0.0-1
diff -Nru python-josepy-1.14.0/CHANGELOG.rst python-josepy-2.0.0/CHANGELOG.rst
--- python-josepy-1.14.0/CHANGELOG.rst	2023-11-01 09:36:41.805958700 -0400
+++ python-josepy-2.0.0/CHANGELOG.rst	1969-12-31 19:00:00.000000000 -0500
@@ -1,6 +1,38 @@
 Changelog
 =========
 
+2.0.0 (2025-02-10)
+------------------
+
+* Breaking Change: PyOpenSSL has been fully removed.
+  - Dropped objects:
+    `josepy.util.ComparableX509`
+  - Functions now expect `cryptography.x509` objects:
+    `josepy.json_util.encode_cert`
+    `josepy.json_util.encode_csr`
+    `josepy.jws.Header.x5c.encoder`
+  - Functions now return `cryptography.x509` objects:
+    `josepy.json_util.decode_cert`
+    `josepy.json_util.decode_csr`
+    `josepy.jws.Header.x5c.decoder`
+* Dropped support for Python 3.8.
+
+
+1.15.0 (2025-01-22)
+-------------------
+
+* Added a deprecation warning about future backwards incompatible changes. The
+  text of that warning is "The next major version of josepy will remove
+  josepy.util.ComparableX509 and all uses of it as part of removing our
+  dependency on PyOpenSSL. This includes modifying any functions with
+  ComparableX509 parameters or return values. This will be a breaking change.
+  To avoid breakage, we recommend pinning josepy < 2.0.0 until josepy 2.0.0 is
+  out and you've had time to update your code."
+* Added support for Python 3.13.
+* Dropped support for Python 3.7.
+* Support for Python 3.8 has been deprecated and will be removed in the next
+  scheduled release.
+
 1.14.0 (2023-11-01)
 -------------------
 
diff -Nru python-josepy-1.14.0/CONTRIBUTING.md python-josepy-2.0.0/CONTRIBUTING.md
--- python-josepy-1.14.0/CONTRIBUTING.md	2023-11-01 09:36:41.806066000 -0400
+++ python-josepy-2.0.0/CONTRIBUTING.md	1969-12-31 19:00:00.000000000 -0500
@@ -28,7 +28,7 @@
 
 If you're a developer, we have some helpful information in our
 [Developer's Guide](https://certbot.eff.org/docs/contributing.html) to get you
-started. In particular, we recommend you read these sections 
+started. In particular, we recommend you read these sections
 
  - [Finding issues to work on](https://certbot.eff.org/docs/contributing.html#find-issues-to-work-on)
  - [Coding style](https://certbot.eff.org/docs/contributing.html#coding-style)
@@ -71,3 +71,10 @@
 ```bash
 $ tox -l
 ```
+
+## Updating dependencies
+
+Our poetry.lock file is only used during development so security
+vulnerabilities in the pinned packages are rarely relevant. With that said, if
+you want to update package versions, you can use the [`poetry update`
+command](https://python-poetry.org/docs/cli/#update).
diff -Nru python-josepy-1.14.0/debian/changelog python-josepy-2.0.0/debian/changelog
--- python-josepy-1.14.0/debian/changelog	2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/changelog	2025-05-24 14:47:42.000000000 -0400
@@ -1,3 +1,20 @@
+python-josepy (2.0.0-1) unstable; urgency=medium
+
+  * New upstream version 2.0.0 (Closes: #1106463)
+  * Drop patches applied upstream; refresh remaining
+  * Drop dependency on openssl, following upstream (Closes: #1080132)
+  * Drop dependency on importlib.resources (Closes: #1104684)
+  * Bump S-V; no changes needed
+  * Touch all files at build time
+
+ -- Harlan Lieberman-Berg <hlieberman@debian.org>  Sat, 24 May 2025 14:47:42 -0400
+
+python-josepy (1.14.0-3) unstable; urgency=medium
+
+  * Add patch to fix intersphinx mapping (Closes: #1090148)
+
+ -- Harlan Lieberman-Berg <hlieberman@debian.org>  Wed, 25 Dec 2024 14:54:15 -0500
+
 python-josepy (1.14.0-2) unstable; urgency=medium
 
   [ Janitor ]
diff -Nru python-josepy-1.14.0/debian/control python-josepy-2.0.0/debian/control
--- python-josepy-1.14.0/debian/control	2024-08-31 15:52:57.000000000 -0400
+++ python-josepy-2.0.0/debian/control	2025-05-24 14:46:31.000000000 -0400
@@ -8,14 +8,12 @@
         pybuild-plugin-pyproject,
         python3,
         python3-cryptography,
-        python3-importlib-resources,
-        python3-openssl,
         python3-poetry-core,
         python3-pytest,
         python3-setuptools,
         python3-sphinx,
         python3-sphinx-rtd-theme
-Standards-Version: 4.6.2
+Standards-Version: 4.7.2
 Homepage: https://certbot.eff.org/
 Vcs-Git: https://salsa.debian.org/letsencrypt-team/certbot/josepy.git
 Vcs-Browser: https://salsa.debian.org/letsencrypt-team/certbot/josepy
diff -Nru python-josepy-1.14.0/debian/patches/0001-remove-files.patch python-josepy-2.0.0/debian/patches/0001-remove-files.patch
--- python-josepy-1.14.0/debian/patches/0001-remove-files.patch	2024-08-31 15:48:55.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0001-remove-files.patch	2025-05-24 14:25:13.000000000 -0400
@@ -5,7 +5,7 @@
 ===================================================================
 --- josepy.orig/pyproject.toml
 +++ josepy/pyproject.toml
-@@ -30,8 +30,6 @@ homepage = "https://github.com/certbot/j
+@@ -29,8 +29,6 @@ homepage = "https://github.com/certbot/j
  authors = ["Certbot Project <certbot-dev@eff.org>"]
  readme = "README.rst"
  include = [
diff -Nru python-josepy-1.14.0/debian/patches/0002-disable-depreciation.patch python-josepy-2.0.0/debian/patches/0002-disable-depreciation.patch
--- python-josepy-1.14.0/debian/patches/0002-disable-depreciation.patch	2024-08-31 16:03:16.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0002-disable-depreciation.patch	1969-12-31 19:00:00.000000000 -0500
@@ -1,18 +0,0 @@
-Description: Disable depreciation warning relating to pyOpenSSL
-Author: Harlan Lieberman-Berg <hlieberman@debian.org>
-Bug: https://github.com/certbot/josepy/issues/181
-Bug-Debian: https://bugs.debian.org/1080132
-Forwarded: not-needed
-Index: josepy/pyproject.toml
-===================================================================
---- josepy.orig/pyproject.toml
-+++ josepy/pyproject.toml
-@@ -94,7 +94,7 @@ disallow_untyped_defs = true
- 
- [tool.pytest.ini_options]
- # We also ignore our own deprecation warning about dropping Python 3.7 support.
--filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning"]
-+filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning", "ignore:CSR support in pyOpenSSL is deprecated:DeprecationWarning"]
- norecursedirs = "*.egg .eggs dist build docs .tox"
- 
- # Isort tooling configuration
diff -Nru python-josepy-1.14.0/debian/patches/0003-fix-intersphinx.patch python-josepy-2.0.0/debian/patches/0003-fix-intersphinx.patch
--- python-josepy-1.14.0/debian/patches/0003-fix-intersphinx.patch	2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/0003-fix-intersphinx.patch	1969-12-31 19:00:00.000000000 -0500
@@ -1,28 +0,0 @@
-From 65849c52c96f4b09806ca600e4845abfd30795c2 Mon Sep 17 00:00:00 2001
-From: Harlan Lieberman-Berg <hlieberman@setec.io>
-Date: Wed, 25 Dec 2024 14:43:33 -0500
-Subject: [PATCH] Switch intersphinx_mapping to new format
-Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1090148
-Forwarded: https://github.com/certbot/josepy/pull/198
-Author: Harlan Lieberman-Berg <hlieberman@debian.org>
-
-Sphinx v8 no longer supports intersphinx_mapping being a direct map;
-it now must be a map with identifiers and tuples.  This fixes a FTBFS
-downstream in Debian, bug #1090148.
----
- docs/conf.py | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-Index: josepy/docs/conf.py
-===================================================================
---- josepy.orig/docs/conf.py
-+++ josepy/docs/conf.py
-@@ -185,6 +185,6 @@ texinfo_documents = [
- 
- # Example configuration for intersphinx: refer to the Python standard library.
- intersphinx_mapping = {
--    "https://docs.python.org/": None,
--    "https://cryptography.io/en/latest/": None,
-+    'python': ("https://docs.python.org/";, None),
-+    'cryptography': ("https://cryptography.io/en/latest/";, None),
- }
diff -Nru python-josepy-1.14.0/debian/patches/series python-josepy-2.0.0/debian/patches/series
--- python-josepy-1.14.0/debian/patches/series	2024-08-31 16:11:35.000000000 -0400
+++ python-josepy-2.0.0/debian/patches/series	2025-05-24 14:24:45.000000000 -0400
@@ -1,3 +1 @@
 0001-remove-files.patch
-0002-disable-depreciation.patch
-0003-fix-intersphinx.patch
diff -Nru python-josepy-1.14.0/debian/rules python-josepy-2.0.0/debian/rules
--- python-josepy-1.14.0/debian/rules	2024-08-31 15:59:04.000000000 -0400
+++ python-josepy-2.0.0/debian/rules	2025-05-24 14:44:41.000000000 -0400
@@ -6,6 +6,7 @@
 	dh $@ --with python3,sphinxdoc --buildsystem=pybuild
 
 override_dh_auto_build:
+	find $(CURDIR) -print0 | xargs -0 touch -d@${SOURCE_DATE_EPOCH} '{}' \;
 	dh_auto_build
 	PYTHONPATH=$(CURDIR)/src \
 		http_proxy='127.0.0.1:9' \
diff -Nru python-josepy-1.14.0/docs/conf.py python-josepy-2.0.0/docs/conf.py
--- python-josepy-1.14.0/docs/conf.py	2023-11-01 09:36:41.833833500 -0400
+++ python-josepy-2.0.0/docs/conf.py	1969-12-31 19:00:00.000000000 -0500
@@ -63,9 +63,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'1.14'
+version = "2.0"
 # The full version, including alpha/beta/rc tags.
-release = u'1.14.0'
+release = "2.0.0"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -185,6 +185,6 @@
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {
-    "https://docs.python.org/": None,
-    "https://cryptography.io/en/latest/": None,
+    "python": ("https://docs.python.org/";, None),
+    "cryptography": ("https://cryptography.io/en/latest/";, None),
 }
diff -Nru python-josepy-1.14.0/docs/requirements.txt python-josepy-2.0.0/docs/requirements.txt
--- python-josepy-1.14.0/docs/requirements.txt	2023-11-01 09:36:41.807566200 -0400
+++ python-josepy-2.0.0/docs/requirements.txt	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +0,0 @@
-# We pin our dependencies with a constraints file for increased stability.
-
--c ../constraints.txt
--e .[docs]
diff -Nru python-josepy-1.14.0/PKG-INFO python-josepy-2.0.0/PKG-INFO
--- python-josepy-1.14.0/PKG-INFO	1969-12-31 19:00:00.000000000 -0500
+++ python-josepy-2.0.0/PKG-INFO	1969-12-31 19:00:00.000000000 -0500
@@ -1,30 +1,28 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.3
 Name: josepy
-Version: 1.14.0
+Version: 2.0.0
 Summary: JOSE protocol implementation in Python
-Home-page: https://github.com/certbot/josepy
 License: Apache-2.0
 Author: Certbot Project
 Author-email: certbot-dev@eff.org
-Requires-Python: >=3.7,<4.0
+Requires-Python: >=3.9,<4.0
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
 Classifier: License :: OSI Approved :: Apache Software License
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
 Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
 Classifier: Topic :: Internet :: WWW/HTTP
 Classifier: Topic :: Security
 Provides-Extra: docs
 Requires-Dist: cryptography (>=1.5)
-Requires-Dist: pyopenssl (>=0.13)
 Requires-Dist: sphinx (>=4.3.0) ; extra == "docs"
 Requires-Dist: sphinx-rtd-theme (>=1.0) ; extra == "docs"
+Project-URL: Homepage, https://github.com/certbot/josepy
 Description-Content-Type: text/x-rst
 
 JOSE protocol implementation in Python using cryptography
@@ -38,11 +36,7 @@
 .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
   :target: https://github.com/psf/black
 
-Originally developed as part of the ACME_ protocol implementation.
-
-.. _ACME: https://pypi.python.org/pypi/acme
-
-To learn how to contribute to this project, see CONTRIBUTING.md_.
+For more information about contributing to this project, see CONTRIBUTING.md_.
 
 .. _CONTRIBUTING.md: CONTRIBUTING.md
 
diff -Nru python-josepy-1.14.0/pyproject.toml python-josepy-2.0.0/pyproject.toml
--- python-josepy-1.14.0/pyproject.toml	2023-11-01 09:36:42.032093000 -0400
+++ python-josepy-2.0.0/pyproject.toml	1969-12-31 19:00:00.000000000 -0500
@@ -8,7 +8,7 @@
 
 [tool.poetry]
 name = "josepy"
-version = "1.14.0"
+version = "2.0.0"
 description = "JOSE protocol implementation in Python"
 license = "Apache License 2.0"
 classifiers = [
@@ -17,12 +17,11 @@
     "License :: OSI Approved :: Apache Software License",
     "Programming Language :: Python",
     "Programming Language :: Python :: 3",
-    "Programming Language :: Python :: 3.7",
-    "Programming Language :: Python :: 3.8",
     "Programming Language :: Python :: 3.9",
     "Programming Language :: Python :: 3.10",
     "Programming Language :: Python :: 3.11",
     "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
     "Topic :: Internet :: WWW/HTTP",
     "Topic :: Security",
 ]
@@ -38,26 +37,19 @@
 [tool.poetry.dependencies]
 # This should be kept in sync with the value of target-version in our
 # configuration for black below.
-python = "^3.7"
+python = "^3.9"
 # load_pem_private/public_key (>=0.6)
 # rsa_recover_prime_factors (>=0.8)
 # add sign() and verify() to asymetric keys (RSA >=1.4, ECDSA >=1.5)
 cryptography = ">=1.5"
-# Connection.set_tlsext_host_name (>=0.13)
-pyopenssl = ">=0.13"
 # >=4.3.0 is needed for Python 3.10 support
 sphinx = {version = ">=4.3.0", optional = true}
 sphinx-rtd-theme = {version = ">=1.0", optional = true}
 
-[tool.poetry.dev-dependencies]
+[tool.poetry.group.dev.dependencies]
 # coverage[toml] extra is required to read the coverage config from pyproject.toml
 coverage = {version = ">=4.0", extras = ["toml"]}
-# importlib_resources 1.3 was the version included in Python 3.9 which
-# introduced the functionality we are using. See
-# https://github.com/python/importlib_resources/tree/7f4fbb5ee026d7610636d5ece18b09c64aa0c893#compatibility.
-importlib_resources = {version = ">=1.3", python = "<3.9"}
 mypy = "*"
-types-pyOpenSSL = "*"
 types-pyRFC3339 = "*"
 types-requests = "*"
 types-setuptools = "*"
@@ -82,7 +74,7 @@
 line-length = 100
 # This should be kept in sync with the version of Python specified in poetry's
 # dependencies above.
-target-version = ['py37', 'py38', 'py39', 'py310', 'py311', 'py312']
+target-version = ['py39', 'py310', 'py311', 'py312', 'py313']
 
 # Mypy tooling configuration
 
@@ -95,8 +87,9 @@
 # Pytest tooling configuration
 
 [tool.pytest.ini_options]
-# We also ignore our own deprecation warning about dropping Python 3.7 support.
-filterwarnings = ["error", "ignore:Python 3.7 support will be dropped:DeprecationWarning"]
+filterwarnings = [
+    "error",
+]
 norecursedirs = "*.egg .eggs dist build docs .tox"
 
 # Isort tooling configuration
diff -Nru python-josepy-1.14.0/README.rst python-josepy-2.0.0/README.rst
--- python-josepy-1.14.0/README.rst	2023-11-01 09:36:41.806271000 -0400
+++ python-josepy-2.0.0/README.rst	1969-12-31 19:00:00.000000000 -0500
@@ -9,10 +9,6 @@
 .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
   :target: https://github.com/psf/black
 
-Originally developed as part of the ACME_ protocol implementation.
-
-.. _ACME: https://pypi.python.org/pypi/acme
-
-To learn how to contribute to this project, see CONTRIBUTING.md_.
+For more information about contributing to this project, see CONTRIBUTING.md_.
 
 .. _CONTRIBUTING.md: CONTRIBUTING.md
diff -Nru python-josepy-1.14.0/src/josepy/b64.py python-josepy-2.0.0/src/josepy/b64.py
--- python-josepy-1.14.0/src/josepy/b64.py	2023-11-01 09:36:41.808879100 -0400
+++ python-josepy-2.0.0/src/josepy/b64.py	1969-12-31 19:00:00.000000000 -0500
@@ -10,6 +10,7 @@
    standard library.
 
 """
+
 import base64
 from typing import Union
 
diff -Nru python-josepy-1.14.0/src/josepy/errors.py python-josepy-2.0.0/src/josepy/errors.py
--- python-josepy-1.14.0/src/josepy/errors.py	2023-11-01 09:36:41.808996700 -0400
+++ python-josepy-2.0.0/src/josepy/errors.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """JOSE errors."""
+
 from typing import Any
 
 
diff -Nru python-josepy-1.14.0/src/josepy/__init__.py python-josepy-2.0.0/src/josepy/__init__.py
--- python-josepy-1.14.0/src/josepy/__init__.py	2023-11-01 09:36:41.808747000 -0400
+++ python-josepy-2.0.0/src/josepy/__init__.py	1969-12-31 19:00:00.000000000 -0500
@@ -25,8 +25,6 @@
 .. _ACME: https://pypi.python.org/pypi/acme
 
 """
-import sys
-import warnings
 
 # flake8: noqa
 from josepy.b64 import b64decode, b64encode
@@ -72,13 +70,5 @@
     ComparableECKey,
     ComparableKey,
     ComparableRSAKey,
-    ComparableX509,
     ImmutableMap,
 )
-
-if sys.version_info[:2] == (3, 7):
-    warnings.warn(
-        "Python 3.7 support will be dropped in the next scheduled release of "
-        "josepy. Please upgrade your Python version.",
-        DeprecationWarning,
-    )
diff -Nru python-josepy-1.14.0/src/josepy/interfaces.py python-josepy-2.0.0/src/josepy/interfaces.py
--- python-josepy-1.14.0/src/josepy/interfaces.py	2023-11-01 09:36:41.809141400 -0400
+++ python-josepy-2.0.0/src/josepy/interfaces.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """JOSE interfaces."""
+
 import abc
 import json
 from collections.abc import Mapping, Sequence
diff -Nru python-josepy-1.14.0/src/josepy/json_util.py python-josepy-2.0.0/src/josepy/json_util.py
--- python-josepy-1.14.0/src/josepy/json_util.py	2023-11-01 09:36:41.809327800 -0400
+++ python-josepy-2.0.0/src/josepy/json_util.py	1969-12-31 19:00:00.000000000 -0500
@@ -6,6 +6,7 @@
 .. _`Go's "json" package`: http://golang.org/pkg/encoding/json/
 
 """
+
 import abc
 import binascii
 import logging
@@ -21,7 +22,8 @@
     TypeVar,
 )
 
-from OpenSSL import crypto
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
 
 from josepy import b64, errors, interfaces, util
 
@@ -425,59 +427,65 @@
         raise errors.DeserializationError(error)
 
 
-def encode_cert(cert: util.ComparableX509) -> str:
+def encode_cert(cert: x509.Certificate) -> str:
     """Encode certificate as JOSE Base-64 DER.
 
-    :type cert: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
+    :type cert: `cryptography.x509.Certificate`
     :rtype: unicode
 
+    .. versionchanged:: 2.0.0
+       The `cert` parameter is now `cryptography.x509.Certificate`.
+       Previously this was an `josepy.util.ComparableX509` object, which wrapped
+       an `OpenSSL.crypto.X509` object.
     """
-    if isinstance(cert.wrapped, crypto.X509Req):
-        raise ValueError("Error input is actually a certificate request.")
-
-    return encode_b64jose(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert.wrapped))
+    return encode_b64jose(cert.public_bytes(Encoding.DER))
 
 
-def decode_cert(b64der: str) -> util.ComparableX509:
+def decode_cert(b64der: str) -> x509.Certificate:
     """Decode JOSE Base-64 DER-encoded certificate.
 
     :param unicode b64der:
-    :rtype: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
+    :rtype: `cryptography.x509.Certificate`
 
+    .. versionchanged:: 2.0.0
+       The returned object is now a `cryptography.x509.Certificate`.
+       Previously this was an `josepy.util.ComparableX509` object, which wrapped
+       an `OpenSSL.crypto.X509` object.
     """
     try:
-        return util.ComparableX509(
-            crypto.load_certificate(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
-        )
-    except crypto.Error as error:
+        return x509.load_der_x509_certificate(decode_b64jose(b64der))
+    except ValueError as error:
         raise errors.DeserializationError(error)
 
 
-def encode_csr(csr: util.ComparableX509) -> str:
+def encode_csr(csr: x509.CertificateSigningRequest) -> str:
     """Encode CSR as JOSE Base-64 DER.
 
-    :type csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
+    :type csr: `cryptography.x509.CertificateSigningRequest`
     :rtype: unicode
 
+    .. versionchanged:: 2.0.0
+       The `cert` parameter is now `cryptography.x509.CertificateSigningRequest`.
+       Previously this was an `josepy.util.ComparableX509` object, which wrapped
+       an `OpenSSL.crypto.X509Req` object.
     """
-    if isinstance(csr.wrapped, crypto.X509):
-        raise ValueError("Error input is actually a certificate.")
-
-    return encode_b64jose(crypto.dump_certificate_request(crypto.FILETYPE_ASN1, csr.wrapped))
+    return encode_b64jose(csr.public_bytes(Encoding.DER))
 
 
-def decode_csr(b64der: str) -> util.ComparableX509:
+def decode_csr(b64der: str) -> x509.CertificateSigningRequest:
     """Decode JOSE Base-64 DER-encoded CSR.
 
     :param unicode b64der:
-    :rtype: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
+    :rtype: `cryptography.x509.CertificateSigningRequest`
 
+    .. versionchanged:: 2.0.0
+       The returned object is now a `cryptography.x509.CertificateSigningRequest`.
+       Previously this was an `josepy.util.ComparableX509` object, which wrapped
+       an `OpenSSL.crypto.X509Req` object.
     """
     try:
-        return util.ComparableX509(
-            crypto.load_certificate_request(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
-        )
-    except crypto.Error as error:
+        return x509.load_der_x509_csr(decode_b64jose(b64der))
+    except ValueError as error:
         raise errors.DeserializationError(error)
 
 
diff -Nru python-josepy-1.14.0/src/josepy/jwa.py python-josepy-2.0.0/src/josepy/jwa.py
--- python-josepy-1.14.0/src/josepy/jwa.py	2023-11-01 09:36:41.809478800 -0400
+++ python-josepy-2.0.0/src/josepy/jwa.py	1969-12-31 19:00:00.000000000 -0500
@@ -3,6 +3,7 @@
 https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
 
 """
+
 import abc
 import logging
 from collections.abc import Hashable
diff -Nru python-josepy-1.14.0/src/josepy/jwk.py python-josepy-2.0.0/src/josepy/jwk.py
--- python-josepy-1.14.0/src/josepy/jwk.py	2023-11-01 09:36:41.809641400 -0400
+++ python-josepy-2.0.0/src/josepy/jwk.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """JSON Web Key."""
+
 import abc
 import json
 import logging
diff -Nru python-josepy-1.14.0/src/josepy/jws.py python-josepy-2.0.0/src/josepy/jws.py
--- python-josepy-1.14.0/src/josepy/jws.py	2023-11-01 09:36:41.809802000 -0400
+++ python-josepy-2.0.0/src/josepy/jws.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """JSON Web Signature."""
+
 import argparse
 import base64
 import sys
@@ -14,12 +15,12 @@
     cast,
 )
 
-from OpenSSL import crypto
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
 
 import josepy
 from josepy import b64, errors, json_util, jwa
 from josepy import jwk as jwk_mod
-from josepy import util
 
 
 class MediaType:
@@ -79,7 +80,7 @@
     )
     kid: Optional[str] = json_util.field("kid", omitempty=True)
     x5u: Optional[bytes] = json_util.field("x5u", omitempty=True)
-    x5c: Tuple[util.ComparableX509, ...] = json_util.field("x5c", omitempty=True, default=())
+    x5c: Tuple[x509.Certificate, ...] = json_util.field("x5c", omitempty=True, default=())
     x5t: Optional[bytes] = json_util.field("x5t", decoder=json_util.decode_b64jose, omitempty=True)
     x5tS256: Optional[bytes] = json_util.field(
         "x5t#S256", decoder=json_util.decode_b64jose, omitempty=True
@@ -137,21 +138,25 @@
 
     @x5c.encoder  # type: ignore
     def x5c(value):
-        return [
-            base64.b64encode(crypto.dump_certificate(crypto.FILETYPE_ASN1, cert.wrapped))
-            for cert in value
-        ]
+        """
+        .. versionchanged:: 2.0.0
+           The values are now `cryptography.x509.Certificate` objects.
+           Previously these were `josepy.util.ComparableX509` objects, which wrapped
+           `OpenSSL.crypto.X509` objects.
+        """
+        return [base64.b64encode(cert.public_bytes(Encoding.DER)) for cert in value]
 
     @x5c.decoder  # type: ignore
     def x5c(value):
+        """
+        .. versionchanged:: 2.0.0
+           The values are now `cryptography.x509.Certificate` objects.
+           Previously these were `josepy.util.ComparableX509` objects, which wrapped
+           `OpenSSL.crypto.X509` objects.
+        """
         try:
-            return tuple(
-                util.ComparableX509(
-                    crypto.load_certificate(crypto.FILETYPE_ASN1, base64.b64decode(cert))
-                )
-                for cert in value
-            )
-        except crypto.Error as error:
+            return tuple(x509.load_der_x509_certificate(base64.b64decode(cert)) for cert in value)
+        except ValueError as error:
             raise errors.DeserializationError(error)
 
 
diff -Nru python-josepy-1.14.0/src/josepy/magic_typing.py python-josepy-2.0.0/src/josepy/magic_typing.py
--- python-josepy-1.14.0/src/josepy/magic_typing.py	2023-11-01 09:36:41.809918900 -0400
+++ python-josepy-2.0.0/src/josepy/magic_typing.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Shim class to not have to depend on typing module in prod."""
+
 # mypy: ignore-errors
 import sys
 import warnings
diff -Nru python-josepy-1.14.0/src/josepy/util.py python-josepy-2.0.0/src/josepy/util.py
--- python-josepy-1.14.0/src/josepy/util.py	2023-11-01 09:36:41.810105600 -0400
+++ python-josepy-2.0.0/src/josepy/util.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """JOSE utilities."""
+
 import abc
 import sys
 import warnings
@@ -7,7 +8,6 @@
 from typing import Any, Callable, Iterator, List, Tuple, TypeVar, Union, cast
 
 from cryptography.hazmat.primitives.asymmetric import ec, rsa
-from OpenSSL import crypto
 
 
 # Deprecated. Please use built-in decorators @classmethod and abc.abstractmethod together instead.
@@ -15,51 +15,6 @@
     return classmethod(abc.abstractmethod(func))
 
 
-class ComparableX509:
-    """Wrapper for OpenSSL.crypto.X509** objects that supports __eq__.
-
-    :ivar wrapped: Wrapped certificate or certificate request.
-    :type wrapped: `OpenSSL.crypto.X509` or `OpenSSL.crypto.X509Req`.
-
-    """
-
-    def __init__(self, wrapped: Union[crypto.X509, crypto.X509Req]) -> None:
-        assert isinstance(wrapped, crypto.X509) or isinstance(wrapped, crypto.X509Req)
-        self.wrapped = wrapped
-
-    def __getattr__(self, name: str) -> Any:
-        return getattr(self.wrapped, name)
-
-    def _dump(self, filetype: int = crypto.FILETYPE_ASN1) -> bytes:
-        """Dumps the object into a buffer with the specified encoding.
-
-        :param int filetype: The desired encoding. Should be one of
-            `OpenSSL.crypto.FILETYPE_ASN1`,
-            `OpenSSL.crypto.FILETYPE_PEM`, or
-            `OpenSSL.crypto.FILETYPE_TEXT`.
-
-        :returns: Encoded X509 object.
-        :rtype: bytes
-
-        """
-        if isinstance(self.wrapped, crypto.X509):
-            return crypto.dump_certificate(filetype, self.wrapped)
-
-        # assert in __init__ makes sure this is X509Req
-        return crypto.dump_certificate_request(filetype, self.wrapped)
-
-    def __eq__(self, other: Any) -> bool:
-        if not isinstance(other, self.__class__):
-            return NotImplemented
-        return self._dump() == other._dump()
-
-    def __hash__(self) -> int:
-        return hash((self.__class__, self._dump()))
-
-    def __repr__(self) -> str:
-        return "<{0}({1!r})>".format(self.__class__.__name__, self.wrapped)
-
-
 class ComparableKey:
     """Comparable wrapper for ``cryptography`` keys.
 
diff -Nru python-josepy-1.14.0/tests/b64_test.py python-josepy-2.0.0/tests/b64_test.py
--- python-josepy-1.14.0/tests/b64_test.py	2023-11-01 09:36:41.810268000 -0400
+++ python-josepy-2.0.0/tests/b64_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.b64."""
+
 import sys
 from typing import Union
 
diff -Nru python-josepy-1.14.0/tests/errors_test.py python-josepy-2.0.0/tests/errors_test.py
--- python-josepy-1.14.0/tests/errors_test.py	2023-11-01 09:36:41.810395700 -0400
+++ python-josepy-2.0.0/tests/errors_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.errors."""
+
 import sys
 import unittest
 
diff -Nru python-josepy-1.14.0/tests/init_test.py python-josepy-2.0.0/tests/init_test.py
--- python-josepy-1.14.0/tests/init_test.py	2023-11-01 09:36:41.810521000 -0400
+++ python-josepy-2.0.0/tests/init_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,25 +0,0 @@
-import importlib
-import re
-import sys
-import warnings
-
-import pytest
-
-import josepy
-
-
-@pytest.mark.skipif(sys.version_info[:2] != (3, 7), reason="requires Python 3.7")
-def test_warns() -> None:
-    with pytest.warns(DeprecationWarning, match=re.escape(r"Python 3.7 support")):
-        importlib.reload(josepy)
-
-
-@pytest.mark.skipif(sys.version_info[:2] == (3, 7), reason="requires Python != 3.7")
-def test_does_not_warn() -> None:
-    with warnings.catch_warnings():
-        warnings.simplefilter("error")
-        importlib.reload(josepy)
-
-
-if __name__ == "__main__":
-    sys.exit(pytest.main(sys.argv[1:] + [__file__]))  # pragma: no cover
diff -Nru python-josepy-1.14.0/tests/interfaces_test.py python-josepy-2.0.0/tests/interfaces_test.py
--- python-josepy-1.14.0/tests/interfaces_test.py	2023-11-01 09:36:41.810647000 -0400
+++ python-josepy-2.0.0/tests/interfaces_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.interfaces."""
+
 import sys
 import unittest
 from typing import Any, Dict, List
diff -Nru python-josepy-1.14.0/tests/json_util_test.py python-josepy-2.0.0/tests/json_util_test.py
--- python-josepy-1.14.0/tests/json_util_test.py	2023-11-01 09:36:41.810800800 -0400
+++ python-josepy-2.0.0/tests/json_util_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.json_util."""
+
 import itertools
 import sys
 import unittest
@@ -7,11 +8,12 @@
 
 import pytest
 import test_util
+from cryptography import x509
 
 from josepy import errors, interfaces, util
 
-CERT = test_util.load_comparable_cert("cert.pem")
-CSR = test_util.load_comparable_csr("csr.pem")
+CERT = test_util.load_cert("cert.pem")
+CSR = test_util.load_csr("csr.pem")
 
 
 class FieldTest(unittest.TestCase):
@@ -326,7 +328,7 @@
         from josepy.json_util import decode_cert
 
         cert = decode_cert(self.b64_cert)
-        assert isinstance(cert, util.ComparableX509)
+        assert isinstance(cert, x509.Certificate)
         assert cert == CERT
         with pytest.raises(errors.DeserializationError):
             decode_cert("")
@@ -340,7 +342,7 @@
         from josepy.json_util import decode_csr
 
         csr = decode_csr(self.b64_csr)
-        assert isinstance(csr, util.ComparableX509)
+        assert isinstance(csr, x509.CertificateSigningRequest)
         assert csr == CSR
         with pytest.raises(errors.DeserializationError):
             decode_csr("")
diff -Nru python-josepy-1.14.0/tests/jwa_test.py python-josepy-2.0.0/tests/jwa_test.py
--- python-josepy-1.14.0/tests/jwa_test.py	2023-11-01 09:36:41.810944800 -0400
+++ python-josepy-2.0.0/tests/jwa_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.jwa."""
+
 import sys
 import unittest
 from typing import Any
diff -Nru python-josepy-1.14.0/tests/jwk_test.py python-josepy-2.0.0/tests/jwk_test.py
--- python-josepy-1.14.0/tests/jwk_test.py	2023-11-01 09:36:41.811095200 -0400
+++ python-josepy-2.0.0/tests/jwk_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.jwk."""
+
 import binascii
 import sys
 import unittest
diff -Nru python-josepy-1.14.0/tests/jws_test.py python-josepy-2.0.0/tests/jws_test.py
--- python-josepy-1.14.0/tests/jws_test.py	2023-11-01 09:36:41.811241600 -0400
+++ python-josepy-2.0.0/tests/jws_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,16 +1,18 @@
 """Tests for josepy.jws."""
+
 import base64
 import sys
 import unittest
 from unittest import mock
 
-import OpenSSL
 import pytest
 import test_util
+from cryptography import x509
+from cryptography.hazmat.primitives.serialization import Encoding
 
 from josepy import errors, json_util, jwa, jwk
 
-CERT = test_util.load_comparable_cert("cert.pem")
+CERT = test_util.load_cert("cert.pem")
 KEY = jwk.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
 
 
@@ -71,8 +73,8 @@
 
         header = Header(x5c=(CERT, CERT))
         jobj = header.to_partial_json()
-        assert isinstance(CERT.wrapped, OpenSSL.crypto.X509)
-        cert_asn1 = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, CERT.wrapped)
+        assert isinstance(CERT, x509.Certificate)
+        cert_asn1 = CERT.public_bytes(Encoding.DER)
         cert_b64 = base64.b64encode(cert_asn1)
         assert jobj == {"x5c": [cert_b64, cert_b64]}
         assert header == Header.from_json(jobj)
diff -Nru python-josepy-1.14.0/tests/magic_typing_test.py python-josepy-2.0.0/tests/magic_typing_test.py
--- python-josepy-1.14.0/tests/magic_typing_test.py	2023-11-01 09:36:41.811362300 -0400
+++ python-josepy-2.0.0/tests/magic_typing_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.magic_typing."""
+
 import sys
 import warnings
 from unittest import mock
diff -Nru python-josepy-1.14.0/tests/test_util.py python-josepy-2.0.0/tests/test_util.py
--- python-josepy-1.14.0/tests/test_util.py	2023-11-01 09:36:41.811495300 -0400
+++ python-josepy-2.0.0/tests/test_util.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,26 +1,20 @@
 """Test utilities."""
+
 import atexit
 import contextlib
+import importlib.resources
 import os
-import sys
 from typing import Any
 
+from cryptography import x509
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import serialization
-from OpenSSL import crypto
 
 import josepy.util
-from josepy import ComparableRSAKey, ComparableX509
+from josepy import ComparableRSAKey
 from josepy.util import ComparableECKey
 
-# This approach is based on the recommendation at
-# https://github.com/python/mypy/issues/1153#issuecomment-1207333806.
-if sys.version_info >= (3, 9):
-    import importlib.resources as importlib_resources
-else:
-    import importlib_resources
-
-TESTDATA = importlib_resources.files("testdata")
+TESTDATA = importlib.resources.files("testdata")
 
 
 def vector_path(*names: str) -> str:
@@ -32,7 +26,7 @@
     ref = TESTDATA.joinpath(*names)
     # We convert the value to str here because some of the calling code doesn't
     # work with pathlib objects.
-    return str(file_manager.enter_context(importlib_resources.as_file(ref)))
+    return str(file_manager.enter_context(importlib.resources.as_file(ref)))
 
 
 def load_vector(*names: str) -> bytes:
@@ -50,26 +44,18 @@
         raise ValueError("Loader could not be recognized based on extension")
 
 
-def load_cert(*names: str) -> crypto.X509:
+def load_cert(*names: str) -> x509.Certificate:
     """Load certificate."""
-    loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
-    return crypto.load_certificate(loader, load_vector(*names))
-
-
-def load_comparable_cert(*names: str) -> josepy.util.ComparableX509:
-    """Load ComparableX509 cert."""
-    return ComparableX509(load_cert(*names))
+    loader = _guess_loader(
+        names[-1], x509.load_pem_x509_certificate, x509.load_der_x509_certificate
+    )
+    return loader(load_vector(*names))
 
 
-def load_csr(*names: str) -> crypto.X509Req:
+def load_csr(*names: str) -> x509.CertificateSigningRequest:
     """Load certificate request."""
-    loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
-    return crypto.load_certificate_request(loader, load_vector(*names))
-
-
-def load_comparable_csr(*names: str) -> josepy.util.ComparableX509:
-    """Load ComparableX509 certificate request."""
-    return ComparableX509(load_csr(*names))
+    loader = _guess_loader(names[-1], x509.load_pem_x509_csr, x509.load_der_x509_csr)
+    return loader(load_vector(*names))
 
 
 def load_rsa_private_key(*names: str) -> josepy.util.ComparableRSAKey:
@@ -86,9 +72,3 @@
         names[-1], serialization.load_pem_private_key, serialization.load_der_private_key
     )
     return ComparableECKey(loader(load_vector(*names), password=None, backend=default_backend()))
-
-
-def load_pyopenssl_private_key(*names: str) -> crypto.PKey:
-    """Load pyOpenSSL private key."""
-    loader = _guess_loader(names[-1], crypto.FILETYPE_PEM, crypto.FILETYPE_ASN1)
-    return crypto.load_privatekey(loader, load_vector(*names))
diff -Nru python-josepy-1.14.0/tests/util_test.py python-josepy-2.0.0/tests/util_test.py
--- python-josepy-1.14.0/tests/util_test.py	2023-11-01 09:36:41.813426700 -0400
+++ python-josepy-2.0.0/tests/util_test.py	1969-12-31 19:00:00.000000000 -0500
@@ -1,4 +1,5 @@
 """Tests for josepy.util."""
+
 import functools
 import sys
 import unittest
@@ -7,46 +8,6 @@
 import test_util
 
 
-class ComparableX509Test(unittest.TestCase):
-    """Tests for josepy.util.ComparableX509."""
-
-    def setUp(self) -> None:
-        # test_util.load_comparable_{csr,cert} return ComparableX509
-        self.req1 = test_util.load_comparable_csr("csr.pem")
-        self.req2 = test_util.load_comparable_csr("csr.pem")
-        self.req_other = test_util.load_comparable_csr("csr-san.pem")
-
-        self.cert1 = test_util.load_comparable_cert("cert.pem")
-        self.cert2 = test_util.load_comparable_cert("cert.pem")
-        self.cert_other = test_util.load_comparable_cert("cert-san.pem")
-
-    def test_getattr_proxy(self) -> None:
-        assert self.cert1.has_expired() is True
-
-    def test_eq(self) -> None:
-        assert self.req1 == self.req2
-        assert self.cert1 == self.cert2
-
-    def test_ne(self) -> None:
-        assert self.req1 != self.req_other
-        assert self.cert1 != self.cert_other
-
-    def test_ne_wrong_types(self) -> None:
-        assert self.req1 != 5
-        assert self.cert1 != 5
-
-    def test_hash(self) -> None:
-        assert hash(self.req1) == hash(self.req2)
-        assert hash(self.req1) != hash(self.req_other)
-
-        assert hash(self.cert1) == hash(self.cert2)
-        assert hash(self.cert1) != hash(self.cert_other)
-
-    def test_repr(self) -> None:
-        for x509 in self.req1, self.cert1:
-            assert repr(x509) == "<ComparableX509({0!r})>".format(x509.wrapped)
-
-
 class ComparableRSAKeyTest(unittest.TestCase):
     """Tests for josepy.util.ComparableRSAKey."""
 

--- End Message ---
--- Begin Message ---
Unblocked.

--- End Message ---

Reply to: