/gdata/tlslite/X509CertChain.py

http://radioappz.googlecode.com/ · Python · 181 lines · 131 code · 18 blank · 32 comment · 27 complexity · fdce4a1a010d5ef58ef7f679997f9f9d MD5 · raw file

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