aboutsummaryrefslogtreecommitdiff
path: root/scripts/rpki/x509.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/rpki/x509.py')
-rw-r--r--scripts/rpki/x509.py87
1 files changed, 61 insertions, 26 deletions
diff --git a/scripts/rpki/x509.py b/scripts/rpki/x509.py
index b823c5a3..9b9b1c28 100644
--- a/scripts/rpki/x509.py
+++ b/scripts/rpki/x509.py
@@ -1,8 +1,9 @@
# $Id$
-"""
-One X.509 implementation to rule them all and in the darkness hide the
-twisty maze of partially overlapping X.509 support packages in Python.
+"""One X.509 implementation to rule them all...
+
+...and in the darkness hide the twisty maze of partially overlapping
+X.509 support packages in Python.
There are several existing packages, none of which do quite what I
need, due to age, lack of documentation, specialization, or lack of
@@ -14,15 +15,14 @@ some of the nasty details. This involves a lot of format conversion.
import POW, tlslite.api, POW.pkix, base64
class PEM_converter(object):
- """
- Convert between DER and PEM encodings for various kinds of ASN.1 data.
- """
+ """Convert between DER and PEM encodings for various kinds of ASN.1 data."""
def __init__(self, kind): # "CERTIFICATE", "RSA PRIVATE KEY", ...
self.b = "-----BEGIN %s-----" % kind
self.e = "-----END %s-----" % kind
def toDER(self, pem):
+ """Convert from PEM to DER."""
lines = pem.splitlines(0)
while lines and lines.pop(0) != self.b:
pass
@@ -32,6 +32,7 @@ class PEM_converter(object):
return base64.b64decode("".join(lines))
def toPEM(self, der):
+ """Convert from DER to PEM."""
b64 = base64.b64encode(der)
pem = self.b + "\n"
while len(b64) > 64:
@@ -40,21 +41,21 @@ class PEM_converter(object):
return pem + b64 + "\n" + self.e + "\n"
class DER_object(object):
- """
- Virtual class to hold a generic DER object.
- """
+ """Virtual class to hold a generic DER object."""
formats = ("DER",)
pem_converter = None
other_clear = ()
def empty(self):
+ """Test whether this object is empty."""
for a in self.formats:
if getattr(self, a, None) is not None:
return False
return True
def clear(self):
+ """Make this object empty."""
for a in self.formats + self.other_clear:
setattr(self, a, None)
@@ -64,6 +65,13 @@ class DER_object(object):
self.set(**kw)
def set(self, **kw):
+ """Set this object by setting one of its known formats.
+
+ This method only allows one to set one format at a time.
+ Subsequent calls will clear the object first. The point of all
+ this is to let the object's internal converters handle mustering
+ the object into whatever format you need at the moment.
+ """
name = kw.keys()[0]
if len(kw) == 1:
if name in self.formats:
@@ -82,18 +90,27 @@ class DER_object(object):
raise TypeError
def get_DER(self):
+ """Get the DER value of this object.
+
+ Subclasses will almost certainly override this method.
+ """
assert not self.empty()
if self.DER:
return self.DER
raise RuntimeError, "No conversion path to DER available"
def get_PEM(self):
+ """Get the PEM representation of this object."""
return self.pem_converter.toPEM(self.get_DER())
class X509(DER_object):
- """
- Class to hold all the different representations of X.509 certs we're
- using and convert between them.
+ """X.509 certificates.
+
+ This class is designed to hold all the different representations of
+ X.509 certs we're using and convert between them. X.509 support in
+ Python a nasty maze of half-cooked stuff (except perhaps for
+ cryptlib, which is just different). Users of this module should not
+ have to care about this implementation nightmare.
"""
formats = ("DER", "POW", "POWpkix", "tlslite")
@@ -101,6 +118,7 @@ class X509(DER_object):
other_clear = ("POW_extensions",)
def get_DER(self):
+ """Get the DER value of this certificate."""
assert not self.empty()
if self.DER:
return self.DER
@@ -113,12 +131,14 @@ class X509(DER_object):
raise RuntimeError
def get_POW(self):
+ """Get the POW value of this certificate."""
assert not self.empty()
if not self.POW:
self.POW = POW.derRead(POW.X509_CERTIFICATE, self.get_DER())
return self.POW
def get_POWpkix(self):
+ """Get the POW.pkix value of this certificate."""
assert not self.empty()
if not self.POWpkix:
cert = POW.pkix.Certificate()
@@ -127,6 +147,7 @@ class X509(DER_object):
return self.POWpkix
def get_tlslite(self):
+ """Get the tlslite value of this certificate."""
assert not self.empty()
if not self.tlslite:
cert = tlslite.api.X509()
@@ -135,12 +156,18 @@ class X509(DER_object):
return self.tlslite
def getIssuer(self):
+ """Get the issuer of this certificate."""
return self.get_POW().getIssuer()
def getSubject(self):
+ """Get the subject of this certificate."""
return self.get_POW().getSubject()
- def get_POW_extensions(self):
+ def _get_POW_extensions(self):
+ """Parse extensions from the POW value of this certificate.
+
+ Build a dictionary to ease lookup, and cache the result.
+ """
if not self.POW_extensions:
cert = self.get_POW()
exts = {}
@@ -151,23 +178,25 @@ class X509(DER_object):
return self.POW_extensions
def getAKI(self):
- return self.get_POW_extensions().get("authorityKeyIdentifier")
+ """Get the AKI extension from this certificate."""
+ return self._get_POW_extensions().get("authorityKeyIdentifier")
def getSKI(self):
- return self.get_POW_extensions().get("subjectKeyIdentifier")
+ """Get the SKI extension from this certificate."""
+ return self._get_POW_extensions().get("subjectKeyIdentifier")
class X509_chain(list):
- """
- Collection of certs with sorting and conversion functions
- for various packages.
+ """Collections of certs.
+
+ This class provides sorting and conversion functions for various
+ packages.
"""
def chainsort(self):
- """
- Sort a bag of certs into a chain, leaf first. Various other
- routines want their certs presented in this order.
- """
+ """Sort a bag of certs into a chain, leaf first.
+ Various other routines want their certs presented in this order.
+ """
bag = self[:]
issuer_names = [x.getIssuer() for x in bag]
subject_map = dict([(x.getSubject(), x) for x in bag])
@@ -177,7 +206,8 @@ class X509_chain(list):
cert = subject_map[subject]
chain.append(cert)
bag.remove(cert)
- assert len(chain) == 1
+ if len(chain) != 1:
+ raise RuntimeError, "Certificates in bag don't form a proper chain"
while bag:
cert = subject_map[chain[-1].getIssuer()]
chain.append(cert)
@@ -185,29 +215,33 @@ class X509_chain(list):
self[:] = chain
def tlslite_certChain(self):
+ """Return a certChain in the format tlslite likes."""
return tlslite.api.X509CertChain([x.get_tlslite() for x in self])
def tlslite_trustList(self):
+ """Return a trustList in the format tlslite likes."""
return [x.get_tlslite() for x in self]
def clear(self):
+ """Drop all certs from this bag onto the floor."""
self[:] = []
def load_from_PEM(self, files):
+ """Load a set of certs from a list of PEM files."""
self.extend([X509(PEM_file=f) for f in files])
def load_from_DER(self, files):
+ """Load a set of certs from a list of DER files."""
self.extend([X509(DER_file=f) for f in files])
class PKCS10_Request(DER_object):
- """
- Class to hold a PKCS #10 request.
- """
+ """Class to hold a PKCS #10 request."""
formats = ("DER", "POWpkix")
pem_converter = PEM_converter("CERTIFICATE REQUEST")
def get_DER(self):
+ """Get the DER value of this certification request."""
assert not self.empty()
if self.DER:
return self.DER
@@ -217,6 +251,7 @@ class PKCS10_Request(DER_object):
raise RuntimeError
def get_POWpkix(self):
+ """Get the POW.pkix value of this certification request."""
assert not self.empty()
if not self.POWpkix:
req = POW.pkix.CertificationRequest()