高级加密标准AES的实际应用
作者:不赖猴
1. 什么是对称密钥加密?
对称密钥加密又称单密钥加密,它是在非对称密钥加密(又称公钥加密)前使用的唯一的加密类型。它的加密和解密过程都使用同一个密钥。
2. 对称密钥加密算法及适用场合。
对称密钥算法分为分组密码和流密码两种。
l 分组密码对数据分组进行操作。当用分组密码对大量数据进行加密和解密时,它通常将数据分解为多个分组,然后独立地对每个分组进行操作。
l 流密码对数据不分组,而是一次一位(或一个字节)地连续进行操作。
流密码比分组密码快,而且使用的代码也比分组的少很多。但是分组密码可以重复使用密钥,流密码则更像一次一密,它的密钥只能使用一次。下表是分组密码和流密码的一些典型应用。
应用领域 | 对称密码算法 | 说明 |
数据包交换 | 分组密码 | 重复使用密钥 |
电子邮件 | 分组密码 | 标准化需要 |
SSL | 流密码 | 速度要求 |
文件加密 | 分组密码 | 重复使用密钥 |
常用的分组密码算法有:DES、AES、Lucifer、Madryga、NewDES、FEAL、REDOC、LOKI、Khufu和Khafre、RC2、IDEA、GOST、CAST、Blowfish、SAFER、RC5、3-WAY、Crab、SXAL8/MBAL等。
常用的流密码算法有:A5/1、A5/2、Chameleon、FISH、Helix、ISAAC、MUGI、Panama、Phelix、Pike、SEAL、SOBER、SOBER-128、WAKE等。
3. 高级加密标准(Advanced Encryption Standard, AES)。
NIST于1997年发布公告寻找一个用来替代当时已不安全的数据加密标准(Data Encryption Standard, DES)。经过多轮筛选,最终选定了由两个比利时密码学家Joan daemen和Vincent Rijmen所设计的Rijndael算法。
3.1 AES的设计。
AES分组密码接受一个128位的明文,并且在一个128、192或者256位密钥的控制下产生一个128位的密文。它是一个替代-置换网络的设计,并且带有一个称为轮的步骤的集合,其中轮数可以为9、11或者13(对应于128、192或者256位的密钥)。
AES的设计文档的下载地址是:http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf。
3.2 AES的工作模式。
如果分组密码把明文分组后,对每个分组各自加密后合成密文。这样就会产生一个问题,就是如果两个地方出现相同的明文分组,它们将被加密为相同的密文。而如果相同的明文分组被多次加密成相同的密文的话它就会泄露信息。理论上制作一个包含明文和其他相对应的密文的密码本是可能的。同时,加密算法应用的复杂性,有的强调效率,有的强调安全,有的强调容错性。基于这些事实,我们需要一些分组密码的工作模式来适应不同的状况。
常用的分组密码工作模式有电码本模式(Electronic Codebook, ECB)、密码分组链接(Cipher Block Chaining, CBC)、输出反馈(Output Feedback, OFB)、密文反馈(Ciphertext Feedback, CFB)和计数器模式(Counter Mode, CTR)。
3.2.1电码本模式(Electronic Codebook, ECB)
直接用分组密码对明文的分组进行加密。也就是上面所说的会出现相同的明文多次被直接加密而无法实现保密性。它只适用于发送少数量的分组数据,而且明文是密码分组大小的某个倍数。
3.2.2密码分组链接(Cipher Block Chaining, CBC)
为了解决ECB的问题,我们希望设计一个技术可以使得当同一个明文分组重复出现时产生不同的密文分组。一种做到这一点的简单方法是密码分组链接CBC方式:前一个分组的加密结果被反馈到当前分组的加密中,换句话说,每一个分组被用来修改下一个分组的加密。每个加密分组不仅依赖于产生它的明文分组,而且依赖于所有前面的明文分组。
<1>. 对IV的要求。
l IV(Initialization Value)是一个初始值,对于CBC模式来说,它必须是随机选取并且需要保密的;而且它的长度和密码分组相同(比如:对于AES为128位)。
l 存储IV的方法有两种:
n 将IV和消息一起存储。这会使消息的大小增加了一个分组。
n 在密钥协商处理过程中生成IV。使用一个称为PKCS#5的算法,从一个随机生成的共享秘密中,即能够得到加密密钥,也能够获得链接IV。
<2>. 明文长度
从上图可以看到,所有的明文是密码分组大小的某个倍数。但是实际上并不是这样。解决的方法有两种:
l 密文窃取(Ciphertext stealing)的技术:把最后一个密文分组传递给ECB模式的密码,并且把它的输出和剩余的明文信息字节(或位)进行异或。
l 分组填充技术:用足够的0对最后一个分组进行填充,使得消息长度是分组大小的某个倍数。
<3>. 不足之处
从上图我们可以看到CBC实际上是串行化处理的。密文C2只有在知道C1的情况下才能计算出来,这使得密码不能以并行的方式进行处理。但是解密是可以并行执行的,因为此时密文都已经知道。
3.2.3密文反馈(Ciphertext Feedback, CFB)
在CBC模式下,整个数据分组在接收完之后才能进行加密。这对许多网络应用来说是个问题。例如,在一个安全的网络环境中,当从某个终端输入时,它必须把每一个字符马上传给主机,当数据在字节大小的分组里进行处理时,CBC模式就不适用了。为了适应这种情况,我们需要使用密文反馈模式CFB或者输出反馈模式OFB(下一节介绍)。实际上,通过这两种方式,是把分组密码转化成流密码,实现了实时运行。在传输一个字符流,每个字符都可以使用面向字符的流密码立即进行加密和传输。
如上图所示,这里假设加密分组是64位的。加密函数的输入是一个64位的移位寄存器,这个移位寄存器被初始化为初始值IV。对64位的初始值加密后输出64位的值。这个值的最左边的s位与明文分组P1(这里要求将明文分成s大小的分组)异或后产生密文C1。然后这个密文C1被传输出去。同时,移位寄存器的内容被左移s位,而C1被反馈回移位寄存器
的最右边的s位里。这个过程如此重复直到所有明文分组都完成加密。根据s的取值不同最常用的CFB有两种模式:CFB-8和CFB。
l CFB-8:s = 8,也就是一次加密一个字节。
l CFB: s = 分组密码大小,对于AES来说,s = 128,也就是一次处理16个字节。
根据不同的应用,选取不同的CFB模式。比如:流密码的一个必要的特性是密文的长度和明文的长度相等。因而,如果要传输一个字节,就应该采用CFB-8的模式。如果使用多于一个字节的CFB模式,传输带宽就会被浪费。
CFB对IV的要求是必须唯一。如果在CFB模式下IV不是唯一的,密码分析者可以恢复出相应的明文。
3.2.4输出反馈(Output Feedback, OFB)
输出反馈OFB在结构上类似CFB。也是另外一种转换分组密码到流密码的方式。
跟CFB不同的是,OFB是将加密函数的输出反馈回移位寄存器。由于反馈机制独立于明文和密文而存在,所以OFB又称内部反馈。OFB模式的一个很好的特性就是在明文存在之前大部分工作可以离线进行。当消息最终到达时,它可以与算法的输出相异或产生密文。OFB对IV的要求跟CFB一样。
3.2.5计数器模式(Counter Mode, CTR)
计数器模式是被设计用来提取分组密码的最大效能以实现保密性的。
在CTR模式中,有一个类似IV的值Counter。不同的是它并不需要随机的,只要是唯一就可以了。在对Counter加密后,将输出和第一个明文分组进行异或产生第一个密文分组,然后Counter递增且对下一个明文分组进行重复处理。
<1>. 明文长度
在CTR中,明文只是跟Counter加密的输出异或,这意味着,明文的长度不要求为密码分组大小的整数倍。CTR模式要求Counter和密文一同传输。CBC中的IV可以从密钥中获得,同样,CTR也可以这么做。
<2>. 解密
在CTR中,加密和解密的处理过程是一样的。这使得实现更加简单,因为这样只需要处理很少的代码。
<3>. 性能
CTR允许并行加密或解密。在硬件实现中,这个特点优势更为凸显。
<4>. 安全性
CTR非常依赖Counter的唯一性。这是因为,如果你重用一个Counter,那么对两个密文分组的异或就将是相应的明文分组的异或。
3.2.6 选择工作模式
分组密码的工作模式一览表如下:
工作模式 | 安全性 | 效率 | 容错性 | 应用领域 |
ECB | l - 明文模式不能隐藏 l - 分组密码的输入并不是随机,它与明文一样 l + 一个密钥可以加密多个消息 l - 明文很容易篡改;分组可以被删除、再现或互换。 | l + 速度与分组密码相同 l - 由于填充,密文比明文长一个分组 l - 不可以进行预处理 l + 处理过程并行进行 | l - 一个密文错误会影响整个明文分组 l - 同步错误不可恢复 | 适合加密密钥,随机数等短数据。 |
CBC | l + 明文模式通过与前一个密文分组相异或被隐藏 l + 与前一个密文分组相异或后分组密码的输入是随机的 l + 一个密钥可以加密多个消息 l +/- 篡改明文稍难;分组可以被从消息头和尾处删除,第一分组位可被更换,并且复制允许控制的改变 | l + 速度与分组密码相同 l - 不考虑IV,密文比明文长一个分组 l - 不可能进行预处理 l +/- 加密不是并行的;解密是并行的且有随机存取特性 | l - 一个密文错误会影响整个明文分组以及下一个分组的相应位 l - 同步错误不可恢复 | 可加密任意长度的数据;适用于计算产生检测数据完整性的消息认证码MAC。 |
CFB | l + 明文模式可以隐藏 l + 分组密码的输入是随机的 l + 用不同的IV,一个密钥可加密多个消息 l +/- 篡改明文稍难;分组可以被从消息头和尾处删除,第一分组可被更换,并且复制允许控制的改变 | l + 速度与分组密码相同 l - 不考虑IV,密文与明文同大小 l - 在分组出现之前作些预处理是可能的,前面的密文分组可以被加密 l + 加密不是并行的;解密是并行的且有随机存取特性 | l - 一个密文错误会影响明文的响应位及下一个分组 l + 同步错误可以恢复,1位CFB能够恢复1位的添加或丢失 | 因错误传播无界,可用于检查发现明密文的篡改。 |
OFB | l + 明文模式被隐藏 l + 分组密文的输入是随机的 l + 用不同的IV,一个密钥可以加密多个消息 l - 明文很容易被控制篡改,任何对密文的改变都会直接影响明文 | l + 速度与分组密码相同 l - 不考虑IV,密文跟明文有同样大小 l + 消息出现前作些预处理是可能的 l - 处理过程不是并行的 | l + 一个密文错误仅影响明文的相应位 l - 同步错误不可恢复 | 适用于加密冗余性较大的数据,比如语音和图像数据。 |
CTR | l + 明文模式被隐藏 l + 分组密文的输入是随机的 l + 用不同的Counter,一个密钥可以加密多个消息 l - 明文很容易被控制篡改,任何对密文的改变都会直接影响明文 | l + 速度与分组密码相同 l - 不考虑Counter,密文跟明文有同样大小 l + 消息出现前作些预处理是可能的 l + 处理过程是并行的 | l + 一个密文错误仅影响明文的相应位 l - 同步错误不可恢复 | 适用于各种加密应用。缺点是没有错误传播,因此不易确保数据完整性。 |
3.3 密钥衍生算法。
在AES的实际应用中,经常会选择CBC和CTR模式。更多的是选择CTR。在选择了工作模式后,我们需要做两件事:
l 给分组密码设置密钥。
l 给工作模式生成一个IV(或Counter)。
对一个分组密码设置密钥的最常用方法是使用一个主密钥。然后再使用一种密钥衍生算法(key derivation algorithm),例如PKCS#5,衍生出一个密钥和一个IV(或Counter)。
密钥衍生函数(Key Derivation Function, KDF)从其他的熵源衍生出密钥,同时会保留输入的熵而且是单向的。密钥衍生不仅仅是常用于生成密钥,它也用于衍生IV(或Counter)。密钥衍生算法通常分两大类:
l 从共享秘密和附加的其他信息中衍生出会话密钥或IV。通常称为KDF。
l 从口令、盐渍和迭代次数中衍生出会话密钥或IV。比如PKCS #5标准。通常称为PBKDF。
至少存在四个版本的KDF和三个版本的BPKDF。其中一个KDF算法被用于MGF(mask generation function),MGF通常被用于公钥密码加密中。
算法 | 参考资料 |
KDF1 | ISO-18033-2 |
KDF2 | ISO-18033-2, ANSI-X9-42 |
KDF3 | ISO-18033-2, NIST SP800-56A |
KDF4 | ISO-18033-2 |
MGF1 | P1363, PKCS1-v2 |
PBKDF-Schneier | [SCHN96] |
PBKDF1 | PKCS5-v1 |
PBKDF2 | PKCS5-v2 |
- [AX942] ANSI X9.42-2003 Agreement of Symmetric Keys Using Discrete Logarithm Cryptography, American National Standards Institute, 19 November 2003.
- [ISO18033] ISO/IEC 18033-2:2006, Information technology - Security techniques - Encryption algorithms - Part 2: Asymmetric ciphers, Ed. Victor Shoup, 2006. The final committee draft version FCD 18033-2, dated December 2004, is available at <www.shoup.net/iso/std6.pdf>.
- [P1363] IEEE P1363 Standard Specifications for Public Key Cryptography, IEEE, November 1993.
- [PKCS1] RSA Laboratories. PKCS #1 v2.1: RSA Encryption Standard. June 2002, <download>.
- [PKCS5] RSA Laboratories. PKCS #5 v2.1: Password-Based Cryptography Standard, 5 October 2006, <download>.
- [SCHN96] Bruce Schneier, Applied Cryptography - Protocols, Algorithms and Source Code in C, second edition, John Wiley, 1996.
- [SP80056A] NIST Special Publication 800-56A Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography, National Institute of Standards and Technology, March 2007, <download>.
3.3.1从共享秘密和附加的其他信息中衍生出会话密钥或IV
通常,共享秘密是可以直接用来衍生密钥或IV。具体的算法请参考上表的链接地址。这里有三个函数kdf1、kdf2和kdf3来衍生出密钥或IV。它们的输入参数都是一样的。
l phash : 所用的散列函数标识。
l Z: 存放共享秘密的缓冲区的指针。
l zLen: 存放共享秘密的缓冲区的大小。
l pOhterInfo: 存放附加信息的缓冲区的指针。
l oiLen: 存放附加信息的缓冲区的大小。
l DK: 存放衍生出的密钥或IV的缓冲区的指针。
l dkLen: 衍生出的密钥或IV的大小。
function kdf1(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
function kdf2(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
function kdf3(phash: PHashDesc; Z: pointer; zLen: word; pOtherInfo: pointer; oiLen: word; var DK; dkLen: word): integer;
function mgf1(phash: PHashDesc; pSeed: pointer; sLen: word; var Mask; mLen: word): integer;
3.3.2从口令、盐渍和迭代次数中衍生出会话密钥或IV
口令是不能直接拿来衍生密钥或IV的。它需要跟盐渍及内部运算的迭代次数结合来衍生的。PKCS #5(ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-5v2/pkcs5v2-0.pdf) 是公钥密码标准系列中的一种。它定义了两种利用口令的KDF算法,分别为PBKDF1和PBKDF2。PBKDF1是旧标准,主要用于DES算法。PBKDF2是新标准,为PKCS #5标准所推荐。下面是实现了这两种算法的函数。它们的输入参数是一样的:
l phash : 所用的散列函数标识。
l pPW: 存放口令的缓冲区的指针。
l pLen: 存放口令的缓冲区的大小。
l salt: 存放8个字节的盐渍的缓冲区指针。
l C: 迭代的次数,至少要大于1000次。
l DK: 存放衍生出的密钥或IV的缓冲区的变量。
l dkLen: 衍生出的密钥或IV的大小。
{PBKDF1标准}
function pbkdf1(phash: PHashDesc; pPW: pointer; pLen: word; salt: pointer; C: longint; var DK; dkLen: word): integer;
{PBKDF2标准}
function pbkdf2(phash: PHashDesc; pPW:pointer; pLen:word; salt:pointer; sLen:word; C:longint; var DK; dkLen:word): integer;
4. AES应用举例。
一个完整的基于口令的AES加解密分四步:
第一步:根据应用的实际情况,选择AES密码的工作模式。AES最常用的模式是CTR。
第二步:用密钥衍生函数从口令中衍生出会话密钥和IV值。
l 先用一个CSPRNG产生一个随机的盐渍,通常是8个字节。
l 设置密钥衍生函数内部的迭代次数。通常不能小于1000次。
l 最后将口令、盐渍和迭代次数输入衍生函数,生成一个32位的随机数。前16位为会话密钥(因为我们这里选择AES128),后16位为IV值。
第三步:开始用AES128进行加密。
第四步:开始用AES128进行解密。