java中常用的对称加密算法

一 常用的对称加密算法

对称加密算法简单来讲就是加密和解密使用同一个密钥,并且加密解密互为逆运算,如加法和减法,先加密再解密与先解密后加密都能得到原结果,常用的对称加密算法有DES;3DES(二倍长,三倍长);AES;

3DES是DES扩展,3DES使用DES转换:
3DES 2倍长密钥长度16个字节,使用前八个字节对数据des加密,后八个字节对数据des解密,再用前八个字节对数据des加密
3DES 3倍长密钥长度24个字节,使用前八个字节对数据des加密,中间八个字节对数据des解密,再用后八个字节对数据des加密
算法具体实现推荐文章:
DES算法简介
AES算法简介

1>分组模式与填充

1:分组模式

<1>ECB模式:
这里写图片描述

对数据按照block长度(des,3DES为8个字节,AES16字节)分成多组b1,b2……bn(不足按设置的填充模式填充),再用密钥对每组分别加密

<2>CBC模式:
这里写图片描述

对数据按照block长度(des,3DES为8个字节,AES 16字节)分成多组b1,b2……bn(不足按设置的填充模式填充),用给定IV向量与第一组异或运算,对异或结果加密生成密文块1,再用密文块与第二明文块异或运算对异或结果加密生成密文块2,以此类推

两种分组模式比较:
1》ECB模式速度比CBC模式块。—>ECB不用等上一个块的计算结果,可以多线程计算
2》ECB模式不会传递误差。—>一组计算错误不会影响其他组
3》ECB模式容易被攻击。—>每组加密因子一样,加密实例多,易被破解

2: 填充

<1>NoPadding:
数据不填充,需要满足条件(n*block)
<2>PKCS5Padding:

如:des/3des block 长度为8个字节
数据长度为8n末尾填充8个08
数据长度为8n+m(0<m<8)末尾填充8-m个8-m 如:0102030405060708 09 --->0102030405060708 0907070707070707

<3>ISO10126Padding

如:aes block 长度为16个字节
数据长度为16n末尾填充15个随机字节和一个长度10(16进制10十进位16)
数据长度为16n+m(0<m<16)末尾填充16-m个字节,16-m-1个随机字节和一个长度字节
如:0102030405060708 0910 --->0102030405060708 0910+ 5个随机字节+06

JCE中AES支持五中模式:CBC,CFB,ECB,OFB,PCBC;支持三种填充:NoPadding,PKCS5Padding,ISO10126Padding。不支持SSL3Padding。不支持“NONE”模式。

其他分组模式 :分组模式和填充

2>具体实现与应用

     /**
     * 对称加密解密
     *
     * @param alg    算法
     *               "DES/ECB/NoPadding" "DES/ECB/PKCS5Padding" "DES/CBC/NoPadding" "DES/CBC/NoPadding"
     *               "DESede/ECB/NoPadding" "DESede/ECB/PKCS5Padding" "DESede/CBC/PKCS5Padding"  "DESede/CBC/PKCS5Padding"
     *               "AES/ECB/NOPADDING" "AES/ECB/PKCS5Padding" "AES/CBC/NOPADDING" "AES/CBC/PKCS5Padding"
     *               "AES/ECB/ISO10126Padding"
     * @param key    密钥 长度需要按照对应加密算法
     * @param opMode 模式,加密还是解密 {@link  Cipher#ENCRYPT_MODE }{@link Cipher#DECRYPT_MODE}
     * @param data   待加密的数据
     * @param iv     CBC模式向量
     * @return 加密后的数据
     */
    public static byte[] SymmetricAlg(String alg, byte[] key, int opMode, byte[] data, byte[] iv) throws Exception {
        IvParameterSpec ivParameterSpec = null;
        if (iv != null && iv.length != 0) {
            ivParameterSpec = new IvParameterSpec(iv);
        }
        SecretKey secretKey = new SecretKeySpec(key, alg);
        Cipher cipher = Cipher.getInstance(alg);
        cipher.init(opMode, secretKey, ivParameterSpec);
        return cipher.doFinal(data);
    }

alg 上面的不全还有其他的分组模式和填充

注意点:
1》加密解密需要对应的分组模式和填充要一致
2》alg 传 “des”等不传分组模式和填充时都有一个默认值,不同平台默认值可能不同,最好是显示传参数
3》密钥长度问题:des 的密钥长度为8字节;DESede(3des) 的密钥长度为16个字节或者24个字节(2倍长或者3倍长);AES的密钥长度为16个字节,24个字节或者32个字节都可以(AES-128,AES-192或者AE-256)
4》数据长度 NoPadding时,des/3des 为8n,aes 为16n

二 字符编码

1>常用的字符编码有Unicode、ASCII、GBK、GB2312、UTF-8 还有一个特殊的编码 base64 ,区别 如UTF-8等编码都是与字符集对应的,如果不在编码表内就会乱码,而Base64编码是从二进制到字符的过程,还都是可打印字符,不会出现乱码,所以常在加密后使用Base64处理显示文本,还有一种是直接显示对应字节数组的16进制字符串

2>加密和base64

        String test = "对称加密AES";
        byte[] input = test.getBytes("utf-8");
        byte[] key = BytesUtils.hexStr2Bytes("111111111111111111111111111222221111111111122222");
        byte[] result = SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
        String base64Out = Base64.encodeToString(result, Base64.DEFAULT);
        Log.e(TAG, "base64:" + base64Out);
        input = Base64.decode(base64Out, Base64.DEFAULT);
        result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);
        Log.e(TAG, "base64:" + new String(result, "utf-8"));

打印结果:
base64:gMb8YHWx8mfaAKYzvkROEA==
原数据:对称加密AES

3>加密和显示16进制字符串

        String test = "对称加密AES";
        byte[] input = test.getBytes("utf-8");
        byte[] key = BytesUtils.hexStr2Bytes("111111111111111111111111111222221111111111122222");
        byte[] result = SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
        String hexOut = BytesUtils.byte2HexStr(result);
        Log.e(TAG, "hex:" + hexOut);
        input = BytesUtils.hexStr2Bytes(hexOut);
        result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);
        Log.e(TAG, "原数据:" + new String(result, "utf-8"));

打印结果:
hex:80C6FC6075B1F267DA00A633BE444E10
原数据:对称加密AES

4>对密钥的处理

上面密钥都是二进制的不利于记忆,一般处理是对字符串密钥做摘要(md5)当做计算密钥

        String keyStr = "我的密钥";
        String test = "对称加密AES";
        byte[] input = test.getBytes("utf-8");
        byte[] key = MessageDigestUtils.MD5(keyStr.getBytes());
        byte[] result = SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.ENCRYPT_MODE, input, null);
        String base64Out = Base64.encodeToString(result, Base64.DEFAULT);
        Log.e(TAG, "base64:" + base64Out);
        input = Base64.decode(base64Out, Base64.DEFAULT);
        result = SymmetricEncryptionAlgUtils.SymmetricAlg("AES/ECB/PKCS5Padding", key, Cipher.DECRYPT_MODE, input, null);
        Log.e(TAG, "原数据:" + new String(result, "utf-8"));
    }

打印结果:
base64:cfnxIg2ngoORziek5SX/kA==
原数据:对称加密AES

BytesUtils 工具类将16进制字符串装换成对应字节数组,和字节数组转化成对应16进制字符串

public class BytesUtils {
    /**
     * 字节数组转换成十六进制大写字符串
     *
     * @param b
     * @return
     */
    public static String byte2HexStr(byte[] b) {
        StringBuilder hexSb = new StringBuilder();
        String stmp = "";
        int len = b.length;
        for (int n = 0; n < len; n++) {
            stmp = (Integer.toHexString(b[n] & 0xFF));
            if (stmp.length() == 1)
                hexSb.append("0" + stmp);
            else
                hexSb.append(stmp);
        }
        return hexSb.toString().toUpperCase();
    }

    /**
     * 十六进制字符串转换成字节数组
     *
     * @param hexStr
     * @return
     */
    public static byte[] hexStr2Bytes(String hexStr) {
        int len = hexStr.length();
        if (len % 2 != 0) {
            throw new RuntimeException("十六进制字符串长度不对");
        }
        byte[] b = new byte[hexStr.length() / 2];
        int bLen = b.length;
        int j = 0;
        for (int i = 0; i < bLen; i++) {
            char c0 = hexStr.charAt(j++);
            char c1 = hexStr.charAt(j++);
            b[i] = (byte) ((parse(c0) << 4) | parse(c1));
        }
        return b;
    }
    /**
     * 十六进制字符串转换成字节数组
     *
     * @param hexStr
     * @return
     */
    public static byte[] hexStr2Bytes1(String hexStr) {
        int len = hexStr.length();
        if (len % 2 != 0) {
            throw new RuntimeException("十六进制字符串长度不对");
        }
        byte[] b = new byte[hexStr.length() / 2];
        int bLen = b.length;
        int j = 0;
        String tem="";
        for (int i = 0; i < bLen; i++) {
            tem=hexStr.substring(2*i,2*i+2);
            b[i]= (byte) Integer.parseInt(tem,16);
        }
        return b;
    }
    private static int parse(char c) {
        if (c >= 'a')
            return (c - 'a' + 10) & 0x0f;
        if (c >= 'A')
            return (c - 'A' + 10) & 0x0f;
        return (c - '0') & 0x0f;
    }
 
}

三 总结

密钥:自己生成,与服务器协商,对用户输入密码做摘要(md5),需要注意长度
加密算法:选取其中一种加密算法,分组模式和填充 (具体选择看需求)
需要加密数据:获取对应加密数据的编码,需要注意长度,加密的分组模式和填充
加密后的数据显示:base64编码显示和16进制显示
解密回原数据:将加密后显示的数据转换成对应加秘数据,对加密后数据解密(注意分组和填充模式),解密后转换成原来编码数据(注意编码字符集)


此文要是对你有帮助,如果方便麻烦点个赞,谢谢!!!

猜你喜欢

转载自blog.csdn.net/kingyc123456789/article/details/79072090