Ejemplo n.º 1
0
def utcparse(input):
    """ Translate a string into a time using dateutil.parser.parse but make sure it's in UTC time and strip
the timezone, so that it's compatible with normal datetime.datetime objects.

For safety this can also handle inputs that are either timestamps, or datetimes
"""
    # prepare the input for the checks below by
    # casting strings ('1327098335') to ints
    if isinstance(input, StringTypes):
        try:
            input = int(input)
        except ValueError:
            pass

    if isinstance (input, datetime.datetime):
        logger.warn ("argument to utcparse already a datetime - doing nothing")
        return input
    elif isinstance (input, StringTypes):
        t = dateutil.parser.parse(input)
        if t.utcoffset() is not None:
            t = t.utcoffset() + t.replace(tzinfo=None)
            # the above line is wrong: you need to substract the utcoffset, not add it!
            # a better way is: t = t.astimezone(pytz.utc)   #only works if not naive 
            raise RuntimeException('unfixed bug in code triggered')
        return t
    elif isinstance (input, (int,float,long)):
        return datetime.datetime.fromtimestamp(input)
    else:
        logger.error("Unexpected type in utcparse [%s]"%type(input))
Ejemplo n.º 2
0
def utcparse(input):
    """ Translate a string into a time using dateutil.parser.parse but make sure it's in UTC time and strip
the timezone, so that it's compatible with normal datetime.datetime objects.

For safety this can also handle inputs that are either timestamps, or datetimes
"""
    # prepare the input for the checks below by
    # casting strings ('1327098335') to ints
    if isinstance(input, StringTypes):
        try:
            input = int(input)
        except ValueError:
            pass

    if isinstance (input, datetime.datetime):
        logger.warn ("argument to utcparse already a datetime - doing nothing")
        return input
    elif isinstance (input, StringTypes):
        t = dateutil.parser.parse(input)
        if t.utcoffset() is not None:
            t = t.utcoffset() + t.replace(tzinfo=None)
        return t
    elif isinstance (input, (int,float,long)):
        return datetime.datetime.fromtimestamp(input)
    else:
        logger.error("Unexpected type in utcparse [%s]"%type(input))
Ejemplo n.º 3
0
def utcparse(input):
    """ Translate a string into a time using dateutil.parser.parse but make sure it's in UTC time and strip
the timezone, so that it's compatible with normal datetime.datetime objects.

For safety this can also handle inputs that are either timestamps, or datetimes
"""
    # prepare the input for the checks below by
    # casting strings ('1327098335') to ints
    if isinstance(input, StringTypes):
        try:
            input = int(input)
        except ValueError:
            pass

    if isinstance(input, datetime.datetime):
        logger.warn("argument to utcparse already a datetime - doing nothing")
        return input
    elif isinstance(input, StringTypes):
        t = dateutil.parser.parse(input)
        if t.utcoffset() is not None:
            t = t.utcoffset() + t.replace(tzinfo=None)
        return t
    elif isinstance(input, (int, float, long)):
        return datetime.datetime.fromtimestamp(input)
    else:
        logger.error("Unexpected type in utcparse [%s]" % type(input))
Ejemplo n.º 4
0
    def verify_chain(self, trusted_certs = None):
        # Verify a chain of certificates. Each certificate must be signed by
        # the public key contained in it's parent. The chain is recursed
        # until a certificate is found that is signed by a trusted root.

        # verify expiration time
        if self.cert.has_expired():
            logger.debug("verify_chain: NO, Certificate %s has expired" % self.get_printable_subject())
            raise CertExpired(self.get_printable_subject(), "client cert")

        # if this cert is signed by a trusted_cert, then we are set
        for trusted_cert in trusted_certs:
            if self.is_signed_by_cert(trusted_cert):
                # verify expiration of trusted_cert ?
                if not trusted_cert.cert.has_expired():
                    logger.debug("verify_chain: YES. Cert %s signed by trusted cert %s"%(
                            self.get_printable_subject(), trusted_cert.get_printable_subject()))
                    return trusted_cert
                else:
                    logger.debug("verify_chain: NO. Cert %s is signed by trusted_cert %s, but that signer is expired..."%(
                            self.get_printable_subject(),trusted_cert.get_printable_subject()))
                    raise CertExpired(self.get_printable_subject()," signer trusted_cert %s"%trusted_cert.get_printable_subject())

        # if there is no parent, then no way to verify the chain
        if not self.parent:
            logger.debug("verify_chain: NO. %s has no parent and issuer %s is not in %d trusted roots"%(self.get_printable_subject(), self.get_issuer(), len(trusted_certs)))
            raise CertMissingParent(self.get_printable_subject() + ": Issuer %s is not one of the %d trusted roots, and cert has no parent." % (self.get_issuer(), len(trusted_certs)))

        # if it wasn't signed by the parent...
        if not self.is_signed_by_cert(self.parent):
            logger.debug("verify_chain: NO. %s is not signed by parent %s, but by %s"%\
                             (self.get_printable_subject(), 
                              self.parent.get_printable_subject(), 
                              self.get_issuer()))
            raise CertNotSignedByParent("%s: Parent %s, issuer %s"\
                                            % (self.get_printable_subject(), 
                                               self.parent.get_printable_subject(),
                                               self.get_issuer()))

        # Confirm that the parent is a CA. Only CAs can be trusted as
        # signers.
        # Note that trusted roots are not parents, so don't need to be
        # CAs.
        # Ugly - cert objects aren't parsed so we need to read the
        # extension and hope there are no other basicConstraints
        if not self.parent.isCA and not (self.parent.get_extension('basicConstraints') == 'CA:TRUE'):
            logger.warn("verify_chain: cert %s's parent %s is not a CA" % \
                            (self.get_printable_subject(), self.parent.get_printable_subject()))
            raise CertNotSignedByParent("%s: Parent %s not a CA" % (self.get_printable_subject(),
                                                                    self.parent.get_printable_subject()))

        # if the parent isn't verified...
        logger.debug("verify_chain: .. %s, -> verifying parent %s"%\
                         (self.get_printable_subject(),self.parent.get_printable_subject()))
        self.parent.verify_chain(trusted_certs)

        return
Ejemplo n.º 5
0
    def sign(self):
        if not self.issuer_privkey:
            logger.warn("Cannot sign credential (no private key)")
            return
        if not self.issuer_gid:
            logger.warn("Cannot sign credential (no issuer gid)")
            return
        doc = parseString(self.get_xml())
        sigs = doc.getElementsByTagName("signatures")[0]

        # Create the signature template to be signed
        signature = Signature()
        signature.set_refid(self.get_refid())
        sdoc = parseString(signature.get_xml())        
        sig_ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
        sigs.appendChild(sig_ele)

        self.xml = doc.toxml()


        # Split the issuer GID into multiple certificates if it's a chain
        chain = GID(filename=self.issuer_gid)
        gid_files = []
        while chain:
            gid_files.append(chain.save_to_random_tmp_file(False))
            if chain.get_parent():
                chain = chain.get_parent()
            else:
                chain = None


        # Call out to xmlsec1 to sign it
        ref = 'Sig_%s' % self.get_refid()
        filename = self.save_to_random_tmp_file()
        command='%s --sign --node-id "%s" --privkey-pem %s,%s %s' \
            % (self.xmlsec_path, ref, self.issuer_privkey, ",".join(gid_files), filename)
#        print 'command',command
        signed = os.popen(command).read()
        os.remove(filename)

        for gid_file in gid_files:
            os.remove(gid_file)

        self.xml = signed

        # This is no longer a legacy credential
        if self.legacy:
            self.legacy = None

        # Update signatures
        self.decode()       
Ejemplo n.º 6
0
    def sign(self):
        if not self.issuer_privkey:
            logger.warn("Cannot sign credential (no private key)")
            return
        if not self.issuer_gid:
            logger.warn("Cannot sign credential (no issuer gid)")
            return
        doc = parseString(self.get_xml())
        sigs = doc.getElementsByTagName("signatures")[0]

        # Create the signature template to be signed
        signature = Signature()
        signature.set_refid(self.get_refid())
        sdoc = parseString(signature.get_xml())        
        sig_ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
        sigs.appendChild(sig_ele)

        self.xml = doc.toxml()


        # Split the issuer GID into multiple certificates if it's a chain
        chain = GID(filename=self.issuer_gid)
        gid_files = []
        while chain:
            gid_files.append(chain.save_to_random_tmp_file(False))
            if chain.get_parent():
                chain = chain.get_parent()
            else:
                chain = None


        # Call out to xmlsec1 to sign it
        ref = 'Sig_%s' % self.get_refid()
        filename = self.save_to_random_tmp_file()
        command='%s --sign --node-id "%s" --privkey-pem %s,%s %s' \
            % (self.xmlsec_path, ref, self.issuer_privkey, ",".join(gid_files), filename)
#        print 'command',command
        signed = os.popen(command).read()
        os.remove(filename)

        for gid_file in gid_files:
            os.remove(gid_file)

        self.xml = signed

        # This is no longer a legacy credential
        if self.legacy:
            self.legacy = None

        # Update signatures
        self.decode()       
Ejemplo n.º 7
0
    def __init__(self, create=False, subject=None, string=None, filename=None):
        self.gidCaller = None
        self.gidObject = None
        self.expiration = None
        self.privileges = None
        self.issuer_privkey = None
        self.issuer_gid = None
        self.issuer_pubkey = None
        self.parent = None
        self.signature = None
        self.xml = None
        self.refid = None
        self.legacy = None

        # Check if this is a legacy credential, translate it if so
        if string or filename:
            if string:
                str = string
            elif filename:
                str = file(filename).read()

            if str.strip().startswith("-----"):
                self.legacy = CredentialLegacy(False, string=str)
                self.translate_legacy(str)
            else:
                self.xml = str
                self.decode()

        # Find an xmlsec1 path
        self.xmlsec_path = ''
        paths = [
            '/usr/bin', '/usr/local/bin', '/bin', '/opt/bin', '/opt/local/bin'
        ]
        for path in paths:
            if os.path.isfile(path + '/' + 'xmlsec1'):
                self.xmlsec_path = path + '/' + 'xmlsec1'
                break
        if not self.xmlsec_path:
            logger.warn(
                "Could not locate binary for xmlsec1 - SFA will be unable to sign stuff !!"
            )
Ejemplo n.º 8
0
    def __init__(self, create=False, subject=None, string=None, filename=None):
        self.gidCaller = None
        self.gidObject = None
        self.expiration = None
        self.privileges = None
        self.issuer_privkey = None
        self.issuer_gid = None
        self.issuer_pubkey = None
        self.parent = None
        self.signature = None
        self.xml = None
        self.refid = None
        self.legacy = None

        # Check if this is a legacy credential, translate it if so
        if string or filename:
            if string:                
                str = string
            elif filename:
                str = file(filename).read()
                
            if str.strip().startswith("-----"):
                self.legacy = CredentialLegacy(False,string=str)
                self.translate_legacy(str)
            else:
                self.xml = str
                self.decode()

        # Find an xmlsec1 path
        self.xmlsec_path = ''
        paths = ['/usr/bin','/usr/local/bin','/bin','/opt/bin','/opt/local/bin']
        for path in paths:
            if os.path.isfile(path + '/' + 'xmlsec1'):
                self.xmlsec_path = path + '/' + 'xmlsec1'
                break
        if not self.xmlsec_path:
            logger.warn("Could not locate binary for xmlsec1 - SFA will be unable to sign stuff !!")
Ejemplo n.º 9
0
    def encode(self):
        # Create the XML document
        doc = Document()
        signed_cred = doc.createElement("signed-credential")

# Declare namespaces
# Note that credential/policy.xsd are really the PG schemas
# in a PL namespace.
# Note that delegation of credentials between the 2 only really works
# cause those schemas are identical.
# Also note these PG schemas talk about PG tickets and CM policies.
        signed_cred.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
        signed_cred.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.planet-lab.org/resources/sfa/credential.xsd")
        signed_cred.setAttribute("xsi:schemaLocation", "http://www.planet-lab.org/resources/sfa/ext/policy/1 http://www.planet-lab.org/resources/sfa/ext/policy/1/policy.xsd")

# PG says for those last 2:
#        signed_cred.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.protogeni.net/resources/credential/credential.xsd")
#        signed_cred.setAttribute("xsi:schemaLocation", "http://www.protogeni.net/resources/credential/ext/policy/1 http://www.protogeni.net/resources/credential/ext/policy/1/policy.xsd")

        doc.appendChild(signed_cred)  
        
        # Fill in the <credential> bit        
        cred = doc.createElement("credential")
        cred.setAttribute("xml:id", self.get_refid())
        signed_cred.appendChild(cred)
        append_sub(doc, cred, "type", "privilege")
        append_sub(doc, cred, "serial", "8")
        append_sub(doc, cred, "owner_gid", self.gidCaller.save_to_string())
        append_sub(doc, cred, "owner_urn", self.gidCaller.get_urn())
        append_sub(doc, cred, "target_gid", self.gidObject.save_to_string())
        append_sub(doc, cred, "target_urn", self.gidObject.get_urn())
        append_sub(doc, cred, "uuid", "")
        if not self.expiration:
            self.set_expiration(datetime.datetime.utcnow() + datetime.timedelta(seconds=DEFAULT_CREDENTIAL_LIFETIME))
        self.expiration = self.expiration.replace(microsecond=0)
        append_sub(doc, cred, "expires", self.expiration.isoformat())
        privileges = doc.createElement("privileges")
        cred.appendChild(privileges)

        if self.privileges:
            rights = self.get_privileges()
            for right in rights.rights:
                priv = doc.createElement("privilege")
                append_sub(doc, priv, "name", right.kind)
                append_sub(doc, priv, "can_delegate", str(right.delegate).lower())
                privileges.appendChild(priv)

        # Add the parent credential if it exists
        if self.parent:
            sdoc = parseString(self.parent.get_xml())
            # If the root node is a signed-credential (it should be), then
            # get all its attributes and attach those to our signed_cred
            # node.
            # Specifically, PG and PLadd attributes for namespaces (which is reasonable),
            # and we need to include those again here or else their signature
            # no longer matches on the credential.
            # We expect three of these, but here we copy them all:
#        signed_cred.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
# and from PG (PL is equivalent, as shown above):
#        signed_cred.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.protogeni.net/resources/credential/credential.xsd")
#        signed_cred.setAttribute("xsi:schemaLocation", "http://www.protogeni.net/resources/credential/ext/policy/1 http://www.protogeni.net/resources/credential/ext/policy/1/policy.xsd")

            # HOWEVER!
            # PL now also declares these, with different URLs, so
            # the code notices those attributes already existed with
            # different values, and complains.
            # This happens regularly on delegation now that PG and
            # PL both declare the namespace with different URLs.
            # If the content ever differs this is a problem,
            # but for now it works - different URLs (values in the attributes)
            # but the same actual schema, so using the PG schema
            # on delegated-to-PL credentials works fine.

            # Note: you could also not copy attributes
            # which already exist. It appears that both PG and PL
            # will actually validate a slicecred with a parent
            # signed using PG namespaces and a child signed with PL
            # namespaces over the whole thing. But I don't know
            # if that is a bug in xmlsec1, an accident since
            # the contents of the schemas are the same,
            # or something else, but it seems odd. And this works.
            parentRoot = sdoc.documentElement
            if parentRoot.tagName == "signed-credential" and parentRoot.hasAttributes():
                for attrIx in range(0, parentRoot.attributes.length):
                    attr = parentRoot.attributes.item(attrIx)
                    # returns the old attribute of same name that was
                    # on the credential
                    # Below throws InUse exception if we forgot to clone the attribute first
                    oldAttr = signed_cred.setAttributeNode(attr.cloneNode(True))
                    if oldAttr and oldAttr.value != attr.value:
                        msg = "Delegating cred from owner %s to %s over %s:\n - Replaced attribute %s value '%s' with '%s'" % (self.parent.gidCaller.get_urn(), self.gidCaller.get_urn(), self.gidObject.get_urn(), oldAttr.name, oldAttr.value, attr.value)
                        logger.warn(msg)
                        #raise CredentialNotVerifiable("Can't encode new valid delegated credential: %s" % msg)

            p_cred = doc.importNode(sdoc.getElementsByTagName("credential")[0], True)
            p = doc.createElement("parent")
            p.appendChild(p_cred)
            cred.appendChild(p)
        # done handling parent credential

        # Create the <signatures> tag
        signatures = doc.createElement("signatures")
        signed_cred.appendChild(signatures)

        # Add any parent signatures
        if self.parent:
            for cur_cred in self.get_credential_list()[1:]:
                sdoc = parseString(cur_cred.get_signature().get_xml())
                ele = doc.importNode(sdoc.getElementsByTagName("Signature")[0], True)
                signatures.appendChild(ele)
                
        # Get the finished product
        self.xml = doc.toxml()
Ejemplo n.º 10
0
    def encode(self):
        # Create the XML document
        doc = Document()
        signed_cred = doc.createElement("signed-credential")

        # Declare namespaces
        # Note that credential/policy.xsd are really the PG schemas
        # in a PL namespace.
        # Note that delegation of credentials between the 2 only really works
        # cause those schemas are identical.
        # Also note these PG schemas talk about PG tickets and CM policies.
        signed_cred.setAttribute("xmlns:xsi",
                                 "http://www.w3.org/2001/XMLSchema-instance")
        signed_cred.setAttribute(
            "xsi:noNamespaceSchemaLocation",
            "http://www.planet-lab.org/resources/sfa/credential.xsd")
        signed_cred.setAttribute(
            "xsi:schemaLocation",
            "http://www.planet-lab.org/resources/sfa/ext/policy/1 http://www.planet-lab.org/resources/sfa/ext/policy/1/policy.xsd"
        )

        # PG says for those last 2:
        #        signed_cred.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.protogeni.net/resources/credential/credential.xsd")
        #        signed_cred.setAttribute("xsi:schemaLocation", "http://www.protogeni.net/resources/credential/ext/policy/1 http://www.protogeni.net/resources/credential/ext/policy/1/policy.xsd")

        doc.appendChild(signed_cred)

        # Fill in the <credential> bit
        cred = doc.createElement("credential")
        cred.setAttribute("xml:id", self.get_refid())
        signed_cred.appendChild(cred)
        append_sub(doc, cred, "type", "privilege")
        append_sub(doc, cred, "serial", "8")
        append_sub(doc, cred, "owner_gid", self.gidCaller.save_to_string())
        append_sub(doc, cred, "owner_urn", self.gidCaller.get_urn())
        append_sub(doc, cred, "target_gid", self.gidObject.save_to_string())
        append_sub(doc, cred, "target_urn", self.gidObject.get_urn())
        append_sub(doc, cred, "uuid", "")
        if not self.expiration:
            self.set_expiration(datetime.datetime.utcnow() +
                                datetime.timedelta(
                                    seconds=DEFAULT_CREDENTIAL_LIFETIME))
        self.expiration = self.expiration.replace(microsecond=0)
        append_sub(doc, cred, "expires", self.expiration.isoformat())
        privileges = doc.createElement("privileges")
        cred.appendChild(privileges)

        if self.privileges:
            rights = self.get_privileges()
            for right in rights.rights:
                priv = doc.createElement("privilege")
                append_sub(doc, priv, "name", right.kind)
                append_sub(doc, priv, "can_delegate",
                           str(right.delegate).lower())
                privileges.appendChild(priv)

        # Add the parent credential if it exists
        if self.parent:
            sdoc = parseString(self.parent.get_xml())
            # If the root node is a signed-credential (it should be), then
            # get all its attributes and attach those to our signed_cred
            # node.
            # Specifically, PG and PLadd attributes for namespaces (which is reasonable),
            # and we need to include those again here or else their signature
            # no longer matches on the credential.
            # We expect three of these, but here we copy them all:
            #        signed_cred.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
            # and from PG (PL is equivalent, as shown above):
            #        signed_cred.setAttribute("xsi:noNamespaceSchemaLocation", "http://www.protogeni.net/resources/credential/credential.xsd")
            #        signed_cred.setAttribute("xsi:schemaLocation", "http://www.protogeni.net/resources/credential/ext/policy/1 http://www.protogeni.net/resources/credential/ext/policy/1/policy.xsd")

            # HOWEVER!
            # PL now also declares these, with different URLs, so
            # the code notices those attributes already existed with
            # different values, and complains.
            # This happens regularly on delegation now that PG and
            # PL both declare the namespace with different URLs.
            # If the content ever differs this is a problem,
            # but for now it works - different URLs (values in the attributes)
            # but the same actual schema, so using the PG schema
            # on delegated-to-PL credentials works fine.

            # Note: you could also not copy attributes
            # which already exist. It appears that both PG and PL
            # will actually validate a slicecred with a parent
            # signed using PG namespaces and a child signed with PL
            # namespaces over the whole thing. But I don't know
            # if that is a bug in xmlsec1, an accident since
            # the contents of the schemas are the same,
            # or something else, but it seems odd. And this works.
            parentRoot = sdoc.documentElement
            if parentRoot.tagName == "signed-credential" and parentRoot.hasAttributes(
            ):
                for attrIx in range(0, parentRoot.attributes.length):
                    attr = parentRoot.attributes.item(attrIx)
                    # returns the old attribute of same name that was
                    # on the credential
                    # Below throws InUse exception if we forgot to clone the attribute first
                    oldAttr = signed_cred.setAttributeNode(
                        attr.cloneNode(True))
                    if oldAttr and oldAttr.value != attr.value:
                        msg = "Delegating cred from owner %s to %s over %s:\n - Replaced attribute %s value '%s' with '%s'" % (
                            self.parent.gidCaller.get_urn(),
                            self.gidCaller.get_urn(), self.gidObject.get_urn(),
                            oldAttr.name, oldAttr.value, attr.value)
                        logger.warn(msg)
                        #raise CredentialNotVerifiable("Can't encode new valid delegated credential: %s" % msg)

            p_cred = doc.importNode(
                sdoc.getElementsByTagName("credential")[0], True)
            p = doc.createElement("parent")
            p.appendChild(p_cred)
            cred.appendChild(p)
        # done handling parent credential

        # Create the <signatures> tag
        signatures = doc.createElement("signatures")
        signed_cred.appendChild(signatures)

        # Add any parent signatures
        if self.parent:
            for cur_cred in self.get_credential_list()[1:]:
                sdoc = parseString(cur_cred.get_signature().get_xml())
                ele = doc.importNode(
                    sdoc.getElementsByTagName("Signature")[0], True)
                signatures.appendChild(ele)

        # Get the finished product
        self.xml = doc.toxml()
Ejemplo n.º 11
0
    def verify_chain(self, trusted_certs=None):
        # Verify a chain of certificates. Each certificate must be signed by
        # the public key contained in it's parent. The chain is recursed
        # until a certificate is found that is signed by a trusted root.

        # verify expiration time
        if self.cert.has_expired():
            logger.debug("verify_chain: NO, Certificate %s has expired" %
                         self.get_printable_subject())
            raise CertExpired(self.get_printable_subject(), "client cert")

        # if this cert is signed by a trusted_cert, then we are set
        for trusted_cert in trusted_certs:
            if self.is_signed_by_cert(trusted_cert):
                # verify expiration of trusted_cert ?
                if not trusted_cert.cert.has_expired():
                    logger.debug(
                        "verify_chain: YES. Cert %s signed by trusted cert %s"
                        % (self.get_printable_subject(),
                           trusted_cert.get_printable_subject()))
                    return trusted_cert
                else:
                    logger.debug(
                        "verify_chain: NO. Cert %s is signed by trusted_cert %s, but that signer is expired..."
                        % (self.get_printable_subject(),
                           trusted_cert.get_printable_subject()))
                    raise CertExpired(
                        self.get_printable_subject(),
                        " signer trusted_cert %s" %
                        trusted_cert.get_printable_subject())

        # if there is no parent, then no way to verify the chain
        if not self.parent:
            logger.debug(
                "verify_chain: NO. %s has no parent and issuer %s is not in %d trusted roots"
                % (self.get_printable_subject(), self.get_issuer(),
                   len(trusted_certs)))
            raise CertMissingParent(
                self.get_printable_subject() +
                ": Issuer %s is not one of the %d trusted roots, and cert has no parent."
                % (self.get_issuer(), len(trusted_certs)))

        # if it wasn't signed by the parent...
        if not self.is_signed_by_cert(self.parent):
            logger.debug("verify_chain: NO. %s is not signed by parent %s, but by %s"%\
                             (self.get_printable_subject(),
                              self.parent.get_printable_subject(),
                              self.get_issuer()))
            raise CertNotSignedByParent("%s: Parent %s, issuer %s"\
                                            % (self.get_printable_subject(),
                                               self.parent.get_printable_subject(),
                                               self.get_issuer()))

        # Confirm that the parent is a CA. Only CAs can be trusted as
        # signers.
        # Note that trusted roots are not parents, so don't need to be
        # CAs.
        # Ugly - cert objects aren't parsed so we need to read the
        # extension and hope there are no other basicConstraints
        if not self.parent.isCA and not (
                self.parent.get_extension('basicConstraints') == 'CA:TRUE'):
            logger.warn("verify_chain: cert %s's parent %s is not a CA" % \
                            (self.get_printable_subject(), self.parent.get_printable_subject()))
            raise CertNotSignedByParent("%s: Parent %s not a CA" %
                                        (self.get_printable_subject(),
                                         self.parent.get_printable_subject()))

        # if the parent isn't verified...
        logger.debug("verify_chain: .. %s, -> verifying parent %s"%\
                         (self.get_printable_subject(),self.parent.get_printable_subject()))
        self.parent.verify_chain(trusted_certs)

        return