Résolu C/C++ RSA Encryption des credentials

Discussion dans 'Questions / Réponses' créé par nathw96, 11 Mai 2018.

  1. nathw96

    nathw96 Membre

    Inscrit:
    14 Septembre 2013
    Messages:
    11
    J'aime reçus:
    1
    Salut à tous,
    Après de longues heures de travail, j'ai pas mal avancé sur mon projet de bot en c++. Seulement voilà, comme beaucoup, je me heurte à une difficulté, l'encryption des credentials. Tout se passe très bien pour ce qui est de la génération de ma chaine à encrypter ainsi que pour la récupération et la transformation de la clé envoyée par le serveur. Mon problème se pose au moment de générer les credentials étant donné que si je traite le même paquet plusieurs fois, j'obtiens des credentials différents (peut-être un problème de stupide mais je bloque vraiment).
    Voici ma classe, elle est très inspirée de celle de Munrek trouvée ici https://cadernis.fr/index.php?threads/résolu-serialisation-identificationmessage.1404/#post-16492 avec laquelle je rencontre le même problème

    rsa.cpp
    Code (C):
    #include "rsa.h"

    const std::string RSAD2::MPublicKey =
            "-----BEGIN PUBLIC KEY-----\nMIIBUzANBgkqhkiG9w0BAQEFAAOCAUAAMIIBOwKCATIAgucoka9J2PXcNdjcu6CuDmgteIMB+rih\n2UZJIuSoNT/0J/lEKL/W4UYbDA4U/6TDS0dkMhOpDsSCIDpO1gPG6+6JfhADRfIJItyHZflyXNUj\nWOBG4zuxc/L6wldgX24jKo+iCvlDTNUedE553lrfSU23Hwwzt3+doEfgkgAf0l4ZBez5Z/ldp9it\n2NH6/2/7spHm0Hsvt/YPrJ+EK8ly5fdLk9cvB4QIQel9SQ3JE8UQrxOAx2wrivc6P0gXp5Q6bHQo\nad1aUp81Ox77l5e8KBJXHzYhdeXaM91wnHTZNhuWmFS3snUHRCBpjDBCkZZ+CxPnKMtm2qJIi57R\nslALQVTykEZoAETKWpLBlSm92X/eXY2DdGf+a7vju9EigYbX0aXxQy2Ln2ZBWmUJyZE8B58CAwEA\nAQ==\n-----END PUBLIC KEY-----"
            ;

    RSAD2::RSAD2()
    {
        SSL_load_error_strings();
        ERR_load_BIO_strings();
        OpenSSL_add_all_algorithms();
    }

    QByteArray RSAD2::Encrypt(QByteArray helloConnectMessageKey, std::string accountName, std::string accountPassword, std::string salt) {
        QByteArray* byteList = new QByteArray();
        std::string s = AdaptSalt(salt);
        byteList->append(QString::fromStdString(s));
        char* empty = new char[32];
        for (int i =0; i<32; ++i){
            empty[i]='\0';
        }
        byteList->append(empty, 32);
        unsigned char nameLenght = accountName.length();
        byteList->append(QString(nameLenght));
        byteList->append(QString::fromStdString(accountName));
        byteList->append(QString::fromStdString(accountPassword));
        return LoginPKeyEncrypt(*byteList, transformLoginPublicKey(DecryptHelloConnectMessageKey(helloConnectMessageKey)));
    }


    bool RSAD2::CompareByteArrays(QByteArray firstArray, QByteArray secondArray) {
        if (firstArray.length() != secondArray.length()){
            return false;
        }

        for (int index = 0; index < firstArray.length(); ++index) {
            if (firstArray[index] != secondArray[index]) {
                return false;
            }
            ++index;
        }
        return true;
    }

    QByteArray RSAD2::DecryptHelloConnectMessageKey(QByteArray helloConnectMessageKey){
        std::vector<unsigned char> signature = std::vector<unsigned char>(
            helloConnectMessageKey.begin(), helloConnectMessageKey.end());

        char * DPKey =(char *) malloc(MPublicKey.size());
        strcpy(DPKey, MPublicKey.c_str());

        BIO *bp_dofus = BIO_new_mem_buf(DPKey, MPublicKey.size());
        RSA *my_rsa = PEM_read_bio_RSA_PUBKEY(bp_dofus, NULL, NULL, NULL);

        unsigned char *inputSignature, *outputSignature;
        inputSignature = (unsigned char*) malloc(5000);
        outputSignature = (unsigned char*) malloc(5000);
        inputSignature = reinterpret_cast<unsigned char*> (&signature[0]);

        int buflen = RSA_public_decrypt(signature.size(), inputSignature, outputSignature, my_rsa, RSA_PKCS1_PADDING);

        QByteArray outputSignatureArray((const char*)outputSignature, buflen);
        return outputSignatureArray;
    }


    std::string RSAD2::AdaptSalt(std::string salt) {
        if (salt.length() < 32) {
            while (salt.length() < 32) {
               salt += " ";
            }
        }
        return salt;
    }

    std::string RSAD2::transformLoginPublicKey(QByteArray key) {
        std::vector<unsigned char> lPKey = std::vector<unsigned char>(
            key.begin(), key.end());

        const std::string lPKeyStr(lPKey.begin(), lPKey.end());
        std::string lPKeyStrEnc = base64_encode(reinterpret_cast<const unsigned char*>(lPKeyStr.c_str()), lPKeyStr.length());

        std::stringstream lPKeyStrEncFor;
        for(int i=0; i!= lPKeyStrEnc.length(); i++){

            lPKeyStrEncFor << lPKeyStrEnc[i];
                if((i+1)%76==0){
                    if(i!=0) lPKeyStrEncFor << "\n";
                }
        }

        std::stringstream SlPKeyStrEnc;
        SlPKeyStrEnc << "-----BEGIN PUBLIC KEY-----" << "\n" << lPKeyStrEncFor.str() << "\n" << "-----END PUBLIC KEY-----";

        return SlPKeyStrEnc.str();
    }

    QByteArray RSAD2::LoginPKeyEncrypt(QByteArray credentialsArray, std::string LoginPublicKey) {
        std::vector<unsigned char> credentials = std::vector<unsigned char>(
            credentialsArray.begin(), credentialsArray.end());

        char * LoginPublicKeyByte = (char *) malloc(LoginPublicKey.size());
        strcpy(LoginPublicKeyByte, LoginPublicKey.c_str());

        BIO *bp_login = BIO_new_mem_buf(LoginPublicKeyByte, -1);
        RSA *my_second_rsa = PEM_read_bio_RSA_PUBKEY(bp_login, NULL, NULL, NULL);

        unsigned char *pinputCredentials, *poutputCredentials;
        pinputCredentials = (unsigned char*) malloc(5000);
        poutputCredentials = (unsigned char*) malloc(5000);
        pinputCredentials = reinterpret_cast<unsigned char*> (&credentials[0]);

        int buflen = RSA_public_encrypt(credentials.size(), pinputCredentials, poutputCredentials, my_second_rsa, RSA_PKCS1_PADDING);

        QByteArray outputCredentialsArray((const char*)poutputCredentials, buflen);
        return outputCredentialsArray;
    }
     
    Un très grand merci à ceux qui prendront le temps de m'aider :)
     
  2. Labo

    Labo Membre Actif

    Inscrit:
    16 Août 2013
    Messages:
    797
    J'aime reçus:
    59
    C'est normal d'avoir des credentials différents à chaque fois, vu qu'ils dependent de ce que le serveur envoie. C'est une mesure de sécurité pour éviter de pouvoir replay la trame.
     
  3. nathw96

    nathw96 Membre

    Inscrit:
    14 Septembre 2013
    Messages:
    11
    J'aime reçus:
    1
    Salut,
    Alors oui, tout cela je le sais sauf que pour mes tests, j’ai enregistré un paquet du serveur et je traite toujours le même donc (comme la clef générée par le client est également constante) je devrais avoir les mêmes credentials je pense :)
     
  4. astro05

    astro05 Membre

    Inscrit:
    3 Janvier 2017
    Messages:
    32
    J'aime reçus:
    17
    Il me semble que c'est également normal, j'avais fais la même chose pour mon code en Java (https://cadernis.fr/index.php?threads/classe-java-permettant-de-générer-le-credentials.2109/).
    Et j'avais également des credentials différents. Pourtant l'authentification fonctionne bien. ;)
    D'ailleurs, avec le même code et les même paramètres, a chaque relance, le credentials généré est différents.
     
    Dernière édition: 22 Mai 2018
  5. nathw96

    nathw96 Membre

    Inscrit:
    14 Septembre 2013
    Messages:
    11
    J'aime reçus:
    1
    Salut,
    Ceci n'est-il pas du à la pAesKey de ta classe (qui chez moi est remplacée par des 0)? Parce que sinon, il me semble que les credentials devraient être les mêmes pour le même pour le même message du serveur...
    En tout cas merci pour ta réponse, je vais me pencher un peu sur ton code pour voir si j'ai le même résultat avec :)
     
  6. astro05

    astro05 Membre

    Inscrit:
    3 Janvier 2017
    Messages:
    32
    J'aime reçus:
    17
    Il faudrait re-tester, ça fait un petit moment que je ne me suis pas penché sur ce code.
    Mais il me semble qu'avec les même paramètres de getCredentials (pAesKey y compris) tu auras un résultat différents après plusieurs relances.
    Du coup, cela ne servirait a rien de comparer le résultat de ton code avec le miens. ;)
    Le serveur te répond quoi quand tu lui envoie tes credentials ?
     
    Dernière édition: 22 Mai 2018
  7. nathw96

    nathw96 Membre

    Inscrit:
    14 Septembre 2013
    Messages:
    11
    J'aime reçus:
    1
    Un message de type 10 (LoginQueueStatus) ce qui me laisse penser qu’au moins l’en-tête est ok puis plus rien. D’ailleurs maintenant que j’y pense, j’avais comparé mon packet avec celui d’une version C# (qui lui arrive à se connecter) et tout à l’exception des Credentials m’avait paru correspondre

    D’ailleurs je ne comptais pas juste tester ton code mais bien le comprendre pour déceler ce qui cloche chez moi (même si mon code cpp est vraiment moche, j’ai quand même les compétences, promis ^^)
     
    Dernière édition: 22 Mai 2018
  8. astro05

    astro05 Membre

    Inscrit:
    3 Janvier 2017
    Messages:
    32
    J'aime reçus:
    17
    Tout a fais, le LoginQueueStatus ne veut pas dire que tu as réussis a t'authentifier. Il faut que tu reçoive un CredentialsAcknowledgementMessage pour être sur d'être bien authentifié.
    En général, quand on envoie un mauvais credentials au serveur, il répond par un message contenant ces données :
    Code (Text):
    51 01 02
    Le 2 voulant dire WRONG_CREDENTIAL.

    EDIT : Je t'invite a lire ces sujets qui m'ont beaucoup aidé a comprendre l'authentification et la génération du credentials :
    https://cadernis.fr/index.php?threads/procédure-dauthentification-et-rsa.1704/#post-19189
    https://cadernis.fr/index.php?threads/lecture-du-helloconnectmessage.2101/#post-22608

    Ces deux sujets devraient t'aider a générer un credentials manuellement dans un premier temps avec des commandes Linux.
    Après il existe une lib openssl en C++, et du coup ca ne devrait pas être compliqué de faire la même chose. ;)
     
    Dernière édition: 22 Mai 2018
  9. nathw96

    nathw96 Membre

    Inscrit:
    14 Septembre 2013
    Messages:
    11
    J'aime reçus:
    1
    Alors pour cette partie, j'ai un résultat différent:
    Pour ma part je reçois ce message (systématiquement)
    Code (Text):

    0029040000000000550a01022e10000000000000
     
    Qui une fois parsé m'indique que je suis à la position 0 sur 0 dans la file d'attente, pas de trace de message d'erreur (sa longueur étant de 4 et les deux nombres étant des shorts, je vois pas ou pourrait être l'info manquée) (EDIT: boulet, t'as manqué la moitié du message :p)

    Pour cette partie, j'utilise déjà openSSL dans mon code ^^
    Mais merci du conseil, je vais me pencher là-dessus sérieusement quand j'aurai un peu plus de temps (et essayer de générer les credentials à la main me semble être judicieux) :)

    Encore une fois merci pour l'aide apportée, ça me fait vraiment plaisir

    EDIT:
    Pour plus de précision, voici le fonctionnement exact de mon programme:
    • Je reçois un message 3
    Code (Text):

    000e015500206a677a316c74336b5f2e4d7965487a24427477464f6672306958505263256036b10222a2f93aa57fda50520f0b4d8453e19bd476cae853950279048178d2d1377f81e95ac7e17015295a9e27b491ae45d06f475e644a861a9a9fe01d836679afebb026f730f9e3bb84c70db6908b54ad024c57d36e28ce8d5d6ddd5f6994de4a274de6f4409d97ca632d93dfbcb6263798920aed2b8772b40f9f9643b2a6a151c523414a2635eff9d9eba16b0b02fda31528073e10a3a423400f8319edc89cf0cc26512cd77f98fa3d45f5b51a0de44ba034c7f506a54ced0b1730aadddbb08ecd8471c08cd28992d8424655ff1f0871610b76f5f6bffcdb2599abd8fceaa76c1fe6b6ea0c82ff8a6dfb7e529e1e37bc3b5f107624c24dd93195c12dbf756e2d334b1a28b7cc7c97981971ebacb3adbd3d5941301547bd06baf8309ad94e67ddcc2f1afd8d3e334a554e898a569a7ee53dc14d
     
    • J'en extrais la key (en hex) et le salt (en string)
    Key:
    Code (Text):

    22a2f93aa57fda50520f0b4d8453e19bd476cae853950279048178d2d1377f81e95ac7e17015295a9e27b491ae45d06f475e644a861a9a9fe01d836679afebb026f730f9e3bb84c70db6908b54ad024c57d36e28ce8d5d6ddd5f6994de4a274de6f4409d97ca632d93dfbcb6263798920aed2b8772b40f9f9643b2a6a151c523414a2635eff9d9eba16b0b02fda31528073e10a3a423400f8319edc89cf0cc26512cd77f98fa3d45f5b51a0de44ba034c7f506a54ced0b1730aadddbb08ecd8471c08cd28992d8424655ff1f0871610b76f5f6bffcdb2599abd8fceaa76c1fe6b6ea0c82ff8a6dfb7e529e1e37bc3b5f107624c24dd93195c12dbf756e2d334b1a28b7cc7c97981971ebacb3adbd3d5941301547bd06baf8309ad94e67ddcc2f1afd8d3e334a554e898a569a7ee53dc14d
     
    Salt:
    Code (Text):

    jgz1lt3k_.MyeHz$BtwFOfr0iXPRc%`6
     
    • Avec mon pseudo "accountName" et mon mot de passe "accountPassword" et une clef AES constituée de 0, je génère une chaîne à encrypter
    Code (Text):

    6a677a316c74336b5f2e4d7965487a24427477464f667230695850526325603600000000000000000000000000000000000000000000000000000000000000000b6163636f756e744e616d656163636f756e7450617373776f7264
     
    • A l'aide de la clef trouvée dans les fichiers du jeu, je transforme la clef du HelloConnect en une clef qui va me servir à encrypter mes credentials

    Code (Text):

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAls/I2meLiomgCWX69rEgqoqzWWhOeMaU
    awW1quUVdN36RVzANb05iUHKlvxDSgLcI+KkMkYKJDFUzmRnI+i3tUo4t0ZbuAU32s8bOG0ov5g2
    QmA1qFJG+4I6177bYx8Q1HxWzB6qj3LvrenccGj/4RkHeyUfcEEZ7HcF16TiOZXSI+tAEKQ+A/W+
    PkTzPQdkQrgQOrMlSWUEaX90gz2k6wjoahJG+duegFNwn78EjocIAcb8997z/DrCr/QAP5wGMEck
    oc4FVVPgeIRRGy3QL8dyNHy+8qy16Ew6wWw0FuF+Yvme8rQ7eKsWtuvRV/hFJof2sW/yZm7tzm4w
    LNP2awIDAQAB
    -----END PUBLIC KEY-----
     
    • Enfin, j'encrypte mes credentials avec cette clef et à ce moment, j'obtiens un résultat qui semble aléatoire (et a une taille de 256) comme cela par exemple

    Code (Text):

    624fc86d97da67577dafe16c9d7f14919b83423c61c9b68cc7a66f8c9295efcbfa58c676846c499ccab79a39adc3ce6cde38175e669fc08a19fa6a0a1d7b08ec7a7f47d986ce66e09364e5c3da5a0e320f099898dc81231988a3d9c5e2794873bca9f783ef3323b53cd00a1e2bbee4e0f0a2735927a3eb343066daa81d44967f4196540c3aef606f21b94afde27be4e86b0b96dea354da2a1aa7f27b4ff57bb905f0b02940bb23cb91038077fe63e1d029d79bdba20fd9b3044ee16e17ad237abb36bad9633fc8b23b19e05ab607b515dff54f205a0d48d67ee8899175eeaffd4944fbeac7d86c1f6a1cc9d4a06049a96775c407c48fa976f0a21387ef392535
     
    • Malgré le fait que l'étape précédente me semble ratée, je l'envoie et je reçois un message de type 10 puis plus rien...
    EDIT 2:
    En fait il est normal que la clef générée ne soit pas toujours la même, malgré le fait que le chiffrement RSA soit constant, on lui ajoute de l'aléatoire pour plus de sécurité via le RSA_PKCS1_PADDING (ou un truc comme ça) donc bref rien d'anormal si ce n'est que je ne reçois rien d'autre qu'un message de type 10. Mais étant donné que le problème se situe en fait peut-être ailleurs que dans les credentials, voici le message 4 que j'envoie pour ces credentials-ci:

    credentials:
    Code (Text):

    1e4c0fa5bee5f2d9e2eb9806ca6503f1b683d1bbeb4f8e96696c91eb3729bba958cb8cd430ae38999dd68d4c463766996aca29deb615ec81dc80a27e009a0639ffefa1f3e597d4aa4612f26ba301f91f0c5d3b479c16fb155ce37bcb05c5123c19bc0d3648f55a3f191c2570fac5b23ea328b7e20948a458e9e32d1db16872a12cc221584c90a1a1b3193cafd54cea07ee5c3dc1ed1c948dd04241ac65f5cfc3bcb86224de13f92f10079da850c9ef9f49411bc04330f2f34f0cc12466638e91f0ba8875f2609c54b12edf1550b6c9fc09fb13a9bfe92b3163df8d2aff30d87a5f23d2ee4b69a0024851be0744947c220a375d1b300b05cda64b23b604d6237c
     
    message envoyé:
    Code (Text):

    001200000001011700022e0a03d27ba1000001010002667280021e4c0fa5bee5f2d9e2eb9806ca6503f1b683d1bbeb4f8e96696c91eb3729bba958cb8cd430ae38999dd68d4c463766996aca29deb615ec81dc80a27e009a0639ffefa1f3e597d4aa4612f26ba301f91f0c5d3b479c16fb155ce37bcb05c5123c19bc0d3648f55a3f191c2570fac5b23ea328b7e20948a458e9e32d1db16872a12cc221584c90a1a1b3193cafd54cea07ee5c3dc1ed1c948dd04241ac65f5cfc3bcb86224de13f92f10079da850c9ef9f49411bc04330f2f34f0cc12466638e91f0ba8875f2609c54b12edf1550b6c9fc09fb13a9bfe92b3163df8d2aff30d87a5f23d2ee4b69a0024851be0744947c220a375d1b300b05cda64b23b604d6237c0000000000
     
    Un caramel abstrait à celui qui trouve mon erreur :)

    EDIT 3:
    Oh non, je viens de comprende, le message 10 que je reçois est en fait 2 messages que je n'ai pas séparé, voilà pourquoi je ne recevais plus rien, reste plus qu'à tester avec de vrais id pour voir si ça fonctionne (si ça fonctionne je suis officiellement un boulet)

    EDIT 4:
    Après une petite correction sur la version et avec un vrai compte, tout fonctionne, je reçois un message de type 22 puis de type 30. Un gros facepalm pour moi ce soir qui essayait de corriger des fonctions qui allaient très bien depuis facilement un mois :')
     
    Dernière édition: 28 Mai 2018

En naviguant sur ce site web, vous acceptez les Termes et Conditions d'utilisation. Tout le contenu que vous pourrez trouver sur ce site est soumis à la license Creative Commons.