PageRenderTime 29ms CodeModel.GetById 8ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/gdata/tlslite/X509CertChain.py

http://radioappz.googlecode.com/
Python | 181 lines | 131 code | 18 blank | 32 comment | 32 complexity | fdce4a1a010d5ef58ef7f679997f9f9d MD5 | raw file
  1"""Class representing an X.509 certificate chain."""
  2
  3from utils import cryptomath
  4
  5class X509CertChain:
  6    """This class represents a chain of X.509 certificates.
  7
  8    @type x509List: list
  9    @ivar x509List: A list of L{tlslite.X509.X509} instances,
 10    starting with the end-entity certificate and with every
 11    subsequent certificate certifying the previous.
 12    """
 13
 14    def __init__(self, x509List=None):
 15        """Create a new X509CertChain.
 16
 17        @type x509List: list
 18        @param x509List: A list of L{tlslite.X509.X509} instances,
 19        starting with the end-entity certificate and with every
 20        subsequent certificate certifying the previous.
 21        """
 22        if x509List:
 23            self.x509List = x509List
 24        else:
 25            self.x509List = []
 26
 27    def getNumCerts(self):
 28        """Get the number of certificates in this chain.
 29
 30        @rtype: int
 31        """
 32        return len(self.x509List)
 33
 34    def getEndEntityPublicKey(self):
 35        """Get the public key from the end-entity certificate.
 36
 37        @rtype: L{tlslite.utils.RSAKey.RSAKey}
 38        """
 39        if self.getNumCerts() == 0:
 40            raise AssertionError()
 41        return self.x509List[0].publicKey
 42
 43    def getFingerprint(self):
 44        """Get the hex-encoded fingerprint of the end-entity certificate.
 45
 46        @rtype: str
 47        @return: A hex-encoded fingerprint.
 48        """
 49        if self.getNumCerts() == 0:
 50            raise AssertionError()
 51        return self.x509List[0].getFingerprint()
 52
 53    def getCommonName(self):
 54        """Get the Subject's Common Name from the end-entity certificate.
 55
 56        The cryptlib_py module must be installed in order to use this
 57        function.
 58
 59        @rtype: str or None
 60        @return: The CN component of the certificate's subject DN, if
 61        present.
 62        """
 63        if self.getNumCerts() == 0:
 64            raise AssertionError()
 65        return self.x509List[0].getCommonName()
 66
 67    def validate(self, x509TrustList):
 68        """Check the validity of the certificate chain.
 69
 70        This checks that every certificate in the chain validates with
 71        the subsequent one, until some certificate validates with (or
 72        is identical to) one of the passed-in root certificates.
 73
 74        The cryptlib_py module must be installed in order to use this
 75        function.
 76
 77        @type x509TrustList: list of L{tlslite.X509.X509}
 78        @param x509TrustList: A list of trusted root certificates.  The
 79        certificate chain must extend to one of these certificates to
 80        be considered valid.
 81        """
 82
 83        import cryptlib_py
 84        c1 = None
 85        c2 = None
 86        lastC = None
 87        rootC = None
 88
 89        try:
 90            rootFingerprints = [c.getFingerprint() for c in x509TrustList]
 91
 92            #Check that every certificate in the chain validates with the
 93            #next one
 94            for cert1, cert2 in zip(self.x509List, self.x509List[1:]):
 95
 96                #If we come upon a root certificate, we're done.
 97                if cert1.getFingerprint() in rootFingerprints:
 98                    return True
 99
100                c1 = cryptlib_py.cryptImportCert(cert1.writeBytes(),
101                                                 cryptlib_py.CRYPT_UNUSED)
102                c2 = cryptlib_py.cryptImportCert(cert2.writeBytes(),
103                                                 cryptlib_py.CRYPT_UNUSED)
104                try:
105                    cryptlib_py.cryptCheckCert(c1, c2)
106                except:
107                    return False
108                cryptlib_py.cryptDestroyCert(c1)
109                c1 = None
110                cryptlib_py.cryptDestroyCert(c2)
111                c2 = None
112
113            #If the last certificate is one of the root certificates, we're
114            #done.
115            if self.x509List[-1].getFingerprint() in rootFingerprints:
116                return True
117
118            #Otherwise, find a root certificate that the last certificate
119            #chains to, and validate them.
120            lastC = cryptlib_py.cryptImportCert(self.x509List[-1].writeBytes(),
121                                                cryptlib_py.CRYPT_UNUSED)
122            for rootCert in x509TrustList:
123                rootC = cryptlib_py.cryptImportCert(rootCert.writeBytes(),
124                                                    cryptlib_py.CRYPT_UNUSED)
125                if self._checkChaining(lastC, rootC):
126                    try:
127                        cryptlib_py.cryptCheckCert(lastC, rootC)
128                        return True
129                    except:
130                        return False
131            return False
132        finally:
133            if not (c1 is None):
134                cryptlib_py.cryptDestroyCert(c1)
135            if not (c2 is None):
136                cryptlib_py.cryptDestroyCert(c2)
137            if not (lastC is None):
138                cryptlib_py.cryptDestroyCert(lastC)
139            if not (rootC is None):
140                cryptlib_py.cryptDestroyCert(rootC)
141
142
143
144    def _checkChaining(self, lastC, rootC):
145        import cryptlib_py
146        import array
147        def compareNames(name):
148            try:
149                length = cryptlib_py.cryptGetAttributeString(lastC, name, None)
150                lastName = array.array('B', [0] * length)
151                cryptlib_py.cryptGetAttributeString(lastC, name, lastName)
152                lastName = lastName.tostring()
153            except cryptlib_py.CryptException, e:
154                if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
155                    lastName = None
156            try:
157                length = cryptlib_py.cryptGetAttributeString(rootC, name, None)
158                rootName = array.array('B', [0] * length)
159                cryptlib_py.cryptGetAttributeString(rootC, name, rootName)
160                rootName = rootName.tostring()
161            except cryptlib_py.CryptException, e:
162                if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
163                    rootName = None
164
165            return lastName == rootName
166
167        cryptlib_py.cryptSetAttribute(lastC,
168                                      cryptlib_py.CRYPT_CERTINFO_ISSUERNAME,
169                                      cryptlib_py.CRYPT_UNUSED)
170
171        if not compareNames(cryptlib_py.CRYPT_CERTINFO_COUNTRYNAME):
172            return False
173        if not compareNames(cryptlib_py.CRYPT_CERTINFO_LOCALITYNAME):
174            return False
175        if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONNAME):
176            return False
177        if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONALUNITNAME):
178            return False
179        if not compareNames(cryptlib_py.CRYPT_CERTINFO_COMMONNAME):
180            return False
181        return True