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

C#调用Crypto++库AES ECB CBC加解密的更多相关文章

  1. python 实现 AES ECB模式加解密

    AES ECB模式加解密使用cryptopp完成AES的ECB模式进行加解密. AES加密数据块分组长度必须为128比特,密钥长度可以是128比特.192比特.256比特中的任意一个.(8比特 == ...

  2. golang AES/ECB/PKCS5 加密解密 url-safe-base64

    因为项目的需要用到golang的一种特殊的加密解密算法AES/ECB/PKCS5,但是算法并没有包含在标准库中,经过多次失败的尝试,终于解码成功,特此分享: /* 描述 : golang AES/EC ...

  3. Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密

    本文目录 1. 摘要 2. MD5加密封装 3. AES的加密.解密 4. DES加密/解密 5. 总结 1.  摘要 C#中常用的一些加密和解密方案,如:md5加密.RSA加密与解密和DES加密等, ...

  4. iOS CommonCrypto 对称加密 AES ecb,cbc

    CommonCrypto 为苹果提供的系统加密接口,支持iOS 和 mac 开发: 不仅限于AES加密,提供的接口还支持其他DES,3DES,RC4,BLOWFISH等算法, 本文章主要讨论AES在i ...

  5. AES/ECB/NoPadding 加减密

    package unit; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import org.apache. ...

  6. POCO库——Foundation组件之加解密Crypt

    加解密Crypt:内部提供多种加解密方式.信息摘要提取.随机数产生等,具体的算法内部实现不做研究学习: DigestEngine.h :DigestEngine类作为各种摘要提取的基类,提供必要的接口 ...

  7. 使用java实现AES算法的加解密(亲测可用)

    话不多说,直接上代码 import javax.crypto.Cipher;   import javax.crypto.spec.IvParameterSpec; import javax.cryp ...

  8. AES和RSA加解密的Python用法

    AES AES 是一种对称加密算法,用key对一段text加密,则用同一个key对密文解密, from Crypto import Random from Crypto.Hash import SHA ...

  9. 使用RAS+AES对接口数据加解密

    在实际开发中,会遇到两个系统之间传输数据,为了对传输的数据进行保护,需要发送方对接口数据进行加密,接收方对数据解密. 对数据加密,采用RSA+AES双重加密,是数据更加安全. 使用前提: 如果客户端本 ...

随机推荐

  1. GTMD并查集!

    徐州的A我因为并查集写错T了整场.. int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } GTMD!

  2. ELK搭建(docker环境)

    ELK是Elasticsearch.Logstash.Kibana的简称,这三者是核心套件,但并非全部. Elasticsearch是实时全文搜索和分析引擎,提供搜集.分析.存储数据三大功能:是一套开 ...

  3. TestStand 基础知识[7]--Build-in Step Types (2)

    接着上一篇文章:TestStand 基础知识[6] Build-In StepTypes(1) 继续介绍: 还是先把Build-in StepTypes图片贴一下, 1. Call Executabl ...

  4. 大话IDL编程之函数功能调用(envi_doit、ENVIRaster、ENVITask)

    2020年2月1日.好长时间没更新博客,还真有点不习惯.受新型冠性病毒的影响,平时街上熙熙攘攘的人流了无踪影,2020的春节竟然来的如此冷清.为响应“呆在家里就是做贡献的号召”,在家一宅就是十多天.闲 ...

  5. Golang内存模型

    Ref: https://golang.org/ref/mem 简介 golang内存模型,主要说明了如下问题.在一个goroutine中读取变量,而该变量是由其他goroutine赋值的,这种情况下 ...

  6. 《Head first设计模式》学习笔记

    1. 单例模式 2. 工厂模式 3. 抽象工厂 4. 策略模式 5. 观察者模式 6. 装饰者模式 7. 命令模式 8. 适配器模式 9. 外观模式 10. 模版方法模式 11. 迭代器模式 设计模式 ...

  7. 草台班子--Window10文件管理器默认显示图片DPI及位深信息

    草台班子--Window10文件管理器默认显示图片DPI及位深信息    ​ 经常处理一些图片的DPI/PPI信息,总是需要找软件打开确认,通过Windows10文件浏览器可以默认显示图片的DPI信息 ...

  8. javascript获取一个字符串的长度(包含中文)

    /* getStrLen(str):获取一个字符串的长度(包含中文) */ function getStrLen(str){ let len = 0, i, c; for (i = 0; i < ...

  9. 面试题|手写JSON解析器

    这周的 Cassidoo 的每周简讯有这么一个面试题:: 写一个函数,这个函数接收一个正确的 JSON 字符串并将其转化为一个对象(或字典,映射等,这取决于你选择的语言).示例输入: fakePars ...

  10. k8s系列---pod介绍

    # yaml格式的pod定义文件完整内容: apiVersion: v1 #必选,版本号,例如v1 kind: Pod #必选,Pod metadata: #必选,元数据 name: string # ...