aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-12-17 13:55:49 -0800
committerAlex Gaynor <alex.gaynor@gmail.com>2014-12-17 13:55:49 -0800
commit4d8de138910628db04a1c861303e744e7f10729a (patch)
tree3d7fec1de040cb7bb168150d8778262253f3e3fb /src
parentd1746da04faa07ebc597f721afbe4980593564eb (diff)
parentbbffc4067c1278a5d96f73bb89255becd43da29e (diff)
downloadcryptography-4d8de138910628db04a1c861303e744e7f10729a.tar.gz
cryptography-4d8de138910628db04a1c861303e744e7f10729a.tar.bz2
cryptography-4d8de138910628db04a1c861303e744e7f10729a.zip
Merge pull request #1499 from reaperhulk/x509-ossl-impl
X509Backend support in OpenSSL backend
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py48
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py112
-rw-r--r--src/cryptography/hazmat/bindings/openssl/asn1.py1
-rw-r--r--src/cryptography/hazmat/primitives/interfaces.py4
-rw-r--r--src/cryptography/x509.py68
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)
+ """