diff options
Diffstat (limited to 'src/lib/tlslite/X509.py')
-rwxr-xr-x | src/lib/tlslite/X509.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/lib/tlslite/X509.py b/src/lib/tlslite/X509.py new file mode 100755 index 000000000..a47ddcfa2 --- /dev/null +++ b/src/lib/tlslite/X509.py @@ -0,0 +1,133 @@ +"""Class representing an X.509 certificate.""" + +from utils.ASN1Parser import ASN1Parser +from utils.cryptomath import * +from utils.keyfactory import _createPublicRSAKey + + +class X509: + """This class represents an X.509 certificate. + + @type bytes: L{array.array} of unsigned bytes + @ivar bytes: The DER-encoded ASN.1 certificate + + @type publicKey: L{tlslite.utils.RSAKey.RSAKey} + @ivar publicKey: The subject public key from the certificate. + """ + + def __init__(self): + self.bytes = createByteArraySequence([]) + self.publicKey = None + + def parse(self, s): + """Parse a PEM-encoded X.509 certificate. + + @type s: str + @param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded + certificate wrapped with "-----BEGIN CERTIFICATE-----" and + "-----END CERTIFICATE-----" tags). + """ + + start = s.find("-----BEGIN CERTIFICATE-----") + end = s.find("-----END CERTIFICATE-----") + if start == -1: + raise SyntaxError("Missing PEM prefix") + if end == -1: + raise SyntaxError("Missing PEM postfix") + s = s[start+len("-----BEGIN CERTIFICATE-----") : end] + + bytes = base64ToBytes(s) + self.parseBinary(bytes) + return self + + def parseBinary(self, bytes): + """Parse a DER-encoded X.509 certificate. + + @type bytes: str or L{array.array} of unsigned bytes + @param bytes: A DER-encoded X.509 certificate. + """ + + if isinstance(bytes, type("")): + bytes = stringToBytes(bytes) + + self.bytes = bytes + p = ASN1Parser(bytes) + + #Get the tbsCertificate + tbsCertificateP = p.getChild(0) + + #Is the optional version field present? + #This determines which index the key is at. + if tbsCertificateP.value[0]==0xA0: + subjectPublicKeyInfoIndex = 6 + else: + subjectPublicKeyInfoIndex = 5 + + #Get the subjectPublicKeyInfo + subjectPublicKeyInfoP = tbsCertificateP.getChild(\ + subjectPublicKeyInfoIndex) + + #Get the algorithm + algorithmP = subjectPublicKeyInfoP.getChild(0) + rsaOID = algorithmP.value + if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: + raise SyntaxError("Unrecognized AlgorithmIdentifier") + + #Get the subjectPublicKey + subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1) + + #Adjust for BIT STRING encapsulation + if (subjectPublicKeyP.value[0] !=0): + raise SyntaxError() + subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:]) + + #Get the modulus and exponent + modulusP = subjectPublicKeyP.getChild(0) + publicExponentP = subjectPublicKeyP.getChild(1) + + #Decode them into numbers + n = bytesToNumber(modulusP.value) + e = bytesToNumber(publicExponentP.value) + + #Create a public key instance + self.publicKey = _createPublicRSAKey(n, e) + + def getFingerprint(self): + """Get the hex-encoded fingerprint of this certificate. + + @rtype: str + @return: A hex-encoded fingerprint. + """ + return sha.sha(self.bytes).hexdigest() + + def getCommonName(self): + """Get the Subject's Common Name from the certificate. + + The cryptlib_py module must be installed in order to use this + function. + + @rtype: str or None + @return: The CN component of the certificate's subject DN, if + present. + """ + import cryptlib_py + import array + c = cryptlib_py.cryptImportCert(self.bytes, cryptlib_py.CRYPT_UNUSED) + name = cryptlib_py.CRYPT_CERTINFO_COMMONNAME + try: + try: + length = cryptlib_py.cryptGetAttributeString(c, name, None) + returnVal = array.array('B', [0] * length) + cryptlib_py.cryptGetAttributeString(c, name, returnVal) + returnVal = returnVal.tostring() + except cryptlib_py.CryptException, e: + if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: + returnVal = None + return returnVal + finally: + cryptlib_py.cryptDestroyCert(c) + + def writeBytes(self): + return self.bytes + + |