zoukankan      html  css  js  c++  java
  • [Windows编程笔记]AES加解密

    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]
    ", 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]
    ", dwDataLength);
    for (i = 0; i < dwDataLength; i++)
        {
    printf("%x ", pData[i]);
        }
    printf("
    
    ");
    ​
    // AES 加密
    AesEncrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength);
    printf("AES Encrypt[%d]
    ", dwDataLength);
    for (i = 0; i < dwDataLength; i++)
        {
    printf("%x ", pData[i]);
        }
    printf("
    
    ");
    ​
    // AES 解密
    AesDecrypt((BYTE *)"DemonGanDemonGan", 16, pData, dwDataLength, dwBufferLength);
    printf("AES Decrypt[%d]
    ", dwDataLength);
    for (i = 0; i < dwDataLength; i++)
        {
    printf("%x ", pData[i]);
        }
    printf("
    
    ");
    ​
    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解密数据中的最后一组数据,这样系统会自动按照分组长度对数据进行填充并计算。

     

  • 相关阅读:
    MySQL基准测试--innodb_buffer_pool_instances
    MySQL参数优化:back_log
    MySQL open_files_limit相关设置
    Django权限系统auth模块详解
    2.9 go mod 之本地仓库搭建
    my40_MySQL锁概述之意向锁
    my39_InnoDB锁机制之Gap Lock、Next-Key Lock、Record Lock解析
    2.8 GO 参数传递
    my38_MySQL事务知识点零记
    my37_MGR流控对数据库性能的影响以及MGR与主从的性能对比
  • 原文地址:https://www.cnblogs.com/rnss/p/15333629.html
Copyright © 2011-2022 走看看