文章目录

openssl3.2 - exp - aes-128-cbc概述笔记openssl 命令行实现简单直白的实现简单直白的实现 - 测试效果简单直白的实现 - 测试工程

周全灵活的实现周全灵活的实现 - 测试效果周全灵活的实现 - 测试工程

清晰一些的版本END

openssl3.2 - exp - aes-128-cbc

概述

想将工程中用到的字符串明文用openssl的对称加密算法加密一下, 防止逆向静态分析, 只作为字符串编码/解码的作用. 只是为了防止逆向静态分析, 起到一个编码/解码作用, 不介意别人知道密钥是啥.

看了一下openssl性能(AES性能大概是2000MB/秒, AES256比AES128稍低)

D:\my_dev\my_local_git_prj\study\openSSL\exp\exp029_enc\aes-128-cbc\doc>openssl speed -mlock -seconds 1 -bytes 4096 aes-128-cbc

Doing aes-128-cbc ops for 1s on 4096 size blocks: 567655 aes-128-cbc ops in 1.02s

version: 3.2.0

built on: Sun Feb 25 02:20:27 2024 UTC

options: bn(64,64)

compiler: cl /Zi /Fdossl_static.pdb /Gs0 /GF /Gy /MDd /W3 /wd4090 /nologo /Od -DLIBZ=".\\\\my_zlib_1d3.dll" -DL_ENDIAN -DOPENSSL_PIC -D"OPENSSL_BUILDING_OPENSSL" -D"ZLIB" -D"ZLIB_SHARED" -D"OPENSSL_SYS_WIN32" -D"WIN32_LEAN_AND_MEAN" -D"UNICODE" -D"_UNICODE" -D"_CRT_SECURE_NO_DEPRECATE" -D"_WINSOCK_DEPRECATED_NO_WARNINGS" -D"DEBUG" -D"_DEBUG" -I"D:\\my_dev\\lib\\zlib_1d3"

CPUINFO: OPENSSL_ia32cap=0x7ffaf3ffffebffff:0x18c07fcef3bfa7eb

The 'numbers' are in 1000s of bytes per second processed. // 处理的“数字”以每秒1000字节为单位

type 4096 bytes

aes-128-cbc 2289343.88k => 2289343.88 * 1000 / 1024 / 1024 = 2183 MB/秒

D:\my_dev\my_local_git_prj\study\openSSL\exp\exp029_enc\aes-128-cbc\doc>openssl speed -mlock -seconds 1 -bytes 4096 aes-256-cbc

Doing aes-256-cbc ops for 1s on 4096 size blocks: 414597 aes-256-cbc ops in 1.02s

version: 3.2.0

built on: Sun Feb 25 02:20:27 2024 UTC

options: bn(64,64)

compiler: cl /Zi /Fdossl_static.pdb /Gs0 /GF /Gy /MDd /W3 /wd4090 /nologo /Od -DLIBZ=".\\\\my_zlib_1d3.dll" -DL_ENDIAN -DOPENSSL_PIC -D"OPENSSL_BUILDING_OPENSSL" -D"ZLIB" -D"ZLIB_SHARED" -D"OPENSSL_SYS_WIN32" -D"WIN32_LEAN_AND_MEAN" -D"UNICODE" -D"_UNICODE" -D"_CRT_SECURE_NO_DEPRECATE" -D"_WINSOCK_DEPRECATED_NO_WARNINGS" -D"DEBUG" -D"_DEBUG" -I"D:\\my_dev\\lib\\zlib_1d3"

CPUINFO: OPENSSL_ia32cap=0x7ffaf3ffffebffff:0x18c07fcef3bfa7eb

The 'numbers' are in 1000s of bytes per second processed.

type 4096 bytes

aes-256-cbc 1672063.32k

用openssl speed 列出的算法(openssl3.2 - exp - openssl speed test), 想选其他强度更低(速度更快)的其他对称加密算法, 好像没有啊. 非主流的算法, 用openssl speed都不能测试

那就拿AES128来弄吧.

笔记

openssl 命令行实现

// enc

openssl enc -aes-128-cbc -e -in pt.txt -out pt.txt.enc -k my_pwd_for_enc -pbkdf2

// dec

openssl enc -aes-128-cbc -d -in pt.txt.enc -out pt.txt.enc.dec -k my_pwd_for_enc -pbkdf2

将命令行作为参数代进入, 跟一下openssl命令行工程, 就可以得到C实现(基于openssl API)

简单直白的实现

在查资料过程中, 发现官方测试代码中有一个直白的实现, 自己先弄一个工程试试. 然后再跟官方命令行的实现. 在openssl源码中, 看到了 D:\3rd_prj\crypt\openssl-3.2.0\test\afalgtest.c, 测试了aes-128-cbc, 实现特别简单.

简单直白的实现 - 测试效果

before enc:

0000 - 00 01 02 03 04 05 06 07-08 09 0a 0b 0c 0d 0e 0f ................

0010 - 10 11 12 13 14 15 16 17-18 19 1a 1b 1c 1d 1e 1f ................

0020 - 20 21 22 23 24 25 26 27-28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./

0030 - 30 31 32 33 34 35 36 37-38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?

0040 - 40 41 42 43 44 45 46 47-48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

0050 - 50 51 52 53 54 55 56 57-58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_

0060 - 60 61 62 63 64 65 66 67-68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno

0070 - 70 71 72 73 74 75 76 77-78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.

0080 - 80 81 82 83 84 85 86 87-88 89 8a 8b 8c 8d 8e 8f ................

0090 - 90 91 92 93 94 95 96 97-98 99 9a 9b 9c 9d 9e 9f ................

00a0 - a0 a1 a2 a3 a4 a5 a6 a7-a8 a9 aa ab ac ad ae af ................

00b0 - b0 b1 b2 b3 b4 b5 b6 b7-b8 b9 ba bb bc bd be bf ................

00c0 - c0 c1 c2 c3 c4 c5 c6 c7-c8 c9 ca cb cc cd ce cf ................

00d0 - d0 d1 d2 d3 d4 d5 d6 d7-d8 d9 da db dc dd de df ................

00e0 - e0 e1 e2 e3 e4 e5 e6 e7-e8 e9 ea eb ec ed ee ef ................

00f0 - f0 f1 f2 f3 f4 f5 f6 f7-f8 f9 fa fb fc .............

after enc:

0000 - c6 a1 3b 37 87 8f 5b 82-6f 4f 81 62 a1 c8 d8 79 ..;7..[.oO.b...y

0010 - 35 d9 dc db 82 9f ec 33-52 e7 bf 10 b8 4b e4 a5 5......3R....K..

0020 - 7b 30 46 46 05 f0 2a 09-4c 0a f7 ad 98 4f 61 fc {0FF..*.L....Oa.

0030 - d8 84 34 a5 59 1d bc 8f-d9 63 08 12 d3 a2 7b 87 ..4.Y....c....{.

0040 - 25 cc 26 ee 46 93 13 40-c1 a9 f1 0d 82 2e aa e3 %.&.F..@........

0050 - 69 f3 ff d0 fc b1 a1 5e-a0 dc d6 c1 75 07 9d 45 i......^....u..E

0060 - 37 d1 e1 58 ec 3d 2f 67-07 de 48 12 5f c4 a4 cb 7..X.=/g..H._...

0070 - 22 62 2b 9d d6 ea 3d 1e-ec c9 c1 5e 53 ea 33 8b "b+...=....^S.3.

0080 - 92 3a b9 cc 0f cc 8d 8d-da 45 57 a7 09 e5 3e e1 .:.......EW...>.

0090 - 24 0d fb bb 4e 93 18 85-7e 4b 84 2a 9d ae 3c 2d $...N...~K.*..<-

00a0 - 72 7a 39 39 db 64 24 ed-ba 80 e6 f8 98 d8 35 a7 rz99.d$.......5.

00b0 - 43 13 b6 bd aa ca e7 03-11 e9 97 63 c6 f2 36 7f C..........c..6.

00c0 - ab 39 b5 fd 1b 4c 34 1f-49 99 b3 06 ff 3e 77 2a .9...L4.I....>w*

00d0 - df 3f e7 db 00 43 48 99-6f bc b9 43 34 66 50 8e .?...CH.o..C4fP.

00e0 - f6 0a 2d d3 1a cc 9e d1-36 98 89 40 60 40 3a 48 ..-.....6..@`@:H

00f0 - c6 f1 c5 38 48 65 68 06-b5 df ac ed 84 74 d7 be ...8Heh......t..

after dec:

0000 - 00 01 02 03 04 05 06 07-08 09 0a 0b 0c 0d 0e 0f ................

0010 - 10 11 12 13 14 15 16 17-18 19 1a 1b 1c 1d 1e 1f ................

0020 - 20 21 22 23 24 25 26 27-28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./

0030 - 30 31 32 33 34 35 36 37-38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?

0040 - 40 41 42 43 44 45 46 47-48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

0050 - 50 51 52 53 54 55 56 57-58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_

0060 - 60 61 62 63 64 65 66 67-68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno

0070 - 70 71 72 73 74 75 76 77-78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.

0080 - 80 81 82 83 84 85 86 87-88 89 8a 8b 8c 8d 8e 8f ................

0090 - 90 91 92 93 94 95 96 97-98 99 9a 9b 9c 9d 9e 9f ................

00a0 - a0 a1 a2 a3 a4 a5 a6 a7-a8 a9 aa ab ac ad ae af ................

00b0 - b0 b1 b2 b3 b4 b5 b6 b7-b8 b9 ba bb bc bd be bf ................

00c0 - c0 c1 c2 c3 c4 c5 c6 c7-c8 c9 ca cb cc cd ce cf ................

00d0 - d0 d1 d2 d3 d4 d5 d6 d7-d8 d9 da db dc dd de df ................

00e0 - e0 e1 e2 e3 e4 e5 e6 e7-e8 e9 ea eb ec ed ee ef ................

00f0 - f0 f1 f2 f3 f4 f5 f6 f7-f8 f9 fa fb fc .............

enc / dec all ok

free map, g_mem_hook_map.size() = 0

D:\my_dev\my_local_git_prj\study\openSSL\exp\exp029_enc\aes-128-cbc\prj-aes-128-cbc-simple\x64\Debug\prj-aes-128-cbc-simple.exe (进程 276104)已退出,代码为 0。

要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。

按任意键关闭此窗口. . .

简单直白的实现 - 测试工程

/*!

* \file prj-aes-128-cbc-simple

*/

#include "my_openSSL_lib.h"

#include

#include

#include

#include

#include

#include "CMemHookRec.h"

#include

void my_openssl_app();

bool aes_128_cbc_EncDec(

bool isEnc,

IN const UCHAR* pszBufIn, IN int lenBufIn,

IN const UCHAR* key, IN int lenKey,

IN const UCHAR* iv, IN int lenIv,

OUT UCHAR*& pOutBuf, OUT int& lenOutBuf);

int main(int argc, char** argv)

{

setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞

mem_hook();

my_openssl_app();

mem_unhook();

return 0;

}

void my_openssl_app()

{

UCHAR ucBuf[0x100 - 3];

int lenBuf = sizeof(ucBuf);

int i = 0;

UCHAR* pEncBuf = NULL;

int lenEncBuf = 0;

UCHAR* pDecBuf = NULL;

int lenDecBuf = 0;

// 可以在EVP_CipherInit_ex()之后, 用EVP_CIPHER_CTX_get_key_length()/EVP_CIPHER_CTX_get_iv_length()看长度

UCHAR key[0x10]; // aes-128-cbc's key len = 0x10

UCHAR iv[0x10]; // aes-128-cbc's iv len = 0x10

for (i = 0; i < 0x10; i++)

{

key[i] = (UCHAR)i;

iv[i] = (UCHAR)i;

}

for (i = 0; i < lenBuf; i++)

{

ucBuf[i] = (UCHAR)i;

}

do {

printf("before enc:\n");

BIO_dump_fp(stdout, ucBuf, lenBuf);

// enc

//

// 如果输入不是0x10对齐, 加密后, 就会自动0x10对齐(多出几个字节)

// 所以要自己记录加密前的长度, 且加密时, 要将输出(加密后)buffer 16对齐(或直接比输入的长度多16字节)

// 且加密后, 要自己记录加密后的长度

if (!aes_128_cbc_EncDec(true, ucBuf, lenBuf, (UCHAR*)key, sizeof(key), (UCHAR*)iv, sizeof(iv), pEncBuf, lenEncBuf))

{

assert(false);

break;

}

// printf("enc before lenBuf = %d, enc after lenEncBuf = %d\n", lenBuf, lenEncBuf);

// enc before lenBuf = 253, enc after lenEncBuf = 256

printf("after enc:\n");

BIO_dump_fp(stdout, pEncBuf, lenEncBuf);

// dec

if (!aes_128_cbc_EncDec(false, pEncBuf, lenEncBuf, (UCHAR*)key, sizeof(key), (UCHAR*)iv, sizeof(iv), pDecBuf, lenDecBuf))

{

assert(false);

break;

}

// 解密后的数据长度和解密前一样了

printf("after dec:\n");

BIO_dump_fp(stdout, pDecBuf, lenDecBuf);

// 比较明文和解密后的明文是否相同

if ((lenDecBuf != lenBuf) || (0 != memcmp(ucBuf, pDecBuf, lenBuf)))

{

assert(false);

break;

}

printf("enc / dec all ok\n");

} while (false);

if (NULL != pEncBuf)

{

OPENSSL_free(pEncBuf);

pEncBuf = NULL;

}

if (NULL != pDecBuf)

{

OPENSSL_free(pDecBuf);

pDecBuf = NULL;

}

}

bool aes_128_cbc_EncDec(

bool isEnc,

IN const UCHAR* pszBufIn, IN int lenBufIn,

IN const UCHAR* key, IN int lenKey,

IN const UCHAR* iv, IN int lenIv,

OUT UCHAR*& pOutBuf, OUT int& lenOutBuf)

{

bool b_rc = false;

int i_rc = 0;

int i_tmp = 0;

const EVP_CIPHER* _evp_chipher = NULL;

EVP_CIPHER_CTX* _evp_cipher_ctx = NULL;

do {

if ((NULL == pszBufIn) || (lenBufIn <= 0) ||

(NULL == key) || (lenKey <= 0) ||

(NULL == iv) || (lenIv <= 0))

{

break;

}

_evp_chipher = EVP_aes_128_cbc();

_evp_cipher_ctx = EVP_CIPHER_CTX_new();

if (NULL == _evp_cipher_ctx)

{

break;

}

i_rc = EVP_CipherInit_ex(_evp_cipher_ctx, _evp_chipher, NULL, key, iv, (isEnc ? 1 : 0));

if (1 != i_rc)

{

break;

}

i_tmp = EVP_CIPHER_CTX_get_key_length(_evp_cipher_ctx);

if (i_tmp != lenKey)

{

break;

}

i_tmp = EVP_CIPHER_CTX_get_iv_length(_evp_cipher_ctx);

if (i_tmp != lenIv)

{

break;

}

lenOutBuf = 0;

pOutBuf = (UCHAR*)OPENSSL_malloc(lenBufIn + 0x10); // 输出必须比输入大0x10

if (NULL == pOutBuf)

{

break;

}

i_tmp = 0;

lenOutBuf = 0;

// 如果lenBufIn不是16整除, update 还剩下一个尾巴的数据(len < 0x10)

i_rc = EVP_CipherUpdate(_evp_cipher_ctx, pOutBuf, &i_tmp, pszBufIn, lenBufIn);

if (1 != i_rc)

{

break;

}

lenOutBuf += i_tmp;

i_tmp = 0;

i_rc = EVP_CipherFinal_ex(_evp_cipher_ctx, pOutBuf + lenOutBuf, &i_tmp);

if (1 != i_rc)

{

break;

}

lenOutBuf += i_tmp;

b_rc = true;

} while (false);

if (NULL != _evp_cipher_ctx)

{

EVP_CIPHER_CTX_free(_evp_cipher_ctx);

_evp_cipher_ctx = NULL;

}

return b_rc;

}

周全灵活的实现

单步openssl 命令行(openssl enc -aes-128-cbc …)的实现, 如果口令给的强度不够, 会加沙. 用BIO链的方式加密, 不用自己分别调用具体的加密API, 用起来简单.

不过, 这个实现的前提是知道上一个简单直白的方法(最直接的调用openssl API), 知道对应算法的key/iv的数据长度 跟了一下官方实现, 没全用.

官方考虑了弱密码的情况, 会根据参数加沙和用pbkdf2(KDF是基于弱密码生成新密码), 然后再用BIO链条完成加密.

我自己用时, 知道每种算法的key, iv长度, 会自己用随机数填满, 就没必要基于KDF来从输入的弱口令生成新的实际加密的密码.

我只用了BIO链的方式加密. BIO链干活的好处:

算法都是针对BIO链头操作, e.g. 加密时, 只需要向BIO链头写输入数据, 等全部写完(执行BIO_flush(BIO链头)), 只需要再从BIO链头读东西, 就是加密完成的内容. 用起来方便.如果对输入数据的操作不是一个加密操作, 而是很多操作(e.g. 对称加密, 做hash…), 只需要加入新的BIO节点(通过BIO_push()来控制BIO链中的BIO对象执行顺序), 其他代码不用动, 对于维护友好.

如果只是干一个简单的活(e.g. 针对一块数据加密), 那就用第一种那样直白的方法.

等以后有机会找找官方实现中, 是否有BIO链条大于2个节点的实现, 也继续学习一下.

官方实现中, 是文件到文件的加/解密, 我改成了针对buffer的加/解密.

周全灵活的实现 - 测试效果

before enc:

0000 - 00 01 02 03 04 05 06 07-08 09 0a 0b 0c 0d 0e 0f ................

0010 - 10 11 12 13 14 15 16 17-18 19 1a 1b 1c 1d 1e 1f ................

0020 - 20 21 22 23 24 25 26 27-28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./

0030 - 30 31 32 33 34 35 36 37-38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?

0040 - 40 41 42 43 44 45 46 47-48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

0050 - 50 51 52 53 54 55 56 57-58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_

0060 - 60 61 62 63 64 65 66 67-68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno

0070 - 70 71 72 73 74 75 76 77-78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.

0080 - 80 81 82 83 84 85 86 87-88 89 8a 8b 8c 8d 8e 8f ................

0090 - 90 91 92 93 94 95 96 97-98 99 9a 9b 9c 9d 9e 9f ................

00a0 - a0 a1 a2 a3 a4 a5 a6 a7-a8 a9 aa ab ac ad ae af ................

00b0 - b0 b1 b2 b3 b4 b5 b6 b7-b8 b9 ba bb bc bd be bf ................

00c0 - c0 c1 c2 c3 c4 c5 c6 c7-c8 c9 ca cb cc cd ce cf ................

00d0 - d0 d1 d2 d3 d4 d5 d6 d7-d8 d9 da db dc dd de df ................

00e0 - e0 e1 e2 e3 e4 e5 e6 e7-e8 e9 ea eb ec ed ee ef ................

00f0 - f0 f1 f2 f3 f4 f5 f6 f7-f8 f9 fa fb fc .............

after enc:

0000 - c6 a1 3b 37 87 8f 5b 82-6f 4f 81 62 a1 c8 d8 79 ..;7..[.oO.b...y

0010 - 35 d9 dc db 82 9f ec 33-52 e7 bf 10 b8 4b e4 a5 5......3R....K..

0020 - 7b 30 46 46 05 f0 2a 09-4c 0a f7 ad 98 4f 61 fc {0FF..*.L....Oa.

0030 - d8 84 34 a5 59 1d bc 8f-d9 63 08 12 d3 a2 7b 87 ..4.Y....c....{.

0040 - 25 cc 26 ee 46 93 13 40-c1 a9 f1 0d 82 2e aa e3 %.&.F..@........

0050 - 69 f3 ff d0 fc b1 a1 5e-a0 dc d6 c1 75 07 9d 45 i......^....u..E

0060 - 37 d1 e1 58 ec 3d 2f 67-07 de 48 12 5f c4 a4 cb 7..X.=/g..H._...

0070 - 22 62 2b 9d d6 ea 3d 1e-ec c9 c1 5e 53 ea 33 8b "b+...=....^S.3.

0080 - 92 3a b9 cc 0f cc 8d 8d-da 45 57 a7 09 e5 3e e1 .:.......EW...>.

0090 - 24 0d fb bb 4e 93 18 85-7e 4b 84 2a 9d ae 3c 2d $...N...~K.*..<-

00a0 - 72 7a 39 39 db 64 24 ed-ba 80 e6 f8 98 d8 35 a7 rz99.d$.......5.

00b0 - 43 13 b6 bd aa ca e7 03-11 e9 97 63 c6 f2 36 7f C..........c..6.

00c0 - ab 39 b5 fd 1b 4c 34 1f-49 99 b3 06 ff 3e 77 2a .9...L4.I....>w*

00d0 - df 3f e7 db 00 43 48 99-6f bc b9 43 34 66 50 8e .?...CH.o..C4fP.

00e0 - f6 0a 2d d3 1a cc 9e d1-36 98 89 40 60 40 3a 48 ..-.....6..@`@:H

00f0 - c6 f1 c5 38 48 65 68 06-b5 df ac ed 84 74 d7 be ...8Heh......t..

after dec:

0000 - 00 01 02 03 04 05 06 07-08 09 0a 0b 0c 0d 0e 0f ................

0010 - 10 11 12 13 14 15 16 17-18 19 1a 1b 1c 1d 1e 1f ................

0020 - 20 21 22 23 24 25 26 27-28 29 2a 2b 2c 2d 2e 2f !"#$%&'()*+,-./

0030 - 30 31 32 33 34 35 36 37-38 39 3a 3b 3c 3d 3e 3f 0123456789:;<=>?

0040 - 40 41 42 43 44 45 46 47-48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO

0050 - 50 51 52 53 54 55 56 57-58 59 5a 5b 5c 5d 5e 5f PQRSTUVWXYZ[\]^_

0060 - 60 61 62 63 64 65 66 67-68 69 6a 6b 6c 6d 6e 6f `abcdefghijklmno

0070 - 70 71 72 73 74 75 76 77-78 79 7a 7b 7c 7d 7e 7f pqrstuvwxyz{|}~.

0080 - 80 81 82 83 84 85 86 87-88 89 8a 8b 8c 8d 8e 8f ................

0090 - 90 91 92 93 94 95 96 97-98 99 9a 9b 9c 9d 9e 9f ................

00a0 - a0 a1 a2 a3 a4 a5 a6 a7-a8 a9 aa ab ac ad ae af ................

00b0 - b0 b1 b2 b3 b4 b5 b6 b7-b8 b9 ba bb bc bd be bf ................

00c0 - c0 c1 c2 c3 c4 c5 c6 c7-c8 c9 ca cb cc cd ce cf ................

00d0 - d0 d1 d2 d3 d4 d5 d6 d7-d8 d9 da db dc dd de df ................

00e0 - e0 e1 e2 e3 e4 e5 e6 e7-e8 e9 ea eb ec ed ee ef ................

00f0 - f0 f1 f2 f3 f4 f5 f6 f7-f8 f9 fa fb fc .............

enc / dec all ok

free map, g_mem_hook_map.size() = 0

D:\my_dev\my_local_git_prj\study\openSSL\exp\exp029_enc\aes-128-cbc\prj-aes-128-cbc\x64\Debug\prj-aes-128-cbc.exe (进程 413000)已退出,代码为 0。

要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。

按任意键关闭此窗口. . .

周全灵活的实现 - 测试工程

/*!

* \file prj-aes-128-cbc.cpp

*/

#include "my_openSSL_lib.h"

#include

#include

#include

#include

#include

#include "CMemHookRec.h"

#include

#include

#include

void my_openssl_app();

bool aes_128_cbc_EncDec_v1(

bool isEnc,

IN const UCHAR* pszBufIn, IN int lenBufIn,

IN const UCHAR* key, IN int lenKey,

IN const UCHAR* iv, IN int lenIv,

OUT UCHAR*& pOutBuf, OUT int& lenOutBuf);

int main(int argc, char** argv)

{

setvbuf(stdout, NULL, _IONBF, 0); // 清掉stdout缓存, 防止调用printf时阻塞

mem_hook();

my_openssl_app();

mem_unhook();

return 0;

}

void my_openssl_app()

{

UCHAR ucBuf[0x100 - 3];

int lenBuf = sizeof(ucBuf);

int i = 0;

UCHAR* pEncBuf = NULL;

int lenEncBuf = 0;

UCHAR* pDecBuf = NULL;

int lenDecBuf = 0;

// 可以在EVP_CipherInit_ex()之后, 用EVP_CIPHER_CTX_get_key_length()/EVP_CIPHER_CTX_get_iv_length()看长度

UCHAR key[0x10]; // aes-128-cbc's key len = 0x10

UCHAR iv[0x10]; // aes-128-cbc's iv len = 0x10

for (i = 0; i < 0x10; i++)

{

key[i] = (UCHAR)i;

iv[i] = (UCHAR)i;

}

for (i = 0; i < lenBuf; i++)

{

ucBuf[i] = (UCHAR)i;

}

do {

printf("before enc:\n");

BIO_dump_fp(stdout, ucBuf, lenBuf);

// enc

//

// 如果输入不是0x10对齐, 加密后, 就会自动0x10对齐(多出几个字节)

// 所以要自己记录加密前的长度, 且加密时, 要将输出(加密后)buffer 16对齐(或直接比输入的长度多16字节)

// 且加密后, 要自己记录加密后的长度

if (!aes_128_cbc_EncDec_v1(true, ucBuf, lenBuf, (UCHAR*)key, sizeof(key), (UCHAR*)iv, sizeof(iv), pEncBuf, lenEncBuf))

{

assert(false);

break;

}

// printf("enc before lenBuf = %d, enc after lenEncBuf = %d\n", lenBuf, lenEncBuf);

// enc before lenBuf = 253, enc after lenEncBuf = 256

printf("after enc:\n");

BIO_dump_fp(stdout, pEncBuf, lenEncBuf);

// dec

if (!aes_128_cbc_EncDec_v1(false, pEncBuf, lenEncBuf, (UCHAR*)key, sizeof(key), (UCHAR*)iv, sizeof(iv), pDecBuf, lenDecBuf))

{

assert(false);

break;

}

// 解密后的数据长度和解密前一样了

printf("after dec:\n");

BIO_dump_fp(stdout, pDecBuf, lenDecBuf);

// 比较明文和解密后的明文是否相同

if ((lenDecBuf != lenBuf) || (0 != memcmp(ucBuf, pDecBuf, lenBuf)))

{

assert(false);

break;

}

printf("enc / dec all ok\n");

} while (false);

if (NULL != pEncBuf)

{

OPENSSL_free(pEncBuf);

pEncBuf = NULL;

}

if (NULL != pDecBuf)

{

OPENSSL_free(pDecBuf);

pDecBuf = NULL;

}

}

bool aes_128_cbc_EncDec_v1(

bool isEnc,

IN const UCHAR* pszBufIn, IN int lenBufIn,

IN const UCHAR* key, IN int lenKey,

IN const UCHAR* iv, IN int lenIv,

OUT UCHAR*& pOutBuf, OUT int& lenOutBuf)

{

bool b_rc = false;

const EVP_CIPHER* c = NULL;

const EVP_MD* dgst = NULL;

BIO* bio_in = NULL;

BIO* bio_out = NULL;

BIO* bio_filter = NULL;

int i_tmp = 0;

int i_rc = 0;

int i_out_len_all = 0;

size_t sz_rd = 0;

size_t sz_wt = 0;

const char* psz = NULL;

EVP_CIPHER_CTX* _evp_cipher_ctx = NULL;

UCHAR ucBufRd[1024];

do {

lenOutBuf = 0;

if ((NULL == pszBufIn) || (lenBufIn <= 0) ||

(NULL == key) || (lenKey <= 0) ||

(NULL == iv) || (lenIv <= 0))

{

break;

}

c = EVP_aes_128_cbc(); // 这是最初的加密, 没有任何保护的代码, 不用EVP_CIPHER_fetch()来暴露算法名称字符串

// 可以从算法对象得到算法名称

//psz = EVP_CIPHER_get0_name(c);

//printf("EVP_aes_128_cbc()'s alg name is : %s\n", psz);

// EVP_aes_128_cbc()'s alg name is : AES-128-CBC

i_tmp = EVP_CIPHER_get_key_length(c);

if (i_tmp != lenKey)

{

break;

}

i_tmp = EVP_CIPHER_get_iv_length(c);

if (i_tmp != lenIv)

{

break;

}

dgst = EVP_sha256(); // 这是最初的加密, 没有任何保护的代码, 不用EVP_MD_fetch()来暴露算法名称字符串

bio_in = BIO_new_mem_buf(pszBufIn, lenBufIn);

if (NULL == bio_in)

{

break;

}

bio_filter = BIO_new(BIO_f_cipher());

// BIO_set_cipher(bio_out, c, key, iv, (isEnc ? 1 : 0));

// 由于要改变算法的上下文, 所以要调用BIO_get_cipher_ctx, 而不是调用BIO_set_cipher

// 此时 _evp_cipher_ctx 是 NULL

// !!! _evp_cipher_ctx 是从bio_filter取出来的, 不能自己新建ctx, 否则向bio_filter写东西时, 就不会加密了, 因为上下文不对

BIO_get_cipher_ctx(bio_filter, &_evp_cipher_ctx); // 这里将bio和算法上下文关联了

// 此时 _evp_cipher_ctx 不为空, 是bio_filter要用到的算法ctx地址

// 向ctx中单独设置加密算法/key/iv

// 官方原版实现是分成2步(先设置算法, 再设置key/iv, 有点脱裤子放屁的感觉)

if (!EVP_CipherInit_ex(_evp_cipher_ctx, c, NULL, key, iv, (isEnc ? 1 : 0))) {

// ERR_print_errors(bio_err);

ERR_print_errors_fp(stderr);

break;

}

bio_out = BIO_new(BIO_s_mem());

if (NULL == bio_out)

{

break;

}

// BIO_push返回的就是参数1 bio_out

// 释放时, 只需要 BIO_free_all(bio_filter), 不用管bio_out, 因为 bio_out已经加入bio_filter链

// !!! 必须向 bio_filter中显势写入从明文bio_in读到的内容, 而不能直接读取bio_filter或者bio_out, 否则报错

BIO_push(bio_filter, bio_out); // BIO链, 将明文写入bio_filter, 等全部写完了, 再读biofilter, 就是加密好的密文了

// bio_in = BIO_new_mem_buf(pszBufIn, lenBufIn);

i_out_len_all = lenBufIn + 0x10; // 加密时, 如果明文长度不是0x10对齐, 那么加密后的长度可能比明文长最多0x10个字节

pOutBuf = (UCHAR*)OPENSSL_malloc(i_out_len_all);

if (NULL == pOutBuf)

{

break;

}

lenOutBuf = 0;

do {

i_rc = BIO_read_ex(bio_in, ucBufRd, sizeof(ucBufRd), &sz_rd);

if ((1 != i_rc) || (0 == sz_rd))

{

// 如果将东西读完了, 也是失败, 这里不错算.

i_rc = BIO_flush(bio_filter); // 相当于EVP_CipherFinal_ex()

if (1 != i_rc)

{

ERR_print_errors_fp(stderr);

goto END;

}

break;

}

// 必须向链条的顶部写(write to bio_filter)

// 等全部写完(BIO_flush(bio_filter)), 再从链头(bio_filter)读取时, 就已经是加密完的密文了

i_rc = BIO_write_ex(bio_filter, ucBufRd, sz_rd, &sz_wt); // 相当于 EVP_CipherUpdate()

if (1 != i_rc)

{

ERR_print_errors_fp(stderr);

break;

}

} while (true);

lenOutBuf = 0;

do {

i_rc = BIO_read_ex(bio_out, ucBufRd, sizeof(ucBufRd), &sz_rd);

// 这里读最后一块的时候

// 加密时, 已经是0x10对齐了

// 解密时, 已经是实际的size了

if (1 != i_rc)

{

// 如果将东西读完了, 也是失败, 这里不错算.

// ERR_print_errors_fp(stderr);

break;

}

memcpy(pOutBuf + lenOutBuf, ucBufRd, sz_rd);

lenOutBuf += sz_rd;

} while (true);

b_rc = true;

} while (false);

END:

if (NULL != bio_in)

{

BIO_free(bio_in);

}

if (NULL != bio_filter)

{

BIO_free_all(bio_filter); // bio_filter是BIO链, 释放时要用 BIO_free_all()

}

// 不用释放 _evp_cipher_ctx, 因为 _evp_cipher_ctx属于 bio_filter

if (!b_rc)

{

if (NULL != pOutBuf)

{

OPENSSL_free(pOutBuf);

pOutBuf = NULL;

}

}

return b_rc;

}

清晰一些的版本

对BIO_push()有了新的认识, 修正了一个版本, 优化了一下, 去掉了没用的代码.

bool aes_128_cbc_v2(

bool isEnc,

IN const UCHAR* pszBufIn, IN int lenBufIn,

IN const UCHAR* key, IN int lenKey,

IN const UCHAR* iv, IN int lenIv,

OUT UCHAR*& pOutBuf, OUT int& lenOutBuf)

{

bool b_rc = false;

const EVP_CIPHER* _evp_cipher = NULL;

BIO* bio_container = NULL;

BIO* bio_cipher = NULL;

BIO* bio_header = NULL;

BIO* bio_tail = NULL;

BIO* bio_write_to = NULL;

BIO* bio_read_from = NULL;

int i_tmp = 0;

int i_rc = 0;

size_t sz_rd = 0;

size_t sz_wt = 0;

const char* psz = NULL;

EVP_CIPHER_CTX* _evp_cipher_ctx = NULL;

int len = 0;

do {

lenOutBuf = 0;

if ((NULL == pszBufIn) || (lenBufIn <= 0) ||

(NULL == key) || (lenKey <= 0) ||

(NULL == iv) || (lenIv <= 0))

{

break;

}

_evp_cipher = EVP_aes_128_cbc(); // 这是最初的加密, 没有任何保护的代码, 不用EVP_CIPHER_fetch()来暴露算法名称字符串

// 可以从算法对象得到算法名称

//psz = EVP_CIPHER_get0_name(c);

//printf("EVP_aes_128_cbc()'s alg name is : %s\n", psz);

// EVP_aes_128_cbc()'s alg name is : AES-128-CBC

i_tmp = EVP_CIPHER_get_key_length(_evp_cipher);

if (i_tmp != lenKey)

{

break;

}

i_tmp = EVP_CIPHER_get_iv_length(_evp_cipher);

if (i_tmp != lenIv)

{

break;

}

//bio_in = BIO_new_mem_buf(pszBufIn, lenBufIn);

//if (NULL == bio_in)

//{

// break;

//}

bio_cipher = BIO_new(BIO_f_cipher());

// BIO_set_cipher(bio_out, c, key, iv, (isEnc ? 1 : 0));

// 由于要改变算法的上下文, 所以要调用BIO_get_cipher_ctx, 而不是调用BIO_set_cipher

// 此时 _evp_cipher_ctx 是 NULL

// !!! _evp_cipher_ctx 是从bio_filter取出来的, 不能自己新建ctx, 否则向bio_filter写东西时, 就不会加密了, 因为上下文不对

BIO_get_cipher_ctx(bio_cipher, &_evp_cipher_ctx); // 这里将bio和算法上下文关联了

// 此时 _evp_cipher_ctx 不为空, 是bio_filter要用到的算法ctx地址

if (NULL == _evp_cipher_ctx)

{

break;

}

// 向ctx中单独设置加密算法/key/iv

// 官方原版实现是分成2步(先设置算法, 再设置key/iv, 有点脱裤子放屁的感觉)

if (!EVP_CipherInit_ex(_evp_cipher_ctx, _evp_cipher, NULL, key, iv, (isEnc ? 1 : 0))) {

// ERR_print_errors(bio_err);

ERR_print_errors_fp(stderr);

break;

}

bio_container = BIO_new(BIO_s_mem());

if (NULL == bio_container)

{

break;

}

// BIO_push返回的就是参数1 bio_out

// 释放时, 只需要 BIO_free_all(bio_filter), 不用管bio_out, 因为 bio_out已经加入bio_filter链

// !!! 必须向 bio_filter中显势写入从明文bio_in读到的内容, 而不能直接读取bio_filter或者bio_out, 否则报错

bio_header = BIO_push(bio_cipher, bio_container); // BIO链, 将明文写入bio_filter, 等全部写完了, 再读biofilter, 就是加密好的密文了

bio_tail = bio_container;

// !!! 对于aes加解密, 都是从bio_header写, 从 bio_tail读

if (isEnc)

{

// 加密时

bio_write_to = bio_header;

bio_read_from = bio_tail;

}

else {

// 解密时

bio_write_to = bio_header;

bio_read_from = bio_tail;

}

lenOutBuf = 0;

do {

// 必须向链条的顶部写(write to bio_filter)

// 等全部写完(BIO_flush(bio_filter)), 再从链头(bio_filter)读取时, 就已经是加密完的密文了

i_rc = BIO_write_ex(bio_write_to, pszBufIn, lenBufIn, &sz_wt); // 相当于 EVP_CipherUpdate()

if ((1 != i_rc) && (lenBufIn != sz_wt))

{

goto END;

break;

}

} while (false);

BIO_flush(bio_write_to);

len = BIO_pending(bio_read_from);

// 加密时, 在这里读到的数据长度是加密后的长度

// 解密时, 在这里读到的数据长度还是密文长度. 不过可以拿这个len开buffer, 因为明文比密文短

// bio_in = BIO_new_mem_buf(pszBufIn, lenBufIn);

// 加密时, 如果明文长度不是0x10对齐, 那么加密后的长度可能比明文长最多0x10个字节

pOutBuf = (UCHAR*)OPENSSL_malloc(len + 1); // 多留一个字节(可选)

if (NULL == pOutBuf)

{

break;

}

pOutBuf[len] = '\0';

lenOutBuf = 0;

do {

i_rc = BIO_read_ex(bio_read_from, pOutBuf + lenOutBuf, len - lenOutBuf, &sz_rd);

// 这里读最后一块的时候

// 加密时, 已经是0x10对齐了

// 解密时, 已经是实际的size了

if (1 != i_rc)

{

// 如果将东西读完了, 也是失败, 这里不错算.

// ERR_print_errors_fp(stderr);

break;

}

lenOutBuf += sz_rd;

if (lenOutBuf >= len)

{

break;

}

} while (true);

b_rc = true;

} while (false);

END:

if (NULL != bio_header)

{

BIO_free_all(bio_header); // bio_filter是BIO链, 释放时要用 BIO_free_all()

bio_header = NULL;

}

// 不用释放 _evp_cipher_ctx, 因为 _evp_cipher_ctx属于 bio_filter

if (!b_rc)

{

if (NULL != pOutBuf)

{

OPENSSL_free(pOutBuf);

pOutBuf = NULL;

}

}

return b_rc;

}

END

文章来源

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。