diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/__init__.py | 10 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 12 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 18 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 52 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/utils.py | 2 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/x509.py | 68 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/asn1.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/pem.py | 4 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/pkcs7.py | 30 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/ssl.py | 6 | ||||
-rw-r--r-- | src/cryptography/hazmat/bindings/openssl/x509.py | 22 | ||||
-rw-r--r-- | src/cryptography/x509.py | 157 |
12 files changed, 356 insertions, 29 deletions
diff --git a/src/cryptography/__init__.py b/src/cryptography/__init__.py index 6da0b383..985ebd6f 100644 --- a/src/cryptography/__init__.py +++ b/src/cryptography/__init__.py @@ -4,6 +4,9 @@ from __future__ import absolute_import, division, print_function +import sys +import warnings + from cryptography.__about__ import ( __author__, __copyright__, __email__, __license__, __summary__, __title__, __uri__, __version__ @@ -14,3 +17,10 @@ __all__ = [ "__title__", "__summary__", "__uri__", "__version__", "__author__", "__email__", "__license__", "__copyright__", ] + +if sys.version_info[:2] == (2, 6): + warnings.warn( + "Python 2.6 is no longer supported by the Python core team, please " + "upgrade your Python.", + DeprecationWarning + ) diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index 79808909..5224f5c7 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -261,3 +261,15 @@ class X509Backend(object): """ Load an X.509 certificate from DER encoded data. """ + + @abc.abstractmethod + def load_der_x509_csr(self, data): + """ + Load an X.509 CSR from DER encoded data. + """ + + @abc.abstractmethod + def load_pem_x509_csr(self, data): + """ + Load an X.509 CSR from PEM encoded data. + """ diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 6e378c19..784ab84d 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -324,3 +324,21 @@ class MultiBackend(object): "This backend does not support X.509.", _Reasons.UNSUPPORTED_X509 ) + + def load_der_x509_csr(self, data): + for b in self._filtered_backends(X509Backend): + return b.load_der_x509_csr(data) + + raise UnsupportedAlgorithm( + "This backend does not support X.509.", + _Reasons.UNSUPPORTED_X509 + ) + + def load_pem_x509_csr(self, data): + for b in self._filtered_backends(X509Backend): + return b.load_pem_x509_csr(data) + + raise UnsupportedAlgorithm( + "This backend does not support X.509.", + _Reasons.UNSUPPORTED_X509 + ) diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 60aa4531..665771a8 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -34,7 +34,9 @@ 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.backends.openssl.x509 import ( + _Certificate, _CertificateSigningRequest +) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa @@ -475,20 +477,20 @@ class Backend(object): pointer. """ - type = evp_pkey.type + key_type = evp_pkey.type - if type == self._lib.EVP_PKEY_RSA: + if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) assert rsa_cdata != self._ffi.NULL rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPrivateKey(self, rsa_cdata) - elif type == self._lib.EVP_PKEY_DSA: + elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) assert dsa_cdata != self._ffi.NULL dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPrivateKey(self, dsa_cdata) elif (self._lib.Cryptography_HAS_EC == 1 and - type == self._lib.EVP_PKEY_EC): + key_type == self._lib.EVP_PKEY_EC): ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) assert ec_cdata != self._ffi.NULL ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) @@ -502,20 +504,20 @@ class Backend(object): pointer. """ - type = evp_pkey.type + key_type = evp_pkey.type - if type == self._lib.EVP_PKEY_RSA: + if key_type == self._lib.EVP_PKEY_RSA: rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) assert rsa_cdata != self._ffi.NULL rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) return _RSAPublicKey(self, rsa_cdata) - elif type == self._lib.EVP_PKEY_DSA: + elif key_type == self._lib.EVP_PKEY_DSA: dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) assert dsa_cdata != self._ffi.NULL dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) return _DSAPublicKey(self, dsa_cdata) elif (self._lib.Cryptography_HAS_EC == 1 and - type == self._lib.EVP_PKEY_EC): + key_type == self._lib.EVP_PKEY_EC): ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) assert ec_cdata != self._ffi.NULL ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) @@ -669,7 +671,7 @@ class Backend(object): def dsa_parameters_supported(self, p, q, g): if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f: - return (utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160) + return utils.bit_length(p) <= 1024 and utils.bit_length(q) <= 160 else: return True @@ -820,6 +822,28 @@ class Backend(object): x509 = self._ffi.gc(x509, self._lib.X509_free) return _Certificate(self, x509) + def load_pem_x509_csr(self, data): + mem_bio = self._bytes_to_bio(data) + x509_req = self._lib.PEM_read_bio_X509_REQ( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if x509_req == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load request") + + x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) + return _CertificateSigningRequest(self, x509_req) + + def load_der_x509_csr(self, data): + mem_bio = self._bytes_to_bio(data) + x509_req = self._lib.d2i_X509_REQ_bio(mem_bio.bio, self._ffi.NULL) + if x509_req == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to load request") + + x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) + return _CertificateSigningRequest(self, x509_req) + def _load_key(self, openssl_read_func, convert_func, data, password): mem_bio = self._bytes_to_bio(data) @@ -1205,13 +1229,13 @@ class Backend(object): assert res == 1 return self._read_mem_bio(bio) - def _private_key_bytes_traditional_der(self, type, cdata): - if type == self._lib.EVP_PKEY_RSA: + def _private_key_bytes_traditional_der(self, key_type, cdata): + if key_type == self._lib.EVP_PKEY_RSA: write_bio = self._lib.i2d_RSAPrivateKey_bio elif (self._lib.Cryptography_HAS_EC == 1 and - type == self._lib.EVP_PKEY_EC): + key_type == self._lib.EVP_PKEY_EC): write_bio = self._lib.i2d_ECPrivateKey_bio - elif type == self._lib.EVP_PKEY_DSA: + elif key_type == self._lib.EVP_PKEY_DSA: write_bio = self._lib.i2d_DSAPrivateKey_bio bio = self._create_mem_bio() diff --git a/src/cryptography/hazmat/backends/openssl/utils.py b/src/cryptography/hazmat/backends/openssl/utils.py index 498c100d..001121f9 100644 --- a/src/cryptography/hazmat/backends/openssl/utils.py +++ b/src/cryptography/hazmat/backends/openssl/utils.py @@ -16,7 +16,7 @@ def _truncate_digest(digest, order_bits): if 8 * digest_len > order_bits: rshift = 8 - (order_bits & 0x7) - assert rshift > 0 and rshift < 8 + assert 0 < rshift < 8 mask = 0xFF >> rshift << rshift diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py index 3502d122..6a7032ba 100644 --- a/src/cryptography/hazmat/backends/openssl/x509.py +++ b/src/cryptography/hazmat/backends/openssl/x509.py @@ -45,7 +45,7 @@ def _build_x509_name(backend, x509_name): assert res >= 0 assert buf[0] != backend._ffi.NULL buf = backend._ffi.gc( - buf, lambda buf: backend._lib.OPENSSL_free(buf[0]) + buf, lambda buffer: backend._lib.OPENSSL_free(buffer[0]) ) value = backend._ffi.buffer(buf[0], res)[:].decode('utf8') oid = _obj2txt(backend, obj) @@ -168,13 +168,16 @@ class _Certificate(object): raise x509.DuplicateExtension( "Duplicate {0} extension found".format(oid), oid ) - elif oid == x509.OID_BASIC_CONSTRAINTS and critical: + elif oid == x509.OID_BASIC_CONSTRAINTS: + value = self._build_basic_constraints(ext) + elif oid == x509.OID_KEY_USAGE and critical: # TODO: remove this obviously. warnings.warn( - "Extension support is not fully implemented. A basic " - "constraints extension with the critical flag was seen and" - " IGNORED." + "Extension support is not fully implemented. A key usage " + "extension with the critical flag was seen and IGNORED." ) + seen_oids.add(oid) + continue elif critical: raise x509.UnsupportedExtension( "{0} is not currently supported".format(oid), oid @@ -185,5 +188,60 @@ class _Certificate(object): continue seen_oids.add(oid) + extensions.append(x509.Extension(oid, critical, value)) return x509.Extensions(extensions) + + def _build_basic_constraints(self, ext): + bc_st = self._backend._lib.X509V3_EXT_d2i(ext) + assert bc_st != self._backend._ffi.NULL + basic_constraints = self._backend._ffi.cast( + "BASIC_CONSTRAINTS *", bc_st + ) + basic_constraints = self._backend._ffi.gc( + basic_constraints, self._backend._lib.BASIC_CONSTRAINTS_free + ) + # The byte representation of an ASN.1 boolean true is \xff. OpenSSL + # chooses to just map this to its ordinal value, so true is 255 and + # false is 0. + ca = basic_constraints.ca == 255 + if basic_constraints.pathlen == self._backend._ffi.NULL: + path_length = None + else: + bn = self._backend._lib.ASN1_INTEGER_to_BN( + basic_constraints.pathlen, self._backend._ffi.NULL + ) + assert bn != self._backend._ffi.NULL + bn = self._backend._ffi.gc(bn, self._backend._lib.BN_free) + path_length = self._backend._bn_to_int(bn) + + return x509.BasicConstraints(ca, path_length) + + +@utils.register_interface(x509.CertificateSigningRequest) +class _CertificateSigningRequest(object): + def __init__(self, backend, x509_req): + self._backend = backend + self._x509_req = x509_req + + def public_key(self): + pkey = self._backend._lib.X509_REQ_get_pubkey(self._x509_req) + assert pkey != self._backend._ffi.NULL + pkey = self._backend._ffi.gc(pkey, self._backend._lib.EVP_PKEY_free) + return self._backend._evp_pkey_to_public_key(pkey) + + @property + def subject(self): + subject = self._backend._lib.X509_REQ_get_subject_name(self._x509_req) + assert subject != self._backend._ffi.NULL + return _build_x509_name(self._backend, subject) + + @property + def signature_hash_algorithm(self): + oid = _obj2txt(self._backend, self._x509_req.sig_alg.algorithm) + try: + return x509._SIG_OIDS_TO_HASH[oid] + except KeyError: + raise UnsupportedAlgorithm( + "Signature algorithm OID:{0} not recognized".format(oid) + ) diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py index d8b8331e..45dfe758 100644 --- a/src/cryptography/hazmat/bindings/openssl/asn1.py +++ b/src/cryptography/hazmat/bindings/openssl/asn1.py @@ -40,6 +40,7 @@ struct asn1_string_st { typedef struct asn1_string_st ASN1_OCTET_STRING; typedef struct asn1_string_st ASN1_IA5STRING; +typedef ... ASN1_BIT_STRING; typedef ... ASN1_OBJECT; typedef ... ASN1_STRING; typedef ... ASN1_TYPE; @@ -115,9 +116,12 @@ int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long); ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long, const ASN1_ITEM *); +int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *, int, int); """ MACROS = """ +/* This is not a macro, but is const on some versions of OpenSSL */ +int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *, int); ASN1_TIME *M_ASN1_TIME_dup(void *); const ASN1_ITEM *ASN1_ITEM_ptr(ASN1_ITEM_EXP *); diff --git a/src/cryptography/hazmat/bindings/openssl/pem.py b/src/cryptography/hazmat/bindings/openssl/pem.py index 98c7648f..8ec3fefd 100644 --- a/src/cryptography/hazmat/bindings/openssl/pem.py +++ b/src/cryptography/hazmat/bindings/openssl/pem.py @@ -32,7 +32,9 @@ int i2d_PKCS8PrivateKey_bio(BIO *, EVP_PKEY *, const EVP_CIPHER *, int i2d_PKCS8PrivateKey_nid_bio(BIO *, EVP_PKEY *, int, char *, int, pem_password_cb *, void *); +int i2d_PKCS7_bio(BIO *, PKCS7 *); PKCS7 *d2i_PKCS7_bio(BIO *, PKCS7 **); + EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *, EVP_PKEY **, pem_password_cb *, void *); @@ -45,6 +47,8 @@ X509_CRL *PEM_read_bio_X509_CRL(BIO *, X509_CRL **, pem_password_cb *, void *); int PEM_write_bio_X509_CRL(BIO *, X509_CRL *); PKCS7 *PEM_read_bio_PKCS7(BIO *, PKCS7 **, pem_password_cb *, void *); +int PEM_write_bio_PKCS7(BIO *, PKCS7 *); + DH *PEM_read_bio_DHparams(BIO *, DH **, pem_password_cb *, void *); DSA *PEM_read_bio_DSAPrivateKey(BIO *, DSA **, pem_password_cb *, void *); diff --git a/src/cryptography/hazmat/bindings/openssl/pkcs7.py b/src/cryptography/hazmat/bindings/openssl/pkcs7.py index 3196fa13..df82afef 100644 --- a/src/cryptography/hazmat/bindings/openssl/pkcs7.py +++ b/src/cryptography/hazmat/bindings/openssl/pkcs7.py @@ -13,10 +13,37 @@ typedef struct { ASN1_OBJECT *type; ...; } PKCS7; + +static const int PKCS7_BINARY; +static const int PKCS7_DETACHED; +static const int PKCS7_NOATTR; +static const int PKCS7_NOCERTS; +static const int PKCS7_NOCHAIN; +static const int PKCS7_NOINTERN; +static const int PKCS7_NOSIGS; +static const int PKCS7_NOSMIMECAP; +static const int PKCS7_NOVERIFY; +static const int PKCS7_STREAM; +static const int PKCS7_TEXT; """ FUNCTIONS = """ +PKCS7 *SMIME_read_PKCS7(BIO *, BIO **); +int SMIME_write_PKCS7(BIO *, PKCS7 *, BIO *, int); + void PKCS7_free(PKCS7 *); + +PKCS7 *PKCS7_sign(X509 *, EVP_PKEY *, Cryptography_STACK_OF_X509 *, + BIO *, int); +int PKCS7_verify(PKCS7 *, Cryptography_STACK_OF_X509 *, X509_STORE *, BIO *, + BIO *, int); +Cryptography_STACK_OF_X509 *PKCS7_get0_signers(PKCS7 *, + Cryptography_STACK_OF_X509 *, + int); + +PKCS7 *PKCS7_encrypt(Cryptography_STACK_OF_X509 *, BIO *, + const EVP_CIPHER *, int); +int PKCS7_decrypt(PKCS7 *, EVP_PKEY *, X509 *, BIO *, int); """ MACROS = """ @@ -26,7 +53,6 @@ int PKCS7_type_is_signedAndEnveloped(PKCS7 *); int PKCS7_type_is_data(PKCS7 *); """ -CUSTOMIZATIONS = """ -""" +CUSTOMIZATIONS = "" CONDITIONAL_NAMES = {} diff --git a/src/cryptography/hazmat/bindings/openssl/ssl.py b/src/cryptography/hazmat/bindings/openssl/ssl.py index bc4b2e79..d680c3a5 100644 --- a/src/cryptography/hazmat/bindings/openssl/ssl.py +++ b/src/cryptography/hazmat/bindings/openssl/ssl.py @@ -546,11 +546,7 @@ static const long Cryptography_HAS_ALPN = 0; #else static const long Cryptography_HAS_ALPN = 1; #endif -/* LibreSSL has removed support for compression, and with it the - * COMP_METHOD use in ssl.h. This is a hack to make the function types - * in this code match those in ssl.h. - */ -#ifdef LIBRESSL_VERSION_NUMBER +#if defined(OPENSSL_NO_COMP) || defined(LIBRESSL_VERSION_NUMBER) static const long Cryptography_HAS_COMPRESSION = 0; typedef void COMP_METHOD; #else diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py index 949a936e..b5c9ee14 100644 --- a/src/cryptography/hazmat/bindings/openssl/x509.py +++ b/src/cryptography/hazmat/bindings/openssl/x509.py @@ -44,7 +44,10 @@ typedef struct { typedef ... X509_EXTENSIONS; -typedef ... X509_REQ; +typedef struct { + X509_ALGOR *sig_alg; + ...; +} X509_REQ; typedef struct { ASN1_INTEGER *serialNumber; @@ -165,6 +168,9 @@ void X509_REVOKED_free(X509_REVOKED *); int X509_REVOKED_set_serialNumber(X509_REVOKED *, ASN1_INTEGER *); +int X509_REVOKED_get_ext_count(X509_REVOKED *); +X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *, int); +int X509_REVOKED_add_ext(X509_REVOKED *, X509_EXTENSION*, int); int X509_REVOKED_add1_ext_i2d(X509_REVOKED *, int, void *, int, unsigned long); X509_CRL *d2i_X509_CRL_bio(BIO *, X509_CRL **); @@ -175,6 +181,9 @@ int i2d_X509_CRL_bio(BIO *, X509_CRL *); int X509_CRL_print(BIO *, X509_CRL *); int X509_CRL_set_issuer_name(X509_CRL *, X509_NAME *); int X509_CRL_sign(X509_CRL *, EVP_PKEY *, const EVP_MD *); +int X509_CRL_get_ext_count(X509_CRL *); +X509_EXTENSION *X509_CRL_get_ext(X509_CRL *, int); +int X509_CRL_add_ext(X509_CRL *, X509_EXTENSION *, int); int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *); int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *); @@ -259,11 +268,22 @@ void sk_X509_EXTENSION_free(X509_EXTENSIONS *); int sk_X509_REVOKED_num(Cryptography_STACK_OF_X509_REVOKED *); X509_REVOKED *sk_X509_REVOKED_value(Cryptography_STACK_OF_X509_REVOKED *, int); +Cryptography_STACK_OF_X509_CRL *sk_X509_CRL_new_null(void); +void sk_X509_CRL_free(Cryptography_STACK_OF_X509_CRL *); +int sk_X509_CRL_num(Cryptography_STACK_OF_X509_CRL *); +int sk_X509_CRL_push(Cryptography_STACK_OF_X509_CRL *, X509_CRL *); +X509_CRL *sk_X509_CRL_value(Cryptography_STACK_OF_X509_CRL *, int); + int i2d_RSAPublicKey(RSA *, unsigned char **); int i2d_RSAPrivateKey(RSA *, unsigned char **); int i2d_DSAPublicKey(DSA *, unsigned char **); int i2d_DSAPrivateKey(DSA *, unsigned char **); +int X509_CRL_get_version(X509_CRL *); +ASN1_TIME *X509_CRL_get_lastUpdate(X509_CRL *); +ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *); +X509_NAME *X509_CRL_get_issuer(X509_CRL *); + /* These aren't macros these arguments are all const X on openssl > 1.0.x */ int X509_CRL_set_lastUpdate(X509_CRL *, ASN1_TIME *); int X509_CRL_set_nextUpdate(X509_CRL *, ASN1_TIME *); diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py index 29602b3e..697d7d6e 100644 --- a/src/cryptography/x509.py +++ b/src/cryptography/x509.py @@ -42,8 +42,30 @@ _OID_NAMES = { "1.2.840.10040.4.3": "dsa-with-sha1", "2.16.840.1.101.3.4.3.1": "dsa-with-sha224", "2.16.840.1.101.3.4.3.2": "dsa-with-sha256", - "2.5.29.19": "basicConstraints", + "1.3.6.1.5.5.7.3.1": "serverAuth", + "1.3.6.1.5.5.7.3.2": "clientAuth", + "1.3.6.1.5.5.7.3.3": "codeSigning", + "1.3.6.1.5.5.7.3.4": "emailProtection", + "1.3.6.1.5.5.7.3.8": "timeStamping", + "1.3.6.1.5.5.7.3.9": "OCSPSigning", + "2.5.29.9": "subjectDirectoryAttributes", + "2.5.29.14": "subjectKeyIdentifier", "2.5.29.15": "keyUsage", + "2.5.29.17": "subjectAltName", + "2.5.29.18": "issuerAltName", + "2.5.29.19": "basicConstraints", + "2.5.29.30": "nameConstraints", + "2.5.29.31": "cRLDistributionPoints", + "2.5.29.32": "certificatePolicies", + "2.5.29.33": "policyMappings", + "2.5.29.35": "authorityKeyIdentifier", + "2.5.29.36": "policyConstraints", + "2.5.29.37": "extendedKeyUsage", + "2.5.29.46": "freshestCRL", + "2.5.29.54": "inhibitAnyPolicy", + "1.3.6.1.5.5.7.1.1": "authorityInfoAccess", + "1.3.6.1.5.5.7.1.11": "subjectInfoAccess", + "1.3.6.1.5.5.7.48.1.5": "OCSPNoCheck", } @@ -60,6 +82,14 @@ def load_der_x509_certificate(data, backend): return backend.load_der_x509_certificate(data) +def load_pem_x509_csr(data, backend): + return backend.load_pem_x509_csr(data) + + +def load_der_x509_csr(data, backend): + return backend.load_der_x509_csr(data) + + class InvalidVersion(Exception): def __init__(self, msg, parsed_version): super(InvalidVersion, self).__init__(msg) @@ -78,6 +108,12 @@ class UnsupportedExtension(Exception): self.oid = oid +class ExtensionNotFound(Exception): + def __init__(self, msg, oid): + super(ExtensionNotFound, self).__init__(msg) + self.oid = oid + + class NameAttribute(object): def __init__(self, oid, value): if not isinstance(oid, ObjectIdentifier): @@ -155,14 +191,37 @@ class Name(object): return len(self._attributes) +OID_SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") +OID_SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") OID_KEY_USAGE = ObjectIdentifier("2.5.29.15") +OID_SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") +OID_ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") OID_BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") +OID_NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") +OID_CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") +OID_CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") +OID_POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") +OID_AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") +OID_POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") +OID_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") +OID_FRESHEST_CRL = ObjectIdentifier("2.5.29.46") +OID_INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") +OID_AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") +OID_SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") +OID_OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") class Extensions(object): def __init__(self, extensions): self._extensions = extensions + def get_extension_for_oid(self, oid): + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound("No {0} extension was found".format(oid), oid) + def __iter__(self): return iter(self._extensions) @@ -193,6 +252,26 @@ class Extension(object): "value={0.value})>").format(self) +class ExtendedKeyUsage(object): + def __init__(self, usages): + for oid in usages: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) + + self._usages = usages + + def __iter__(self): + return iter(self._usages) + + def __len__(self): + return len(self._usages) + + def __repr__(self): + return "<ExtendedKeyUsage({0})>".format(self._usages) + + class BasicConstraints(object): def __init__(self, ca, path_length): if not isinstance(ca, bool): @@ -220,6 +299,53 @@ class BasicConstraints(object): "path_length={0.path_length})>").format(self) +class KeyUsage(object): + def __init__(self, digital_signature, content_commitment, key_encipherment, + data_encipherment, key_agreement, key_cert_sign, crl_sign, + encipher_only, decipher_only): + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + digital_signature = utils.read_only_property("_digital_signature") + content_commitment = utils.read_only_property("_content_commitment") + key_encipherment = utils.read_only_property("_key_encipherment") + data_encipherment = utils.read_only_property("_data_encipherment") + key_agreement = utils.read_only_property("_key_agreement") + key_cert_sign = utils.read_only_property("_key_cert_sign") + crl_sign = utils.read_only_property("_crl_sign") + + @property + def encipher_only(self): + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self): + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + OID_COMMON_NAME = ObjectIdentifier("2.5.4.3") OID_COUNTRY_NAME = ObjectIdentifier("2.5.4.6") OID_LOCALITY_NAME = ObjectIdentifier("2.5.4.7") @@ -266,6 +392,13 @@ _SIG_OIDS_TO_HASH = { OID_DSA_WITH_SHA256.dotted_string: hashes.SHA256() } +OID_SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") +OID_CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") +OID_CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") +OID_EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") +OID_TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") +OID_OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") + @six.add_metaclass(abc.ABCMeta) class Certificate(object): @@ -323,3 +456,25 @@ class Certificate(object): Returns a HashAlgorithm corresponding to the type of the digest signed in the certificate. """ + + +@six.add_metaclass(abc.ABCMeta) +class CertificateSigningRequest(object): + @abc.abstractmethod + def public_key(self): + """ + Returns the public key + """ + + @abc.abstractproperty + def subject(self): + """ + Returns the subject name object. + """ + + @abc.abstractproperty + def signature_hash_algorithm(self): + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ |