Cómo manejar BadPaddingException Durante encriptación AES256 en C # y descifrado en Java

Sean Sin :

No sé por qué un error que se avecina.

Excepción en el hilo "principal" javax.crypto.BadPaddingException: dado el bloque final no rellena correctamente. Tales problemas pueden surgir si se utiliza una llave mal durante el descifrado.

Entiendo que este error se produce cuando se utiliza la llave incorrecta durante el descifrado. Sin embargo, si nos fijamos en los resultados de las pruebas resultado a continuación, se puede ver que tanto C # y Java son los mismos (Clave, IV, La sal es codificado base 64).

  1. Resultado de Prueba C #

Resultado de Prueba C #

  1. Resultado de prueba de Java

Resultado de prueba de Java

Es lo mismo! (Key, IV, Salt)

Sin embargo, se genera el error BadpaddingException actual. ¿Cual podría ser el problema? Te adjunto mi archivo de origen.

  1. C # (cifrado)

    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 (descifrado)

    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){}
                }
            }
        }

He verificado la siguiente manera.

  1. Comprobación de las claves y 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. Comprobación de las claves y 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()));

Actualizado

c # .NETFramework 4.5 / Java8 modificado lo que dijo @Topaco y confirmó que funcionó bien.

Quiero decir muchas gracias a @Topaco y @ Gusto2, y voy a realizar cambios en las partes que han sido modificados en la seguridad, al igual que @ dijo Gusto2!

Topaco:

1) En el C # Encrypt-method el texto plano se encripta primero y luego codificado en Base64. Así, en el proceso de descifrado los datos deben ser Base64 decodificados-primero y luego descifrados. Actualmente esto se maneja en el orden equivocado decir, los datos se descifran primero y luego decodificados. Por lo tanto, en el Java fileProcessing-method reemplazar

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

con

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) No es necesario pasar buffer(o bufferDecoded) a la doFinal-method, puesto que ya se hizo en la update-method. Así,

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

debe ser reemplazado con

output.write(c.doFinal());

3) Dado que el Base64-decodificación ya se realiza en 1) en el try-bloque todas las líneas siguientes la doFinal-statement tienen que ser eliminado. En general, este resultado en

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) El tamaño de la memoria intermedia tiene que ser un múltiplo de 4, a fin de asegurar una Base64-decodificación adecuada. Por lo tanto, es más fiable para reemplazar

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

con

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

Siempre y cuando los datos se leen en un trozo (que no está garantizado, véase, por ejemplo https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream. html # disponible () ) no hay ningún problema. Sin embargo, si los datos se leen en varios trozos que es importante leer un múltiplo de 4 bytes, de lo contrario la base 64 de descodificación de fallará. Que puede ser fácilmente demostró mediante el uso de un tamaño de búfer que no es un múltiplo de 4. Este punto también se debe considerar si el tamaño del búfer es explícitamente definido con respecto a archivos más grandes.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=211382&siteId=1
Recomendado
Clasificación