安全编程技术实验五

WindowsCryptoAPI的使用

一、实验目的

该实验为设计性实验,实验目的如下:
1.熟悉WindowsCryptoAPI提供的常用函数接口。
2.掌握WindowsCryptoAPI的使用。

二、实验内容及步骤

1.熟悉WindowsCryptoAPI提供的常用函数接口。
2.掌握WindowsCryptoAPI的使用。
3.利用WindowsCryptoAPI设计和实现一个小型密码系统(如文件加密机),完成加解密、摘要运算、数字签名等功能

三、实验要求

1.本实验一人一组,编程语言为C/C++/C#。
2.要求学生掌握WindowsCryptoAPI,并熟练掌握C/C++/C#语言编程。
3.要求学生能够设计和实现一个小型密码系统,具体要求如下:
(1)界面友好,操作方便。
(2)利用WindowsCryptoAPI完成该密码系统的相关功能,如加解密、摘要运算、数字签名等。
4.实验报告要求:
(1)实验报告要求包括实验目的、实验要求、实验内容(系统的设计和实现)、实验结果分析和实验体会等,重点在于实验内容(系统的设计和实现)和实验结果分析。实验报告要求上传和打印。
(2)写出系统设计和实现过程中的心得和体会,并回答实验中的思考题。
(3)实验报告撰写规范请见附。

四、实验环境

计算机:Windows11、CodeBlocks C

五、实验提示

1.本实验可以参考以下资料:
(1)《编写安全的代码(第2版)》,(美)MichaelHoward,DavidLeBlanc,机械工业出版社,2005年
(2)《C#数据安全手册》,(美)MatthewMacDonald、ErikHohansson,清华大学出版社,2003年
(3)DanFox.使用.NETFramework的Cryptography命名空间保护私有数据
http://www.microsoft.com/china/msdn/Archives/msdnmag/issues/02/06/crypto/crypto.asp
(4)PaulD.Sheriff.Microsoft.NET中的简化加密
http://www.microsoft.com/china/MSDN/library/default.aspx?mfr=true
(5)《Windows系统中的安全组件—CAPICOM》
http://tag.csdn.net/Article/009a481c-c023-4a5f-926e-f4488e7314d4.html
2.此外,MSDN中有关于WindowsCryptoAPI的详细描述。更多关于WindowsCryptoAPI的参考资料和参考例程请到实验室服务器或BB系统下载。

六、实验知识点

Windows CryptoAPI系统调用中的加解密、信息摘要和签名。
1、加密
使用CryptGenKey函数生成会话密钥,或使用CryptDeriveKey函数将密码转换为适用于加密的密钥,然后使用用CryptEncrypt函数加密文件中的数据;
2、解密
使用CryptDecryptMessage解密数据,获取指向加密 BLOB 的指针,打开证书存储,创建证书存储数组,初始化CRYPT_DECRYPT_MESSAGE_PARA构,最后调用CryptDecryptMessage以解密消息中包含的数据。
3、摘要
通过调用CryptAcquireContext获取加密服务提供程序的句柄。通过调用CryptCreateHash、CryptHashData和CryptDeriveKey派生字节字符串的对称
密钥。使用对称密钥通过调用CryptCreateHash和CryptSetHashParam创建HMAC哈希对象。通过调用CryptHashData对消息进行哈希处理。通过调CryptGetHashParam检索哈希值;
4、签名
首先获取指向要签名的数据的指针。将指向数据的指针分配给"要签名的数据"数组的索引零。获取加密提供程序的句柄。打开包含签名者证书的证书存储。获取签名者证书的地址。将证书的地址分配给MsgCert数组的零索引。将消息中包含的任何其他证书的地址分配给MsgCert数组。初始CRYPT_ALGORITHM_DENTIFIER结构,将pszObjId 成员初始化为所需的哈希算法,并适当地初始化其他成员。初始化CRYPT_SIGN_MESSAGE_PARA 结构,将pSigningCert成员初始化为签名者 证书的地址,将MsgCert数组成员初始化为签名者和其他证书的地址,将HashAlgorithm成员初始化为CRYPT ALGORITHM__IDENTIFIER结构的地址,并视情况初始化其他成员。调用CryptSignMessage函数,传递pSignPara参数的CRYPT_SIGN MESSAGE__PARA结构、rgpbToBeSigned参数的"要签名的数据"数组的地址、pbSignedBlob输出参数的地址以及其他参数的值。

七、实验运行截图





八、实验代码

#include <tchar.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <conio.h>

// Link with the Advapi32.lib file.
#pragma comment (lib, "advapi32")

#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH  0x00800000

//--------------------------------------------------------------------
// These additional #define statements are required.
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8
#define MAX_FILE_SIZE 4000000
#define SIGNATURE_SIZE 500

BYTE* pbKeyBlob; // 用来保存导出的公钥
DWORD dwBlobLen;

void HandleError(char* s)
{
    fprintf(stderr, "运行程序时出现错误。\n");
    fprintf(stderr, "%s\n", s);
    fprintf(stderr, "Error number %x.\n", GetLastError());
    fprintf(stderr, "程序终止。\n");
    exit(1);
} // End of HandleError



//-----------------------------------------------------------------------
//   Code for the function VerifyFile 功能:验证数字签名
extern BOOL VerifyFile(
    PCHAR szSource,   //原文件
    PCHAR szSignature)   //数字签名文件
{
    //--------------------------------------------------------------------
    //   Declare and initialize local variables.

    FILE* hSource;
    FILE* hSignature;

    HCRYPTPROV hCryptProv; //CSP:钥匙容器
    HCRYPTKEY hKey;   //公钥对:包括配对的一个公钥和一个密钥
    HCRYPTKEY hPubKey;  //公钥对中的公钥
    HCRYPTHASH hHash;  //hash对象,用于对数据文件进行hash处理,得到hash值
    //公钥签名就是针对hash值进行签名,而不是原文件,
    //这是因为公钥处理的速度非常慢

    BYTE* pbSignature;
    DWORD dwSigLen;
    PBYTE pbBuffer;
    DWORD dwBufferLen;
    DWORD dwCount;

    //DWORD dwBlobLen;

    //--------------------------------------------------------------------
    // Open source file.
    if (hSource = fopen(szSource, "rb"))
    {
        printf("\n源明文文件 %s 已打开。\n", szSource);
    }
    else
    {
        HandleError("\n打开源代码明文文件时出错!");
    }
    //--------------------------------------------------------------------
    // Allocate memory.
    if (pbBuffer = (BYTE*)malloc(MAX_FILE_SIZE))
    {
        printf("已为缓冲区分配了内存。 \n");
    }
    else
    {
        HandleError("内存不足。\n");
    }
    //将源文件读入pbBuffer
    dwCount = fread(pbBuffer, 1, MAX_FILE_SIZE, hSource);
    if (ferror(hSource))
    {
        HandleError("读取明文错误!\n");
    }

    //---------------------------------------------------------------------
    // Open signature file 读入签名文件(特殊处理:直接采用保留在内存中的签名来进行验证)
    if (hSignature = fopen(szSignature, "rb"))
    {
        printf("\n签名明文文件 %s 已打开。\n", szSignature);
    }
    else
    {
        HandleError("\n打开签名文件时出错!");
    }
    //--------------------------------------------------------------------
    // Allocate memory.
    if (pbSignature = (BYTE*)malloc(SIGNATURE_SIZE))
    {
        printf("已为缓冲区分配了内存。\n");
    }
    else
    {
        HandleError("内存不足。\n");
    }
    //将签名读入pbSignature
    dwSigLen = fread(pbSignature, 1, SIGNATURE_SIZE, hSignature);
    if (ferror(hSource))
    {
        HandleError("读取明文错误!\n");
    }

    //以下获得一个CSP句柄
    if (CryptAcquireContext(
                &hCryptProv,  //调用完成之后hCryptProv保存密钥容器的句柄
                NULL,    //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名
                NULL,
                PROV_RSA_FULL,
                0))
    {
        printf("已获取加密提供程序。\n");
    }
    else
    {
        if (CryptAcquireContext(
                    &hCryptProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_NEWKEYSET))   //创建密钥容器
        {
            //创建密钥容器成功,并得到CSP句柄
            printf("已经创建了一个新的密钥容器。\n");
        }
        else
        {
            HandleError("无法创建新的密钥容器。\n");
        }
    }

    //导入 pbKeyBlob 公钥(这个公钥与签名时所用的私钥配对,在签名时导出到pbKeyBlob中)
    if (CryptImportKey(
                hCryptProv,
                pbKeyBlob,
                dwBlobLen,
                0,
                0,
                &hPubKey))
    {
        printf("已导入密钥。\n");
    }
    else
    {
        HandleError("公钥导入失败。");
    }
    //-------------------------------------------------------------------
    // Create a new hash object. 对原文件进行hash处理

    if (CryptCreateHash(
                hCryptProv,
                CALG_MD5,
                0,
                0,
                &hHash))
    {
        printf("已经重新创建了哈希对象。\n");
    }
    else
    {
        HandleError("CryptCreateHash 过程中出错。");
    }
    //-------------------------------------------------------------------
    // Compute the cryptographic hash of the buffer.

    if (CryptHashData(
                hHash,
                pbBuffer,
                dwCount,
                0))
    {
        printf("The new has been created.\n");
    }
    else
    {
        HandleError("CryptHashData 期间发生错误。");
    }
    //-------------------------------------------------------------------
    // Validate the digital signature. 验证数字签名是否正确

    if (CryptVerifySignature(
                hHash,
                pbSignature,
                dwSigLen,
                hPubKey,
                NULL,
                0))
    {
        printf("\n**********************************************\n");
        printf("签名已通过验证!\n");
        printf("\n**********************************************\n");
    }
    else
    {
        printf("\n**********************************************\n");
        printf("签名未验证!");
        printf("\n**********************************************\n");
    }

    //--------------------------------------------------------------------
    // Close files.

    if (hSource)
        fclose(hSource);
    //if(hSignature)
    // fclose(hSignature);

    //-------------------------------------------------------------------
    // Free memory to be used to store signature.

    if (pbSignature)
        free(pbSignature);

    //-------------------------------------------------------------------
    // Destroy the hash object.

    if (hHash)
        CryptDestroyHash(hHash);

    //-------------------------------------------------------------------
    // Release the provider handle.

    if (hCryptProv)
        CryptReleaseContext(hCryptProv, 0);
    return(TRUE);

}



//-----------------------------------------------------------------------
//   Code for the function SignFile 功能:对文件进行数字签名
extern BOOL SignFile(
    PCHAR szSource,
    PCHAR szDestination)
{
    //--------------------------------------------------------------------
    //   Declare and initialize local variables.

    FILE* hSource;
    FILE* hDestination;

    HCRYPTPROV hCryptProv;
    HCRYPTKEY hKey;
    HCRYPTHASH hHash;

    BYTE* pbSignature;
    PBYTE pbBuffer;
    DWORD dwBufferLen;
    DWORD dwCount;
    DWORD dwSigLen;


    //--------------------------------------------------------------------
    // Open source file.
    if (hSource = fopen(szSource, "rb"))
    {
        printf("\n源明文文件 %s 已打开。\n", szSource);
    }
    else
    {
        HandleError("\n打开源代码明文文件时出错!");
    }
    //--------------------------------------------------------------------
    // Allocate memory.
    if (pbBuffer = (BYTE*)malloc(MAX_FILE_SIZE))
    {
        printf("已为缓冲区分配了内存。\n");
    }
    else
    {
        HandleError("内存不足。\n");
    }
    dwCount = fread(pbBuffer, 1, MAX_FILE_SIZE, hSource);
    if (ferror(hSource))
    {
        HandleError("读取明文错误!\n");
    }
    //--------------------------------------------------------------------
    // Open destination file.
    if (hDestination = fopen(szDestination, "wb"))
    {
        printf("/n目标文件 %s 已打开。\n", szDestination);
    }
    else
    {
        HandleError("\n打开目标密文文件时出错!");
    }

    //以下获得一个CSP句柄
    if (CryptAcquireContext(
                &hCryptProv,
                NULL,    //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名
                NULL,
                PROV_RSA_FULL,
                0))
    {
        printf("已经获得了一个加密提供者。\n");
    }
    else
    {
        if (CryptAcquireContext(
                    &hCryptProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_NEWKEYSET))   //创建密钥容器
        {
            //创建密钥容器成功,并得到CSP句柄
            printf("已经创建了一个新的密钥容器。\n");
        }
        else
        {
            HandleError("无法创建新的密钥容器。\n");
        }
    }
    if (CryptGetUserKey(
                hCryptProv,                     // 我们已经得到的CSP句柄
                AT_SIGNATURE,                   // 这里想得到signature key pair
                &hKey))                         // 返回密钥句柄
    {
        printf("已准备好签名密钥。\n");
    }
    else     //取signature key pair错误
    {
        printf("无可用签名密钥。\n");
        if (GetLastError() == NTE_NO_KEY)   //密钥容器里不存在signature key pair
        {
            // 创建 signature key pair.
            printf("签名密钥不存在。\n");
            printf("创建签名密钥对。\n");
            if (CryptGenKey(
                        hCryptProv,  //CSP句柄
                        AT_SIGNATURE, //创建的密钥对类型为signature key pair
                        0,    //key类型,这里用默认值
                        &hKey))   //创建成功返回新创建的密钥对的句柄
            {
                printf("创建签名密钥对。\n");
            }
            else
            {
                printf("创建签名密钥时发生错误。\n");
            }
        }
        else
        {
            printf("一个错误,而不是 NTE_NO_KEY 获取签名/密钥。\n");
        }
    } // end if
    //-------------------------------------------------------------------
    // 导出公钥
    // Export the public key. Here the public key is exported to a
    // PUBLICKEYBOLB so that the receiver of the signed hash can
    // verify the signature. This BLOB could be written to a file and
    // sent to another user.

    if (CryptExportKey(
                hKey,
                NULL,
                PUBLICKEYBLOB,
                0,
                NULL,
                &dwBlobLen))
    {
        printf("确定公钥的 BLOB 的大小。\n");
    }
    else
    {
        HandleError("计算 BLOB 长度错误。");
    }
    //-------------------------------------------------------------------
    // Allocate memory for the pbKeyBlob.

    if (pbKeyBlob = (BYTE*)malloc(dwBlobLen))
    {
        printf("已经为 BLOB 分配了内存。\n");
    }
    else
    {
        HandleError("内存不足。\n");
    }
    //-------------------------------------------------------------------
    // Do the actual exporting into the key BLOB.

    if (CryptExportKey(
                hKey,
                NULL,
                PUBLICKEYBLOB,
                0,
                pbKeyBlob,
                &dwBlobLen))
    {
        printf("内容已写入 BLOB。\n");
    }
    else
    {
        HandleError("CryptExportKey 期间出错。");
    }
    //签名密钥已经准备完毕,公钥也已导出到 pbKeyBlob 中



    //*****************************签名***********************************
    //-------------------------------------------------------------------
    // Create the hash object.
    if (CryptCreateHash(
                hCryptProv,
                CALG_MD5,
                0,
                0,
                &hHash))
    {
        printf("创建的散列对象。\n");
    }
    else
    {
        HandleError("CryptCreateHash 过程中出错。");
    }
    if (CryptHashData(
                hHash,
                pbBuffer,
                dwCount,
                0))
    {
        printf("数据缓冲区已被散列。\n");
    }
    else
    {
        HandleError("CryptHashData 期间发生错误。\n");
    }
    //释放缓冲区
    if (pbBuffer)
        free(pbBuffer);
    pbBuffer = NULL;
    //-------------------------------------------------------------------
    // Determine the size of the signature and allocate memory.

    dwSigLen = 0;
    if (CryptSignHash(
                hHash,
                AT_SIGNATURE,
                NULL,
                0,
                NULL,
                &dwSigLen))
    {
        printf("发现签名长度 %d。\n", dwSigLen);
    }
    else
    {
        HandleError("CryptSignHash 期间发生错误。");
    }
    //-------------------------------------------------------------------
    // Allocate memory for the signature buffer.

    if (pbSignature = (BYTE*)malloc(dwSigLen))
    {
        printf("为签名分配的内存。\n");
    }
    else
    {
        HandleError("内存不足。");
    }
    //-------------------------------------------------------------------
    // Sign the hash object.

    if (CryptSignHash(
                hHash,
                AT_SIGNATURE,
                NULL,
                0,
                pbSignature,
                &dwSigLen))
    {
        printf("pbSignature 是哈希签名。\n");
    }
    else
    {
        HandleError("CryptSignHash 期间发生错误。");
    }

    if (fwrite(pbSignature, 1, dwSigLen, hDestination) != dwSigLen)
        HandleError("将签名写入文件失败!");

    printf("散列对象已被销毁。\n");
    printf("该项目的签约阶段已经完成。\n\n");

    //善后工作
    //--------------------------------------------------------------------
    // Destroy session key.

    if (hKey)
        CryptDestroyKey(hKey);
    //-------------------------------------------------------------------
    // Destroy the hash object.

    if (hHash)
        CryptDestroyHash(hHash);

    //-------------------------------------------------------------------
    // Release the provider handle.

    if (hCryptProv)
        CryptReleaseContext(hCryptProv, 0);
    //--------------------------------------------------------------------
    // Close files.

    if (hSource)
        fclose(hSource);
    if (hDestination)
        fclose(hDestination);


    return(TRUE);
}
//--------------------------------------------------------------------
//   Code for the function Decryptfile, which is called by main too
extern BOOL DecryptFile(
    PCHAR szSource,
    PCHAR szDestination,
    PCHAR szPassword)
//--------------------------------------------------------------------
//   Parameters passed are:
//     szSource, the name of the input, a plaintext file.
//     szDestination, the name of the output, an encrypted file to be
//         created.
//     szPassword, the password.
{
    //--------------------------------------------------------------------
    //   Declare and initialize local variables.

    FILE* hSource;
    FILE* hDestination;

    HCRYPTPROV hCryptProv;
    HCRYPTKEY hKey;
    HCRYPTHASH hHash;

    PBYTE pbBuffer;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;

    //--------------------------------------------------------------------
    // Open source file.
    if (hSource = fopen(szSource, "rb"))
    {
        printf("\n源明文文件 %s 已打开。\n", szSource);
    }
    else
    {
        HandleError("\n打开源代码明文文件时出错!");
    }

    //--------------------------------------------------------------------
    // Open destination file.
    if (hDestination = fopen(szDestination, "wb"))
    {
        printf("\n目标文件 %s 已打开。\n", szDestination);
    }
    else
    {
        HandleError("\n打开目标密文文件时出错!");
    }

    //以下获得一个CSP句柄
    if (CryptAcquireContext(
                &hCryptProv,
                NULL,    //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名
                NULL,
                PROV_RSA_FULL,
                0))
    {
        printf("已获取加密提供程序。\n");
    }
    else
    {
        if (CryptAcquireContext(
                    &hCryptProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_NEWKEYSET))   //创建密钥容器
        {
            //创建密钥容器成功,并得到CSP句柄
            printf("已经创建了一个新的密钥容器。\n");
        }
        else
        {
            HandleError("无法创建新的密钥容器。\n");
        }

    }

    //--------------------------------------------------------------------
    // 创建一个会话密钥(session key)
    // 会话密钥也叫对称密钥,用于对称加密算法。
    // (注: 一个Session是指从调用函数CryptAcquireContext到调用函数
    //   CryptReleaseContext 期间的阶段。会话密钥只能存在于一个会话过程)

    //--------------------------------------------------------------------
    // Create a hash object.
    if (CryptCreateHash(
                hCryptProv,
                CALG_MD5,
                0,
                0,
                &hHash))
    {
        printf("已经创建了一个哈希对象。\n");
    }
    else
    {
        HandleError("CryptCreateHash 过程中出错!\n");
    }

    //--------------------------------------------------------------------
    // 用输入的密码产生一个散列
    if (CryptHashData(
                hHash,
                (BYTE*)szPassword,
                strlen(szPassword),
                0))
    {
        printf("密码已添加到散列中。\n");
    }
    else
    {
        HandleError("CryptHashData 期间发生错误。\n");
    }

    //--------------------------------------------------------------------
    // 通过散列生成会话密钥
    if (CryptDeriveKey(
                hCryptProv,
                ENCRYPT_ALGORITHM,
                hHash,
                KEYLENGTH,
                &hKey))
    {
        printf("加密密钥源自密码哈希。\n");
    }
    else
    {
        HandleError("CryptDeriveKey 期间出错!\n");
    }
    //--------------------------------------------------------------------
    // Destroy the hash object.

    CryptDestroyHash(hHash);
    hHash = NULL;

    //--------------------------------------------------------------------
    //  The session key is now ready.

    //--------------------------------------------------------------------
    // 因为加密算法是按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))
    {
        printf("已为缓冲区分配了内存。\n");
    }
    else
    {
        HandleError("内存不足。\n");
    }



    //*****************************解密***********************************

    do
    {
        //--------------------------------------------------------------------
        // Read up to dwBlockLen bytes from the source file.
        dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
        if (ferror(hSource))
        {
            HandleError("读取明文错误!\n");
        }

        //--------------------------------------------------------------------
        // 解密数据
        if (!CryptDecrypt(
                    hKey,   //密钥
                    0,    //如果数据同时进行散列和加密,这里传入一个散列对象
                    feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输.
                    //入FALSE这里通过判断是否到文件尾来决定是否为最后一块。
                    0,    //保留
                    pbBuffer,  //输入被加密数据,输出加密后的数据
                    &dwCount))   //输入被加密数据实际长度,输出加密后数据长度
        {
            HandleError("CryptEncrypt 期间发生错误。\n");
        }

        //--------------------------------------------------------------------
        // Write data to the destination file.

        fwrite(pbBuffer, 1, dwCount, hDestination);
        if (ferror(hDestination))
        {
            HandleError("写入密文错误。");
        }
    }
    while (!feof(hSource));

    //*****************************解密***********************************



    //善后工作
    //--------------------------------------------------------------------
    // Close files.

    if (hSource)
        fclose(hSource);
    if (hDestination)
        fclose(hDestination);



    //--------------------------------------------------------------------
    // Free memory.

    if (pbBuffer)
        free(pbBuffer);

    //--------------------------------------------------------------------
    // Destroy session key.

    if (hKey)
        CryptDestroyKey(hKey);

    //--------------------------------------------------------------------
    // Destroy hash object.

    if (hHash)
        CryptDestroyHash(hHash);

    //--------------------------------------------------------------------
    // Release provider handle.

    if (hCryptProv)
        CryptReleaseContext(hCryptProv, 0);
    return(TRUE);

}
//--------------------------------------------------------------------
//   Code for the function EncryptFile called by main.

extern BOOL EncryptFile(
    PCHAR szSource,
    PCHAR szDestination,
    PCHAR szPassword)
//--------------------------------------------------------------------
//   Parameters passed are:
//     szSource, the name of the input, a plaintext file.
//     szDestination, the name of the output, an encrypted file to be
//         created.
//     szPassword, the password.
{
    //--------------------------------------------------------------------
    //   Declare and initialize local variables.

    FILE* hSource;
    FILE* hDestination;

    HCRYPTPROV hCryptProv;
    HCRYPTKEY hKey;
    HCRYPTHASH hHash;

    PBYTE pbBuffer;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;

    //--------------------------------------------------------------------
    // Open source file.
    if (hSource = fopen(szSource, "rb"))
    {
        printf("\n源明文文件 %s 已打开。\n", szSource);
    }
    else
    {
        HandleError("\n打开源代码明文文件时出错!");
    }

    //--------------------------------------------------------------------
    // Open destination file.
    if (hDestination = fopen(szDestination, "wb"))
    {
        printf("\n目标文件 %s 已打开。\n", szDestination);
    }
    else
    {
        HandleError("\n打开目标密文文件时出错!");
    }

    //以下获得一个CSP句柄
    if (CryptAcquireContext(
                &hCryptProv,
                NULL,    //NULL表示使用默认密钥容器,默认密钥容器名为用户登陆名
                NULL,
                PROV_RSA_FULL,
                0))
    {
        printf("已获取加密提供程序。\n");
    }
    else
    {
        if (CryptAcquireContext(
                    &hCryptProv,
                    NULL,
                    NULL,
                    PROV_RSA_FULL,
                    CRYPT_NEWKEYSET))   //创建密钥容器
        {
            //创建密钥容器成功,并得到CSP句柄
            printf("已经创建了一个新的密钥容器。\n");
        }
        else
        {
            HandleError("无法创建新的密钥容器。\n");
        }

    }

    //--------------------------------------------------------------------
    // 创建一个会话密钥(session key)
    // 会话密钥也叫对称密钥,用于对称加密算法。
    // (注: 一个Session是指从调用函数CryptAcquireContext到调用函数
    //   CryptReleaseContext 期间的阶段。会话密钥只能存在于一个会话过程)

    //--------------------------------------------------------------------
    // Create a hash object.
    if (CryptCreateHash(
                hCryptProv,
                CALG_MD5,
                0,
                0,
                &hHash))
    {
        printf("已经创建了一个哈希对象。\n");
    }
    else
    {
        HandleError("CryptCreateHash 过程中出错!\n");
    }

    //--------------------------------------------------------------------
    // 用输入的密码产生一个散列
    if (CryptHashData(
                hHash,
                (BYTE*)szPassword,
                strlen(szPassword),
                0))
    {
        printf("密码已添加到散列中。\n");
    }
    else
    {
        HandleError("CryptHashData 期间发生错误。\n");
    }

    //--------------------------------------------------------------------
    // 通过散列生成会话密钥
    if (CryptDeriveKey(
                hCryptProv,
                ENCRYPT_ALGORITHM,
                hHash,
                KEYLENGTH,
                &hKey))
    {
        printf("加密密钥源自密码哈希。\n");
    }
    else
    {
        HandleError("CryptDeriveKey 期间出错!\n");
    }
    //--------------------------------------------------------------------
    // Destroy the hash object.

    CryptDestroyHash(hHash);
    hHash = NULL;

    //--------------------------------------------------------------------
    //  The session key is now ready.

    //--------------------------------------------------------------------
    // 因为加密算法是按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))
    {
        printf("已为缓冲区分配了内存。\n");
    }
    else
    {
        HandleError("内存不足。\n");
    }
    //--------------------------------------------------------------------
    // In a do loop, encrypt the source file and write to the source file.

    do
    {
        //--------------------------------------------------------------------
        // Read up to dwBlockLen bytes from the source file.
        dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
        if (ferror(hSource))
        {
            HandleError("读取明文错误!\n");
        }

        //--------------------------------------------------------------------
        // 加密数据
        if (!CryptEncrypt(
                    hKey,   //密钥
                    0,    //如果数据同时进行散列和加密,这里传入一个散列对象
                    feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输.
                    //入FALSE这里通过判断是否到文件尾来决定是否为最后一块。
                    0,    //保留
                    pbBuffer,  //输入被加密数据,输出加密后的数据
                    &dwCount,  //输入被加密数据实际长度,输出加密后数据长度
                    dwBufferLen))   //pbBuffer的大小。
        {
            HandleError("CryptEncrypt 期间发生错误。\n");
        }

        //--------------------------------------------------------------------
        // Write data to the destination file.

        fwrite(pbBuffer, 1, dwCount, hDestination);
        if (ferror(hDestination))
        {
            HandleError("写入密文错误。");
        }
    }
    while (!feof(hSource));

    //--------------------------------------------------------------------
    //  End the do loop when the last block of the source file has been
    //  read, encrypted, and written to the destination file.

    //--------------------------------------------------------------------
    // Close files.

    if (hSource)
        fclose(hSource);
    if (hDestination)
        fclose(hDestination);








    /*****************************解密***********************************

    	//解密
    --------------------------------------------------------------------
    	//Open source file.
    	//if(hSource = fopen(szDestination,"rb"))
    	//{
    	// printf("The source plaintext file, %s, is open. \n", szSource);
    	//}
    	//else
    	//{
    	// HandleError("Error opening source plaintext file!");
    	//}

    	--------------------------------------------------------------------
    	//Open destination file.
    	//if(hDestination = fopen("ccc.txt","wb"))
    	//{
    	// printf("Destination file %s is open. \n", szDestination);
    	//}
    	//else
    	//{
    	// HandleError("Error opening destination ciphertext file!");
    	//}

    	//do
    	//{
    	// //--------------------------------------------------------------------
    	// // Read up to dwBlockLen bytes from the source file.
    	// dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
    	// if(ferror(hSource))
    	// {
    	//  HandleError("Error reading plaintext!\n");
    	// }
    	//
    	// //--------------------------------------------------------------------
    	// // 解密数据
    	// if(!CryptDecrypt(
    	//  hKey,   //密钥
    	//  0,    //如果数据同时进行散列和加密,这里传入一个散列对象
    	//  feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输.
    	//      //入FALSE这里通过判断是否到文件尾来决定是否为最后一块。
    	//  0,    //保留
    	//  pbBuffer,  //输入被加密数据,输出加密后的数据
    	//  &dwCount))  //输入被加密数据实际长度,输出加密后数据长度
    	// {
    	//  HandleError("Error during CryptEncrypt. \n");
    	// }
    	//
    	// //--------------------------------------------------------------------
    	// // Write data to the destination file.
    	//
    	// fwrite(pbBuffer, 1, dwCount, hDestination);
    	// if(ferror(hDestination))
    	// {
    	//  HandleError("Error writing ciphertext.");
    	// }
    	//}
    	//while(!feof(hSource));

    	//***************************** 解密***********************************/

    //--------------------------------------------------------------------
    // Free memory.

    if (pbBuffer)
        free(pbBuffer);

    //--------------------------------------------------------------------
    // Destroy session key.

    if (hKey)
        CryptDestroyKey(hKey);

    //--------------------------------------------------------------------
    // Destroy hash object.

    if (hHash)
        CryptDestroyHash(hHash);

    //--------------------------------------------------------------------
    // Release provider handle.

    if (hCryptProv)
        CryptReleaseContext(hCryptProv, 0);
    return(TRUE);
} // End of Encryptfile

//--------------------------------------------------------------------
//  This example uses the function HandleError, 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.



//--------------------------------------------------------------------
//   Begin main.

int main(int argc, char* argv[])
{
    CHAR szSource[100];
    CHAR szDestination[100];
    CHAR szPassword[100];

    //--------------------------------------------------------------------
// Call EncryptFile to do the actual encryption. 加密文件
    printf("\n------------------------------------------------------------\n");
    printf("\n\n1.加密文件。\n\n");
    printf("\n输入要加密的文件名: ");
    scanf("%s", szSource);
    printf("\n输入输出文件的名称: ");
    scanf("%s", szDestination);
    printf("\n输入密码:");
    scanf("%s", szPassword);

    if (EncryptFile(szSource, szDestination, szPassword))
    {
        printf("\n文件 %s 的加密成功。\n", szSource);
        printf("\n加密的数据在文件 %s 中。\n", szDestination);
    }
    else
    {
        HandleError("/n加密文件错误!");
    }

    //--------------------------------------------------------------------
// Call Decryptfile to do the actual decryption. 解密文件
    printf("\n------------------------------------------------------------\n");
    printf("\n\n2.解密文件。\n\n");
    printf("\n输入要解密的文件的名称: ");
    scanf("%s", szSource);
    printf("\n输入输出文件的名称: ");
    scanf("%s", szDestination);
    printf("\n输入密码:");
    scanf("%s", szPassword);

    if (DecryptFile(szSource, szDestination, szPassword))
    {
        printf("\n文件 %s 的解密成功。\n", szSource);
        printf("\n解密的数据在文件 %s 中。\n", szDestination);
    }
    else
    {
        HandleError("\n解密文件出错!");
    }

    //--------------------------------------------------------------------
// Call SignFile to do the actual signature   签名文件
    printf("\n------------------------------------------------------------\n");
    printf("\n\n3.签名文件。\n\n");
    printf("\n输入要签名的文件名: ");
    scanf("%s", szSource);
    printf("\n输入签名文件名: ");
    scanf("%s", szDestination);

    if (SignFile(szSource, szDestination))
    {
        printf("\n文件 %s 的签名成功。\n", szSource);
        printf("\n签名数据在文件 %s 中。\n", szDestination);
    }
    else
    {
        HandleError("\n签名文件时出错!");
    }

    //---------------------------------------------------------------------
// Call VerifyFile to do the actual verification 验证签名
    printf("\n------------------------------------------------------------\n");
    printf("\n\n4.验证文件及其签名。\n\n");
    printf("\n输入需要验证的文件名: ");
    scanf("%s", szSource);
    printf("\n输入签名文件名: ");
    scanf("%s", szDestination);
    //printf("/nEnter the name of the public key file: ");
    //scanf("%s",szDestination);

    if (VerifyFile(szSource, szDestination))
    {
        printf("\n文件 %s 的验证成功。\n", szSource);
    }
    else
    {
        HandleError("\n验证失败。文件错误!");
    }
    return 0;
} // End of main

九、实验总结

在本次实验中我对WindowsCryptoAPI有了一定的掌握。实现了对文本的加密解密签名和验签。

十、思考题

利用WindowsCryptoAPI进行加解密的一般步骤是怎样的?

posted @ 2022-11-26 17:12  戴骏  阅读(116)  评论(0编辑  收藏  举报