数字信封包含被加密的内容和被加密的用于加密该内容的密钥。虽然经常使用接收方的公钥来加密“加密密钥”,但这并不是必须的,也可以使用发送方和接收方预共享的对称密钥来加密。当接收方收到数字信封时,先用私钥或预共享密钥解密,得到“加密密钥”,再用该密钥解密密文,获得原文。数字信封技术使用两层加密体系。
数字信封是公钥密码体制在实际中的一个应用,是用加密技术来保证只有规定的特定收信人才能阅读通信的内容。 在数字信封中,信息发送方采用对称密钥来加密信息内容,然后将此对称密钥用接收方的公开密钥来加密(这部分称数字信封)之后,将它和加密后的信息一起发送给接收方,接收方先用相应的私有密钥打开数字信封,得到对称密钥,然后使用对称密钥解开加密信息。这种技术的安全性相当高。数字信封主要包括数字信封打包和数字信封拆解,数字信封打包是使用对方的公钥将加密密钥进行加密的过程,只有对方的私钥才能将加密后的数据(通信密钥)还原;数字信封拆解是使用私钥将加密过的数据解密的过程。
密钥的更换
数字信封的功能类似于普通信封,普通信封在法律的约束下保证只有收信人才能阅读信的内容;数字信封则采用密码技术保证了只有规定的接收人才能阅读信息的内容。数字信封中采用了对称密码体制和公钥密码体制。信息发送者首先利用随机产生的对称密码加密信息,再利用接收方的公钥加密对称密码,被公钥加密后的对称密码被称之为数字信封。在传递信息时,信息接收方若要解密信息,必须先用自己的私钥解密数字信封,得到对称密码,才能利用对称密码解密所得到的信息。这样就保证了数据传输的真实性和完整性。 在一些重要的电子商务交易中密钥必须经常更换,为了解决每次更换密钥的问题,结合对称加密技术和公开密钥技术的优点,它克服了秘密密钥加密中秘密密钥分发困难和公开密钥加密中加密时间长的问题,使用两个层次的加密来获得公开密钥技术的灵活性和秘密密钥技术高效性。信息发送方使用密码对信息进行加密,从而保证只有规定的收信人才能阅读信的内容。采用数字信封技术后,即使加密文件被他人非法截获,因为截获者无法得到发送方的通信密钥,故不可能对文件进行解密。
VC++实现代码如下,详细分析请见代码注释
#include <stdio.h> #include <windows.h> #include <wincrypt.h> #define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) void HandleError(char *s); PCCERT_CONTEXT GetRecipientCert(HCERTSTORE hCertStore); void ByteToStr( DWORD cb, void* pv, LPSTR sz); BOOL DecryptMessage( BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, HCRYPTPROV hCryptProv, HCERTSTORE hStoreHandle); HCRYPTPROV GetCryptProv(); void main() { //-------------------------------------------------------------------- // 变量申明与初始化 BYTE* pbContent = (BYTE*) "Security is our business.";// 待加密消息 DWORD cbContent = strlen((char *)pbContent)+1; // 消息长度 HCRYPTPROV hCryptProv; // CSP句柄 HCERTSTORE hStoreHandle; //证书库句柄 PCCERT_CONTEXT pRecipientCert; //接收证书 PCCERT_CONTEXT RecipientCertArray[1]; //接收证书列表 DWORD EncryptAlgSize; CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm; CRYPT_ENCRYPT_MESSAGE_PARA EncryptParams; DWORD EncryptParamsSize; BYTE* pbEncryptedBlob; DWORD cbEncryptedBlob; printf("即将从消息 %s 开始.\n",pbContent); printf("这个消息的长度是 %d 字节. \n", cbContent); //获取加密服务者句柄 hCryptProv = GetCryptProv(); //-------------------------------------------------------------------- // 打开系统证书库. if(hStoreHandle = CertOpenSystemStore( hCryptProv, "MY")) { printf(" MY 证书库已经打开. \n"); } else { HandleError( "获取证书库句柄出错."); } //-------------------------------------------------------------------- // 调用函数GetRecipientCert获取接收者证书 if(pRecipientCert = GetRecipientCert( hStoreHandle)) { printf("接收者的证书已经获取. \n"); } else { printf("No certificate with a CERT_KEY_CONTEXT_PROP_ID \n"); printf("property and an AT_KEYEXCHANGE private key available. \n"); printf("While the message could be encrypted, in this case, \n"); printf("it could not be decrypted in this program. \n"); printf("For more information, see the documentation for \n"); printf("CrypteEncryptMessage and CryptDecryptMessage.\n\n"); HandleError( "No Certificate with AT_KEYEXCHANGE key in store."); } //-------------------------------------------------------------------- // 创建接收证书列表.这里仅包含一个接收者证书 RecipientCertArray[0] = pRecipientCert; //-------------------------------------------------------------------- // 初始化算法结构. EncryptAlgSize = sizeof(EncryptAlgorithm); //-------------------------------------------------------------------- // 初始化算法结构为0. memset(&EncryptAlgorithm, 0, EncryptAlgSize); //-------------------------------------------------------------------- // 设置算法结构的必要的成员变量. EncryptAlgorithm.pszObjId = szOID_RSA_RC4; //-------------------------------------------------------------------- // 初始化CRYPT_ENCRYPT_MESSAGE_PARA 结构. EncryptParamsSize = sizeof(EncryptParams); memset(&EncryptParams, 0, EncryptParamsSize); EncryptParams.cbSize = EncryptParamsSize; EncryptParams.dwMsgEncodingType = MY_ENCODING_TYPE; EncryptParams.hCryptProv = hCryptProv; EncryptParams.ContentEncryptionAlgorithm = EncryptAlgorithm; //-------------------------------------------------------------------- // 调用 CryptEncryptMessage加密消息. //获取加密后数据长度 if(CryptEncryptMessage( &EncryptParams, 1, RecipientCertArray,//收件证书列表 pbContent, cbContent, NULL, &cbEncryptedBlob)) { printf("加密消息是 %d 字节. \n",cbEncryptedBlob); } else { HandleError( "获取加密后数据块长度失败."); } //-------------------------------------------------------------------- // 分配内存空间. if(pbEncryptedBlob = (BYTE*)malloc(cbEncryptedBlob)) { printf("已经为加密数据块分配了内存空间. \n"); } else { HandleError("加密时内存分配出错."); } //-------------------------------------------------------------------- // 加密消息 if(CryptEncryptMessage( &EncryptParams, 1, RecipientCertArray, pbContent, cbContent, pbEncryptedBlob, &cbEncryptedBlob)) { printf( "加密成功. \n"); } else { HandleError("加密失败."); } //-------------------------------------------------------------------- // 调用DecryptMessage解密消息 if(DecryptMessage( pbEncryptedBlob, cbEncryptedBlob, hCryptProv, hStoreHandle)) { printf("解密成功. \n"); } else { printf("解密失败. \n"); } //-------------------------------------------------------------------- //释放内存空间. CertFreeCertificateContext(pRecipientCert); if(CertCloseStore( hStoreHandle, CERT_CLOSE_STORE_CHECK_FLAG)) { printf("MY证书库顺利关闭. \n"); } else { printf("加密后证书库关闭 -- \n" "但是不是所有证书或证书撤消列表都被释放. \n"); } if(hCryptProv) { CryptReleaseContext(hCryptProv,0); printf("CSP句柄已经释放. \n"); } else { printf("CSP句柄为空. \n"); } } // End of main //-------------------------------------------------------------------- // 功能:解密用函数CryptDecryptMessage加密的消息. // 参数 // pbEncryptedBlob:待解密的消息 // cbEncryptedBlob:待解密消息长度 // hCryptProv: CSP句柄 // hStoreHandle: 已打开的证书库句柄 BOOL DecryptMessage( BYTE *pbEncryptedBlob, DWORD cbEncryptedBlob, HCRYPTPROV hCryptProv, HCERTSTORE hStoreHandle) { //-------------------------------------------------------------------- //申明与初始化变量 DWORD cbDecryptedMessage; char* EncryptedString = (char*)malloc(sizeof(char) *(cbEncryptedBlob * 2 +1)); HCERTSTORE CertStoreArray[] = {hStoreHandle}; CRYPT_DECRYPT_MESSAGE_PARA DecryptParams; DWORD DecryptParamsSize = sizeof(DecryptParams); BYTE* pbDecryptedMessage; LPSTR DecryptedString; BOOL fReturn = TRUE; ByteToStr( cbEncryptedBlob, pbEncryptedBlob, EncryptedString); //-------------------------------------------------------------------- //打印转化后的密文. printf("加密后的字符串是: \n%s\n",EncryptedString); //-------------------------------------------------------------------- // 初始化CRYPT_DECRYPT_MESSAGE_PARA结构. memset(&DecryptParams, 0, DecryptParamsSize); DecryptParams.cbSize = DecryptParamsSize; DecryptParams.dwMsgAndCertEncodingType = MY_ENCODING_TYPE; DecryptParams.cCertStore = 1; DecryptParams.rghCertStore = CertStoreArray;//证书库列表 //-------------------------------------------------------------------- // 解密消息数据. // 先调用CryptDecryptMessage确定解密后数据长度. if(CryptDecryptMessage( &DecryptParams, pbEncryptedBlob, cbEncryptedBlob, NULL, &cbDecryptedMessage, NULL)) { printf("解密后数据的长度是: %d.\n",cbDecryptedMessage); } else { HandleError( "获取解密后消息长度出错"); } //-------------------------------------------------------------------- // 分配空间 if(pbDecryptedMessage = (BYTE*)malloc( cbDecryptedMessage)) { printf("已经为解密消息分配了内存空间. \n"); } else { HandleError("解密时内存分配出错"); } //-------------------------------------------------------------------- // 调用 CryptDecryptMessage 解密数据. if(CryptDecryptMessage( &DecryptParams, pbEncryptedBlob, cbEncryptedBlob, pbDecryptedMessage, &cbDecryptedMessage, NULL)) { DecryptedString = (LPSTR) pbDecryptedMessage; printf("消息解密成功. \n"); printf("被解密的字符串是: %s\n",DecryptedString); } else { printf("解密此消息出错 \n"); printf("错误代码 %x \n",GetLastError()); fReturn = FALSE; } //-------------------------------------------------------------------- //释放内存空间 free(pbEncryptedBlob); free(pbDecryptedMessage); return fReturn; } // End of DecryptMessage //-------------------------------------------------------------------- // ByteToStr: 转换BYTE类型数组为字符串 //参数:cb[in] 需要转换的BYTE数组的长度 // pv[in] 需要转换的BYTE数组指针 // sz[out] 字符串指针 void ByteToStr( DWORD cb, void* pv, LPSTR sz) { BYTE* pb = (BYTE*) pv; DWORD i; int b; for (i = 0; i<cb; i++) { //pb的前4位转换为字符 b = (*pb & 0xF0) >> 4; *sz++ = (b <= 9) ? b + '0' : (b - 10) + 'A'; //pb的后4位转换为字符 b = *pb & 0x0F; *sz++ = (b <= 9) ? b + '0' : (b - 10) + 'A'; pb++; } *sz++ = 0; } //-------------------------------------------------------------------- // 功能:遍历证书库中的证书,才,找到则返回此证书. // 参数:hCertStore 证书库句柄 PCCERT_CONTEXT GetRecipientCert( HCERTSTORE hCertStore) { //-------------------------------------------------------------------- // 申明与初始化变量 PCCERT_CONTEXT pCertContext = NULL; BOOL fMore = TRUE; DWORD dwSize = 0; CRYPT_KEY_PROV_INFO* pKeyInfo = NULL; DWORD PropId = CERT_KEY_PROV_INFO_PROP_ID; //-------------------------------------------------------------------- // 寻找包含交换密钥对的证书 while(fMore && (pCertContext= CertFindCertificateInStore( hCertStore, // 证书库句柄. 0, // 编码类型. 0, // dwFindFlags. 说明查询标准,这里没有使用 CERT_FIND_PROPERTY, // 查询类型,决定进行何种类型的查询。这里使用证书的特定扩展属性 &PropId, // pvFindPara. 给出查询的特定值,这里为 扩展属性的标识符 pCertContext))) //证书句柄,第一次调用时为NULL ,之后为上一次返回的指针 { //------------------------------------------------------------- // 获取证书属性:密钥对信息 if(!(CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize))) { HandleError("获取密钥特性出错."); } //-------------------------------------------------------------- // 分配空间 if(pKeyInfo) free(pKeyInfo); if(!(pKeyInfo = (CRYPT_KEY_PROV_INFO*)malloc(dwSize))) { HandleError("为变量 pKeyInfo 分配内存出错."); } //-------------------------------------------------------------- // 获取密钥信息块. if(!(CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pKeyInfo, &dwSize))) { HandleError("第二次调用此函数失败."); } //------------------------------------------- // 检查密钥信息块是否包含交换密钥对. if(pKeyInfo->dwKeySpec == AT_KEYEXCHANGE) { fMore = FALSE; } } // End of while loop if(pKeyInfo) free(pKeyInfo); return (pCertContext); } // End of GetRecipientCert //获取加密提供者句柄 HCRYPTPROV GetCryptProv() { HCRYPTPROV hCryptProv; // 加密服务提供者句柄 //获取加密提供者句柄 if(CryptAcquireContext( &hCryptProv, // 加密服务提供者句柄 "ruanou", // 密钥容器名,这里使用登陆用户名 MS_ENHANCED_PROV, // 加密服务提供者 PROV_RSA_FULL, // 加密服务提供者类型,可以提供加密和签名等功能 0)) // 标志 { printf("加密服务提供者句柄获取成功!\n"); } else { //重新建立一个新的密钥集 if(!CryptAcquireContext(&hCryptProv, "ruanou", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET)) { HandleError("重新建立一个新的密钥集出错!"); } } return hCryptProv; } // HandleError:错误处理函数,打印错误信息,并退出程序 void HandleError(char *s) { printf("程序执行发生错误!\n"); printf("%s\n",s); printf("错误代码为: %x.\n",GetLastError()); printf("程序终止执行!\n"); exit(1); }