在用unity开发过程中,由于客户需要将消息传输的消息体进行加密,由于客户提供了服务器代码,在我们开发过程中,尝试着使用C# 官方MD5, AES加解密算法去对消息进行加密,解密操作。然而我们所得的结果与想要的结果差别很大。所以我们采用了将客户的C++加密解密代码部分进行拆分,修改,生成动态链接库程序,以此在unity中调用。
由于unity开发过程中使用的是C#,所以在测试程序中我用控制台程序替换了unity程序。
在我提供的源码文件中包含了两个文件,第一个AES ,这是C++动态链接库程序,里面包含了MD5 ,AES的加解密。代码结构如图:
aes.h,aesencryptor.h, aes.cpp,aesencryptor.cpp是AES加解密部分,md5.h,md5.cpp 是MD5加密部分 ,MyDll.h和MyDll.cpp是对加密,解密方法的封装,以便生成dll文件后,方法可以被外部程序访问。
MyDll.h文件有效代码如下:
#pragma once
#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif
#include <string>
extern "C" MATHLIBRARY_API int Encrypt(char* md5, char* result);
extern "C" MATHLIBRARY_API int GetMD5(char* msg, char* result);
extern "C" MATHLIBRARY_API int Decrypt(char* aes, char* md5);
MyDll.cpp文件有效代码如下:
unsigned char key[16] = { 0x40, 0x1e, 0x78, 0x26 };
AesEncryptor l_AES(key);
char returnsmall[1024];
char returnlarge[1024*30];
std::ofstream outfile;
int Encrypt(char* md5, char* result ) {
std::string temp = std::string(md5);
std::string AESstr = l_AES.EncryptString(temp);
//const int len = AESstr.length();
char* c =const_cast<char*>( AESstr.c_str());
//strcpy_s(c, len+1,AESstr.c_str());
int length = strlen(c);
memset(returnsmall, 0, length);
memcpy(returnsmall, c, length);
memcpy(result, returnsmall, length);
return length;
}
int GetMD5(char* msg, char* result)
{
std::string temp = std::string(msg);
std::string str = MD5(temp).toString();
char* c= const_cast<char*>(str.c_str());
int Length = strlen(c);
memset(returnsmall, 0, Length);
memcpy(returnsmall, c, Length);
memcpy(result, returnsmall, Length);
return Length;
}
int Decrypt(char* aes, char* md5) {
std::string temp = std::string(aes);
std::string str = l_AES.DecryptString(temp);
char* c = const_cast<char*>(str.c_str());
int Length = strlen(c);
memset(returnsmall, 0, Length);
memcpy(returnsmall, c, Length);
memcpy(md5, returnsmall, Length);
return Length;
}
Encrypt 方法有两个Char * 类型的参数,第一个是需要在C#程序中传输MD5加密后的字符串,第二个参数用于C# 接收AES加密后的字符串,返回值是C#程序判断接收之后的有效长度,GetMD5,Decrypt分别是MD5 加密和AES解密方法,参数与返回值用法和Encrypt 相同。
C# 测试代码程序如下:
using System.Collections;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Runtime.InteropServices;
public class sUserInfo
{
public string password;
public string username;//用户名
public sUserInfo()
{
password = "1";
username = "1";
}
}
/// <summary>
/// AES加密算法
/// </summary>
public class AESEncryption
{
[DllImport(@".\DLL\AES.dll", EntryPoint = "Encrypt", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int Encrypt(string str, ref byte res);
[DllImport(@".\DLL\AES.dll", EntryPoint = "GetMD5", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int GetMD5(string str, ref byte res);
[DllImport(@".\DLL\AES.dll", EntryPoint = "Decrypt", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int Decrypt(string str, ref byte res);
public static string AesEncrypt(string str)
{
byte[] s = new byte[1024];
int l = Encrypt(str, ref s[0]);
byte[] data = null;
if (l > s.Length) return "";
else
{
data = new byte[l];
Buffer.BlockCopy(s, 0, data, 0, l);
string strGet = System.Text.Encoding.Default.GetString(data, 0, data.Length);
return strGet;
}
}
public static string MD5Encrypt(string str)
{
byte[] s = new byte[1024];
int l = GetMD5(str, ref s[0]);
byte[] data = null;
if (l > s.Length) return "";
else
{
data = new byte[l];
Buffer.BlockCopy(s, 0, data, 0, l);
string strGet = System.Text.Encoding.Default.GetString(data, 0, data.Length);
return strGet;
}
}
public static string CreateData(string msg, string encrypt)
{
string buffer = "";
string ms = msg + encrypt;
int length = ms.Length;
int cou = 8 - length.ToString().Length;
for (int i = 0; i < cou; i++)
{
buffer += "0";
}
buffer += length.ToString() + ms;
return buffer;
}
public static bool DataDecrypt(string msg)
{
int a = 0; int index = 0;
for (int i = msg.Length - 1; i >= 0; i--)
{
a++;
if (a == 96)
{
index = i;
break;
}
}
string aes = ""; string data = "";
aes = msg.Substring(index);
data = msg.Substring(0, msg.Length - 96);
string md5 = MD5Encrypt(data);
byte[] s = new byte[1024];
int l = Decrypt(aes, ref s[0]);
byte[] redata = null; string strGet = "";
if (l > s.Length) strGet = "";
else
{
redata = new byte[l];
Buffer.BlockCopy(s, 0, redata, 0, l);
strGet = System.Text.Encoding.Default.GetString(redata, 0, redata.Length);
}
if (md5 == strGet) return true;
return false;
}
}
首先需要将使用的方法进行声明:
DllImport(@".\DLL\AES.dll", EntryPoint = "Encrypt", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int Encrypt(string str, ref byte res);
[DllImport(@".\DLL\AES.dll", EntryPoint = "GetMD5", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int GetMD5(string str, ref byte res);
[DllImport(@".\DLL\AES.dll", EntryPoint = "Decrypt", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, ExactSpelling = false)]
public static extern int Decrypt(string str, ref byte res);
具体使用请参照测试程序进行测试。
由于C#的字符串与C++的std::string 的区别,所以在C++方法内的参数是不能用std::string 的参数进行传递。至于接收加密后的字符串,为什么不能直接返回char* 类型数据,本渣渣也不是很清楚,如有大佬知晓欢迎留言,膜拜大佬。在此,我才用的是将地址传输给C++方法,在C++程序方法内将加密后的数据直接拷贝到内存中,然后根据返回值大小(有效数据长度),拷贝内存中有效数据进行转换。
源码请自行下载:https://download.csdn.net/download/qq_37699470/12412348