winston 发表于 2012-2-29 15:08:56

[加密解密]使用CryptoAPI进行文件加密解密——源码

前一篇简单介绍了下CryptoAPI的东西,这篇就将那点代码放出来给大家看看,以作交流参考目的。
/************************************************************************

                FileName:CryptoDefine.h
                Author      :eliteYang
                Mail      :elite_yang@163.com
                Desc      :加密需要的常量定义

************************************************************************/
#ifndef __CRYPTO_DEFINE_H_
#define __CRYPTO_DEFINE_H_

#include <WinCrypt.h>

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

#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8

#endif
/************************************************************************

                FileName:LogDefine.h
                Author      :eliteYang
                Mail      :elite_yang@163.com
                Desc      :提示信息

************************************************************************/
#ifndef __LOG_DEFINE_H__
#define __LOG_DEFINE_H__

#define MAX_INFO 1024
#include <stdio.h>

void (*g_fnPrintInfoCallBack)( char* format ) = NULL;

void OutputInfoMessage( char* format )
{
      if ( g_fnPrintInfoCallBack != NULL )
      {
                char temp[ MAX_INFO ] = { 0 };
                memset( temp, 0, sizeof( temp ) );
                va_list list;
                va_start( list, format );
                vsprintf_s( temp, sizeof( temp ) - 1, format, list );
                va_end( list );
                g_fnPrintInfoCallBack( temp );
      }
}

#endif
/************************************************************************

                        FileName:Crypto.h
                        Author      :eliteYang
                        Mail      :elite_yang@163.com
                        Desc      :Crypto加解密算法实现函数

************************************************************************/
#ifndef __CRYPTO_H__
#define __CRYPTO_H__

#pragma once

#define MAX_INFO_LENGTH 1024

#include "CryptoDefine.h"
#include <windows.h>
#include <string>
#include <stdio.h>
#include "LogDefine.h"

extern void MyOutputInfoMessage( char* format );

void HandleError( char* s )
{
      char cErrorInfo[ MAX_INFO_LENGTH ];
      memset( cErrorInfo, 0, sizeof( cErrorInfo ) );
      sprintf_s( cErrorInfo, sizeof( cErrorInfo ) - 1, "%s 错误代码:%x 程序终止执行!", s, GetLastError() );
      MyOutputInfoMessage( cErrorInfo );
      exit(1);
}

HCRYPTPROV GetCryptProv()
{
      HCRYPTPROV hCryptProv;
      if ( CryptAcquireContext( &hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0 ) )
      {
                MyOutputInfoMessage( "加密服务提供者(CSP)句柄获取成功" );
      }
      else
      {
                if ( !CryptAcquireContext( &hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET ) )
                {
                        HandleError( "重新建立一个新的密钥集出错!" );
                }
      }

      return hCryptProv;
}

HCRYPTKEY GetKeyByPassword( HCRYPTPROV hCryptProv,PCHAR szPassword )
{
      HCRYPTKEY hKey;
      HCRYPTHASH hHash;

      if ( CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash ) )
      {
                MyOutputInfoMessage( "一个Hash句柄已经被创建" );
      }
      else
      {
                HandleError( "CryptCreateHash 创建Hash句柄失败" );
      }

      if ( CryptHashData( hHash, (BYTE*)szPassword, strlen( szPassword ), 0 ) )
      {
                MyOutputInfoMessage( "密码已经被添加到了Hash表中" );
      }
      else
      {
                HandleError( "CryptHashData 计算密码的Hash值失败" );
      }

      // 通过Hash创建值创建会话密钥
      if ( CryptDeriveKey( hCryptProv, ENCRYPT_ALGORITHM, hHash, KEY_LENGTH, &hKey ) )
      {
                MyOutputInfoMessage( "通过密码的Hash值获得了加密密钥" );
      }
      else
      {
                HandleError( "CryptDeriveKey 通过Hash值创建会话密钥失败" );
      }

      if ( hHash )
      {
                if ( !CryptDestroyHash( hHash ) )
                {
                        HandleError( "CryptDestroyHash 销毁Hash句柄失败" );
                }
                else
                {
                        MyOutputInfoMessage( "释放Hash句柄成功" );
                        hHash = 0;
                }
      }

      return hKey;
}

HCRYPTKEY GenKeyByRandom( HCRYPTPROV hCryptProv,FILE* hDestination )
{
      HCRYPTKEY hKey;
      HCRYPTKEY hXchgKey;

      PBYTE pbKeyBlob;
      DWORD dwKeyBlobLen;

      if( CryptGenKey( hCryptProv, ENCRYPT_ALGORITHM, KEY_LENGTH | CRYPT_EXPORTABLE, &hKey) )
      {
                MyOutputInfoMessage( "一个会话密钥已经被创建" );
      }
      else
      {
                HandleError("CryptGenKey 创建会话密钥失败");
      }

      // 创建交换密钥
      if( CryptGenKey( hCryptProv, AT_KEYEXCHANGE, 0, &hXchgKey ) )
      {
                MyOutputInfoMessage( "交换密钥对已经创建" );
      }
      else
      {
                HandleError("在试图创建交换密钥时出错");
      }

      // 确定密钥数据块长度,并分配空间.
      if( CryptExportKey( hKey, hXchgKey,SIMPLEBLOB, 0, NULL, &dwKeyBlobLen ) )
      {
                char cInfo[ MAX_INFO_LENGTH ];
                memset( cInfo, 0, sizeof( cInfo ) );
                sprintf_s( cInfo, sizeof( cInfo ) - 1, "此密钥块的长度是 %d 字节", dwKeyBlobLen );
                MyOutputInfoMessage( cInfo );
      }
      else
      {
                HandleError( "计算密钥数据块长度出错" );
      }

      if (pbKeyBlob =(BYTE *)malloc( dwKeyBlobLen ) )
      {
                MyOutputInfoMessage( "已经问此密钥块分配了内存" );
      }
      else
      {
                HandleError("所需内存不够");
      }

      // 导出会话密钥到简单密钥数据块中.
      if( CryptExportKey( hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwKeyBlobLen ) )
      {
                MyOutputInfoMessage( "此会话密钥已经被导出" );
      }
      else
      {
                HandleError("CryptExportKey 导出会话密钥到简单密钥数据块中失败");
      }

      //释放交换密钥句柄.
      if( hXchgKey )
      {
                if(!(CryptDestroyKey(hXchgKey)))
                {
                        HandleError("CryptDestroyKey 释放交换密钥句柄失败");
                }
                else
                        MyOutputInfoMessage( "释放交换密钥句柄成功" );

                hXchgKey = 0;
      }

      // 写密钥块长度到目标文件.
      fwrite( &dwKeyBlobLen, sizeof(DWORD), 1, hDestination );
      if( ferror( hDestination ) )
      {
                HandleError("写密钥块长度出错");
      }
      else
      {
                MyOutputInfoMessage( "密钥块长度已经被写入" );
      }

      //写密钥块数据到目标文件.
      fwrite( pbKeyBlob, 1, dwKeyBlobLen, hDestination );
      if( ferror( hDestination ) )
      {
                HandleError( "写密钥数据出错" );
      }
      else
      {
                MyOutputInfoMessage( "此密钥块数据已经被写入目标文件" );
      }

      // 释放内存空间.
      free(pbKeyBlob);

      //返回创建的会话密钥
      return hKey;
}

BOOL MyEncryptFile( char* szSource, char* szDestination, char* szPassword )
{
      FILE *hSource;
      FILE *hDestination;

      HCRYPTPROV hCryptProv;
      HCRYPTKEY hKey;

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

      char cInfo[ MAX_INFO_LENGTH ] = { 0 };

      // 打开原文文件.
      hSource = fopen( szSource,"rb" );
      if ( hSource )
      {
                memset( cInfo, 0, sizeof( cInfo ) );
                sprintf_s( cInfo, sizeof( cInfo ) - 1, "原文文件 %s 已经打开", szSource );
                MyOutputInfoMessage( cInfo );
      }
      else
      {
                HandleError("打开原文文件出错!");
      }

      // 打开目标文件.
      hDestination = fopen(szDestination,"w+");
      if( hDestination )
      {
                memset( cInfo, 0, sizeof( cInfo ) );
                sprintf_s( cInfo, sizeof( cInfo ) - 1, "目标文件 %s 已经打开", szDestination );
                MyOutputInfoMessage( cInfo );
      }
      else
      {
                MyOutputInfoMessage( "文件不存在,创建文件" );
      }

      //获取加密服务者句柄
      hCryptProv = GetCryptProv();

      // 创建会话密钥.
      if( !szPassword || strcmp(szPassword,"")==0 )
      {
                // 当输入密码为空时,则创建随机的加密密钥,并导出创建的密钥保存到文件中.
                hKey = GenKeyByRandom( hCryptProv, hDestination);
      }
      else
      {
                hKey = GetKeyByPassword( hCryptProv, szPassword);
      }

      // 因为加密算法按ENCRYPT_BLOCK_SIZE 大小块加密,所以被加密的
      // 数据长度必须是ENCRYPT_BLOCK_SIZE 的整数倍。下面计算一次加密的
      // 数据长度。
      dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;

      // 确定加密后密文数据块大小. 若是分组密码模式,则必须有容纳额外块的空间
      if( ENCRYPT_BLOCK_SIZE > 1 )
                dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
      else
                dwBufferLen = dwBlockLen;

      // 分配内存空间.
      if( pbBuffer = (BYTE*)malloc( dwBufferLen ) )
      {
                MyOutputInfoMessage( "已经为缓冲区分配了内存" );
      }
      else
      {
                HandleError( "所需内存不够" );
      }

      // 循环加密 原文件
      while( !feof(hSource) )
      {
                // 每次从原文件中读取dwBlockLen字节数据.
                dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
                if( ferror(hSource) )
                {
                        HandleError("读取明文文件出错");
                }

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

                // 把加密后数据写到密文文件中
                fwrite(pbBuffer, 1, dwCount, hDestination);
                if( ferror(hDestination) )
                {
                        HandleError("写入密文时出错");
                }

      }

      // 关闭文件
      if( hSource )
      {
                if(fclose(hSource))
                {
                        HandleError("关闭源文文件出错!");
                }
      }

      if( hDestination )
      {
                if(fclose(hDestination))
                {
                        HandleError("关闭目标文件出错!");
                }
      }

      // 释放内存空间.
      if(pbBuffer)
                free(pbBuffer);

      // 销毁会话密钥
      if ( hKey )
      {
                if( !CryptDestroyKey( hKey ) )
                {
                        HandleError("CryptDestroyKey 会话密钥失败");
                }
      }

      // 释放CSP句柄
      if ( hCryptProv )
      {
                if( !CryptReleaseContext(hCryptProv, 0) )
                {
                        HandleError("CryptReleaseContext 释放CSP句柄失败");
                }
      }

      return(TRUE);
}

HCRYPTKEY GenKeyFromFile(HCRYPTPROV hCryptProv,FILE* hSource)
{
      HCRYPTKEY hKey;

      PBYTE pbKeyBlob;
      DWORD dwKeyBlobLen;

      //从密文文件中获取密钥数据块长度,并分配内存空间.
      fread(&dwKeyBlobLen, sizeof(DWORD), 1, hSource);
      if(ferror(hSource) || feof(hSource))
      {
                HandleError("读取密文文件中密钥数据块长度出错!");
      }
      if(!(pbKeyBlob = (BYTE *)malloc(dwKeyBlobLen)))
      {
                HandleError("内存分配出错.");
      }
      //--------------------------------------------------------------------
      // 从密文文件中获取密钥数据块

      fread(pbKeyBlob, 1, dwKeyBlobLen, hSource);
      if(ferror(hSource) || feof(hSource))
      {
                HandleError("读取密文文件中密钥数据块出错!\n");
      }
      //--------------------------------------------------------------------
      // 导入会话密钥到 CSP.
      if(!CryptImportKey(
                hCryptProv,
                pbKeyBlob,
                dwKeyBlobLen,
                0,
                0,
                &hKey))
      {
                HandleError("Error during CryptImportKey!");
      }

      if(pbKeyBlob)
                free(pbKeyBlob);

      //返回导出的会话密钥
      return hKey;
}

BOOL MyDecryptFile( char* szSource,      char* szDestination, char* szPassword)
{
      //--------------------------------------------------------------------
      // 局部变量申明与初始化.
      FILE *hSource;
      FILE *hDestination;

      HCRYPTPROV hCryptProv;
      HCRYPTKEY hKey;

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

      BOOL status = FALSE;

      //--------------------------------------------------------------------
      // 打开密文文件.
      if(!(hSource = fopen(szSource,"rb")))
      {
                HandleError("打开密文文件出错!");
      }
      //--------------------------------------------------------------------
      // 打开目标文件,用于存储解密后的数据.
      hDestination = fopen( szDestination,"w+" );
      if( !hDestination )
      {
                HandleError("打开明文文件出错!");
      }

      //获取加密服务者句柄
      hCryptProv = GetCryptProv();

      //获取或创建会话密钥
      if(!szPassword|| strcmp(szPassword,"")==0 )
      {
                //--------------------------------------------------------------------
                //从密文文件导入保存的会话密钥

                hKey = GenKeyFromFile( hCryptProv,hSource);

      }
      else
      {
                //--------------------------------------------------------------------
                // 通过输入密码重新创建会话密钥.

                hKey = GetKeyByPassword( hCryptProv, szPassword);
      }

      // 计算一次解密的数据长度,它是ENCRYPT_BLOCK_SIZE 的整数倍

      dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
      dwBufferLen = dwBlockLen;

      //--------------------------------------------------------------------
      // 分配内存空间.

      if(!(pbBuffer = (BYTE *)malloc(dwBufferLen)))
      {
                HandleError("所需内存不够!\n");
      }
      //--------------------------------------------------------------------
      // 解密密文文件,解密后数据保存在目标文件

      do {
                //--------------------------------------------------------------------
                // 每次从密文文件中读取dwBlockLen字节数据.

                dwCount = fread(
                        pbBuffer,
                        1,
                        dwBlockLen,
                        hSource);
                if(ferror(hSource))
                {
                        HandleError("读取密文文件出错!");
                }
                //--------------------------------------------------------------------
                // 解密 数据
                if(!CryptDecrypt(
                        hKey,
                        0,
                        feof(hSource),
                        0,
                        pbBuffer,
                        &dwCount))
                {
                        HandleError("Error during CryptDecrypt!");
                }
                //--------------------------------------------------------------------
                // 把解密后的数据写入目标文件中.

                fwrite(
                        pbBuffer,
                        1,
                        dwCount,
                        hDestination);
                if(ferror(hDestination))
                {
                        HandleError("Error writing plaintext!");
                }
      }         while(!feof(hSource));

      status = TRUE;

      //--------------------------------------------------------------------
      // 关闭文件
      if(hSource)
      {
                if(fclose(hSource))
                        HandleError("关闭原文件出错");
      }
      if(hDestination)
      {
                if(fclose(hDestination))
                        HandleError("关闭目标文件出错");
      }

      //--------------------------------------------------------------------
      // 释放内存空间

      if(pbBuffer)
                free(pbBuffer);

      //--------------------------------------------------------------------
      // 销毁会话密钥

      if(hKey)
      {
                if(!(CryptDestroyKey(hKey)))
                        HandleError("Error during CryptDestroyKey");
      }

      //--------------------------------------------------------------------
      // 释放CSP句柄
      if(hCryptProv)
      {
                if(!(CryptReleaseContext(hCryptProv, 0)))
                        HandleError("Error during CryptReleaseContext");
      }

      return status;
} // end Decryptfile

#endif
其中MyOutputInfoMessage函数为我自己定义的一个打印Log的函数,在一个ListBox上输出所有的提示信息,不然用MessageBox太恶心了,这点大家可以用别的方式实现,就不贴MFC部分的代码了。
   
本文固定链接: http://www.cppfans.org/985.html | C++爱好者博客™

页: [1]
查看完整版本: [加密解密]使用CryptoAPI进行文件加密解密——源码