AES加密之填充
前言
在上一篇介绍Tiny AES中,该库本身是没有提供填充模式的,所以,明文,密码,初始向量都是16字节的倍数,但是在实际使用中,明文是没有办法时时刻刻都是16字节的倍数的。所以需要找到一种填充方式。大部分PADDING模式为PKCS5和PKCS7,由于PKCS5填充是的8字节,PKCS7填充的是16字节,所以推荐使用PKCS7。
通过在网上搜索,找到了一篇教程:Tiny AES in CBC mode with PKCS7 padding written in C,于是记录下,直接放代码:
#define CBC 1
#include "aes.h"
//Initialization Vector
uint8_t iv[] = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";- 首先选择 CBC 模式
- 定义16字节的初始化向量(对于正常使用,IV应当是随机产生的,后续文章将介绍如何随机产生IV)
- 需要加密的明文和密码都不是16字节的倍数,需要填充
int dlen = strlen(report); # the length of the report
int klen = strlen(key); # the length of the key
int dlenu = dlen;
if (dlen % 16) {
dlenu += 16 - (dlen % 16); # make the length multiple of 16 bytes
}
int klenu = klen;
if (klen % 16) {
klenu += 16 - (klen % 16); # make the length multiple of 16 bytes
}通过余数法来确定明文和密码所要达到16字节倍数的长度:
假设 dlen = 38 ====> dlen % 16 ======> 38 % 16 = 38-(16×2) = 38-32 = 6
dlenu += 16 -6 =====> 38 + 10 = 48 # 即长度应当是48
// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];
// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );
// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
kexarray[i] = (uint8_t)key[i];
}定义上述长度的数组,初始化后将明文和密码各自写入数组
int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );填充明文数组和密码数组
//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);之后就是开始AES加密数据了
// reset the iv !! important to work!
AES_ctx_set_iv(&ctx,iv);
// start decryption
AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);
size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);
printf("the decrypted STRING = ");
for (i=0; i<actualDataLength;i++){
printf("%02x",hexarray[i]);
}
printf("\n");解密数据,其中pkcs7_padding_data_length来计算移除填充之后的长度,即明文原本的长度
整个代码的结构:
.
├── aes.c
├── aes.h
├── pkcs7_padding.c
├── pkcs7_padding.h
└── test.ctest.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define CBC 1
#include "aes.h"
#include "pkcs7_padding.c"
int main(void)
{
//Initialization Vector
uint8_t iv[] = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };
uint8_t i;
char* report = "my super secret thing that needs to remain that way!";
char* key = "thisIstheKey";
int dlen = strlen(report);
int klen = strlen(key);
printf("THE PLAIN TEXT STRING = ");
for (i=0; i<dlen;i++){
printf("%c", report[i]);
}
printf("\n");
//Proper Length of report
int dlenu = dlen;
if (dlen % 16) {
dlenu += 16 - (dlen % 16);
printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
}
//Proper length of key
int klenu = klen;
if (klen % 16) {
klenu += 16 - (klen % 16);
printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
}
// Make the uint8_t arrays
uint8_t hexarray[dlenu];
uint8_t kexarray[klenu];
// Initialize them with zeros
memset( hexarray, 0, dlenu );
memset( kexarray, 0, klenu );
// Fill the uint8_t arrays
for (int i=0;i<dlen;i++) {
hexarray[i] = (uint8_t)report[i];
}
for (int i=0;i<klen;i++) {
kexarray[i] = (uint8_t)key[i];
}
int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );
printf("Is the pkcs7 padding pad report = %d | key = %d\n", reportPad, keyPad);
printf("The padded STRING in hex is = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
printf("The padded key in hex is = ");
for (i=0; i<klenu;i++){
printf("%02x",kexarray[i]);
}
printf("\n");
// In case you want to check if the padding is valid
int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
printf("Is the pkcs7 padding valid report = %d | key = %d\n", valid, valid2);
//start the encryption
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, kexarray, iv);
// encrypt
AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);
printf("the encrypted STRING = ");
for (i=0; i<dlenu;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
// reset the iv !! important to work!
AES_ctx_set_iv(&ctx,iv);
// start decryption
AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);
size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);
printf("The actual data length (without the padding) = %ld\n", actualDataLength);
printf("the decrypted STRING in hex = ");
for (i=0; i<actualDataLength;i++){
printf("%02x",hexarray[i]);
}
printf("\n");
return 0;
}这里定义了两个变长数组,在GCC中没有关系,在Visual Studio 2019中会报错,可以将其改成指针的形式uint8_t * hexarray = malloc(dlenu);和uint8_t * kexarray = malloc(klenu);,但是别忘了#include <stdlib.h>
pkcs7_padding.h
#ifndef _PKCS7_PADDING_H_
#define _PKCS7_PADDING_H_
#include <stdint.h>
#include <stddef.h>
/* Pad a buffer with bytes as defined in PKCS#7
* Returns the number of pad bytes added, or zero if
* the buffer size is not large enough to hold the correctly padded data
*/
int pkcs7_padding_pad_buffer( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus );
int pkcs7_padding_valid( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus );
/* Given a block of pkcs7 padded data, return the actual data length in the block based on the padding applied.
* buffer_size must be a multiple of modulus
* last byte 'x' in buffer must be between 1 and modulus
* buffer_size must be at least x + 1 in size
* last 'x' bytes in buffer must be same as 'x'
* returned size will be buffer_size - 'x'
*/
size_t pkcs7_padding_data_length( uint8_t * buffer, size_t buffer_size, uint8_t modulus );
#endifpkcs7_padding.c
#include "pkcs7_padding.h"
int pkcs7_padding_pad_buffer( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus ){
if ( data_length % modulus == 0) {
return 0;
}
uint8_t pad_byte = modulus - ( data_length % modulus ) ;
if( data_length + pad_byte > buffer_size ){
return -pad_byte;
}
int i = 0;
while( i < pad_byte){
buffer[data_length+i] = pad_byte;
i++;
}
return pad_byte;
}
int pkcs7_padding_valid( uint8_t *buffer, size_t data_length, size_t buffer_size, uint8_t modulus ){
uint8_t expected_pad_byte = modulus - ( data_length % modulus ) ;
if( data_length + expected_pad_byte > buffer_size ){
return 0;
}
int i = 0;
while( i < expected_pad_byte ){
if( buffer[data_length + i] != expected_pad_byte){
return 0;
}
i++;
}
return 1;
}
size_t pkcs7_padding_data_length( uint8_t * buffer, size_t buffer_size, uint8_t modulus ){
/* test for valid buffer size */
if( buffer_size % modulus != 0 ||
buffer_size < modulus ){
return 0;
}
uint8_t padding_value;
padding_value = buffer[buffer_size-1];
/* test for valid padding value */
if( padding_value < 1 || padding_value > modulus ){
return buffer_size;
}
/* buffer must be at least padding_value + 1 in size */
if( buffer_size < padding_value + 1 ){
return 0;
}
uint8_t count = 1;
buffer_size --;
for( ; count < padding_value ; count++){
buffer_size --;
if( buffer[buffer_size] != padding_value ){
return 0;
}
}
return buffer_size;
}aes.h和aes.c直接使用上一篇文章的即可。
本文参考了Tiny AES in CBC mode with PKCS7 padding written in C,对其给出的代码有修改。
评论已关闭