aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_cffi_src/openssl/x509v3.py6
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py6
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py9
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py161
-rw-r--r--src/cryptography/hazmat/backends/openssl/rsa.py5
-rw-r--r--src/cryptography/hazmat/backends/openssl/x509.py154
-rw-r--r--src/cryptography/hazmat/primitives/serialization.py4
-rw-r--r--src/cryptography/x509.py155
8 files changed, 392 insertions, 108 deletions
diff --git a/src/_cffi_src/openssl/x509v3.py b/src/_cffi_src/openssl/x509v3.py
index 89822b85..f6a18903 100644
--- a/src/_cffi_src/openssl/x509v3.py
+++ b/src/_cffi_src/openssl/x509v3.py
@@ -203,6 +203,9 @@ int i2d_GENERAL_NAMES(GENERAL_NAMES *, unsigned char **);
int i2d_EXTENDED_KEY_USAGE(EXTENDED_KEY_USAGE *, unsigned char **);
+int i2d_AUTHORITY_INFO_ACCESS(Cryptography_STACK_OF_ACCESS_DESCRIPTION *,
+ unsigned char **);
+
int sk_GENERAL_NAME_num(struct stack_st_GENERAL_NAME *);
int sk_GENERAL_NAME_push(struct stack_st_GENERAL_NAME *, GENERAL_NAME *);
GENERAL_NAME *sk_GENERAL_NAME_value(struct stack_st_GENERAL_NAME *, int);
@@ -216,6 +219,9 @@ void sk_ACCESS_DESCRIPTION_free(Cryptography_STACK_OF_ACCESS_DESCRIPTION *);
int sk_ACCESS_DESCRIPTION_push(Cryptography_STACK_OF_ACCESS_DESCRIPTION *,
ACCESS_DESCRIPTION *);
+ACCESS_DESCRIPTION *ACCESS_DESCRIPTION_new(void);
+void ACCESS_DESCRIPTION_free(ACCESS_DESCRIPTION *);
+
X509_EXTENSION *X509V3_EXT_conf_nid(Cryptography_LHASH_OF_CONF_VALUE *,
X509V3_CTX *, int, char *);
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index 4d378e6b..49ccda18 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -280,6 +280,12 @@ class X509Backend(object):
Create and sign an X.509 CSR from a CSR builder object.
"""
+ @abc.abstractmethod
+ def sign_x509_certificate(self, builder, private_key, algorithm):
+ """
+ Sign an X.509 Certificate from a CertificateBuilder object.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class DHBackend(object):
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index 6e911fd5..8008989e 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -351,3 +351,12 @@ class MultiBackend(object):
"This backend does not support X.509.",
_Reasons.UNSUPPORTED_X509
)
+
+ def sign_x509_certificate(self, builder, private_key, algorithm):
+ for b in self._filtered_backends(X509Backend):
+ return b.sign_x509_certificate(builder, private_key, algorithm)
+
+ 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 db4f963a..f9da9ea7 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -4,6 +4,7 @@
from __future__ import absolute_import, division, print_function
+import calendar
import collections
import itertools
from contextlib import contextmanager
@@ -78,6 +79,12 @@ def _encode_asn1_int(backend, x):
return i
+def _encode_asn1_int_gc(backend, x):
+ i = _encode_asn1_int(backend, x)
+ i = backend._ffi.gc(i, backend._lib.ASN1_INTEGER_free)
+ return i
+
+
def _encode_asn1_str(backend, data, length):
"""
Create an ASN1_OCTET_STRING from a Python byte string.
@@ -195,6 +202,32 @@ def _encode_basic_constraints(backend, basic_constraints):
return pp, r
+def _encode_authority_information_access(backend, authority_info_access):
+ aia = backend._lib.sk_ACCESS_DESCRIPTION_new_null()
+ assert aia != backend._ffi.NULL
+ aia = backend._ffi.gc(
+ aia, backend._lib.sk_ACCESS_DESCRIPTION_free
+ )
+ for access_description in authority_info_access:
+ ad = backend._lib.ACCESS_DESCRIPTION_new()
+ method = _txt2obj(
+ backend, access_description.access_method.dotted_string
+ )
+ gn = _encode_general_name(backend, access_description.access_location)
+ ad.method = method
+ ad.location = gn
+ res = backend._lib.sk_ACCESS_DESCRIPTION_push(aia, ad)
+ assert res >= 1
+
+ pp = backend._ffi.new('unsigned char **')
+ r = backend._lib.i2d_AUTHORITY_INFO_ACCESS(aia, pp)
+ assert r > 0
+ pp = backend._ffi.gc(
+ pp, lambda pointer: backend._lib.OPENSSL_free(pointer[0])
+ )
+ return pp, r
+
+
def _encode_subject_alt_name(backend, san):
general_names = backend._lib.GENERAL_NAMES_new()
assert general_names != backend._ffi.NULL
@@ -1063,6 +1096,114 @@ class Backend(object):
return _CertificateSigningRequest(self, x509_req)
+ def sign_x509_certificate(self, builder, private_key, algorithm):
+ if not isinstance(builder, x509.CertificateBuilder):
+ raise TypeError('Builder type mismatch.')
+ if not isinstance(algorithm, hashes.HashAlgorithm):
+ raise TypeError('Algorithm must be a registered hash algorithm.')
+
+ if self._lib.OPENSSL_VERSION_NUMBER <= 0x10001000:
+ if isinstance(private_key, _DSAPrivateKey):
+ raise NotImplementedError(
+ "Certificate signatures aren't implemented for DSA"
+ " keys on OpenSSL versions less than 1.0.1."
+ )
+ if isinstance(private_key, _EllipticCurvePrivateKey):
+ raise NotImplementedError(
+ "Certificate signatures aren't implemented for EC"
+ " keys on OpenSSL versions less than 1.0.1."
+ )
+
+ # Resolve the signature algorithm.
+ evp_md = self._lib.EVP_get_digestbyname(
+ algorithm.name.encode('ascii')
+ )
+ assert evp_md != self._ffi.NULL
+
+ # Create an empty certificate.
+ x509_cert = self._lib.X509_new()
+ x509_cert = self._ffi.gc(x509_cert, backend._lib.X509_free)
+
+ # Set the x509 version.
+ res = self._lib.X509_set_version(x509_cert, builder._version.value)
+ assert res == 1
+
+ # Set the subject's name.
+ res = self._lib.X509_set_subject_name(
+ x509_cert, _encode_name(self, list(builder._subject_name))
+ )
+ assert res == 1
+
+ # Set the subject's public key.
+ res = self._lib.X509_set_pubkey(
+ x509_cert, builder._public_key._evp_pkey
+ )
+ assert res == 1
+
+ # Set the certificate serial number.
+ serial_number = _encode_asn1_int_gc(self, builder._serial_number)
+ res = self._lib.X509_set_serialNumber(x509_cert, serial_number)
+ assert res == 1
+
+ # Set the "not before" time.
+ res = self._lib.ASN1_TIME_set(
+ self._lib.X509_get_notBefore(x509_cert),
+ calendar.timegm(builder._not_valid_before.timetuple())
+ )
+ assert res != self._ffi.NULL
+
+ # Set the "not after" time.
+ res = self._lib.ASN1_TIME_set(
+ self._lib.X509_get_notAfter(x509_cert),
+ calendar.timegm(builder._not_valid_after.timetuple())
+ )
+ assert res != self._ffi.NULL
+
+ # Add extensions.
+ for i, extension in enumerate(builder._extensions):
+ if isinstance(extension.value, x509.BasicConstraints):
+ pp, r = _encode_basic_constraints(self, extension.value)
+ elif isinstance(extension.value, x509.KeyUsage):
+ pp, r = _encode_key_usage(self, extension.value)
+ elif isinstance(extension.value, x509.ExtendedKeyUsage):
+ pp, r = _encode_extended_key_usage(self, extension.value)
+ elif isinstance(extension.value, x509.SubjectAlternativeName):
+ pp, r = _encode_subject_alt_name(self, extension.value)
+ elif isinstance(extension.value, x509.AuthorityInformationAccess):
+ pp, r = _encode_authority_information_access(
+ self, extension.value
+ )
+ else:
+ raise NotImplementedError('Extension not yet supported.')
+
+ obj = _txt2obj(self, extension.oid.dotted_string)
+ extension = self._lib.X509_EXTENSION_create_by_OBJ(
+ self._ffi.NULL,
+ obj,
+ 1 if extension.critical else 0,
+ _encode_asn1_str_gc(self, pp[0], r)
+ )
+ res = self._lib.X509_add_ext(x509_cert, extension, i)
+ assert res == 1
+
+ # Set the issuer name.
+ res = self._lib.X509_set_issuer_name(
+ x509_cert, _encode_name(self, list(builder._issuer_name))
+ )
+ assert res == 1
+
+ # Sign the certificate with the issuer's private key.
+ res = self._lib.X509_sign(
+ x509_cert, private_key._evp_pkey, evp_md
+ )
+ if res == 0:
+ errors = self._consume_errors()
+ assert errors[0][1] == self._lib.ERR_LIB_RSA
+ assert errors[0][3] == self._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY
+ raise ValueError("Digest too big for RSA key")
+
+ return _Certificate(self, x509_cert)
+
def load_pem_private_key(self, data, password):
return self._load_key(
self._lib.PEM_read_bio_PrivateKey,
@@ -1577,13 +1718,15 @@ class Backend(object):
if format is serialization.PrivateFormat.PKCS8:
write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey
key = evp_pkey
- elif format is serialization.PrivateFormat.TraditionalOpenSSL:
+ else:
+ assert format is serialization.PrivateFormat.TraditionalOpenSSL
if evp_pkey.type == self._lib.EVP_PKEY_RSA:
write_bio = self._lib.PEM_write_bio_RSAPrivateKey
elif evp_pkey.type == self._lib.EVP_PKEY_DSA:
write_bio = self._lib.PEM_write_bio_DSAPrivateKey
- elif (self._lib.Cryptography_HAS_EC == 1 and
- evp_pkey.type == self._lib.EVP_PKEY_EC):
+ else:
+ assert self._lib.Cryptography_HAS_EC == 1
+ assert evp_pkey.type == self._lib.EVP_PKEY_EC
write_bio = self._lib.PEM_write_bio_ECPrivateKey
key = cdata
@@ -1600,7 +1743,8 @@ class Backend(object):
return self._private_key_bytes_traditional_der(
evp_pkey.type, cdata
)
- elif format is serialization.PrivateFormat.PKCS8:
+ else:
+ assert format is serialization.PrivateFormat.PKCS8
write_bio = self._lib.i2d_PKCS8PrivateKey_bio
key = evp_pkey
else:
@@ -1625,7 +1769,8 @@ class Backend(object):
elif (self._lib.Cryptography_HAS_EC == 1 and
key_type == self._lib.EVP_PKEY_EC):
write_bio = self._lib.i2d_ECPrivateKey_bio
- elif key_type == self._lib.EVP_PKEY_DSA:
+ else:
+ assert key_type == self._lib.EVP_PKEY_DSA
write_bio = self._lib.i2d_DSAPrivateKey_bio
bio = self._create_mem_bio()
@@ -1640,7 +1785,8 @@ class Backend(object):
if format is serialization.PublicFormat.SubjectPublicKeyInfo:
if encoding is serialization.Encoding.PEM:
write_bio = self._lib.PEM_write_bio_PUBKEY
- elif encoding is serialization.Encoding.DER:
+ else:
+ assert encoding is serialization.Encoding.DER
write_bio = self._lib.i2d_PUBKEY_bio
key = evp_pkey
@@ -1649,7 +1795,8 @@ class Backend(object):
assert evp_pkey.type == self._lib.EVP_PKEY_RSA
if encoding is serialization.Encoding.PEM:
write_bio = self._lib.PEM_write_bio_RSAPublicKey
- elif encoding is serialization.Encoding.DER:
+ else:
+ assert encoding is serialization.Encoding.DER
write_bio = self._lib.i2d_RSAPublicKey_bio
key = cdata
diff --git a/src/cryptography/hazmat/backends/openssl/rsa.py b/src/cryptography/hazmat/backends/openssl/rsa.py
index 21414c05..822c7304 100644
--- a/src/cryptography/hazmat/backends/openssl/rsa.py
+++ b/src/cryptography/hazmat/backends/openssl/rsa.py
@@ -268,8 +268,9 @@ class _RSASignatureContext(object):
self._backend._lib.RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE):
reason = ("Salt length too long for key size. Try using "
"MAX_LENGTH instead.")
- elif (errors[0].reason ==
- self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY):
+ else:
+ assert (errors[0].reason ==
+ self._backend._lib.RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY)
reason = "Digest too large for key size. Use a larger key."
assert reason is not None
raise ValueError(reason)
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
index 493abc83..ee9a3bbf 100644
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -234,7 +234,15 @@ class _X509ExtensionParser(object):
"{0} is not currently supported".format(oid), oid
)
else:
- value = handler(backend, ext)
+ d2i = backend._lib.X509V3_EXT_d2i(ext)
+ if d2i == backend._ffi.NULL:
+ backend._consume_errors()
+ raise ValueError(
+ "The {0} extension is invalid and can't be "
+ "parsed".format(oid)
+ )
+
+ value = handler(backend, d2i)
extensions.append(x509.Extension(oid, critical, value))
seen_oids.add(oid)
@@ -358,12 +366,8 @@ class _Certificate(object):
return self._backend._read_mem_bio(bio)
-def _decode_certificate_policies(backend, ext):
- cp = backend._ffi.cast(
- "Cryptography_STACK_OF_POLICYINFO *",
- backend._lib.X509V3_EXT_d2i(ext)
- )
- assert cp != backend._ffi.NULL
+def _decode_certificate_policies(backend, cp):
+ cp = backend._ffi.cast("Cryptography_STACK_OF_POLICYINFO *", cp)
cp = backend._ffi.gc(cp, backend._lib.sk_POLICYINFO_free)
num = backend._lib.sk_POLICYINFO_num(cp)
certificate_policies = []
@@ -386,7 +390,8 @@ def _decode_certificate_policies(backend, ext):
pqi.d.cpsuri.data, pqi.d.cpsuri.length
)[:].decode('ascii')
qualifiers.append(cpsuri)
- elif pqualid == x509.OID_CPS_USER_NOTICE:
+ else:
+ assert pqualid == x509.OID_CPS_USER_NOTICE
user_notice = _decode_user_notice(
backend, pqi.d.usernotice
)
@@ -431,12 +436,8 @@ def _decode_user_notice(backend, un):
return x509.UserNotice(notice_reference, explicit_text)
-def _decode_basic_constraints(backend, ext):
- bc_st = backend._lib.X509V3_EXT_d2i(ext)
- assert bc_st != backend._ffi.NULL
- basic_constraints = backend._ffi.cast(
- "BASIC_CONSTRAINTS *", bc_st
- )
+def _decode_basic_constraints(backend, bc_st):
+ basic_constraints = backend._ffi.cast("BASIC_CONSTRAINTS *", bc_st)
basic_constraints = backend._ffi.gc(
basic_constraints, backend._lib.BASIC_CONSTRAINTS_free
)
@@ -447,19 +448,13 @@ def _decode_basic_constraints(backend, ext):
if basic_constraints.pathlen == backend._ffi.NULL:
path_length = None
else:
- path_length = _asn1_integer_to_int(
- backend, basic_constraints.pathlen
- )
+ path_length = _asn1_integer_to_int(backend, basic_constraints.pathlen)
return x509.BasicConstraints(ca, path_length)
-def _decode_subject_key_identifier(backend, ext):
- asn1_string = backend._lib.X509V3_EXT_d2i(ext)
- assert asn1_string != backend._ffi.NULL
- asn1_string = backend._ffi.cast(
- "ASN1_OCTET_STRING *", asn1_string
- )
+def _decode_subject_key_identifier(backend, asn1_string):
+ asn1_string = backend._ffi.cast("ASN1_OCTET_STRING *", asn1_string)
asn1_string = backend._ffi.gc(
asn1_string, backend._lib.ASN1_OCTET_STRING_free
)
@@ -468,13 +463,9 @@ def _decode_subject_key_identifier(backend, ext):
)
-def _decode_authority_key_identifier(backend, ext):
- akid = backend._lib.X509V3_EXT_d2i(ext)
- assert akid != backend._ffi.NULL
+def _decode_authority_key_identifier(backend, akid):
akid = backend._ffi.cast("AUTHORITY_KEYID *", akid)
- akid = backend._ffi.gc(
- akid, backend._lib.AUTHORITY_KEYID_free
- )
+ akid = backend._ffi.gc(akid, backend._lib.AUTHORITY_KEYID_free)
key_identifier = None
authority_cert_issuer = None
authority_cert_serial_number = None
@@ -499,15 +490,9 @@ def _decode_authority_key_identifier(backend, ext):
)
-def _decode_authority_information_access(backend, ext):
- aia = backend._lib.X509V3_EXT_d2i(ext)
- assert aia != backend._ffi.NULL
- aia = backend._ffi.cast(
- "Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia
- )
- aia = backend._ffi.gc(
- aia, backend._lib.sk_ACCESS_DESCRIPTION_free
- )
+def _decode_authority_information_access(backend, aia):
+ aia = backend._ffi.cast("Cryptography_STACK_OF_ACCESS_DESCRIPTION *", aia)
+ aia = backend._ffi.gc(aia, backend._lib.sk_ACCESS_DESCRIPTION_free)
num = backend._lib.sk_ACCESS_DESCRIPTION_num(aia)
access_descriptions = []
for i in range(num):
@@ -521,13 +506,9 @@ def _decode_authority_information_access(backend, ext):
return x509.AuthorityInformationAccess(access_descriptions)
-def _decode_key_usage(backend, ext):
- bit_string = backend._lib.X509V3_EXT_d2i(ext)
- assert bit_string != backend._ffi.NULL
+def _decode_key_usage(backend, bit_string):
bit_string = backend._ffi.cast("ASN1_BIT_STRING *", bit_string)
- bit_string = backend._ffi.gc(
- bit_string, backend._lib.ASN1_BIT_STRING_free
- )
+ bit_string = backend._ffi.gc(bit_string, backend._lib.ASN1_BIT_STRING_free)
get_bit = backend._lib.ASN1_BIT_STRING_get_bit
digital_signature = get_bit(bit_string, 0) == 1
content_commitment = get_bit(bit_string, 1) == 1
@@ -551,11 +532,8 @@ def _decode_key_usage(backend, ext):
)
-def _decode_general_names_extension(backend, ext):
- gns = backend._ffi.cast(
- "GENERAL_NAMES *", backend._lib.X509V3_EXT_d2i(ext)
- )
- assert gns != backend._ffi.NULL
+def _decode_general_names_extension(backend, gns):
+ gns = backend._ffi.cast("GENERAL_NAMES *", gns)
gns = backend._ffi.gc(gns, backend._lib.GENERAL_NAMES_free)
general_names = _decode_general_names(backend, gns)
return general_names
@@ -573,11 +551,8 @@ def _decode_issuer_alt_name(backend, ext):
)
-def _decode_name_constraints(backend, ext):
- nc = backend._ffi.cast(
- "NAME_CONSTRAINTS *", backend._lib.X509V3_EXT_d2i(ext)
- )
- assert nc != backend._ffi.NULL
+def _decode_name_constraints(backend, nc):
+ nc = backend._ffi.cast("NAME_CONSTRAINTS *", nc)
nc = backend._ffi.gc(nc, backend._lib.NAME_CONSTRAINTS_free)
permitted = _decode_general_subtrees(backend, nc.permittedSubtrees)
excluded = _decode_general_subtrees(backend, nc.excludedSubtrees)
@@ -602,12 +577,8 @@ def _decode_general_subtrees(backend, stack_subtrees):
return subtrees
-def _decode_extended_key_usage(backend, ext):
- sk = backend._ffi.cast(
- "Cryptography_STACK_OF_ASN1_OBJECT *",
- backend._lib.X509V3_EXT_d2i(ext)
- )
- assert sk != backend._ffi.NULL
+def _decode_extended_key_usage(backend, sk):
+ sk = backend._ffi.cast("Cryptography_STACK_OF_ASN1_OBJECT *", sk)
sk = backend._ffi.gc(sk, backend._lib.sk_ASN1_OBJECT_free)
num = backend._lib.sk_ASN1_OBJECT_num(sk)
ekus = []
@@ -621,14 +592,9 @@ def _decode_extended_key_usage(backend, ext):
return x509.ExtendedKeyUsage(ekus)
-def _decode_crl_distribution_points(backend, ext):
- cdps = backend._ffi.cast(
- "Cryptography_STACK_OF_DIST_POINT *",
- backend._lib.X509V3_EXT_d2i(ext)
- )
- assert cdps != backend._ffi.NULL
- cdps = backend._ffi.gc(
- cdps, backend._lib.sk_DIST_POINT_free)
+def _decode_crl_distribution_points(backend, cdps):
+ cdps = backend._ffi.cast("Cryptography_STACK_OF_DIST_POINT *", cdps)
+ cdps = backend._ffi.gc(cdps, backend._lib.sk_DIST_POINT_free)
num = backend._lib.sk_DIST_POINT_num(cdps)
dist_points = []
@@ -716,12 +682,8 @@ def _decode_crl_distribution_points(backend, ext):
return x509.CRLDistributionPoints(dist_points)
-def _decode_inhibit_any_policy(backend, ext):
- asn1_int = backend._ffi.cast(
- "ASN1_INTEGER *",
- backend._lib.X509V3_EXT_d2i(ext)
- )
- assert asn1_int != backend._ffi.NULL
+def _decode_inhibit_any_policy(backend, asn1_int):
+ asn1_int = backend._ffi.cast("ASN1_INTEGER *", asn1_int)
asn1_int = backend._ffi.gc(asn1_int, backend._lib.ASN1_INTEGER_free)
skip_certs = _asn1_integer_to_int(backend, asn1_int)
return x509.InhibitAnyPolicy(skip_certs)
@@ -789,35 +751,33 @@ class _CertificateSigningRequest(object):
return self._backend._read_mem_bio(bio)
+_EXTENSION_HANDLERS = {
+ x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
+ x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
+ x509.OID_KEY_USAGE: _decode_key_usage,
+ x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
+ x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
+ x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
+ x509.OID_AUTHORITY_INFORMATION_ACCESS: (
+ _decode_authority_information_access
+ ),
+ x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies,
+ x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
+ x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
+ x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
+ x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
+ x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
+}
+
+
_CERTIFICATE_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.X509_get_ext_count(x),
get_ext=lambda backend, x, i: backend._lib.X509_get_ext(x, i),
- handlers={
- x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
- x509.OID_SUBJECT_KEY_IDENTIFIER: _decode_subject_key_identifier,
- x509.OID_KEY_USAGE: _decode_key_usage,
- x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
- x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
- x509.OID_AUTHORITY_KEY_IDENTIFIER: _decode_authority_key_identifier,
- x509.OID_AUTHORITY_INFORMATION_ACCESS: (
- _decode_authority_information_access
- ),
- x509.OID_CERTIFICATE_POLICIES: _decode_certificate_policies,
- x509.OID_CRL_DISTRIBUTION_POINTS: _decode_crl_distribution_points,
- x509.OID_OCSP_NO_CHECK: _decode_ocsp_no_check,
- x509.OID_INHIBIT_ANY_POLICY: _decode_inhibit_any_policy,
- x509.OID_ISSUER_ALTERNATIVE_NAME: _decode_issuer_alt_name,
- x509.OID_NAME_CONSTRAINTS: _decode_name_constraints,
- }
+ handlers=_EXTENSION_HANDLERS
)
_CSR_EXTENSION_PARSER = _X509ExtensionParser(
ext_count=lambda backend, x: backend._lib.sk_X509_EXTENSION_num(x),
get_ext=lambda backend, x, i: backend._lib.sk_X509_EXTENSION_value(x, i),
- handlers={
- x509.OID_BASIC_CONSTRAINTS: _decode_basic_constraints,
- x509.OID_KEY_USAGE: _decode_key_usage,
- x509.OID_SUBJECT_ALTERNATIVE_NAME: _decode_subject_alt_name,
- x509.OID_EXTENDED_KEY_USAGE: _decode_extended_key_usage,
- }
+ handlers=_EXTENSION_HANDLERS
)
diff --git a/src/cryptography/hazmat/primitives/serialization.py b/src/cryptography/hazmat/primitives/serialization.py
index 098b31dc..fc50456e 100644
--- a/src/cryptography/hazmat/primitives/serialization.py
+++ b/src/cryptography/hazmat/primitives/serialization.py
@@ -33,9 +33,9 @@ def load_der_public_key(data, backend):
def load_ssh_public_key(data, backend):
- key_parts = data.split(b' ')
+ key_parts = data.split(b' ', 2)
- if len(key_parts) != 2 and len(key_parts) != 3:
+ if len(key_parts) < 2:
raise ValueError(
'Key is not in the proper format or contains extra data.')
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index 75552fc1..978eb560 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -5,6 +5,7 @@
from __future__ import absolute_import, division, print_function
import abc
+import datetime
import ipaddress
from email.utils import parseaddr
from enum import Enum
@@ -17,6 +18,7 @@ from six.moves import urllib_parse
from cryptography import utils
from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa
_OID_NAMES = {
@@ -95,6 +97,8 @@ _GENERAL_NAMES = {
8: "registeredID",
}
+_UNIX_EPOCH = datetime.datetime(1970, 1, 1)
+
class Version(Enum):
v1 = 0
@@ -1598,3 +1602,154 @@ class CertificateSigningRequestBuilder(object):
if self._subject_name is None:
raise ValueError("A CertificateSigningRequest must have a subject")
return backend.create_x509_csr(self, private_key, algorithm)
+
+
+class CertificateBuilder(object):
+ def __init__(self, issuer_name=None, subject_name=None,
+ public_key=None, serial_number=None, not_valid_before=None,
+ not_valid_after=None, extensions=[]):
+ self._version = Version.v3
+ self._issuer_name = issuer_name
+ self._subject_name = subject_name
+ self._public_key = public_key
+ self._serial_number = serial_number
+ self._not_valid_before = not_valid_before
+ self._not_valid_after = not_valid_after
+ self._extensions = extensions
+
+ def issuer_name(self, name):
+ """
+ Sets the CA's distinguished name.
+ """
+ if not isinstance(name, Name):
+ raise TypeError('Expecting x509.Name object.')
+ if self._issuer_name is not None:
+ raise ValueError('The issuer name may only be set once.')
+ return CertificateBuilder(
+ name, self._subject_name, self._public_key,
+ self._serial_number, self._not_valid_before,
+ self._not_valid_after, self._extensions
+ )
+
+ def subject_name(self, name):
+ """
+ Sets the requestor's distinguished name.
+ """
+ if not isinstance(name, Name):
+ raise TypeError('Expecting x509.Name object.')
+ if self._subject_name is not None:
+ raise ValueError('The subject name may only be set once.')
+ return CertificateBuilder(
+ self._issuer_name, name, self._public_key,
+ self._serial_number, self._not_valid_before,
+ self._not_valid_after, self._extensions
+ )
+
+ def public_key(self, key):
+ """
+ Sets the requestor's public key (as found in the signing request).
+ """
+ if not isinstance(key, (dsa.DSAPublicKey, rsa.RSAPublicKey,
+ ec.EllipticCurvePublicKey)):
+ raise TypeError('Expecting one of DSAPublicKey, RSAPublicKey,'
+ ' or EllipticCurvePublicKey.')
+ if self._public_key is not None:
+ raise ValueError('The public key may only be set once.')
+ return CertificateBuilder(
+ self._issuer_name, self._subject_name, key,
+ self._serial_number, self._not_valid_before,
+ self._not_valid_after, self._extensions
+ )
+
+ def serial_number(self, number):
+ """
+ Sets the certificate serial number.
+ """
+ if not isinstance(number, six.integer_types):
+ raise TypeError('Serial number must be of integral type.')
+ if self._serial_number is not None:
+ raise ValueError('The serial number may only be set once.')
+ if number < 0:
+ raise ValueError('The serial number should be non-negative.')
+ if utils.bit_length(number) > 160: # As defined in RFC 5280
+ raise ValueError('The serial number should not be more than 160 '
+ 'bits.')
+ return CertificateBuilder(
+ self._issuer_name, self._subject_name,
+ self._public_key, number, self._not_valid_before,
+ self._not_valid_after, self._extensions
+ )
+
+ def not_valid_before(self, time):
+ """
+ Sets the certificate activation time.
+ """
+ if not isinstance(time, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._not_valid_before is not None:
+ raise ValueError('The not valid before may only be set once.')
+ if time <= _UNIX_EPOCH:
+ raise ValueError('The not valid before date must be after the unix'
+ ' epoch (1970 January 1).')
+ return CertificateBuilder(
+ self._issuer_name, self._subject_name,
+ self._public_key, self._serial_number, time,
+ self._not_valid_after, self._extensions
+ )
+
+ def not_valid_after(self, time):
+ """
+ Sets the certificate expiration time.
+ """
+ if not isinstance(time, datetime.datetime):
+ raise TypeError('Expecting datetime object.')
+ if self._not_valid_after is not None:
+ raise ValueError('The not valid after may only be set once.')
+ if time <= _UNIX_EPOCH:
+ raise ValueError('The not valid after date must be after the unix'
+ ' epoch (1970 January 1).')
+ return CertificateBuilder(
+ self._issuer_name, self._subject_name,
+ self._public_key, self._serial_number, self._not_valid_before,
+ time, self._extensions
+ )
+
+ def add_extension(self, extension, critical):
+ """
+ Adds an X.509 extension to the certificate.
+ """
+ if isinstance(extension, BasicConstraints):
+ extension = Extension(OID_BASIC_CONSTRAINTS, critical, extension)
+ elif isinstance(extension, KeyUsage):
+ extension = Extension(OID_KEY_USAGE, critical, extension)
+ elif isinstance(extension, ExtendedKeyUsage):
+ extension = Extension(OID_EXTENDED_KEY_USAGE, critical, extension)
+ elif isinstance(extension, SubjectAlternativeName):
+ extension = Extension(
+ OID_SUBJECT_ALTERNATIVE_NAME, critical, extension
+ )
+ elif isinstance(extension, AuthorityInformationAccess):
+ extension = Extension(
+ OID_AUTHORITY_INFORMATION_ACCESS, critical, extension
+ )
+ elif isinstance(extension, InhibitAnyPolicy):
+ extension = Extension(OID_INHIBIT_ANY_POLICY, critical, extension)
+ else:
+ raise NotImplementedError('Unsupported X.509 extension.')
+
+ # TODO: This is quadratic in the number of extensions
+ for e in self._extensions:
+ if e.oid == extension.oid:
+ raise ValueError('This extension has already been set.')
+
+ return CertificateBuilder(
+ self._issuer_name, self._subject_name,
+ self._public_key, self._serial_number, self._not_valid_before,
+ self._not_valid_after, self._extensions + [extension]
+ )
+
+ def sign(self, private_key, algorithm, backend):
+ """
+ Signs the certificate using the CA's private key.
+ """
+ return backend.sign_x509_certificate(self, private_key, algorithm)