LibTomCrypt里AES/ECB/256与OpenSSL/.Net/Java结果不一致的处理

话不多说,直接上代码。下面.Net代码返回的是GvACpmgPkvPrRgt5afp82w==

using System;
using System.Security.Cryptography;
using System.Text;

protected static string EncryptToBase64String()
{
    string encryptStr = "12345678", string strKey = "1234567812345678123456781234567812345678";
    byte[] keyArray = UTF8Encoding.UTF8.GetBytes(strKey);
    byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(encryptStr);
    RijndaelManaged rDel = new RijndaelManaged();
    rDel.Key = keyArray;
    rDel.Mode = CipherMode.ECB;
    rDel.Padding = PaddingMode.PKCS7;
    ICryptoTransform cTransform = rDel.CreateEncryptor();
    byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
    return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}

 

AES在线解密 AES在线加密 Aes online hex 十六进制密钥 - The X 在线工具  ,选择GB2312/ECB/PKCS7/输出BASE64,显示也是GvACpmgPkvPrRgt5afp82w==

新建test.c,代码如下

#include <tomcrypt.h>

void dump_bytes(unsigned char *in, unsigned long len)
{
   unsigned long idx;
   for(idx=0; idx<len; idx++)
      printf("%02hhX", *(in+idx));
}

int main(int argc, char *argv[]) {
	symmetric_key sk;
	const unsigned char *strIn = "12345678";
	const unsigned char *strKey = "12345678123456781234567812345678";
	unsigned char strOut[16];
	unsigned char strTest[16];
	unsigned char strBase64[256];
	zeromem(strOut, sizeof(strOut));
	rijndael_setup(strKey, 32, 0, &sk);
	rijndael_ecb_encrypt(strIn, strOut, &sk);
	unsigned long nLen = sizeof(strBase64);
	base64_encode(strOut, 16, strBase64, &nLen);
	printf(strBase64);	
	printf("\n");
	printf("%d", nLen);
	printf("\n");
	rijndael_ecb_decrypt(strOut, strTest, &sk);
	printf(strTest);
}

执行

gcc -I ../src/headers -L ../ -o test test.c -ltomcrypt

验证的结果却是tuE8VyU8IDK8IkBzNPeneQ==

阅读了AES的加密方式,AES能加密的数据,都是采用标准长度,也称为块。

在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。

AES密钥长度分组长度加密轮数
AES-12812812810
AES-19219212811
AES-25625612814

这是因为如果明文不是128位(16字节)的则需要进行填充,需要将明文补充到16个字节整数倍的长度。在我们进行加解密时需要采用同样的填充方式,否则无法解密成功。填充模式有:No Padding、PKCS5 Padding、PKCS7 Padding、ISO10126 Padding、Ansix923 Padding、Zero Padding等等。

1、No Padding

不需要填充,但是明文必须是16个字节的整数倍。如果不是,那么就需要使用者自己去实现填充。除了这种模式外,其他的填充模式如果已经是16个字节的数据的话,会在填充一个16字节的数据。

2、PKCS5 Padding(C#上不支持此方式)

在明文的末尾进行填充,填充的数据时当前和16个字节相差的数量。

这里明文长度明显不足16个字节,那么就需要进行填充。明文长度为14,则填充字节数为2,16进制为02

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61

填充后:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 02 02

3、PKCS7 Padding

在明文的末尾进行填充,填充的数据时当前和16个字节相差的数量。

这里明文长度为14,则填充字节数为2,16进制为02

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61

填充后:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 02 02

4、ISO10126 Padding

在明文的末尾进行填充,当前和16个字节相差的数量填写在最后,其余字节填充随机数。

当前和16个字节相差的数量写在最后,所以填充后倒数第二字节填充随机数,最后一个字节是与16个字节相差的数量。

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61

填充后:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 01 02

5、Ansix923 Padding

最后一个字节填充字节序列的长度,其余字节均填充数字0。

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61

填充后:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 00 02

6、Zero padding

后面补0,缺多少就补多少个0

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61

填充后:明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 00 00

除了No padding这种填充模式,其余的填充模式,如果已经是16个字节的数据,会在填充一个16字节的数据.

明文:70 61 73 73 77 6f 72 64 54 65 78 74 43 61 73 65

填充后:{70 61 73 73 77 6f 72 64 54 65 78 74 43 61 73 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00}

除了No padding这种填充模式,其余的填充模式,如果已经是16个字节的数据,会在填充一个16字节的数据.

这个非常关键。

由于OpenSSL默认的填充方式是PKCS7 Padding,我们首先测试PKCS7 Padding填充,12345678一共占用了8个字符,剩下的8个字符全部填充为16-8=8。

#include <tomcrypt.h>

void dump_bytes(unsigned char *in, unsigned long len)
{
   unsigned long idx;
   for(idx=0; idx<len; idx++)
      printf("%02hhX", *(in+idx));
}


int main(int argc, char *argv[]) {
	symmetric_key sk;
	char strIn[16] = "12345678";
	for(int i = 8;i<16;i++){
		strIn[i] = 8;
	}
	for(int i = 0;i<16;i++){
		printf("%d is %d\n", i, strIn[i]);
	}
	printf("\n");
	
	const unsigned char *strKey = "12345678123456781234567812345678";
	unsigned char strOut[16];
	unsigned char strTest[32];
	unsigned char strBase64[256];
	zeromem(strOut, sizeof(strOut));
	rijndael_setup(strKey, 32, 0, &sk);
	rijndael_ecb_encrypt(strIn, strOut, &sk);
	unsigned long nLen = sizeof(strBase64);
	base64_encode(strOut, 16, strBase64, &nLen);
	printf(strBase64);	
	printf("\n");
	printf("%d", nLen);
	printf("\n");
	rijndael_ecb_decrypt(strOut, strTest, &sk);
	printf(strTest);
}

这时候编译的结果就跟OpenSSL的结果一样了。

至此,困扰多时的不同平台的加密不一致的问题也得到了解决。

吐槽一下,感觉这些国外的程序员真的很无语。。

作为一个AES加密算法,对于其他调用这算法的程序员来说不需要操心那么多吧。。

参数传入字符串、字符串长度、密码、密码长度、加密方式(ECB/CBC等等)、填充方式、IV,传几个参数进去,然后丢出来一个字符串结果回来不香吗。。

LibTomCrypt已经很好了,大家试试WInCrypt来实现一段AES加密看看。。百度搜索一天的资料,都不一定能写的出来能跟OpenSSL匹配的代码。

还是VisualFreeBasic比较适合我们国人的程序员,嘎嘎,每个函数都写的那个掏心窝子的舒服。

必须给勇芳赞一个。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值