Java IdentificationFailed : Wrong Credential

Inscrit
7 Juillet 2022
Messages
8
Reactions
5
#1
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)
Message:
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));
    }
}
HelloConnectMessage:
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[i] = 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;
}
IdentificationMessage:
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();
}
RSA:
    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[i] = (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.
 
Dernière édition:
Inscrit
7 Juillet 2022
Messages
8
Reactions
5
#2
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.
En fait, j'ai même testé 2 classes différentes de génération de credentials trouvées en ligne et j'ai toujours la même erreur. Ainsi, soit ils ont changé qlq chose dans la façon de générer les credentials entre 2020 et maintenant, soit mon erreur vient d'autre part. La seule possibilité que je vois ici ça serait que ça vienne de ma façon de récupérer l'AESKey et le salt du HelloConnectMessage, cependant quand je les récupère et que je les print, ils ont une bonne tête de ce qu'ils sont censés être...
 
Dernière édition:
Inscrit
7 Juillet 2022
Messages
8
Reactions
5
#3
Bon, j'ai cherché un peu plus loin. En réalité, je me suis rendu compte d'une chose : j'envoyai mon IdentificationMessage avec useLoginToken en paramètre, sauf que mes credentials n'utilisent pas de token. Or, lorsque je met ce tag en false, et bien j'ai un IdentificationFailedMessage avec UNKNOWN_AUTH_ERROR. J'ai donc deux questions :
1 - Est-il possible (depuis la MAJ de dofus qui force l'auth par launcher Ankama) de se connecter sans token ?
2 - Comment récupérer ce token de connexion sinon ?

PS : Ce token ne semble être normalement utilisé que pour les reconnexions lors de changement de personnage (cf sources dofusInvoker), cependant j'ai remarqué que mon client Dofus n'utilise que ça pour se connecter au jeu puisque dans l'IdentificationMessage il y a un magnifique 04 correspondant à autoConnect=False, useCertificate=False et useLoginToken=True

1658107609962.png
 
Dernière édition:
Haut Bas