本文章使用上一篇《C#調用C++類庫例子》的項目代碼作為Demo。本文中,C#將調用C++的Crypto++庫,實現AES的ECB和CBC加解密。
一、下載Crypto
1、進入Crypto的官網下載openssl。網址是: https://www.cryptopp.com/。

2、點擊“DownLoad”,選擇最新的可下載的版本即可。此時我下載的是cryptopp820.zip,如下圖所示的。

3、解壓 cryptopp820.zip。
4、打開cryptopp820文件夾中的cryptest.sln,點擊“重定解決方案目標”。


5、重新生成解決方案。
二、建立自己使用的Crypto++ Library
由於從官方網下載的Crypto++庫是開源的,只有源文件和幾個可以生成lib、dll的工程,以及一個使用的例子工程,因此希望生成自己建的工程能使用的SDK。
1. 編譯鏈接生成cryptlib.lib
根據當前項目系統設置平台(我是設置為x64),分別在Debug模式和Release模式下編譯鏈接cryptlib工程,成功后會在cryptopp820\x64\Output\Debug和cryptopp820\x64\Output\Release下生成cryptlib.lib文件。
2. 建立Crypto++ SDK
在解決方案下新建一文件夾,取名“CryptoPP”,里面新建文件夾“include”、“lib”,在“lib”中新建文件夾“debug”、“release”。將Crypto++庫中的所有頭文件復制到“include”文件夾中,再將上面生成的兩個cryptlib.lib分別復制到“debug”和“release”中。

三、設置工程屬性
在EncryptBese項目--右鍵--屬性:
(1)“配置屬性”→“C/C++” →“常規”,右邊的“附加包含目錄”設置為上面建好的Crypto++ SDK的Include文件夾,“E:\Project\ArticleProject\AuthorizationTest\CryptoPP\include”;

(2) “配置屬性”→“Linker” →“鏈接器”,右邊的“附加庫目錄”設置為上面建好的Crypto++ SDK的Lib\Debug文件夾,“E:\Project\ArticleProject\AuthorizationTest\CryptoPP\lib\debug”(Release模式下對應着Release文件夾);

(3) “配置屬性”→“C/C++” →“代碼生成”,右邊的“運行庫”設置為“Multi-threaded Debug (/MTd)”(Release模式下對應着“Multi-threaded (/MT)”)

四、為EncryptBase項目添加代碼
1、在EncryptBase.h文件添加以下代碼
#ifndef _ENCRYPTBASE_H //定義_ENRYPTBASE_H宏,是為了防止頭文件的重復引用
#define _ENCRYPTBASE_H
#ifdef __cplusplus //而這一部分就是告訴編譯器,如果定義了__cplusplus(即如果是cpp文件,
extern "C" { //因為cpp文件默認定義了該宏),則采用C語言方式進行編譯
#endif
#ifdef DLL_EXPORTS
#define DLL_EXPORTS __declspec(dllexport)
#else
#define DLL_EXPORTS __declspec(dllimport)
#endif
enum AESMode
{
ECB = 0x00,
CBC,
};
enum BlockPaddingScheme {
NO_PADDING,
ZEROS_PADDING,
PKCS_PADDING,
ONE_AND_ZEROS_PADDING,
W3C_PADDING,
DEFAULT_PADDING
};
DLL_EXPORTS int AesEncrypt(AESMode mode, BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int ivlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen);
DLL_EXPORTS int AesDecrypt(AESMode mode, BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int inlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen);
int AesEcbEncrypt(BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int inlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen);
int AesEcbDecrypt(BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int inlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen);
int AesCbcEncrypt(BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int inlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen);
int AesCbcDecrypt(BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int inlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen);
#ifdef __cplusplus
}
#endif
#endif // !_ENCRYPTBASE_H
2、EncryptBase.cpp添加相關代碼,如下圖。

在EncryptBase.cpp修改為以下代碼:
// EncryptBase.cpp: 定義 DLL 應用程序的導出函數。
//
#include "stdafx.h"
#include "EncryptBase.h"
#include "base64.h"
#include <aes.h>
#include <modes.h>
#include <Hex.h> // StreamTransformationFilter
using namespace std;
using namespace CryptoPP;
#pragma comment(lib, "cryptlib.lib")
//加密
int AesEncrypt(AESMode mode, BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int ivlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen)
{
int rtnRs = 0;
switch (mode)
{
case AESMode::ECB:
rtnRs = AesEcbEncrypt(scheme, key, keylen, iv, ivlen, plainText, txtlen, out, outLen);
break;
case AESMode::CBC:
rtnRs = AesCbcEncrypt(scheme, key, keylen, iv, ivlen, plainText, txtlen, out, outLen);
break;
}
return rtnRs;
}
//解密
int AesDecrypt(AESMode mode, BlockPaddingScheme scheme, const BYTE *key, int keylen, const BYTE *iv, int ivlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen)
{
int rtnRs = 0;
switch (mode)
{
case AESMode::ECB:
rtnRs = AesEcbDecrypt(scheme, key, keylen, iv, ivlen, cipherText, txtlen, out, outLen);
break;
case AESMode::CBC:
rtnRs = AesCbcDecrypt(scheme, key, keylen, iv, ivlen, cipherText, txtlen, out, outLen);
break;
}
return rtnRs;
}
//AES_ECB加密
int AesEcbEncrypt(BlockPaddingScheme scheme, const BYTE *skey, int keylen, const BYTE *iv, int ivlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen)
{
int rtnLength = -1;
try {
//填key
SecByteBlock key(AES::MIN_KEYLENGTH);
memset(key, 0x00, key.size());
if (keylen <= AES::MIN_KEYLENGTH)
{
memcpy(key, skey, keylen);
}
else
{
memcpy(key, skey, AES::MIN_KEYLENGTH);
}
AES::Encryption aesEncryption(key, AES::MIN_KEYLENGTH);
ECB_Mode_ExternalCipher::Encryption ecbEncryption(aesEncryption);
vector<BYTE> encrypted;
StreamTransformationFilter stfEncryptor(
ecbEncryption,
new VectorSink(encrypted),
(CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme)scheme);
stfEncryptor.Put(plainText, txtlen); //this is where it crashes
stfEncryptor.MessageEnd();
rtnLength = encrypted.size();
memcpy(out, encrypted.data(), rtnLength);
}
catch (exception e)
{
cout << e.what() << endl;
//TODO:記錄日志
rtnLength = -1;
}
return rtnLength;
}
//AES_ECB解密
int AesEcbDecrypt(BlockPaddingScheme scheme, const BYTE *skey, int keylen, const BYTE *siv, int ivlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen)
{
int rtnLength = -1;
try {
//填key
SecByteBlock key(AES::MIN_KEYLENGTH);
memset(key, 0x00, key.size());
if (keylen <= AES::MIN_KEYLENGTH)
{
memcpy(key, skey, keylen);
}
else
{
memcpy(key, skey, AES::MIN_KEYLENGTH);
}
ECB_Mode<AES>::Decryption ecbDecryption(key, AES::MIN_KEYLENGTH);
vector<BYTE> decrypted;
StreamTransformationFilter stfDecryptor(
ecbDecryption,
new VectorSink(decrypted),
(CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme)scheme);
stfDecryptor.Put(cipherText, txtlen);
stfDecryptor.MessageEnd();
rtnLength = decrypted.size();
memcpy(out, decrypted.data(), rtnLength);
}
catch (exception e)
{
cout << e.what() << endl;
//TODO:記錄日志
}
return rtnLength;
}
//AES_CBC加密
int AesCbcEncrypt(BlockPaddingScheme scheme, const BYTE *skey, int keylen, const BYTE *siv, int ivlen, const BYTE *plainText, int txtlen, BYTE *out, int outLen)
{
int rtnLength = -1;
try {
//填key
SecByteBlock key(AES::MIN_KEYLENGTH);
memset(key, 0x00, key.size());
if (keylen <= AES::MIN_KEYLENGTH)
{
memcpy(key, skey, keylen);
}
else
{
memcpy(key, skey, AES::MIN_KEYLENGTH);
}
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv, 0x00, AES::BLOCKSIZE);
if (ivlen <= AES::BLOCKSIZE)
{
memcpy(iv, siv, ivlen);
}
else
{
memcpy(iv, siv, AES::BLOCKSIZE);
}
AES::Encryption aesEncryption((byte *)key, AES::MIN_KEYLENGTH);
CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, iv);
vector<BYTE> decrypted;
StreamTransformationFilter stfDecryptor(
cbcEncryption,
new VectorSink(decrypted),
(CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme)scheme);
stfDecryptor.Put(plainText, txtlen);
stfDecryptor.MessageEnd();
rtnLength = decrypted.size();
memcpy(out, decrypted.data(), rtnLength);
}
catch (exception e)
{
cout << e.what() << endl;
//TODO:記錄日志
rtnLength = -1;
}
return rtnLength;
}
//AES_CBC解密
int AesCbcDecrypt(BlockPaddingScheme scheme, const BYTE *skey, int keylen, const BYTE *siv, int ivlen, const BYTE *cipherText, int txtlen, BYTE *out, int outLen)
{
int rtnLength = -1;
try
{
//填key
SecByteBlock key(AES::MIN_KEYLENGTH);
memset(key, 0x00, key.size());
if (keylen <= AES::MIN_KEYLENGTH)
{
memcpy(key, skey, keylen);
}
else
{
memcpy(key, skey, AES::MIN_KEYLENGTH);
}
//填iv
byte iv[AES::BLOCKSIZE];
memset(iv, 0x00, AES::BLOCKSIZE);
if (ivlen <= AES::BLOCKSIZE)
{
memcpy(iv, siv, ivlen);
}
else
{
memcpy(iv, siv, AES::BLOCKSIZE);
}
CBC_Mode<AES>::Decryption cbcDecryption(key, AES::MIN_KEYLENGTH, iv);
vector<BYTE> decrypted;
StreamTransformationFilter stfDecryptor(
cbcDecryption,
new VectorSink(decrypted),
(CryptoPP::BlockPaddingSchemeDef::BlockPaddingScheme)scheme);
stfDecryptor.Put(cipherText, txtlen);
stfDecryptor.MessageEnd();
rtnLength = decrypted.size();
memcpy(out, decrypted.data(), rtnLength);
}
catch (exception e)
{
cout << e.what() << endl;
//TODO:記錄日志
}
return rtnLength;
}
五、編寫測試代碼
1、在在FrameworkConsoleTest項目的Program編寫以下代碼。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace FrameworkConsoleTest
{
class Program
{
[DllImport("EncryptBase.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AesEncrypt(AESMode mode, BlockPaddingScheme scheme, byte[] key, int keylen, byte[] iv, int ivlen, byte[] plainTextPtr, int txtlen, byte[] outBytes, int outLen);
[DllImport("EncryptBase.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AesDecrypt(AESMode mode, BlockPaddingScheme scheme, byte[] key, int keylen, byte[] iv, int ivlen, byte[] plainTextPtr, int txtlen, byte[] outBytes, int outLen);
static void Main(string[] args)
{
try
{
string plainText = "123456";
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
//AesKey密匙
string key = "abc123456_+,./@$";
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
//IV向量
byte[] ivBytes = new byte[16];
for (int i = 0; i < 16; i++)
{
ivBytes[i] = 0;
}
string strEncrypt = string.Empty;
string strDecrypt = string.Empty;
byte[] encryptOutBytes = new byte[100];
int execRs = -1;
execRs = ToAesEncrypt(AESMode.ECB, BlockPaddingScheme.PKCS_PADDING, keyBytes, ivBytes, plainTextBytes, ref encryptOutBytes);
if (execRs > 0)
{
strEncrypt = Convert.ToBase64String(encryptOutBytes, 0, execRs);
}
Console.WriteLine("Aes Ecb Encrypt,明文:{0},密文:{1}", plainText, strEncrypt);
if (execRs > 0)
{
byte[] decryptBytes = new byte[execRs];
for (int i = 0; i<execRs; i++)
{
decryptBytes[i] = encryptOutBytes[i];
}
byte[] decryptOutBytes = new byte[100];
execRs = ToAesDecrypt(AESMode.ECB, BlockPaddingScheme.PKCS_PADDING, keyBytes, ivBytes, decryptBytes, ref decryptOutBytes);
if (execRs > 0)
{
strDecrypt = System.Text.Encoding.UTF8.GetString(decryptOutBytes, 0, execRs);
}
Console.WriteLine("Aes Ecb Decrypt,解密后明文:{0}", strDecrypt);
}
byte[] encryptOutBytes2 = new byte[100];
execRs = ToAesEncrypt(AESMode.CBC, BlockPaddingScheme.PKCS_PADDING, keyBytes, ivBytes, plainTextBytes, ref encryptOutBytes2);
if (execRs > 0)
{
strEncrypt = Convert.ToBase64String(encryptOutBytes2, 0, execRs);
}
Console.WriteLine("Aes Cbc Encrypt,明文:{0},密文:{1}", plainText, strEncrypt);
if (execRs > 0)
{
byte[] decryptBytes2 = new byte[execRs];
for (int i = 0; i<execRs; i++)
{
decryptBytes2[i] = encryptOutBytes2[i];
}
byte[] decryptOutBytes2 = new byte[100];
execRs = ToAesDecrypt(AESMode.CBC, BlockPaddingScheme.PKCS_PADDING, keyBytes, ivBytes, decryptBytes2, ref decryptOutBytes2);
if (execRs > 0)
{
strDecrypt = System.Text.Encoding.UTF8.GetString(decryptOutBytes2, 0, execRs);
}
Console.WriteLine("Aes Cbc Decrypt,解密后明文:{0}", strDecrypt);
}
}
catch (Exception ex)
{
Console.WriteLine("Main,ex:{0}", ex);
}
Console.ReadKey();
}
private static int ToAesEncrypt(AESMode mode, BlockPaddingScheme scheme, byte[] keyBytes, byte[] ivBytes, byte[] encryptBytes, ref byte[] outBytes)
{
int rtnRs = -1;
if (encryptBytes == null || encryptBytes.Count() == 0)
{
rtnRs = 0;
goto TOEND;
}
try
{
int keyBytesLen = keyBytes.Length;
int ivBytesLen = ivBytes.Length;
int txtBytesLen = encryptBytes.Length;
int outLen = outBytes.Length;
//C#數組有長度,C++指針沒長度,所以函數應該定義兩個參數,一個數據BYTE*,一個長度int
rtnRs = AesEncrypt(mode, scheme, keyBytes, keyBytesLen, ivBytes, ivBytesLen, encryptBytes, txtBytesLen, outBytes, outLen);
}
catch (Exception ex)
{
Console.WriteLine("AesEncrypt,ex:{0}", ex);
}
TOEND:
;
return rtnRs;
}
private static int ToAesDecrypt(AESMode mode, BlockPaddingScheme scheme, byte[] keyBytes, byte[] ivBytes, byte[] decryptBytes, ref byte[] outBytes)
{
int rtnRs = -1;
if (decryptBytes == null || decryptBytes.Count() == 0)
{
rtnRs = 0;
goto TOEND;
}
try
{
//因為 C++ 返回的是 char* ,是指針,所以c# 要用 IntPtr 來接收
int txtBytesLen = decryptBytes.Length;
int keyBytesLen = keyBytes.Length;
int ivBytesLen = ivBytes.Length;
int outLen = outBytes.Length;
//C#數組有長度,C++指針沒長度,所以函數應該定義兩個參數,一個數據char*,一個長度int
rtnRs = AesDecrypt(mode, scheme, keyBytes, keyBytesLen, ivBytes, ivBytesLen, decryptBytes, txtBytesLen, outBytes, outLen);
}
catch (Exception ex)
{
Console.WriteLine("AesDecrypt,ex:{0}", ex);
}
TOEND:
;
return rtnRs;
}
}
}
2、添加Enum.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FrameworkConsoleTest
{
enum AESMode
{
ECB = 0x00,
CBC,
};
enum BlockPaddingScheme
{
NO_PADDING,
ZEROS_PADDING,
PKCS_PADDING,
ONE_AND_ZEROS_PADDING,
W3C_PADDING,
DEFAULT_PADDING
};
}
六、整體結構

七、結果測試
1、運行結果

2、通過和其它平台測試比較結果,我是在http://tool.chacuo.net/cryptaes進行結果比對的。 
八、下載地址
https://download.csdn.net/download/suterfo/12155867
九、參考資料:
https://www.cnblogs.com/cxun/archive/2008/07/30/743541.html
https://blog.csdn.net/liang19890820/article/details/51659452
https://www.cryptopp.com/wiki/Filter
