《ASCE1885的信息安全》のCryptoAPI---密钥的产生和交换函数
在公开密码算法的前提下,数据的安全取决于密钥。因此,密钥的产生、销毁、交换(分发)是数据保密工作中的重要部分。
CryptoAPI密钥产生和交换函数主要有生成密钥函数CryptGenKey、派生密钥函数CryptDeriveKey、销毁密钥函数CryptDestroyKey、复制密钥函数CryptDuplicateKey、导出密钥函数CryptExportKey、导入密钥函数CryptImportKey、获得密钥参数函数CryptGetKeyParam、设置密钥参数函数CryptSetKeyParam、产生随机函数CryptGenRandom、
一、生成函数CryptGenKey:
功能:产生一个随机的对称或非对称算法的密钥;
原型:
BOOL WINAPI CryptGenKey(
__in HCRYPTPROV hProv, //CSP句柄指针
__in ALG_ID Algid, //密码算法标识
__in DWORD dwFlags, //标志位,指定生成密钥的参数,
//如对称密钥的长度,RSA密钥的长度
__out HCRYPTKEY *phKey //新产生的密钥的句柄
);
其中,Algid参数取值如下:
CALG_HMAC---HMAC带密钥的摘要算法;
CALG_MD2---MD2摘要算法;
CALG_MD4---MD4摘要算法;
CALG_MD5---MD5摘要算法;
CALG_SHA---SHA摘要算法;
CALG_SHA1---SHA1摘要算法;
CALG_MAC---MAC算法;
CALG_SSL3_SHAMD5---SSLV3客户端算法;
CALG_RSA_SIGN---RSA签名算法;
CALG_DSS_SIGN---DSS签名算法;
CALG_RSA_KEYX---RSA加密算法;
CALG_DES---DES---对称加密算法;
CALG_3DES_112---112位的3DES加密算法;
CALG_3DES---3DES加密算法;
CALG_RC2---RC2分组算法;
CALG_RC4---RC4流加密算法。
返回值:操作成功返回TRUE,否则返回FALSE,使用GetLastError获取更多信息。
二、派生密钥函数CryptDeriveKey:
功能:根据基础数据派生以对称密钥(会话密钥);
原型:
BOOL WINAPI CryptDeriveKey(
__in HCRYPTPROV hProv, //CSP句柄指针
__in ALG_ID Algid, //密码算法标识
__in HCRYPTHASH hBaseData, //基础数据的摘要句柄
__in DWORD dwFlags, //标志位
__inout HCRYPTKEY *phKey //新产生的会话密钥句柄
);
此函数和CryptGenKey函数相似,不同的是CryptGenKey是通过随机数产生的,而CryptDeriveKey是通过指定的数据产生的。CryptDeriveKey只能产生对称算法的会话密钥,不能产生非对称算法的公/私钥。
返回值:操作成功返回TRUE,否则返回FALSE,使用GetLastError获取更多信息。
三、销毁密钥函数CryptDestroyKey:
功能:销毁密钥;
原型:
BOOL WINAPI CryptDestroyKey(
__in HCRYPTKEY hKey //密钥句柄
);
返回值:操作成功返回TRUE,否则返回FALSE,使用GetLastError()获取更多信息。
下面的示例由一个密码产生会话密钥:
#include <windows.h>
#include <wincrypt.h>
#include <iostream>
#include <string.h>
#include <conio.h>
#include <stdio.h>
#pragma comment(lib, "crypt32.lib")
#define PASSWORD_LENGTH 512
void HandleError(TCHAR* s);
void GetConsoleInput(CHAR*, UINT);
int _tmain(int argc, _TCHAR* argv[])
{
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
CHAR szPassword[PASSWORD_LENGTH] = "";
DWORD dwLength;
fprintf(stderr, "请输入用于产生会话密钥的密码:");
//获得用户输入密码,控制台上显示的是*号
GetConsoleInput(szPassword, PASSWORD_LENGTH);
printf("密码已保存/n");
dwLength = (DWORD)strlen(szPassword);
//获得CSP句柄
if(!CryptAcquireContext(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, 0))
{
HandleError(L"获取CSP句柄时出错");
}
//创建一个空的哈希对象
if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))
{
HandleError(L"创建空的哈希对象时出错");
}
//对密码进行哈希
if(!CryptHashData(hHash, (BYTE*)szPassword, dwLength, 0))
{
HandleError(L"对密码进行哈希时出错");
}
//由密码的哈希值创建会话密钥
if(!CryptDeriveKey(hCryptProv, CALG_RC2, hHash,
CRYPT_EXPORTABLE, &hKey))
{
HandleError(L"由密码的哈希值创建会话密钥时出错");
}
//这里使用上面得到的会话密钥进行加解密等操作,略...
//释放资源
if(hHash)
{
if(!CryptDestroyHash(hHash))
HandleError(L"销毁哈希对象时出错");
}
if(hKey)
{
if(!CryptDestroyKey(hKey))
HandleError(L"销毁会话密钥时出错");
}
if(hCryptProv)
{
if(!CryptReleaseContext(hCryptProv, 0))
HandleError(L"释放CSP句柄时出错");
}
printf("程序正常结束运行/n");
system("pause");
return 0;
}
void HandleError(TCHAR* s)
{
_tprintf(TEXT("程序运行出现错误./n"));
_tprintf(TEXT("%s/n"),s);
_tprintf(TEXT("错误代码%x/n."), GetLastError());
_tprintf(TEXT("程序终止./n"));
exit(1);
}
//从键盘获取字符串输入,并控制台上*号显示
void GetConsoleInput(char* strInput, UINT intMaxChars)
{
char ch;
char minChar = ' ';
minChar++;
ch = _getch();
while(ch != '/r')
{
if(ch == '/b' && strlen(strInput) > 0)
{
strInput[strlen(strInput)-1] = '/0';
printf("/b /b");
}
else if((ch >= minChar) && (strlen(strInput) < intMaxChars))
{
strInput[strlen(strInput)+1] = '/0';
strInput[strlen(strInput)] = ch;
_putch('*');
}
ch = _getch();
}
_putch('/n');
}
四、复制密钥函数CryptDuplicateKey:
功能:复制一个密钥,产生一个密钥的拷贝,包括其状态;
原型:
BOOL WINAPI CryptDuplicateKey(
__in HCRYPTKEY hKey, //密钥句柄
__in DWORD *pdwReserved, //保留参数,必须为NULL
__in DWORD dwFlags, //保留参数,必须为0
__out HCRYPTKEY *phKey //复制后的密钥句柄
);
返回值:操作成功返回TRUE,否则返回FALSE,使用GetLastError()获取更多信息。
五、获得密钥参数函数CryptGetKeyParam:
功能:获得key句柄的各项参数;
原型:
BOOL WINAPI CryptGetKeyParam(
__in HCRYPTKEY hKey, //密钥句柄
__in DWORD dwParam, //参数类型
__out BYTE *pbData, //接收key参数数据缓冲区指针
__inout DWORD *pdwDataLen, //输出的数据长度
__in DWORD dwFlags //保留参数,必须为0
);
其中,对于所有的key,dwParam可取值如下:
KP_ALGID---算法ID;
KP_BLOCKLEN---如果hKey为会话密钥,则此参数获得对称算法分组长度(单位为比特);
如果hKey为非对称密钥,则此参数获得密钥对加密强度(单位为比特,如1024bits RSA)
KP_KEYLEN---密钥长度;
KP_SALT---salt值,不使用于非对称密钥对;
对于对称密钥或者会话密钥,dwParam还可取值如下:
KP_IV---初始化向量;
KP_PADDING---补位的方式;
KP_MODE---加密模式(CBC、ECB等)。
返回值:操作成功返回TRUE,否则返回FALSE,调用GetLastError()获得更多信息。
六、获得密钥参数函数CryptSetKeyParam:
功能:设置key句柄的各项参数;
原型:
BOOL WINAPI CryptSetKeyParam(
__in HCRYPTKEY hKey, //密钥句柄
__in DWORD dwParam, //参数类型
__in const BYTE *pbData, //参数数据缓冲区指针
__in DWORD dwFlags //只用于dwParam为KP_ALGID时
);
其中dwParam可取值如下:
KP_ALGID---算法ID;
KP_SALT---salt值,不适用于非对称密钥对;
KP_IV---初始化向量;
KP_PADDING---补位的方式;
KP_MODE---加密模式(CBC,ECB等);
KP_CERTIFICATE---设置数字证书,对于RSA密钥,设置与其对应的数字证书;pbData即为DER编码的X509证书指针;
KP_KEYEXCHANGE_PIN---设置操作该交换密钥的口令;
KP_SIGNATRUE_PIN---设置操作该签名密钥的口令。
返回值:操作成功返回TRUE,否则返回FALSE,调用GetLastError()获得更多信息。
七、获得随机数函数CryptGenRandom:
功能:生成随机数;
原型:
BOOL WINAPI CryptGenRandom(
__in HCRYPTPROV hProv, //CSP句柄
__in DWORD dwLen, //生成随机数的长度
__inout BYTE *pbBuffer //接收随机数缓冲区的指针
);
返回值:操作成功返回TRUE,否则返回FALSE,调用GetLastError()获得更多信息。
下面代码示例演示了复制一个会话密钥的过程:
#include <windows.h>
#include <wincrypt.h>
#include <iostream>
#include <string.h>
#include <conio.h>
#include <stdio.h>
#pragma comment(lib, "crypt32.lib")
//#define ASCE_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define PASSWORD_LENGTH 512
void HandleError(TCHAR* s);
void GetConsoleInput(CHAR*, UINT);
int _tmain(int argc, _TCHAR* argv[])
{
HCRYPTPROV hCryptProv;
HCRYPTKEY hOriginalKey;
HCRYPTKEY hDuplicateKey;
DWORD dwMode;
BYTE pbData[16];
printf("本程序创建一个会话密钥,并复制它,同时添加参数到原会话密钥中/n");
if(!CryptAcquireContext(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, 0))
{
HandleError("创建CSP句柄时出错");
}
//生成一个会话密钥
if(!CryptGenKey(hCryptProv, CALG_RC4, 0, &hOriginalKey))
{
HandleError("生成会话密钥时出错");
}
//复制会话密钥
if(!CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey))
{
HandleError("复制会话密钥时出错");
}
//给原会话密钥设置参数
dwMode = CRYPT_MODE_ECB;
if(!CryptSetKeyParam(hOriginalKey, KP_MODE,
(BYTE*)&dwMode, 0))
{
HandleError("给原会话密钥设置参数时出错");
}
//生成一个随机初始化向量
if(!CryptGenRandom(hCryptProv, 8, pbData))
{
HandleError("生成一个随机初始化向量时出错");
}
//给原会话密钥设置初始化向量
if(!CryptSetKeyParam(hOriginalKey, KP_IV, pbData, 0))
{
HandleError("给原会话密钥设置初始化向量时出错");
}
//释放资源
if(hOriginalKey)
{
if(!CryptDestroyKey(hOriginalKey))
HandleError(L"销毁原会话密钥时出错");
}
if(hDuplicateKey)
{
if(!CryptDestroyKey(hDuplicateKey))
HandleError(L"销毁复制会话密钥时出错");
}
if(hCryptProv)
{
if(!CryptReleaseContext(hCryptProv, 0))
HandleError(L"释放CSP句柄时出错");
}
printf("程序正常结束运行/n");
system("pause");
return 0;
}
void HandleError(TCHAR* s)
{
_tprintf(TEXT("程序运行出现错误./n"));
_tprintf(TEXT("%s/n"),s);
_tprintf(TEXT("错误代码%x/n."), GetLastError());
_tprintf(TEXT("程序终止./n"));
exit(1);
}
八、导出密钥函数CryptExportKey:
功能:从CSP导出密钥或密钥对;
原型:
BOOL WINAPI CryptExportKey(
__in HCRYPTKEY hKey, //要导出的密钥句柄
__in HCRYPTKEY hExpKey, //目标用户的加密密钥,用于加密要导出的数
__in DWORD dwBlobType, //pbData中的数据类型
__in DWORD dwFlags, //标志位
__out BYTE *pbData, //接收导出密钥数据块的缓冲区指针
__inout DWORD *pdwDataLen //pbData字节大小
);
返回值:操作成功返回TRUE,否则返回FALSE,调用GetLastError()获得更多信息。
九、导入密钥函数CryptImportKey:
功能:把BLOB数据导入CSP,该函数可导入会话密钥、公钥、或者公私钥对。
原型:
BOOL WINAPI CryptImportKey(
__in HCRYPTPROV hProv, //CSP句柄
__in BYTE *pbData, //BLOB数据
__in DWORD dwDataLen, //BLOB数据长度
__in HCRYPTKEY hPubKey, //此参数的意义根据CSP类型以及导入的BLOB数据的类型不同而不同:如果BLOB数据是由交换密钥加密的,该参数就是交换密钥的句柄
如果BLOB数据是由会话密钥加密的,该参数就是会话密钥的句柄
如果BLOB数据没有被加密,则该参数为NULL
__in DWORD dwFlags, //标志位
__out HCRYPTKEY *phKey //导入密钥的句柄
);
返回值:操作成功返回TRUE,否则返回FALSE,调用GetLastError()获得更多信息。
下面代码演示了密钥的导入和导出操作:
#include <windows.h>
#include <wincrypt.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib, "crypt32.lib")
//存在数组中的明文密钥BLOB,数组格式必须如下所示:
// BLOBHEADER hdr;
// DWORD dwKeySize;
// BYTE rgbKeyData[];
BYTE DesKeyBlob[] =
{
0x08, 0x02, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, //BLOB header
0x08, 0x00, 0x00, 0x00, //key length, in bytes
0xf1, 0x0e, 0x25, 0x7c, 0x6b, 0xce, 0x0d, 0x34 //DES key with parity
};
//#define ASCE_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define PASSWORD_LENGTH 512
void HandleError(TCHAR* s);
void GetConsoleInput(CHAR*, UINT);
int _tmain(int argc, _TCHAR* argv[])
{
HCRYPTPROV hCryptProv = NULL;
HCRYPTKEY hKey = NULL;
DWORD dwBlobLen;
BYTE* pbKeyBlob;
//获得CSP句柄
if(!CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
//如果密钥容器打不开,则创建一个新的
if(NTE_BAD_KEYSET == GetLastError())
{
if(!CryptAcquireContext(&hCryptProv, L"ASCEContainer",
MS_ENHANCED_PROV, PROV_RSA_FULL,
CRYPT_NEWKEYSET |CRYPT_VERIFYCONTEXT))
{
printf("创建新的密钥容器出错/n");
return -1;
}
else
{
printf("打开密钥容器失败/n");
return -1;
}
}
}
//导入PLAINTEXTKEYBLOB数组到密钥容器中
if(!CryptImportKey(hCryptProv, DesKeyBlob,
sizeof(DesKeyBlob), 0, CRYPT_EXPORTABLE, &hKey))
{
printf("导入密钥时出错/n");
return -1;
}
//将刚导入的密钥导出来,验证是否正确
if(!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB,
0, NULL, &dwBlobLen))
{
printf("获取BLOB长度时出错/n");
return -1;
}
if(!(pbKeyBlob = (BYTE*)malloc(dwBlobLen)))
{
printf("分配内存时出错/n");
return -1;
}
if(!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB,
0, pbKeyBlob, &dwBlobLen))
{
printf("导出密钥时出错/n");
return -1;
}
DWORD count;
//打印明文BLOB以验证密钥导入是否正确
for(count=0; count<dwBlobLen;)
{
printf("%02x", pbKeyBlob[count]);
count++;
}
//释放资源
if(pbKeyBlob)
{
free(pbKeyBlob);
}
if(hCryptProv)
{
if(!CryptReleaseContext(hCryptProv, 0))
HandleError(L"释放CSP句柄时出错");
}
printf("程序正常结束运行/n");
system("pause");
return 0;
}
《ASCE1885的信息安全》のCryptoAPI---密钥的产生和交换函数的更多相关文章
- CSP学习之CryptoAPI初识
Crypto API目的就是提供开发者在windows下使用PKI的编程接口. Crypto 提供了很多的加解密相关函数,如编码.解码.加密解密,哈希,数字证书.证书管理证书存储等. 有关 ...
- CSP应用开发-CryptAPI函数库介绍
基本加密函数为开发加密应用程序提供了足够灵活的空间.所有CSP的通讯都是通过这些函数.一个CSP是实现所有加密操作的独立模块.在每一个应用程序中至少需要提供一个CSP来完成所需的加密操作.如果使用多于 ...
- [加密解密]CryptoAPI简介
CryptoAPI概述 Windows CryptoAPI是Microsoft 公司提出的安全加密应用服务框架,也是PKI推荐使用的加密 API.它提供了在Win32 环境下使用认证.编码.加密和签名 ...
- 2019第十二届全国大学生信息安全实践创新赛线上赛Writeup
本文章来自https://www.cnblogs.com/iAmSoScArEd/p/10780242.html 未经允许不得转载! 1.MISC-签到 下载附件后,看到readme.txt打开后提 ...
- Wifi 开放系统认证和共享密钥身份认证
记录开放系统认证和共享密钥认证的区别. 开放系统身份认证(open-systern authentication) 是802.11 要求必备的惟一方式. 由行动式工作站所发出的第一个帧被归类为auth ...
- CSP学习之导出密钥BLOB 解析
通过CryptExportKey( hKey, NULL, PUBLICKEYBLOB,0, NULL, &dwBlobLen) 函数导出的公钥信息如下: 06 02 00 00 00 A4 ...
- HTTPS 和 SSL/TLS 协议:密钥交换(密钥协商)算法及其原理
转自:https://blog.csdn.net/andylau00j/article/details/54583769 本系列的前一篇,咱们聊了“密钥交换的难点”以及“证书体系”的必要性.今天这篇来 ...
- 写给开发人员的实用密码学(三)—— MAC 与密钥派生函数 KDF
目录 一.MAC 消息认证码 MAC 与哈希函数.数字签名的区别 MAC 的应用 1. 验证消息的真实性.完整性 2. AE 认证加密 - Authenticated encryption 3. 基于 ...
- Openssl编程--源码分析
Openssl编程 赵春平 著 Email: forxy@126.com 第一章 基础知识 8 1.1 对称算法 8 1.2 摘要算法 9 1.3 公钥算法 9 1.4 回调函数 11 第二章 ope ...
随机推荐
- linux环境下vim创建java文件,并编译运行
一.前提 安装Java 二.创建工作目录并编辑java文件 三.编译 四.运行
- MySQL函数--(1)
/*函数与存储过程的区别1.存储过程:可以有0个返回值,可以有多个返回值函数:有且仅有一个返回值*/ #创建语法create FUNCTION 函数名(参数列表) return 返回类型BEGIN函数 ...
- RabbitMQ的一些有用教程
最近学习了一些RabbitMQ的知识,现在对所阅读过的一些非常优秀的教程进行总结,感谢各位博主和大神的无私奉献. 一.原理篇 https://blog.51cto.com/lookingdream/2 ...
- Webstorm的一些常用快捷键
ctrl+/ 单行注释ctrl+shift+/块注释Ctrl+X 删除行Ctrl+D 复制行Ctrl+B 快速打开光标处的类或方法Ctrl+F 查找文本Ctrl+R 替换文本ctrl+shift+ + ...
- maven:私服的相关配置
添加到settings.xml中 <server> <id>releases</id> <username>admin</username> ...
- asp.net动态为网页添加关键词的代码
如下资料是关于asp.net动态为网页添加关键词的代码,希望能对小伙伴们有较大用.HtmlMeta keywords = new HtmlMeta();keywords.Name = "ke ...
- [LOJ3014][JOI 2019 Final]独特的城市——树的直径+长链剖分
题目链接: [JOI 2019 Final]独特的城市 对于每个点,它的答案最大就是与它距离最远的点的距离. 而如果与它距离为$x$的点有大于等于两个,那么与它距离小于等于$x$的点都不会被计入答案. ...
- 洛谷P1169[ZJOI2007]棋盘制作
题目 一道悬线法的裸题,悬线法主要是可以处理最大子矩阵的问题. 而这道题就是比较经典的可以用悬线法来处理的题. 而悬线法其实就是把矩阵中对应的每个位置上的元素分别向左向上向右,寻找到不能到达的地方,然 ...
- nodejs+express创建一个简单的服务器
//首先安装express //1.引入express const express = require('express'); //2.创建服务器对象 let server = express(); ...
- 高并发环境下全局id生成策略
解决方案: 基于Redis的全局id生成策略:(推荐此方法) 基于雪花算法的全局id生成: https://www.cnblogs.com/kobe-qi/p/8761690.html 基于zooke ...