代码改变世界

[Windows编程笔记]AES加解密

2021-09-25 10:21  rnss  阅读(1412)  评论(0编辑  收藏  举报

AES高级加密标准为最常见的对称加密算法,所谓对称加密算法也就是加密和解密使用相同密钥的加密算法。AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。AES对称加密算法的优势在于算法公开,计算量小,加密效率高。

AES加密函数

// AES加密
BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
    BOOL bRet = TRUE;
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;
    HCRYPTKEY hCryptKey = NULL;
​
    do
    {
        // 获取CSP句柄
        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
        if (FALSE == bRet)
        {
            ShowError("CryptAcquireContext");
            break;
        }
​
        // 创建HASH对象
        bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
        if (FALSE == bRet)
        {
            ShowError("CryptCreateHash");
            break;
        }
​
        // 对密钥进行HASH计算
        bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
        if (FALSE == bRet)
        {
            ShowError("CryptHashData");
            break;
        }
​
        // 使用HASH来生成密钥
        bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
        if (FALSE == bRet)
        {
            ShowError("CryptDeriveKey");
            break;
        }
​
        // 加密数据
        bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
        if (FALSE == bRet)
        {
            ShowError("CryptEncrypt");
            break;
        }
​
    } while (FALSE);
​
    // 关闭释放
    if (hCryptKey)
    {
        ::CryptDestroyKey(hCryptKey);
    }
    if (hCryptHash)
    {
        ::CryptDestroyHash(hCryptHash);
    }
    if (hCryptProv)
    {
        ::CryptReleaseContext(hCryptProv, 0);
    }
​
    return bRet;
}

 

流程如下:

首先用CryptAcquireContext函数获取CSP句柄

然后用CryptCreateHash函数创建HASH对象,HASH算法设置为CALG_MD5

然后用CryptHashData函数计算用户传入数据的MD5值

然后使用CryptDeriveKey函数来派生密钥

// 用于派生密钥
BOOL CryptDeriveKey(
  HCRYPTPROV hProv,  // CryptAcquireContext创建的CSP句柄
  ALG_ID     Algid,  // 标识加密算法,CALG_AES_128表示128位AES对称加密算法
  HCRYPTHASH hBaseData,  // CryptCreateHash创建的HASH对象的句柄
  DWORD      dwFlags,  // 指定生成密钥的类型,CRYPT_EXPORTABLE表示可以用CryptExportKey函数导出
  HCRYPTKEY  *phKey  // 新生成密钥的句柄地址
);

 

然后用CryptEncrypt函数来加密数据

// 用来加密数据
BOOL CryptEncrypt(
  HCRYPTKEY  hKey,  // CryptDeriveKey函数生成的密钥
  HCRYPTHASH hHash,  // NULL
  BOOL       Final,  // TRUE
  DWORD      dwFlags,  // 0
  BYTE       *pbData,  // 要加密的明文,该缓冲区中的纯文本会被密文覆盖
  DWORD      *pdwDataLen,  // 入口处是要加密的明文长度,退出时是写入到pbData中的密文长度
  DWORD      dwBufLen  // 指定输入pbData缓冲区的总大小,注意:密文长度可能大于明文长度
);

 

然后用CryptDestroyKey函数释放密钥句柄

然后用CryptDestroyHash函数释放HASH对象句柄

最后用CryptReleaseContext函数释放CSP句柄

AES解密函数

// AES解密
BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
    BOOL bRet = TRUE;
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;
    HCRYPTKEY hCryptKey = NULL;
​
    do
    {
        // 获取CSP句柄
        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
        if (FALSE == bRet)
        {
            ShowError("CryptAcquireContext");
            break;
        }
​
        // 创建HASH对象
        bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
        if (FALSE == bRet)
        {
            ShowError("CryptCreateHash");
            break;
        }
​
        // 对密钥进行HASH计算
        bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
        if (FALSE == bRet)
        {
            ShowError("CryptHashData");
            break;
        }
​
        // 使用HASH来生成密钥
        bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
        if (FALSE == bRet)
        {
            ShowError("CryptDeriveKey");
            break;
        }
​
        // 解密数据
        bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
        if (FALSE == bRet)
        {
            ShowError("CryptDecrypt");
            break;
        }
​
    } while (FALSE);
​
    // 关闭释放
    if (hCryptKey)
    {
        ::CryptDestroyKey(hCryptKey);
    }
    if (hCryptHash)
    {
        ::CryptDestroyHash(hCryptHash);
    }
    if (hCryptProv)
    {
        ::CryptReleaseContext(hCryptProv, 0);
    }
​
    return bRet;
}

 

流程如下,前四步与后三步与加密相同,只是将加密函数改为了解密函数:

首先用CryptAcquireContext函数获取CSP句柄

然后用CryptCreateHash函数创建HASH对象,HASH算法设置为CALG_MD5

然后用CryptHashData函数计算用户传入数据的MD5值

然后使用CryptDeriveKey函数来派生密钥

然后用CryptDecrypt函数来解密数据

// 用来解密数据
BOOL CryptDecrypt(
  HCRYPTKEY  hKey,  // 密钥句柄
  HCRYPTHASH hHash,  // NULL
  BOOL       Final,  // TRUE
  DWORD      dwFlags,  // 0
  BYTE       *pbData,  // 要解密的数据,解密完成后,明文会放回到该缓冲区
  DWORD      *pdwDataLen  // 调用此函数前,表示密文长度,返回时,表示明文长度
);

 

然后用CryptDestroyKey函数释放密钥句柄

然后用CryptDestroyHash函数释放HASH对象句柄

最后用CryptReleaseContext函数释放CSP句柄

完整代码

// CryptoApi_Aes_Test.cpp : 定义控制台应用程序的入口点。
//
​
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
​
​
void ShowError(char *pszText)
{
    char szErr[MAX_PATH] = { 0 };
    ::wsprintf(szErr, "%s Error[0x%x]\n", pszText, ::GetLastError());
#ifdef _DEBUG
    ::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
#endif
}
​
​
// AES加密
BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
    BOOL bRet = TRUE;
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;
    HCRYPTKEY hCryptKey = NULL;
​
    do
    {
        // 获取CSP句柄
        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
        if (FALSE == bRet)
        {
            ShowError("CryptAcquireContext");
            break;
        }
​
        // 创建HASH对象
        bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
        if (FALSE == bRet)
        {
            ShowError("CryptCreateHash");
            break;
        }
​
        // 对密钥进行HASH计算
        bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
        if (FALSE == bRet)
        {
            ShowError("CryptHashData");
            break;
        }
​
        // 使用HASH来生成密钥
        bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
        if (FALSE == bRet)
        {
            ShowError("CryptDeriveKey");
            break;
        }
​
        // 加密数据
        bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);
        if (FALSE == bRet)
        {
            ShowError("CryptEncrypt");
            break;
        }
​
    } while (FALSE);
​
    // 关闭释放
    if (hCryptKey)
    {
        ::CryptDestroyKey(hCryptKey);
    }
    if (hCryptHash)
    {
        ::CryptDestroyHash(hCryptHash);
    }
    if (hCryptProv)
    {
        ::CryptReleaseContext(hCryptProv, 0);
    }
​
    return bRet;
}
​
​
// AES解密
BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{
    BOOL bRet = TRUE;
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTHASH hCryptHash = NULL;
    HCRYPTKEY hCryptKey = NULL;
​
    do
    {
        // 获取CSP句柄
        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);
        if (FALSE == bRet)
        {
            ShowError("CryptAcquireContext");
            break;
        }
​
        // 创建HASH对象
        bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);
        if (FALSE == bRet)
        {
            ShowError("CryptCreateHash");
            break;
        }
​
// 对密钥进行HASH计算
bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);
if (FALSE == bRet)
    {
ShowError("CryptHashData");
break;
    }
​
// 使用HASH来生成密钥
bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);
if (FALSE == bRet)
    {
ShowError("CryptDeriveKey");
break;
    }
​
// 解密数据
bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);
if (FALSE == bRet)
    {
ShowError("CryptDecrypt");
break;
    }
​
    } while (FALSE);
​
// 关闭释放
if (hCryptKey)
    {
    ::CryptDestroyKey(hCryptKey);
    }
if (hCryptHash)
    {
    ::CryptDestroyHash(hCryptHash);
    }
if (hCryptProv)
    {
    ::CryptReleaseContext(hCryptProv, 0);
    }
​
return bRet;
}
​
​
int _tmain(int argc, _TCHAR* argv[])
{
BYTE pData[MAX_PATH] = { 0 };
DWORD dwDataLength = 0, dwBufferLength = MAX_PATH;
DWORD i = 0;
​
    ::RtlZeroMemory(pData, dwBufferLength);
    ::lstrcpy((char *)pData, "What is your name? DemonGan");
dwDataLength = 1 + ::lstrlen((char *)pData);
​
printf("Text[%d]\n", dwDataLength);
for (i = 0; i < dwDataLength; i++)
    {
printf("%x ", pData[i]);
    }
printf("\n\n");
​
// AES 加密
AesEncrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength);
printf("AES Encrypt[%d]\n", dwDataLength);
for (i = 0; i < dwDataLength; i++)
    {
printf("%x ", pData[i]);
    }
printf("\n\n");
​
// AES 解密
AesDecrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength);
printf("AES Decrypt[%d]\n", dwDataLength);
for (i = 0; i < dwDataLength; i++)
    {
printf("%x ", pData[i]);
    }
printf("\n\n");
​
system("pause");
return 0;
}

 

 

 

小结

在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥长度可以使用128位、192位或256位。因此,在本程序中,密钥为16字节(128位)。

任何程序在使用CryptoAPI函数来进行数据计算或是加/解密之前,都需要先调用CryptAcquireContext函数来获取加密服务提供程序所需的CSP句柄。

本节的程序并不直接使用明文密钥作为AES的加密密码,而是把明文密钥的MD5值作为基础密钥通过调用CryptDeriveKey函数来派生出AES的加密密钥。

完成派生密钥后,可以调用CryptEncrypt函数来根据派生密钥中指定的加密算法进行加密运算。将第三个参数Final置为TRUE,它表示该加密是AES加密数据中的最后一组数据,这样系统会自动按照分组长度对数据进行填充并计算。其中,一定要确保数据缓冲区足够大,能够满足加密数据的存放,否则程序会出错。

在获取解密密钥后,可以直接调用CryptDecrypt函数来对密文进行解密操作。由于在加密的时候,将第三个参数Final置为TRUE来加密数据,使用在解密的时候,也要对应把Final置为TRUE来解密密文。Final置为TRUE表示该数据是AES解密数据中的最后一组数据,这样系统会自动按照分组长度对数据进行填充并计算。