summaryrefslogtreecommitdiffstats
path: root/src/lib/tlslite/X509.py
blob: a47ddcfa2a29b46fb86d7505b73a78647ef4bf56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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