加密API学习

微软的Crypto API

​ Crypto API作为一部分 Microsoft Windows 提供的应用程序编程接口(API) ,提供了一组允许应用程序在对用户的敏感私钥数据提供保护时,以灵活的方式对数据进行加密或数字签名的函数,而实际的加密操作是由称为加密服务提供程序 (CSP) 的独立模块执行。

image-20210424224038340

摘要

​ 可以按照如下顺序调用接口实现摘要:

摘要运算的第一步——调用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密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数数据类型、参数的定义和设备的安全要求。

​ 本标准适用于智能密码钥匙产品的研制、使用和检测。

智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图所示。

img

设备管理系列函数:

img

应用管理系列函数:

img

容器管理系列函数:

img

密码服务系列函数:

img

img

GMT 0018-2012

实际数据结构定义:
image

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_enumobj

  • pkcs11_GetUSBInfos
    pkcs11_GetUSBInfos

  • pkcs11_exportcert
    pkcs11_exportcert

  • pkcs11_PKCSDEMO
    pkcs11_PKCSDEMO

  • pkcs11_pkcstest
    pkcs11_pkcstest

CryptoAPI

  • CryptoAPI_EncryptFile
    CryptoAPI_EncryptFile

  • CryptoAPI_Signa_Verify
    CryptoAPI_Signa_Verify

skf

  • skf_EncryData
    skf_EncryData
posted @ 2021-04-25 14:25  seven昔年  阅读(318)  评论(1编辑  收藏  举报