Ejemplo n.º 1
0
    def __init__(self,
                 caLoc=os.path.expanduser('~'),
                 csca=None,
                 cscaKey=None,
                 opensslLocation=""):
        """ 
        Initiate the CA infrastructure.
        @caLoc: The location where the openssl config files will be stored 
        @param csca: An existing CSCA Certificate in PEM
        @param cscaKey: The private key of the CSCA in PEM
        @param opensslLocation: The openssl executable location 
        """
        Logger.__init__(self, "CA")

        self._csca = csca
        self._cscaKey = cscaKey
        self._loc = os.path.normpath(caLoc)
        self._loc = os.path.join(self._loc, 'ca')
        self._configFile = os.path.join(self._loc, 'openssl.cfg')

        try:
            os.mkdir(self._loc)
        except:
            pass

        self._openssl = OpenSSL('"' + self._configFile + '"')
        self._openssl.register(self._traceOpenSSl)
Ejemplo n.º 2
0
 def __init__(self, openssl=None):
     Logger.__init__(self, "PA")
     self._content = None
     self._data = None
     if not openssl:
         self._openSSL = OpenSSL()
     else:
         self._openSSL = openssl
Ejemplo n.º 3
0
class CA(Logger):
    def __init__(self,
                 caLoc=os.path.expanduser('~'),
                 csca=None,
                 cscaKey=None,
                 opensslLocation=""):
        """
        Initiate the CA infrastructure.
        @caLoc: The location where the openssl config files will be stored
        @param csca: An existing CSCA Certificate in PEM
        @param cscaKey: The private key of the CSCA in PEM
        @param opensslLocation: The openssl executable location
        """
        Logger.__init__(self, "CA")

        self._csca = csca
        self._cscaKey = cscaKey
        self._loc = os.path.normpath(caLoc)
        self._loc = os.path.join(self._loc, 'ca')
        self._configFile = os.path.join(self._loc, 'openssl.cfg')

        try:
            os.mkdir(self._loc)
        except:
            pass

        self._openssl = OpenSSL('"' + self._configFile + '"')
        self._openssl.register(self._traceOpenSSl)

    def createCSCA(self,
                   size=1024,
                   days=720,
                   dn=DistinguishedName(C="BE", O="Gouv", CN="CSCA-BELGIUM")):
        """
        Create a Country Signing Certificate Authority.
        Return a couple with the x509 as first item and the private key as second item

        The default distinguished name for the CSCA is:
        C=BE
        O=Gouv
        CN=CSCA-BELGIUM

        @param size: The RSA key size in bits
        @param days: The validity period of the certificate
        @param dn: The distinguised name of the certificate
        @return: (x509, privateKey) both in PEM
        """
        self._cscaKey = self._openssl.genRSAprKey(size)
        self._csca = self._genRootHelper(self.cscaKey, days, dn)
        return (self.csca, self.cscaKey)

    def _genRootHelper(self, cscaKey, days, dn):
        try:
            return self._openssl.genRootX509(cscaKey, days, dn)
        except OpenSSLException, msg:
            msg = str(msg)
            self._errorHandler(msg)
            return self._openssl.genRootX509(cscaKey, days, dn)
Ejemplo n.º 4
0
    def __init__(self, iso7816):
        Logger.__init__(self, "SIGN EVERYTHING ATTACK")
        self._iso7816 = iso7816

        if type(self._iso7816) != type(Iso7816(None)):
            raise SignEverythingException("The sublayer iso7816 is not available")

        self._iso7816.rstConnection()

        self._bac = bac.BAC(iso7816)
        self._openssl = OpenSSL()
Ejemplo n.º 5
0
class CA(Logger):
    def __init__(self, caLoc=os.path.expanduser('~'), csca=None, cscaKey=None, opensslLocation=""):
        """
        Initiate the CA infrastructure.
        @caLoc: The location where the openssl config files will be stored
        @param csca: An existing CSCA Certificate in PEM
        @param cscaKey: The private key of the CSCA in PEM
        @param opensslLocation: The openssl executable location
        """
        Logger.__init__(self, "CA")

        self._csca = csca
        self._cscaKey = cscaKey
        self._loc = os.path.normpath(caLoc)
        self._loc = os.path.join(self._loc, 'ca')
        self._configFile = os.path.join(self._loc, 'openssl.cfg')

        try:
            os.mkdir(self._loc)
        except:
            pass

        self._openssl = OpenSSL('"' + self._configFile + '"')
        self._openssl.register(self._traceOpenSSl)

    def createCSCA(self, size=1024, days=720, dn=DistinguishedName(C="BE", O="Gouv", CN="CSCA-BELGIUM")):
        """
        Create a Country Signing Certificate Authority.
        Return a couple with the x509 as first item and the private key as second item

        The default distinguished name for the CSCA is:
        C=BE
        O=Gouv
        CN=CSCA-BELGIUM

        @param size: The RSA key size in bits
        @param days: The validity period of the certificate
        @param dn: The distinguised name of the certificate
        @return: (x509, privateKey) both in PEM
        """
        self._cscaKey = self._openssl.genRSAprKey(size)
        self._csca = self._genRootHelper(self.cscaKey, days, dn)
        return (self.csca, self.cscaKey)

    def _genRootHelper(self, cscaKey, days, dn):
        try:
            return self._openssl.genRootX509(cscaKey, days, dn)
        except OpenSSLException, msg:
            msg = str(msg)
            self._errorHandler(msg)
            return self._openssl.genRootX509(cscaKey, days, dn)
 def __init__(self, openssl=None):
     Logger.__init__(self, "PA")
     self._content = None
     self._data = None
     if not openssl:
         self._openSSL = OpenSSL()
     else:
         self._openSSL = openssl
    def __init__(self, iso7816, openssl=None):
        """
        @param iso7816: a valid iso7816 object
        @type iso7816: doc9303  
        """
        Logger.__init__(self, "AA")
        self._iso7816 = iso7816
        if not openssl:
            self._openssl = OpenSSL()
        else:
            self._openssl = openssl
        self._openssl.register(self.log)
        self.RND_IFD = None
        self.F = None
        self.T = None
        self.decryptedSignature = None
        self.D = None
        self.D_ = None
        self.M1 = None
        self.M_ = None

        self._dg15 = None
Ejemplo n.º 8
0
class SODCreation(Creation):
    def __init__(self):
        self._dgc = DataGroupFileCreation(converter.toTAG("SecurityData"))
        self._hashAlgo = "sha1"
        self._openssl = OpenSSL()

    def create(self, ds, dsKey, dgs):
        sodContent = self._createSODContent(dgs)
        self._dgc.body = self._openssl.signData(sodContent, ds, dsKey)

        return SOD(self._dgc).parse()

    def _createSODContent(self, dgs):
        self._hashAlgo = "sha1"
        hashes = self._calculateHashes(dgs)

        lds = LDSSecurityObject()
        lds.setComponentByName('version', 0)

        ai = AlgorithmIdentifier()
        ai.setComponentByName('algorithm',
                              ObjectIdentifier(OIDrevert[self._hashAlgo]))
        ai.setComponentByName('parameters', Null(''))
        lds.setComponentByName('hashAlgorithm', ai)

        dghv = DataGroupHashValues()

        keys = list(hashes.keys())
        keys.sort()
        cpt = 0
        for hashNb in keys:
            dgh = DataGroupHash()
            dgh.setComponentByName('dataGroupNumber', Integer(hashNb))
            dgh.setComponentByName('dataGroupHashValue',
                                   OctetString(hashes[hashNb]))
            dghv.setComponentByPosition(cpt, dgh)
            cpt += 1

        lds.setComponentByName('dataGroupHashValues', dghv)

        return encoder.encode(lds)

    def _calculateHashes(self, dgs):
        hashes = {}
        hashAlgo = eval(self._hashAlgo)
        for dg in dgs:
            if dg.tag != '60' and dg.tag != '77':
                res = hashAlgo(dg.file)
                hashes[converter.toOrder(dg.tag)] = res.digest()

        return hashes
Ejemplo n.º 9
0
class SODCreation(Creation):
    def __init__(self):
        self._dgc = DataGroupFileCreation(converter.toTAG("SecurityData"))
        self._hashAlgo = "sha1"
        self._openssl = OpenSSL()

    def create(self, ds, dsKey, dgs):
        sodContent = self._createSODContent(dgs)
        self._dgc.body = self._openssl.signData(sodContent, ds, dsKey)

        return SOD(self._dgc).parse()

    def _createSODContent(self, dgs):
        self._hashAlgo = "sha1"
        hashes = self._calculateHashes(dgs)

        lds = LDSSecurityObject()
        lds.setComponentByName("version", 0)

        ai = AlgorithmIdentifier()
        ai.setComponentByName("algorithm", ObjectIdentifier(OIDrevert[self._hashAlgo]))
        ai.setComponentByName("parameters", Null(""))
        lds.setComponentByName("hashAlgorithm", ai)

        dghv = DataGroupHashValues()

        keys = hashes.keys()
        keys.sort()
        cpt = 0
        for hashNb in keys:
            dgh = DataGroupHash()
            dgh.setComponentByName("dataGroupNumber", Integer(hashNb))
            dgh.setComponentByName("dataGroupHashValue", OctetString(hashes[hashNb]))
            dghv.setComponentByPosition(cpt, dgh)
            cpt += 1

        lds.setComponentByName("dataGroupHashValues", dghv)

        return encoder.encode(lds)

    def _calculateHashes(self, dgs):
        hashes = {}
        hashAlgo = eval(self._hashAlgo)
        for dg in dgs:
            if dg.tag != "60" and dg.tag != "77":
                res = hashAlgo(dg.file)
                hashes[converter.toOrder(dg.tag)] = res.digest()

        return hashes
    def __init__(self, iso7816, openssl=None):
        """
        @param iso7816: a valid iso7816 object
        @type iso7816: doc9303
        """
        Logger.__init__(self, "AA")
        self._iso7816 = iso7816
        if not openssl:
            self._openssl = OpenSSL()
        else:
            self._openssl = openssl

        self.RND_IFD = None
        self.F = None
        self.T = None
        self.decryptedSignature = None
        self.D = None
        self.D_ = None
        self.M1 = None
        self.M_ = None

        self._dg15 = None
Ejemplo n.º 11
0
    def __init__(self, caLoc=os.path.expanduser('~'), csca=None, cscaKey=None, opensslLocation=""):
        """
        Initiate the CA infrastructure.
        @caLoc: The location where the openssl config files will be stored
        @param csca: An existing CSCA Certificate in PEM
        @param cscaKey: The private key of the CSCA in PEM
        @param opensslLocation: The openssl executable location
        """
        Logger.__init__(self, "CA")

        self._csca = csca
        self._cscaKey = cscaKey
        self._loc = os.path.normpath(caLoc)
        self._loc = os.path.join(self._loc, 'ca')
        self._configFile = os.path.join(self._loc, 'openssl.cfg')

        try:
            os.mkdir(self._loc)
        except:
            pass

        self._openssl = OpenSSL('"' + self._configFile + '"')
        self._openssl.register(self._traceOpenSSl)
class ActiveAuthentication(Logger):
    """
    This class implements the Active Authentication protocol.
    The main method is I{executeAA} that returns True if the verification is ok or False.
    """
    def __init__(self, iso7816, openssl=None):
        """
        @param iso7816: a valid iso7816 object
        @type iso7816: doc9303
        """
        Logger.__init__(self, "AA")
        self._iso7816 = iso7816
        if not openssl:
            self._openssl = OpenSSL()
        else:
            self._openssl = openssl

        self.RND_IFD = None
        self.F = None
        self.T = None
        self.decryptedSignature = None
        self.D = None
        self.D_ = None
        self.M1 = None
        self.M_ = None

        self._dg15 = None

    def executeAA(self, dg15):
        """
        Perform the Active Authentication protocol.
        Work only with RSA, modulus length of 1024 and with SHA1.

        @param dg15: An initialized dataGroup15 object
        @type dg15: dataGroup15
        @return: True if the authentication succeeded, else False.
        @rtype: Boolean
        @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported).
        @raise ActiveAuthenticationException: If the parameter is not set or is invalid.
        @raise ActiveAuthenticationException: If OpenSSL is not installed.
        @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15.
        @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified.
        """
        self._dg15 = dg15

        self.RND_IFD = self._genRandom(8)
        self.signature = self._getSignature(self.RND_IFD)
        self.F = self._decryptSignature(dg15.body, self.signature)

        (hash, hashSize, offset) = self._getHashAlgo(self.F)
        self.D = self._extractDigest(self.F, hashSize, offset)
        self.M1 = self._extractM1(self.F, hashSize, offset)

        self.M_ = self.M1 + self.RND_IFD

        self.log("Concatenate M1 with known M2")
        self.log("\tM*: " + binToHexRep(self.M_))

        self.D_ = self._hash(hash, self.M_)

        self.log("Compare D and D*")
        self.log("\t" + str(self.D == self.D_))

        return self.D == self.D_

    def _genRandom(self, size):
        rnd_ifd = os.urandom(size)
        self.log("Generate an 8 byte random")
        self.log("\tRND.IFD: " + binToHexRep(rnd_ifd))
        return rnd_ifd

    def _getSignature(self, rnd_ifd):
        return self._iso7816.internalAuthentication(rnd_ifd)

    def getPubKey(self, dg15):
        """
        Retrieve the public key in PEM format from the dataGroup15

        @return: A PEM representation of the public key
        @rtype: A string
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or is invalid.
        @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed?
        """

        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException("The parameter type is not valid, must be a dataGroup15 object")

        return self._openssl.retrieveRsaPubKey(dg15.body)

    def _decryptSignature(self, pubK, signature):
        data = self._openssl.retrieveSignedData(pubK, signature)
        self.log("Decrypt the signature with the public key")
        self.log("\tF: " + binToHexRep(data))

        return data

    def _hash(self, hash, data):
        digest = hash(data).digest()

        self.log("Calculate digest of M*")
        self.log("\tD*: " + binToHexRep(digest))

        return digest

    def _getHashAlgo(self, sig):
        hash = None
        offset = None
        hashSize = None

        if sig[-1] == hexRepToBin("BC"):
            self.T = sig[-1]
            hash = sha1
            offset = -1
        elif sig[-1] == hexRepToBin("CC"):
            self.T = sig[-2]
            #hash = The algorithm corresponding to the algo designed by T
            offset = -2
        else:
            raise ActiveAuthenticationException("Unknow hash algorithm")

        self.log("Determine hash algorithm by trailer T*")
        self.log("\tT: " + binToHexRep(self.T))

        #Find out the hash size
        hashSize = len(hash("test").digest())

        return (hash, hashSize, offset)

    def _extractDigest(self, sig, hashSize, offset):
        digest = sig[offset - hashSize:offset]

        self.log("Extract digest:")
        self.log("\tD: " + binToHexRep(digest))

        return digest

    def _extractM1(self, sig, hashSize, offset):
        M1 = sig[1:offset - hashSize]

        self.log("Extract M1:")
        self.log("\tM1: " + binToHexRep(M1))

        return M1

    def __str__(self):
        spec = self._asn1Parse()
        return spec.prettyPrint()

    def algorithm(self, dg15):
        """
        Return the algorithm name used to store the signature
        @return: A string from the OID dictionary.
        @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration.
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or is invalid.
        """
        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException("The parameter type is not valid, must be a dataGroup15 object")
        algo = ""
        try:
            spec = self._asn1Parse()
            algo = spec.getComponentByName('algorithm').getComponentByName('algorithm').prettyPrint()
            return OID[algo]
        except KeyError:
            raise ActiveAuthenticationException("Unsupported algorithm: " + algo)
        except Exception, msg:
            raise ActiveAuthenticationException("Active Authentication not supported: ", msg)
class ActiveAuthentication(Logger):
    """  
    This class implement the Active Authentication protocol.
    The main method is I{executeAA} that return True is the verification is ok or False.
    """
    def __init__(self, iso7816, openssl=None):
        """
        @param iso7816: a valid iso7816 object
        @type iso7816: doc9303  
        """
        Logger.__init__(self, "AA")
        self._iso7816 = iso7816
        if not openssl:
            self._openssl = OpenSSL()
        else:
            self._openssl = openssl
        self._openssl.register(self.log)
        self.RND_IFD = None
        self.F = None
        self.T = None
        self.decryptedSignature = None
        self.D = None
        self.D_ = None
        self.M1 = None
        self.M_ = None

        self._dg15 = None

    def executeAA(self, dg15, EC=False, hash=sha256):
        """
        Perform the Active Authentication protocol.
        Work only with RSA, modulus length of 1024 and with SHA1.
        
        @param dg15: A initialized dataGroup15 object
        @type dg15: dataGroup15 
        @param EC: True if ECDSA is used y the passport
        @param hash: hash function (default: sha256)
        @return: True if the authentication succeed, else False.
        @rtype: Boolean
        @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported). 
        @raise ActiveAuthenticationException: If the parameter is not set or invalid.
        @raise ActiveAuthenticationException: If OpenSSL is not installed.
        @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15.
        @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified.
        """
        self._dg15 = dg15

        self.RND_IFD = self._genRandom(8)
        self.signature = self._getSignature(self.RND_IFD)
        if EC:
            return self._verifyECSignature(dg15.body, self.signature,
                                           self.RND_IFD, hash)
        self.F = self._decryptSignature(dg15.body, self.signature)
        (hash, hashSize, offset) = self._getHashAlgo(self.F)
        self.D = self._extractDigest(self.F, hashSize, offset)
        self.M1 = self._extractM1(self.F, hashSize, offset)

        self.M_ = self.M1 + self.RND_IFD

        self.log("Concatenate M1 with known M2")
        self.log("\tM*: " + binToHexRep(self.M_))

        self.D_ = self._hash(hash, self.M_)

        self.log("Compare D and D*")
        self.log("\t" + str(self.D == self.D_))

        return self.D == self.D_

    def _genRandom(self, size):
        rnd_ifd = os.urandom(size)
        self.log("Generate an 8 byte random")
        self.log("\tRND.IFD: " + binToHexRep(rnd_ifd))
        return rnd_ifd

    def _getSignature(self, rnd_ifd):
        return self._iso7816.internalAuthentication(rnd_ifd)

    def getPubKey(self, dg15):
        """  
        Retrieve the public key in PEM format from the dataGroup15
        
        @return: A PEM reprensation of the public key
        @rtype: A string
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid.
        @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed?
        """

        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException(
                "The parameter type is not valid, must be a dataGroup15 object"
            )

        try:
            return self._openssl.retrieveRsaPubKey(dg15.body)
        except OpenSSLException:
            return self._openssl.retrieveECPubKey(dg15.body)

    def _verifyECSignature(self, pubK, signature, challenge, hash):

        # Generate hash of the challenge
        challenge = sha256(challenge).digest()

        # Signature is returned in plain format by the card (big endian)
        # It must be converted to ASN.1 to be usable by OpenSSL
        # See https://crypto.stackexchange.com/questions/57731/ecdsa-signature-rs-to-asn1-der-encoding-question
        sig_rec = ECSignatureRecord()
        l = int(len(signature) / 2)
        sig_rec['r'] = int.from_bytes(signature[:l], 'big')
        sig_rec['s'] = int.from_bytes(signature[-l:], 'big')
        sig_der = encoder.encode(sig_rec)

        status = self._openssl.verifyECSignature(pubK, sig_der, challenge)
        self.log("Verify the EC signature with the public key")
        self.log("\tStatus: " + str(status))
        return status

    def _decryptSignature(self, pubK, signature):

        data = self._openssl.retrieveSignedData(pubK, signature)
        self.log("Decrypt the signature with the public key")
        self.log("\tF: " + binToHexRep(data))

        return data

    def _hash(self, hash, data):
        digest = hash(data).digest()

        self.log("Calculate digest of M*")
        self.log("\tD*: " + binToHexRep(digest))

        return digest

    def _getHashAlgo(self, sig):
        hash = None
        offset = None
        hashSize = None

        if sig[-1] == 0xBC:
            self.T = sig[-1]
            hash = sha1
            offset = -1
        elif sig[-1] == 0xCC:
            self.T = sig[-2]
            #hash = The algorithm corresponding to the algo designed by T
            offset = -2
        else:
            raise ActiveAuthenticationException("Unknow hash algorithm")

        self.log("Determine hash algorithm by trailer T*")
        self.log("\tT: " + binToHexRep(self.T))

        #Find out the hash size
        hashSize = len(hash(b"test").digest())

        return (hash, hashSize, offset)

    def _extractDigest(self, sig, hashSize, offset):
        digest = sig[offset - hashSize:offset]

        self.log("Extract digest:")
        self.log("\tD: " + binToHexRep(digest))

        return digest

    def _extractM1(self, sig, hashSize, offset):
        M1 = sig[1:offset - hashSize]

        self.log("Extract M1:")
        self.log("\tM1: " + binToHexRep(M1))

        return M1

    def __str__(self):
        spec = self._asn1Parse()
        return spec.prettyPrint()

    def algorithm(self, dg15):
        """
        Return the algorithm name used to store the signature
        @return: A string from the OID dictionnary.
        @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration.
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid.
        """
        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException(
                "The parameter type is not valid, must be a dataGroup15 object"
            )
        algo = ""
        try:
            spec = self._asn1Parse()
            algo = spec.getComponentByName('algorithm').getComponentByName(
                'algorithm').prettyPrint()
            return OID[algo]
        except KeyError:
            raise ActiveAuthenticationException("Unsupported algorithm: " +
                                                algo)
        except Exception as msg:
            raise ActiveAuthenticationException(
                "Active Authentication not supported: ", msg)

    def _asn1Parse(self):
        if self._dg15 != None:
            certType = SubjectPublicKeyInfo()
            return decoder.decode(self._dg15.body, asn1Spec=certType)[0]
        return ""
class PassiveAuthentication(Logger):

    """
    This class implements the passive authentication protocol.
    The two main methods are I{verifySODandCDS} and I{executePA}. The first verifies the SOD and the CDS and retrieves the relevant dataGroups
    from the LDS, that's why this method must be called before I{executePA} that uses these extracted informations to calculate the hashes.
    Even if the Certificate validation failed, it does not mean that the data could not be retrieved from the LDS.
    """

    def __init__(self, openssl=None):
        Logger.__init__(self, "PA")
        self._content = None
        self._data = None
        if not openssl:
            self._openSSL = OpenSSL()
        else:
            self._openSSL = openssl

    def verifySODandCDS(self, sodObj, CSCADirectory):
        """
        Execute the first part of the Passive Authentication protocol.
            - Read the document signer from the Document Security Object
            - Verify SOD by using Document Signer Public Key (KPuDS).
            - Verify CDS by using the Country Signing CA Public Key (KPuCSCA).
            - Read the relevant Data Groups from the LDS.

        The I{toHash} method of the CSCADirectory object must be called before the passive authentication.
        Once the hashing processing is done, the I{toHash} method does not need to be called again.

        @param sodObj: An initialized security data object
        @type sodObj: A sod object
        @param CSCADirectory: The object representing the CSCA directory.
        @type CSCADirectory: A CAManager object

        @return: True if the DS Certificate is valided

        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise PassiveAuthenticationException: I{CSCADirectory is not set}
        @raise openSSLException: See the openssl documentation
        """

        if CSCADirectory == None:
            raise PassiveAuthenticationException("CSCADirectory is not set")

        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if type(CSCADirectory) != type(CAManager("")):
            raise PassiveAuthenticationException("CSCADirectory must be a CAManager object")

        CDS = self.getCertificate(sodObj)
        if CDS == None:
            #No certificate
            raise PassiveAuthenticationException("The certificate could not be retrieved")

        self._data = self.getSODContent(sodObj)

        self._content = self._readDGfromLDS(self._data)

        return self.verifyDSC(CDS, CSCADirectory.dir)

    def executePA(self, sodObj, dgs):
        """
        Execute the second part of the Passive Authentication protocol
            - Calculate the hashes of the given Data Groups.
            - Compare the calculated hashes with the corresponding hash values in the SOD.

        @param sodObj: An initialized security data object
        @type sodObj: A sod object
        @param dgs: A list of dataGroup objects to verify
        @type dgs: A list of dataGroup
        @return: The dictionary is indexed with the DataGroup name (DG1...DG15) and the value is a boolean: True if the check is ok.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """

#        f = open("/home/jf/CA/sod", "wb")
#        f.write(sodObj.body)
#        f.close()

        if self._data == None:
            self._data = self.getSODContent(sodObj)

#        f = open("/home/jf/CA/sod_content", "wb")
#        f.write(self._data)
#        f.close()

        if self._content == None:
            self._content = self._readDGfromLDS(self._data)

        hashes = self._calculateHashes(dgs)
        return self._compareHashes(hashes)


    def getSODContent(self, sodObj):
        """
        Verify SOD by using Document Signer Public Key (KPuDS))

        @param sodObj: A filled SOD object
        @type sodObj: A sod object
        @return: The data (a binary string) if the verification is ok, else an PassiveAuthentication is raised.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """
        self.log("Verify SOD by using Document Signer Public Key (KPuDS))")

        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if sodObj.body == None:
            raise PassiveAuthenticationException("sodObj object is not initialized")

        return self._openSSL.getPkcs7SignatureContent(sodObj.body)


    def verifyDSC(self, CDS, CSCADirectory):
        """
        Verify CDS by using the Country Signing CA Public Key (KPuCSCA).

        @param CDS: The document signer certificate
        @type CDS: A string formated in PEM
        @param CSCADirectory: The complete path to the directory where the CSCA are. The certificates must first be renamed with the corresponding hash. (See the CAManager.py)
        @type CSCADirectory: A string
        @return: True if the verification is ok
        @raise PassiveAuthenticationException: I{The CDS is not set}: The CDS parameter must be a non-empty string.
        @raise PassiveAuthenticationException: I{The CA is not set}: The CSCADirectory parameter must be a non-empty string.
        @raise openSSLException: See the openssl documentation
        """

        self.log("Verify CDS by using the Country Signing CA Public Key (KPuCSCA). ")

        if not CDS and type(CDS) == type(""):
            raise PassiveAuthenticationException("The CDS is not set")

        if not CSCADirectory and type(CSCADirectory) == type(""):
            raise PassiveAuthenticationException("The CA is not set")

        return self._openSSL.verifyX509Certificate(CDS, CSCADirectory)

    def getCertificate(self, sodObj):
        """
        Retrieve de DocumentSigner certificate out of the SOD.
        @return: A PEM representation of the certificate or None if not present.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """
        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if sodObj.body is None:
            raise PassiveAuthenticationException("sodObj object is not initialized")

        return self._openSSL.retrievePkcs7Certificate(sodObj.body)

    def _readDGfromLDS(self, data):
        """
        Read the relevant Data Groups from the LDS

        @param data: The content of the verified signature.
        @type data:  A binary string
        @return: A dictionary with the parsed data of the signature (version, hashAlgorithm and dataGrouphashValues)
        """
        self.log("Read the relevant Data Groups from the LDS")

        content = {}
        hash = {}

        certType = LDSSecurityObject()
        cert = decoder.decode(data, asn1Spec = certType)[0]

        content['version'] = cert.getComponentByName('version').prettyPrint()
        content['hashAlgorithm'] = cert.getComponentByName('hashAlgorithm').getComponentByName('algorithm').prettyPrint()

        for h in cert.getComponentByName('dataGroupHashValues'):
            hash[h.getComponentByName('dataGroupNumber').prettyPrint()] = h.getComponentByName('dataGroupHashValue')

        content['dataGroupHashValues'] = hash

        return content

    def _calculateHashes(self, dgs):
        """
        Calculate the hashes of the relevant Data Groups, theses presents in the signature.

        @param dgs: A list of dataGroup objects to calculate the hash values.
        @type dgs: A list.
        @return: A dictionary indexed with DG1..DG15 with the calculated hashes of the DGs.
        """
        self.log("Calculate the hashes of the relevant Data Groups")
        hashes = {}
        #Find the hash function from the content dictionary
        hashAlgo = self._getHashAlgorithm()
        for dg in dgs:
            res = hashAlgo(dg.file)
            hashes[converter.toDG(dg.tag)] = res.digest()

        return hashes

    def _compareHashes(self, hashes):
        """
        Compare the calculated hashes with the corresponding hashes present in the SOD.

        @param hashes: A dictionary of hashes to compare with the security object hashes.
        @type hashes: A dictionary
        @return: A dictionary indexed with the DG name (DG1..DG15) and with the result of the hash comparison (True or False, None if the DG is not present in the SOD)
        """
        self.log("Compare the calculated hashes with the corresponding hash values in the SOD")

        res = {}

        for dg in hashes:
            try:
                res[converter.toDG(dg)] = (hashes[dg] == self._content["dataGroupHashValues"][converter.toOther(dg)])
            except KeyError:
                res[converter.toDG(dg)] = None

        return res

    def _getHashAlgorithm(self):
        """
        Return the object corresponding to the hash algorithm used to calculate the hashes present in the SOD.
        @return: The object corresponding to the hash algorithm, or an oidException if not found.
        """
        if self._content is None:
            raise PassiveAuthenticationException("The object is not set. Call init first.")
        return self._getAlgoByOID(self._content['hashAlgorithm'])

    def _getAlgoByOID(self, oid):
        try:
            algo = OID[oid]
            return eval(algo)
        except KeyError:
            raise OIDException("No such algorithm for OID " + str(oid))

    def __str__(self):
        res =  "version: " + self._content["version"] + "\n"
        res += "Hash algorithm: " + OID[self._content["hashAlgorithm"]] + " (" + self._content["hashAlgorithm"] + ")\n"
        res += "Data group hash values: " + "\n"

        for dghv in self._content["dataGroupHashValues"].keys():
            res += "Data group: " + converter.toEF(dghv) + "\n"
            res += "Hash value: " + binToHexRep(self._content["dataGroupHashValues"][dghv]) + "\n"

        return res
Ejemplo n.º 15
0
class PassiveAuthentication(Logger):
    """
    This class implements the passive authentication protocol.
    The two main methods are I{verifySODandCDS} and I{executePA}. The first verifies the SOD and the CDS and retrieves the relevant dataGroups
    from the LDS, that's why this method must be called before I{executePA} that uses these extracted informations to calculate the hashes.
    Even if the Certificate validation failed, it does not mean that the data could not be retrieved from the LDS.
    """
    def __init__(self, openssl=None):
        Logger.__init__(self, "PA")
        self._content = None
        self._data = None
        if not openssl:
            self._openSSL = OpenSSL()
        else:
            self._openSSL = openssl

    def verifySODandCDS(self, sodObj, CSCADirectory):
        """
        Execute the first part of the Passive Authentication protocol.
            - Read the document signer from the Document Security Object
            - Verify SOD by using Document Signer Public Key (KPuDS).
            - Verify CDS by using the Country Signing CA Public Key (KPuCSCA).
            - Read the relevant Data Groups from the LDS.

        The I{toHash} method of the CSCADirectory object must be called before the passive authentication.
        Once the hashing processing is done, the I{toHash} method does not need to be called again.

        @param sodObj: An initialized security data object
        @type sodObj: A sod object
        @param CSCADirectory: The object representing the CSCA directory.
        @type CSCADirectory: A CAManager object

        @return: True if the DS Certificate is valided

        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise PassiveAuthenticationException: I{CSCADirectory is not set}
        @raise openSSLException: See the openssl documentation
        """

        if CSCADirectory == None:
            raise PassiveAuthenticationException("CSCADirectory is not set")

        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if type(CSCADirectory) != type(CAManager("")):
            raise PassiveAuthenticationException(
                "CSCADirectory must be a CAManager object")

        CDS = self.getCertificate(sodObj)
        if CDS == None:
            #No certificate
            raise PassiveAuthenticationException(
                "The certificate could not be retrieved")

        self._data = self.getSODContent(sodObj)

        self._content = self._readDGfromLDS(self._data)

        return self.verifyDSC(CDS, CSCADirectory.dir)

    def executePA(self, sodObj, dgs):
        """
        Execute the second part of the Passive Authentication protocol
            - Calculate the hashes of the given Data Groups.
            - Compare the calculated hashes with the corresponding hash values in the SOD.

        @param sodObj: An initialized security data object
        @type sodObj: A sod object
        @param dgs: A list of dataGroup objects to verify
        @type dgs: A list of dataGroup
        @return: The dictionary is indexed with the DataGroup name (DG1...DG15) and the value is a boolean: True if the check is ok.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """

        #        f = open("/home/jf/CA/sod", "wb")
        #        f.write(sodObj.body)
        #        f.close()

        if self._data == None:
            self._data = self.getSODContent(sodObj)

#        f = open("/home/jf/CA/sod_content", "wb")
#        f.write(self._data)
#        f.close()

        if self._content == None:
            self._content = self._readDGfromLDS(self._data)

        hashes = self._calculateHashes(dgs)
        return self._compareHashes(hashes)

    def getSODContent(self, sodObj):
        """
        Verify SOD by using Document Signer Public Key (KPuDS))

        @param sodObj: A filled SOD object
        @type sodObj: A sod object
        @return: The data (a binary string) if the verification is ok, else an PassiveAuthentication is raised.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """
        self.log("Verify SOD by using Document Signer Public Key (KPuDS))")

        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if sodObj.body == None:
            raise PassiveAuthenticationException(
                "sodObj object is not initialized")

        return self._openSSL.getPkcs7SignatureContent(sodObj.body)

    def verifyDSC(self, CDS, CSCADirectory):
        """
        Verify CDS by using the Country Signing CA Public Key (KPuCSCA).

        @param CDS: The document signer certificate
        @type CDS: A string formated in PEM
        @param CSCADirectory: The complete path to the directory where the CSCA are. The certificates must first be renamed with the corresponding hash. (See the CAManager.py)
        @type CSCADirectory: A string
        @return: True if the verification is ok
        @raise PassiveAuthenticationException: I{The CDS is not set}: The CDS parameter must be a non-empty string.
        @raise PassiveAuthenticationException: I{The CA is not set}: The CSCADirectory parameter must be a non-empty string.
        @raise openSSLException: See the openssl documentation
        """

        self.log(
            "Verify CDS by using the Country Signing CA Public Key (KPuCSCA). "
        )

        if not CDS and type(CDS) == type(""):
            raise PassiveAuthenticationException("The CDS is not set")

        if not CSCADirectory and type(CSCADirectory) == type(""):
            raise PassiveAuthenticationException("The CA is not set")

        return self._openSSL.verifyX509Certificate(CDS, CSCADirectory)

    def getCertificate(self, sodObj):
        """
        Retrieve de DocumentSigner certificate out of the SOD.
        @return: A PEM representation of the certificate or None if not present.
        @raise PassiveAuthenticationException: I{sodObj must be a sod object}: the sodObj parameter must be a sod object.
        @raise PassiveAuthenticationException: I{sodObj object is not initialized}: the sodObj parameter is a sod object, but is not initialized.
        @raise openSSLException: See the openssl documentation
        """
        if type(sodObj) != type(datagroup.SOD(None)):
            raise PassiveAuthenticationException("sodObj must be a sod object")

        if sodObj.body is None:
            raise PassiveAuthenticationException(
                "sodObj object is not initialized")

        return self._openSSL.retrievePkcs7Certificate(sodObj.body)

    def _readDGfromLDS(self, data):
        """
        Read the relevant Data Groups from the LDS

        @param data: The content of the verified signature.
        @type data:  A binary string
        @return: A dictionary with the parsed data of the signature (version, hashAlgorithm and dataGrouphashValues)
        """
        self.log("Read the relevant Data Groups from the LDS")

        content = {}
        hash = {}

        certType = LDSSecurityObject()
        cert = decoder.decode(data, asn1Spec=certType)[0]

        content['version'] = cert.getComponentByName('version').prettyPrint()
        content['hashAlgorithm'] = cert.getComponentByName(
            'hashAlgorithm').getComponentByName('algorithm').prettyPrint()

        for h in cert.getComponentByName('dataGroupHashValues'):
            hash[h.getComponentByName('dataGroupNumber').prettyPrint(
            )] = h.getComponentByName('dataGroupHashValue')

        content['dataGroupHashValues'] = hash

        return content

    def _calculateHashes(self, dgs):
        """
        Calculate the hashes of the relevant Data Groups, theses presents in the signature.

        @param dgs: A list of dataGroup objects to calculate the hash values.
        @type dgs: A list.
        @return: A dictionary indexed with DG1..DG15 with the calculated hashes of the DGs.
        """
        self.log("Calculate the hashes of the relevant Data Groups")
        hashes = {}
        #Find the hash function from the content dictionary
        hashAlgo = self._getHashAlgorithm()
        for dg in dgs:
            res = hashAlgo(dg.file)
            hashes[converter.toDG(dg.tag)] = res.digest()

        return hashes

    def _compareHashes(self, hashes):
        """
        Compare the calculated hashes with the corresponding hashes present in the SOD.

        @param hashes: A dictionary of hashes to compare with the security object hashes.
        @type hashes: A dictionary
        @return: A dictionary indexed with the DG name (DG1..DG15) and with the result of the hash comparison (True or False, None if the DG is not present in the SOD)
        """
        self.log(
            "Compare the calculated hashes with the corresponding hash values in the SOD"
        )

        res = {}

        for dg in hashes:
            try:
                res[converter.toDG(dg)] = (
                    hashes[dg] == self._content["dataGroupHashValues"][
                        converter.toOther(dg)])
            except KeyError:
                res[converter.toDG(dg)] = None

        return res

    def _getHashAlgorithm(self):
        """
        Return the object corresponding to the hash algorithm used to calculate the hashes present in the SOD.
        @return: The object corresponding to the hash algorithm, or an oidException if not found.
        """
        if self._content is None:
            raise PassiveAuthenticationException(
                "The object is not set. Call init first.")
        return self._getAlgoByOID(self._content['hashAlgorithm'])

    def _getAlgoByOID(self, oid):
        try:
            algo = OID[oid]
            return eval(algo)
        except KeyError:
            raise OIDException("No such algorithm for OID " + str(oid))

    def __str__(self):
        res = "version: " + self._content["version"] + "\n"
        res += "Hash algorithm: " + OID[self._content[
            "hashAlgorithm"]] + " (" + self._content["hashAlgorithm"] + ")\n"
        res += "Data group hash values: " + "\n"

        for dghv in self._content["dataGroupHashValues"].keys():
            res += "Data group: " + converter.toEF(dghv) + "\n"
            res += "Hash value: " + binToHexRep(
                self._content["dataGroupHashValues"][dghv]) + "\n"

        return res
 def __init__(self):
     self._dgc = DataGroupFileCreation(converter.toTAG("SecurityData"))
     self._hashAlgo = "sha1"
     self._openssl = OpenSSL()
Ejemplo n.º 17
0
 def __init__(self):
     self._dgc = DataGroupFileCreation(converter.toTAG("SecurityData"))
     self._hashAlgo = "sha1"
     self._openssl = OpenSSL()
Ejemplo n.º 18
0
class SignEverything(Logger):
    """
    This class allows a user to sign any 64bits message.
    The main method is I{sign}
    """
    def __init__(self, iso7816):
        Logger.__init__(self, "SIGN EVERYTHING ATTACK")
        self._iso7816 = iso7816

        if type(self._iso7816) != type(Iso7816(None)):
            raise SignEverythingException("The sublayer iso7816 is not available")

        self._iso7816.rstConnection()

        self._bac = bac.BAC(iso7816)
        self._openssl = OpenSSL()

    def sign(self, message_to_sign="1122334455667788", mrz_value=None):
        """
        Get the signature of a 64bits message from the reader.
        In order to prevent ICC cloning, the passport implements an Active Authentication (AA) security.
        The passport signs the 64bits message sent by the reader thanks to its Private key stored in secured memory.
        This method lets the user decide the 64bits and checks (if MRZ set) with the public key if the message has been signed properly

        @params message_to_sign: 64bits message to sign
        @type message_to_sign: String (16 HEX values)

        @return: A set composed of (The signature, Boolean that states if the signature has been checked)
        """
        validated = False
        if mrz_value:
            self.log("Validation required")
            self.log("MRZ: {0}".format(mrz_value))
            public_key = self.getPubKey(self._bac, mrz_value)

        message = message_to_sign
        message_bin = hexRepToBin(message)

        signature = self._iso7816.internalAuthentication(message_bin)
        self.log("Signature: {0}".format(binToHexRep(signature)))

        if mrz_value:
            self.log("Check if the signature is correct regarding the public key:")
            data = self._openssl.retrieveSignedData(public_key, signature)
            data_hex = binToHexRep(data)
            header = data_hex[:2]
            self.log("\tHeader: {0}".format(header))
            M1 = data_hex[2:214]
            self.log("\tM1: {0}".format(M1))
            hash_M = data_hex[214:254]
            self.log("\tHash: {0}".format(hash_M))
            trailer = data_hex[254:256]
            self.log("\tTrailer: {0}".format(trailer))

            # If using SHA-1
            if header=='6A' and trailer=='BC':
                M = hexRepToBin(M1 + message)
                new_hash = sha1(M).digest()
                hash_M_bin = hexRepToBin(hash_M)
                if new_hash==hash_M_bin:
                    self.log("hash(M|message to sign) == Hash")
                    validated = True

        return (binToHexRep(signature), validated)

    def getPubKey(self, bac_cp, mrz_value):
        """
        It uses method from pypassport.doc9303.bac in order to authenticate and establish the session keys

        @param bac_cp: A BAC for the authentication and establishment of session keys
        @type bac_cp: A pypassport.doc9303.bac.BAC() object
        @param mrz_value: A MRZ
        @type mrz_value: String value ("PPPPPPPPPPcCCCYYMMDDcSYYMMDDc<<<<<<<<<<<<<<cd")

        @return: The public key (DG15)
        """
        self.log("Reset conenction")
        self._iso7816.rstConnection()

        self.log("Generate the MRZ object")
        mrz_pass = mrz.MRZ(mrz_value)
        self.log("Check the MRZ")
        mrz_pass.checkMRZ()

        self.log("Authentication and establishment of session keys")
        (KSenc, KSmac, ssc) = bac_cp.authenticationAndEstablishmentOfSessionKeys(mrz_pass)
        self.log("Encryption key: {0}".format(binToHexRep(KSenc)))
        self.log("MAC key: {0}".format(binToHexRep(KSmac)))
        self.log("Send Sequence Counter: {0}".format(binToHexRep(ssc)))
        sm = SecureMessaging(KSenc, KSmac, ssc)
        self._iso7816.setCiphering(sm)

        dgReader = datagroup.DataGroupReaderFactory().create(self._iso7816)

        tag = converter.toTAG("DG15")
        dgFile = dgReader.readDG(tag)
        self.log("Get public key")
        dg15 = datagroup.DataGroupFactory().create(dgFile)
        self.log("Public key: {0}".format(binToHexRep(dg15.body)))
        return dg15.body
Ejemplo n.º 19
0
class CA(Logger):
    def __init__(self,
                 caLoc=os.path.expanduser('~'),
                 csca=None,
                 cscaKey=None,
                 opensslLocation=""):
        """ 
        Initiate the CA infrastructure.
        @caLoc: The location where the openssl config files will be stored 
        @param csca: An existing CSCA Certificate in PEM
        @param cscaKey: The private key of the CSCA in PEM
        @param opensslLocation: The openssl executable location 
        """
        Logger.__init__(self, "CA")

        self._csca = csca
        self._cscaKey = cscaKey
        self._loc = os.path.normpath(caLoc)
        self._loc = os.path.join(self._loc, 'ca')
        self._configFile = os.path.join(self._loc, 'openssl.cfg')

        try:
            os.mkdir(self._loc)
        except:
            pass

        self._openssl = OpenSSL('"' + self._configFile + '"')
        self._openssl.register(self._traceOpenSSl)

    def createCSCA(self,
                   size=1024,
                   days=720,
                   dn=DistinguishedName(C="BE", O="Gouv", CN="CSCA-BELGIUM")):
        """ 
        Create a Country Signing Certificate Authority.
        Return a couple with the x509 as first item and the private key as second item
        
        The default distinguished name for the CSCA is:
        C=BE
        O=Gouv
        CN=CSCA-BELGIUM
        
        @param size: The RSA key size in bits
        @param days: The validity period of the certificate
        @param dn: The distinguised name of the certificate
        @return: (x509, privateKey) both in PEM
        """
        self._cscaKey = self._openssl.genRSAprKey(size)
        self._csca = self._genRootHelper(self.cscaKey, days, dn)
        return (self.csca, self.cscaKey)

    def _genRootHelper(self, cscaKey, days, dn):
        try:
            return self._openssl.genRootX509(cscaKey, days, dn)
        except OpenSSLException as msg:
            msg = str(msg)
            self._errorHandler(msg)
            return self._openssl.genRootX509(cscaKey, days, dn)

    def createDS(self,
                 size=1024,
                 days=365,
                 dn=DistinguishedName(C="BE",
                                      O="Gouv",
                                      CN="Document-Signer-BELGIUM")):
        """ 
        Create a Document Signer Certificate.
        Return a couple with the x509 as first item and the private key as second item
        
        The default distinguished name for the DS is:
        C=BE
        O=Gouv
        CN=Document Signer BELGIUM
        
        @param size: The RSA key size in bits
        @param days: The validity period of the certificate
        @param dn: The distinguised name of the certificate
        @return: (x509, privateKey) both in PEM
        """
        self._testinit()
        dsKey = self._openssl.genRSAprKey(size)
        dsReq = self._openssl.genX509Req(dsKey, dn)
        ds = self._signX509Helper(dsReq, days)

        return (ds, dsKey)

    def _signX509Helper(self, dsReq, days):
        try:
            return self._openssl.signX509Req(dsReq, self.csca, self.cscaKey,
                                             days)
        except OpenSSLException as msg:
            msg = str(msg)
            self._errorHandler(msg)
            return self._signX509Helper(dsReq, days)

    def revoke(self, x509):
        """   
        Revoke the certificate.
        Return the CRL in PEM.
        
        @param x509: A x509 certificate
        @return: The CRL in PEM
        @rtype: A string
        """
        self._testinit()
        self._openssl.revokeX509(x509, self.csca, self.cscaKey)

    def _traceOpenSSl(self, name, msg):
        self.log(msg, name)

    def getCrl(self):
        if not os.path.isfile(os.path.join(self._loc, 'crlnumber')):
            self._openssl._toDisk(os.path.join(self._loc, 'crlnumber'), "01")
            self.log("echo '01' > ca/cerlnumber")
        try:
            crl = self._openssl.genCRL(self.csca, self.cscaKey)
        except OpenSSLException as msg:
            msg = str(msg)
            self._errorHandler(msg)
            self.getCrl()
        return self._openssl.crlToDER(crl)

    def _errorHandler(self, msg):
        if msg.find('newcerts') > 0:
            os.makedirs(os.path.join(self._loc, 'newcerts'))
            self.log("mkdir ca/newcerts")
        elif msg.find('ca/index.txt') > 0:
            self._openssl._toDisk(os.path.join(self._loc, 'index.txt'))
            self.log("touch ca/index.txt")
            self._openssl._toDisk(os.path.join(self._loc, 'index.txt.attr'),
                                  "unique_subject = no")
            self.log("echo 'unique_subject = no' > ca/index.txt.attr")
        elif msg.find('ca/serial') > 0:
            self._openssl._toDisk(os.path.join(self._loc, 'serial'), "01")
            self.log("echo '01' > ca/serial")
        elif msg.find('openssl.cfg') > 0:
            self._openssl._toDisk(self._configFile,
                                  self._getConfigFile(self._loc))
            self.log("Creation of ppenssl.cfg")
        else:
            raise OpenSSLException(msg)
        self.log(msg)

    def resetConfig(self):
        try:
            shutil.rmtree(self._loc)
        except:
            pass
        os.makedirs(os.path.join(self._loc, 'newcerts'))
        self._openssl._toDisk(os.path.join(self._loc, 'index.txt'))
        self._openssl._toDisk(os.path.join(self._loc, 'index.txt.attr'),
                              "unique_subject = no")
        self._openssl._toDisk(os.path.join(self._loc, 'serial'), "01")
        self._openssl._toDisk(os.path.join(self._loc, 'crlnumber'), "01")
        self._openssl._toDisk(self._configFile, self._getConfigFile(self._loc))

    def _testinit(self):
        if not ((self.csca != None) and (self.cscaKey != None)):
            raise OpenSSLException("The root CSCA Certificate is not set.")

    def printCrl(self, crl):
        return self._openssl.printCrl(crl)

    def getCsca(self):
        return self._csca

    def getCscaKey(self):
        return self._cscaKey

    def setCsca(self, value):
        self._csca = value

    def setCscaKey(self, value):
        self._cscaKey = value

    csca = property(getCsca, setCsca, None, None)
    cscaKey = property(getCscaKey, setCscaKey, None, None)

    def _getConfigFile(self, path):

        altsep = os.altsep
        if not altsep:
            altsep = os.path.sep
        return """# pour signer un certificat CA intermediaire
[ ca ]
default_ca     = CA_default           # The default ca section

[ CA_default ]
database       =     """ + os.path.join(self._loc, "index.txt").replace(
            os.path.sep, altsep) + """       # database index file.
new_certs_dir  =     """ + os.path.join(self._loc, "newcerts").replace(
                os.path.sep, altsep) + """       # default place for new certs.
crlnumber      =     """ + os.path.join(self._loc, "crlnumber").replace(
                    os.path.sep, altsep) + """
serial         =     """ + os.path.join(self._loc, "serial").replace(
                        os.path.sep,
                        altsep) + """          # The current serial number
class ActiveAuthentication(Logger):
    """  
    This class implement the Active Authentication protocol.
    The main method is I{executeAA} that return True is the verification is ok or False.
    """
    def __init__(self, iso7816, openssl=None):
        """
        @param iso7816: a valid iso7816 object
        @type iso7816: doc9303  
        """
        Logger.__init__(self, "AA")
        self._iso7816 = iso7816
        if not openssl:
            self._openssl = OpenSSL()
        else:
            self._openssl = openssl

        self.RND_IFD = None
        self.F = None
        self.T = None
        self.decryptedSignature = None
        self.D = None
        self.D_ = None
        self.M1 = None
        self.M_ = None

        self._dg15 = None

    def executeAA(self, dg15):
        """
        Perform the Active Authentication protocol.
        Work only with RSA, modulus length of 1024 and with SHA1.
        
        @param dg15: A initialized dataGroup15 object
        @type dg15: dataGroup15 
        @return: True if the authentication succeed, else False.
        @rtype: Boolean
        @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported). 
        @raise ActiveAuthenticationException: If the parameter is not set or invalid.
        @raise ActiveAuthenticationException: If OpenSSL is not installed.
        @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15.
        @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified.
        """
        self._dg15 = dg15

        self.RND_IFD = self._genRandom(8)
        self.signature = self._getSignature(self.RND_IFD)
        self.F = self._decryptSignature(dg15.body, self.signature)

        (hash, hashSize, offset) = self._getHashAlgo(self.F)
        self.D = self._extractDigest(self.F, hashSize, offset)
        self.M1 = self._extractM1(self.F, hashSize, offset)

        self.M_ = self.M1 + self.RND_IFD

        self.log("Concatenate M1 with known M2")
        self.log("\tM*: " + binToHexRep(self.M_))

        self.D_ = self._hash(hash, self.M_)

        self.log("Compare D and D*")
        self.log("\t" + str(self.D == self.D_))

        return self.D == self.D_

    def _genRandom(self, size):
        rnd_ifd = os.urandom(size)
        self.log("Generate an 8 byte random")
        self.log("\tRND.IFD: " + binToHexRep(rnd_ifd))
        return rnd_ifd

    def _getSignature(self, rnd_ifd):
        return self._iso7816.internalAuthentication(rnd_ifd)

    def getPubKey(self, dg15):
        """  
        Retrieve the public key in PEM format from the dataGroup15
        
        @return: A PEM reprensation of the public key
        @rtype: A string
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid.
        @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed?
        """

        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException(
                "The parameter type is not valid, must be a dataGroup15 object"
            )

        return self._openssl.retrieveRsaPubKey(dg15.body)

    def _decryptSignature(self, pubK, signature):
        data = self._openssl.retrieveSignedData(pubK, signature)
        self.log("Decrypt the signature with the public key")
        self.log("\tF: " + binToHexRep(data))

        return data

    def _hash(self, hash, data):
        digest = hash(data).digest()

        self.log("Calculate digest of M*")
        self.log("\tD*: " + binToHexRep(digest))

        return digest

    def _getHashAlgo(self, sig):
        hash = None
        offset = None
        hashSize = None

        if sig[-1] == hexRepToBin("BC"):
            self.T = sig[-1]
            hash = sha1
            offset = -1
        elif sig[-1] == hexRepToBin("CC"):
            self.T = sig[-2]
            #hash = The algorithm corresponding to the algo designed by T
            offset = -2
        else:
            raise ActiveAuthenticationException("Unknow hash algorithm")

        self.log("Determine hash algorithm by trailer T*")
        self.log("\tT: " + binToHexRep(self.T))

        #Find out the hash size
        hashSize = len(hash("test").digest())

        return (hash, hashSize, offset)

    def _extractDigest(self, sig, hashSize, offset):
        digest = sig[offset - hashSize:offset]

        self.log("Extract digest:")
        self.log("\tD: " + binToHexRep(digest))

        return digest

    def _extractM1(self, sig, hashSize, offset):
        M1 = sig[1:offset - hashSize]

        self.log("Extract M1:")
        self.log("\tM1: " + binToHexRep(M1))

        return M1

    def __str__(self):
        spec = self._asn1Parse()
        return spec.prettyPrint()

    def algorithm(self, dg15):
        """
        Return the algorithm name used to store the signature
        @return: A string from the OID dictionnary.
        @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration.
        @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid.
        """
        if type(dg15) != type(datagroup.DataGroup15(None)):
            raise ActiveAuthenticationException(
                "The parameter type is not valid, must be a dataGroup15 object"
            )
        algo = ""
        try:
            spec = self._asn1Parse()
            algo = spec.getComponentByName('algorithm').getComponentByName(
                'algorithm').prettyPrint()
            return OID[algo]
        except KeyError:
            raise ActiveAuthenticationException("Unsupported algorithm: " +
                                                algo)
        except Exception, msg:
            raise ActiveAuthenticationException(
                "Active Authentication not supported: ", msg)