int mbedtls_aes_crypt_cbc(
mbedtls_aes_context* ctx,
int mode,
size_t inputLength,
unsigned char iv[16],
const unsigned char* input,
unsigned char* output
)
AES/CBC
算法
mbedtls_aes_context* ctx
必须调用mbedtls_aes_init(&ctx)
进行初始化。
int mode
可取值MBEDTLS_AES_ENCRYPT
或MBEDTLS_AES_DECRYPT
,分别表示加密模式
、解密模式
。
size_t inputLength
是const unsigned char* input
的长度,单位是字节
。 该值只能是16
的整数倍。实际上,要加密的字节数据不可能总是16
的倍数,这就要求我们补足
,以满足是16
的整数倍。补
上什么数据呢,这有很多种方法。这称为Padding
。有很多种Padding
。
unsigned char iv[16]
是初始向量,16
个字节。这是为了增加破解的难度。
unsigned char* output
是输出的字节。
0
表示成功。
MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH
表示传入的inputLength
不符合要求。
step1、创建一个项目目录AES
,并进入该目录
mkdir AES && cd AES
step2、使用curl命令下载代码
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/include/mbedtls/aes.h
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/library/aes.c
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/include/mbedtls/platform_util.h
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/library/platform_util.c
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/include/mbedtls/platform.h
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/library/platform.c
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/include/mbedtls/threading.h
curl -LO https://raw.githubusercontent.com/ARMmbed/mbedtls/master/library/threading.c
step3、创建config.h
文件,其内容如下
#define MBEDTLS_AES_C
#define MBEDTLS_CIPHER_MODE_CBC
MBEDTLS_AES_C
这个宏控制着是否开启AES
相关的定义。
MBEDTLS_CIPHER_MODE_CBC
这个宏表示我们要使用CBC
模式,当然还有其他模式,要使用哪个模式,就定义对应的宏:
#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_CIPHER_MODE_CTR
#define MBEDTLS_CIPHER_MODE_CFB
#define MBEDTLS_CIPHER_MODE_OFB
#define MBEDTLS_CIPHER_MODE_XTS
step4、将代码中的mbedtls/
字符串去掉
sed -i 's@mbedtls/@@g' aes.c platform_util.h platform_util.c platform.c threading.h threading.c 2> /dev/null ||
sed -i "" 's@mbedtls/@@g' aes.c platform_util.h platform_util.c platform.c threading.h threading.c
step5、编写一个C语言源程序aesTest.c
,其内容如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aes.h"
#include "../../../algorithm/base16/base16.h"
void showHelp() {
printf("usage: aes encode STRING\n");
printf("usage: aes decode HEX\n");
exit(1);
}
//AES/CBC/PKCS5Padding
//input可以是任意长度
//key只能是16、24、32个ASSCII字符组成的串儿
unsigned char* aes_cbc_pkcs5padding_encode(const char* key, unsigned char iv[16], const char* input, size_t* outputLength) {
size_t keyLength = strlen(key);
if (keyLength != 32 && keyLength != 24 && keyLength != 16) {
perror("key必须是16、24、32个ASSCII字符组成的串\n");
return NULL;
}
//获取到输入的要加密的内容的长度
size_t inputLength = strlen(input);
printf("input = %s, inputLength = %zu\n", input, inputLength);
//看看需要分成多少个块,即使输入的数据是16的整数倍,也要补充16个字节的整数16
size_t n = (inputLength >> 4) + 1;
printf("n = %zu\n", n);
//https://tools.ietf.org/html/rfc8018#appendix-B.2.5
unsigned int padding = 16 - (inputLength & 15);
printf("padding = %d\n", padding);
//用于加密的的数据长度:字节数,此字节数正好等于输出的字节数
size_t needInputLength = n << 4;
printf("needInputLength = %zu\n", needInputLength);
*outputLength = needInputLength;
unsigned char* toBeEncryptBytes = (unsigned char*) calloc(needInputLength, sizeof(unsigned char));
memcpy(toBeEncryptBytes, (unsigned char*)input, inputLength);
//填充数据
for (unsigned int i = 0; i < padding; i++) {
toBeEncryptBytes[inputLength + i] = padding;
}
//加密后的数据
unsigned char* output = (unsigned char*) calloc(needInputLength, sizeof(unsigned char));
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
//设置加密的key
mbedtls_aes_setkey_enc(&aes, (unsigned char*)key, keyLength << 3);
//加密
int resultCode = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, needInputLength, iv, toBeEncryptBytes, output);
if (NULL != toBeEncryptBytes) {
free(toBeEncryptBytes);
toBeEncryptBytes = NULL;
}
if (0 == resultCode) {
return output;
} else {
free(output);
return NULL;
}
}
//AES/CBC/PKCS5Padding
//key只能是16、24、32个ASSCII字符组成的串儿
int aes_cbc_pkcs5padding_decode(const char*key, unsigned char iv[16], unsigned char* input, size_t inputLength, unsigned char* output, size_t* outputLength) {
size_t keyLength = strlen(key);
if (keyLength != 32 && keyLength != 24 && keyLength != 16) {
perror("key必须是16、24、32个ASSCII字符组成的串\n");
return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
}
if ((inputLength & 15) != 0) {
perror("inputLength必须是16大的整数倍");
return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
}
//设置解密的key
mbedtls_aes_context aes;
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_dec(&aes, (unsigned char*)key, keyLength << 3);
//解密数据
int resultCode = mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, inputLength, iv, input, output);
if (0 == resultCode) {
int padding = output[inputLength - 1];
(*outputLength) = inputLength - padding;
}
return resultCode;
}
int main(int argc, char* argv[]) {
if (argc != 3) {
showHelp();
}
if (strcmp("encode", argv[1]) == 0 || strcmp("decode", argv[1]) == 0) {
//初始向量,一般是一个随机数组成的,长度必须是块大小,一个块是16字节
unsigned char iv[16] = {0};
const char* key = "abcdefghjklqwert";
size_t written = 0;
if (strcmp("encode", argv[1]) == 0) {
unsigned char* aesEncodedBytes = aes_cbc_pkcs5padding_encode(key, iv, argv[2], &written);
char* hex = base16_encode(aesEncodedBytes, written, true);
printf("aes_cbc_pkcs5padding(content=%s,key=%s)=%s\n", argv[2], key, hex);
return 0;
} else {
unsigned char* input = base16_decode(argv[2]);
size_t inputLength = strlen(argv[2]) >> 1;
unsigned char output[inputLength];
int resultCode = aes_cbc_pkcs5padding_decode(key, iv, input, inputLength, output, &written);
if (0 == resultCode) {
output[written] = '\0';
printf("aes_cbc_pkcs5padding(content=%s,key=%s)=%s\n", output, key, argv[2]);
} else {
printf("error occurred. code is %d\n", resultCode);
}
return resultCode;
}
} else {
showHelp();
}
}
step6、使用cc命令进行编译
cc -o aesTest *.c
step7、执行aesTest
,结果如下
./aesTest encode abc
aes_cbc_pkcs5padding(content=abc,key=abcdefghjklqwert)=EBBF70FEF806D11C4F1FA1007597105A
step8、与用Java实现的相同算法进行结果比较
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
public class AES {
public static byte[] aes_cbc_pkcs5padding(byte[] content, String key, byte[] iv) {
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes("ASCII"), "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher.doFinal(content);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String bytesToHex(byte[] bytes) {
StringBuilder buf = new StringBuilder(bytes.length << 2);
for(byte b : bytes) { // 使用String的format方法进行转换
buf.append(String.format("%02X", new Integer(b & 0xff)));
}
return buf.toString();
}
public static void main(String[] args) throws Exception {
String content = args[0];
String key = "abcdefghjklqwert";
byte[] iv = new byte[16];
for (int i = 0; i < 16; i++) {
iv[i] = 0;
}
String hex = bytesToHex(aes_cbc_pkcs5padding(content.getBytes("ASCII"), key, iv));
System.out.printf("aes_cbc_pkcs5padding(content=%s,key=%s)=%s\n", content, key, hex);
}
}
step9、使用javac命令进行编译
javac -cp . AES.java
step10、执行java AES abc
,结果如下
java AES abc
aes_cbc_pkcs5padding(content=abc,key=abcdefghjklqwert)=EBBF70FEF806D11C4F1FA1007597105A
两种语言的实现结果是一样的。