Package: release.debian.org
Severity: normal
X-Debbugs-Cc: python-jos...@packages.debian.org, hlieber...@debian.org
Control: affects -1 + src:python-josepy
User: release.debian....@packages.debian.org
Usertags: unblock
User: hlieber...@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 <hlieber...@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 <hlieber...@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-...@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 <hlieber...@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 <hlieber...@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 <hlieber...@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-...@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."""
 

Reply via email to