sm4

            唔,美国压制得越狠,各种替代产品就越能活。

            本文分享SM4的一种快速实现与集成方式。

              

            SM4(原名SMS4)是中华人民共和国政府采用的一种分组密码标准,由国家密码管理局于2012年3月21日发布[1][2],相关标准为“GM/T 0002-2012《SM4分组密码算法》(原SMS4分组密码算法)”[1]。2016年8月,成为中国国家密码标准(GB/T 32907-2016)[3]。在商用密码体系中,SM4主要用于数据加密,其算法公开,分组长度与密钥长度均为128bit,加密算法密钥扩展算法都采用32轮非线性迭代结构,S盒为固定的8比特输入8比特输出[4][5]。  

             是密码界大神wangxiaoyun(让美国安全部颤抖的人)提供的。

             主要对标的是对称加密里面的DES与AES。

             安全性被大机构验证过,是目前最好的国产对称加密方案。

 

             废话不多话了,既然搜到了本文,肯定是想着怎么实现的。

 

             首先,GMSSL 应该是实现最完整的,啥算法基本都有,但比较大。简单得了解下放弃了。感兴趣的可以自己看http://gmssl.org/

             本人对Crypto++之前有接触,使用过他提供的AES加密方式,搜了下官网,发现从6.0开始便支持SM4了,太好了。

             https://www.cryptopp.com/docs/ref/class_s_m4.html#details  这里可以查看详细信息。

 

             本人下载了最新版本8.2的。解压之后打开VS2013,对lib库进行编译,会有个cryptlib.lib的输出。我们只需要这个库即可。

  

                                                                               

 

           我添加了下测试项目SM4-Cipher进行测试与验证。 验证是通过在线平台看看是否一致。http://aks.jd.com/tools/sec/

           直接上代码了。

           

#include <windows.h>
#include <string>
#include <iostream>
#include "modes.h"
#include "filters.h"
#include "base64.h"
#include "aes.h"
#include "sm4.h"
using namespace std;
using namespace CryptoPP;

std::string base64_encrypt(const std::string& src)
{
    //byte decoded[] = { 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
    std::string encoded;

    try
    {
        StringSource ss(src, true,
            new Base64Encoder(
            new StringSink(encoded), false
            ) // Base64Encoder
            ); // StringSource

        return encoded;
    }
    catch (...)
    {
    }

    return encoded;
}

std::string base64_decrypt(const std::string& src)
{
    std::string decoded;

    try
    {
        StringSource ss(src, true,
            new Base64Decoder(
            new StringSink(decoded)
            ) // Base64Decoder
            ); // StringSource

        return decoded;
    }
    catch (...)
    {

    }
    return decoded;

}
//typedef unsigned char byte;

static CryptoPP::byte AES_KEY[] = "1234567890111111";
static CryptoPP::byte AES_IV[] = "1234567890111111";

//这里封装下sm4 加密算法加解密
bool sm4_encrypt(void* data, size_t size, std::string& out)
{
    out.clear();
    try
    {
        ECB_Mode<SM4>::Encryption cbcEncryption(AES_KEY, 16);
        StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(out));
        stfEncryptor.Put(reinterpret_cast<const unsigned char*>(data), size);
        stfEncryptor.MessageEnd();
    }
    catch (...)
    {
        return false;
    }
    return true;
}

bool sm4_decrypt(void* data, int size, std::string& out)
{
    out.clear();
    try
    {
        ECB_Mode<SM4>::Decryption cbcDecryption(AES_KEY, 16);
        StreamTransformationFilter stfEncryptor(cbcDecryption, new CryptoPP::StringSink(out));
        stfEncryptor.Put(reinterpret_cast<const unsigned char*>(data), size);
        stfEncryptor.MessageEnd();
    }
    catch (...)
    {
        return false;
    }
    return true;
}


std::string aes_encrypt(const char* src, const int &length,std::string& out)
{
    //  
    try
    {
        AES::Encryption aesEncryption(AES_KEY, 16);
        CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, AES_IV);
        StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(out));
        stfEncryptor.Put(reinterpret_cast<const unsigned char*>(src), length);
        stfEncryptor.MessageEnd();

        return out;
    }
    catch (...)
    {
    }
    return out;
}

std::string aes_decrypt(const char* src, const int &length, std::string& out)
{
    try
    {
        AES::Decryption aesDecryption(AES_KEY, 16);
        CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, AES_IV);
        StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(out));
        stfDecryptor.Put(reinterpret_cast<const unsigned char*>(src), length);
        stfDecryptor.MessageEnd();

        return out;
    }
    catch (...)
    {
    }
    return out;
}

 

           以上简单得实现了 base64、aes、sm4。并测试可行。

 

           大文件加密需要分块,注意无论是aes还是sm4,都有16字节对齐的概念。而且若块大小不是16字节倍数,补齐为16字节倍数。如果块大小是16字节倍数,则再添加16字节。

           切记。

          

void testfile()
{
    const DWORD Two_G = 2 << 30;
    string path;
    int type = 0;
    int encrypt = 1;
    char name[6] = { 0 };
    name[0] = \'0\';
    name[1] = \'.\';
    name[2] = \'t\';
    name[3] = \'m\';
    name[4] = \'p\';
    cout << "----------本程序基于crypto++库开发------------\n";
    while (true)
    {
        cout << "请输入您要加解密的文件\n";
        cin >> path;
        if (GetFileAttributesA(path.c_str()) == INVALID_FILE_ATTRIBUTES)
        {
            cout << "文件输入错误\n";
            continue;
        }

        cout << "请输入您要加解密文件的种类\n 1:SM4 2:AES-256 \n";
        cin >> type;
        if (type < 1 || type>2)
        {
            cout << "暂不支持的类型\n";
            continue;
        }


        cout << "您要加密还是解密?\n 1:加密 0:解密 \n";
        cin >> encrypt;
        if (encrypt != 0 && encrypt != 1)
        {
            cout << "输入有误\n";
            continue;
        }

        string path2 = "D:\\";
        path2.append(name);

        cout << "加解密文件会输出到" << path2 << endl;
        cout << "开始加解密操作了,从打开文件开始到生成全部临时文件结束,请耐心等待\n";
        DWORD begin = GetTickCount();



        HANDLE hFile = CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            cout << "打开文件出错\n";
            continue;
        }
        DWORD dwBytesToRead = GetFileSize(hFile, NULL);
        if (dwBytesToRead > Two_G)
        {
            cout << "文件太大啦\n";
            CloseHandle(hFile);
            continue;
        }
        if (GetFileAttributesA(path2.c_str()) != INVALID_FILE_ATTRIBUTES)
        {
            DeleteFileA(path2.c_str());
        }
        HANDLE hFile2 = CreateFileA(path2.c_str(), GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
        if (hFile2 == INVALID_HANDLE_VALUE)
        {
            CloseHandle(hFile);
            cout << "打开文件出错\n";
            continue;
        }
        //一次加密1M吧 1M = 16B * 65536
        //一次解密需要多16字节。
        DWORD size_once = 1024 * 1024;
        if (encrypt == 0)
        {
            size_once += 16;
        }
        char * pbuff = (char*)malloc(size_once);
        //string 的大小已经在堆里了
        DWORD readbyte;
        do{                                       //循环读文件,确保读出完整的文件    
            ReadFile(hFile, pbuff, size_once, &readbyte, NULL);
            if (readbyte==0)
                break;
            dwBytesToRead -= readbyte;

            //注意,本次只有readbyte可以用
            std::string out;
            if (encrypt == 1)
            {
                if (type == 1)
                {
                    sm4_encrypt(pbuff, readbyte, out);
                }
                else
                {
                    aes_encode(pbuff, readbyte, out);
                }

            }
            else
            {
                if (type == 1){
                    sm4_decrypt(pbuff, readbyte, out);
                }
                else
                {
                    aes_decode(pbuff, readbyte, out);
                }

            }

            //out写入文件
            DWORD dwBytesToWrite = out.size();
            DWORD dwBytesWrite = 0;
            const char * tmpBuf = out.c_str();
            do{                                       //循环写文件,确保完整的文件被写入  
                WriteFile(hFile2, tmpBuf, dwBytesToWrite, &dwBytesWrite, NULL);
                dwBytesToWrite -= dwBytesWrite;
                tmpBuf += dwBytesWrite;

            } while (dwBytesToWrite > 0);


        } while (dwBytesToRead > 0);
        free(pbuff);
        CloseHandle(hFile);
        CloseHandle(hFile2);
        DWORD end = GetTickCount();
        cout << "本次加密结束,使用的时间为" << end - begin << "毫秒\n";

        name[0]++;
    }
}

 

版权声明:本文为xuhuajie原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xuhuajie/p/11950829.html