Salut ! J'essaye de coder un bot en Java et je dois avouer que j'ai quelques soucis. J'ai déjà fait un bot dofus touch mais j'dois avouer que j'avais récup le système de connexion tout fait de Lindo ou de jsp quel autre bot open-source, donc je n'avais pas eu à faire cette partie. Pour l'instant, j'en suis à l'envoi de l'IdentificationMessage (oui le fameux qui fait chier autant de monde, c'est fou qu'un packet pose autant de soucis).
Le problème est que je reçois un WRONG_CREDENTIALS à la suite de son envoi. Pourtant, j'ai bien récupéré la clé publique dans Dofus2, je décrypte la clé fournie dans le HelloConnectMessage avec puis je crypte les credentials (soit salt+AESKey+loginLength+login+passWord) avec cette même clé (alors décryptée). Dans la théorie c'est censé marcher, mais pourtant non (j'ai checké plusieurs fois et avec des logins de différents comptes, j'ai pas simplement mal recopié les identifiants de compte). Je crois faire ABSOLUMENT tout comme les sources de Dofus, sauf pour l'envoi de la partie credentials. En effet, dofus convertit le byteArray donné par le cryptage RSA en un Vector<Int>. Je sais pas si c'est vraiment important, si ça change tout en Java ou encore comment j'implante ça en Java. (En fait mon stockage est en byte[] donc même quand je convertit les credentials en int[] en passant chaque byte avec un filtre &0xFF, bah au moment de l'envoie ma méthode writePacket prend en compte que un byte[] donc je le reconvertit, et jsp si je perd tout, si ça change quelque chose ou pas, je dois avouer que je suis pas hyper calé en conversion). Après si ça se trouve c'est pas ça et ça vient d'autre part. Si vous voulez je vous file n'importe quelle partie de mon code si vous en avez besoin pour voir le problème, demandez moi.
Merci d'avance, j'avoue que là j'suis un peu au bout de mes efforts, je sais pas trop comment débloquer ça moi même.
EDIT 1 : l'erreur ne vient que de la partie credentials car j'ai comparé le packet de l'IdentificationMessage que j'envoyais avec celui envoyé par mon client dofus lors d'une connexion normale et les seuls bytes qui diffèrent (ce qui est normal évidemment car la clé reçue n'est pas la même) sont ceux du credentials. (Puis bon le message d'erreur est assez clair mais c'était un moyen d'en être quand même sûr)
Code JAVA
Cliquez pour révéler
Cliquez pour masquer
public static boolean processMessage(int id, int length, byte[] data, Connexion connexion) throws Exception {
switch(id) {
case 7299:
HelloConnectMessage HCMessage = new HelloConnectMessage(id,length,data);
connexion.salt = HCMessage.salt;
connexion.key = HCMessage.key;
IdentificationMessage IDMessage = new IdentificationMessage(connexion);
connexion.writeMessage(IDMessage);
return true;
case 5663 :
IdentificationFailedMessage IDFMessage = new IdentificationFailedMessage(id,length,data);
System.out.println("ID failed : "+IdentificationFailureReasonEnum.getReason(IDFMessage.reason));
}
}
Méthode appelée lors de la création du HelloConnectMessage
@Override
public HelloConnectMessage deserialize() throws Exception {
this.salt = this.data.readString();
int keyLen = this.data.readVarInt();
byte[] tempKey = new byte[keyLen];
for(int i = 0; i < keyLen; i++) {
tempKey = this.data.readByte();
}
//Cette clé est encryptée ici, il faut la décrypter avec la clé publique fournie dans dofus
Cipher asymmetricCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] data = Base64.getDecoder().decode(server_public_key.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(data);
Key key = keyFactory.generatePublic(publicKeySpec);
asymmetricCipher.init(Cipher.DECRYPT_MODE, key);
byte[] plainText = asymmetricCipher.doFinal(tempKey);
this.key = DatatypeConverter.printBase64Binary(plainText);
return this;
}
public String lang = "";
public int serverId = 0;
public byte[] credentials;
public boolean autoconnect = false, useCertificate = false, useLoginToken = true;
//5.0_2.64.5.12
private int major = 2, minor = 64, code = 5, build = 12, buildType = 0;
public IdentificationMessage(Connexion connexion) throws Exception {
super(8381);
lang = ConfigHandler.config.get("lang");
//System.out.println("Username : "+connexion.username+" Password : "+connexion.password);
credentials = RSA.cipherRsa(connexion.username, connexion.password, connexion);
System.out.println("Longueur de credentials ici :" +credentials.length);
}
@Override
public byte[] serialize() {
Data output = new Data();
int _box0 = 0;
_box0 = BooleanByteWrapper.setFlag(_box0,0,this.autoconnect);
_box0 = BooleanByteWrapper.setFlag(_box0,1,this.useCertificate);
_box0 = BooleanByteWrapper.setFlag(_box0,2,this.useLoginToken);
output.writeByteFromInt(_box0);
output.writeByteFromInt(this.major);
output.writeByteFromInt(this.minor);
output.writeByteFromInt(this.code);
output.writeInt(this.build);
output.writeByteFromInt(this.buildType);
output.writeUTF(this.lang);
output.writeVarInt(this.credentials.length);
output.writeBytes(this.credentials);
output.writeShort(this.serverId);
output.writeVarLong(0);
output.writeShort(0); //Failed attempts
return output.toByteArray();
}
public static int AES_KEY_LENGTH = 32;
public static byte[] generateAESKey() {
byte[] aesKey = new byte[AES_KEY_LENGTH];
for (int i = 0; i < AES_KEY_LENGTH; i++) {
aesKey = (byte) ((int) (Math.random() * 256));
}
return aesKey;
}
public static byte[] cipherRsa(String login, String pwd, Connexion connexion) throws Exception {
int saltLength = connexion.salt.length();
int calcul = saltLength+AES_KEY_LENGTH+1+login.length()+pwd.length();
Data data = new Data(new byte[calcul]);
data.writeBytes(connexion.salt.getBytes(StandardCharsets.UTF_8));
connexion.AESKey = RSA.generateAESKey();
data.writeBytes(connexion.AESKey);
data.writeByteFromInt(login.length());
data.writeBytes(login.getBytes(StandardCharsets.UTF_8));
data.writeBytes(pwd.getBytes(StandardCharsets.UTF_8));
return publicEncrypt(connexion.key, data.toByteArray());
}
public static byte[] publicEncrypt(String publicKey, byte[] data) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] keyBytes = Base64.getDecoder().decode(publicKey.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
Key key = keyFactory.generatePublic(keySpec);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
EDIT 2 :
J'ai cherché plus profondément sur le forum et j'ai trouvé quelqu'un proposant sa classe (en JAVA incroyable) pour générer les credentials. J'lai testé et ça ne marche pas non plus. Il y a donc deux possibilités : soit le mec est un menteur, soit le problème n'est pas dans le cryptage (en tout cas pas seulement). La première option étant peu probable, je vais essayer dans les prochains jours de trouver ce qui ne va pas. Je soupçonne soit mon stockage de données d'être à l'origine de l'erreur (ma classe Data pour les lecteurs attentifs) soit ma méthode d'envoi. Dans tous les cas je vais vérifier les deux et je reviens vers vous.