加密API学习
微软的Crypto API
Crypto API作为一部分 Microsoft Windows 提供的应用程序编程接口(API) ,提供了一组允许应用程序在对用户的敏感私钥数据提供保护时,以灵活的方式对数据进行加密或数字签名的函数,而实际的加密操作是由称为加密服务提供程序 (CSP) 的独立模块执行。
摘要
可以按照如下顺序调用接口实现摘要:
摘要运算的第一步——调用CryptAcquireContext方法。
实际上,下面介绍的每一个密码运算基本都要先通过调用此方法,设置相应的密码运算参数,并返回相应的CSP句柄,用于后面的运算。此方法调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。
BOOL CryptAcquireContext (
HCRYPTPROV * phProv, //返回的CSP句柄
LPCTSTR pszContainer, //要使用的密钥是在容器,摘要运算不涉及密钥,所以这里设置为NULL
LPCTSTR pszProvider, //使用到的CSP的名称,如果设置为NULL,则CryptAcquireContext会调用系统缺省CSP
DWORD dwProvType, //所使用的CSP的类型,一般这里设置为PROV_RSA_FULL(0x1)
DWORD dwFlags //标志值,如果是涉及到私钥的运算,如签名或解密,应设置为0,否则应设置成CRYPT_VERIFYCONTEXT。
)
调用CryptCreateHash方法生成一个摘要运算的对象。
此方法调用成功返回true(-1),否则返回false(0)。
BOOL CryptCreateHash(
HCRYPTPROV hProv, //上一步返回的CSP句柄
ALG_ID Algid, //摘要算法
HCRYPTKEY hKey, //设置成0
DWORD dwFlags, //设置成0
HCRYPTHASH * phHash //返回的摘要运算对象
)
调用CryptHashData方法进行摘要运算。
此方法调用成功返回true(-1),否则返回false(0)。
BOOL CryptHashData(
HCRYPTHASH hHash, //上一步返回的摘要运算对象
BYTE * pbData, //原文
DWORD dwDataLen, //原文长度
DWORD dwFlags //设置成0
)
调用CryptGetHashParam返回摘要的各种相关数据信息。
BOOL CryptGetHashParam(
HCRYPTHASH hHash, //上一步返回的摘要运算对象
DWORD dwParam, //返回摘要的数据长度时:HP_HASHSIZE(0x0004),返回摘要值时:HP_HASHVAL(0x0002)
BYTE *pbData, //返回摘要的数据长度时:长度值,返回摘要值时:摘要值
DWORD *pdwDataLen, //长度值所占字节数
DWORD dwFlags //设置为0
)
对称加密
生成摘要(同上)
CryptAcquireContext //返回CSP句柄,参数设置与摘要运算时一致。
CryptCreateHash //生成摘要运算对象。
CryptHashData //生成摘要。pbData为调用加密功能的上位程序输入的加密口令。
生成一个会话密钥,用来加密数据。
BOOL CryptDeriveKey(
HCRYPTPROV hProv, //上一步返回的CSP句柄
ALG_ID Algid, //加密算法
HCRYPTHASH hBaseData, //上一步返回的摘要对象
DWORD dwFlags, //密钥类型,如果调用的CSP没有特别要求,设置为0
HCRYPTKEY * phKey //返回的密钥对象
)
设置密钥参数。
PS:如果采用的是RC2\RC4等流加密算法,这一步可以省略。如果采用的是分组加密算法,那应该在这一步设置加密模式等参数,如:
- CryptSetKeyParam(hKey, KP_MODE, CRYPT_MODE_CBC, 0);//设置成CBC模式
- CryptSetKeyParam(hKey, KP_IV, pbIV, 0);//设置初始向量
BOOL CryptSetKeyParam(
HCRYPTKEY hKey,
DWORD dwParam,
BYTE * pbData,
DWORD dwFlags
)
调用CryptEncrypt进行加密。
BOOL CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash, //如果不需要对原数据进行摘要运算可以设为NULL
BOOL Final, //true时表示没有分块加密或当前是最后一块加密,否则为false
DWORD dwFlags, //设置为0
BYTE *pbData, //原文,调用后输出密文
DWORD *pdwDataLen, //要加密原文长度,调用后返回密文长度
DWORD dwBufLen //为pbData分配的缓冲区长度
)
-
要注意的是,这里的分块和分组加密里的分组是不同的概念,分组是加密算法本身的处理过程,而这里的分块是调用加密功能的业务逻辑,它们处于不同的层面,但分块长度必须是分组长度的整数倍。
-
在采用分组加密的情况下,密文长度会比明文长度长一些,所以dwBufLen的值应该设置的足够大,以满足返回加密结果的要求。一般的做法是调用两次CryptEncrypt,第一次调用时pbData传NULL,dwBufLen传0,调用后pdwDataLen输出密文所需长度;第二次调用时 dwBufLen设置的值不小于第一次调用后pdwDataLen即可。
-
调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。
对称解密
CryptAcquireContext
CryptCreateHash
CryptHashData
CryptDeriveKey
CryptSetKeyParam
上面的方法与加密的调用顺序和参数设置基本一致,不再赘述。
BOOL CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData, //输入密文,调用后输出明文
DWORD *pdwDataLen //输入为密文长度,调用后输出明文长度
)
CryptDecrypt方法调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。
对同一数据的加密和解密可以采用不同的分块方式。比如,加密时不分块,解密时分块,不影响最后的解密结果。
PKCS#11
PKCS# 11标准定义了与密码令牌(如硬件安全模块(HSM)和智能卡)的独立于平台的API,API定义了最常用的加密对像类型(RSA密钥,X.509证书,DES / 三重DES密钥等)以及使用,创建/生成,修改和删除这些对象所需的所有功能。
PKCS#11创建和支持下列对象:
对象 | 说明 |
---|---|
CKO_DATA | 应用程序定义的对象。对象的数据结构可由应用程序任意定义,但是数据意义的解释由应用程序负责。 |
CKO_SECRET_KEY | 对称加密算法使用的密钥。 |
CKO_CERTIFICATE | X.509 |
CKO_PUBLIC_KEY | RSA |
CKO_PRIVATE_KEY | RSA |
CKO_MECHANISM | 算法对象 |
接口指令:
接口类型 | 函数名称 | 描述 |
---|---|---|
通用接口函数 | C_Initialize | 初始化 Cryptoki |
C_Finalize | 整理各种适合 Cryptoki的资源 | |
C_GetInfo | 获得关于Cryptoki的通用信息 | |
C_GetFunctionList | 获得Cryptoki 库函数的进入点 | |
槽和令牌管理函数 | C_GetSlotList | 获得系统中槽的名单 |
C_GetSlotInfo | 获得关于特殊槽的信息 | |
C_GetTokenInfo | 获得关于特殊令牌的信息 | |
C_WaitForSlotEvent | 等待槽事件(令牌插入,转移等) 的发生 | |
C_GetMechanismList | 获得由令牌支持的机制的名单 | |
C_GetMechanismInfo | 获得关于特殊机制的信息 | |
C_InitToken | 初始化一个令牌 | |
C_InitPIN | 初始化普通用户的 PIN | |
C_SetPIN | 改变现在用户的PIN | |
会话管理函数 | C_OpenSession | 打开一个应用程序和特殊令牌之间的连接或安装一个应用程序呼叫返回令牌插入 |
C_CloseSession | 关闭一个会话 | |
C_CloseAllSessions | 用令牌关闭所有的会话 | |
C_GetSessionInfo | 获得关于会话的信息 | |
C_GetOperationState | 获得会话的加密操作状态 | |
C_SetOperationState | 设置会话的加密操作状态 | |
C_Login | 注册一个令牌 | |
C_Logout | 从一个令牌注销 | |
对象管理函数 | C_CreateObject | 建立一个对象 |
C_CopyObject | 建立一个对象的拷贝 | |
C_DestroyObject | 销毁一个对象 | |
C_GetObjectSize | 获取字节中一个对象的大小 | |
C_GetAttributeValue | 获取一个对象的属性值 | |
C_SetAttributeValue | 改变一个对象的属性值 | |
C_FindObjectsInit | 初始化一个对象的搜索操作 | |
C_FindObjects | 继续一个对象搜索操作 | |
C_FindObjectsFinal | 完成一个对象搜索操作 | |
加密函数 | C_EncryptInit | 初始化一个加密操作 |
C_Encrypt | 加密单部分数据 | |
C_EncryptUpdate | 继续一个多部分加密操作 | |
C_EncryptFinal | 完成一个多部分加密操作 | |
解密函数 | C_DecryptInit | 初始化一个解密操作 |
C_Decrypt | 解密单部分加密数据 | |
C_DecryptUpdate | 继续一个多部分解密操作 | |
C_DecryptFinal | 完成一个多部分解密操作 | |
消息解密函数 | C_DigestInit | 初始化一个消息摘要操作 |
C_Digest | 摘要单部分数据 | |
C_DigestUpdate | 继续一个多部分摘要操作 | |
C_DigestKey | 摘要一个密钥 | |
C_DigestFinal | 完成一个多部分摘要操作 | |
签名和消息鉴别函数 | C_SignInit | 初始化一个签名操作 |
C_Sign | 签名单部分数据 | |
C_SignUpdate | 继续一个多部分签名操作 | |
C_SignFinal | 完成一个多部分签名操作 | |
C_SignRecoverInit | 初始化一个签名操作,在操作中数据能从签名中恢复 | |
C_SignRecover | 签名单部分数据,在操作中数据能从签名中恢复 | |
签名鉴定消息鉴别函数 | C_VerifyInit | 初始化一个鉴定操作 |
C_Verify | 在单部分数据上鉴定一个签名 | |
C_VerifyUpdate | 继续一个多部分鉴定操作 | |
C_VerifyFinal | 完成一个多部分鉴定操作 | |
C_VerifyRecoverInit | 初始化一个鉴定操作,在操作中数据能从签名中恢复 | |
C_VerifyRecover | 在单部分数据上鉴定一个签名,在操作中数据能从签名中恢复 | |
双效加密函数 | C_DigestEncryptUpdate | 继续类似的多部分摘要和加密操作 |
C_DecryptDigestUpdate | 继续类似的多部分解密和摘要操作 | |
C_SignEncryptUpdate | 继续类似的多部分签名和加密操作 | |
C_DecryptVerifyUpdate | 继续类似的多部分解密和鉴定操作 | |
密钥管理函数 | C_GenerateKey | 产生一个保密密钥 |
C_GenerateKeyPair | 产生一个公共/私钥对 | |
C_WrapKey | 加密一个密钥 | |
C_UnwrapKey | 解密一个密钥 | |
C_DeriveKey | 从基础密钥派生一个密钥 | |
随机数生成函数 | C_SeedRandom | 把一个附加种子材料加入随机数字生成器 |
C_GenerateRandom | 生成随机数 | |
并行功能管理函数 | C_GetFunctionStatus | 已废弃函数,返回CKR_FUNCTION_NOT_PARALLEL |
C_CancelFunction | 已废弃函数,返回CKR_FUNCTION_NOT_PARALLEL |
CSP接口标准:
接口类型 | 函数名称 | 描述 |
---|---|---|
CSP连接函数 | CPAcquireContext | 为应用程序创建一个上下文 |
CPGetProvParam | 返回CSP相关的信息 | |
CPReleaseContext | 释放CPAcquireContext创建的上下文 | |
CPSetProvParam | 设置CSP的参数操作 | |
CSP密钥生成和交换函数 | CPDeriveKey | 从一个数据散列中生成一个会话密钥,它保证生成的密钥互不相同 |
CPDestroyKey | 释放一个密钥句柄,释放后,句柄将无效,密钥将无法再被访问 | |
CPExportKey | 从CSP容器中导出密钥 | |
CPGenKey | 用来生成密钥或密钥对 | |
CPGenRandom | 使用随机数填充一个缓冲 | |
CPGetKeyParam | 用来得到加密操作密钥的属性 | |
CPGetUserKey | 用来获取CSP容器中的持久密钥对 | |
CPImportKey | 从一个blob中导入密钥到CSP容器中 | |
CPSetKeyParam | 设置密钥的属性 | |
CSP加解密函数 | CPDecrypt | 用来解密先前被加密的数据 |
CPEncrypt | 用来加密明文 | |
CSP散列和数字签名函数 | CPCreateHash | 初始化并散列输入数据 |
CPDestroyHash | 删除一个散列对象句柄 | |
CPDuplicateHash | 创建一个散列对象的拷贝 | |
CPGetHashParam | 获取散列对象的计算结果 | |
CPHashData | 散列输入的数据 | |
CPSetHashParam | 定制一个散列对象的属性 | |
CPSignHash | 签名一个散列对象 | |
CPVerifySignature | 校验一个数字签名 |
GMT 0016-2012
本标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数数据类型、参数的定义和设备的安全要求。
本标准适用于智能密码钥匙产品的研制、使用和检测。
智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图所示。
设备管理系列函数:
应用管理系列函数:
容器管理系列函数:
密码服务系列函数:
GMT 0018-2012
实际数据结构定义:
typedef struct DeviceInfo_st{
unsigned char IssuerName[40];
unsigned char DeviceName[16];
unsigned char DeviceSerial[16];
unsigned int DeviceVersion;
unsigned int StandardVersion;
unsigned int AsymAlgAbility[2];
unsigned int SymAlgAbility;
unsigned int HashAlgAbility;
unsigned int BufferSize;
}DEVICEINFO;
RSA密钥实际数据结构定义:
#define RSAref_MAX_BIT S2048
#define RSAref_MAX_LEN ((RSAref_MAX_BITS+7)/8)
#define RSAref_MAX_PBITS ((RSAref_MAX_BITS+1)/2)
#define RSAref_MAX_PLEN ((RSAref_MAX_PBITS+7)/8)
typedef struct RSArefPublicKey_st
{
unsigned int bits;
unsigned char m[RSAref_MAX_LEN];
unsigned char e[RSAref_MAX_LEN];
}RSArefPublicKey;
typedef struct RSArefPrivateKey_st
{
unsigned int bits;
unsigned char m[RSAref_MAX_LEN];
unsigned char e[RSAref_MAX_LEN];
unsigned char d[RSAref_MAX_LEN];
unsigned char prime[2][RSAref_MAX_PLEN];
unsigned char pexp[2][RSAref_MAX_PLEN];
unsigned char coefRSArefMAX_PLEN];
}RSArefPrivateKey;
ECC密钥实际数据结构定义:
#define ECCrcf_MAX_BITS 512
#define ECCref_MAX_LEN ((ECCref_MAXBITS+7)/8)
typedef struct ECCrefPublicKey_st
{
unsigned int bits;
unsigned char x[ECCref_MAX_LEN];
unsigned char y[ECCref_MAX_LEN];
}ECCrefPublicKey;
typedef struct ECCrefPrivateKey_st{
unsigned int bits;
unsigned char K[ECCref_MAX_LEN];
}ECCrefPrivateKey,
设备接口描述:
//************************************************************************************
//设备管理
//************************************************************************************
/*
功能:打开密码设备,返回设备句柄。
参数:
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_OpenDevice(void** phDeviceHandle);
/*
功能:关闭密码设备,并释放相关资源。
参数:hDeviceHandle[in] 已打开的设备句柄
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_CloseDevice(void* hDeviceHandle);
/*
功能: 创建与密码设备的会话。
已打开的设备句柄
hDeviceHandlein]参数:h
phessionHiandle[out]
返回与密码设备建立的新会话句柄成功
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_OpenSession(void* hDeviceHandle, void** phSessionHandle);
/*
功能:关闭与密码设备已建立的会话,并释放相关资源。
参数:hSessionHandle[in] 与密码设备已建立的会话句柄
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_CloseSesson(void* hSessionHandle);
/*
功能:获取密码设备能力描述。
参数:hSessionHandle[in] 与设备建立的会话句柄
pstDevicelnfo[our] 设备能力描述信息,内容及格式见设备信息定义成功
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_GetDeviceInfo(
void* hSessionHandle,
DEVICEINFO* pstDeviceInfo
);
/*
功能:获取指定长度的随机数。
参数:
hSessonHandle[in] 与设备建立的会话句柄
uiLegth[in] 欲获取的随机数长度
pucRandom[out] 缓冲区指针,用于存放获取的随机数
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_GenerateRandom(
void* hSessionHandle,
unsigned int uiLength,
unsigned char* pucRandom
);
/*
功能:获取密码设备内部存储的指定索引私钥的使用权。
参数:
hSessionHandle[in] 与设备建立的会话句柄
uiKeyIndex[in] 密码设备存储私钥的索引值
pucPassword[in] 使用私钥权限的标识码
uiPwdLength[in] 私钥访问控制码长度,不少于8 字节
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_GetPrivateKeyAccessRight(
void* hSessionHandle,
unsigned int uiKeyIndex,
unsigned char* pucPassword,
unsigned int uiPwdLength
);
/*
功能:释放密码设备存储的指定索引私钥的使用授权。
参数:
hSessonHandle[in] 与设备建立的会话句柄
uiKeyIndex[in] 密码设备存储私钥索引值成功
返回值∶0(SDR OK) 成功
非0 失败,返回错误代码
*/
int SDF_ReleasePrivateKeyAccessRight(
void* hSessionHandle,
unsigned int uiKeyIndex
);
龙脉实现
PKCS#11
-
pkcs11_enumobj
-
pkcs11_GetUSBInfos
-
pkcs11_exportcert
-
pkcs11_PKCSDEMO
-
pkcs11_pkcstest
CryptoAPI
-
CryptoAPI_EncryptFile
-
CryptoAPI_Signa_Verify
skf
- skf_EncryData