aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/x509.rst81
-rw-r--r--src/cryptography/hazmat/bindings/openssl/asn1.py1
-rw-r--r--src/cryptography/hazmat/bindings/openssl/x509.py15
-rw-r--r--src/cryptography/x509.py114
-rw-r--r--tasks.py9
-rw-r--r--tests/test_x509_ext.py58
6 files changed, 275 insertions, 3 deletions
diff --git a/docs/x509.rst b/docs/x509.rst
index 594cd81d..bdcd60b1 100644
--- a/docs/x509.rst
+++ b/docs/x509.rst
@@ -208,7 +208,7 @@ X.509 Certificate Object
:class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
that will be used to generate the fingerprint.
- :return bytes: The fingerprint using the supplied hash algorithm as
+ :return bytes: The fingerprint using the supplied hash algorithm, as
bytes.
.. doctest::
@@ -335,6 +335,61 @@ X.509 Certificate Object
:return bytes: The data that can be written to a file or sent
over the network to be verified by clients.
+X.509 CRL (Certificate Revocation List) Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: CertificateRevocationList
+
+ .. versionadded:: 1.0
+
+ .. method:: fingerprint(algorithm)
+
+ :param algorithm: The
+ :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
+ that will be used to generate the fingerprint.
+
+ :return bytes: The fingerprint using the supplied hash algorithm, as
+ bytes.
+
+ .. attribute:: signature_hash_algorithm
+
+ :type: :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm`
+
+ Returns the
+ :class:`~cryptography.hazmat.primitives.hashes.HashAlgorithm` which
+ was used in signing this CRL.
+
+ .. attribute:: issuer
+
+ :type: :class:`Name`
+
+ The :class:`Name` of the issuer.
+
+ .. attribute:: next_update
+
+ :type: :class:`datetime.datetime`
+
+ A naïve datetime representing when the next update to this CRL is
+ expected.
+
+ .. attribute:: last_update
+
+ :type: :class:`datetime.datetime`
+
+ A naïve datetime representing when the this CRL was last updated.
+
+ .. attribute:: revoked_certificates
+
+ :type: list of :class:`RevokedCertificate`
+
+ The revoked certificates listed in this CRL.
+
+ .. attribute:: extensions
+
+ :type: :class:`Extensions`
+
+ The extensions encoded in the CRL.
+
X.509 CSR (Certificate Signing Request) Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -388,6 +443,30 @@ X.509 CSR (Certificate Signing Request) Object
over the network to be signed by the certificate
authority.
+X.509 Revoked Certificate Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. class:: RevokedCertificate
+
+ .. versionadded:: 1.0
+
+ .. attribute:: serial_number
+
+ :type: :class:`int`
+
+ An integer representing the serial number of the revoked certificate.
+
+ .. attribute:: revocation_date
+
+ :type: :class:`datetime.datetime`
+
+ A naïve datetime representing the date this certificates was revoked.
+
+ .. attribute:: extensions
+
+ :type: :class:`Extensions`
+
+ The extensions encoded in the revoked certificate.
.. class:: Name
diff --git a/src/cryptography/hazmat/bindings/openssl/asn1.py b/src/cryptography/hazmat/bindings/openssl/asn1.py
index 711c30b2..c18708c5 100644
--- a/src/cryptography/hazmat/bindings/openssl/asn1.py
+++ b/src/cryptography/hazmat/bindings/openssl/asn1.py
@@ -114,6 +114,7 @@ void ASN1_GENERALIZEDTIME_free(ASN1_GENERALIZEDTIME *);
ASN1_ENUMERATED *ASN1_ENUMERATED_new(void);
void ASN1_ENUMERATED_free(ASN1_ENUMERATED *);
int ASN1_ENUMERATED_set(ASN1_ENUMERATED *, long);
+long ASN1_ENUMERATED_get(ASN1_ENUMERATED *);
ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **, const unsigned char **, long,
const ASN1_ITEM *);
diff --git a/src/cryptography/hazmat/bindings/openssl/x509.py b/src/cryptography/hazmat/bindings/openssl/x509.py
index caa33969..e0f42fbb 100644
--- a/src/cryptography/hazmat/bindings/openssl/x509.py
+++ b/src/cryptography/hazmat/bindings/openssl/x509.py
@@ -194,6 +194,7 @@ int X509_CRL_verify(X509_CRL *, EVP_PKEY *);
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 X509_CRL_cmp(const X509_CRL *, const X509_CRL *);
int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *, EVP_PKEY *);
int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *, EVP_PKEY *, const EVP_MD *);
@@ -310,9 +311,14 @@ int i2d_EC_PUBKEY(EC_KEY *, unsigned char **);
EC_KEY *d2i_EC_PUBKEY(EC_KEY **, const unsigned char **, long);
EC_KEY *d2i_EC_PUBKEY_bio(BIO *, EC_KEY **);
int i2d_EC_PUBKEY_bio(BIO *, EC_KEY *);
+EC_KEY *d2i_ECPrivateKey(EC_KEY **, const unsigned char **, long);
EC_KEY *d2i_ECPrivateKey_bio(BIO *, EC_KEY **);
+int i2d_ECPrivateKey(EC_KEY *, unsigned char **);
int i2d_ECPrivateKey_bio(BIO *, EC_KEY *);
+EC_KEY *o2i_ECPublicKey(EC_KEY **, const unsigned char **, long);
+int i2o_ECPublicKey(EC_KEY *, unsigned char **);
+
// declared in safestack
int sk_ASN1_OBJECT_num(Cryptography_STACK_OF_ASN1_OBJECT *);
ASN1_OBJECT *sk_ASN1_OBJECT_value(Cryptography_STACK_OF_ASN1_OBJECT *, int);
@@ -329,8 +335,13 @@ int (*i2d_EC_PUBKEY)(EC_KEY *, unsigned char **) = NULL;
EC_KEY *(*d2i_EC_PUBKEY)(EC_KEY **, const unsigned char **, long) = NULL;
EC_KEY *(*d2i_EC_PUBKEY_bio)(BIO *, EC_KEY **) = NULL;
int (*i2d_EC_PUBKEY_bio)(BIO *, EC_KEY *) = NULL;
+EC_KEY *(*d2i_ECPrivateKey)(EC_KEY **, const unsigned char **, long) = NULL;
EC_KEY *(*d2i_ECPrivateKey_bio)(BIO *, EC_KEY **) = NULL;
+int (*i2d_ECPrivateKey)(EC_KEY *, unsigned char **) = NULL;
int (*i2d_ECPrivateKey_bio)(BIO *, EC_KEY *) = NULL;
+
+EC_KEY *(*o2i_ECPublicKey)(EC_KEY **, const unsigned char **, long) = NULL;
+int (*i2o_ECPublicKey)(EC_KEY *, unsigned char **) = NULL;
#endif
"""
@@ -340,7 +351,11 @@ CONDITIONAL_NAMES = {
"d2i_EC_PUBKEY",
"d2i_EC_PUBKEY_bio",
"i2d_EC_PUBKEY_bio",
+ "d2i_ECPrivateKey",
"d2i_ECPrivateKey_bio",
+ "i2d_ECPrivateKey",
"i2d_ECPrivateKey_bio",
+ "i2o_ECPublicKey",
+ "o2i_ECPublicKey",
]
}
diff --git a/src/cryptography/x509.py b/src/cryptography/x509.py
index b11c7949..6592684b 100644
--- a/src/cryptography/x509.py
+++ b/src/cryptography/x509.py
@@ -55,6 +55,9 @@ _OID_NAMES = {
"2.5.29.17": "subjectAltName",
"2.5.29.18": "issuerAltName",
"2.5.29.19": "basicConstraints",
+ "2.5.29.21": "cRLReason",
+ "2.5.29.24": "invalidityDate",
+ "2.5.29.29": "certificateIssuer",
"2.5.29.30": "nameConstraints",
"2.5.29.31": "cRLDistributionPoints",
"2.5.29.32": "certificatePolicies",
@@ -224,6 +227,9 @@ 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_CRL_REASON = ObjectIdentifier("2.5.29.21")
+OID_INVALIDITY_DATE = ObjectIdentifier("2.5.29.24")
+OID_CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29")
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")
@@ -969,7 +975,7 @@ class IPAddress(object):
return not self == other
-class SubjectAlternativeName(object):
+class GeneralNames(object):
def __init__(self, general_names):
if not all(isinstance(x, GeneralName) for x in general_names):
raise TypeError(
@@ -989,6 +995,32 @@ class SubjectAlternativeName(object):
return [i.value for i in self if isinstance(i, type)]
def __repr__(self):
+ return "<GeneralNames({0})>".format(self._general_names)
+
+ def __eq__(self, other):
+ if not isinstance(other, GeneralNames):
+ return NotImplemented
+
+ return self._general_names == other._general_names
+
+ def __ne__(self, other):
+ return not self == other
+
+
+class SubjectAlternativeName(object):
+ def __init__(self, general_names):
+ self._general_names = GeneralNames(general_names)
+
+ def __iter__(self):
+ return iter(self._general_names)
+
+ def __len__(self):
+ return len(self._general_names)
+
+ def get_values_for_type(self, type):
+ return self._general_names.get_values_for_type(type)
+
+ def __repr__(self):
return "<SubjectAlternativeName({0})>".format(self._general_names)
def __eq__(self, other):
@@ -1201,6 +1233,65 @@ class Certificate(object):
@six.add_metaclass(abc.ABCMeta)
+class CertificateRevocationList(object):
+
+ @abc.abstractmethod
+ def fingerprint(self, algorithm):
+ """
+ Returns bytes using digest passed.
+ """
+
+ @abc.abstractproperty
+ def signature_hash_algorithm(self):
+ """
+ Returns a HashAlgorithm corresponding to the type of the digest signed
+ in the certificate.
+ """
+
+ @abc.abstractproperty
+ def issuer(self):
+ """
+ Returns the X509Name with the issuer of this CRL.
+ """
+
+ @abc.abstractproperty
+ def next_update(self):
+ """
+ Returns the date of next update for this CRL.
+ """
+
+ @abc.abstractproperty
+ def last_update(self):
+ """
+ Returns the date of last update for this CRL.
+ """
+
+ @abc.abstractproperty
+ def revoked_certificates(self):
+ """
+ Returns a list of RevokedCertificate objects for this CRL.
+ """
+
+ @abc.abstractproperty
+ def extensions(self):
+ """
+ Returns an Extensions object containing a list of CRL extensions.
+ """
+
+ @abc.abstractmethod
+ def __eq__(self, other):
+ """
+ Checks equality.
+ """
+
+ @abc.abstractmethod
+ def __ne__(self, other):
+ """
+ Checks not equal.
+ """
+
+
+@six.add_metaclass(abc.ABCMeta)
class CertificateSigningRequest(object):
@abc.abstractmethod
def public_key(self):
@@ -1232,3 +1323,24 @@ class CertificateSigningRequest(object):
"""
Encodes the request to PEM or DER format.
"""
+
+
+@six.add_metaclass(abc.ABCMeta)
+class RevokedCertificate(object):
+ @abc.abstractproperty
+ def serial_number(self):
+ """
+ Returns the serial number of the revoked certificate.
+ """
+
+ @abc.abstractproperty
+ def revocation_date(self):
+ """
+ Returns the date of when this certificate was revoked.
+ """
+
+ @abc.abstractproperty
+ def extensions(self):
+ """
+ Returns an Extensions object containing a list of Revoked extensions.
+ """
diff --git a/tasks.py b/tasks.py
index 74ff6b19..76d0d029 100644
--- a/tasks.py
+++ b/tasks.py
@@ -47,7 +47,16 @@ def download_artifacts(session):
paths = []
+ last_build_number = response.json()["number"]
for run in response.json()["runs"]:
+ if run["number"] != last_build_number:
+ print(
+ "Skipping {0} as it is not from the latest build ({1})".format(
+ run["url"], last_build_number
+ )
+ )
+ continue
+
response = session.get(
run["url"] + "api/json/",
headers={
diff --git a/tests/test_x509_ext.py b/tests/test_x509_ext.py
index 0405b798..f16db337 100644
--- a/tests/test_x509_ext.py
+++ b/tests/test_x509_ext.py
@@ -1139,6 +1139,61 @@ class TestIPAddress(object):
assert gn != object()
+class TestGeneralNames(object):
+ def test_get_values_for_type(self):
+ gns = x509.GeneralNames(
+ [x509.DNSName(u"cryptography.io")]
+ )
+ names = gns.get_values_for_type(x509.DNSName)
+ assert names == [u"cryptography.io"]
+
+ def test_iter_names(self):
+ gns = x509.GeneralNames([
+ x509.DNSName(u"cryptography.io"),
+ x509.DNSName(u"crypto.local"),
+ ])
+ assert len(gns) == 2
+ assert list(gns) == [
+ x509.DNSName(u"cryptography.io"),
+ x509.DNSName(u"crypto.local"),
+ ]
+
+ def test_invalid_general_names(self):
+ with pytest.raises(TypeError):
+ x509.GeneralNames(
+ [x509.DNSName(u"cryptography.io"), "invalid"]
+ )
+
+ def test_repr(self):
+ gns = x509.GeneralNames(
+ [
+ x509.DNSName(u"cryptography.io")
+ ]
+ )
+ assert repr(gns) == (
+ "<GeneralNames([<DNSName(value=cryptography.io)>])>"
+ )
+
+ def test_eq(self):
+ gns = x509.GeneralNames(
+ [x509.DNSName(u"cryptography.io")]
+ )
+ gns2 = x509.GeneralNames(
+ [x509.DNSName(u"cryptography.io")]
+ )
+ assert gns == gns2
+
+ def test_ne(self):
+ gns = x509.GeneralNames(
+ [x509.DNSName(u"cryptography.io")]
+ )
+ gns2 = x509.GeneralNames(
+ [x509.RFC822Name(u"admin@cryptography.io")]
+ )
+ assert gns != gns2
+ assert gns != object()
+
+
class TestSubjectAlternativeName(object):
def test_get_values_for_type(self):
san = x509.SubjectAlternativeName(
@@ -1171,7 +1226,8 @@ class TestSubjectAlternativeName(object):
]
)
assert repr(san) == (
- "<SubjectAlternativeName([<DNSName(value=cryptography.io)>])>"
+ "<SubjectAlternativeName("
+ "<GeneralNames([<DNSName(value=cryptography.io)>])>)>"
)
def test_eq(self):