用 Windows CrptoAPI 实现 3DE 加密解密,并且与JAVA对接

这几日工作需要研究 windows 的 CrptoAPI 实现3DES加密解密,着实耗费了一番功夫

除了windows CrptoAPI之外还是openssl可以实现3des比较便捷,可是需要配置,于是我选择使用的是windows的API

我们实现的简单的ECB模式 补码模式选用PKCS#7

3DES 对应的是 24位秘钥

windows提供了自己生成 Session key 方法,利用自己的 password ,经过hash,derive等方法生成

但是我们是已经拥有了key,并不需要自己生成

所以需要自己构建

struct keyBlob{
		BLOBHEADER hdr;
		DWORD cbKeySize;
		BYTE rgbKeyData[24];
	}keyBlob;	

这个结构体是为了 CryptImportKey 使用,需要满足windows内部的格式才如此麻烦

下面是初始化

	keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    	keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
   	keyBlob.hdr.reserved = 0;
	keyBlob.hdr.aiKeyAlg = CALG_3DES;
	keyBlob.cbKeySize = 24;

	DWORD dwLength = key.length();
    	for(int i = 0; i < 24; ++i)
    	{
		keyBlob.rgbKeyData[i] = (BYTE)key[i];
   	}


注意 keyBlob.hdr.aiKeyAlg = CALG_3DES 这句话, 后面对应的 CALG_3DES 就是对应你选的算法。

rgbKeyData 就是你所使用的key


HCRYPTPROV hProv;
 CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)
这是初始化你的秘钥池,是为了Import key,import的时候需要一个池子。
如果你想知道各个参数的意义,上MSDN吧


接下去就是 Imoprt Key了;

HCRYPTKEY hKey;
CryptImportKey(hProv, (BYTE*)(&keyBlob), sizeof(keyBlob), 0, 0, &hKey)


接下去就是导入明文了,一开始padding模式我不知道如何设置,padding是指
当明文不满八位分组的时候需要对明文进行补位处理
这里我是自己添加的,因为我没有找到windows 的api如何设置padding 模式
为了对应 JAVA的 PKCS#7 八位分组
也就是当 缺7位补7位0x7,6为补6位0x6,以此类推,到1位的时候补1位0x1
但是当不缺位的时候能 你就要补一组0x8 
附上导入明文 和 补位的代码

DWORD dwLen = text.length();
BYTE *pCryptData = new BYTE[dwLen*2];
for(int i = 0;i < dwLen; i++)
{
	pCryptData[i] = (BYTE)text[i];
}

errJiaoYan = dwLen % 8;
errJiaoYan = 8 - errJiaoYan;

for(int i = 0;i < errJiaoYan;i++)
{
	pCryptData[i+dwLen] = errJiaoYan;
}

dwLen = dwLen +  errJiaoYan; 

做好了所有的准备工作,就做到最重要的一步了!加密
if(!CryptEncrypt(hKey, 0, FALSE, 0, pCryptData, &dwLen, dwLen*2)){
				int err = GetLastError();
			}

第一个参数顾名思义,就是key, key里保存了你的密钥值,第二个是 hash什么的,我倒是没有用到,第三个 是看你是否加密的数据是不是最后一组,第四个是一个dwFlag,我也没有用过,后面的三个都比较重要:
pCryptData就是传入的明文值,加密之后的密文也是通过这个指针出来
dwLen 就是你传入明文的长度,一开始我自己补位之后发现还是不正确,就是因为这个值取错
             另外你加密完之后的长度也是通过这个指针传出来。
最后个 dwLen*2 就是你允许密文的最大长度,一般都取的稍微大一点


最后你就完成了加密,但是此时加密的结果是一堆2进制数据,你必须通过Base64Encode一下

LPCSTR result = Base64Encode((LPCBYTE)pCryptData,dwLen);

最后记得 释放 hKey,hProv

CryptDestroyKey(hKey);
delete pCryptData;
CryptReleaseContext(hProv, 0);

加密与解密相同,只是把函数改成
CryptDecrypt(hKey, 0, FALSE, 0, pDecrypt, &dwLen)
默认 解密之后的数组长度比密文短,所以比加密少了个参数,另外
解密之后你会保留补码,我找不到好的措施,只能过滤。
考虑到补的位数的值只有0x1~9,随意扫描一遍,但是可能造成原明文的丢失,但是原来明文内char 1-9的概率也应该是极低 但也是个隐患
另外 解密之前 需要对密文Base64 decode 
好了 话不多说!回馈社会!贴上源码!


#include
#include
#include

using namespace std;

#pragma comment(lib, "crypt32.lib")



LPTSTR Base64Decode(LPCTSTR lpData, DWORD &dwSize)
{
	DWORD dwResult = 0;
	if(CryptStringToBinary(lpData, dwSize, CRYPT_STRING_BASE64, NULL, &dwResult,NULL,NULL))
	{
		LPTSTR lpszBase64Decoded = new TCHAR[dwResult+(sizeof(TCHAR) * 2)];
		memset(lpszBase64Decoded,0,dwResult);
		if(CryptStringToBinary(lpData, dwSize, CRYPT_STRING_BASE64,(BYTE *)lpszBase64Decoded, &dwResult,NULL,NULL))
		{
			dwSize = dwResult;
			return lpszBase64Decoded;
		}
	}
	return NULL;
}
 
LPTSTR Base64Encode(LPCBYTE lpData, DWORD dwSize)
{
	DWORD dwResult = 0;
	if(CryptBinaryToString(lpData, dwSize, CRYPT_STRING_BASE64, NULL, &dwResult))
	{
		LPTSTR lpszBase64 = new TCHAR[dwResult];
		if(CryptBinaryToString(lpData, dwSize, CRYPT_STRING_BASE64, lpszBase64, &dwResult))
		{
			TCHAR pByteLF = *(LPWORD)(lpszBase64 + dwResult -1);
			TCHAR pByteCR = *(LPWORD)(lpszBase64 + dwResult -2);
			if(pByteCR == 0x0D && pByteLF == 0x0A)
			{
				*(LPWORD)(lpszBase64 + dwResult -2) = 0;
			}
			return lpszBase64;
			
		}
	}
	return NULL;
}

BOOL Encrypt3DES(string key,string text, string& encrpted){
	int errJiaoYan = 0;

	struct keyBlob{
		BLOBHEADER hdr;
		DWORD cbKeySize;
		BYTE rgbKeyData[24];
	}keyBlob;

	keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
    keyBlob.hdr.reserved = 0;
	keyBlob.hdr.aiKeyAlg = CALG_3DES;
	keyBlob.cbKeySize = 24;

	DWORD dwLength = key.length();
    for(int i = 0; i < 24; ++i)
    {
		keyBlob.rgbKeyData[i] = (BYTE)key[i];
    }


 
    HCRYPTPROV hProv;
    if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))
    {
        HCRYPTKEY hKey;
        if (CryptImportKey(hProv, (BYTE*)(&keyBlob), sizeof(keyBlob), 0, 0, &hKey))
        {
            DWORD dwMode = CRYPT_MODE_ECB; 
            CryptSetKeyParam(hKey, KP_MODE, (BYTE*)(&dwMode), 0);


			DWORD dwLen = text.length();
			BYTE *pCryptData = new BYTE[dwLen*2];
			for(int i = 0;i < dwLen; i++)
			{
				pCryptData[i] = (BYTE)text[i];
			}

			errJiaoYan = dwLen % 8;
			errJiaoYan = 8 - errJiaoYan;

			for(int i = 0;i < errJiaoYan;i++)
			{
				pCryptData[i+dwLen] = errJiaoYan;
			}

			dwLen = dwLen +  errJiaoYan; 

			if(!CryptEncrypt(hKey, 0, FALSE, 0, pCryptData, &dwLen, dwLen*2)){
				int err = GetLastError();
			}
			

			LPCSTR result = Base64Encode((LPCBYTE)pCryptData,dwLen);

			encrpted = result;

            CryptDestroyKey(hKey);
			delete pCryptData;
        }
        CryptReleaseContext(hProv, 0);
    }
	return true;
}

BOOL Decrypt3DES(string key,string text, string &decrypted){
	struct keyBlob{
		BLOBHEADER hdr;
		DWORD cbKeySize;
		BYTE rgbKeyData[24];
	}keyBlob;

	keyBlob.hdr.bType = PLAINTEXTKEYBLOB;
    keyBlob.hdr.bVersion = CUR_BLOB_VERSION;
    keyBlob.hdr.reserved = 0;
	keyBlob.hdr.aiKeyAlg = CALG_3DES;
    keyBlob.cbKeySize = 24;

	int len = key.length();
	for(int i = 0; i < len; i++)
	{
		keyBlob.rgbKeyData[i] = (BYTE)key[i];
	}
	
 
    HCRYPTPROV hProv;
    if (CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0))
    {
        HCRYPTKEY hKey;
        if (CryptImportKey(hProv, (BYTE*)(&keyBlob), sizeof(keyBlob), 0, 0, &hKey))
        {
			
            DWORD dwMode = CRYPT_MODE_ECB;
            CryptSetKeyParam(hKey, KP_MODE, (BYTE*)(&dwMode), 0);
			
			DWORD dwLen = text.length();

			LPTSTR pDecryptData = Base64Decode(text.c_str(),dwLen);
			BYTE *pDecrypt = (BYTE*)pDecryptData;

			if(!CryptDecrypt(hKey, 0, FALSE, 0, pDecrypt, &dwLen)){
				int err = GetLastError();;
				cout< 0 && ch < 9 && dwLen > 0){
					dwLen -= 1;
					ch = 0;
				}else{
					break;
				}
			}while (true);
			
			decrypted.assign(pDecrypt, pDecrypt + dwLen);
			
            
			CryptDestroyKey(hKey);
        }
        CryptReleaseContext(hProv, 0);
    }
	return true;
}


int main(){
	string key = "BU4jWNCl9W14yqdWCCgHKg49";
	string text = "SP#000017#TjWGjNfphIchj0t9";
	string encrpt = "pE35dbhcakaxuQ7+SQzoNKUUFMtrsMBFHPHClIX+cYA=";
	string answer = " ";
	 BOOL bRes = Encrypt3DES(key,text,answer);
	
	 cout<

参考链接

http://bbs.csdn.net/topics/300048230

http://www.vckbase.com/index.php/wv/716

http://www.mythroad.net/2012/11/01/3des%E7%AE%97%E6%B3%95java%E4%B8%8Ec%E7%9A%84%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90%E4%B8%8E%E5%AE%9E%E7%8E%B0/

你可能感兴趣的:(CLOUME)