分组密码以及分组密码的模式

摘要

为了保证信息的安全性,在传输过程中不被攻击(攻击分为主动攻击和被动攻击),通常都会采用把消息加密的方式,使用的过程中,我们会经常使用DES,3DES以及AES这些对称加密的加密算法,并且我们也经常听到分组密码和流密码这些名词。并且也见到什么ECB,CBC这些东西,本文以DES这种对称加密算法的分组密码做一个浅显的介绍。

密码算法分为分组密码和流密码两种。

分组密码:是每次只能处理特定长度的一块数据的一类密码算法,这里的“一块”就称为分组。此外一个分组的比特数就称为分组长度

流密码:流密码是对数据流进行连续处理的一类密码算法,流密码中一般以1比特,8比特或者32比特等为单位进行解密和加密。

     分组密码处理完一个分组就结束了,因此不需要通过内部状态来记录加密的进度;相对的流密码是对一串数据流进行连续处理,因此需要保持内部状态。

是不是看完以上感觉还比较晕,再看一个更进一步的定义,分组加密(Block cipher),又称为分块加密或者块密码,它将明文分成多个等长的模块,使用确定的算法和对称秘钥对每个分组进行加解密。

分组密码算法

接下来以美国政府核定的标准加密算法DES为例来说明什么分组加密算法。

DES的基本机构是有Hors Feistel设计的,因此也叫Feistel网络,这个结构不仅仅用在了DES上,其他很多的密码算法也有应用。

在Feistel网络中,加密的各个步骤称作轮,整个加密过程就是进行若干次轮的循环。下图展现的是Fesitel网络一轮的计算流程。

085550_S87i_2457218.png

一轮明文输入会被分为左右两部分进行处理,左右两部分分别是32个byte

子秘钥指的是本轮加密所使用的秘钥。在Fesistel网络中,每一轮都需要使用一个不同的子秘钥。由于子秘钥只在一轮中使用,所以只是一个局部的秘钥,因此才叫子秘钥

轮函数的作用是根据右侧和子秘钥生成左侧进行加密的比特序列,是整个加密的核心。讲轮函数的输出与左侧进行XOR(异或)运算,运算后的结果就是加密后的左侧。也就说用XOR与左侧的输入进行合并,而右侧则会直接成为输出的右侧。

其步骤如下:

(1)将输入的数据等分为左右两部分

(2)将输入的右侧直接输入到右侧

(3)讲输入的右侧发送给轮函数

(4)将轮函数根据右侧数据和子秘钥,计算出一串随机比特序列

(5)将上一步得到的比特序列与左侧数据进行XOR运算,并将结果作为加密后的左侧。

上述过程中可以看出来,右侧的数据根本就没有加密,所以需要用不同的子秘钥对这一轮重复处理若干次,并且每次都进行左右对调。

而解密的过程就是一个相反的过程。

以上的一个单位(64byte)就叫做一个分组,而对这组数据进行加密的过程就是分组加密。

分组模式

那什么是模式呢?

从上可以看出分组密码算法只能加密固定长度的分组,但是需要加密的明文长度通常会超过分组密码的分组长度,这时候就需要对分组密码算法进行迭代,才能把所有的明文进行加密,而迭代的方法就称为分组密码的模式。

分组密码有很多模式,比较常见的有下面五种

  1. ECB模式:Electronic CodeBook mode(电子密码本模式)
  2. CBC模式:Cipher Block Chainiing mode(密码分组链接模式)
  3. CFB模式:Cipher FeedBack mode(密文反馈模式)
  4. OFB模式:Output FeedBack mode(输出反馈模式)
  5. CTR模式:CounTeR mode (计数器模式)

ECB模式(电子密码本模式)

ECB模式比较简单,这种模式就是把一段明文,分割成一个分组长度的整数倍(例如DES的一个分组长度是64byte,上文中有提到),如果不是分组长度的整数倍,则会用一些特殊的字符进行填充,然后分别按照上述加密算法,对每一个分组进行加密,最后对所有分组加密出来的数据进行合并,成为密文。下图就是对明文为(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)采用ECB模式加密的示意图

104107_dofi_2457218.png

以下使用代码来演示一下使用AES加密算法,使用ECB模式以后,对字符串加密后并且用16进制显示出来的结果

public class AESTest {
    public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
        byte[] data = str.getBytes();
        String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
        String KEY_ALGORITHM = "AES";
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
        keyGenerator.init(128);
        SecretKey secretKey = keyGenerator.generateKey();
        byte[] encoded = secretKey.getEncoded();
        Key key = new SecretKeySpec(encoded,KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE,key);
        byte[] bytes = cipher.doFinal(data);
        System.out.println("加密后"+parseByte2HexStr(bytes));
    }

    /**将二进制转换成16进制
     * @param buf
     * @return
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
}

输出结果为:

104651_M0D3_2457218.png

C57EC72731C0983EF3FFCB88D8EAB922C57EC72731C0983EF3FFCB88D8EAB922C57EC72731C0983EF3FFCB88D8EAB922C57EC72731C0983EF3FFCB88D8EAB922F628B5120016D660138EBA0EA993A5E7

这样看还不太明显,如果进行换行显示的话可以看到如下规律

C57EC72731C0983EF3FFCB88D8EAB922
C57EC72731C0983EF3FFCB88D8EAB922
C57EC72731C0983EF3FFCB88D8EAB922
C57EC72731C0983EF3FFCB88D8EAB922
F628B5120016D660138EBA0EA993A5E7

其中 C57EC72731C0983EF3FFCB88D8EAB922 这个重复显示,这个就是ECB模式加密的特点,这样只需要观察一下密文的组合,就能根据一些线索来破译密码。

所以ECB模式是存在一定风险的,因此此种加密模式并不推荐采用。

CBC模式(密码分组链接模式)

CBC模式的全称是Cipher Block Chaining模式,是因为密文分组像链条一样互相连接在一起。

在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密

111134_KDtJ_2457218.png

从上图可以看出,在和ECB模式相比较,在加密之前CBC模式先和上一组加密后的密文进行了一次XOR,然后才进行加密,由于第一次没有前一个分组,所以准备了一个长度为一个分组长度的比特序列用来代替“前一个分组”,这个比特序列叫做初始化向量(initialization vector),CBC模式可以克服ECB模式由于字符相同,解密出来的密文分组相同的缺点。

如果把ECB中章节中程序的模式更改为CBC,则加密结果为:

F354627AE9BA742ECB61C2D9114A3CA2938DFA3A0E7AC12D913E9A48E4AE8CBF9BC93BE23122A583DA00EE6440DD30447920421E410E40211216D126C2641FA21F70E1039D7ADC440928E3CF94E1F982

不会出现重复。

其中初始化向量是必须的,如果仅仅把CIPHER_ALGORITHM 改为AES/CBC/PKCS5Padding,再次运行程序,解密时将会报错:

加密后:FDA2BF43B9A8A4C8E0C4D2315B2B18C497B999D931BD8EE61AD60E70ABAEEE3DF9CF92C1672AE26B45A83DF7B05BF437830CAC49FDC267A53D928E72188EB2F7D26B06A2DE2673D97E5922A80A12D343
Exception in thread "main" java.security.InvalidKeyException: Parameters missing
	at com.sun.crypto.provider.CipherCore.init(CipherCore.java:388)
	at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:186)
	at javax.crypto.Cipher.implInit(Cipher.java:787)
	at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
	at javax.crypto.Cipher.init(Cipher.java:1213)
	at javax.crypto.Cipher.init(Cipher.java:1153)
	at com.wtf.crypto.AESTest.main(AESTest.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

 

需要对上述的程序进行修正,初始化一个向量,修改后的程序为:

public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
    String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    byte[] data = str.getBytes();

    String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    String KEY_ALGORITHM = "AES";
    KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
    keyGenerator.init(128);
    SecretKey secretKey = keyGenerator.generateKey();
    byte[] encoded = secretKey.getEncoded();
    Key key = new SecretKeySpec(encoded,KEY_ALGORITHM);
    Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE,key,new IvParameterSpec(new byte[16])); //设置初始向量
    byte[] bytes = cipher.doFinal(data);
    System.out.println("加密后:"+parseByte2HexStr(bytes));
    Cipher decode = Cipher.getInstance(CIPHER_ALGORITHM);
    decode.init(Cipher.DECRYPT_MODE,key,new IvParameterSpec(new byte[16])); //设置初始向量
    byte[] bytes1 = decode.doFinal(bytes);
    System.out.println("解密后:"+new String(bytes1));

}

 

这样加解密以后会产生如下输出:

加密后:976CF9D4DDE0B0544C6008CFE9CB57EFA888E0B8AC2187F1039D837CEAD88BC42A50E033B70CC2FA18BDC507F71C1CD4E0D7AA23F1A9281EB208C815FC3785E4397C00809E9C474F06131A65345B3854
解密后:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

 

CFB模式(秘钥反馈模式)

113259_oX5J_2457218.png

在CBC模式以及ECB模式中,都是要把明文进行加密,从上图可以看出,在CFB模式中,并没有经过对明文进行加密,而是对上一个密文分组直接进行了XOR,初始化向量和CBC模式的初始化向量含义相同。在CFB模式中,密码算法的输出相当于一次性密码本中的随机比特序列。由密码算法所生成的比特序列成为秘钥流,明文数据可以被逐个比特加密,因此可以讲CFB模式看做是一种使用分组密码来实现流密码的方式。

 

OFB(输出反馈模式)

OFB模式和CFB模式的区别仅仅在于密码算法的输入。

CFB模式中,密码算法的输入是前一个密文分组,也就是将密文分组反馈到密码的算法中,因此叫做“密文反馈模式”。所以对于OFB模式,密码算法的输入则是密码算法的前一个输出,也就是将输出反馈给密码算法,故叫做“输出反馈模式”。

 

由于CFB模式中是对密文分组进行反馈的,因此必须从第一个明文分组开始按顺序进行加密,也就是说无法跳过明文分组1而先对明文分组2进行加密。

而在OFB模式中,XOR所需要的比特序列可以事先通过密码算法生成,和明文的分组没什么关系。只要提前准备好所需要的秘钥流,则在实际从明文生成密文的过程中,就完全不在需要动秘钥算法了,只需要把准备好的秘钥流和明文分组XOR即可。

CTR模式(计数器模式)

125610_RdKF_2457218.png

 

CTR模式是通过将逐次累加的计数器进行累加加密后生成秘钥流的流密码

CTR模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成秘钥流。也就是说,最终的密文分组是通过将计数器加密得到的比特序列,然后与明文XOR而得到的。

各模式比较

分组密码模式对比

模式名称优点缺点备注
ECB电子密码本模式
  • 简单、快速
  • 加解密时支持并行计算
  • 明文中的重复排列会反馈到密文中
  • 可以通过删除、替换密文分组进行攻击
  • 不能抵御重放攻击
不推荐使用
CBC密码分组链接模式
  • 明文中重复排列不会反馈到密文
  • 解密支持并行计算
  • 能够解密任意密文分组
  • 对包含某些错误比特的密文进行解密时,第一个分组的全部比特及后一个分组比特会出错
  • 加密不支持并行计算
推荐使用
CFB秘钥反馈模式
  • 不需要进行填充
  • 解密支持并行计算
  • 能够解密任意密文分组
  • 加密不支持并行计算
  • 对包含某些错误比特的密文进行解密时,第一个分组的全部比特以及后一个分组比特会出错
  • 不能抵御重放攻击
现在已经不再使用,推荐使用CTR模式做替代
OFB输出反馈模式
  • 不需要填充
  • 可以实现准备好秘钥流直接和明文进行XOR加密
  • 加解密使用相同结构
  • 一个分组的错误不影响到另外一个分组的加解密
  • 不支持并行计算
  • 主动攻击者反转密文分组中的某些比特时,明文分钟中相应的比特也会被反转
推荐用CTR模式替代
CTR计数器模式
  • 不需要填充
  • 可事先为加解密做准备
  • 加解密时使用相同的结构
  • 错误分组不影响到其他分组
  • 支持加解密并行计算    
  • 主动攻击者反转密文分组中的某些比特时,明文分钟中相应的比特也会被反转
推荐使用

 

 

 

转载于:https://my.oschina.net/u/2457218/blog/873333

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值