Bootstrap

Java应用DES加密

DES加密

之前没了解过这方面,这次由于公司需要,要求Java和C#对加密做一个互通加密。在这里做一个小结。

概念

DES(Data Encryption Standard)是一种对称加密算法,对称加密本质就是加密和解密都使用的是同一个密钥,因此密钥一定要保存好,如果泄露,可想而知后果是什么。

算法原理

这里我附上我在网上学习的链接如下:

  1. DES算法原理完整版
  2. 数据加密算法–详解DES加密算法原理与实现

Java代码实现

/**
     * DES加密
     *
     * @param strIn -- 待加密明文
     * @param secretkey -- 密钥
     * @return
     * @throws Exception
     * IvParameterSpec对象————初始化向量,使用DES进行加/解密时字节长度必须为8的倍数,
     * 否则会报错(java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 8 bytes long)
     *
     * 实例化Cipher加密对象,参数为加密方式,使用IvParameterSpec时,Cipher参数要使用CBC模式。
     * 加密方式为【DES】加密(JAVA封装的DES算法的默认模式————ECB模式)
     * 加密方式为【DES/CBC/PKCS5Padding】加密(密文分组链接方式————CBC模式)
     */
    public static String encrypt(String strIn, String secretkey) throws Exception {
        Key key = getKey(secretkey.getBytes("UTF-8")); // 不足8的倍数位,则填充/截取(密钥)
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(1, key);
        byte[] strInByte =  strIn.getBytes("UTF-8"); // 待加密明文字符串转字节数组
        byte[] arrB = cipher.doFinal(strInByte); // 加密明文字节数组,并返回加密后的字节数组
        System.out.println("待加密明文字符串转字节数组strInByte: " + strInByte);
        System.out.println("返回加密后的字节数组arrB: " + arrB);
        return byteArr2HexStr(arrB); // 字节数组转16进制字符串
    }

    /**
     * DES解密
     * @param strIn -- 待解密密文
     * @param secretkey -- 密钥
     * @return
     * @throws Exception
     */
    public static String decrypt(String strIn, String secretkey) throws Exception {
        Key key = getKey(secretkey.getBytes("UTF-8"));
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(2, key);
        byte[] param = cipher.doFinal(hexStr2ByteArr(strIn));
        return new String(param, "UTF-8");
    }

DESUtils工具类

自己整理的DESUtils工具类都贴下,大家可以练习练习,有问题欢迎留言哦,,代码如下:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * DES加密/解密工具类
 * <a href = "https://www.cnblogs.com/Lawson/archive/2012/05/20/2510781.html">JAVA和.NET使用DES对称加密的区别</a>
 *
 * @author zhang
 * @date 2020-04-28 16:09:10
 */
public class DESUtils {

    /**
     * 密钥算法
     * JAVA封装的DES算法的默认模式————ECB模式(电子密本方式)
     */
    private static final String KEY_ALGORITHM = "DES";
    /**
     * 加密方式(算法/模式/补码方式)
     * DES/CBC/PKCS5Padding————CBC模式(密文分组链接方式)
     */
    private static final String ENCRYPTION  = "DES/CBC/PKCS5Padding";
    /**
     * 编码格式
     */
    private static final String CHARSET  = "UTF-8";
    /**
     * 此类指定一个初始化向量zeroIv(使用 byteIV 中的字节作为 IV 来构造一个 IvParameterSpec 对象)(指定数组大小为8)
     * // 使用CBC模式,需要一个向量iv,可增加加密算法的强度
     * <a href="https://download.oracle.com/technetwork/java/javase/6/docs/zh/api/javax/crypto/spec/IvParameterSpec.html">javax.crypto.spec.IvParameterSpec</a>
     * <a href= "https://blog.csdn.net/qq_25816185/article/details/81626499">java使用AES算法的CBC模式加密</a>
     */
    private static final IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]);

    /**
     * 设置指定初始化向量值(指定值)
     */
    private static byte[] IV  = { 0x13, 0x24, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF };

    /**
     * DES加密
     *
     * @param strIn -- 待加密明文
     * @param secretkey -- 密钥
     * @return
     * @throws Exception
     * IvParameterSpec对象————初始化向量,使用DES进行加/解密时字节长度必须为8的倍数,
     * 否则会报错(java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 8 bytes long)
     *
     * 实例化Cipher加密对象,参数为加密方式,使用IvParameterSpec时,Cipher参数要使用CBC模式。
     * 加密方式为【DES】加密(JAVA封装的DES算法的默认模式————ECB模式)
     * 加密方式为【DES/CBC/PKCS5Padding】加密(密文分组链接方式————CBC模式)
     */
    public static String encrypt(String strIn, String secretkey) throws Exception {

//        IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]);
//        IvParameterSpec zeroIv = new IvParameterSpec(IV);
        Key key = getKey(secretkey.getBytes(CHARSET)); // 不足8的倍数位,则填充/截取(密钥)
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);

//        cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv); // 初始化(第一个参数是工作模式,第二个是密钥key,第三个是初始化向量)
        cipher.init(Cipher.ENCRYPT_MODE, key); // ENCRYPT_MODE = 1; (加密模式)

        byte[] strInByte =  strIn.getBytes(CHARSET); // 待加密明文字符串转字节数组
        byte[] arrB = cipher.doFinal(strInByte); // 加密明文字节数组,并返回加密后的字节数组
        System.out.println("待加密明文字符串转字节数组strInByte: " + strInByte);
        System.out.println("返回加密后的字节数组arrB: " + arrB);
        return byteArr2HexStr(arrB); // 字节数组转16进制字符串
    }

    /**
     * DES解密
     * @param strIn -- 待解密密文
     * @param secretkey -- 密钥
     * @return
     * @throws Exception
     */
    public static String decrypt(String strIn, String secretkey) throws Exception {
//        IvParameterSpec zeroIv = new IvParameterSpec(new byte[8]);
//        IvParameterSpec zeroIv = new IvParameterSpec(IV);
        Key key = getKey(secretkey.getBytes(CHARSET));
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, key); // DECRYPT_MODE = 2 (解密模式)
        byte[] param = cipher.doFinal(hexStr2ByteArr(strIn));
        return new String(param, CHARSET);
    }

    /**
     * 填充盐值(密钥),使用DES进行加/解密时字节长度必须为8的倍数,
     * 否则会报错(java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 8 bytes long)
     * @param arrBTmp
     * @return
     */
    private static Key getKey(byte[] arrBTmp) {
        byte[] arrB = new byte[8];
        for(int i = 0; i < arrBTmp.length && i < arrB.length; ++i) {
            arrB[i] = arrBTmp[i];
        }
        Key key = new SecretKeySpec(arrB, KEY_ALGORITHM);
        return key;
    }

    /**
     * 字节数组转换16进制字符串
     * @param arrB
     * @return
     * @throws Exception
     */
    public static String byteArr2HexStr(byte[] arrB) {
        int iLen = arrB.length;
        StringBuffer sb = new StringBuffer(iLen * 2);
        for(int i = 0; i < iLen; ++i) {
            int intTmp;
            for(intTmp = arrB[i]; intTmp < 0; intTmp += 256) {
            }
            if (intTmp < 16) {
                sb.append("0");
            }
            sb.append(Integer.toString(intTmp, 16));
        }
        return sb.toString();
    }

    /**
     * 16进制字符串转换字节数组
     * @param strIn
     * @return
     * @throws Exception
     */
    public static byte[] hexStr2ByteArr(String strIn) throws Exception {
        byte[] arrB = strIn.getBytes();
        int iLen = arrB.length;
        byte[] arrOut = new byte[iLen / 2];

        for(int i = 0; i < iLen; i += 2) {
            String strTmp = new String(arrB, i, 2);
            arrOut[i / 2] = (byte)Integer.parseInt(strTmp, 16);
        }

        return arrOut;
    }

    private static String initReturnData(String key, String data) {
        String str = "";
        try {
            if (!data.equals("")) {
                str = DESUtils.decrypt(replaceBlank(data), key);
            }
        } catch (Exception var4) {
            var4.printStackTrace();
            str = "{\"status\":false,\"msg\":\"请检查密钥或加密方式是否正确!\",\"data\":\"\"}";
        }

        return str;
    }

    /**
     * 检查字符串中是否包含特殊字符(换行符等),并去除
     * @param str
     * @return
     */
    private static String replaceBlank(String str) {
        String dest = "";
        if (str != null) {
            Pattern p = Pattern.compile("\t|\r|\n");
            Matcher m = p.matcher(str);
            dest = m.replaceAll("");
        }

        return dest;
    }


    /**
     * testZhang
     * @param args
     */
    public static void main(String[] args) {
        String strIn = "123456";
        String saltValue = "ZHANG";
        String encryptResult = ""; // 加密结果
        String decryptResult = ""; // 解密结果

        /**加密*/
        try {
            encryptResult = encrypt(strIn, saltValue);
        } catch (Exception e) {
            System.out.println("加密异常");
        }
        System.out.println("密钥: " + saltValue);
        System.out.println("加密result: " + encryptResult);

        /**解密*/
        try {
            decryptResult = decrypt(encryptResult, saltValue);
        } catch (Exception e) {
            System.out.println("解密异常");
        }
        System.out.println("密钥: " + saltValue);
        System.out.println("解密result1: " + decryptResult);


    }

}

总结

  1. 使用DES进行加/解密时字节长度必须为8的倍数,不然会报错
    错误信息如下:
    java.security.InvalidAlgorithmParameterException: Wrong IV length: must be 8 bytes long

  2. JAVA封装的【DES】算法的默认模式——ECB模式(电子密本方式),而C#不是,这点要特别注意,否则Java和C#加解密无法互通。

  3. 加密方式为【DES/CBC/PKCS5Padding】加密(密文分组链接方式————CBC模式)

  4. 加密方式这块有出现一个问题:实例化Cipher加密对象,参数为加密方式,使用IvParameterSpec对象时,Cipher参数要使用CBC模式(DES/CBC/PKCS5Padding),不然会报错,错误信息如下:

    java.security.InvalidAlgorithmParameterException: ECB mode cannot use IV
    IvParameterSpec 干什么用的?

参考链接

  1. JAVA和.NET使用DES对称加密的区别
  2. DES加密JAVA与C#通用

java应用相关

  1. cipher加密解密
  2. 安全体系(一)—— DES算法详解
  3. DES算法原理完整版
  4. Java实现DES加密解密

C#应用相关

  1. C#实现DES加密解密
  2. C# 发送Post请求(带参数)
;