c/c++编写dll进行AES加解密供其他语言调用

上一篇文章:c/c++编写dll供其他语言调用

那篇文章说了怎么用dev c++和vs2017编写dll计算md5,这好像并没什么软用,只是做演示用的代码,因为大部分语言想找md5的库很简单,所以这篇文章说说怎么编写AES加解密的dll

AES代码

叫我用c/c++写个aes算法来不太可能,还是照旧去github借鉴一个。找了很久,发现c语言写的要么不全,要么看不懂。最后只能筛选c++写的,选中了下面这个,理由很简单,有很多调用演示代码,很容易看出怎么调用的,而且我用dev c++一跑就通,加密模式也支持(ECB, CBC, CFB)。

https://github.com/SergeyBel/AES

修改代码

首先肯定是用dev c++测试一下代码是不是正常,结果对不对了。github中aes主要代码在src文件夹里的两个文件,tests文件夹放的则是如何调用的

具体测试的细节就不说了,因为我对c/c++也不熟,基本都是一步一百度,不过这个代码调用接口倒是很简单,也没什么太大的问题

实例化一个aes类,如何调用类方法就行,比如ecb模式

AES aes(128);
unsigned char plain[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
unsigned char key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
unsigned char right[] = { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a };
unsigned int len = 0;
unsigned char *out = aes.EncryptECB(plain, 16, key, len);

ASSERT_FALSE(memcmp(right, out, BLOCK_BYTES_LENGTH));
ASSERT_EQ(BLOCK_BYTES_LENGTH, len);
delete[] out;

其中plain是要加密的内容,right是加密后的内容,而out则是实际加密后的结果,后面那两句只是对比一下out和right是不是一样。其他模式也差不多,就只需要多传一个iv。

AES aes(128);
unsigned char plain[] = "1234567890abcdef";
unsigned char key[] = "1234567890abcdef";
unsigned int len = 0;
unsigned char *out = aes.EncryptECB(plain, 16, key, len);
for(int i=0; i<len; i++ ){
  printf( "%02x", out[i]);
}
delete[] out;

比如运行这段代码,查看输出结果
95b012b0bc898e5c37eeed6588635f09

找一个测试网站看看结果是不是一样:https://the-x.cn/cryptography/Aes.aspx

c/c++编写dll进行AES加解密供其他语言调用
对比一下是没问题的,而且给出的代码里也演示了key长度为128,192,和256bit的情况,现在还少了一样,填充模式。

看了下演示代码里也有不对齐blockSize(这里是16个字节)的情况,在看一下源码,就知道了。如果加密数据长度正好整除16字节,则不填充也就是NoPadding模式,如果整除不了,则用0x0来填充,也就是ZeroPadding模式。但是实际的ZeroPadding模式在能整除的情况,也是要填充16个0x0的,

这显然不符合规范,所以可以改一下,让他支持两种填充模式(ZeroPadding和Pkcs7Padding)和不填充。ZeroPadding填充:填充n个0x0,n为blockSize-加密的文本长度%blockSize;Pkcs7Padding填充,填充n个0xn,n为blockSize-加密的文本长度%blockSize。,

假设要加密的文本为input, 其长度是inputSize, alignIn就是填充后的内容。
ZeroPadding代码:

uint8_t paddingNum = 16 - (inputSize % 16);
unsigned int alignInSize = inputSize + paddingNum;
unsigned char *alignIn = new unsigned char[alignInSize];
memcpy(alignIn, input, inputSize);
memset(alignIn + inputSize, 0x0, paddingNum);
delete[] alignIn;

Pkcs7Padding

uint8_t paddingNum = 16 - (inputSize % 16);
unsigned int alignInSize = inputSize + paddingNum;
unsigned char *alignIn = new unsigned char[alignInSize];
memcpy(alignIn, input, inputSize);
memset(alignIn + inputSize, paddingNum, paddingNum);
delete[] alignIn;

dll源码

https://gitee.com/kanadeblisst/vs2017-aes-dll

Python调用

import ctypes
import base64



class AES:
    Pkcs7Padding = 2
    ZeroPadding = 1
    NoPadding = 0
    ECB = 0
    CBC = 1
    CFB = 2
    def __init__(self, key, iv):
        self.key = key.encode()
        self.iv = iv.encode()
        self.aes_dll = ctypes.CDLL("D:\\Android\\vs2017\\AES\\Debug\\AES.dll")

    def decrypt(self, text, mode=0, padding=0):
        btext = text.encode()
        AESDecrypt = self.aes_dll.AESDecrypt
        AESDecrypt.argtypes=[ctypes.c_uint, ctypes.c_uint, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
        AESDecrypt.restype = ctypes.c_int
        result = ctypes.create_string_buffer(len(text.encode()) + 17)
        result_len = AESDecrypt(ctypes.c_uint(mode), ctypes.c_uint(padding), ctypes.c_char_p(btext), ctypes.c_char_p(self.key), ctypes.c_char_p(self.iv), result)
        return result.value[:result_len]

    def encrypt(self, text, mode=0, padding=0):
        btext = text.encode()
        AESDecrypt = self.aes_dll.AESEncrypt
        AESDecrypt.argtypes=[ctypes.c_uint, ctypes.c_uint, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p]
        AESDecrypt.restype = ctypes.c_int
        result = ctypes.create_string_buffer(len(text.encode()) + 17)
        result_len = AESDecrypt(ctypes.c_uint(mode), ctypes.c_uint(padding), ctypes.c_char_p(btext), ctypes.c_char_p(self.key), ctypes.c_char_p(self.iv), result)
        return result.raw[:result_len]


if __name__ == "__main__":
    a = AES("1234567890abcdef", "1234567890abcdef")
    text = "1234567890abcdef"
    result = a.encrypt(text, mode=AES.CFB, padding=AES.ZeroPadding)
    print("字符串: %s, 长度:%d, aes: %s, base64: %s" % (text, len(result), result.hex(), base64.b64encode(result)))

dll中只是把char数组加密成了char数组,转为Python中的类型也就是字节,所以要想转为hex和base64只需要调用Python的方法即可。

其他

写这个dll也是突发奇想,正好国庆在家有时间,奈何本人基础有点差,代码写的也有点烂,所以上面的代码很可能会有bug,如果你测试正好遇到了,可以提出一起解决。

另外其实hex和base64也可以在dll 中实现,百度或者github找代码应该不难。

上一篇:缓冲区溢出实验


下一篇:python利用ctypes调用C++动态库