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

 

posted @ 2020-02-14 11:53  努力吧兄Dei  阅读(2857)  评论(0编辑  收藏  举报