Comment gérer BadPaddingException Au cours de cryptage AES256 en C # et décryptage en Java

Sean Sin:

Je ne sais pas pourquoi une erreur est à venir.

Exception dans le thread « principal » javax.crypto.BadPaddingException: bloc donné finale pas correctement rembourré. Ces problèmes peuvent survenir si une mauvaise clé est utilisée lors du déchiffrement.

Je comprends que cette erreur se produit lorsque la clé incorrecte est utilisée pendant le décryptage. Toutefois, si vous regardez les résultats des tests résultat inférieur, vous pouvez voir que C # et Java sont les mêmes (Key, IV, Le sel est codé en base64).

  1. C # Résultat d'essai

C # Résultat d'essai

  1. Résultat Java test

Résultat Java test

C'est la même! (Key, IV, sel)

Mais l'erreur de BadpaddingException courant est généré. Quel pourrait être le problème? Je joins mon fichier source.

  1. C # (cryptage)

    class AES {
            private readonly static string keyStr = "This is Key";
            private readonly static string vector = "This is Vector";

            public static Rfc2898DeriveBytes MakeKey(string password){

                byte[] keyBytes = System.Text.Encoding.UTF8.GetBytes(password);
                byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
                Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(keyBytes, saltBytes, 65536);

                return result;
            }

            public static Rfc2898DeriveBytes MakeVector(string vector){

                byte[] vectorBytes = System.Text.Encoding.UTF8.GetBytes(vector);
                byte[] saltBytes = SHA512.Create().ComputeHash(vectorBytes);
                Rfc2898DeriveBytes result = new Rfc2898DeriveBytes(vectorBytes, saltBytes, 65536);

                return result;
            }

            public static void Encrypt(String inputFile, String outputFile) {
                using (RijndaelManaged aes = new RijndaelManaged()){
                    //Create Key and Vector
                    Rfc2898DeriveBytes key = AES.MakeKey(AES.keyStr);
                    Rfc2898DeriveBytes vector = AES.MakeVector(AES.vector);

                    //AES256
                    aes.BlockSize = 128;
                    aes.KeySize = 256;

                    // It is equal in java 
                    // Cipher _Cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");    
                    aes.Mode = CipherMode.CBC; 
                    aes.Padding = PaddingMode.PKCS7; 
                    aes.Key = key.GetBytes(32); //256bit key
                    aes.IV  = vector.GetBytes(16); //128bit block size


                    //processing Encrypt
                    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
                    byte[] encrypted;

                    using (MemoryStream msEncrypt = new MemoryStream()) { 
                            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) {
                                byte[] inputBytes = File.ReadAllBytes(inputFile);
                                csEncrypt.Write(inputBytes, 0, inputBytes.Length);
                            }
                            encrypted = msEncrypt.ToArray();     
                        }
                        string encodedString = Convert.ToBase64String(encrypted);
                        File.WriteAllText(outputFile, encodedString);
                    }
                }
            }

  1. Java (Decryption)

    public class AES256File {
        private static final String algorithm = "AES";
        private static final String blockNPadding = algorithm+"/CBC/PKCS5Padding";
        private static final String password = "This is Key";
        private static final String IV = "This is Vector";

        private static IvParameterSpec ivSpec;
        private static Key keySpec;

        public static void MakeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            MessageDigest digest = MessageDigest.getInstance("SHA-512");
            byte[] keyBytes = password.getBytes("UTF-8");

            // C# : byte[] saltBytes = SHA512.Create().ComputeHash(keyBytes);
            byte[] saltBytes = digest.digest(keyBytes);

            //256bit
            PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 65536, 256);
            Key secretKey = factory.generateSecret(pbeKeySpec);

            byte[] key = new byte[32];
            System.arraycopy(secretKey.getEncoded(), 0, key, 0, 32);

            SecretKeySpec secret = new SecretKeySpec(key, "AES");
            setKeySpec(secret);
        }

        public static void MakeVector(String IV) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException{
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            MessageDigest digest = MessageDigest.getInstance("SHA-512");
            byte[] vectorBytes = IV.getBytes("UTF-8");
            byte[] saltBytes = digest.digest(vectorBytes);

            // 128bit
            PBEKeySpec pbeKeySpec = new PBEKeySpec(IV.toCharArray(), saltBytes, 65536, 128);
            Key secretIV = factory.generateSecret(pbeKeySpec);

            byte[] iv = new byte[16];
            System.arraycopy(secretIV.getEncoded(), 0, iv, 0, 16);

            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            setIvSpec(ivSpec);
        }

        public void decrypt(File source, File dest) throws Exception {
            Cipher c = Cipher.getInstance(blockNPadding);
            c.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            fileProcessing(source, dest, c);
        }

        public  void fileProcessing(File source, File dest, Cipher c) throws Exception{
            InputStream input = null;
            OutputStream output = null;

            try{
                input = new BufferedInputStream(new FileInputStream(source));
                output = new BufferedOutputStream(new FileOutputStream(dest));
                byte[] buffer = new byte[input.available()];
                int read = -1;
                while((read = input.read(buffer)) != -1){
                    output.write(c.update(buffer, 0, read));
                }
                byte[] deryptedBytes = c.doFinal(buffer); // -----------------------> Error!! Showing! 
                byte[] decodedBytes = Base64.getDecoder().decode(deryptedBytes);
                String decodeString = new String(decodedBytes, "UTF-8");
                decodedBytes = decodeString.getBytes(StandardCharsets.UTF_8);
                output.write(decodedBytes);

            }finally{
                if(output != null){
                    try{output.close();}catch(IOException e){}
                }
                if(input != null){
                    try{input.close();}catch(IOException e){}
                }
            }
        }

J'ai vérifié comme ci-dessous.

  1. Clé de vérification et IV en C #

    //Key Verification
        var salt = Convert.ToBase64String(saltBytes);
                Console.Write("Salt Result : ");
                Console.WriteLine(salt);

        var result_test = Convert.ToBase64String(result.GetBytes(32));
                Console.Write("Key Test Result: ");
                Console.WriteLine(result_test);
    //IV Verification (Salt is Using same code)
        var result_test = Convert.ToBase64String(result.GetBytes(16));
                Console.Write("IV Test Result: ");
                Console.WriteLine(result_test);
  1. Clé de vérification et IV en Java

    //Key Verification
        /* print Salt */
        String base64 = Base64.getEncoder().encodeToString(saltBytes);
        System.out.println("Salt Result : " + base64);

        /* print Key */
        String result_test = Base64.getEncoder().encodeToString(key);
        System.out.println("Key Test Result : " + result_test);

        /* print generated Key */
        System.out.println("Secret Key Result : " + Base64.getEncoder().encodeToString(secret.getEncoded()));

    //IV Verification (Salt is Using same code)
        /* print IV */
        String result_test = Base64.getEncoder().encodeToString(iv);
        System.out.println("IV Test Result : " + result_test);

        /* print generated IV */
        System.out.println("IV Result : " + Base64.getEncoder().encodeToString(ivSpec.getIV()));

Mis à jour

c # .NETFramework 4.5 / Java8 modifié ce que @Topaco a dit et a confirmé qu'il a bien fonctionné.

Je veux vous dire merci beaucoup à @Topaco et @ Gusto2, et je vais apporter des modifications aux pièces qui ont été modifiés dans la sécurité, comme @ dit Gusto2!

Topaco:

1) Dans le C # Encrypt-method le texte brut est crypté d' abord, puis base64. Ainsi, dans le processus de déchiffrement des données doivent être décodées base64 d' abord, puis déchiffrées. À l' heure actuelle , cela est géré dans le mauvais ordre dire que les données sont décryptées en premier et ensuite décodées. Par conséquent, dans le Java fileProcessing-method remplacer

while((read = input.read(buffer)) != -1){
    output.write(c.update(buffer, 0, read));
}

avec

while((read = input.read(buffer)) != -1) {
    byte[] bufferEncoded = buffer;
    if (read != buffer.length) { 
        bufferEncoded = Arrays.copyOf(buffer, read);
    }
    byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
    output.write(c.update(bufferDecoded));
}

2) Il n'est pas nécessaire de passer buffer(ou bufferDecoded) à la doFinal-method, puisque cela a déjà été fait dans le update-method. Donc,

byte[] deryptedBytes = c.doFinal(buffer);

doit être remplacé par

output.write(c.doFinal());

3) Etant donné que le décodage base64 est déjà fait en 1) dans la try-bloc toutes les lignes qui suivent la doFinal-Déclaration doivent être enlevés. Dans l' ensemble, cela se traduit par

try {
    input = new BufferedInputStream(new FileInputStream(source));
    output = new BufferedOutputStream(new FileOutputStream(dest));
    byte[] buffer = new byte[input.available()];
    int read = -1;
    while((read = input.read(buffer)) != -1) {
        byte[] bufferEncoded = buffer;
        if (read != buffer.length) { 
            bufferEncoded = Arrays.copyOf(buffer, read);
        }
        byte[] bufferDecoded = Base64.getDecoder().decode(bufferEncoded);
        output.write(c.update(bufferDecoded));
    }
    output.write(c.doFinal()); 
}

4) La taille de la mémoire tampon doit être un multiple de quatre, afin d'assurer une base64-décodage approprié. Ainsi, il est plus fiable pour remplacer

byte[] buffer = new byte[input.available()];

avec

byte[] buffer = new byte[4 * (input.available() / 4)];

Tant que les données sont lues en voir un morceau (qui est pas garanti, par exemple https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream. html # disponible () ) il n'y a pas de problème. Cependant, si les données sont lues en plusieurs morceaux , il est important de lire un multiple de 4 octets, sinon le décodage base64 échouera. Cela peut être facilement démontrables en utilisant une taille de mémoire tampon qui est un multiple de 4. Ce point doit également être envisagée si la taille de la mémoire tampon est explicitement défini en ce qui concerne les fichiers plus volumineux.

Je suppose que tu aimes

Origine http://43.154.161.224:23101/article/api/json?id=211380&siteId=1
conseillé
Classement