diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 48 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 112 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/asn1.py | 1 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/interfaces.py | 4 | ||||
-rw-r--r-- | src/cryptography/x509.py | 68 |
5 files changed, 230 insertions, 3 deletions
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index ebe38e20..daccf5ca 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -19,7 +19,7 @@ from cryptography.hazmat.backends.interfaces import ( CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, PEMSerializationBackend, PKCS8SerializationBackend, RSABackend, - TraditionalOpenSSLSerializationBackend + TraditionalOpenSSLSerializationBackend, X509Backend ) from cryptography.hazmat.backends.openssl.ciphers import ( _AESCTRCipherContext, _CipherContext @@ -36,6 +36,7 @@ from cryptography.hazmat.backends.openssl.hmac import _HMACContext from cryptography.hazmat.backends.openssl.rsa import ( _RSAPrivateKey, _RSAPublicKey ) +from cryptography.hazmat.backends.openssl.x509 import _Certificate from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa @@ -66,6 +67,7 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", @utils.register_interface(RSABackend) @utils.register_interface(TraditionalOpenSSLSerializationBackend) @utils.register_interface(PEMSerializationBackend) +@utils.register_interface(X509Backend) class Backend(object): """ OpenSSL API binding interfaces. @@ -445,6 +447,28 @@ class Backend(object): return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_char_p) + def _create_mem_bio(self): + """ + Creates an empty memory BIO. + """ + bio_method = self._lib.BIO_s_mem() + assert bio_method != self._ffi.NULL + bio = self._lib.BIO_new(bio_method) + assert bio != self._ffi.NULL + bio = self._ffi.gc(bio, self._lib.BIO_free) + return bio + + def _read_mem_bio(self, bio): + """ + Reads a memory BIO. This only works on memory BIOs. + """ + buf = self._ffi.new("char **") + buf_len = self._lib.BIO_get_mem_data(bio, buf) + assert buf_len > 0 + assert buf[0] != self._ffi.NULL + bio_data = self._ffi.buffer(buf[0], buf_len)[:] + return bio_data + def _evp_pkey_to_private_key(self, evp_pkey): """ Return the appropriate type of PrivateKey given an evp_pkey cdata @@ -675,6 +699,28 @@ class Backend(object): None, ) + def load_pem_x509_certificate(self, data): + mem_bio = self._bytes_to_bio(data) + x509 = self._lib.PEM_read_bio_X509( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if x509 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load certificate") + + x509 = self._ffi.gc(x509, self._lib.X509_free) + return _Certificate(self, x509) + + def load_der_x509_certificate(self, data): + mem_bio = self._bytes_to_bio(data) + x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL) + if x509 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load certificate") + + x509 = self._ffi.gc(x509, self._lib.X509_free) + return _Certificate(self, x509) + def load_traditional_openssl_pem_private_key(self, data, password): warnings.warn( "load_traditional_openssl_pem_private_key is deprecated and will " diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py new file mode 100644 index 00000000..0828f3cc --- /dev/null +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -0,0 +1,112 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import, division, print_function + +import datetime + +from cryptography import utils, x509 +from cryptography.hazmat.primitives import hashes + + +@utils.register_interface(x509.Certificate) +class _Certificate(object): + def __init__(self, backend, x509): + self._backend = backend + self._x509 = x509 + + def fingerprint(self, algorithm): + h = hashes.Hash(algorithm, self._backend) + bio = self._backend._create_mem_bio() + res = self._backend._lib.i2d_X509_bio( + bio, self._x509 + ) + assert res == 1 + der = self._backend._read_mem_bio(bio) + h.update(der) + return h.finalize() + + @property + def version(self): + version = self._backend._lib.X509_get_version(self._x509) + if version == 0: + return x509.Version.v1 + elif version == 2: + return x509.Version.v3 + else: + raise x509.InvalidVersion( + "{0} is not a valid X509 version".format(version), version + ) + + @property + def serial(self): + asn1_int = self._backend._lib.X509_get_serialNumber(self._x509) + assert asn1_int != self._backend._ffi.NULL + bn = self._backend._lib.ASN1_INTEGER_to_BN( + asn1_int, self._backend._ffi.NULL + ) + assert bn != self._backend._ffi.NULL + bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free) + return self._backend._bn_to_int(bn) + + def public_key(self): + pkey = self._backend._lib.X509_get_pubkey(self._x509) + assert pkey != self._backend._ffi.NULL + pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free) + # The following check is to find ECDSA certificates with unnamed + # curves and raise an error for now. + if ( + self._backend._lib.Cryptography_HAS_EC == 1 and + pkey.type == self._backend._lib.EVP_PKEY_EC + ): + ec_cdata = self._backend._lib.EVP_PKEY_get1_EC_KEY(pkey) + assert ec_cdata != self._backend._ffi.NULL + ec_cdata = self._backend._ffi.gc( + ec_cdata, self._backend._lib.EC_KEY_free + ) + group = self._backend._lib.EC_KEY_get0_group(ec_cdata) + assert group != self._backend._ffi.NULL + nid = self._backend._lib.EC_GROUP_get_curve_name(group) + if nid == self._backend._lib.NID_undef: + raise NotImplementedError( + "ECDSA certificates with unnamed curves are unsupported " + "at this time" + ) + + return self._backend._evp_pkey_to_public_key(pkey) + + @property + def not_valid_before(self): + asn1_time = self._backend._lib.X509_get_notBefore(self._x509) + return self._parse_asn1_time(asn1_time) + + @property + def not_valid_after(self): + asn1_time = self._backend._lib.X509_get_notAfter(self._x509) + return self._parse_asn1_time(asn1_time) + + def _parse_asn1_time(self, asn1_time): + assert asn1_time != self._backend._ffi.NULL + generalized_time = self._backend._lib.ASN1_TIME_to_generalizedtime( + asn1_time, self._backend._ffi.NULL + ) + assert generalized_time != self._backend._ffi.NULL + generalized_time = self._backend._ffi.gc( + generalized_time, self._backend._lib.ASN1_GENERALIZEDTIME_free + ) + time = self._backend._ffi.string( + self._backend._lib.ASN1_STRING_data( + self._backend._ffi.cast("ASN1_STRING *", generalized_time) + ) + ).decode("ascii") + return datetime.datetime.strptime(time, "%Y%m%d%H%M%SZ") diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py index e3631237..d8b8331e 100644 --- a/src/cryptography/hazmat/bindings/openssl/asn1.py +++ b/src/cryptography/hazmat/bindings/openssl/asn1.py @@ -123,6 +123,7 @@ const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *); /* These aren't macros these arguments are all const X on openssl > 1.0.x */ +int ASN1_TIME_print(BIO *, ASN1_TIME *); int ASN1_STRING_length(ASN1_STRING *); ASN1_STRING *ASN1_STRING_dup(ASN1_STRING *); int ASN1_STRING_cmp(ASN1_STRING *, ASN1_STRING *); diff --git a/src/cryptography/hazmat/primitives/interfaces.py b/src/cryptography/hazmat/primitives/interfaces.py index 18a62601..76616e1f 100644 --- a/src/cryptography/hazmat/primitives/interfaces.py +++ b/src/cryptography/hazmat/primitives/interfaces.py @@ -517,13 +517,13 @@ class X509Certificate(object): """ @abc.abstractproperty - def not_before(self): + def not_valid_before(self): """ Not before time (represented as UTC datetime) """ @abc.abstractproperty - def not_after(self): + def not_valid_after(self): """ Not after time (represented as UTC datetime) """ diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py new file mode 100644 index 00000000..be1298b6 --- /dev/null +++ b/src/cryptography/x509.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import, division, print_function + +import abc +from enum import Enum + +import six + + +class Version(Enum): + v1 = 0 + v3 = 2 + + +def load_pem_x509_certificate(data, backend): + return backend.load_pem_x509_certificate(data) + + +def load_der_x509_certificate(data, backend): + return backend.load_der_x509_certificate(data) + + +class InvalidVersion(Exception): + def __init__(self, msg, parsed_version): + super(InvalidVersion, self).__init__(msg) + self.parsed_version = parsed_version + + +@six.add_metaclass(abc.ABCMeta) +class Certificate(object): + @abc.abstractmethod + def fingerprint(self, algorithm): + """ + Returns bytes using digest passed. + """ + + @abc.abstractproperty + def serial(self): + """ + Returns certificate serial number + """ + + @abc.abstractproperty + def version(self): + """ + Returns the certificate version + """ + + @abc.abstractmethod + def public_key(self): + """ + Returns the public key + """ + + @abc.abstractproperty + def not_valid_before(self): + """ + Not before time (represented as UTC datetime) + """ + + @abc.abstractproperty + def not_valid_after(self): + """ + Not after time (represented as UTC datetime) + """ |