Java 8, chiffrement AES, encodage, Windows 10, invite de commande, batch et IDE [Résolu/Fermé]

Signaler
-
 Oliv -
Hello world !
Et vous, qui allez, j'en suis sûr, répondre avec brio...

Voilà, j'ai un soucis lorsque j'exécute mon programme - un triolet de programmes en fait - son résultat diffère en fonction de son mode de lancement, à savoir :

soit depuis l'IDE (Eclipse 2020-03) sur lequel il est écrit avec comme arguments du Run Configuration de Encrypt :
"Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "Aïe, aïe, ça écrit pô bien !"

output : GhpMryHkHJ6uZ87SZg816R4TbHwaliyYxSwzjJyyelA=

puis pour le Run Configuration de Decypher :
"Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "GhpMryHkHJ6uZ87SZg816R4TbHwaliyYxSwzjJyyelA="

output : Aïe, aïe, ça écrit pô bien !


soit depuis l'invite de commande Windows 10 (variable environnement ad hoc) avec successivement :
java -jar "C:\Users\moi\Encrypt.jar" "Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "Aïe, aïe, ça écrit pô bien !"

output : GhpMryHkHJ6uZ87SZg816R4TbHwaliyYxSwzjJyyelA=

Puis :
java -jar "C:\Users\moi\Decypher.jar" "Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "GhpMryHkHJ6uZ87SZg816R4TbHwaliyYxSwzjJyyelA="

output : Aïe, aïe, ça écrit pô bien !


soit depuis des fichiers batch (encodés en ANSI ou en UTF-8, même résultat) qui contiennent successivement :
@echo off
java -jar "C:\Users\moi\Encrypt.jar" "Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "Aïe, aïe, ça écrit pô bien !"
pause

output : 5JaO3P+T0ZAEv4+Xbc/bbGvykgT1R0zO7vquGvHF150=

Puis :
@echo off
java -jar "C:\Users\moi\Decypher.jar" "Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "5JaO3P+T0ZAEv4+Xbc/bbGvykgT1R0zO7vquGvHF150="
pause

output : A´e, a´e, þa Úcrit p¶ bien !
Mais si je mets en second argument du batch du Decypher.jar le message chiffré que j'aurais obtenu par une des deux autres méthodes, l'encodage est ok, l'output est conforme.


Pourquoi Encrypt.jar donne un output différent suivant qu'on le lance directement depuis la console ou via un batch ? D'où cela vient-il ? Comment y remédier ?

Voici le triolet de programmes en question :

class KeysGenerator {
    public static void main(String _arguments[]) {
        try {
            KeyGenerator key_generator = KeyGenerator.getInstance("AES");
            key_generator.init(256, SecureRandom.getInstanceStrong());
            System.out.println(Base64.getUrlEncoder().encodeToString(key_generator.generateKey().getEncoded()));
        } catch(NoSuchAlgorithmException _e) {
            System.err.println("Erreur lors de la création de la clé : ".concat(_e.getMessage()));
        }
    }
}


class Encrypt {
    public static void main(String _arguments[]) {
        try {
            Cipher _encrypt = Cipher.getInstance("AES");
            _encrypt.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(Base64.getUrlDecoder().decode(_arguments[0]), "AES"));
            System.out.println(Base64.getEncoder().encodeToString(_encrypt.doFinal(_arguments[1].getBytes())));
        } catch(NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException _e) {
            System.err.println("Erreur lors du chiffrement : ".concat(_e.getMessage()));
        }
    }
}


class Decypher {
    public static void main(String _arguments[]) {
        try {
            Cipher _decypher = Cipher.getInstance("AES");
            _decypher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.getUrlDecoder().decode(_arguments[0]), "AES"));
            System.out.println(new String(_decypher.doFinal(Base64.getDecoder().decode(_arguments[1]))));
        } catch(NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException _e) {
            System.err.println("Erreur lors du déchiffrement : ".concat(_e.getMessage()));
        }
    }
}



Merci de bien vouloir m'éclairer, l'encodage, entre l'unicode de la JVM, le cp1252 de ses flux par défaut sous Windows, et l'ANSI de Windows (est-ce à 100 % le cp1252 ?), n'est vraiment pas ma tasse de thé, d'autant que je ne sais pas où il se perd, ni même si c'est bien strictement un problème d'encodage de l'affichage console.

1 réponse

Messages postés
16370
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
18 juillet 2021
2 856
Bonjour,

Effectivement tu t'es pris les pieds dans l'encodage.

Prenons une analogie avec les langues :
  • Eclipse fait une traduction du français vers l'anglais puis de l'anglais vers le français, c'est bon.
  • L'invite de commandes fait une traduction du français vers l'allemand puis de l'allemand vers le français, donc c'est bon aussi.

Ton problème, c'est que tu demandes à Eclipse de faire une traduction du français vers l'anglais, puis à l'invite de commandes de traduire de l'allemand vers le français.
Or le texte en anglais ce n'est pas de l'allemand ! Donc la traduction de l'allemand à partir du texte anglais ça ne peut pas donner un français correct...

Dans ton code, le problème se situe ici au niveau de Encrypt
_arguments[1].getBytes()
et de Decypher
new String(...)
, car ces méthodes prennent une valeur d'encodage par défaut, or si celle-ci est différente entre le chiffrement et le déchiffrement alors ça ne fonctionnera pas.

Ci dessous un petit code d'exemple pour comprendre : peu importe l'encodage choisi si on utilises le même lors de la conversion String/byte[] et byte[]/String ça fonctionnera, mais dès qu'on mélange les encodages, ça fera un peu n'importe quoi :

import java.nio.charset.Charset;
import java.util.Arrays;

public class Test {    
    public static void main(String[] args) throws Exception {
        Charset c1 = Charset.forName("windows-1252");
        Charset c2 = Charset.forName("UTF-8");

        String text = "Aïe";
        System.out.println("text"); // Aïe

        byte[] textC1 = text.getBytes(c1);
        System.out.println("c1\t" + Arrays.toString(textC1)); // 65, -17, 101

        byte[] textC2 = text.getBytes(c2);
        System.out.println("c2\t" + Arrays.toString(textC2)); // 65, -61, -81, 101

        String textC1C1 = new String(textC1, c1);
        System.out.println("c1c1\t" + textC1C1); // Aïe

        String textC2C2 = new String(textC2, c2);
        System.out.println("c2c2\t" + textC2C2); // Aïe

        String textC1C2 = new String(textC1, c2);
        System.out.println("c1c2\t" + textC1C2); // A?e

        String textC2C1 = new String(textC2, c1);
        System.out.println("c2c1\t" + textC2C1); // Aïe
    }
}

Pour corriger ton code, il faut donc que tu imposes un encodage (peu importe lequel) pour que la conversion String/byte[] et byte[]/String soit cohérente.
>
Messages postés
16370
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
18 juillet 2021

Ah flûte désolé, pas été aux mails... depuis des années. Oups. Merci aussi donc pour ça.
>
Messages postés
16370
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
18 juillet 2021

Bon, je profite du fait que ce ne soit pas classé en "Résolu" pour dire que du coup, maintenant que je sais d'où venait le problème, gloire à toi, c'est quand même beaucoup plus simple. Parce qu'en fait plutôt que de passer par un troisième argument, pas forcément à la portée du premier venu, je rajoute plutôt dans le code :
String _encoding = System.getProperty("sun.stdout.encoding") == null ? Charset.defaultCharset().name() : System.getProperty("sun.stdout.encoding");

pour ta modification qui devient donc :
_arguments[1].getBytes(_encoding);


Aaaah. Merci encore.
Messages postés
16370
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
18 juillet 2021
2 856 > Oliv
Remarque : "dans la vraie vie" on s'embête rarement à passer des valeurs aussi complexes en argument d'un programme.

Dans le cas de la cryptographie par exemple, on passerait plutôt en paramètres les chemins d'accès vers un fichier d'entrée, un fichier contenant la clé et un fichier de sortie.

Cela éviterait tous les problèmes d'encodage car on traiterait les fichiers comme une suite d'octets en byte[] sans essayer de les interpréter avec un encodage particulier.
>
Messages postés
16370
Date d'inscription
samedi 31 mai 2008
Statut
Modérateur
Dernière intervention
18 juillet 2021

"Dans le cas de la cryptographie par exemple, on passerait plutôt en paramètres les chemins d'accès vers un fichier d'entrée, un fichier contenant la clé et un fichier de sortie"

Et oui, clairement, mais là-dessus j'ai pas de problème (et là du coup, comme je le disais avant, j'encode et décode tout en UTF-8 et je formate en hexa avant de hacher, saler, signer, certifier, chiffrer, envoyer, recevoir, déchiffrer, stocker, que ce soit avec RSA, AES, SHA3-512, Sha512withRSA) par contre c'est toujours lors d'une utilisation d'interface graphique.
Là il me fallait la console...
Bon dernière intervention, cette fois c'est la bonne. Ma solution n'en n'était pas vraiment une, allez savoir pourquoi, c'est du coup l'invite de commande qui ne reconnaissait plus son latin. D'ailleurs, d'après l'affichage des
System.getProperties()
lors de l'utilsation en invite de commande que tu m'a demandé, la page de code active est 850, comme avec le batch, alors, comment se fait-il qu'elle ait affiché correctement le texte en 1252 à ce moment là, et que maintenant, quand le texte est en 850 (c'est ce que retourne
System.getProperty("sun.stdout.encoding")
) elle l'affiche mal d'autant qu'un
chcp
en ligne de commande retourne bien lui aussi 850 ?

Quoiqu'il en soit aucune importance parce la solution, elle était là : le problème venait du batch, il fallait donc le régler avec le batch, pas avec le programme.
Et donc très très simplement il devient :
@echo off
chcp 1252
java -jar "C:\Users\Oliv\AESEncryptor2.jar" "Eo6Uam3jA0-utTtI94bj2lPduuS4Jy9XPtkQG30JQzE=" "Aïe, aïe, ça écrit pô bien !"
pause

Alors que
_arguments[1].getBytes()
reste tel quel comme avant.

La console de l'IDE, la console du batch et l'invite de commande (qui me répond donc pourtant 850 à un
chcp
) m'affichent tous
GhpMryHkHJ6uZ87SZg816R4TbHwaliyYxSwzjJyyelA=


Voilouuu ! Au revoir et encore, si, si, si, meeEEERCIIIIII !