diff --git a/src/josepy/json_util.py b/src/josepy/json_util.py index ed4e4811..733ca0fc 100644 --- a/src/josepy/json_util.py +++ b/src/josepy/json_util.py @@ -10,6 +10,7 @@ import abc import binascii import logging +import warnings from typing import ( Any, Callable, @@ -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 @@ -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)) @@ -454,10 +473,23 @@ 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.X509` wrapped in `.ComparableX509` :rtype: unicode """ @@ -466,14 +498,33 @@ def encode_csr(csr: util.ComparableX509) -> str: 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_new, x509.Certificate): + 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)) @@ -482,6 +533,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" )