Android使用AES加密时遇到的问题及解决办法

什么是AES加密,可查看博客 :https://blog.csdn.net/chenhuakang/article/details/80223202?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

需要注意的点有如下:

1、算法的名称与加密时是否相同

2、加密和解密时的模式、填充是否相同

3、加密和解密时的密钥是否相同

4、模式、填充是否是该算法支持的

会出现的问题有

1、InvalidAlgorithmParameterException异常expected IV length of 16 but was 8

解决办法:

/**
* 补码
* aes加密的向量为16位,des加密的向量为8位,当不足时需要补位,否则抛异常。
* 补码 这里不能补"0",要补 "\0",因为在ASCII码中"0"48,所以需要把0转译,故"\0"才为0
*
* @param iv
* @return
*/
private static byte[] getIv(String iv) {
StringBuffer buffer = new StringBuffer(16);
buffer.append(iv);
while (buffer.length() < 16) {
buffer.append("\0");
}
if (buffer.length() > 16) {
buffer.setLength(16);
}
return buffer.toString().getBytes();
}

2、javax.crypto.BadPaddingException: error:1e000065:Cipher functions:OPENSSL_internal:BAD_DECRYPT

导致这个问题的原因就是 密钥不对 ,要严格按照密钥的生成规则
解决办法可参考如下:
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String ENCODING = "utf-8";
private static final String ALGORITHM_AES = "AES";
private static final String KEY = "换成你自己的key"; //密钥 key跟后台协商一个即可,保持一致
private static final String IV = "换成你自己的iv"; // 密钥向量,这里的偏移量也需要跟后台一致,一般跟key一样就行

/**
 * 加密
 *
 * @param srcData 原数据
 * @return
 * @throws Exception
 */
public static String encryptAESCBC(String srcData) throws Exception {
    SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM_AES);
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    //使用CBC模式必须指定IvParameterSpec,且expected IV length of 16,即长度限制为16
    //如果报异常,IV.getBytes()换成第一点中的getIv(IV)方法即可
    cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
    byte[] encData = cipher.doFinal(srcData.getBytes(ENCODING));
    return parseByte2HexStr(encData);
}

/**
 * 解密
 *
 * @param encData 需解密数据
 * @return
 * @throws Exception
 */
public static String decryptAESCBC(String encData) throws Exception {
    SecretKeySpec keySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM_AES);
    Cipher cipher = Cipher.getInstance(ALGORITHM);
    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(IV.getBytes()));
    byte[] decryptData = cipher.doFinal(parseHexStr2Byte(encData));
    return new String(decryptData, ENCODING);
}

/**
 * 将16进制转换为二进制
 *
 * @param hexStr
 * @return
 */
public static byte[] parseHexStr2Byte(String hexStr) {
    if (hexStr.length() < 1)
        return null;
    byte[] result = new byte[hexStr.length() / 2];
    for (int i = 0; i < hexStr.length() / 2; i++) {
        int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
        int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
        result[i] = (byte)(high * 16 + low);
    }
    return result;
}

/**
 * 将二进制转换成16进制
 *
 * @param buf
 * @return
 */
public static String parseByte2HexStr(byte[] buf) {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < buf.length; i++) {
        String hex = Integer.toHexString(buf[i] & 0xff);
        if (hex.length() == 1) {
            hex = '0' + hex;
        }
        sb.append(hex.toUpperCase());
    }
    return sb.toString();
}

3、javax.crypto.IllegalBlockSizeException: error:1e00007b:Cipher functions:OPENSSL_internal:WRONG_FINAL_BLOCK_LENGTH 报这个问题的解决办法可参考 https://www.cnblogs.com/lsdb/p/9324372.html

4、加解密参考代码:

/**
* 加密
*
* @param srcData 原数据
* @param key 密钥
* @param iv 密钥向量
* @return
* @throws Exception
*/
public static byte[] encryptAESCBC(byte[] srcData, byte[] key, byte[] iv) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM_AES);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] encData = cipher.doFinal(srcData);
return encData;
}

/**
* 解密
*
* @param encData 需解密数据
* @param key 密钥
* @param iv 密钥向量
* @return
* @throws Exception
*/
public static String decryptAESCBC(byte[] encData, byte[] key, byte[] iv) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM_AES);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
return new String(cipher.doFinal(encData), Charset.forName(ENCODING));

}

发生错误时,先确认是否满足了需要注意的点。

《How Objects Work》第四章

面向对象编程的基本结构,对于程序员来说都不陌生,即类、多态、继承这三种结构。
1、“类”能给我们带来什么好处?
类可以帮我们汇总很多的变量和方法;可以帮我们隐藏功能,把不需要向外界透露的变量或者方法,进行很好的封装;可以在运行时,就能创建很多个实例,让其简单的实现该类内部的逻辑。
2、“多态”顾名思义,即可变为各种状态。能给我们带来什么好处?
它可以帮助我们的程序得以扩展。
3、“继承”相当于是我们数学中所学过的全集和子集。能给我们带来什么好处?
它整理了相似事物的共同点和不同点,然后把相同的点汇总在一起,帮我们去除重复部分。
4、这三种结构能给我们带来什么好处?
它为我们提供了去除冗余逻辑、进行整理,使我们工作变得轻松。
在编程中,如果仅仅使用这三种结构,也许不能提高程序的可维护性和可重用性。但是,这些结构,如果弄错了一个,也会让问题变得棘手。所以,别胡乱使用。我们目的是编写高质量、易维护、可重用的程序,而OOP只是我们实现该目的的一种手段而已。