目录

一、问题

二、解决源码


一、问题

在Java 后端发来的AES加密数据时,发现Android APP不能解密 而且Android 端 加密同样的数据返回的结果居然不一致,所以我在网上查询后,发现在SecureRandom在产生安全随机数时 Windows系统和Linux系统的机制不一样,所以在同样的java AES加密源码加密同意的数据产生不一样的加密结果。

下面我给出网络的常用的AES加密的源码。

网络常用的AES加解密源码:

/**
     * AES加密字符串
     *
     * @param content
     *            需要被加密的字符串
     * @param password
     *            加密需要的密码
     * @return 密文
     */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者

            kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出
                                                                    // 128位的key生产者
            //加密没关系,SecureRandom是生成安全随机数序列,password.getBytes()是种子,只要种子相同,序列就一样,所以解密只要有password就行

            SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥

            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回
                                                            // null。

            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥

            Cipher cipher = Cipher.getInstance("AES");// 创建密码器

            byte[] byteContent = content.getBytes("utf-8");

            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器

            byte[] result = cipher.doFinal(byteContent);// 加密

            return result;

        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }


/**
     * 解密AES加密过的字符串
     *
     * @param content
     *            AES加密过过的内容
     * @param password
     *            加密时的密码
     * @return 明文
     */
    public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者
            kgen.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kgen.generateKey();// 根据用户密码,生成一个密钥
            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, key);// 初始化为解密模式的密码器
            byte[] result = cipher.doFinal(content);
            return result; // 明文

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

AES模式: AES/ECB/PKCS5Padding 不需要自己分组补位,相应也是相对CBC模式安全性较低。

二、解决源码

我的解决方案是自己写AES的秘钥生成128位的Key和使用Java Base64加密生成加密数据字符串;这样可以避免因为系统的随机数机制不同而不能加解密。

我的AES源码:


import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES加密工具
 * <p>
 * 加密流程 AES -> BAse64
 * 解密流程 Base64 -> AES
 */
public class AESUtil {
    //补充字典
    private final static String DIC = "1231g81f5456hhssg84h1f9q3f2x789s";

    // 加密
    public static String encrypt(String str, String key) {
        if (str == null || "".equals(str))
            return null;
        if (key == null || "".equals(key))
            return null;
        try {
            byte[] enCodeFormat = generateKey(key);// 返回基本编码格式的密钥,如果此密钥不支持编码,则返回
            SecretKeySpec skey = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 创建密码器
            cipher.init(Cipher.ENCRYPT_MODE, skey);// 初始化为加密模式的密码器
            byte[] result = cipher.doFinal(str.getBytes("UTF-8"));// 加密
            return Base64.getUrlEncoder().encodeToString(result);
        } catch (Exception e) {
            LogUtil.e(e.toString());
            return null;
        }
    }

    // 解密
    public static String decrypt(String str, String key) {
        if (str == null || "".equals(str))
            return null;
        if (key == null || "".equals(key))
            return null;
        try {
            byte[] enCodeFormat = generateKey(key);// 返回基本编码格式的密钥
            SecretKeySpec skey = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, skey);// 初始化为解密模式的密码器
            byte[] base64 = Base64.getUrlDecoder().decode(str.getBytes("UTF-8"));
            byte[] result = cipher.doFinal(base64);
            return new String(result, "UTF-8"); // 明文
        } catch (Exception e) {
            LogUtil.e(e.getMessage());
            return null;
        }
    }

    /**
     * 自定义 秘钥补充
     *
     * @param key
     * @return
     * @throws Exception
     */
    private static byte[] generateKey(String key) throws Exception {
        byte[] dics = DIC.getBytes("UTF-8");
        byte[] bkeys = key.getBytes("UTF-8");
        byte[] keys = bkeys;
        int keylength = bkeys.length;
        if (keylength > 0 && keylength < 16) {
            keys = new byte[16];
            System.arraycopy(bkeys, 0, keys, 0, keylength);
            for (int i = keylength; i < 16; i++) {
                keys[i] = dics[i];
            }
        }

        if (keylength > 16 && keylength < 24) {
            keys = new byte[24];
            System.arraycopy(bkeys, 0, keys, 0, keylength);
            for (int i = keylength; i < 24; i++) {
                keys[i] = dics[i];
            }
        }

        if (keylength > 24 && keylength < 32) {
            keys = new byte[32];
            System.arraycopy(bkeys, 0, keys, 0, keylength);
            for (int i = keylength; i < 32; i++) {
                keys[i] = dics[i];
            }
        }

        if (keylength > 32) {
            keys = new byte[32];
            System.arraycopy(bkeys, 0, keys, 0, 32);
        }

        return keys;
    }

generateKey()的用于生成AES使用的128位秘钥;

注意:DIC这个秘钥补充字典 这个可以自己定义,但是一定要和java 后端一致。

加密解密时使用Base64我有相关的文章可以去参考一下:

自学Android开发 Java和Android SDK的Base64https://blog.csdn.net/Ym_quiet/article/details/123835335?spm=1001.2014.3001.5501

如果对您有一些意义,希望您给博主一些鼓励(点赞、关注、收藏),如果有错误欢迎大家评论。

Logo

更多推荐