字符串加解密代码

时间:2024-04-16 11:07:39

今天把很久以前使用的字符串简单加解密代码仔细检查了一遍,发现个别问题,现在已经整理好了,该算法的特点如下:

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 == 0

    int 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