MySQL新特性之mysql_config_editor 密碼編譯演算法與解密實現

來源:互聯網
上載者:User

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
  • 下一頁

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.