关于PKCS5Padding与PKCS7Padding的区别
工作中,我们常常会遇到跨语言平台的加密解密算法的交互使用,特别是一些标准的加解密算法,都设计到数据块Block与填充算法的
问题,例如C#与JAVA中的常见的填充算法如下:
.Net中的填充算法:
成员名称 | 说明 |
ANSIX923 | ANSIX923 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节均填充数字零。 下面的示例演示此模式的工作原理。假定块长度为 8,数据长度为 9,则填充用八位字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF: 数据: FF FF FF FF FF FF FF FF FF X923 填充: FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 07 |
ISO10126 | ISO10126 填充字符串由一个字节序列组成,此字节序列的最后一个字节填充字节序列的长度,其余字节填充随机数据。 下面的示例演示此模式的工作原理。假定块长度为 8,数据长度为 9,则填充用八位字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF: 数据: FF FF FF FF FF FF FF FF FF ISO10126 填充: FF FF FF FF FF FF FF FF FF 7D 2A 75 EF F8 EF 07 |
None | 不填充。 |
PKCS7 | PKCS #7 填充字符串由一个字节序列组成,每个字节填充该字节序列的长度。 下面的示例演示这些模式的工作原理。假定块长度为 8,数据长度为 9,则填充用八位字节数等于 7,数据等于 FF FF FF FF FF FF FF FF FF: 数据: FF FF FF FF FF FF FF FF FF PKCS7 填充: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07 |
Zeros | 填充字符串由设置为零的字节组成。 |
JAVA中支持的填充算法(Cipher)有
Alg. Name | Description |
NoPadding | No padding. |
ISO10126Padding | This padding for block ciphers is described in 5.2 Block Encryption Algorithms in the W3C's "XML Encryption Syntax and Processing" document. |
OAEPPadding, OAEPWith<digest>And<mgf>Padding | Optimal Asymmetric Encryption Padding scheme defined in PKCS1, where <digest> should be replaced by the message digest and <mgf> by the mask generation function. Examples: OAEPWithMD5AndMGF1Padding and OAEPWithSHA-512AndMGF1Padding. |
PKCS1Padding | The padding scheme described in PKCS1, used with the RSA algorithm. |
The padding scheme described in RSA Laboratories, "PKCS5: Password-Based Encryption Standard," version 1.5, November 1993. | |
SSL3Padding | The padding scheme defined in the SSL Protocol Version 3.0, November 18, 1996, section 5.2.3.2 (CBC block cipher): block-ciphered struct { opaque content[SSLCompressed.length]; opaque MAC[CipherSpec.hash_size]; uint8 padding[ GenericBlockCipher.padding_length]; uint8 padding_length; } GenericBlockCipher; The size of an instance of a GenericBlockCipher must be a multiple of the block cipher's block length. sizeof(content) + sizeof(MAC) % block_length = 0, padding has to be (block_length - 1) bytes long, because of the existence of padding_length. |
简单对比之下发现,通用的有None,ISO10126两种填充法,实际上PKCS5Padding与PKCS7Padding基本上也是可以通用的。
通过研读参考资料下面的参考资料可以发现两者定义的区别:
· [Def] PKCS#7: Cryptographic Message Syntax Standard,
An RSA Laboratories Technical Note, Version 1.5. Revised November 1,1993. http://www.cnblogs.com/midea0978/admin/ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-7.asc
· [Inf] PKCS#5: Password-Based Encryption Standard,
An RSA Laboratories Technical Note, Version 1.5. Revised November 1,1993. http://www.cnblogs.com/midea0978/admin/ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-5.asc
在PKCS5Padding中,明确定义Block的大小是8位,而在PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间(块长度超出255的尚待研究),填充值的算法都是一样的:
value=k - (l mod k) ,K=块大小,l=数据长度,如果l=8, 则需要填充额外的8个byte的8
在.net中,例如TripleDESCryptoServiceProvider ,默认BlockSize=64bits=8bytes,所以在这种情况下在PKCS5Padding=PKCS7Padding。
如果在C#中自己定义了一个不是64bits的加密块大小,同时使用PKCS7Padding,那么在java中使用JDK标准的PKCS5Padding就不能解密了。
淌过得坑。
主要问题:
一、Des加解密问题(使用的是最普通的Des ecb方式)
1.key补齐,基本就是超过8字节截取前8字节,不足8字节右补0x00(这个争议不大基本都是这样)
2.数据补齐。这里加解密需要统一padding方式(补齐方式),特别是设计第三方系统交互的时候。当初调试了很久就是因为第三方系统指定的补齐方式与规范定义的方式不一致。下面列举一下常用补齐方式。
a.none不补齐,即待加密数据非8字节整数倍时不进行补齐。(非常非常少使用)
b.zeros零补齐,即待加密数据按8字节分组,最后一组不足8字节数据右补0x00至8字节。(使用得也比较少)
c.pcks5/7补齐,即待加密数据按8字节分组,最后一组不足8字数据按补位长度分别右补1-7个字节的相应数据(如补1个字节为0x01,补两个字节为0x02 0x02依次类推到七个字节补七个0x07)。特别注意当待加密数据为8字节整数倍时,需要最后补8字节的0x00(当时连的第三方系统就是没注意这点调了N久,此补齐方式比较常用)
总结,与第三方系统交互Des密文进行加解密工作时,必须统一双方的key补齐方式与数据的padding方式。不然就是无尽的噩梦。当然一个好的系统应该兼容规范支持的大多数padding模式,由外部系统指定即可。
二、Openssl及Jni相关问题
1. 不能用DES_string_to_key,该函数是根据输入的string随机计算key,并不是将输入的string当作key。
2. 设置key必须使用DES_set_key_unchecked函数,而不能使用DES_set_key_check函数
3. openssl仅按8字节块加密,所以必须自己进行数据拆分与padding(即自行实现padding逻辑)
4. c++/c函数名不能有下划线会被Java误认为包名(Jni)
5. 使用java生成的头文件,对应生成函数。Windows版本的jni.h与Linux版本的jni.h有区别(记得区分,不要简单从一个平台拷贝到另一个平台使用)
OpenssL的所有加密都是使用字节的,而des加密也是按照8位8为加密的,所以我们需要注意加密函数的编写。