今天把很久以前使用的字符串简单加解密代码仔细检查了一遍,发现个别问题,现在已经整理好了,该算法的特点如下:
1、采用逐个字符移位变换、相邻三个字符按位错位的对称加密算法
2、支持对汉字和不可见字符等各种字符的加密,可以说是对任意字符组成的串都可加密
3、支持源字符串中包含多个’\0’零字符,这样就可以把简单字符串的加密扩展到XML内容、文件内容的加解密上。
4、密文采用Base64编码,由大写字母、小写字符、数字、下划线、小数点组成,共64个不同的可见字符
由于算法内部是针对字节进行加密的,所以将函数参数定为char*,对于UNICODE字符串可先转换为ANSI串再调用。当然,如果程序是老式的ANSI串编码,例如VC++6.0默认产生的程序,则不需要进UNICODE转换。
下面是实际项目中的调用举例:
--------读取密码----------
// 读取出已加密的密码文字
std::wstring password(secOption->GetString(L"password"));// 转换为ANSI串后再解密
m_password = std::a2w(StringDecrypt(std::w2a(password).c_str())).c_str();
---------保存密码--------------
// 由于每次加密都会生成随机的密文文字,故仅在输入密码后才保存
if (m_bPasswordChanged)
{
std::wstring password(towstr(m_password));// 将明文密码转换为ANSI串,然后加密
password = std::a2w(StringEncrypt(std::w2a(password).c_str()));// 保存经过加密的密码到文件中
secOption->SetString(L"password", password.c_str());m_bPasswordChanged = FALSE;
}
附源码:
StrEncrypt.h
//! \file StrEncrypt.h
//! \brief 定义字符串加解密函数 StringEncrypt/StringDecrypt
#pragma once
//! 文字串加密
/*!
\ingroup _GROUP_UTILFUNC
\param srcstr 明文串,支持汉字等各种字符,可包含多个0字符,
对于UNICODE串要先转换为ANSI串
\param srclen 明文串的字符数,默认取strlen(srcstr),
如果串中包含多个0字符时可指定实际字符数
\return 生成的加密串,由字母、数字、'_'、'.'组成,
长度为4的倍数,至少为4,外加0结束符,len= (3 + len(srcstr))/3*4
*/
std::string StringEncrypt(const char* srcstr, int srclen = -1);
//! 字符串解密
/*!
\ingroup _GROUP_UTILFUNC
\param srcstr 已加密的串,对于UNICODE串要先转换为ANSI串,字符数应为4的倍数且至少为4
\return 生成的明文串,长度为 len(srcstr))/4*3。\n
如果加密时的明文中包含了多个0字符,则此处得到的串中也有这些0字符,可遍历得到全部字符。
*/
std::string StringDecrypt(const char* srcstr);
----------------------------------------------------------------
StrEncrypt.cpp:
#include "stdafx.h"
#include "StrEncrypt.h"
// 将一个索引数(0到63)转换为字符(字母、数字、'_'、'.')
static char UnIndex64(BYTE nIndex)
{
char ch;
nIndex %= 64; // 取到[0,64)范围内
if (nIndex < 26) // [0,26)返回大写字母
{
ch = (char)('A' + nIndex);
}
else if (nIndex < 52) // 26+[0,26)返回小写字母
{
ch = (char)('a' + nIndex - 26);
}
else if (nIndex < 62) // 52+[0,10)返回数字
{
ch = (char)('0' + nIndex - 52);
}
else if (nIndex == 62) // 62返回'_'
{
ch = '_';
}
else if (nIndex == 63) // 63返回'.'
{
ch = '.';
}
else
{
ch = 'A';
}return ch;
}
// Index64: 将一个字符(字母、数字、'_'、'.')转换为索引数(0到63)
static BYTE Index64(char ch)
{
BYTE nIndex;
if (_istupper(ch))
{
nIndex = ch - 'A';
}
else if (_istlower(ch))
{
nIndex = 26 + ch - 'a';
}
else if (_istdigit(ch))
{
nIndex = 52 + ch - '0';
}
else if (ch == '_')
{
nIndex = 62;
}
else if (ch == '.')
{
nIndex = 63;
}
else
{
nIndex = 0;
}return nIndex;
}
// ToBase64: 将一个索引字符串转换为由字符(字母、数字、'_'、'.')组成的字符串
// instr 索引字符串,长度为len,为3的倍数
// len instr的字符个数,为3的倍数
// outstr 填充由字符(字母、数字、'_'、'.')组成的字符串,长度为4的倍数
static void ToBase64(const char* instr, int len, char* outstr)
{
ASSERT(instr && len > 0 && len % 3 == 0);
int i, j;
BYTE ch1, ch2, ch3;i = 0;
j = 0;
while (i + 2 < len)
{
ch1 = (BYTE)instr[i];
ch2 = (BYTE)instr[i + 1];
ch3 = (BYTE)instr[i + 2];outstr[j] = UnIndex64(ch1 >> 2);
outstr[j + 1] = UnIndex64(((ch1 & 0x3) << 4) | (ch2 >> 4));
outstr[j + 2] = UnIndex64(((ch2 & 0x0f) << 2) | (ch3 >> 6));
outstr[j + 3] = UnIndex64(ch3 & 0x3f);i += 3;
j += 4;
}
outstr[j] = '\0';
}
// UnBase64: 将一个由字符(字母、数字、'_'、'.')组成的字符串转换为索引字符串
// instr 由字符(字母、数字、'_'、'.')组成的字符串,长度为len,为4的倍数
// len instr的字符个数,为4的倍数
// outstr 索引字符串,长度为3的倍数
static void UnBase64(const char* instr, int len, char* outstr)
{
ASSERT(instr && len % 4 == 0);
int i, j;
BYTE ch1, ch2, ch3, ch4;i = 0;
j = 0;
while (i + 3 < len)
{
ch1 = Index64(instr[i]);
ch2 = Index64(instr[i + 1]);
ch3 = Index64(instr[i + 2]);
ch4 = Index64(instr[i + 3]);outstr[j] = (ch1 << 2) | ((ch2 >> 4) & 0x3);
outstr[j + 1] = (ch2 << 4) | ((ch3 >> 2) & 0xf);
outstr[j + 2] = (ch3 << 6) | ch4;i += 4;
j += 3;
}
outstr[j] = '\0';
}
// EncryptChar: 对一个字符进行移位变换:最高位不变,其余7位对调(1-7,2-6,3-5)
static char EncryptChar(char c)
{
BYTE x = 0;
x += (c & 0x80);
x += (c & 0x40) >> 6;
x += (c & 0x20) >> 4;
x += (c & 0x10) >> 2;
x += (c & 0x08);// << 3;
x += (c & 0x04) << 2;
x += (c & 0x02) << 4;
x += (c & 0x01) << 6;return x;
}
static inline char randchar()
{
char c = (char)rand();
return 0 == c ? '\xAA' : c;
}
// StringEncrypt: 加密文字串,输出的串的长度为(3 + len(srcstr))/3*4
std::string StringEncrypt(const char* srcstr, int srclen)
{
if (srclen < 0)
{
srclen = (NULL == srcstr) ? 0 : strlen(srcstr);
}
int inlen = (1 + srclen + 2) + 1;
int outlen = (3 + srclen) / 3 * 4 + 1;char *buf = new char[inlen + outlen];
char *inbuf = buf;
char *outbuf = buf + inlen;memset(buf, 0, sizeof(char) * (inlen + outlen));
// 复制源串到(inbuf+1),每个字符进行移位变换
for (int i = 0; i < srclen; i++)
{
inbuf[i + 1] = EncryptChar(srcstr[i]);
}// 设置长度标记字符 inbuf[0]
// 移位变换可能产生0字符,用最低两位代表inbuf的长度, 00:3n, 01:3n+1,10:3n+2
//
srand( (unsigned)time(NULL) );
inbuf[0] = randchar() & (~0x03); // 最低两位为0时,leadlen % 3 == 0int actlen = srclen + 1;
if (actlen % 3 == 1) // 原长3n+1,补两个随机字符保证inbuf长度为3n
{
inbuf[0] |= 0x01;
inbuf[actlen] = randchar();
inbuf[actlen + 1] = randchar();
actlen += 2;
}
else if (actlen % 3 == 2) // 原长3n+2,补1个随机字符保证inbuf长度为3n
{
inbuf[0] |= 0x02;
inbuf[actlen] = randchar();
actlen++;
}// 从inbuf转换出outbuf,outbuf由字母、数字、'_'、'.'组成,长度为4的倍数
ToBase64(inbuf, actlen, outbuf);std::string strResult(outbuf);
delete[] buf;
return strResult;
}
// StringDecrypt: 解密文字串,输出的串的长度为 len(srcstr))/4*3
std::string StringDecrypt(const char* srcstr)
{
int srclen = (NULL == srcstr) ? 0 : strlen(srcstr) / 4 * 4;
int len = srclen * 3 / 4;
if (0 == len)
{
return "";
}char *chBuf = new char[len + 1];
UnBase64(srcstr, srclen, chBuf);
if (1 == (chBuf[0] & 0x03))
len -= 2;
else if (2 == (chBuf[0] & 0x03))
len--;
chBuf[len] = 0;for (int i = 1; i < len; i++)
{
chBuf[i] = EncryptChar(chBuf[i]);
}std::string strResult(chBuf + 1, len - 1);
delete[] chBuf;
return strResult;
}
--------------------------------------------------------
Unicode和Ansi串的转换:
//! \file ConvStr.h
//! \brief 定义UNICODE串和ANSI串的相互转化函数
#pragma once
_STD_BEGIN
//! UNICODE串转换为ANSI串, std::w2a
/*!
\ingroup _GROUP_UTILFUNC
*/
inline std::string w2a(LPCWSTR s)
{
std::string str;
int wlen = (NULL == s) ? 0 : (int)wcslen(s);
if (wlen > 0)
{
long len = WideCharToMultiByte(CP_ACP, 0, s, wlen, NULL, 0, NULL, NULL);
str.resize(len);
WideCharToMultiByte(CP_ACP, 0, s, wlen,
const_cast<char*>(str.data()), len, NULL, NULL);
}return str;
}
//! UNICODE串转换为ANSI串, std::w2a
/*!
\ingroup _GROUP_UTILFUNC
*/
inline std::string w2a(const std::wstring& s)
{
return w2a(s.c_str());
}
//! ANSI串转换为UNICODE串, std::a2w
/*!
\ingroup _GROUP_UTILFUNC
*/
inline std::wstring a2w(LPCSTR s)
{
std::wstring wstr;
int len = (NULL == s) ? 0 : (int)strlen(s);
if (len > 0)
{
int wlen = MultiByteToWideChar(CP_ACP, 0, s, len, NULL, 0);
wstr.resize(wlen);
MultiByteToWideChar(CP_ACP, 0, s, len,
const_cast<LPWSTR>(wstr.data()), wlen);
}return wstr;
}
//! ANSI串转换为UNICODE串, std::a2w
/*!
\ingroup _GROUP_UTILFUNC
*/
inline std::wstring a2w(const std::string& s)
{
return a2w(s.c_str());
}
#ifdef _UNICODE
inline std::wstring w2t(LPCWSTR s) { return s; }
inline std::wstring w2t(const std::wstring& s) { return s; }
inline std::wstring t2w(LPCTSTR s) { return s; }
#else
inline std::string w2t(LPCWSTR s) { return w2a(s); }
inline std::string w2t(const std::wstring& s) { return w2a(s); }
inline std::wstring t2w(LPCTSTR s) { return a2w(s); }
#endif
_STD_END