X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Ftrust%2Fcertificate.py;h=ac643669f8208dde28702571840edb4abd561da4;hb=8cbccf66e8c89243e1d55c0e71841cebb6c98006;hp=55d46d3fba2f58b46017d03270ac8a0f8b58caa8;hpb=04a3f20dc71bf8b3f96b1e3172623aa346a638a7;p=sfa.git diff --git a/sfa/trust/certificate.py b/sfa/trust/certificate.py index 55d46d3f..ac643669 100644 --- a/sfa/trust/certificate.py +++ b/sfa/trust/certificate.py @@ -35,6 +35,15 @@ ## # +# Notes on using the openssl command line +# +# for verifying the chain in a gid, assuming it is split into pieces p1.pem p2.pem p3.pem +# you can use openssl to verify the chain using this command +# openssl verify -verbose -CAfile <(cat p2.pem p3.pem) p1.pem +# also you can use sfax509 to invoke openssl x509 on all parts of the gid +# + + from __future__ import print_function import functools @@ -572,6 +581,17 @@ class Certificate: message += "]" return message + def pretty_chain(self): + message = "{}".format(self.x509.get_subject()) + parent = self.parent + while parent: + message += "->{}".format(parent.x509.get_subject()) + parent = parent.parent + return message + + def pretty_name(self): + return self.get_filename() or self.pretty_chain() + ## # Get the public key of the certificate. # @@ -623,7 +643,6 @@ class Certificate: # @param value string containing value of the extension def add_extension(self, name, critical, value): - import M2Crypto oldExtVal = None try: oldExtVal = self.get_extension(name) @@ -674,13 +693,13 @@ class Certificate: # the X509 subject_alt_name extension. Set_data can only be called once, due # to limitations in the underlying library. - def set_data(self, str, field='subjectAltName'): + def set_data(self, string, field='subjectAltName'): # pyOpenSSL only allows us to add extensions, so if we try to set the # same extension more than once, it will not work if field in self.data: raise Exception("Cannot set {} more than once".format(field)) - self.data[field] = str - self.add_extension(field, 0, str) + self.data[field] = string + self.add_extension(field, 0, string) ## # Return the data string that was previously set with set_data @@ -720,9 +739,17 @@ class Certificate: m2x509 = M2Crypto.X509.load_cert_string(self.save_to_string()) m2pubkey = pubkey.get_m2_pubkey() # verify it - # verify returns -1 or 0 on failure depending on how serious the - # error conditions are - return m2x509.verify(m2pubkey) == 1 + # https://www.openssl.org/docs/man1.1.0/crypto/X509_verify.html + # verify returns + # 1 if it checks out + # 0 if if does not + # -1 if it could not be checked 'for some reason' + m2result = m2x509.verify(m2pubkey) + result = m2result == 1 + if debug_verify_chain: + logger.debug("Certificate.verify: <- {} (m2={}) ({} x {})" + .format(result, m2result, self.pretty_cert(), m2pubkey)) + return result # XXX alternatively, if openssl has been patched, do the much simpler: # try: @@ -745,6 +772,7 @@ class Certificate: # @param cert certificate object def is_signed_by_cert(self, cert): + logger.debug("Certificate.is_signed_by_cert -> invoking verify") k = cert.get_pubkey() result = self.verify(k) return result @@ -789,6 +817,7 @@ class Certificate: # the public key contained in it's parent. The chain is recursed # until a certificate is found that is signed by a trusted root. + logger.debug("Certificate.verify_chain {}".format(self.pretty_name())) # verify expiration time if self.x509.has_expired(): if debug_verify_chain: @@ -797,13 +826,15 @@ class Certificate: raise CertExpired(self.pretty_cert(), "client cert") # if this cert is signed by a trusted_cert, then we are set - for trusted_cert in trusted_certs: + for i, trusted_cert in enumerate(trusted_certs, 1): + logger.debug("Certificate.verify_chain - trying trusted #{} : {}" + .format(i, trusted_cert.pretty_name())) if self.is_signed_by_cert(trusted_cert): # verify expiration of trusted_cert ? if not trusted_cert.x509.has_expired(): if debug_verify_chain: logger.debug("verify_chain: YES. Cert {} signed by trusted cert {}" - .format(self.pretty_cert(), trusted_cert.pretty_cert())) + .format(self.pretty_name(), trusted_cert.pretty_name())) return trusted_cert else: if debug_verify_chain: @@ -811,28 +842,31 @@ class Certificate: "but that signer is expired..." .format(self.pretty_cert(), trusted_cert.pretty_cert())) raise CertExpired("{} signer trusted_cert {}" - .format(self.pretty_cert(), trusted_cert.pretty_cert())) + .format(self.pretty_name(), trusted_cert.pretty_name())) + else: + logger.debug("verify_chain: not a direct descendant of a trusted root". + format(self.pretty_name(), trusted_cert)) # if there is no parent, then no way to verify the chain if not self.parent: if debug_verify_chain: logger.debug("verify_chain: NO. {} has no parent " "and issuer {} is not in {} trusted roots" - .format(self.pretty_cert(), self.get_issuer(), len(trusted_certs))) + .format(self.pretty_name(), self.get_issuer(), len(trusted_certs))) raise CertMissingParent("{}: Issuer {} is not one of the {} trusted roots, " "and cert has no parent." - .format(self.pretty_cert(), self.get_issuer(), len(trusted_certs))) + .format(self.pretty_name(), self.get_issuer(), len(trusted_certs))) # if it wasn't signed by the parent... if not self.is_signed_by_cert(self.parent): if debug_verify_chain: - logger.debug("verify_chain: NO. {} is not signed by parent {}, but by {}" - .format(self.pretty_cert(), - self.parent.pretty_cert(), - self.get_issuer())) + logger.debug("verify_chain: NO. {} is not signed by parent {}" + .format(self.pretty_name(), + self.parent.pretty_name())) + self.save_to_file("/tmp/xxx-capture.pem", save_parents=True) raise CertNotSignedByParent("{}: Parent {}, issuer {}" - .format(self.pretty_cert(), - self.parent.pretty_cert(), + .format(self.pretty_name(), + self.parent.pretty_name(), self.get_issuer())) # Confirm that the parent is a CA. Only CAs can be trusted as @@ -843,14 +877,14 @@ class Certificate: # 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 parent {} is not a CA" - .format(self.pretty_cert(), self.parent.pretty_cert())) + .format(self.pretty_name(), self.parent.pretty_name())) raise CertNotSignedByParent("{}: Parent {} not a CA" - .format(self.pretty_cert(), self.parent.pretty_cert())) + .format(self.pretty_name(), self.parent.pretty_name())) # if the parent isn't verified... if debug_verify_chain: logger.debug("verify_chain: .. {}, -> verifying parent {}" - .format(self.pretty_cert(), self.parent.pretty_cert())) + .format(self.pretty_name(),self.parent.pretty_name())) self.parent.verify_chain(trusted_certs) return