常用加解密算法总结1-DES、TDES、3DES

概述

常用算法类别包括对称密钥算法、非对称密钥算法以及摘要算法
在了解具体类别算法之前,要先了解以下几个概念:

  1. 明文:原始信息
  2. 加密算法:以密钥为参数,对明文进行多种置换和转换的规则和步骤,变换结果为密文。
  3. 密钥:加密与解密算法的参数,直接影响对明文进行变换的结果
  4. 密文:对明文进行变换的结果
  5. 解密算法:加密算法的逆变换,以密文为输入、密钥为参数,变换结果为明文

这里写图片描述

算法类别

对称密钥算法

简单点理解,就是加密和解密所用的密钥是相同的,用哪个密钥对明文进行加密,就用哪个密钥对密文进行解密
常用的对称密钥算法有DES、AES、SM4

非对称密钥算法

言下之意,就是加密和解密所用的密钥不同。生成的密钥是一对的,一个为公钥,一个为私钥,当使用公钥对明文进行加密后,就用私钥对密文进行解密
常用的非对称密钥算法有RSA、DSA、SM2

摘要算法

将一串不定长的明文数据(消息)通过特定的算法,生成一串固定长度的摘要数据。摘要算法的特点是当明文数据(消息)的任何一部分被修改后,生成的摘要都不相同。
这样可以保证原明文数据在传输过程中没有被篡改,常用于数字签名、消息认证以及数据完整性检测
常用的摘要算法有HASH、MD5、SM3

本节主要是学习对称算法中的DES算法。

DES算法

DES是Data Encryption Standard(数据加密标准)的缩写。它是由IBM公司研制的一种对称密码算法,美国国家标准局于1977年公布把它作为非机要部门使用的数据加密标准,三十年来,它一直活跃在国际保密通信的舞台上,扮演了十分重要的角色。
DES是一种分组加密算法。分组加密算法指的是DES每次只能对固定8个字节的数据进行加密或者解密,若数据长度超过8个字节,则需要分组,每8个字节为一组,不足8个字节的需要填充8个字节。

DES算法原理

DES算法主要是通过移位、置换、扩展、压缩、异或等几种常用的数学运算对明文数据进行加密,从而加大破译的难度。
常用数学运算汇总:

  1. 移位或者循环移位。移位就是将一段数码按照规定的位数整体性地左移或右移。循环右移就是当右移时,把数码的最后的位移到数码的最前头,循环左移正相反。例如,对十进制数码12345678循环右移1位(十进制位)的结果为81234567,而循环左移1位的结果则为23456781。
  2. 置换。就是将数码中的某一位的值根据置换表的规定,用另一位代替。它不像移位操作那样整齐有序,看上去杂乱无章。这正是加密所需,被经常应用。
  3. 压缩。就是将一段数码压缩成比原来位数更短的数码。压缩方法有多种,例如,也可以用置换的方法,以表来规定压缩后的数码每一位的替代值。
  4. 异或。这是一种二进制布尔代数运算。异或的数学符号为⊕ , 参与异或运算的两数位如相等,则结果为0,不等则为1。


算法种类

主要包括单DES、TDES、3DES三种,三者都是使用DES算法,区别是密钥长度不同。

单DES
单DES**密钥为8个字节共64位**,有效密钥长度为56位,因为每个字节第8位为奇偶校验位,不参与加解密运算,因此第8位不相同的两个密钥对同一串明文数据进行加密后得到的结果是相同的。
举个例子(数据都是以16进制方式表示):

       //两组密钥校验位不同,加密结果相同
       密钥1     1111111111111111    第8位都为1
       数据    + 0102030405060708  
       结果    = 73331971A6E1AB01
------------------------------------- 
       密钥2     1010101010101010   第8位都为0
       数据    + 0102030405060708  
       结果    = 73331971A6E1AB01

奇偶校验位是用来做什么的呢?
顾名思义,这是用于密钥中的每个字节是否符合奇校验或者偶校验而保留的,当一个字节的二进制数位中的“1”的总个数为奇数时,则称该字节符合奇校验;总个数为偶数时,则称该字节符合偶校验。
就像前面举的例子,密钥 1111111111111111 是符合偶校验的,而密钥1010101010101010 则是符合奇校验的。


当今计算机的处理能力和速度越来越高,单DES算法56位密钥已经能够被破解,由此产生了TDES、2DES、3DES。它们都是单DES的变种,是对DES算法进行某种变型和改进。

TDES
又称为2DES。指的是密钥长度为16个字节,是原来单DES的两倍。
在使用TDES对数据进行加密时,需要先把密钥分成长度为8个字节的两部分K1和K2,明文数据和单DES相同分为D1D2…Dn。
然后对D1进行加密,加密过程为:

     使用K1对D1进行加密得到结果T1;
     使用K2对T1进行解密得到结果T2;
     使用K1对T2进行加密得到结果C1。

其中的加解密过程都是单DES算法,只是每次数据加密过程叠加了三次单DES的加解密算法。
举个例子:

密钥:11111111111111112222222222222222
数据:0102030405060708  

密钥分为两部分->    K11111111111111111
                  K22222222222222222

使用K1加密得到->   T173331971A6E1AB01
使用K2解密得到->   T2E3A76B63B05270CC
使用K1加密得到->   C1B8016234E48E685F

TDES加密最终结果为-> C1B8016234E48E685F


3DES
3DES指的是密钥长度为24个字节,是单DES的三倍。
3DES的加密过程和TDES类似,它是把密钥分成三部分K1K2K3,加密过程同样是加密解密加密,只是第三次单DES加密使用的是K3,而TDES用的是K1。
同样,举个例子:
密钥为 111111111111111122222222222222221111111111111111
数据为 0102030405060708
最终结果和前面的例子一样为 B8016234E48E685F

分组加密模式

对称加密分为分组加密序列密码
分组密码,也叫块加密(block cyphers),一次加密明文中的一个块。是将明文按一定的位长分组,明文组经过加密运算得到密文组,密文组经过解密运算(加密运算的逆运算),还原成明文组。
序列密码,也叫流加密(stream cyphers),一次加密明文中的一个位。是指利用少量的密钥(制乱元素)通过某种复杂的运算(密码算法)产生大量的伪随机位流,用于对明文位流的加密。
解密是指用同样的密钥和密码算法及与加密相同的伪随机位流,用以还原明文位流。

在分组加密算法中,有ECB,CBC,CFB,OFB这几种算法模式。

ECB模式(电子密码本模式:Electronic codebook)

ECB是最简单的块密码加密模式,加密前根据加密块大小(如DES为64位)分成若干块,之后将每块使用相同的密钥单独加密,解密同理。
这里写图片描述
—–ECB加密过程—–


这里写图片描述
—–ECB解密过程—–

ECB模式由于每块数据的加密是独立的因此加密和解密都可以并行计算,ECB模式最大的缺点是相同的明文块会被加密成相同的密文块,这种方法在某些环境下不能提供严格的数据保密性。

举个例子单DES算法的ECB模式
这种模式比较简单,各段数据分开加密,没有联系。
首先将明文数据按8个字节分成N组D1D2….Dn,最后一组不足8个字节时填充(一般为0x00或者0xFF)为8个字节。
然后,分别对D1、D2、…、Dn做加密,得到加密后的C1、C2、…、Cn,最后把加密后的分组数据拼在一起就得到最后的结果C1C2C3…Cn。

密钥为     1111111111111111   
数据为     01020304050607080102030405060708
计算过程:
数据分为两组->    D10102030405060708    
                D20102030405060708

使用密钥加密得到-> C173331971A6E1AB01
                 C273331971A6E1AB01

拼接得到最终结果-> C1C273331971A6E1AB0173331971A6E1AB01

CBC模式(密码分组链接:Cipher-block chaining)

CBC模式对于每个待加密的密码块在加密前会先与前一个密码块的密文异或然后再用加密器加密。第一个明文块与一个叫初始化向量的数据块异或。
这里写图片描述
—–CBC加密过程—–


这里写图片描述
—–CBC解密过程—–

CBC模式相比ECB有更高的保密性,但由于对每个数据块的加密依赖与前一个数据块的加密所以加密无法并行。与ECB一样在加密前需要对数据进行填充,不是很适合对流数据进行加密。

举个例子单DES算法的CBC模式
这种模式各段数据的加密有了联系,不是单独的。
首先和ECB模式一样,将明文数据按8个字节分成N组D1D2….Dn,最后一组不足8个字节时填充(一般为0x00或者0xFF)为8个字节。
然后使用密钥对D1进行加密得到密文C1,接着将C1和D2进行异或操作得到的结果,再使用密钥进行加密得到C2
以此类推,得到C3、C4、…、Cn
最后把前面加密获取到的分组数据拼在一起就得到最后的结果C1C2C3…Cn
还是拿前面那个例子的数据:

密钥为     1111111111111111   
数据为     01020304050607080102030405060708
计算过程:
数据分为两组->    D10102030405060708    
                D20102030405060708

使用密钥加密得到-> C173331971A6E1AB01
将C1D2进行异或得到->新D2: 72311A75A3E7AC09
再使用密钥加密得到-> C2C85E8384E36D1CD6

拼接得到最终结果-> C1C273331971A6E1AB01C85E8384E36D1CD6

CFB模式(密文反馈:Cipher feedback)

与ECB和CBC模式只能够加密块数据不同,CFB能够将块密文(Block Cipher)转换为流密文(Stream Cipher)。
这里写图片描述
—–CFB加密过程—–


这里写图片描述
—–CFB解密过程—–

注意:CFB、OFB和CTR模式中解密也都是用的加密器而非解密器。
CFB的加密工作分为两部分:

  • 将前一段加密得到的密文再加密;
  • 将第1步加密得到的数据与当前段的明文异或。

由于加密流程和解密流程中被块加密器加密的数据是前一段密文,因此即使明文数据的长度不是加密块大小的整数倍也是不需要填充的,这保证了数据长度在加密前后是相同的。

OFB模式(输出反馈:Output feedback)

OFB是先用块加密器生成密钥流(Keystream),然后再将密钥流与明文流异或得到密文流,解密是先用块加密器生成密钥流,再将密钥流与密文流异或得到明文,由于异或操作的对称性所以加密和解密的流程是完全一样的。
这里写图片描述
—–OFB加密过程—–


这里写图片描述
—–OFB解密过程—–

OFB与CFB一样都非常适合对流数据的加密,OFB由于加密和解密都依赖与前一段数据,所以加密和解密都不能并行。

填充模式

这里的填充指的是对于分组加密算法,需要将数据分成一个个固定长度的字节块,当长度不够时,需要使用规则进行填充。这里的规则指的就是填充模式。
DES算法是对64位数据的加密算法,如数据位数不足64位的倍数,需要填充,补充到64位的倍数。
NoPadding
API或算法本身不对数据进行处理,加密数据由加密双方约定填补算法。例如若对字符串数据进行加解密,可以补充\0或者空格,然后trim。

PKCS5Padding

  • 加密:数据字节长度对8取余,余数为m,若m>0,则补足8-m个字节,字节数值为8-m,即差几个字节就补几个字节,字节数值即为补充的字节数,若为0则补充8个字节的8 。
  • 解密:取最后一个字节,值为m,则从数据尾部删除m个字节,剩余数据即为加密前的原文。


DES算法实例

以下是使用java语言实现的DES算法工具类,封装了常用的加密、解密、计算checkvalue以及分组模式、填充模式的实现。


import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class DesCrypter {
    private DesCryptType type;
    private String groupMode; //分组模式 ECB、CBC、OFB、CFB
    private String fillMode; //填充模式 NoPadding、PKCS5Padding
    private byte[] initVector;

    public DesCrypter(DesCryptType type) {
        this(type, "ECB", "PKCS5Padding");
    }

    public DesCrypter(DesCryptType type, String groupMode) {
        this(type, groupMode, "PKCS5Padding");
    }

    public DesCrypter(DesCryptType type, String groupMode, String fillMode) {
        if (StringUtil.isEmpty(groupMode)) {
            groupMode = "ECB";
        }
        if (StringUtil.isEmpty(fillMode)) {
            fillMode = "PKCS5Padding";
        }
        this.type = type;
        this.groupMode = groupMode;
        this.fillMode = fillMode;
        initVector = HexUtil.hexStringToByte("0000000000000000");
    }

    /**
     * 生成随机密钥key
     * @return
     */
    public byte[] generateRandomKey() {
        byte[] result = null;
        String des = getDesString();
        SecretKey key = generateSecretKey(des, HexUtil.hexStringToByte("11111111111111111111111111111111"));
        if (key != null) {
            result = key.getEncoded();
        }
        return result;
    }

    private String getDesString() {
        String des = "";
        if (this.type == DesCryptType.DES) {
            des = "DES";
        } else {
            des = "DESede";
        }

        return des;
    }

    /**
     * 使用DES算法加密data
     * @param data 带加密的明文
     * @param keyBytes 密钥
     * @return 密文
     */
    public byte[] encrypt(byte[] data, byte[] keyBytes) {
        byte[] result = null;
        String des = getDesString();
        try {
            //生成密钥
            SecretKey key = generateSecretKey(des, keyBytes);
            if (key == null) {
                return null;
            }

            //加密
            String cipherStr = getCipherStr(data.length, groupMode, fillMode);
            Cipher cipher = Cipher.getInstance(cipherStr);
            if (groupMode.equals("ECB")) {
                cipher.init(Cipher.ENCRYPT_MODE, key);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(initVector));
            }
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 设置分组模式的初始向量
     * @param data
     * @return
     */
    public boolean setInitVector(byte[] data) {
        if (data == null || data.length != 8) {
            return false;
        }
        System.arraycopy(data, 0, initVector, 0, 8);
        return true;
    }

    private String getCipherStr(int length, String groupMode, String fillMode) {
        String des = getDesString();
        String currFillMode = fillMode;
        if (fillMode.equals("PKCS5Padding") && length % 8 == 0) {
            //数据长度刚好是8的倍数,不填充
            currFillMode = "NoPadding";
        }
        return des + "/" + groupMode + "/" + currFillMode;
    }

    /**
     * 使用DES算法加密data
     * @param data 待解密的密文
     * @param keyBytes 密钥
     * @return 明文
     */
    public byte[] decrypt(byte[] data, byte[] keyBytes) {
        byte[] result = null;
        String des = getDesString();
        try {
            //生成密钥
            SecretKey key = generateSecretKey(des, keyBytes);
            if (key == null) {
                return null;
            }

            //解密
            String cipherStr = getCipherStr(data.length, groupMode, fillMode);
            Cipher cipher = Cipher.getInstance(cipherStr);
            if (groupMode.equals("ECB")) {
                cipher.init(Cipher.DECRYPT_MODE, key);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(initVector));
            }
            result = cipher.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    private SecretKey generateSecretKey(String des, byte[] keyBytes) {
        //第一种,原值返回
        SecretKey key = null;
//        try {
//            DESKeySpec keySpec = new DESKeySpec(keyBytes);
//            SecretKeyFactory factory = SecretKeyFactory.getInstance(des);
//            key = factory.generateSecret(keySpec);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }

        //第二种,原值返回
//        key = new SecretKeySpec(keyBytes, des);

        //第三种,随机生成
        try {
            KeyGenerator generator = KeyGenerator.getInstance(des);
            int keyLen = getKeyLength();
            generator.init(keyLen, new SecureRandom(keyBytes));
            key = generator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        System.out.println("生成的DES密钥为:" + HexUtil.bcd2str(key.getEncoded()));
        return key;
    }

    private int getKeyLength() {
        int len = 56;
        switch (this.type) {
            case DES:
                len = 56;
                break;
            case DES_2:
                len = 112;
                break;
            case DES_3:
                len = 168;
                break;
        }
        return len;
    }

    /**
     * 获取当前key的校验值
     * @param key
     * @return 4个字节校验结果
     */
    public byte[] getCheckValue(byte[] key) {
        byte[] zero = HexUtil.hexStringToByte("0000000000000000");
        byte[] result = encrypt(zero, key);
        if (result != null && result.length > 4) {
            //截取四个字节
            byte[] tmp = new byte[4];
            System.arraycopy(result, 0, tmp, 0, 4);
            result = tmp;
        }
        return result;
    }

    public enum DesCryptType {
        DES,
        DES_2,
        DES_3
    }
}

以下是在android当中使用DesCrpter的实例(只摘取片段)。

    /**
     * 输入密钥
     * @param view
     */
    public void inputKey(View view) {
        showMessage("请输入密钥");
        DialogFactory.showMessage(this, "请输入密钥", "11111111111111111111111111111111", "确定" ,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //输入数据后
                        String text = DialogFactory.getContentMsg(DesTestActivity.this);
                        if (TextUtils.isEmpty(text)) {
                            showMessage("密钥不能为空", Color.RED);
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }
//                        if (text.length() != 16) {
//                            showMessage("密钥长度不对,DES密钥长度为8个字节", Color.RED);
//                            DialogFactory.dismissAlert(DesTestActivity.this);
//                            return;
//                        }

                        keyBytes = HexUtil.hexStringToByte(text);
                        DialogFactory.dismissAlert(DesTestActivity.this);
                        showMessage(text);
                    }
                }, "取消",  null);
    }

    /**
     * 随机生成密钥
     * @param view
     */
    public void generateKey(View view) {
        DesCrypter desCrypter = new DesCrypter(DesCrypter.DesCryptType.DES_2);
        byte[] key = desCrypter.generateRandomKey();
        showMessage("生成密钥:" + HexUtil.bcd2str(key));
    }

    /**
     * 设备初始向量
     * @param view
     */
    public void inputVector(View view) {
        showMessage("设置初始向量");
        DialogFactory.showMessage(this, "请输入密钥", "0000000000000000", "确定" ,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //输入数据后
                        String text = DialogFactory.getContentMsg(DesTestActivity.this);
                        if (TextUtils.isEmpty(text)) {
                            showMessage("初始向量不能为空", Color.RED);
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }
                        if (text.length() != 16) {
                            showMessage("初始向量长度不对,长度需为8个字节", Color.RED);
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }

                        initVectorBytes = HexUtil.hexStringToByte(text);
                        DialogFactory.dismissAlert(DesTestActivity.this);
                        showMessage(text);
                    }
                }, "取消",  null);
    }

    /**
     * DES 加密
     * @param view
     */
    public void encryptByDES(View view) {
        if (keyBytes == null) {
            showMessage("当前密钥为空,请先输入密钥");
            return;
        }
        final String groupMode = this.mode == 0 ? "ECB" : "CBC";
        DialogFactory.showMessage(this, "请输入数据", "0102030405060708", "确定" ,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //输入数据后
                        String text = DialogFactory.getContentMsg(DesTestActivity.this);
                        if (TextUtils.isEmpty(text)) {
                            showMessage("数据不能为空");
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }

                        byte[] data = HexUtil.hexStringToByte(text);
                        //开始加密
                        DesCrypter desCrypter = new DesCrypter(DesCrypter.DesCryptType.DES_2, groupMode);
                        //设置初始向量
                        if (initVectorBytes != null) {
                            desCrypter.setInitVector(initVectorBytes);
                        }
                        byte[] result = desCrypter.encrypt(data, keyBytes);
                        if (result == null) {
                            showMessage("加密出错,请重试");
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }
                        showResult(text, HexUtil.bcd2str(result));
                        DialogFactory.dismissAlert(DesTestActivity.this);
                    }
                }, "取消",  null);

    }

    /**
     * DES 解密
     * @param view
     */
    public void decryptByDES(View view) {
        if (keyBytes == null) {
            showMessage("当前密钥为空,请先输入密钥");
            return;
        }
        final String groupMode = this.mode == 0 ? "ECB" : "CBC";
        DialogFactory.showMessage(this, "请输入数据", "73331971A6E1AB0173331971A6E1AB01", "确定" ,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //输入数据后
                        String text = DialogFactory.getContentMsg(DesTestActivity.this);
                        if (TextUtils.isEmpty(text)) {
                            showMessage("数据不能为空");
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }

                        byte[] data = HexUtil.hexStringToByte(text);
                        //开始加密
                        DesCrypter desCrypter = new DesCrypter(DesCrypter.DesCryptType.DES_2, groupMode);
                        //设置初始向量
                        if (initVectorBytes != null) {
                            desCrypter.setInitVector(initVectorBytes);
                        }
                        byte[] result = desCrypter.decrypt(data, keyBytes);
                        if (result == null) {
                            showMessage("解密出错,请重试");
                            DialogFactory.dismissAlert(DesTestActivity.this);
                            return;
                        }
                        showResult(text, HexUtil.bcd2str(result));
                        DialogFactory.dismissAlert(DesTestActivity.this);
                    }
                }, "取消",  null);
    }

    /**
     * 计算密钥的checkvalue
     * @param view
     */
    public void getCheckValue(View view) {
        if (keyBytes == null) {
            showMessage("当前密钥为空,请先输入密钥");
            return;
        }
        DesCrypter desCrypter = new DesCrypter(DesCrypter.DesCryptType.DES_2);
        byte[] ckv = desCrypter.getCheckValue(keyBytes);
        showMessage("CHECKVALUE: " + HexUtil.bcd2str(ckv));
    }

参考资料

  • 12
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值