四、对称加解密
一次完整对称加解密分为三个步骤:密钥交换、加密及解密,这样才能做到密钥不直接传递,并且一次一密。
首先是数据传输双方进行密钥交换。
1.SKF_GenerateAgreementDataWithECC(HCONTAINER hContainer, ULONG ulAlgId,ECCPUBLICKEYBLOB* pTempECCPubKeyBlob,BYTE* pbID, ULONG ulIDLen,HANDLE *phAgreementHandle);
传输的发起方调用此方法进行密钥协商,为计算会话密钥而产生协商参数,返回临时密钥对的公钥及协商句柄。hContainer是容器句柄,任何有公私钥对的容器均可;ulAlgId是会话密钥算法标识,比如SGD_SMS4_ECB(0x00000401)即SM4算法ECB模式; pTempECCPubKeyBlob返回的发起方临时公钥,此数据要传递给响应方; pbID是发起方的ID,这个ID可根据实际情况设置;ulIDLen是发起方ID的长度,不大于32;phAgreementHandle为返回的密钥协商句柄,备用。
2.SKF_GenerateAgreementDataAndKeyWithECC(HANDLE hContainer, ULONG ulAlgId,ECCPUBLICKEYBLOB* pSponsorECCPubKeyBlob,ECCPUBLICKEYBLOB* pSponsorTempECCPubKeyBlob,ECCPUBLICKEYBLOB* pTempECCPubKeyBlob,BYTE* pbID, ULONG ulIDLen, BYTE *pbSponsorID, ULONG ulSponsorIDLen,HANDLE *phKeyHandle);
传输的响应方调用此方法,进行密钥协商,产生协商参数并计算会话密钥,输出临时密钥对公钥,并返回产生的密钥句柄。hContainer是容器句柄;ulAlgId 是会话密钥算法标识,这里要和上一个方法里设置的算法标识一致;pSponsorECCPubKeyBlob是发起方的公钥,这个公钥是发起方所使用容器里的公钥,可以采用SKF_ImportCertificate和SKF_ExportPublicKey方法从发起方的证书里获得;pSponsorTempECCPubKeyBlob为发起方的临时公钥;pTempECCPubKeyBlob返回响应方的临时公钥;pbID是响应方的ID;ulIDLen是响应方ID的长度,不大于32;pbSponsorID是发起方的ID;ulSponsorIDLen是发起方ID的长度,不大于32;phKeyHandle返回的对称算法密钥句柄。这样,响应方率先拿到了密钥句柄。
3.SKF_GenerateKeyWithECC(HANDLE hAgreementHandle, ECCPUBLICKEYBLOB* pECCPubKeyBlob, ECCPUBLICKEYBLOB* pTempECCPubKeyBlob, BYTE* pbID, ULONG ulIDLen, HANDLE *phKeyHandle);
接下来发起方调用此方法,使用自身协商句柄和响应方的协商参数计算会话密钥,同时返回会话密钥句柄。hAgreementHandle是第1步返回的密钥协商句柄;pECCPubKeyBlob是响应方的公钥,即响应方所使用容器里的公钥;pTempECCPubKeyBlob是响应方的临时公钥;pbID 为响应方的ID;ulIDLen是响应方ID的长度,不大于32;phKeyHandle是返回的密钥句柄。
传输双方完成了密钥的协商和交换过程,现在双方都有同样的密钥,可以进行加密传输了。
上述密钥交换的过程适用于加密的数据需实时解密的场景,如加密数据在网络传输。如果不需要实时解密,比如加密的数据保存在数据库,以后查询时再解密,那就无法使用密钥交换的方式确定密钥。这时可以使用下面的方法,通过给定的值产生对称密钥。
SKF_SetSymmKey(DEVHANDLE hDev, BYTE* pbKey, ULONG ulAlgID, HANDLE* phKey);
hDev是设备句柄;pbKey是输入的密钥值;ulAlgID 是会话密钥的算法标识;phKey 是返回的密钥句柄。这样,加解密双方可以通过输入同样的pbKey产生相同的密钥,进行加解密。当然,这种方法安全性较低,在国密标准里并不推荐使用。
4.SKF_EncryptInit(HANDLE hKey, BLOCKCIPHERPARAM EncryptParam)
此方法进行数据加密初始化,并设置数据加密的算法相关参数。hKey是加密密钥句柄;EncryptParam是分组密码算法相关参数,包括初始向量、初始向量长度、填充方法、和反馈值的位长度。如果采用CBC模式,就需要设置初始向量等参数。
5.SKF_Encrypt(HANDLE hKey, BYTE * pbData, ULONG ulDataLen, BYTE *pbEncryptedData, ULONG *pulEncryptedLen);
调用此方法进行数据的加密操作,hKey为加密密钥句柄;pbData是待加密数据;ulDataLen是待加密数据长度;pbEncryptedData返回加密后的数据;pulEncryptedLen输入时表示预设的加密后数据长度;输出时表示返回加密后的数据实际长度。可以将pbEncryptedData设置为NULL,得到加密数据实际长度后,再做第二次调用;也可以预先判断返回的加密数据长度(在待加密数据长度和加密算法已知情况下),给pbEncryptedData分配好长度空间,一次调用。
和杂凑运算类似,上述加密方法只适用于单组数据,如果是分组数据,则需要使用下列方法:
SKF_EncryptUpdate(HANDLE hKey, BYTE * pbData, ULONG ulDataLen, BYTE *pbEncryptedData, ULONG *pulEncryptedLen);
SKF_EncryptFinal(HANDLE hKey, BYTE *pbEncryptedData, ULONG *ulEncryptedDataLen);
对于每一组数据使用SKF_EncryptUpdate加密,最后调用SKF_EncryptFinal返回最终的加密数据。可以看出,和杂凑不同,对一个分组数据的加密结果可单独得到。
解密
6.SKF_DecryptInit(HANDLE hKey, BLOCKCIPHERPARAM DecryptParam);
调用此方法数据解密初始化并设置解密密钥相关参数。两个参数的意义用法与SKF_EncryptInit一致。
7.SKF_Decrypt(HANDLE hKey, BYTE * pbEncryptedData, ULONG ulEncryptedLen, BYTE * pbData, ULONG * pulDataLen);
调用此方法进行数据的解密操作。hKey为解密密钥句柄; pbEncryptedData是待解密数据;ulEncryptedLen是待解密数据长度;pbData返回解密后的数据;pulDataLen是解密后的数据实际长度。可以将pbData设置为NULL,得到解密数据实际长度后,再做第二次调用;也可以预先判断返回的解密数据长度(在待解密数据长度和加密算法已知情况下),给pbData分配好长度空间,一次调用。
同样,对于分组加密结果的解密,应使用下列方法:
SKF_DecryptUpdate(HANDLE hKey, BYTE * pbEncryptedData, ULONG ulEncryptedLen, BYTE * pbData, ULONG * pulDataLen);
SKF_DecryptFinal(HANDLE hKey, BYTE *pbDecryptedData, ULONG *pulDecryptedDataLen);
对于每一组数据使用DecryptUpdate解密,最后调用SKF_ DecryptFinal返回最终的解密数据。
五、非对称加解密
SKF里的非对称加解密有两种模式:一种是对会话密钥进行加解密,一种是使用外部导入的公私钥进行加解密。很明显,第一种就是我们之前说的数字信封:只对会话密钥进行非对称加解密,用会话密钥对原文进行对称加解密。
1.SKF_ECCExportSessionKey(HCONTAINER hContainer, ULONG ulAlgId, ECCPUBLICKEYBLOB *pPubKey, PECCCIPHERBLOB pData, HANDLE *phSessionKey);
加密方调用此方法生成会话密钥并用外部公钥加密输出。hContainer是容器句柄;ulAlgID 是会话密钥的算法标识;pPubKey是用来加密的外部公钥(这个当然是解密方的加密公钥);pbData返回会话密钥密文;phSessionKey是返回的会话密钥句柄。然后加密方就可以用会话密钥对数据进行加密了,调用方法在对称加解密已经介绍过,此处不再介绍。
2.SKF_ImportSessionKey(HCONTAINER hContainer, ULONG ulAlgId,BYTE *pbWrapedData,ULONG ulWrapedLen,HANDLE *phKey);
解密方调用此方法导入会话密钥密文,并使用容器内的加密私钥解密得到会话密钥。hContainer是加密私钥所在容器句柄;ulAlgID 是会话密钥的算法标识;pbWrapedData 要导入的密文数据;ulWrapedLen密文数据长度;phKey返回会话密钥句柄。之后便可以用会话密钥句柄对加密的数据进行解密了。
至于另外一种方式,使用的是下面两个方法:
SKF_ExtECCEncrypt(DEVHANDLE hDev, ECCPUBLICKEYBLOB* pECCPubKeyBlob,BYTE* pbPlainText, ULONG ulPlainTextLen, PECCCIPHERBLOB pCipherText);
SKF_ExtECCDecrypt(DEVHANDLE hDev, ECCPRIVATEKEYBLOB* pECCPriKeyBlob, PECCCIPHERBLOB pCipherText, BYTE* pbPlainText, ULONG* pulPlainTextLen);
这种方式实际用的很少,也不是国密标准推荐的方式,这里就不详细介绍,有兴趣的同学可以自行参看标准。
六、杂凑运算
在介绍数字签名时已经包含杂凑运算相关SKF API的调用方法,代码就不再贴了。这里着重要说明的是,数字签名里的杂凑运算,按照国密标准要进行SM2算法签名预处理,即SKF_DigestInit方法 的ulAlgID、pPubKey、pucID和ulIDLen都有传值要求。因此,如果只是进行纯粹的SM3杂凑运算,则这个方法的ulAlgID参数应是SGD_SM3,pPubKey和pucID是NULL,ulIDLen是0。
上面就介绍了使用国密SKF库进行几项主要密码功能运算的方法,希望能够帮助到大家。
————————————————
版权声明:本文为CSDN博主「weixin_45303938」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_45303938/article/details/107827427