使用AES ECB模式进行加密和解密在某些情况下会出现“BadPaddingException”错误

5
在安卓/Java应用中,
   public static void setKey(String myKey) {

        MessageDigest sha = null;
        try {
            key = new byte[]{(byte) '5', (byte) 'F', (byte) '8', (byte) 'p', (byte) 'J', (byte) 't', (byte) 'v', (byte) 'U', (byte) 'm', (byte) 'q', (byte) 'k', (byte) '7', (byte) 'A', (byte) 'M', (byte) 'v', (byte) 'b', (byte) 'q', (byte) 'o', (byte) 'H', (byte) 'M', (byte) '9', (byte) 'a', (byte) 'p', (byte) '4', (byte) '9', (byte) 'm', (byte) 'c', (byte) 'u', (byte) 'u', (byte) '5', (byte) 'B', (byte) 'X'};
            System.out.println(new String(key, "UTF-8"));
            secretKey = new SecretKeySpec(key, "AES");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public static String encrypt(String strToEncrypt) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            setEncryptedarr(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
            setEncryptedString(String.valueOf(Base64.encode(cipher.doFinal(strToEncrypt.getBytes("UTF-8")), Base64.DEFAULT)));
            //setEncryptedString(Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Error while encrypting: " + e.toString());
        }
        return null;
    }

    public static String decryptbyte(byte[] strToDecrypt) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            setDecryptedarr(cipher.doFinal(strToDecrypt));
            System.out.println("encrypt : decropted size : " + getDecryptedarr().length);
            setDecryptedString(new String(cipher.doFinal(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error wnhile decrypting: " + e.toString());
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String strToDecrypt) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            setDecryptedarr(cipher.doFinal(Base64.decode(strToDecrypt, Base64.DEFAULT)));
            setDecryptedString(new String(cipher.doFinal(Base64.decode(strToDecrypt, Base64.DEFAULT))));

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Error while decrypting: " + e.toString());

        }
        return null;
    }

我正在处理这段代码:http://aesencryption.net/。当使用encryptbyte和decryptbyte方法时,它们接受byte[]参数并且运行良好。我测试了加密结果,在这个网站http://aesencryption.net/上。
但是,我需要解密来自服务器的数据(响应是字符串),当我将该字符串转换为byte[]时,代码总是抛出异常:javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length。
我认为问题在于将字符串转换为byte[]时会改变其格式,请问正确的转换方式是什么?请给予建议。
这段代码会抛出异常:
try {
    AES.setKey("");
    final String strToDecrypt = "tATTXSdXI4w0oiu/fzgpyA==";
    AES.decryptbyte(toBytes(strToDecrypt.toCharArray()));
} catch (Exception ex) {
    ex.printStackTrace();
}

这是一个异常。

01-21 15:24:55.861 15700-15700/ W/System.err: javax.crypto.IllegalBlockSizeException: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length
01-21 15:24:55.861 15700-15700/ W/System.err:     at com.android.org.conscrypt.NativeCrypto.EVP_CipherFinal_ex(Native Method)
01-21 15:24:55.861 15700-15700/ W/System.err:     at com.android.org.conscrypt.OpenSSLCipher.doFinalInternal(OpenSSLCipher.java:430)
01-21 15:24:55.861 15700-15700/ W/System.err:     at com.android.org.conscrypt.OpenSSLCipher.engineDoFinal(OpenSSLCipher.java:466)
01-21 15:24:55.861 15700-15700/ W/System.err:     at javax.crypto.Cipher.doFinal(Cipher.java:1340)
 01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2648)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2769)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.ActivityThread.access$900(ActivityThread.java:177)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1430)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.os.Looper.loop(Looper.java:135)
01-21 15:24:55.861 15700-15700/ W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5910)
01-21 15:24:55.861 15700-15700/ W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
01-21 15:24:55.861 15700-15700/ W/System.err:     at java.lang.reflect.Method.invoke(Method.java:372)
01-21 15:24:55.861 15700-15700/ W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1405)
01-21 15:24:55.861 15700-15700/ W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1200)

数据在哪里?添加一些十六进制转储并检查它们。如果您了解各种编码,这将有所帮助。此外,ECB模式是不安全的,字符串作为密钥也是如此,只有在您想说您使用了加密但实际上不需要安全性时才适用。 - zaph
2个回答

2
最后一行代码:strToDecrypt = "tATTXSdXI4w0oiu/fzgpyA==" 似乎采用了base64编码,注意到末尾有==字符。这是有道理的,因为加密是基于数据字节而不是字符串的。

Base64 "tATTXSdXI4w0oiu/fzgpyA=="
十六进制:B404D35D2757238C34A22BBF7F3829C8(16个字节)。

你需要将Base64解码成数据字节以便于代码使用:

AES.setKey("");
final String strToDecrypt = "tATTXSdXI4w0oiu/fzgpyA==";
AES.decryptbyte(toBytes(strToDecrypt.toCharArray()));

请注意上面的代码中使用了一个空字符串作为键名,这是错误的做法。

服务器的响应是采用Base64编码的,我将其放入String中,然后转换为字节数组进行解密,在这种情况下会出现异常。请提供处理String的建议。 关于Base64,我在网站http://aesencryption.net/上对其进行了测试,您也可以尝试一下。 密钥是预定义的,请使用.setKey("")来设置预定义密钥,以上请检查setKey()方法。 请提供建议。 - Wael Abo-Aishah
1
加密原语接收数据字节并生成数据字节。有些实现会在加密之前和/或之后添加编码,但这不是加密的一部分。看起来你似乎遇到了编码方面的问题。你真的需要花时间学习字符串及其编码、Base64以及为什么使用它、十六进制和纯数据字节的细节。 - zaph
我解决了,我修复了这个问题。通过将加密类型更改为AES / ECB / ZeroBytePadding,可以解决与Base64和byte[]转换相关的问题,并进行解密操作。 - Wael Abo-Aishah
无法修复此问题,因为在加密时使用了PKCS#5填充:Cipher.getInstance("AES/ECB/PKCS5Padding")。这只是关闭了去除填充的功能,因此可能会出现填充错误。 - zaph

0

我解决了这个问题...... 服务器使用无填充和ZeroBytePadding模式进行AES加密是唯一可行的模式...... 工作代码:

public class AESCryptt {
    private static final String TAG = "AESCrypt";
    private static final String AES_MODE = "AES/ECB/ZeroBytePadding";
    private static final String CHARSET = "UTF-8";//
    public static boolean DEBUG_LOG_ENABLED = false;


    private static SecretKeySpec generateKey(final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] key = new byte[]{your key in byte here (byte) a, (byte) f , ..... };
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        return secretKeySpec;
    }


    /**
     * Encrypt and encode message using 256-bit AES with key generated from password.
     *
     *
     * @param password used to generated key
     * @param message the thing you want to encrypt assumed String UTF-8
     * @return Base64 encoded CipherText
     * @throws GeneralSecurityException if problems occur during encryption
     */
    public static String encrypt(final String password, String message)
            throws GeneralSecurityException {
        try {
            final SecretKeySpec key = generateKey(password);
            log("message", message);
            byte[] cipherText = encrypt(key, ivBytes, message.getBytes(CHARSET));
            //NO_WRAP is important as was getting \n at the end
            String encoded = String.valueOf(Base64.encodeToString(cipherText, Base64.NO_PADDING ));
            log("Base64.NO_WRAP", encoded);
            return encoded;
        } catch (UnsupportedEncodingException e) {
            if (DEBUG_LOG_ENABLED)
                Log.e(TAG, "UnsupportedEncodingException ", e);
            throw new GeneralSecurityException(e);
        }
    }


    /**
     * More flexible AES encrypt that doesn't encode
     * @param key AES key typically 128, 192 or 256 bit
     * @param iv Initiation Vector
     * @param message in bytes (assumed it's already been decoded)
     * @return Encrypted cipher text (not encoded)
     * @throws GeneralSecurityException if something goes wrong during encryption
     */
    public static byte[] encrypt(final SecretKeySpec key, final byte[] iv, final byte[] message)
            throws GeneralSecurityException {
        final Cipher cipher = Cipher.getInstance(AES_MODE);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, key);//, ivSpec
        byte[] cipherText = cipher.doFinal(message);
        log("cipherText", cipherText);
        return cipherText;
    }


    /**
     * Decrypt and decode ciphertext using 256-bit AES with key generated from password
     *
     * @param password used to generated key
     * @param base64EncodedCipherText the encrpyted message encoded with base64
     * @return message in Plain text (String UTF-8)
     * @throws GeneralSecurityException if there's an issue decrypting
     */
    public static String decrypt(final String password, String base64EncodedCipherText)
            throws GeneralSecurityException {

        try {
            final SecretKeySpec key = generateKey(password);
            log("base64EncodedCipherText", base64EncodedCipherText);
            byte[] decodedCipherText = Base64.decode(base64EncodedCipherText, Base64.DEFAULT);
            log("decodedCipherText", decodedCipherText);
            byte[] decryptedBytes = decrypt(key, ivBytes, decodedCipherText);
            log("decryptedBytes", decryptedBytes);
            String message = new String(decryptedBytes, CHARSET);
            log("message", message);
            return message;
        } catch (UnsupportedEncodingException e) {
            if (DEBUG_LOG_ENABLED)
                Log.e(TAG, "UnsupportedEncodingException ", e);
            throw new GeneralSecurityException(e);
        }
    }


    /**
     * More flexible AES decrypt that doesn't encode
     *
     * @param key AES key typically 128, 192 or 256 bit
     * @param iv Initiation Vector
     * @param decodedCipherText in bytes (assumed it's already been decoded)
     * @return Decrypted message cipher text (not encoded)
     * @throws GeneralSecurityException if something goes wrong during encryption
     */
    public static byte[] decrypt(final SecretKeySpec key, final byte[] iv, final byte[] decodedCipherText)
            throws GeneralSecurityException {
        final Cipher cipher = Cipher.getInstance(AES_MODE );
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, key);//, ivSpec
        byte[] decryptedBytes = cipher.doFinal(decodedCipherText);
        log("decryptedBytes", decryptedBytes);
        return decryptedBytes;
    }




    private static void log(String what, byte[] bytes) {
        if (DEBUG_LOG_ENABLED)
            Log.d(TAG, what + "[" + bytes.length + "] [" + bytesToHex(bytes) + "]");
    }

    private static void log(String what, String value) {
        if (DEBUG_LOG_ENABLED)
            Log.d(TAG, what + "[" + value.length() + "] [" + value + "]");
    }


    /**
     * Converts byte array to hexidecimal useful for logging and fault finding
     * @param bytes
     * @return
     */
    private static String bytesToHex(byte[] bytes) {
        final char[] hexArray = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
                '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] hexChars = new char[bytes.length * 2];
        int v;
        for (int j = 0; j < bytes.length; j++) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    private AESCryptt() {
    }
}

你可以这样调用:

    try {
        header = AESCryptt.encrypt("the key is fixed is this case",header);
    } catch (Exception ex) {
        ex.printStackTrace();
        System.out.println("eee , exception during encrypt log in data . . . .");
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接