算法概述

本文主要用了两个参数进行加密解密

一个key:秘钥,一个iv:偏移量。

如果不想要偏移量可根据下面两个步骤进行去除:

1.那cipher.init()方法只传入两个参数即可;

cipher.init(Cipher.ENCRYPT_MODE, deskey);

2.将"DESede/CBC/PKCS5Padding"改成"DESede/ECB/PKCS5Padding"即可

ECB模式和CBC模式的区别

CBC是密码分组链接模式
ECB是电码本模式

ECB模式:

优点:

    简单;
    有利于并行计算;
    误差不会被传递;

缺点:

    不能隐藏明文的模式;
    可能对明文进行主动攻击;

DES ECB(电子密本方式)其实非常简单,就是将数据按照8个字节一段进行DES加密或解密得到一段段的8个字节的密文或者明文,最后一段不足8个字节(一般补0或者F),按照需求补足8个字节进行计算(并行计算),之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。

CBC模式:

优点:

    不容易主动攻击,安全性好于ECB,是SSL、IPSec的标准;
    缺点:
    不利于并行计算;
    误差传递;
    需要初始化向量IV;

DES CBC(密文分组链接方式)有点麻烦,它的实现机制使加密的各段数据之间有了联系。其实现的机理如下:

  1. 首先将数据按照8个字节一组进行分组得到D1D2…Dn(若数据不是8的整数倍,用指定的PADDING数据补位)
  2. 第一组数据D1与初始化向量I异或后的结果进行DES加密得到第一组密文C1(初始化向量I为全零)
  3. 第二组数据D2与第一组的加密结果C1异或以后的结果进行DES加密,得到第二组密文C2
  4. 之后的数据以此类推,得到Cn
  5. 按顺序连为C1C2C3…Cn即为加密结果。

话不多说,直接上代码。(复制即可用)

Des3Utils :

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author: 慌途L
 * @desc: 3DES 加密/解密
 */
@Slf4j
public class Des3Utils {
    /**
     * 加密算法
     */
    private static final String KEY_ALGORITHM = "DESede";
    private static final String CIPHER_ALGORITHM = "DESede/CBC/PKCS5Padding";

    /**
     * 3DES 加密
     *
     * @param key   秘钥(24位)
     * @param iv    偏移量
     * @param data  需要加密的字符串
     * @return 返回加密的字符串
     */
    public static String encrypt(String key, String iv, String data) {
        try {
            DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
            SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
            Key deskey = keyfactory.generateSecret(spec);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
            byte[] bOut = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.encodeBase64String(bOut);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("3DES 解密错误:{}", e);
            throw new RuntimeException("3DES 解密错误");
        }
    }

    /**
     * 3DES 解密
     *
     * @param key   秘钥(24位)
     * @param iv    偏移量
     * @param data  需要解密的密文
     * @return 返回加密的字符串
     */
    public static String decrypt(String key, String iv, String data) {
        try {
            DESedeKeySpec spec = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));
            SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_ALGORITHM);
            Key deskey = keyfactory.generateSecret(spec);
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
            cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
            byte[] bOut = cipher.doFinal(Base64.decodeBase64(data.getBytes(StandardCharsets.UTF_8)));
            return new String(bOut, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("3DES 解密错误:{}", e);
            throw new RuntimeException("3DES 解密错误");
        }
    }

    public static void main(String[] args) {
        String data =  LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + "-" + String.format("%04d", (int) ((Math.random() * 9 + 1) * 1000));

        System.out.println("需要加密的字符串内容为:" + data);

        String des3EncodeCBC = encrypt("H2bsdDdfEEKpldjubTevcPQf", "53152654", data);
        System.out.println("加密后的字符串内容为:" + des3EncodeCBC);

        String des3DecodeCBC = decrypt("H2bsdDdfEEKpldjubTevcPQf", "53152654", des3EncodeCBC);
        System.out.println("解密后的字符串内容为:" + des3DecodeCBC);
    }
}