序
今天同事让写一个DLL给他,3DES+BASE64 加解密接口2个。
以前做的实验,做了半边(java加密,C解密),因为那次就半边的需求。
这次都要由C这边来做(C加密,C解密), 磨叽了2个小时。
其实正确的姿势是,以前就应该将实验做全的,这次应该拿过来就好用。
又杯具一次, 一个愉快的周末没开个好头.
对于基础代码,如果最开始没写好测试用例,以后有可能会写错。会怀疑到接口实现的问题上,在调试上容易浪费时间。
openssl代码维护真容易,都是自解释的函数名和参数名,而且参数摆放规律一致,如果有一个可用的加密函数,很容易维护出一个可用的解密函数。
实验
工程下载点
src_3DESCryptHelper_2017_0528_0026.zip
工程输出
2017/05/28 00:24 <DIR> .
2017/05/28 00:24 <DIR> ..
2017/05/28 00:24 12,800 3DESCryptHelper.dll
2017/05/28 00:24 1,041 3DESCryptHelper.exp
2017/05/28 00:24 2,280 3DESCryptHelper.lib
2017/04/08 11:27 1,183,232 libeay32.dll
2017/04/08 11:27 270,848 ssleay32.dll
2017/05/28 00:24 10,240 test3DESCryptHelper.exe
6 个文件 1,480,441 字节
接口测试程序
// test3DESCryptHelper.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <atlconv.h>
#include <string>
#include "../dll/IF_3DESCryptHelper.h"
#pragma comment(lib, "3DESCryptHelper.lib")
#define _3DES_CRYPT_KEY "123456781234567812345678"
int _tmain(int argc, _TCHAR* argv[])
{
USES_CONVERSION;
std::string strEncBase64 = "";
std::string strPlainTxt = "";
strEncBase64 = IF_3DES_CryptHlerper_encrypt("hello", _3DES_CRYPT_KEY);
printf("strEncBase64 = %s\n", strEncBase64.c_str());
strPlainTxt = IF_3DES_CryptHlerper_decrypt(strEncBase64.c_str(), _3DES_CRYPT_KEY);
printf("strPlainTxt = %s\n", strPlainTxt.c_str());
system("pause");
return 0;
}
运行效果
strEncBase64 = sRjgl3qfUBnbhZgumtlVgtCN+YQap6PM
strPlainTxt = hello
请按任意键继续. . .
接口定义
// @file IF_3DESCryptHelper.h
// @brief 接口文件
#ifndef __IF_3DES_CRYPT_HELPER_H__
#define __IF_3DES_CRYPT_HELPER_H__
#ifndef _3DES_CRYPT_HELPER_EXPORTS
#define _3DES_CRYPT_HELPER_API __declspec(dllimport)
#else
#define _3DES_CRYPT_HELPER_API __declspec(dllexport)
#endif
// =============================================================================
// 错误码定义
// =============================================================================
#define IF_OK 0
#define IF_ERR_BASE IF_OK // 接口执行成功
#define IF_ERR_PARAM1_INVALID (IF_ERR_BASE + 1) // 参数1无效
#define IF_ERR_PARAM2_INVALID (IF_ERR_BASE + 2) // 参数2无效
#define IF_ERR_PARAM3_INVALID (IF_ERR_BASE + 3) // 参数3无效
#define IF_ERR_PARAM1_LEN_TOO_SMALL (IF_ERR_BASE + 4) // 参数1缓冲区长度太短了
#define IF_ERR_PARAM2_LEN_TOO_SMALL (IF_ERR_BASE + 5) // 参数2缓冲区长度太短了
#define IF_ERR_PARAM3_LEN_TOO_SMALL (IF_ERR_BASE + 6) // 参数3缓冲区长度太短了
#define IF_ERR_NOT_IMP (IF_ERR_BASE + 7) // 接口未实现
#define IF_ERR_UNKNOWN (IF_ERR_BASE + 0x1000) // 未知错误
// =============================================================================
// 接口定义
// =============================================================================
#ifdef __cplusplus
extern "C" {
#endif
// @fn _CryptHlerper_encrypt
// @brief 文本加密接口
// 给定明文和密钥, 输出密文(3DES加密+base64)
// @param psz_plain_txt, 明文文本
// @param psz_key, 密钥
// @return const char*, 密文
_3DES_CRYPT_HELPER_API const char* __stdcall IF_3DES_CryptHlerper_encrypt(/*IN*/ const char* psz_plain_txt, /*IN*/ const char* psz_key);
// @fn 3DES_CryptHlerper_decrypt
// @brief 文本解密接口
// 给定加密接口输出的密文和密钥,输出明文(3DES解密+unbase64)
// @param psz_cipher_txt, 密文(3DES_CryptHlerper_encrypt的输出)
// @param psz_key, 密钥
// @return const char*, 明文文本
_3DES_CRYPT_HELPER_API const char* __stdcall IF_3DES_CryptHlerper_decrypt(/*IN*/ const char* psz_cipher_txt, /*IN*/ const char* psz_key);
#ifdef __cplusplus
}
#endif
#endif // #ifndef __IF_3DES_CRYPT_HELPER_H__
// LsBase64.h: interface for the LsBase64 class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_)
#define AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
namespace ns_ls
{
unsigned int base64_encode(const unsigned char* bytes_to_encode, unsigned int in_len, unsigned char* encoded_buffer, unsigned int& out_len);
unsigned int base64_decode(const unsigned char* encoded_string, unsigned int in_len, unsigned char* decoded_buffer, unsigned int& out_len);
}
#endif // !defined(AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_)
接口实现
// 3DESCryptHelper.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#include <atlconv.h>
#include <string>
#include <WinSock2.h>
#include <time.h>
#pragma comment(lib, "ws2_32.lib")
#include "IF_3DESCryptHelper.h"
#include "LsBase64.h"
#include "openssl\evp.h"
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
// iv is 20170528
#define FILL_MY_IV iv[0] = 2; \
iv[1] = 0; \
iv[2] = 1; \
iv[3] = 7; \
iv[4] = 0; \
iv[5] = 5; \
iv[6] = 2; \
iv[7] = 8;
std::string g_str_txt_was_encrypt = ""; // 加密后的密文
std::string g_str_txt_was_decrypt = ""; // 解密后的明文
static void fn_copy_by_end_char(WCHAR * pDst, WCHAR * pSrc, WCHAR cEndChar);
int des3_DecryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_dec, unsigned char* out_dec, int in_len, int* out_len);
int des3_EncryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_enc, unsigned char* out_enc, int in_len, int* out_len);
BOOL php_time_2_systemtime(long lPhpTime, SYSTEMTIME* pst)
{
BOOL bRc = FALSE;
time_t tmt = -1;
struct tm* pGmt = NULL;
do
{
if (NULL == pst)
break;
tmt = lPhpTime;
pGmt = gmtime(&tmt);
if (NULL == pGmt)
break;
pst->wYear = 1900 + pGmt->tm_year;
pst->wMonth = 1 + pGmt->tm_mon;
pst->wDay = pGmt->tm_mday;
pst->wHour = pGmt->tm_hour;
pst->wMinute = pGmt->tm_min;
pst->wSecond = pGmt->tm_sec;
pst->wDayOfWeek = pGmt->tm_wday;
bRc = TRUE;
} while (0);
return bRc;
}
static void fn_copy_by_end_char(WCHAR * pDst, WCHAR * pSrc, WCHAR cEndChar)
{
DWORD dwIndex = 0;
if((NULL != pDst) && (NULL != pSrc))
{
do
{
if((L'\0' == pSrc[dwIndex]) || (cEndChar == pSrc[dwIndex]))
{
break;
}
pDst[dwIndex] = pSrc[dwIndex];
dwIndex++;
}
while(1);
}
}
/*
* key:加密密钥,3DES密钥size = 24
* iv:加密初始向量
* in_dec:密文数组,输入数组
* out_dec:解密后的数组,输出数组
* in_len:密文长度
* out_len:明文长度
* */
//解密函数
int des3_DecryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_dec, unsigned char* out_dec, int in_len, int* out_len)
{
int out1 = 0; //第一次使用update解密的数据长度
int out2 = 0; //剩余的字段,经过final解密并去除填充后的长度
int rv = 0;
EVP_CIPHER_CTX ctx;
//初始化ctx
EVP_CIPHER_CTX_init(&ctx);
//设置解密的算法、key和iv
// java端加密模式采用, "DESede/CBC/PKCS5Padding", so 参数2为EVP_des_ede3_cbc()
rv = EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, key, iv);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//循环读取原文,解密后后保存到明文文件。
rv = EVP_DecryptUpdate(&ctx, out_dec, &out1, in_dec, in_len);//解密
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//解密结束
rv = EVP_DecryptFinal_ex(&ctx, out_dec + out1, &out2);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
*out_len = (out1 + out2);
EVP_CIPHER_CTX_cleanup(&ctx);//清除EVP加密上下文环境
return 0;
}
int des3_EncryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_enc, unsigned char* out_enc, int in_len, int* out_len)
{
int out1 = 0; //第一次使用update解密的数据长度
int out2 = 0; //剩余的字段,经过final解密并去除填充后的长度
int rv = 0;
EVP_CIPHER_CTX ctx;
//初始化ctx
EVP_CIPHER_CTX_init(&ctx);
//设置解密的算法、key和iv
// java端加密模式采用, "DESede/CBC/PKCS5Padding", so 参数2为EVP_des_ede3_cbc()
rv = EVP_EncryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, key, iv);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//循环读取原文,解密后后保存到明文文件。
rv = EVP_EncryptUpdate(&ctx, out_enc, &out1, in_enc, in_len);//解密
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//解密结束
rv = EVP_EncryptFinal_ex(&ctx, out_enc + out1, &out2);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
*out_len = (out1 + out2);
EVP_CIPHER_CTX_cleanup(&ctx);//清除EVP加密上下文环境
return 0;
}
_3DES_CRYPT_HELPER_API const char* __stdcall IF_3DES_CryptHlerper_encrypt(/*IN*/ const char* psz_plain_txt, /*IN*/ const char* psz_key)
{
USES_CONVERSION;
DWORD dwRc = IF_ERR_UNKNOWN;
unsigned char key[EVP_MAX_KEY_LENGTH]; //保存密钥的数组
unsigned char iv[EVP_MAX_IV_LENGTH]; //保存初始化向量的数组
char* psz_plain_txt_new = NULL;
int ilen_plain_txt_new = 0;
char* psz_cipher_txt = NULL;
int ilen_cipher_txt = 0;
char* psz_base64_txt = NULL;
int ilen_base64_txt = 0;
UINT ulRc = 0;
UINT ulLenOut = 0;
int iLenEncOut = 0;
memset(key, 0, sizeof(key));
memset(iv, 0, sizeof(iv));
g_str_txt_was_encrypt.clear();
// 设置IV
// static final byte m_iv8_byte_ary[] = {0,0,0,0,0,0,0,0};
// java加密时, iv就8个字节
memset(iv, 0, sizeof(iv));
FILL_MY_IV
do
{
if ((NULL == psz_plain_txt) || (strlen(psz_plain_txt) <= 0))
{
dwRc = IF_ERR_PARAM1_INVALID;
break;
}
if ((NULL == psz_key) || (strlen(psz_key) != 24))
{
dwRc = IF_ERR_PARAM2_INVALID;
break;
}
// 设置key, 3DES Key is 24bytes
// String str_key = new String("123456781234567812345678");
memcpy(key, psz_key, 24);
ilen_plain_txt_new = strlen(psz_plain_txt);
if (0 != (ilen_plain_txt_new % 16)) {
ilen_plain_txt_new = ilen_plain_txt_new - (ilen_plain_txt_new % 16) + 16; // base64还会再长一些
}
psz_plain_txt_new = new char[ilen_plain_txt_new + 1];
ZeroMemory(psz_plain_txt_new, ilen_plain_txt_new + 1);
strcpy(psz_plain_txt_new, psz_plain_txt);
ilen_cipher_txt = ilen_plain_txt_new * 2;
psz_cipher_txt = new char[ilen_cipher_txt + 1];
ZeroMemory(psz_cipher_txt, ilen_cipher_txt + 1);
ilen_base64_txt = ilen_cipher_txt * 2;
psz_base64_txt = new char[ilen_base64_txt + 1];
ZeroMemory(psz_base64_txt, ilen_base64_txt + 1);
iLenEncOut = ilen_cipher_txt;
if (0 != des3_EncryptBuffer(key, iv, (BYTE*)psz_plain_txt_new, (BYTE*)psz_cipher_txt, ilen_plain_txt_new, &iLenEncOut)) {
break;
}
// base64
ulLenOut = 0;
ulRc = ns_ls::base64_encode((const BYTE *)psz_cipher_txt, iLenEncOut, (BYTE *)psz_base64_txt, ulLenOut);
// test ok
// UINT i_tmp_len_out = 0;
// ZeroMemory(psz_cipher_txt, ilen_cipher_txt + 1);
// ulRc = ns_ls::base64_decode((const BYTE *)psz_base64_txt, ulLenOut, (BYTE*)psz_cipher_txt, i_tmp_len_out);
g_str_txt_was_encrypt = psz_base64_txt;
}
while(0);
if (NULL != psz_plain_txt_new) {
delete[] psz_plain_txt_new;
psz_plain_txt_new = NULL;
}
if (NULL != psz_cipher_txt) {
delete[] psz_cipher_txt;
psz_cipher_txt = NULL;
}
if (NULL != psz_base64_txt) {
delete[] psz_base64_txt;
psz_base64_txt = NULL;
}
return g_str_txt_was_encrypt.c_str();
}
_3DES_CRYPT_HELPER_API const char* __stdcall IF_3DES_CryptHlerper_decrypt(/*IN*/ const char* psz_cipher_txt, /*IN*/ const char* psz_key)
{
USES_CONVERSION;
DWORD dwRc = IF_ERR_UNKNOWN;
unsigned char key[EVP_MAX_KEY_LENGTH]; //保存密钥的数组
unsigned char iv[EVP_MAX_IV_LENGTH]; //保存初始化向量的数组
char* psz_un_base64_txt = NULL;
int ilen_un_base64_txt = 0;
char* psz_plain_txt = NULL;
int ilen_plain_txt = 0;
UINT ulRc = 0;
UINT ulLenOut = 0;
int iLenDecOut = 0;
memset(key, 0, sizeof(key));
memset(iv, 0, sizeof(iv));
g_str_txt_was_encrypt.clear();
// 设置IV
// static final byte m_iv8_byte_ary[] = {0,0,0,0,0,0,0,0};
// java加密时, iv就8个字节
memset(iv, 0, sizeof(iv));
FILL_MY_IV
do
{
if ((NULL == psz_cipher_txt) || (strlen(psz_cipher_txt) <= 0))
{
dwRc = IF_ERR_PARAM1_INVALID;
break;
}
if ((NULL == psz_key) || (strlen(psz_key) != 24))
{
dwRc = IF_ERR_PARAM2_INVALID;
break;
}
// 设置key, 3DES Key is 24bytes
// String str_key = new String("123456781234567812345678");
memcpy(key, psz_key, 24);
ilen_un_base64_txt = strlen(psz_cipher_txt);
psz_un_base64_txt = new char[ilen_un_base64_txt + 1];
ZeroMemory(psz_un_base64_txt, ilen_un_base64_txt + 1);
ilen_plain_txt = ilen_un_base64_txt;
psz_plain_txt = new char[ilen_plain_txt + 1];
ZeroMemory(psz_plain_txt, ilen_plain_txt + 1);
// unbase64
ulLenOut = 0;
ulRc = ns_ls::base64_decode((const BYTE *)psz_cipher_txt, strlen(psz_cipher_txt), (BYTE *)psz_un_base64_txt, ulLenOut);
iLenDecOut = ulLenOut;
if (0 != des3_DecryptBuffer(key, iv, (BYTE*)psz_un_base64_txt, (BYTE*)psz_plain_txt, ulLenOut, &iLenDecOut)) {
break;
}
psz_plain_txt[iLenDecOut] = '\0';
g_str_txt_was_decrypt = psz_plain_txt;
}
while(0);
if (NULL != psz_un_base64_txt) {
delete[] psz_un_base64_txt;
psz_un_base64_txt = NULL;
}
if (NULL != psz_plain_txt) {
delete[] psz_plain_txt;
psz_plain_txt = NULL;
}
return g_str_txt_was_decrypt.c_str();
}
// LsBase64.cpp: implementation of the LsBase64 class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include "LsBase64.h"
// https://www.codeproject.com/Tips/813146/Fast-base-functions-for-encode-decode
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
using namespace std;
namespace ns_ls
{
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
unsigned int base64_encode(const unsigned char* bytes_to_encode, unsigned int in_len, unsigned char* encoded_buffer, unsigned int& out_len)
{
int i = 0;
int j = 0;
unsigned char char_array_3[3] = { 0, 0, 0 };
unsigned char char_array_4[4] = { 0, 0, 0, 0 };
out_len = 0;
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; i < 4 ; i++) {
encoded_buffer[out_len++] = base64_chars[char_array_4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
char_array_3[j] = '\0';
}
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; j < (i + 1); j++) {
encoded_buffer[out_len++] = base64_chars[char_array_4[j]];
}
while (i++ < 3) {
encoded_buffer[out_len++] = '=';
}
}
return out_len;
}
unsigned int base64_decode(const unsigned char* encoded_string, unsigned int in_len, unsigned char* decoded_buffer, unsigned int& out_len)
{
size_t i = 0;
size_t j = 0;
int in_ = 0;
unsigned char char_array_3[3] = { 0, 0, 0 };
unsigned char char_array_4[4] = { 0, 0, 0, 0 };
out_len = 0;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_];
in_++;
if (i == 4) {
for (i = 0; i < 4; i++) {
char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; i < 3; i++) {
decoded_buffer[out_len++] = char_array_3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
char_array_4[j] = 0;
}
for (j = 0; j < 4; j++) {
char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) {
decoded_buffer[out_len++] = char_array_3[j];
}
}
return out_len;
}
}