MySQL新特性之mysql_config_editor 加密算法与解密实现


mysql_config_editor采用的AES ECB加密。关于AES 的ECB加密通常都是块加密,如果要加密超过块大小的数据,就需要涉及填充和链加密模式,文中提到的ECB就是指链加密模式。这篇文章主要介绍在该工具中该加密技术的使用与实现,并未详细介绍该机密技术的算法与实现细节。

在前一篇文章中(见 ),加密的过程如下:
encrypt_and_write_file->encrypt_buffer->my_aes_encrypt
 
my_aes_encrypt的具体实现如下:

int my_aes_encrypt(const char* source, int source_length, char* dest,

const char* key, int key_length)
{
#if defined(HAVE_YASSL)
TaoCrypt::AES_ECB_Encryption enc;
/* 128 bit block used for padding */
uint8 block[MY_AES_BLOCK_SIZE];
int num_blocks; /* number of complete blocks */
int i;
#elif defined(HAVE_OPENSSL)
MyCipherCtx ctx;
int u_len, f_len;
#endif

/* The real key to be used for encryption */
uint8 rkey[AES_KEY_LENGTH / 8];
int rc; /* result codes */

if ((rc= my_aes_create_key(key, key_length, rkey)))
return rc;

#if defined(HAVE_YASSL)
enc.SetKey((const TaoCrypt::byte *) rkey, MY_AES_BLOCK_SIZE);

num_blocks = source_length / MY_AES_BLOCK_SIZE;

for (i = num_blocks; i > 0; i--) /* Encode complete blocks */
{
enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) source,
MY_AES_BLOCK_SIZE);
source += MY_AES_BLOCK_SIZE;
dest += MY_AES_BLOCK_SIZE;
}

/* Encode the rest. We always have incomplete block */
char pad_len = MY_AES_BLOCK_SIZE - (source_length -
MY_AES_BLOCK_SIZE * num_blocks);
memcpy(block, source, 16 - pad_len);
memset(block + MY_AES_BLOCK_SIZE - pad_len, pad_len, pad_len);

enc.Process((TaoCrypt::byte *) dest, (const TaoCrypt::byte *) block,
MY_AES_BLOCK_SIZE);

return MY_AES_BLOCK_SIZE * (num_blocks + 1);
#elif defined(HAVE_OPENSSL)
if (! EVP_EncryptInit(&ctx.ctx, EVP_aes_128_ecb(),
(const unsigned char *) rkey, NULL))
return AES_BAD_DATA; /* Error */
if (! EVP_EncryptUpdate(&ctx.ctx, (unsigned char *) dest, &u_len,
(unsigned const char *) source, source_length))
return AES_BAD_DATA; /* Error */
if (! EVP_EncryptFinal(&ctx.ctx, (unsigned char *) dest + u_len, &f_len))
return AES_BAD_DATA; /* Error */

return u_len + f_len;
#endif
}

上述程序就是mysql的使用AES的机密过程。在加密中,如果mysql定义了自带的AES加密算法,就使用自带的(#define HAVE_YASSL).否则就是用OPENSSL EVP框架的加密算法。
 
这里介绍OPENSSL EVP加密算法的步骤:
点击(此处)折叠或打开

int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, const unsigned char *key, const unsigned char *iv)

EVP_EncryptInit (初始化)
              |
              |
              V
 EVP_EncryptUpdate(&ctx,out+len,&outl,in,inl);
 EVP_EncryptUpdate(这个EVP_EncryptUpdate的实现实际就是将明文按照16 bytes的长度去加密,实现会取得该cipher的块大小(对aes_128来说是16字节)并将block-size的整数倍去加密。如果输入为50字节,则此处仅加密48字节,outl也为48字节。输入in中的最后两字节拷贝到ctx->buf缓存起来。
对于inl为block_size整数倍的情形,且ctx->buf并没有以前遗留的数据时则直接加解密操作,省去很多后续工作)
              |
              |
              V
  EVP_EncryptFinal_ex(&ctx,out+len,&outl);
  对于如本例所述,第一次除了了48字节余两字节,第二次处理了第一次余下的2字节及46字节,余下了输入100字节中的最后4字节。此处进行处理。如果不支持pading,且还有数据的话就出错,否则,将block_size-待处理字节数个数个字节设置为此个数的值,如block_size=16,数据长度为4,则将后面的12字节设置为16-4=12,补齐为一个分组后加密。对于前面为整分组时,如输入数据为16字节,最后再调用此Final时,不过是对16个0进行加密,此密文不用即可,也根本用不着调一下这Final。
由于我们知道了,在加密后的文件中,KEY是存放在文件头部 offset 4bytes的地方,之后的20bytes 存放的都是key的信息 。所以我们只要读取该key,然后对该key之后的信息一行一行的用该key 调用解密程序就好了。具体的实现如下:
 
algo_aes_ecb.h

#ifndef ALGO_AES_H

#define ALGO_AES_H

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *ciphertext);

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext);

#endif

algo_aes_ecb.c


点击(此处)折叠或打开

#include <stdlib.h>

#include <stdio.h>
#include <string.h>
#include "algo_aes_ecb.h"
#include <openssl/evp.h>
#include <openssl/aes.h>

typedef unsigned char uint8;
#define AES_KEY_LENGTH 128

uint8 rkey[AES_KEY_LENGTH / 8];

void handleErrors(void)
{
ERR_print_errors_fp(stderr);
abort();
}


static int my_aes_create_key(const char *key, int key_length, uint8 *rkey)
{
uint8 *rkey_end= rkey + AES_KEY_LENGTH / 8;
uint8 *ptr;
const char *sptr;
const char *key_end= key + key_length;

memset(rkey, 0, AES_KEY_LENGTH / 8);

for (ptr= rkey, sptr= key; sptr < key_end; ptr ++, sptr ++)
{
if (ptr == rkey_end)
ptr= rkey;
*ptr ^= (uint8) *sptr;
}
}

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,unsigned char *ciphertext)
{
EVP_CIPHER_CTX *ctx;

int len;

int ciphertext_len;
my_aes_create_key(key,20,rkey);
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

/* Initialise the encryption operation. IMPORTANT - ensure you use a key
* and IV size appropriate for your cipher
* In this example we are using 128 bit AES (i.e. a 128 bit key).
*/

if(1 != EVP_EncryptInit(ctx, EVP_aes_128_ecb(),(const unsigned char *)rkey,NULL))
handleErrors();

/* Provide the message to be encrypted, and obtain the encrypted output.
* EVP_EncryptUpdate can be called multiple times if necessary
*
*/
if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, (unsigned const char *)plaintext, plaintext_len))
handleErrors();
ciphertext_len = len;

/* Finalise the encryption. Further ciphertext bytes may be written at
* * * this stage.
* * */
if(1 != EVP_EncryptFinal(ctx, ciphertext + len, &len)) handleErrors();
ciphertext_len += len;

/* Clean up */
EVP_CIPHER_CTX_free(ctx);

return ciphertext_len;
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;

int len;

int plaintext_len;
my_aes_create_key(key,20,rkey);

/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

/* Initialise the decryption operation. IMPORTANT - ensure you use a key
* size appropriate for your cipher
* In this example we are using 128 bit AES (i.e. a 128 bit key). The
*/

if(1 != EVP_DecryptInit(ctx, EVP_aes_128_ecb(),rkey,NULL))
handleErrors();

/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*
*/
if(1 != EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
handleErrors();
plaintext_len = len;

/* Finalise the decryption. Further plaintext bytes may be written at
* * * this stage.
* * */
if(1 != EVP_DecryptFinal(ctx, plaintext + len, &len)) handleErrors();
plaintext_len += len;

/* Clean up */
EVP_CIPHER_CTX_free(ctx);

return plaintext_len;
}

更多详情见请继续阅读下一页的精彩内容

  • 1
  • 2
  • 下一页

相关内容

    暂无相关文章