C/C++ Traduction du protocole d'authentification

Inscrit
15 Avril 2011
Messages
457
Reactions
1
#1
Bonjour, je suis actuellement en train d'essayer de traduire le protocole d'authentification au serveur d0fus en C. Mais je me heurte à divers manques de connaissances...

Le premier porte sur le paramètre de la fonction PEM.readRSAPublicKey(string). Dans setKey, elle est appelée avec tout un bordel qui tourne autour de _verifyKey(), soit :

Code:
var _loc5_:RSAKey = PEM.readRSAPublicKey((new this._verifyKey() as ByteArray).readUTFBytes((new this._verifyKey() as ByteArray).length));
Je vais donc voir le fichier AuthentificationManager__verifyKey qui ne contient que super(), qui est un constructeur d'après ce que j'ai compris. Là je me dis, c'est bizarre... Mais je vois que cette classe en utilise une autre, ByteArrayAsset. Je vais voir dans cette dernière et j'y trouve :

Code:
static const VERSION:String = "4.6.0.23201";
Donc d'après ce que j'ai compris, j'ai juste à remplacer tout ce qu'il y a en paramètre de PEM.readRSAPublicKey par :

Code:
PEM.readRSAPublicKey("4.6.0.23201");
C'est beaucoup plus simple mais est-ce vraiment ça ?


PS : Ça dérange si je garde ce topic pour poser diverses questions sur la traduction ? J'espère ne pas trop en avoir.
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#2
Hum, en fait j'ai dit une grosse connerie... C'est bien la clé qui est passé en paramètre, pour être ensuite "purifiée". Mais je ne comprends vraiment pas à quel moment elle y est passé dans ce paramètre, cette _verifyKey() ne représente rien...

Cependant, l'étude de cette partie est en fait inutile vu que Munrek a releasé sa classe RSA. Mais sa classe demande une signature. Or, je ne vois pas ce que c'est, quelqu'un pourrait m'éclairer ?
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#3
Donc on va tout reprendre au début.

D'une part, histoire de ne pas te faire chier dès le début, fout ta Clé Public en Byte.
Ank@ma envoi la clé public au format X509.

Il te faudra dans un premier temps la décoder.

Voila un lien très intéressant:
http://stackoverflow.com/questions/1150 ... in-c-sharp

Code:
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
        {
            // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
            byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
            byte[] seq = new byte[15];
            // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
            MemoryStream mem = new MemoryStream(x509key);
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
            byte bt = 0;
            ushort twobytes = 0;

            try
            {

                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                    binr.ReadByte();    //advance 1 byte
                else if (twobytes == 0x8230)
                    binr.ReadInt16();   //advance 2 bytes
                else
                    return null;

                seq = binr.ReadBytes(15);       //read the Sequence OID
                if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
                    return null;

                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
                    binr.ReadByte();    //advance 1 byte
                else if (twobytes == 0x8203)
                    binr.ReadInt16();   //advance 2 bytes
                else
                    return null;

                bt = binr.ReadByte();
                if (bt != 0x00)     //expect null byte next
                    return null;

                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                    binr.ReadByte();    //advance 1 byte
                else if (twobytes == 0x8230)
                    binr.ReadInt16();   //advance 2 bytes
                else
                    return null;

                twobytes = binr.ReadUInt16();
                byte lowbyte = 0x00;
                byte highbyte = 0x00;

                if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
                    lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
                else if (twobytes == 0x8202)
                {
                    highbyte = binr.ReadByte(); //advance 2 bytes
                    lowbyte = binr.ReadByte();
                }
                else
                    return null;
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
                int modsize = BitConverter.ToInt32(modint, 0);

                byte firstbyte = binr.ReadByte();
                binr.BaseStream.Seek(-1, SeekOrigin.Current);

                if (firstbyte == 0x00)
                {   //if first byte (highest order) of modulus is zero, don't include it
                    binr.ReadByte();    //skip this null byte
                    modsize -= 1;   //reduce modulus buffer size by 1
                }

                byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes

                if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
                    return null;
                int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
                byte[] exponent = binr.ReadBytes(expbytes);

                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                RSAParameters RSAKeyInfo = new RSAParameters();
                RSAKeyInfo.Modulus = modulus;
                RSAKeyInfo.Exponent = exponent;
                RSA.ImportParameters(RSAKeyInfo);
                return RSA;
            }
            catch (Exception)
            {
                return null;
            }

            finally { binr.Close(); }

        }

Voila la fonction en C# que j'ai trouvé qui décode la clé X509 et te la retourne en 'RSACryptoServiceProvider'.

http://msdn.microsoft.com/fr-fr/library/system.security.cryptography.rsacryptoserviceprovider(v=vs.110).aspx
Commence par essayer d'arriver à cette étape.

Cordialement
 

Gohu

Membre Actif
Inscrit
16 Novembre 2013
Messages
222
Reactions
2
#4
J'en suis au meme point que toi Nico, si tu as trouvé une solution ca serait cool de la partager; car les librairies X509 en C++ se font rares donc j'ai aussi décidé de traduire "mot pour mot"ce que fait le client!

Merci Bien!
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#5
Salut à vous deux, j'ai avancé un peu là dessus. Sachez que la conversion x509/bytes est faite dans la classe RSA de Munrek par la fonction :
Code:
RSA *my_rsa = PEM_read_bio_RSA_PUBKEY(bp_dofus, NULL, NULL, NULL);
puis par la fonction :
Code:
RSA_public_decrypt(signature_size, inputSignature, outputSignature, my_rsa, RSA_PKCS1_PADDING);
J'ai traduit les 2 premières fonctions de sa classe en C mais je ne sais pas à quoi sert la troisième...
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#6
NicoMasterChief a dit:
Salut à vous deux, j'ai avancé un peu là dessus. Sachez que la conversion x509/bytes est faite dans la classe RSA de Munrek par la fonction :
Code:
RSA *my_rsa = PEM_read_bio_RSA_PUBKEY(bp_dofus, NULL, NULL, NULL);
puis par la fonction :
Code:
RSA_public_decrypt(signature_size, inputSignature, outputSignature, my_rsa, RSA_PKCS1_PADDING);
J'ai traduit les 2 premières fonctions de sa classe en C mais je ne sais pas à quoi sert la troisième...
Envoi moi la 3eme classe, je vais te dire a quoi elle sert.
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#7
Voici la classe originelle entière :

Code:
/*
Copyright (C) <2013>  <Munrek>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "DofusRSA.h"

DofusRSA::DofusRSA() {
	SSL_load_error_strings();
	ERR_load_BIO_strings();
	OpenSSL_add_all_algorithms();

	stringstream SDofusPublicKey;
	SDofusPublicKey << "-----BEGIN PUBLIC KEY-----\n"
			"MIIBUzANBgkqhkiG9w0BAQEFAAOCAUAAMIIBOwKCATIAqpzRrvO3We7EMi9cWYqdfb3rbdinTay+\n"
			"hxQ6t3dOiJLY4NITxyeIuy97yZYOojOlXS2SuJ4cCHjCeLCQO1FwOz+nynQWcBWecz2QdbHD2Kz7\n"
	        "mNLd2qtZyEDO76rd7LaDOxRvgs9DsH9sfnCuKLKbd725xTLc7wRfJzOH9v9rTTYVXssXe7JUpTx8\n"
	        "nV8yKnTiq3WpzBeZT4C3ZCR18GBBCh3NmSTbze9i2KipgZnOwBvhskVlweuqZ1KNIKsQgipBFuyw\n"
	        "w68RGNYaAKofMVVio4amrGpCT5MM852jpHsgJJfOUHu6md1CnvdwDPbo/PKQUI0RLb0ezE5gsPma\n"
	        "s39QBw+DiaibUkk1aCkBxTOFqpIbjfLM2/4qA6GPcWUJxP3vmGoeCTMBLNEiPfLqVm86QzUCAwEA\n"
	        "AQ==\n"
	        "-----END PUBLIC KEY-----";
	DofusPublicKey = SDofusPublicKey.str();
}

std::vector<byte> DofusRSA::DofusPKeyDecrypt(std::vector<byte> signature) {
    char * DPKey =(char *) malloc(DofusPublicKey.size());
	strcpy(DPKey, DofusPublicKey.c_str());

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

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

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

	std::vector<byte> outputSignatureVector(outputSignature, outputSignature+buflen);
	return outputSignatureVector;
}

std::vector<byte> DofusRSA::LoginPKeyEncrypt(std::vector<byte> credentials) {
    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);

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

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

	std::vector<byte> outputCredentialsVector(poutputCredentials, poutputCredentials+buflen);
	return outputCredentialsVector;
}

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

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

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

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

	LoginPublicKey = SlPKeyStrEnc.str();
}

Je viens juste de la comprendre ! Elle remplace cette ligne dans la fonction setPublicKey :
Code:
this._publicKey = "-----BEGIN PUBLIC KEY-----\n" + Base64.encodeByteArray(_loc4_) + "-----END PUBLIC KEY-----";


Depuis le certificat x509 de 305 octets, la fonction RSA_public_decrypt me retourne une clé de 18 octets, est-ce normal ? Ça me paraît un peu léger.
 

Gohu

Membre Actif
Inscrit
16 Novembre 2013
Messages
222
Reactions
2
#8
Donc avec cette classe ça renvoie la clef publique purifiée ??
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#9
Accompagnée de la classe Base64, cette classe fait tout.
Mais comme je code en C, j'ai dû la traduire et ça ne fonctionne pas...
 

Gohu

Membre Actif
Inscrit
16 Novembre 2013
Messages
222
Reactions
2
#10
OK merci je vais y jeter un oeil. Ou puis-je trouver la classe originale en entier ?
 

Gohu

Membre Actif
Inscrit
16 Novembre 2013
Messages
222
Reactions
2
#13
Salut,

Je ne comprends pas bien a quoi sert la premiere fonction, je pense que c'est ce paramètre "signature" qui me trouble!
Merci d'avance si vous pouviez m'expliquer a quoi sert cette fonction.

EDIT: je pense avoir compris mais corrigez moi tout de meme si je me trompe:
je recois le paquet 3 --> j'en extrait la clef publique (raw) --> j'appelle DofusPKeyDecrypt --> j'appelle setLoginPublicKey
et le tour est joué.
En revanche je ne comprends toujours pas ce qu'est le parametre "Signature" de DofusPKeyDecrypt et "lPKey" de setLoginPublicKey...
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#14
C'est exact. DofusPKeyDecrypt extrait la clé du x509 (signature) avec la clé client (celle du haut), puis ensuite on utilise setLoginPublicKey pour convertir cette clé au format base 64 et lui ajouter l'header, le footer et les \n à chaque fin de ligne, et enfin on utilise LoginPKeyEncrypt pour crypter les credentials (identifiants) avec la clé.
 

Gohu

Membre Actif
Inscrit
16 Novembre 2013
Messages
222
Reactions
2
#15
On donc signature c'est la clef publique que je viens de recevoir et lPKey c'est la clef décodée du format X509
 
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#16
Signature c'est le certificat x509 en entier. Exact pour lPKey.
Moi je bloque sur le fait que la fonction RSA_public_decrypt s'arrête de décrypter quand elle voit un 0 dans le char* que je lui file, synonyme de fin de chaine... Je ne sais pas comment contourner ça...
 
Haut Bas