/*
[]=========================================================================[]

	Copyright(C) Feitian Technologies Co., Ltd.
	All rights reserved.

FILE:
	SignVerifySign.cpp

DESC:
	Test signature and verify signature.
[]=========================================================================[]
*/

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

#define TEST_CSP_NAME	"FEITIAN ePassNG RSA Cryptographic Service Provider"
#define CONTAINER_NAME	"CSPKeyTest"

void MyHandleError(char *s);

void main(void)
{
	//-------------------------------------------------------------
	// Declare and initialize variables.
	
	HCRYPTPROV hProv;
	BYTE *pbBuffer= (BYTE *)"The data that is to be hashed and signed.";//被签名的数据
	DWORD dwBufferLen = strlen((char *)pbBuffer)+1;
	HCRYPTHASH hHash;
	HCRYPTKEY hKey;
	BYTE *pbKeyBlob;			//签名者得公钥数据
	BYTE *pbSignature;			//数字签名
	DWORD dwSigLen;
	DWORD dwBlobLen;
	LPTSTR szDescription = "Test Data Description";
	
	//--------------------------------------------------------------------
	// 获得CSP句柄
	printf("The following phase of this program is signature.\n\n");
	if(CryptAcquireContext(
		&hProv, 
		NULL, 
		TEST_CSP_NAME,
		PROV_RSA_FULL, 
		0)) 
	{
		printf("CSP context acquired.\n");
	}
	else	//密钥容器不存在创建之
	{
		if(CryptAcquireContext(
			&hProv, 
			NULL, 
			TEST_CSP_NAME, 
			PROV_RSA_FULL, 
			CRYPT_NEWKEYSET)) 
		{
			printf("A new key container has been created.\n");
		}
		else
		{
			MyHandleError("Error during CryptAcquireContext.");
		}
	}

	//--------------------------------------------------------------------
	// 从密钥容器中取数字签名用的密钥
	if(CryptGetUserKey(   
		hProv,    
		AT_SIGNATURE,    
		&hKey)) 
	{
		printf("The signature key has been acquired. \n");
	}
	else
	{
		if(GetLastError() == NTE_NO_KEY) //密钥容器里不存在signature key pair创建之
		{
			if(CryptGenKey(
				hProv,			//CSP句柄
				AT_SIGNATURE,	//创建的密钥对类型为signature key pair
				0,				//key类型，这里用默认值
				&hKey)) 		//创建成功返回新创建的密钥对的句柄
			{
				printf("Created a signature key pair.\n");
			}
			else
			{
				MyHandleError("Error occurred creating a signature key.\n"); 
			}
			
		}
		else
		{
			MyHandleError("Error during CryptGetUserKey for signkey.");
		}
	}

	//--------------------------------------------------------------------
	// 因为接收消息者要验证数字签名，所以要导出公钥给接收者。
	if(CryptExportKey(   
		hKey,    
		NULL,    
		PUBLICKEYBLOB,
		0,    
		NULL, 
		&dwBlobLen))  //得到公钥的大小
	{
		printf("Size of the BLOB for the public key determined. \n");
	}
	else
	{
		MyHandleError("Error computing BLOB length.");
	}

	//--------------------------------------------------------------------
	// 为存储公钥的缓冲区分配内存。
	if(pbKeyBlob = (BYTE*)malloc(dwBlobLen)) 
	{
		printf("Memory has been allocated for the BLOB. \n");
	}
	else
	{
		MyHandleError("Out of memory. \n");
	}

	//--------------------------------------------------------------------
	// 真正导出公钥数据
	if(CryptExportKey(   
		hKey, 
		NULL,    
		PUBLICKEYBLOB,    
		0,    
		pbKeyBlob,    //这个数据可以存入文件，发送给接收者。一般被存入数字证书
		&dwBlobLen))
	{
		printf("Contents have been written to the BLOB. \n");
	}
	else
	{
		MyHandleError("Error during CryptExportKey.");
	}

	//--------------------------------------------------------------------
	// 创建hash对象
	if(CryptCreateHash(
		hProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash)) 
	{
		printf("Hash object created. \n");
	}
	else
	{
		MyHandleError("Error during CryptCreateHash.");
	}

	//--------------------------------------------------------------------
	// 对数据进行hash运算
	if(CryptHashData(
		hHash, 
		pbBuffer, 
		dwBufferLen, 
		0)) 
	{
		printf("The data buffer has been hashed.\n");
	}
	else
	{
		MyHandleError("Error during CryptHashData.");
	}

	//--------------------------------------------------------------------
	// 使用signature key pair的私钥对hash数据签名

	dwSigLen= 0;
	if(CryptSignHash(
		hHash, 
		AT_SIGNATURE, 
		szDescription, 
		0, 
		NULL, 
		&dwSigLen)) //得到数字签名大小
	{
		printf("Signature length %d found.\n",dwSigLen);
	}
	else
	{
		MyHandleError("Error during CryptSignHash.");
	}

	//--------------------------------------------------------------------
	// 为数字签名缓冲区分配内存
	if(pbSignature = (BYTE *)malloc(dwSigLen))
	{
		printf("Memory allocated for the signature.\n");
	}
	else
	{
		MyHandleError("Out of memory.");
	}

	//--------------------------------------------------------------------
	// 得到数字签名
	if(CryptSignHash(
		hHash, 
		AT_SIGNATURE, 
		szDescription, 
		0, 
		pbSignature, //这里将返回数字签名，同被签名的数据一起发送给接收方
		&dwSigLen)) 
	{
		printf("pbSignature is the hash signature.\n");
	}
	else
	{
		MyHandleError("Error during CryptSignHash.");
	}

	//--------------------------------------------------------------------
	// Destroy the hash object.
	if(hHash) 
		CryptDestroyHash(hHash);
	
	printf("The hash object has been destroyed.\n");
	printf("The signing phase of this program is completed.\n\n");
	
/********************************************************************************

	下面的代码应该是接收者使用的，这里为了说明方便就把它放在一个文件里了。
	pbBuffer, pbSignature, szDescription， pbKeyBlob, 还有他们的长度
	在这里直接使用了，应该是接收者从文件或其他地方读取的。
	pbBuffer 里保存的是被签名的数据，所以认证时的内容必须跟签名时的一样
	这里使用的CSP句柄也没有重新创建

*********************************************************************************/
	
	printf("The following phase of this program is verify signature.\n\n");
	
	// 把签名者的公钥数据导入生成公钥句柄
	HCRYPTKEY hPubKey;
	if(CryptImportKey(
		hProv,
		pbKeyBlob,
		dwBlobLen,
		0,
		0,
		&hPubKey))
	{
		printf("The key has been imported.\n");
	}
	else
	{
		MyHandleError("Public key import failed.");
	}

	//--------------------------------------------------------------------
	// 创建哈希对象
	if(CryptCreateHash(
		hProv, 
		CALG_MD5, 
		0, 
		0, 
		&hHash)) 
	{
		printf("The hash object has been recreated. \n");
	}
	else
	{
		MyHandleError("Error during CryptCreateHash.");
	}

	//--------------------------------------------------------------------
	// 跟生成时一样对数据进行hash运算
	if(CryptHashData(
		hHash, 
		pbBuffer, 
		dwBufferLen, 
		0)) 
	{
		printf("The new hash has been created.\n");
	}
	else
	{
		MyHandleError("Error during CryptHashData.");
	}

	//--------------------------------------------------------------------
	// 验证数字签名
	if(CryptVerifySignature(
		hHash, 
		pbSignature,	//数字签名数据
		dwSigLen, 
		hPubKey,		//签名者的公钥
		szDescription, 
		0)) 
	{
		printf("The signature has been verified.\n");
	}
	else
	{
		printf("Signature not validated!\n");
	}

	//--------------------------------------------------------------------
	// Free memory to be used to store signature.
	if(pbSignature)
		free(pbSignature);

	if(pbKeyBlob)
		free(pbKeyBlob);
	
	//--------------------------------------------------------------------
	// Destroy the hash object.
	if(hHash) 
		CryptDestroyHash(hHash);
	
	//--------------------------------------------------------------------
	// Release the provider handle.
	if(hProv) 
		CryptReleaseContext(hProv, 0);

	printf("\n\nPress any key to exit...\n");
	getch();
} //  End of main

//--------------------------------------------------------------------
//  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(char *s)
{
    fprintf(stderr,"An error occurred in running the program. \n");
    fprintf(stderr,"%s\n",s);
    fprintf(stderr, "Error number %x.\n", GetLastError());
    fprintf(stderr, "Program terminating. \n");
    exit(1);
} // End of MyHandleError

