blob: a32d879a3a7aa15686029c2a7d1d901ba13ba85d [file] [log] [blame]
"""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.
@type subject: L{array.array} of unsigned bytes
@ivar subject: The DER-encoded ASN.1 subject distinguished name.
"""
def __init__(self):
self.bytes = createByteArraySequence([])
self.publicKey = None
self.subject = 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 subject
self.subject = tbsCertificateP.getChildBytes(\
subjectPublicKeyInfoIndex - 1)
#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)
return self
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