C#調用Crypto++庫AES ECB CBC加解密


 本文章使用上一篇《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://stackoverflow.com/questions/59323973/aes-ctr-decryption-stops-unexpectedly-using-crypto?r=SearchResults

https://www.cryptopp.com/wiki/Filter

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM