文件加密CryptEncrypt和导出密钥CryptExportKey学习笔记

刚刚接触C++,使用CryptEncrypt来对一个文件进行加密,代码都是MSDN上的,一开始使用起来比价困难,这里对代码解读和分享一下。


#include 
#include 
#include 
#include 
#include 

#pragma comment (lib, "advapi32")

#define KEYLENGTH  0x00800000   //产生密钥的长度,类型
#define ENCRYPT_ALGORITHM CALG_RC4 //生成密钥所使用的算法
#define ENCRYPT_BLOCK_SIZE 8 //每次加密数据块的最小单位长度

bool MyEncryptFile(
    LPTSTR szSource,
    LPTSTR szDestination,
    LPTSTR szPassword,
    LPTSTR szexportkey);

void MyHandleError(
    LPTSTR psz,
    int nErrorNumber);

int main()
{

    LPTSTR pszSource = (LPTSTR)L"D:\\testSecurity\\hello11.txt";//要加密的文件
    LPTSTR pszDestination = (LPTSTR)L"D:\\testSecurity\\hello22.txt";//加密后的文件
    LPTSTR pszexportkeyDestination = (LPTSTR)L"D:\\testSecurity\\exportkeyblob.txt";
    //LPTSTR pszPassword = (LPTSTR)"123456";//随便一串字符,用于加解密
    LPTSTR pszPassword = NULL;
    //---------------------------------------------------------------
    // Call EncryptFile to do the actual encryption.
    if (MyEncryptFile(pszSource, pszDestination, pszPassword, pszexportkeyDestination))
    {
        _tprintf(TEXT("Encryption of the file %s was successful. \n"), pszSource);
        _tprintf(TEXT("The encrypted data is in file %s.\n"), pszDestination);
    }
    else
    {
        MyHandleError(TEXT("Error encrypting file!\n"), GetLastError());
    }
    getchar();
    return 0;
}

bool MyEncryptFile(LPTSTR pszSourceFile, LPTSTR pszDestinationFile, LPTSTR pszPassword, LPTSTR pszexportkeyDestination)
{
    bool fReturn = false;
    HANDLE hSourceFile = INVALID_HANDLE_VALUE;
    HANDLE hDestinationFile = INVALID_HANDLE_VALUE;
    HANDLE hexportkeyDestinationFile = INVALID_HANDLE_VALUE;

    HCRYPTPROV hCryptProv = NULL;
    HCRYPTKEY hKey = NULL;
    HCRYPTKEY hXchgKey = NULL;
    HCRYPTHASH hHash = NULL;

    PBYTE pbKeyBlob = NULL;
    DWORD dwKeyBlobLen;

    PBYTE pbBuffer = NULL;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;


    hSourceFile = CreateFile(
        pszSourceFile,
        FILE_READ_DATA,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (INVALID_HANDLE_VALUE != hSourceFile)
    {
        _tprintf(TEXT("The source plaintext file, %s, is open. \n"), pszSourceFile);
    }
    else
    {
        MyHandleError(TEXT("Error opening source plaintext file!\n"), GetLastError());
        goto Exit_MyEncryptFile;
    }

    // 创建目标文件 
    hDestinationFile = CreateFile(pszDestinationFile, FILE_WRITE_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE != hDestinationFile)
    {
        _tprintf(TEXT("The destination file, %s, is open. \n"), pszDestinationFile);
    }
    else
    {
        MyHandleError(TEXT("Error opening destination file!\n"), GetLastError());
        goto Exit_MyEncryptFile;
    }
    //获取容器handle
    if (!CryptAcquireContext(
        &hCryptProv, //返回的CSP句柄,秘钥容器
        NULL,   //密钥容器的名字
        MS_ENHANCED_PROV, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块
        PROV_RSA_FULL, //这里为使用的加密策略
        0))
    {
        //
        if (GetLastError() == NTE_BAD_KEYSET)
        {
            if (!CryptAcquireContext(
                &hCryptProv,
                NULL,
                MS_ENHANCED_PROV,
                PROV_RSA_FULL,
                CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。
            {
                MyHandleError(TEXT("Error during CryptAcquireContext!\n"), GetLastError());
                goto Exit_MyEncryptFile;
                //return FALSE;
            }
        }
        else
        {
            MyHandleError(TEXT("Error during CryptAcquireContext!\n"), GetLastError());
            goto Exit_MyEncryptFile;
            //return FALSE;
        }
    }
    _tprintf(TEXT("A cryptographic provider has been acquired. \n"));

    // 创建密钥
    if (pszPassword == NULL)
    {
        // 如果pszPassword为NULL,则生成一个随机的密钥,然后再把这个随机秘钥写到文件中
        // 创建一个随机秘钥 hKey
        if (CryptGenKey( //产生一个随机的交换密钥或者公/私钥对
            hCryptProv,
            ENCRYPT_ALGORITHM,  //表明产生私钥所使用的算法或者公钥生成的算法
            KEYLENGTH | CRYPT_EXPORTABLE, //表示密钥使用的长度,参数可以为0,采用默认的密钥长度
            &hKey))
        {
            _tprintf(TEXT("A session key has been created. \n"));
        }
        else
        {
            MyHandleError(TEXT("Error during CryptGenKey. \n"), GetLastError());
            goto Exit_MyEncryptFile;
        }

        //-----------------------------------------------------------
        // Get the handle to the exchange public key. 
        //获取交换密钥句柄
        if (CryptGetUserKey(
            hCryptProv,
            AT_KEYEXCHANGE, //AT_KEYEXCHANGE(交换密钥) or AT_SIGNATURE(签名密钥)
            &hXchgKey)) //返回所获取密钥类型的句柄
        {
            _tprintf(TEXT("The user public key has been retrieved. \n"));
        }
        else
        {
            if (NTE_NO_KEY == GetLastError())
            {
                //如果没有交换密钥,可以随机生成一个
                if (!CryptGenKey(
                    hCryptProv,
                    AT_KEYEXCHANGE,
                    CRYPT_EXPORTABLE,
                    &hXchgKey))
                {
                    MyHandleError(TEXT("Could not create a user public key.\n"), GetLastError());
                    goto Exit_MyEncryptFile;
                }
            }
            else
            {
                MyHandleError(TEXT("User public key is not available and may ")TEXT("not exist.\n"), GetLastError());
                goto Exit_MyEncryptFile;
            }
        }

        //-----------------------------------------------------------
        // Determine size of the key BLOB, and allocate memory. 
        //这步导出密钥只是先获取导出key Blob的长度,并对其分配内存,
        if (CryptExportKey(
            hKey, //需要导出的密钥句柄
            hXchgKey, //将待导出密钥用交换密钥进行加密,假如是公开的BLOG当然就设置为0
            SIMPLEBLOB, // 指定导出的密钥BLOB类型,BLOB也就是一种存储结构。六个参数见MSDN
            0,
            NULL, //导出的数据指针,以后就可以将这个数据写如磁盘或者别的任务
            &dwKeyBlobLen)) //导出的数据长度
        {
            _tprintf(TEXT("The key BLOB is %d bytes long. \n"), dwKeyBlobLen);
        }
        else
        {
            MyHandleError(TEXT("Error computing BLOB length! \n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
        //未将要存储秘钥的keyBlob分配内存
        if (pbKeyBlob = (BYTE *)malloc(dwKeyBlobLen))
        {
            _tprintf(TEXT("Memory is allocated for the key BLOB. \n"));
        }
        else
        {
            MyHandleError(TEXT("Out of memory. \n"), E_OUTOFMEMORY);
            goto Exit_MyEncryptFile;
        }

        //-----------------------------------------------------------
        // Encrypt and export the session key into a simple key 
        // BLOB.
        //真正的用交换密钥将密钥加密后,存储到Blob
        if (CryptExportKey(
            hKey,
            hXchgKey,
            SIMPLEBLOB,
            0,
            pbKeyBlob,
            &dwKeyBlobLen))
        {
            _tprintf(TEXT("The key has been exported. \n"));
        }
        else
        {
            MyHandleError(TEXT("Error during CryptExportKey!\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }

        //-----------------------------------------------------------
        // Release the key exchange key handle. 
        //释放交换密钥
        if (hXchgKey)
        {
            if (!(CryptDestroyKey(hXchgKey)))
            {
                MyHandleError(
                    TEXT("Error during CryptDestroyKey.\n"),
                    GetLastError());
                goto Exit_MyEncryptFile;
            }

            hXchgKey = 0;
        }
        hexportkeyDestinationFile = CreateFile(pszexportkeyDestination, FILE_WRITE_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if (INVALID_HANDLE_VALUE != hexportkeyDestinationFile)
        {
            _tprintf(TEXT("The destination file, %s, is open. \n"), pszexportkeyDestination);
        }
        else
        {
            MyHandleError(TEXT("Error opening destination file!\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
        //先将keyblob 长度保存到文件中,然后再将keyblob写入,以后想解密还是啥的可以直接导入该keyblob即可
        if (!WriteFile(
            hexportkeyDestinationFile,
            &dwKeyBlobLen,
            sizeof(DWORD),
            &dwCount,
            NULL))
        {
            MyHandleError(TEXT("Error writing header.\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
        else
        {
            _tprintf(TEXT("A file header has been written. \n"));
        }
        //写入keyblob
        if (!WriteFile(
            hexportkeyDestinationFile,
            pbKeyBlob,
            dwKeyBlobLen,
            &dwCount,
            NULL))
        {
            MyHandleError(TEXT("Error writing header.\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
        else
        {
            _tprintf(TEXT("The key BLOB has been written to the ")TEXT("file. \n"));
        }
        // Free memory.
        free(pbKeyBlob);
    }
    else
    {
        //-----------------------------------------------------------
        // The file will be encrypted with a session key derived 
        // from a password.
        // The session key will be recreated when the file is 
        // decrypted only if the password used to create the key is 
        // available. 

        //创建一个hash对象,将password hash以后再通过CryptDeriveKey得到密钥
        if (CryptCreateHash( //初始化一个HASH对象,产生一个空的HASH对象
            hCryptProv,
            CALG_MD5, //指定的hash算法
            0,
            0,
            &hHash))
        {
            _tprintf(TEXT("A hash object has been created. \n"));
        }
        else
        {
            MyHandleError(TEXT("Error during CryptCreateHash!\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }

        // Hash password后将hash信息保存在hHash中
        if (CryptHashData( //对数据使用HASH,保存在hHash中
            hHash,
            (BYTE *)pszPassword,
            lstrlen(pszPassword),
            0))
        {
            _tprintf(TEXT("The password has been added to the hash. \n"));
        }
        else
        {
            MyHandleError(TEXT("Error during CryptHashData. \n"), GetLastError());
            goto Exit_MyEncryptFile;
        }

        //再使用已经对password的hash对象生成密钥
        if (CryptDeriveKey(  //从某一数据产生会话密钥。有点类似CryptGenKey,但是产生的会话密钥来自固定数据,而CryptGenKey是随机产生的。并且不能产生公 / 私钥对
            hCryptProv,
            ENCRYPT_ALGORITHM, //in,指定的算法,类似CryptGenKey
            hHash, //in,HASH对象的句柄
            KEYLENGTH, //in,指定产生密钥的类型
            &hKey)) //in,out产生的密钥句柄地址
        {
            _tprintf(TEXT("An encryption key is derived from the ")TEXT("password hash. \n"));
        }
        else
        {
            MyHandleError(TEXT("Error during CryptDeriveKey!\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
    }

    //---------------------------------------------------------------
    // The session key is now ready. If it is not a key derived from 
    // a  password, the session key encrypted with the private key 
    // has been written to the destination file.

    //---------------------------------------------------------------
    // Determine the number of bytes to encrypt at a time. 
    // This must be a multiple of ENCRYPT_BLOCK_SIZE.
    //每一次加密的字节长度,必须要是ENCRYPT_BLOCK_SIZE的整数倍
    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;

    //---------------------------------------------------------------
    // Determine the block size. If a block cipher is used, 
    // it must have room for an extra block. 
    if (ENCRYPT_BLOCK_SIZE > 1)
    {
        dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
    }
    else
    {
        dwBufferLen = dwBlockLen;
    }

    //---------------------------------------------------------------
    // Allocate memory. 
    if (pbBuffer = (BYTE *)malloc(dwBufferLen))
    {
        _tprintf(TEXT("Memory has been allocated for the buffer. \n"));
    }
    else
    {
        MyHandleError(TEXT("Out of memory. \n"), E_OUTOFMEMORY);
        goto Exit_MyEncryptFile;
    }

    //---------------------------------------------------------------
    // In a do loop, encrypt the source file, 
    // and write to the source file. 
    bool fEOF = FALSE;
    do
    {
        //-----------------------------------------------------------
        // Read up to dwBlockLen bytes from the source file. 
        //从要加密的文件每次读取dwBlockLen个字节进行加密
        if (!ReadFile(
            hSourceFile, //文件的句柄
            pbBuffer, //用于保存读入数据的一个缓冲区
            dwBlockLen, //要读入的字节数
            &dwCount, //指向实际读取字节数的指针
            NULL))
        {
            MyHandleError(TEXT("Error reading plaintext!\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }

        if (dwCount < dwBlockLen)
        {
            fEOF = TRUE;
        }
        // 加密数据 
        if (!CryptEncrypt(
            hKey,
            NULL,
            fEOF, //若只有一个分组的数据需要加密或者为最后一个分组,则Final为TRUE。
            0,
            pbBuffer,
            &dwCount,
            dwBufferLen))
        {
            MyHandleError(TEXT("Error during CryptEncrypt. \n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
        //将加密后的数据写入目标文件
        if (!WriteFile(
            hDestinationFile,
            pbBuffer,
            dwCount,
            &dwCount,
            NULL))
        {
            MyHandleError(TEXT("Error writing ciphertext.\n"), GetLastError());
            goto Exit_MyEncryptFile;
        }
    } while (!fEOF);

    fReturn = true;

Exit_MyEncryptFile:
    //---------------------------------------------------------------
    // Close files.
    if (hSourceFile)
    {
        CloseHandle(hSourceFile);
    }

    if (hDestinationFile)
    {
        CloseHandle(hDestinationFile);
    }

    //---------------------------------------------------------------
    // Free memory. 
    if (pbBuffer)
    {
        free(pbBuffer);
    }


    //-----------------------------------------------------------
    // Release the hash object. 
    if (hHash)
    {
        if (!(CryptDestroyHash(hHash)))
        {
            MyHandleError(
                TEXT("Error during CryptDestroyHash.\n"),
                GetLastError());
        }

        hHash = NULL;
    }

    //---------------------------------------------------------------
    // Release the session key. 
    if (hKey)
    {
        if (!(CryptDestroyKey(hKey)))
        {
            MyHandleError(
                TEXT("Error during CryptDestroyKey!\n"),
                GetLastError());
        }
    }

    //---------------------------------------------------------------
    // Release the provider handle. 
    if (hCryptProv)
    {
        if (!(CryptReleaseContext(hCryptProv, 0)))
        {
            MyHandleError(
                TEXT("Error during CryptReleaseContext!\n"),
                GetLastError());
        }
    }
    return fReturn;
} // End Encryptfile.


//-------------------------------------------------------------------
//  This example uses the function MyHandleError, a simple error
//  handling function, to print an error message to the  
//  standard error (stderr) file and exit the program. 
//  For most applications, replace this function with one 
//  that does more extensive error reporting.

void MyHandleError(LPTSTR psz, int nErrorNumber)
{
    _ftprintf(stderr, TEXT("An error occurred in the program. \n"));
    _ftprintf(stderr, TEXT("%s\n"), psz);
    _ftprintf(stderr, TEXT("Error number %x.\n"), nErrorNumber);
}

要加密的文件:
文件加密CryptEncrypt和导出密钥CryptExportKey学习笔记_第1张图片
加密后文件:
这里写图片描述
导出的秘钥blob:
这里写图片描述

你可能感兴趣的:(c++)