aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/__init__.py10
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py12
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py18
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py52
-rw-r--r--src/cryptography/hazmat/backends/openssl/utils.py2
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py68
-rw-r--r--src/cryptography/hazmat/bindings/openssl/asn1.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pem.py4
-rw-r--r--src/cryptography/hazmat/bindings/openssl/pkcs7.py30
-rw-r--r--src/cryptography/hazmat/bindings/openssl/ssl.py6
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py22
-rw-r--r--src/cryptography/x509.py157
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.
+ """