密码引擎-加密API研究
目录
一、微软的CryptoAPI加密技术
微软的CryptoAPI是PKI推荐使用的加密API。其功能是为应用程序开发者提供在Win32环境下使用加密、验证等安全服务时的标准加密接口。CryptoAPI处于应用程序和CSP之间
CryptoAPI共有五部分组成:简单消息函数、低层消息函数、基本加密函数、证书编解码函数和证书库管理函数。前三者可用于对敏感信息进行加密或签名处理,可保证网络传输信心的私有性;后两者通过对证书的使用,可保证网络信息交流中的认证性。
-
基本加密函数:为开发加密应用程序提供了足够灵活的空间。所有CSP 的通讯都是通过这些函数。一个CSP 是实现所有加密操作的独立模块。在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作。如果使用多于一个以上的CSP,在加密函数调用中就要指定所需的CSP。基本加密函数包含了以下几种:
-
服务提供者函数:应用程序使用服务提供者函数来连接和断开一个CSP。
-
密钥的产生和交换函数:密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。
-
编码/解码函数:用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
-
数据加密/解密函数:这些函数支持数据的加密/解密操作。CryptEncrypt 和CryptDecrypt 要求在被调用前指定一个密钥。这个密钥可以由CryptGenKey、CryptDeriveKey 或CryptImportKey 产生。创建密钥时要指定加密算法。CryptSetKeyParam 函数可以指定额外的加密参数。
-
哈希和数字签名函数:这些函数在应用程序中完成计算哈希、创建和校验数字签名。
-
-
证书和证书库函数:这组函数管理、使用和取得证书、证书撤销列表和证书信任列表。这些函数可以分成一下几组:
-
证书库函数:一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了对库获得、枚举、验证和使用证书库里的信息。
-
-
- 维护函数:
-
- 证书函数:下列函数是针对于证书的。大多数函数都是处理CRL 和CTL 的。
-
- 证书撤销列表函数:
-
-
证书信任列表函数:
-
-
-
扩展属性函数:
-
-
证书验证函数:证书验证是通过CTL 和证书列表进行的。
-
使用CTL 的函数:
-
-
-
证书链验证函数:
-
-
消息函数:CryptoAPI 消息函数包括两组——低级消息函数和简化消息函数。
-
低级消息函数直接和PKCS#7 消息工作。这些函数对传输的PKCS#7 数据进行编码,对接收到的PKCS#7 数据进行解码,并且对接收到的消息进行解密和验证。
-
-
- 简化消息函数是比较高级的函数,是对几个低级消息函数和证书函数的封装,用来执行指定任务。这些函数在完成一个任务时,减少了函数调用的数量,因此简化了CryptoAPI的使用。
-
辅助函数
-
数据管理函数
-
-
-
数据转换函数
-
-
-
增强密钥用法函数
-
-
-
密钥标示函数
-
-
-
证书库回调函数
-
-
-
OID 支持函数
-
-
-
远程对象恢复函数
-
-
-
PFX 函数
-
CSP:真正实行加密的独立模块,既可以由软件实现也可以由硬件实现。但是必须符合CryptoAPI接口的规范。
创建密钥容器,得到CSP句柄
每一个CSP都有一个名字和一个类型,并且名字保证唯一。所以可以通过名字和类型得到一个CSP。然而,要想加密肯定需要密钥,密钥放在密钥容器。密钥容器并不是一开始就存在的,需要用户去创建。
下面是创建容器的代码:
if(CryptAcquireContext( &hCryptProv, // 返回CSP句柄 UserName, // 密码容器名 NULL, // NULL时使用默认CSP名(微软RSA Base Provider) PROV_RSA_FULL, // CSP类型 0)) // Flag values { //以UserName为名的密钥容器存在,那么我们已经得到了CSP的句柄 printf("A crypto context with the %s key container \n", UserName); printf("has been acquired.\n\n"); } else //如果密钥容器不存在,我们需要创建这个密钥容器 { if(CryptAcquireContext( &hCryptProv, UserName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) //创建以UserName为名的密钥容器 { //创建密钥容器成功,并得到CSP句柄 printf("A new key container has been created.\n"); } else { HandleError("Could not create a new key container.\n"); } } // End of else
代码实现:
#include "tool.h" #include <windows.h> #include <wincrypt.h> #include <stdio.h> #include <QDebug> BOOL CalHash(unsigned char* src, unsigned char* hash, int len) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。 LPCTSTR pszContainerName = TEXT("Hash");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) { printf("CSP Create Success\n"); }else //不成功的处理段 { if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); }else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTHASH hHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash)) { printf("CryptCreateHash fail!"); return FALSE; } if(!CryptHashData( hHash, (BYTE*)src, len, 0 )) { printf("CryptHashData fail!"); return FALSE; } DWORD dwHashLen= 16; if(!CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)hash, &dwHashLen, 0)) { printf("CryptGetHashParam fail!"); return FALSE; } CryptDestroyHash(hHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Encrypt(unsigned char* src, unsigned char* dest, char* passwd ) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。 LPCTSTR pszContainerName = TEXT("Encrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器 { printf("A new key container has been created.\n"); } } HCRYPTHASH hCryptHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash)) { printf("CryptCreateHash fail!"); return FALSE; } //用输入的密码作哈稀散列 if(!CryptHashData( hCryptHash,lse { printf("CSP Create Fail\n"); return FALSE; } } (BYTE*)passwd, strlen(passwd), 0)) { printf("CryptHashData fail!"); return FALSE; } //用哈稀散列生成会话密钥 HCRYPTKEY hCryptKey; if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey )) { printf("CryptDeriveKey fail!"); return FALSE; } //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128 unsigned char buf[16]; DWORD lenght = 16; memcpy(buf,src,16); if(!CryptEncrypt(hCryptKey, NULL,//如果数据同时进行散列和加密,这里传入一个散列对象 0,//如果是最后一个块为TRUE 0, (BYTE*)buf,//输入被加密的数据,输出加密数据 &lenght,//输入输入数据长度,输出加密后数据长度 16 )) { qDebug() << "errre"; qDebug() << GetLastError(); } memcpy(dest,buf,16); CryptDestroyHash(hCryptHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Decrypt(unsigned char* src, unsigned char* dest, char* passwd ) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_AES, //确定密钥的类型 0)) //常设为0,还有些其他的类型,请看MSDN { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); } else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTHASH hCryptHash; //创建hash对象 if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hCryptHash)) { printf("CryptCreateHash fail!"); return FALSE; } //用输入的密码作哈稀散列 if(!CryptHashData( hCryptHash, (BYTE*)passwd, strlen(passwd), 0 )) { printf("CryptHashData fail!"); return FALSE; } //用哈稀散列生成会话密钥 HCRYPTKEY hCryptKey; if(!CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey )) { printf("CryptDeriveKey fail!"); return FALSE; } CryptDestroyHash(hCryptHash); //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128 unsigned char buf[16]; DWORD lenght = 16; memcpy(buf,src,16); CryptDecrypt(hCryptKey, NULL,//如果数据同时进行散列和加密,这里传入一个散列对象 0,//如果是最后一个块为TRUE 0, (BYTE*)buf,//输入被加密的数据,输出加密数据 &lenght//输入输入数据长度,输出加密后数据长度 ); memcpy(dest,buf,16); CryptReleaseContext(hCryptProv, 0); return TRUE; } BOOL Sign(unsigned char* src, unsigned char* dest, int len) { HCRYPTPROV hCryptProv; //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。 LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字, if(CryptAcquireContext( //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。 &hCryptProv, //指向一个CSP模块句柄指针,里面用指定的容器 pszContainerName, //指定容器的名称 NULL, //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块 PROV_RSA_FULL, //确定密钥的类型 0)) //常设为0,还有些其他的类型,请看MSDN { printf("CSP Create Success\n"); } else { //不成功的处理段 if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器 { if(CryptAcquireContext(&hCryptProv, pszContainerName,NULL,PROV_RSA_AES, CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。 { printf("A new key container has been created.\n"); } else { printf("CSP Create Fail\n"); return FALSE; } } } HCRYPTKEY hKey; //创建一个密钥句柄 if(CryptGetUserKey( // CryptGetUserKey是获取一个密钥句柄的函数,成功返回TRUE hCryptProv, //指定容器的CSP模块句柄 AT_SIGNATURE, //指定私钥的类型 &hKey)) //原来接收获取的密钥句柄 { printf("A signature key is available.\n"); } else { printf("No signature key is available.\n"); if(GetLastError() == NTE_NO_KEY) // NTE_NO_KEY意味着密钥不存在,下面就生成一个密钥 { printf("The signature key does not exist.\n"); printf("Create a signature key pair.\n"); if(CryptGenKey( // CryptGenKey生成一个密钥 hCryptProv, //指定CSP模块的句柄 AT_SIGNATURE, //对于公钥密码系统,生成一个私钥和一个公钥,这个参数指定了这个密钥是公钥,于是生成了一个密码对。如果不是公钥系统,则指定了密码算法,具体看MSDN。 0, //指定了生成密钥的类型,这个参数的说明挺多的,想获取更为详尽的资料请看MSDN。 &hKey)) { printf("Created a signature key pair.\n"); } else { printf("Created a signature key fail.\n"); return FALSE; } } else { printf("An error other than NTE_NO_KEY "); return FALSE; } } HCRYPTHASH hHash; if(!CryptHashData( hHash, (BYTE*)src, len, 0 )) { printf("CryptHashData fail!"); return FALSE; } DWORD lenght = 32; if(!CryptSignHash(hHash,AT_SIGNATURE,NULL,CRYPT_TYPE2_FORMAT,(BYTE*)dest,&lenght)) { printf("CryptSignHash fail!"); return FALSE; } if(hKey) //将密钥句柄销毁 { if(!(CryptDestroyKey(hKey))) { printf("Error during CryptDestroyKey."); } hKey = NULL; } CryptDestroyHash(hHash); CryptReleaseContext(hCryptProv, 0); return TRUE; } char *qstoc(const QString Qstr) { QByteArray ba = Qstr.toLatin1(); char *c_str; c_str = (char *)malloc(ba.length() + 1); memset(c_str, 0, ba.length()); memcpy(c_str, ba.data(), ba.length()); c_str[ba.length()] = '\0'; return c_str; }
使用CryptoAPI加密解密
windows crypto API提供了对称加密和非对称加密,并且提供了各种加密、解密的算法,要使用相应的算法进行加密解密,只需要对生成密钥的函数的相关参数改变一下即可。
二、PKCS#11及CSP接口标准
PKCS#11(简称P11)是针对密码设备的接口指令标准。P11模型中重要的概念之一是slot,也称为槽。一个slot为一个密码设备对象。某个打开的slot会话称之为session。Session之间存在不同的验证权限。而同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。另外一个重要的概念是对象。P11中支持几种重要的对象,如公钥、私钥、对称密钥,数据对象等。
PKCS#11创建和支持下列对象:
PKCS#11的对象可根据其生命期长短的不同分成两大类:
-
持久存储的类对象,这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象;
-
会话对象,这类对象只存在于运行时建立的特定会话(Session对象)当中,一旦会话结束,这类对象也跟着被删除。
决定对象生命期的模板属性是CKA_TOKEN,这是个布尔值,所有的对象都有这一属性。当该值为TRUE时,该对象将被保存到Key内的存储空间,否则,该对象保存在会话空间中,当会话结束后,该对象即销毁。
P11标准颁发了70余条指令。其中部分指令简介如下表:
PKCS#11架构
三、GMT 0016-2012
这个标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。
层次关系:智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图:
设备的应用结构:一个设备中存在设备认证密钥和多个应用,应用之间相互独立。设备的逻辑结构如下图:
设备管理系列函数:
访问控制系列函数
应用管理函数
容器管理系列函数
密码服务系列函数
四、GMT 0018-2012
本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。
范围:本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准,适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。
密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置:在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终瑞等设备组成,通过本标准规定的密码设备应用接口向通用密码服务层提供基础密码服务。如下图:
基础密码服务包括密钥生成、单一的密码运算、文件管理等服务。
本标准采用C语言描述接口函数,无特别说明时,函数中参数的长度单位均为字节数。
设备管理类函数:
-
打开设备:SDF_OpenDevice
-
关闭设备:SDF_CloseDevice
-
创建会话:SDF_OpenSession
-
关闭会话:SDF_CloseSession
-
获取设备信息:SDF_GetDeviceInfo
-
产生随机数:SDF_GenerateRandom
-
获取私钥使用权限:SDF_GetPrivateKeyAccessRight
-
释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight
密钥管理类函数:
-
导出 RSA 签名公钥:SDF_ExportSignPublicKey_RSA
-
导出 RSA 加密公钥:SDF_ExportEncPublicKey_RSA
-
产生RSA非对称密钥对并输出:SDF_GenerateKeyPair_RSA
-
生成会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK_RSA
-
生成会话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_RSA - 导人会话密钥并用内部RSA私钥解密:SDF_ImportKeyWithISK_RSA
-
基于 RSA 算法的数宇倍封转换:SDF_ExchangeDigitEnvelopeBaseOnRSA
-
导出 ECC 签名公钥:SDF_ExportSignPublicKey_ECC
-
导出 ECC 加密公钥:SDF_ExportEncPublicKey_ECC
-
产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ECC
-
生成会话密钥并用内部ECC公钥加密输岀:SDF_GenerateKeyWithIPK_ECC - 生成会话密钥并用外部ECC公钥加密输出:SDF_GenerateKeyWithEPK_ECC
-
导入会话密钥并用内部ECC私钥解密:SDFJmportKeyWithlSKJECC
-
生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
-
计算会话密钥:SDF_GenerateKey WithECC
-
产生协商数据并计算会话密钥:SDF—GenerateAgreementDataAndKeyWithECC
-
基于 ECC算法的数字信封转换:SDF_ExchangeDigitEnvelopeBaseOnECC
-
生成会话密钥并用密钥加密密钥加密输出: SDF_GenerateKeyWithKEK
-
导入会话密钥并用密钥加密密钥解密:SDF_ImportKeyWithKEK
-
销毁会话密钥:SDF_DestroyKey
非对称算法运算类函数
-
内部公钥 RSA 运算:SDF_ExternalPublicKeyOperation_RSA
-
内部公钥 RSA 运算:SDF_InternalPublicKeyOperation_RSA
-
内部私钥 RSA 运算:SDF_InternalPrivateKeyOperation_RSA
-
外部密钥 ECC 验证:SDF_ExternalVerify_ECC
-
内部密钥 ECC 签名:SDF_InternalSign_ECC
-
内部密钥 ECC 验证:SDF_InternalVerify_ECC
-
外部密钥 ECC 加密:SDF_ExternalEncrypt_ECC
对称算法运算类函数
-
对称加密:SDF_Encrypt
-
对称解密:SDF_Decrypt
-
计算MAC:SDF_CalculateMAC
杂凑运算类函数
-
杂凑运算初始化:SDF_HashInit
-
多包杂凑运算:SDF_HashUpdate
-
杂凑运算结束:SDF_HashFinal
安全要求
(1)基于本标准设计、开发的密码设备在密钥管理方面,应满足以下要求:
-
设备密钥的使用不对应用系统开放;
-
密钥必须用安全的方法产生并存储;
-
在任何时间、任何情况下,除公钥外的密钥均不能以明文形式出现在密码设备外;
-
密码设备内部存储的密钥应具备有效的密钥保护机制,防止解剖、探测和非法读取;
-
密码设备内部存储的密钥应具备权限控制机制,防止非法使用和导出。
(2)密码服务要求:
-
使用的密码算法应得到国家密码主管部门的批准;
-
使用国家密码主管部门认可的密码算法芯片;
-
本标准所列的所有接口函数均应能被应用系统任意调用。
(3)设备状态要求:
-
密码设备应具有初始和就绪两个状态;
-
未安装设备密钥的密码设备应处于初始状态,已安装设备密钥的密码设备应处于就绪状态;
-
在初始状态下,除可读取设备信息、设备密钥的生成或恢复操作外,不能执行任何操作,生成或恢复设备密钥后,密码设备处于就绪状态;
-
在就绪状态下,除设备密钥的生成或恢复操作外,应能执行任何操作;
-
在就绪状态下进行的密钥操作,设备操作员应经过密码设备的认证。
(4)其他要求:
-
密码设备应有安全机制和措施,保证密钥在生成、安装、导入、存储、备份.恢复及销毁整个生存期间的安全,此安全机制可由设备厂商自行设计实现。
五、龙脉GM3000Key调用
(一)Windows crypticAPI实现加密
#include <stdio.h> #define _WIN64 #include <windows.h> #include <wincrypt.h> #define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) #define KEYLENGTH 0x00800000 void HandleError(char *s); #define ENCRYPT_ALGORITHM CALG_RC4 #define ENCRYPT_BLOCK_SIZE 8 BOOL EncryptFile( PCHAR szSource, PCHAR szDestination, PCHAR szPassword); int main() { CHAR szSource[100]; CHAR szDestination[100]; CHAR szPassword[100]; printf("Encrypt a file. \n\n"); printf("Enter the name of the file to be encrypted: "); scanf("%s",szSource); printf("Enter the name of the output file: "); scanf("%s",szDestination); printf("Enter the password:"); scanf("%s",szPassword); if(EncryptFile(szSource, szDestination, szPassword)) { printf("Encryption of the file %s was a success. \n", szSource); printf("The encrypted data is in file %s.\n",szDestination); } else { HandleError("Error encrypting file!"); } } extern BOOL EncryptFile( PCHAR szSource, PCHAR szDestination, PCHAR szPassword) { FILE *hSource; FILE *hDestination; HCRYPTPROV hCryptProv; HCRYPTKEY hKey; HCRYPTHASH hHash; PBYTE pbBuffer; DWORD dwBlockLen; DWORD dwBufferLen; DWORD dwCount; if(hSource = fopen(szSource,"rb")) { printf("The source plaintext file, %s, is open. \n", szSource); } else { HandleError("Error opening source plaintext file!"); } if(hDestination = fopen(szDestination,"wb")) { printf("Destination file %s is open. \n", szDestination); } else { HandleError("Error opening destination ciphertext file!"); } if(CryptAcquireContext( &hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) { printf("A cryptographic provider has been acquired. \n"); } else { if(CryptAcquireContext( &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))//创建密钥容器 { //创建密钥容器成功,并得到CSP句柄 printf("A new key container has been created.\n"); } else { HandleError("Could not create a new key container.\n"); } } if(CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash)) { printf("A hash object has been created. \n"); } else { HandleError("Error during CryptCreateHash!\n"); } if(CryptHashData( hHash, (BYTE *)szPassword, strlen(szPassword), 0)) { printf("The password has been added to the hash. \n"); } else { HandleError("Error during CryptHashData. \n"); } if(CryptDeriveKey( hCryptProv, ENCRYPT_ALGORITHM, hHash, KEYLENGTH, &hKey)) { printf("An encryption key is derived from the password hash. \n"); } else { HandleError("Error during CryptDeriveKey!\n"); } CryptDestroyHash(hHash); hHash = NULL; dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; if(ENCRYPT_BLOCK_SIZE > 1) dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE; else dwBufferLen = dwBlockLen; if(pbBuffer = (BYTE *)malloc(dwBufferLen)) { printf("Memory has been allocated for the buffer. \n"); } else { HandleError("Out of memory. \n"); } do { dwCount = fread(pbBuffer, 1, dwBlockLen, hSource); if(ferror(hSource)) { HandleError("Error reading plaintext!\n"); } if(!CryptEncrypt( hKey, 0, feof(hSource), 0, //保留 pbBuffer, //输入被加密数据,输出加密后的数据 &dwCount, //输入被加密数据实际长度,输出加密后数据长度 dwBufferLen)) //pbBuffer的大小。 { HandleError("Error during CryptEncrypt. \n"); } fwrite(pbBuffer, 1, dwCount, hDestination); if(ferror(hDestination)) { HandleError("Error writing ciphertext."); } } while(!feof(hSource)); if(hSource) fclose(hSource); if(hDestination) fclose(hDestination); if(pbBuffer) free(pbBuffer); if(hKey) CryptDestroyKey(hKey); if(hHash) CryptDestroyHash(hHash); if(hCryptProv) CryptReleaseContext(hCryptProv, 0); return(TRUE); } void HandleError(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); }
(二)Windows crypticAPI实现解密
#include <stdio.h> #define _WIN64 #include <windows.h> #include <wincrypt.h> #define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) #define KEYLENGTH 0x00800000 void HandleError(char *s); #define ENCRYPT_ALGORITHM CALG_RC4 #define ENCRYPT_BLOCK_SIZE 8 BOOL DecryptFile( PCHAR szSource, PCHAR szDestination, PCHAR szPassword); int main() { CHAR szSource[100]; CHAR szDestination[100]; CHAR szPassword[100]; printf("Decrypt a file. \n\n"); printf("Enter the name of the file to be decrypted: "); scanf("%s",szSource); printf("Enter the name of the output file: "); scanf("%s",szDestination); printf("Enter the password:"); scanf("%s",szPassword); if(!DecryptFile(szSource, szDestination, szPassword)) { printf("\nError decrypting file. \n"); } else { printf("\nDecryption of file %s succeeded. \n", szSource); printf("The decrypted file is %s .\n",szDestination); } } extern BOOL DecryptFile( PCHAR szSource, PCHAR szDestination, PCHAR szPassword) { FILE *hSource; FILE *hDestination; HCRYPTPROV hCryptProv; HCRYPTKEY hKey; HCRYPTHASH hHash; PBYTE pbBuffer; DWORD dwBlockLen; DWORD dwBufferLen; DWORD dwCount; BOOL status = FALSE; if(!(hSource = fopen(szSource,"rb"))) { HandleError("Error opening ciphertext file!"); } if(!(hDestination = fopen(szDestination,"wb"))) { HandleError("Error opening plaintext file!"); } if(!CryptAcquireContext( &hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) { HandleError("Error during CryptAcquireContext!"); } if(!CryptCreateHash( hCryptProv, CALG_MD5, 0, 0, &hHash)) { HandleError("Error during CryptCreateHash!"); } if(!CryptHashData( hHash, (BYTE *)szPassword, strlen(szPassword), 0)) { HandleError("Error during CryptHashData!"); } if(!CryptDeriveKey( hCryptProv, ENCRYPT_ALGORITHM, hHash, KEYLENGTH, &hKey)) { HandleError("Error during CryptDeriveKey!"); } CryptDestroyHash(hHash); hHash = 0; dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; dwBufferLen = dwBlockLen; if(!(pbBuffer = (BYTE *)malloc(dwBufferLen))) { HandleError("Out of memory!\n"); } do { dwCount = fread( pbBuffer, 1, dwBlockLen, hSource); if(ferror(hSource)) { HandleError("Error reading ciphertext!"); } 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) fclose(hSource); if(hDestination) fclose(hDestination); if(pbBuffer) free(pbBuffer); if(hKey) CryptDestroyKey(hKey); if(hHash) CryptDestroyHash(hHash); if(hCryptProv) CryptReleaseContext(hCryptProv, 0); return status; } void HandleError(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 HandleError
(三)实现MD5
#include <stdio.h> #define _WIN64 #include <windows.h> #include <wincrypt.h> #define CHECK_NULL_RET(bCondition) if (!bCondition) goto Exit0 #define BUFSIZE 1024 #define MD5LEN 16 BOOL GetContentMD5( BYTE *pszFilePath, BOOL bFile, BOOL bUpperCase, TCHAR *pszResult, DWORD &dwStatus) { BOOL bResult = FALSE; HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; HANDLE hFile = NULL; BYTE rgbFile[BUFSIZE]; DWORD cbRead = 0; BYTE rgbHash[MD5LEN]; DWORD cbHash = 0; CHAR rgbDigitsL[] = "0123456789abcdef"; CHAR rgbDigitsU[] = "0123456789ABCDEF"; CHAR *rgbDigits = bUpperCase ? rgbDigitsU : rgbDigitsL; TCHAR szResult[MD5LEN*2+1] = {0}; dwStatus = 0; bResult = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); CHECK_NULL_RET(bResult); bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash); CHECK_NULL_RET(bResult); if (bFile) { hFile = CreateFile((TCHAR *)pszFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); CHECK_NULL_RET(!(INVALID_HANDLE_VALUE == hFile)); while (bResult = ReadFile(hFile, rgbFile, BUFSIZE, &cbRead, NULL)) { if (0 == cbRead) { break; } bResult = CryptHashData(hHash, rgbFile, cbRead, 0); CHECK_NULL_RET(bResult); } } else { bResult = CryptHashData(hHash, pszFilePath, strlen((CHAR *)pszFilePath), 0); CHECK_NULL_RET(bResult); } cbHash = MD5LEN; if (bResult = CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) { TCHAR szTmpBuff[3] ; for (DWORD i = 0; i < cbHash; i++) { sprintf (szTmpBuff, TEXT("%c%c"), rgbDigits[rgbHash[i]>>4], rgbDigits[rgbHash[i] & 0xf]); lstrcat(szResult, szTmpBuff); } bResult = TRUE; } Exit0: dwStatus = GetLastError(); CryptDestroyHash(hHash); CryptReleaseContext(hProv, 0); CloseHandle(hFile); lstrcpy(pszResult, szResult); return bResult; } int main(int argc, char* argv[]) { DWORD dwStatus = 0; TCHAR szResult[MD5LEN*2+1] = {0}; TCHAR szFilePath[] = TEXT("C:\Users\Eulalia\Desktop\信息安全系统设计与实现下\code\1234.dll"); CHAR szContent[] = "explorer.exe"; GetContentMD5((BYTE *)szFilePath, TRUE, TRUE, szResult, dwStatus); MessageBox(NULL, szResult, TEXT("该文件的MD5"), MB_OK); return 0; }