Java Encrytion paquet 4 [RESOLU]

Inscrit
15 Avril 2011
Messages
457
Reactions
1
#1
Salut les gens, ça faisait un bail que j'avais pas posté sur le forum (16 mois en fait), j'avais arrêté mon bot en C mais un pote m'a relancé il y a peu pour en faire un en Java. On a principalement copié ce que j'avais déjà fait en C mais impossible d'arriver à construire le paquet 4. Mon problème est similaire à celui d'immortal. Voici le code de la fonction foireuse :

Java:
private static final String publicKey = 
		"MIIBUzANBgkqhkiG9w0BAQEFAAOCAUAAMIIBOwKCATIAq8EYkkGCUg86Bf2CHaM1z1Q2ahQgVXkx" +
		"49I0igwTVCIqG86jsgNb22na1DThZ+IP7DfyBszIecVSP8nwbYPbx6Z7dwq4pnMVx/lx5lyMZUO1" +
		"n/HGEkw1S06AlfXzSg58ci5DL9RJ9ZIa1oMDKtrZiNYA5C3L+7NSCVp/2H/yypWkDjzkFan65+TN" +
		"RExo/2O3+MytJtQ/BXVkbYD58+iiZegddNTNGvz8WlPz2cZvPQt4x1TN+KOgJRKZH5imNAxCtRg6" +
		"l1OLVxfwwUjKFgM4uAsto8vJv5DUFZQMO1Sh9gMpmzeMwXIF4fDD4O1TNiVmu3ABybt2Y4EdaQhs" +
		"/ponC0SNcWbrY0stYbX+Wpk9/Hcxmo3zoduf1ZAdGM01E1g3IjQMd0gOP4v1KQtBjoHim2MCAwEA" +
		"AQ==";


private static byte[] decryptReceivedKey(byte[] receivedKey) {
		byte[] resultKey = null;
		try {
		   byte[] decodedKey = Base64.getDecoder().decode(publicKey);
			X509EncodedKeySpec spec = new X509EncodedKeySpec(decodedKey);
			KeyFactory kf = KeyFactory.getInstance("RSA");
			PublicKey pk = kf.generatePublic(spec);
			
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.DECRYPT_MODE, pk);	
			resultKey = cipher.doFinal(receivedKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return resultKey;
	}

J'ai une "javax.crypto.BadPaddingException : Decryption error". J'ai pas mal fouillé le web et je suis quasiment sûr que ce code est juste. J'ai testé avec mon ancien bot en C et lui aussi ne fonctionne plus. Par conséquent, je me suis dit que le protocole avait été modifié, j'ai donc fouillé les sources du client mais je n'ai rien trouvé qui pourrait faire en sorte que je lis mal la clé envoyée par le serveur (à part les fonctions de la classe "CustomDataWrapper" que j'ai rajouté). Ai-je raté quelque chose ?!
Ainsi, après plusieurs heures de recherches, je me suis résolu à poster ici, en espérant y trouver la solution.

Merci par avance, Nico.
 
Dernière édition par un modérateur:

Kyu

Staff
Membre du personnel
Inscrit
4 Octobre 2009
Messages
327
Reactions
8
#2
Re: Encrytion paquet 4

Petite astuce, tu peux remplacer
Code:
catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | InvalidKeySpecException e)
par
Code:
catch (Exception e)
pour catch toute les exceptions, sachant que ta notation n'est disponible que depuis java 7.
 

BlueDream

Administrateur
Membre du personnel
Inscrit
8 Decembre 2012
Messages
2 010
Reactions
149
#4
Re: Encrytion paquet 4

Il manque l'AES Key dans ton code qui a été ajoutée récemment.
Elle doit être générée donc au moment d'envoyer le paquet 4, tu dois la stoquer, elle te permettera de décrypter le tiquet par la suite lors du switch vers le serveur de jeu.

Elle est générée de cette manière:

com/ankamagames/dofus/logic/connection/managers/AuthentificationManager.as
Code:
private static const AES_KEY_LENGTH:uint = 32;

private var _AESKey:ByteArray;

public function initAESKey() : void
      {
         this._AESKey = this.generateRandomAESKey();
      }

private function generateRandomAESKey() : ByteArray
      {
         var _loc1_:ByteArray = new ByteArray();
         var _loc2_:* = 0;
         while(_loc2_ < AES_KEY_LENGTH)
         {
            _loc1_[_loc2_] = Math.floor(Math.random() * 256);
            _loc2_++;
         }
         return _loc1_;
      }
Pour ce qui est de l'écriture, voici la fonction cipherRsa:
L'AES Key s'écrit après le salt.

com/ankamagames/dofus/logic/connection/managers/AuthentificationManager.as
Code:
private function cipherRsa(param1:String, param2:String, param3:TrustCertificate) : Vector.<int>
      {
         var baOut:ByteArray = null;
         var debugOutput:ByteArray = null;
         var n:int = 0;
         var login:String = param1;
         var pwd:String = param2;
         var certificate:TrustCertificate = param3;
         var baIn:ByteArray = new ByteArray();
         baIn.writeUTFBytes(this._salt);
         baIn.writeBytes(this._AESKey);
         if(certificate)
         {
            baIn.writeUnsignedInt(certificate.id);
            baIn.writeUTFBytes(certificate.hash);
         }
         baIn.writeByte(login.length);
         baIn.writeUTFBytes(login);
         baIn.writeUTFBytes(pwd);
         try
         {
            if(File.applicationDirectory.resolvePath("debug-login.txt") || File.applicationDirectory.resolvePath("debuglogin.txt"))
            {
               _log.debug("login with certificate");
               debugOutput = new ByteArray();
               baIn.position = 0;
               debugOutput.position = 0;
               debugOutput = RSA.publicEncrypt((new PUBLIC_KEY_V2() as ByteArray).readUTFBytes((new PUBLIC_KEY_V2() as ByteArray).length),baIn);
               _log.debug("Login info (RSA Encrypted, " + debugOutput.length + " bytes) : " + Base64.encodeByteArray(debugOutput));
            }
         }
         catch(e:Error)
         {
            _log.error("Erreur lors du log des informations de login " + e.getStackTrace());
         }
         baOut = RSA.publicEncrypt(this._publicKey,baIn);
         var ret:Vector.<int> = new Vector.<int>();
         baOut.position = 0;
         var i:int = 0;
         while(baOut.bytesAvailable != 0)
         {
            n = baOut.readByte();
            ret[i] = n;
            i++;
         }
         return ret;
      }
EDIT: J'avais oublié l'AES_KEY_LENGTH.
 
Dernière édition:
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#5
Re: Encrytion paquet 4

Merci Alex pour ta réponse mais j'avais déjà vu tout ça et j'ai d'ailleurs implémenté la fonction generateRandomAESKey(). La clé AES intervient lors du chiffrement des credentials alors que moi je suis à l'étape d'avant, c'est-à-dire le déchiffrement de la clé du serveur par la clé du client.

Voici le code complet de ma classe pour mieux comprendre ce que je fais :

Java:
package main;

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;

public class Encryption {
	private static final int AES_KEY_LENGTH = 32;
	private static final String publicKey = 
		"MIIBUzANBgkqhkiG9w0BAQEFAAOCAUAAMIIBOwKCATIAq8EYkkGCUg86Bf2CHaM1z1Q2ahQgVXkx" +
		"49I0igwTVCIqG86jsgNb22na1DThZ+IP7DfyBszIecVSP8nwbYPbx6Z7dwq4pnMVx/lx5lyMZUO1" +
		"n/HGEkw1S06AlfXzSg58ci5DL9RJ9ZIa1oMDKtrZiNYA5C3L+7NSCVp/2H/yypWkDjzkFan65+TN" +
		"RExo/2O3+MytJtQ/BXVkbYD58+iiZegddNTNGvz8WlPz2cZvPQt4x1TN+KOgJRKZH5imNAxCtRg6" +
		"l1OLVxfwwUjKFgM4uAsto8vJv5DUFZQMO1Sh9gMpmzeMwXIF4fDD4O1TNiVmu3ABybt2Y4EdaQhs" +
		"/ponC0SNcWbrY0stYbX+Wpk9/Hcxmo3zoduf1ZAdGM01E1g3IjQMd0gOP4v1KQtBjoHim2MCAwEA" +
		"AQ==";
	
	public static byte[] encrypt(byte[] encryptedKey, char[] login, char[] password, char[] salt) {
		byte[] decryptedKey = decryptReceivedKey(encryptedKey);
		return encryptCredentials(decryptedKey, login, password, salt);
	}
	
	private static byte[] decryptReceivedKey(byte[] receivedKey) {
		byte[] resultKey = null;
		try {
		    byte[] decodedKey = Base64.getDecoder().decode(publicKey);
			X509EncodedKeySpec spec = new X509EncodedKeySpec(decodedKey);
			KeyFactory kf = KeyFactory.getInstance("RSA");
			PublicKey pk = kf.generatePublic(spec);
			
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.DECRYPT_MODE, pk);	
			resultKey = cipher.doFinal(receivedKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return resultKey;
	}
	
	private static byte[] encryptCredentials(byte[] key, char[] login, char[] password, char[] salt) {
		byte[] encryptedCredentials = null;
		ByteArray buffer = new ByteArray();
		buffer.writeBytes(new String(salt).getBytes());
		buffer.writeBytes(generateRandomAESKey());
		buffer.writeByte((byte) login.length);
		buffer.writeUTFBytes(new String(login).toCharArray());
		buffer.writeUTFBytes(new String(password).toCharArray());
		
		try {
			KeyFactory kf = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec x509 = new X509EncodedKeySpec(key);
			PublicKey publicKey = kf.generatePublic(x509);
			
			Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			encryptedCredentials = cipher.doFinal(buffer.bytes());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encryptedCredentials;
	}
	
	private static ByteArray generateRandomAESKey() {
		ByteArray array = new ByteArray();
		for(int i = 0; i < AES_KEY_LENGTH; ++i)
			array.writeByte((byte) Math.floor(Math.random() * 256));
		return array;
	}
}


Ma fonction encryptCredentials() est équivalente à la fonction cipherRsa() des sources du client.
 
Dernière édition par un modérateur:
Inscrit
15 Avril 2011
Messages
457
Reactions
1
#6
Re: Encrytion paquet 4

J'ai du nouveau sur mon problème, j'ai essayé de déchiffrer en "NoPadding" et ça a fonctionné. J'ai fait la même chose en C et c'est passé aussi, du moins pour la première étape.
Cependant je reste bloqué à la seconde étape, où j'obtiens l'exception suivante :
"java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=22, too big."
à la fonction "kf.generatePublic(spec)"...


EDIT : Bon en fait, j'avais oublié de mettre à jour la clé du client... Du coup ça fonctionne (avec le padding "PKCS1Padding"). Désolé du dérangement.
 
Haut Bas