Exemplo n.º 1
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)
Exemplo n.º 2
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)
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 ""
Exemplo n.º 4
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