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

文章讨论了.NET代码与C代码使用AES加密时出现的不同结果,问题出在填充模式上。.Net代码使用PKCS7Padding,而C代码使用了不同的填充方式。通过调整C代码以匹配PKCS7Padding,两个环境下的加密结果达成一致。文章强调了填充模式在加密过程中的重要性,并对不同填充方式进行了解释。

话不多说,直接上代码。下面.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比较适合我们国人的程序员,嘎嘎,每个函数都写的那个掏心窝子的舒服。

必须给勇芳赞一个。

 

 

#ifndef TOMCRYPT_H_ #define TOMCRYPT_H_ #include <assert.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <ctype.h> #include <limits.h> /* use configuration data */ #include <tomcrypt_custom.h> #ifdef __cplusplus extern "C" { #endif /* version */ #define CRYPT 0x0116 #define SCRYPT "1.16" /* max size of either a cipher/hash block or symmetric key [largest of the two] */ #define MAXBLOCKSIZE 128 /* descriptor table size */ /* Dropbear change - this should be smaller, saves some size */ #define TAB_SIZE 4 /* error codes [will be expanded in future releases] */ enum { CRYPT_OK=0, /* Result OK */ CRYPT_ERROR, /* Generic Error */ CRYPT_NOP, /* Not a failure but no operation was performed */ CRYPT_INVALID_KEYSIZE, /* Invalid key size given */ CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */ CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */ CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */ CRYPT_INVALID_PACKET, /* Invalid input packet given */ CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */ CRYPT_INVALID_CIPHER, /* Invalid cipher specified */ CRYPT_INVALID_HASH, /* Invalid hash specified */ CRYPT_INVALID_PRNG, /* Invalid PRNG specified */ CRYPT_MEM, /* Out of memory */ CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */ CRYPT_INVALID_ARG, /* Generic invalid argument */ CRYPT_FILE_NOTFOUND, /* File Not Found */ CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */ CRYPT_PK_DUP, /* Duplicate key already in key ring */ CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */ CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ }; #include <tomcrypt_cfg.h> #include <tomcrypt_macros.h> #include <tomcrypt_cipher.h> #include <tomcrypt_hash.h> #include <tomcrypt_mac.h> #include <tomcrypt_prng.h> #include <tomcrypt_pk.h> #include <tomcrypt_math.h> #include <tomcrypt_misc.h> #include <tomcrypt_argchk.h> #include <tomcrypt_pkcs.h> #ifdef __cplusplus } #endif #endif /* TOMCRYPT_H_ */ /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ /* $Revision: 1.20 $ */ /* $Date: 2006/11/26 01:45:14 $ */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值