Проблема с шифрованием из-за заполнения?

1 CasaDelGato [2017-03-12 20:41:00]

Я использую класс шифрования для некоторых передач данных. Я продолжаю получать эту ошибку:

javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:313)
at javax.crypto.Cipher.doFinal(Cipher.java:2131)
at com.casadelgato.util.Encryption.decrypt(Encryption.java:138)
at com.casadelgato.util.Encryption.decryptBase64(Encryption.java:124)
at com.casadelgato.util.Encryption.decryptBase64ToString(Encryption.java:109)
at com.casadelgato.util.Encryption.main(Encryption.java:156)

Странно, я не понимаю, если я запускаю расшифровку с тем же самым объектом шифрования, который я зашифровал. Я получаю сообщение об ошибке, когда новый объект Encryption пытается расшифровать. Я воспроизвел это с помощью main() в приведенном ниже коде.

Второй дешифрованный вызов завершается с ошибкой. По-видимому, шифры сохраняют состояние между сеансами???

Как исправить это, чтобы другая программа могла расшифровать то, что было зашифровано в другом месте?

package com.casadelgato.util;

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

public class Encryption {
    private final byte[]        SALT    = {
                                (byte) 0x26, (byte) 0xe4, (byte) 0x11, (byte) 0xa3,
                                (byte) 0x07, (byte) 0xc6, (byte) 0x55, (byte) 0x42
    };

    private Cipher          ecipher;
    private Cipher          dcipher;

    public Encryption( String password) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException,
                            InvalidAlgorithmParameterException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        KeySpec spec = new PBEKeySpec(password.toCharArray(), SALT, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);
        System.out.println("Encryption: " + Arrays.toString(tmp.getEncoded()));

        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

        ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        ecipher.init(Cipher.ENCRYPT_MODE, secret);

        AlgorithmParameters params = ecipher.getParameters();
        byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();

        dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
    }

    /**
     * Encrypt the string and return the data encoded in base64
     *
     * @param encrypt String to encrypt
     * @return base64 coded encrypted string
     * @throws UnsupportedEncodingException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws Exception
     */
    public byte[] encryptStringToBase64( String encrypt) throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
        byte[] bytes = encrypt.getBytes("UTF8");
        return encryptToBase64(bytes);
    }

    /**
     * Encrypt a block of data and encode to Base64
     *
     * @param bytes
     * @return base64 encoded encrypted data
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws Exception
     */
    public byte[] encryptToBase64( byte[] bytes) throws IllegalBlockSizeException, BadPaddingException {
        byte[] encrypted = encrypt(bytes);
        return Base64.encodeBase64(encrypted);
    }

    /**
     * Encrypt a block of data
     *
     * @param plain
     * @return encryped data
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws Exception
     */
    public byte[] encrypt( byte[] plain) throws IllegalBlockSizeException, BadPaddingException {
        return ecipher.doFinal(plain);
    }

    /**
     * Decrypt a string that was encrypted and coded in base64
     *
     * @param base64
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws UnsupportedEncodingException
     * @throws Exception
     */
    public String decryptBase64ToString( byte[] base64) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
        byte[] decrypted = decryptBase64(base64);
        return new String(decrypted, "UTF8");
    }

    /**
     * Decrypt a Base64 encoded block
     *
     * @param base64
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws Exception
     */
    public byte[] decryptBase64( byte[] base64) throws IllegalBlockSizeException, BadPaddingException {
        byte[] decodedData = Base64.decodeBase64(base64);
        byte[] decrypted = decrypt(decodedData);
        return decrypted;
    }

    /**
     * Decrypt a binary array.
     *
     * @param encrypt
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws Exception
     */
    public byte[] decrypt( byte[] encrypt) throws IllegalBlockSizeException, BadPaddingException {
        return dcipher.doFinal(encrypt);
    }

    public static void main( String[] args) throws Exception {
        String messages[] = { "GETP", "Testing stuff that is longer" };
        String password = "SanityLost";

        try {
            Encryption app = new Encryption(password);
            Encryption app1 = new Encryption(password);

            for (String message : messages) {
                byte[] encrypted = app.encryptStringToBase64(message);
                System.out.println("Encrypted string is: " + new String(encrypted, "UTF-8") + ", " + encrypted.length);

                String decrypted = app.decryptBase64ToString(encrypted);
                System.out.println("Decrypted string is: " + decrypted);

                decrypted = app1.decryptBase64ToString(encrypted);
                System.out.println("App1 Decrypted string is: " + decrypted);
            }

        } catch (Exception e1) {
            e1.printStackTrace();
        }

        return;
    }
}

java encryption aes


2 ответа


3 Решение Artjom B. [2017-03-12 20:54:00]

Ваш dcipher объект получает свой IV от объекта ecipher. Проблема в том, что IV не зависит от пароля или ключа. Он генерируется случайным образом, если вы не устанавливаете его самостоятельно. Вы получите другой IV для дешифрования, чем для шифрования, если вы создадите второй экземпляр Encryption.

IV не должен быть секретным, поэтому вы можете просто отправить его вместе с зашифрованным текстом. Поскольку IV всегда имеет одинаковую длину (16 байт для AES/CBC), вы можете просто написать его перед зашифрованным текстом и прочитать его во время дешифрования. Для этого вам нужно будет переработать весь свой класс. Обратите внимание, что случайный IV важен для семантической безопасности.

Пока мы на нем. Вы также должны рандомизировать соль для каждого шифрования. Если мы предположим, что соль всегда 8 байт, а IV всегда 16 байт, легко создать следующий формат для зашифрованного сообщения:

salt | IV | ciphertext

Здесь | означает конкатенацию.

Вам не нужны два экземпляра Cipher. Просто используйте его и init его для шифрования или дешифрования непосредственно перед использованием, а не внутри конструктора.


1 CasaDelGato [2017-03-12 21:31:00]

Благодаря ответу Arjom, я исправил код:

package com.casadelgato.util;

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/**
 * General data encryption/decryption handling. Can do Strings or byte[].
 *
 * @author John Lussmyer
 */
public class Encryption {
    private String          password;
    private SecretKeyFactory    factory;

    public Encryption( String password) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException,
                            InvalidAlgorithmParameterException {
        this.password = password;
        factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

        return;
    }

    /**
     * Encrypt the string and return the data encoded in base64
     *
     * @param encrypt String to encrypt
     * @return base64 coded encrypted string
     * @throws UnsupportedEncodingException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidParameterSpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public byte[] encryptStringToBase64( String encrypt)    throws UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException,
                                        NoSuchAlgorithmException, NoSuchPaddingException, InvalidParameterSpecException {
        byte[] bytes = encrypt.getBytes("UTF8");
        return encryptToBase64(bytes);
    }

    /**
     * Encrypt a block of data and encode to Base64
     *
     * @param bytes
     * @return base64 encoded encrypted data
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidParameterSpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public byte[] encryptToBase64( byte[] bytes)    throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException,
                                    NoSuchPaddingException, InvalidParameterSpecException {
        byte[] encrypted = encrypt(bytes);
        return Base64.encodeBase64(encrypted);
    }

    /**
     * Encrypt a block of data
     *
     * @param plain
     * @return encryped data
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidParameterSpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public byte[] encrypt( byte[] plain)    throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, NoSuchAlgorithmException, NoSuchPaddingException,
                                InvalidParameterSpecException {
        byte[] salt = new byte[8];
        byte[] iv;
        SecureRandom rand = new SecureRandom();
        rand.nextBytes(salt);

        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);

        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);

        AlgorithmParameters params = cipher.getParameters();
        iv = params.getParameterSpec(IvParameterSpec.class).getIV();

        byte[] data = cipher.doFinal(plain);
        byte[] result = new byte[data.length + salt.length + iv.length];
        System.arraycopy(salt, 0, result, 0, salt.length);
        System.arraycopy(iv, 0, result, salt.length, iv.length);
        System.arraycopy(data, 0, result, salt.length + iv.length, data.length);

        return result;
    }

    /**
     * Decrypt a string that was encrypted and coded in base64
     *
     * @param base64
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public String decryptBase64ToString( byte[] base64) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException,
                                        NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeySpecException {
        byte[] decrypted = decryptBase64(base64);
        return new String(decrypted, "UTF8");
    }

    /**
     * Decrypt a Base64 encoded block
     *
     * @param base64
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public byte[] decryptBase64( byte[] base64) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
                                    NoSuchPaddingException, InvalidKeySpecException {
        byte[] decodedData = Base64.decodeBase64(base64);
        byte[] decrypted = decrypt(decodedData);
        return decrypted;
    }

    /**
     * Decrypt a binary array.
     *
     * @param encrypt
     * @return
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidKeySpecException
     * @throws NoSuchPaddingException
     * @throws NoSuchAlgorithmException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidKeyException
     * @throws Exception
     */
    public byte[] decrypt( byte[] encrypt)  throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
                                NoSuchPaddingException, InvalidKeySpecException {
        byte[] desalt = new byte[8];
        byte[] deiv = new byte[16];
        byte[] data = new byte[encrypt.length - 8 - 16];
        System.arraycopy(encrypt, 0, desalt, 0, desalt.length);
        System.arraycopy(encrypt, desalt.length, deiv, 0, deiv.length);
        System.arraycopy(encrypt, deiv.length + desalt.length, data, 0, encrypt.length - deiv.length - desalt.length);

        KeySpec spec = new PBEKeySpec(password.toCharArray(), desalt, 65536, 128);
        SecretKey tmp = factory.generateSecret(spec);

        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(deiv));

        return cipher.doFinal(data);
    }

    /**
     * Used to test the code.
     *
     * @param args ignored
     * @throws Exception
     */
    public static void main( String[] args) throws Exception {
        String messages[] = { "GETP", "Testing stuff that is longer" };
        String password = "SanityLost";

        try {
            Encryption app = new Encryption(password);
            Encryption app1 = new Encryption(password);

            for (String message : messages) {
                byte[] encrypted = app.encryptStringToBase64(message);
                System.out.println("Encrypted string is: " + new String(encrypted, "UTF-8") + ", " + encrypted.length);

                String decrypted = app.decryptBase64ToString(encrypted);
                System.out.println("Decrypted string is: " + decrypted);

                decrypted = app1.decryptBase64ToString(encrypted);
                System.out.println("App1 Decrypted string is: " + decrypted);
            }

        } catch (Exception e1) {
            e1.printStackTrace();
        }

        return;
    }
}