本文章使用上一篇《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