Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate usage of ComparableX509 in encoding and decoding functions #204

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/josepy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
TypedJSONObjectWithFields,
decode_b64jose,
decode_cert,
decode_cert_cryptography,
decode_csr,
decode_csr_cryptography,
decode_hex16,
encode_b64jose,
encode_cert,
Expand Down
89 changes: 74 additions & 15 deletions src/josepy/json_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import abc
import binascii
import logging
import warnings
from typing import (
Any,
Callable,
Expand All @@ -20,8 +21,11 @@
Optional,
Type,
TypeVar,
Union,
)

from cryptography import x509
from cryptography.hazmat.primitives.serialization import Encoding
from OpenSSL import crypto

from josepy import b64, errors, interfaces, util
Expand Down Expand Up @@ -426,26 +430,41 @@ def decode_hex16(value: str, size: Optional[int] = None, minimum: bool = False)
raise errors.DeserializationError(error)


def encode_cert(cert: util.ComparableX509) -> str:
def encode_cert(cert: Union[util.ComparableX509, x509.Certificate]) -> str:
"""Encode certificate as JOSE Base-64 DER.

:type cert: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
:type cert: `cryptography.x509.Certificate`
or `OpenSSL.crypto.X509` wrapped in `.ComparableX509`
:rtype: unicode

"""
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))
if isinstance(cert, util.ComparableX509):
# DEPRECATED; remove this in major release
warnings.warn(
"`josepy.json_util.encode_cert` has deprecated support for accepting "
"util.ComparableX509 objects, and support will be dropped in the next major release. "
"Please use `cryptography.x509.Certificate` objects instead.",
DeprecationWarning,
)
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))
assert isinstance(cert, x509.Certificate)
return encode_b64jose(cert.public_bytes(Encoding.DER))


def decode_cert(b64der: str) -> util.ComparableX509:
"""Decode JOSE Base-64 DER-encoded certificate.
"""Decode JOSE Base-64 DER-encoded certificate. Deprecated as of 1.15.0 and will be
removed in 2.0.0.

:param unicode b64der:
:rtype: `OpenSSL.crypto.X509` wrapped in `.ComparableX509`

"""
warnings.warn(
"`josepy.json_util.decode_cert` is deprecated, and will be removed in the next major "
"release. Please use `josepy.json_util.decode_cert_cryptography instead.",
DeprecationWarning,
)
try:
return util.ComparableX509(
crypto.load_certificate(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
Expand All @@ -454,26 +473,53 @@ def decode_cert(b64der: str) -> util.ComparableX509:
raise errors.DeserializationError(error)


def encode_csr(csr: util.ComparableX509) -> str:
def decode_cert_cryptography(b64der: str) -> x509.Certificate:
"""Decode JOSE Base-64 DER-encoded certificate.

:param unicode b64der:
:rtype: `cryptography.x509.Certificate`
"""
try:
return x509.load_der_x509_certificate(decode_b64jose(b64der))
except Exception as error:
raise errors.DeserializationError(error)


def encode_csr(csr: Union[util.ComparableX509, x509.CertificateSigningRequest]) -> str:
"""Encode CSR as JOSE Base-64 DER.

:type csr: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
:type csr: `cryptography.x509.CertificateSigningRequest`
or `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`
:rtype: unicode

"""
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))
if isinstance(csr, util.ComparableX509):
# DEPRECATED; remove this in major release
warnings.warn(
"`josepy.json_util.encode_csr` has deprecated support for accepting "
"util.ComparableX509 objects, and support will be dropped in the next major release. "
"Please use `cryptography.x509.CertificateSigningRequest` objects instead.",
DeprecationWarning,
)
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))
assert isinstance(csr, x509.CertificateSigningRequest)
return encode_b64jose(csr.public_bytes(Encoding.DER))


def decode_csr(b64der: str) -> util.ComparableX509:
"""Decode JOSE Base-64 DER-encoded CSR.
"""Decode JOSE Base-64 DER-encoded CSR. Deprecated as of 1.15.0 and will be removed in 2.0.0.

:param unicode b64der:
:rtype: `OpenSSL.crypto.X509Req` wrapped in `.ComparableX509`

"""
warnings.warn(
"`josepy.json_util.decode_csr` is deprecated, and will be removed in the next major "
"release. Please use `josepy.json_util.decode_csr_cryptography instead.",
DeprecationWarning,
)
try:
return util.ComparableX509(
crypto.load_certificate_request(crypto.FILETYPE_ASN1, decode_b64jose(b64der))
Expand All @@ -482,6 +528,19 @@ def decode_csr(b64der: str) -> util.ComparableX509:
raise errors.DeserializationError(error)


def decode_csr_cryptography(b64der: str) -> x509.CertificateSigningRequest:
"""Decode JOSE Base-64 DER-encoded CSR.

:param unicode b64der:
:rtype: `cryptography.x509.CertificateSigningRequest`

"""
try:
return x509.load_der_x509_csr(decode_b64jose(b64der))
except Exception as error:
raise errors.DeserializationError(error)


GenericTypedJSONObjectWithFields = TypeVar(
"GenericTypedJSONObjectWithFields", bound="TypedJSONObjectWithFields"
)
Expand Down
57 changes: 46 additions & 11 deletions tests/json_util_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
import itertools
import sys
import unittest
import warnings
from typing import Any, Dict, Mapping
from unittest import mock

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_CRYPTOGRAPHY = test_util.load_cert_cryptography("cert.pem")
CSR_CRYPTOGRAPHY = test_util.load_csr_cryptography("csr.pem")


class FieldTest(unittest.TestCase):
Expand Down Expand Up @@ -321,30 +325,61 @@ def test_decode_hex16_odd_length(self) -> None:
def test_encode_cert(self) -> None:
from josepy.json_util import encode_cert

assert self.b64_cert == encode_cert(CERT)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
assert self.b64_cert == encode_cert(CERT)

assert self.b64_cert == encode_cert(CERT_CRYPTOGRAPHY)

def test_decode_cert(self) -> None:
from josepy.json_util import decode_cert
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
from josepy.json_util import decode_cert

cert = decode_cert(self.b64_cert)
assert isinstance(cert, util.ComparableX509)
assert cert == CERT
with pytest.raises(errors.DeserializationError):
decode_cert("")

cert = decode_cert(self.b64_cert)
assert isinstance(cert, util.ComparableX509)
assert cert == CERT
def test_decode_cert_cryptography(self) -> None:
from josepy.json_util import decode_cert_cryptography

cert = decode_cert_cryptography(self.b64_cert)
assert isinstance(cert, x509.Certificate)
assert cert == CERT_CRYPTOGRAPHY
with pytest.raises(errors.DeserializationError):
decode_cert("")
decode_cert_cryptography("")

def test_encode_csr(self) -> None:
from josepy.json_util import encode_csr

assert self.b64_csr == encode_csr(CSR)
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
assert self.b64_csr == encode_csr(CSR)

assert self.b64_csr == encode_csr(CSR_CRYPTOGRAPHY)

def test_decode_csr(self) -> None:
from josepy.json_util import decode_csr

csr = decode_csr(self.b64_csr)
assert isinstance(csr, util.ComparableX509)
assert csr == CSR
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)

csr = decode_csr(self.b64_csr)
assert isinstance(csr, util.ComparableX509)
assert csr == CSR
with pytest.raises(errors.DeserializationError):
decode_csr("")

def test_decode_csr_cryptography(self) -> None:
from josepy.json_util import decode_csr_cryptography

csr = decode_csr_cryptography(self.b64_csr)
assert isinstance(csr, x509.CertificateSigningRequest)
assert csr == CSR_CRYPTOGRAPHY
with pytest.raises(errors.DeserializationError):
decode_csr("")
decode_csr_cryptography("")


class TypedJSONObjectWithFieldsTest(unittest.TestCase):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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
Expand Down Expand Up @@ -57,6 +58,14 @@ def load_cert(*names: str) -> crypto.X509:
return crypto.load_certificate(loader, load_vector(*names))


def load_cert_cryptography(*names: str) -> x509.Certificate:
"""Load certificate using cryptography API."""
loader = _guess_loader(
names[-1], x509.load_pem_x509_certificate, x509.load_der_x509_certificate
)
return loader(load_vector(*names))


def load_comparable_cert(*names: str) -> josepy.util.ComparableX509:
"""Load ComparableX509 cert."""
return ComparableX509(load_cert(*names))
Expand All @@ -68,6 +77,12 @@ def load_csr(*names: str) -> crypto.X509Req:
return crypto.load_certificate_request(loader, load_vector(*names))


def load_csr_cryptography(*names: str) -> x509.CertificateSigningRequest:
"""Load certificate request."""
loader = _guess_loader(names[-1], x509.load_pem_x509_csr, x509.load_der_x509_csr)
return loader(load_vector(*names))


def load_comparable_csr(*names: str) -> josepy.util.ComparableX509:
"""Load ComparableX509 certificate request."""
return ComparableX509(load_csr(*names))
Expand Down
Loading