Wednesday, January 22, 2014

Cipher Mode - EBC & CBC & PCBC

使用Block Cipher的时候可以为Cipher指定不同的Mode。Mode的作用是在加密Block之前或者之后对Block进行一些处理,目的是为了增加安全系数。

EBC - Electronic Codebook

最简单的一种Cipher Mode是ECB(Electronic Codebook),它没有对Block进行任何处理。利用加密算法,对每一个Block无差别地进行加密,并且进行输出。解密的过程则是相反,对每个Block独立进行解密。

ECB encryption.svg

ECB decryption.svg

优点:

  1. 速度比较快,因为Block之间是彼此独立的,甚至可以同时加密或者解密多个Block。
  2. 错误隔离,对于加密的数据,如果某个bit损坏,影响的只是损坏bit所在的Block,其它Block中的数据依然可以正常解密。

缺点:

  • 安全性低,对于数据完全相同的两个Block,将会产生两个完全相同的加密结果。因此在对比多次消息后,可能会被推测出部分消息,或者对某些Block中的内容进行篡改。正因为EBC的安全隐患,所以EBC很少用于正式产品中。除非是用于加密一些完全随机的数据,比如说一个Hash结果等。

CBC - Cipher-block chaining

为了避免出现两个相同的Block产生相同的加密结果,IBM在1976年发明了CBC Mode。它的基本原理是每个Block在加密之前,先与之前的一个Block的加密结果进行异或(XOR),然后再将异或的结果进行加密。对于第一个Block,因为之前没有任何加密过的Block,因此需要一个初始化向量IV(Initialization Vector),与第一个Block进行异或。

加密的流程如下:

CBC encryption.svg

因为A XOR B XOR B = A, 所以在解密的时候需要先将Block进行解密,然后将解密的结果与它之前的Block的密文进行异或,进而得到明文。当然,对于第一个密文Block解密后,依然需要与IV进行异或运算来计算第一个Block的明文,而且IV必须和加密时候使用的IV一致。流程如下:

CBC decryption.svg

优点:

  1. 即使加密前是两个完全相同的Block,在加密之后的结果也是不相同的。因为每一个Block的加密都以赖于前一个Block的加密结果。
  2. 因为在解密一个单独的Block的过程中,仅仅以赖于前一个Block的密文,所以每个Block可以独立解密。可以同时对多个block并行解密。

缺点:

  1. 速度较EBC慢,很容易理解,因为多了一步异或操作。
  2. 错误局部传递,当加密数据中的一个bit损坏,那么会造成这个损坏bit所在的block以及之后的一个block无法正常解密。错误的传递比EBC多了一个Block。
  3. 加密时,每个Block需要用到之前一个Block的加密结果,因此无法并行的加密多个Block。

PCBC - Propagating Cipher-block chaining

PCBC和CBC非常相似,唯一的不同点是CBC在加密之前需要和之前Block的密文进行异或操作,而PCBC需要和前一个Block的密文和明文都进行异或操作。类似的,PCBC也需要一个IV,用于处理第一个Block。流程如下:

PCBC encryption.svg

PCBC decryption.svg

优点:

  1. 即使加密前是两个完全相同的Block,在加密之后也不会产生相同的结果。因为每一个Block的加密,都以赖于前一个Block。
  2. 对于明文或者密文的任何一点改动,将影响到后续所有的Block的加密或者解密的结果。

缺点:

  1. 速度比CBC更慢,因为又多进行了一次异或操作。
  2. 错误传递性较强,如果一个bit损坏,将会导致所有后续的所有Block无法解密。
  3. 无论加密还是解密,都需要用到前一个Block的加密或者解密的结果,因此无法同时加密或者解密多个Block。

关于IV

CBC和PCBC都需要使用IV来加密第一个Block。因为这两种Mode在加密一个Block的时候都需要使用前一个Block的信息。而对于第一个Block,之前没有任何信息,所以需要使用一个IV来“伪造”一个Block。

  • 因为需要“伪造Block”,那么IV的长度需要与Block Size相一致。
  • IV需要是一组随机的Byte。
  • IV不需要进行保密,可以和加密信息一起提供给解密方。

使用方法

在Java中,使用各种Mode的方法基本都是一样的。一个主要的区别是EBC不需要IV,而其它两种Mode需要使用IV对Cipher进行初始化。下面是一个使用CBC进行加密的例子。

public class CBCDemo {
    public static byte[] encrypt(byte[] data, Key key, byte[] iv) {
        try {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            System.out.println(cipher.getBlockSize());
            cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
            byte[] cipherData = cipher.doFinal(data);
            return cipherData;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void main(String[] args) throws Exception {
        /**
         * 生成AES Key
         */
        byte[] rawKey = { 33, -52, -105, 76, -47, 123, 109, 119, 5, 2, 124, 112, -116, 3, -120, -126 };
        SecretKey key = new SecretKeySpec(rawKey, "AES");

        /**
         * 构造IV
         */
        byte[] iv = { -26, -29, -38, -70, 127, -111, 47, -98, -119, 78, -30, -8, 6, 65, -100, -93 };
        byte[] data = "Hello CBC".getBytes("UTF-8");

        /**
         * 加密
         */
        byte[] cipherData = encrypt(data, key, iv);
        System.out.println(DatatypeConverter.printBase64Binary(cipherData));
    }
}

注意,这段代码中的IV是我指定的,如果在初始化Cipher的时候不指定IV,那么Cipher的实现类将会随机生成一个IV,为了能够解密,需要把Cipher生成的IV记录下来。可以通过调用Cipher.getIV()来获取当前Cipher的IV。

对于解密,大致过程和加密是一样的,只是在初始化Cipher的时候把状态换成Cipher.DECRYPT_MODE即可。

本文图片来自于维基百科

参考资料:

  • 《Java Cryptography》—— Jonathan B. Knudsen
  • 维基百科

No comments :

Post a Comment