《安全编程技术》实验五 Windows CryptoAPI的使用

一、实验目的与要求:

(一)实验目的

该实验为设计性实验,实验目的如下:

  • 熟悉Windows CryptoAPI提供的常用函数接口。
  • 掌握Windows CryptoAPI的使用。

(二)实验要求

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

二、实验内容、步骤及结论

(一)实验内容

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

(二)代码

// https://learn.microsoft.com/zh-cn/windows/win32/seccrypto/example-c-program-encrypting-a-file
// https://www.cnblogs.com/King-King/p/14701748.html
// https://blog.csdn.net/sunhf_my/article/details/1679986

#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

(三)运行

1.加密

2.解密

3.签名

4.验签

三、实验体会

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

(一)应用程序使用Crypto API进行加密通信的一般步骤如下:

  1. include wincrypt.h
  2. 调用CryptAcquireContext()获得某个CSP模块中的密钥容器(key container)的一个句柄;
  3. 发送方使用CryptImportKey()将接受方的证书导入CSP中,从而获得接收方的公钥;
  4. 发送方式用CryptGenKey()随机产生一个会话密钥,且用对方的公钥对会话密钥进行加密,用CryptExportKey()将加密后的会话密钥导出并且发给对方;
  5. 接收方收到会话密钥后,用自己的私钥调用CryptImportKey(),将会话密钥解出来;
  6. 发送方用会话密钥调用CryptEncrypt()加密数据,并且发送给对方;
  7. 接收方收到加密后的数据,用会话密钥调用CryptDecrypt(),对数据进行解密;
  8. 通信完毕,调用CryptDestroyKey()释放任何密钥句柄,再用CryptReleaseContext()释放CSP句柄。

(二)使用Crypto API进行数字签名及验证的一般步骤如下:

  1. 调用CryptAcquireContext()获得某个CSP模块中的密钥容器(key container)的一个句柄;
  2. 签名者调用CryptGerUserKey()得到用于签名的密钥,并用CryptExportKey()降其中的公钥输出,以便收到数字签名的人对自己的签名进行验证;
  3. 签名者用CryptCreateHash()和CryptHashData()计算需要签名的数据散列值;
  4. 签名者用私钥调用CryptSignHash()给数据的散列值加上自己的签名;
  5. 负责验证签名的人在收到签名者发来的公钥、数据及签名后,先用CryptImportKey()将签名者的公钥导入密钥容器中;
  6. 验证者再签名者那样用CryptCreateHash()和CryptHashData()计算数据的散列值;
  7. 验证者用CryptVerifySignature()检查签名是否有效;
  8. 调用CryptDestroyHash()释放散列值对象,并调用CryptReleaseContext()释放最初的CSP句柄资源。
 
 
 
posted @ 2022-11-21 14:41  油菜园12号  阅读(938)  评论(0编辑  收藏  举报