实验一-密码引擎-加密API实现与测试
实验要求
1 下载并查找GMT 0018-2012密码设备应用接口规范原始文档进行学习 (5分)
2 实现GMT 0018-2012密码设备应用接口规范的接口函数,至少实现:
1)设备管理中的打开设备,关闭设备,获取设备信息,产生随机数(4分)
2)密钥管理导出 ECC 签名公钥;SDF_ExportSignPublicKey_ECC I.导出 ECC加密公钥∶SDF_ExportEncPublicKey_ECC J. 产生 ECC非对称密钥对并输出∶SDF_GenerateKeyPair_ECC
K. (6分)
3)非对称算法(至少支持SM2):外部密钥 ECC验证∶SDF_ExternalVerify_ECC ,内部密钥 ECC签名;SDF_InternalSign_ECC ,内部密钥 ECC验证∶SDF_InternalVerify_ECC 外部密钥 ECC加密∶SDF_ExternalEncrypt_ECC
(8分)
4)对称算法(至少支持SM4)∶SDF_Encrypt 对称解密∶SDF_Dccrypt 计算 MAC∶SDF_CalculateMAC(6分)
5)杂凑算法(至少支持SM3):· 杂凑运算初始化∶SDF_HashInit· 多包杂凑运算∶SDF_HashUpdate· 杂凑运算结束∶SDF_HashFinal(6分)
密钥管理要求(10分)
基于本标准设计、开发的密码设备在密钥管理方面,应满足以下要求; 1)设备密钥的使用不对应用系统开放; 2) 密钥必须用安全的方法产生并存储;
3) 在任何时间、任何情况下,除公钥外的密钥均不能以明文形式出现在密码设备外; 4) 密码设备内部存储的密钥应具备有效的密钥保护机制,防止解剖、探测和非法读取; 5) 密码设备内部存储的密钥应具备权限控制机制,防止非法使用和导出。
设备状态要求(5分)
基于本标准设计、开发的密码设备在设备状态方面,应满足以下要求; 1) 密码设备应具有初始和就绪两个状态;
2) 未安装设备密钥的密码设备应处干初始状态,已安装设备密钥的密码设备应处于就绪状态; 3) 在初始状态下,除可读取设备信息、设备密钥的生成或恢复操作外,不能执行任何操作,生成或恢复设备密钥后,密码设备处于就绪状态;
4) 在就绪状态下,除设备密钥的生成或恢复操作外,应能执行任何操作; 5) 在就绪状态下进行的密钥操作,设备操作员应经过密码设备的认证。
1 下载并查找GMT 0018-2012密码设备应用接口规范原始文档进行学习
GMT 0018-2012密码设备应用接口规范
本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。
范围:本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准,适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。
密码设备应用接口在公钥密码基础设施应用技术体系框架中的位置:在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终瑞等设备组成,通过本标准规定的密码设备应用接口向通用密码服务层提供基础密码服务。
设备管理类函数
打开设备:SDF_OpenDevice
关闭设备:SDF_CloseDevice
创建会话:SDF_OpenSession
关闭会话:SDF_CloseSession
获取设备信息:SDF_GetDeviceInfo
产生随机数:SDF_GenerateRandom
获取私钥使用权限:SDF_GetPrivateKeyAccessRight
释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight
密钥管理类函数
导出 RSA 签名公钥:SDF_ExportSignPublicKey_RSA
导出 RSA 加密公钥:SDF_ExportEncPublicKey_RSA
产生RSA非对称密钥对并输出:SDF_GenerateKeyPair_RSA
生成会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK_RSA
生成会话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_RSA
导人会话密钥并用内部RSA私钥解密:SDF_ImportKeyWithISK_RSA
基于 RSA 算法的数宇信封转换:SDF_ExchangeDigitEnvelopeBaseOnRSA
导出 ECC 签名公钥:SDF_ExportSignPublicKey_ECC
导出 ECC 加密公钥:SDF_ExportEncPublicKey_ECC
产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ECC
生成会话密钥并用内部ECC公钥加密输岀:SDF_GenerateKeyWithIPK_ECC
生成会话密钥并用外部ECC公钥加密输出:SDF_GenerateKeyWithEPK_ECC
导入会话密钥并用内部ECC私钥解密:SDFJmportKeyWithlSKJECC
生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
计算会话密钥:SDF_GenerateKey WithECC
产生协商数据并计算会话密钥:SDF_GenerateAgreementDataAndKeyWithECC
基于 ECC算法的数字信封转换:SDF_ExchangeDigitEnvelopeBaseOnECC
生成会话密钥并用密钥加密密钥加密输出: SDF_GenerateKeyWithKEK
导入会话密钥并用密钥加密密钥解密:SDF_ImportKeyWithKEK
销毁会话密钥:SDF_DestroyKey
非对称算法运算类函数
外部公钥 RSA 运算:SDF_ExternalPublicKeyOperation_RSA
内部公钥 RSA 运算:SDF_InternalPublicKeyOperation_RSA
内部私钥 RSA 运算:SDF_InternalPrivateKeyOperation_RSA
外部密钥 ECC 验证:SDF_ExternalVerify_ECC
内部密钥 ECC 签名:SDF_InternalSign_ECC
内部密钥 ECC 验证:SDF_InternalVerify_ECC
外部密钥 ECC 加密:SDF_ExternalEncrypt_ECC
对称算法运算类函数
对称加密:SDF_Encrypt
对称解密:SDF_Decrypt
计算MAC:SDF_CalculateMAC
杂凑运算类函数
杂凑运算初始化:SDF_HashInit
多包杂凑运算:SDF_HashUpdate
杂凑运算结束:SDF_HashFinal
设备管理中的打开设备,关闭设备,获取设备信息,产生随机数
mkdir sdfproject
cd sdfproject
mkdir src include libs docs test
touch Makefile
touch readme.md
touch compile.shcd docs
mkdir ref
2 实现GMT 0018-2012密码设备应用接口规范的接口函数
加密代码部分:
加密
#include <stdio.h>
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH 0x00800000
void HandleError(char *s);
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8
BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
void main(void)
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Encrypt a file. ");
printf("Enter the name of the file to be encrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(EncryptFile(szSource, szDestination, szPassword))
{
printf("Encryption of the file %s was a success. ", szSource);
printf("The encrypted data is in file %s. ",szDestination);
}
else
{
HandleError("Error encrypting file!");
}
}
static BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
//--------------------------------------------------------------------
// Parameters passed are:
// szSource, the name of the input, a plaintext file.
// szDestination, the name of the output, an encrypted file to be
// created.
// szPassword, the password.
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
if(hSource = fopen(szSource,"rb"))
{
printf("The source plaintext file, %s, is open. ", szSource);
}
else
{
HandleError("Error opening source plaintext file!");
}
if(hDestination = fopen(szDestination,"wb"))
{
printf("Destination file %s is open. ", szDestination);
}
else
{
HandleError("Error opening destination ciphertext file!");
}
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("A cryptographic provider has been acquired. ");
}
else
{
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))//创建密钥容器
{
//创建密钥容器成功,并得到CSP句柄
printf("A new key container has been created. ");
}
else
{
HandleError("Could not create a new key container. ");
}
}
if(CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
printf("A hash object has been created. ");
}
else
{
HandleError("Error during CryptCreateHash! ");
}
if(CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
printf("The password has been added to the hash. ");
}
else
{
HandleError("Error during CryptHashData. ");
}
if(CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
printf("An encryption key is derived from the password hash. ");
}
else
{
HandleError("Error during CryptDeriveKey! ");
}
CryptDestroyHash(hHash);
hHash = NULL;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
if(ENCRYPT_BLOCK_SIZE > 1)
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
else
dwBufferLen = dwBlockLen;
if(pbBuffer = (BYTE *)malloc(dwBufferLen))
{
printf("Memory has been allocated for the buffer. ");
}
else
{
HandleError("Out of memory. ");
}
do
{
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
if(ferror(hSource))
{
HandleError("Error reading plaintext! ");
}
if(!CryptEncrypt(
hKey,
0,
feof(hSource),
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount, //输入被加密数据实际长度,输出加密后数据长度
dwBufferLen)) //pbBuffer的大小。
{
HandleError("Error during CryptEncrypt. ");
}
fwrite(pbBuffer, 1, dwCount, hDestination);
if(ferror(hDestination))
{
HandleError("Error writing ciphertext.");
}
}
while(!feof(hSource));
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return(TRUE);
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. ");
fprintf(stderr,"%s ",s);
fprintf(stderr, "Error number %x. ", GetLastError());
fprintf(stderr, "Program terminating. ");
exit(1);
}
解密代码部分:
#include <stdio.h>
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH 0x00800000
void HandleError(char *s);
//--------------------------------------------------------------------
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8
BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
void main(void)
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Decrypt a file. ");
printf("Enter the name of the file to be decrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(!DecryptFile(szSource, szDestination, szPassword))
{
printf(" Error decrypting file. ");
}
else
{
printf(" Decryption of file %s succeeded. ", szSource);
printf("The decrypted file is %s . ",szDestination);
}
}
static BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
BOOL status = FALSE;
if(!(hSource = fopen(szSource,"rb")))
{
HandleError("Error opening ciphertext file!");
}
if(!(hDestination = fopen(szDestination,"wb")))
{
HandleError("Error opening plaintext file!");
}
if(!CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
HandleError("Error during CryptAcquireContext!");
}
if(!CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
HandleError("Error during CryptCreateHash!");
}
if(!CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
HandleError("Error during CryptHashData!");
}
if(!CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
HandleError("Error during CryptDeriveKey!");
}
CryptDestroyHash(hHash);
hHash = 0;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
dwBufferLen = dwBlockLen;
if(!(pbBuffer = (BYTE *)malloc(dwBufferLen)))
{
HandleError("Out of memory! ");
}
do {
dwCount = fread(
pbBuffer,
1,
dwBlockLen,
hSource);
if(ferror(hSource))
{
HandleError("Error reading ciphertext!");
}
if(!CryptDecrypt(
hKey,
0,
feof(hSource),
0,
pbBuffer,
&dwCount))
{
HandleError("Error during CryptDecrypt!");
}
fwrite(
pbBuffer,
1,
dwCount,
hDestination);
if(ferror(hDestination))
{
HandleError("Error writing plaintext!");
}
} while(!feof(hSource));
status = TRUE;
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return status;
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. ");
fprintf(stderr,"%s ",s);
fprintf(stderr, "Error number %x. ", GetLastError());
fprintf(stderr, "Program terminating. ");
exit(1);
} // End of HandleError
MD5代码部分:
#include <stdio.h>
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <wincrypt.h>
#define CHECK_NULL_RET(bCondition) if (!bCondition) goto Exit0
#define BUFSIZE 1024
#define MD5LEN 16
BOOL GetContentMD5(
BYTE *pszFilePath,
BOOL bFile,
BOOL bUpperCase,
TCHAR *pszResult,
DWORD &dwStatus)
{
BOOL bResult = FALSE;
HCRYPTPROV hProv = 0;
HCRYPTHASH hHash = 0;
HANDLE hFile = NULL;
BYTE rgbFile[BUFSIZE];
DWORD cbRead = 0;
BYTE rgbHash[MD5LEN];
DWORD cbHash = 0;
CHAR rgbDigitsL[] = "0123456789abcdef";
CHAR rgbDigitsU[] = "0123456789ABCDEF";
CHAR *rgbDigits = bUpperCase ? rgbDigitsU : rgbDigitsL;
TCHAR szResult[MD5LEN*2+1] = {0};
dwStatus = 0;
bResult = CryptAcquireContext(&hProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT);
CHECK_NULL_RET(bResult);
bResult = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash);
CHECK_NULL_RET(bResult);
if (bFile)
{
hFile = CreateFile((TCHAR *)pszFilePath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
CHECK_NULL_RET(!(INVALID_HANDLE_VALUE == hFile));
while (bResult = ReadFile(hFile, rgbFile, BUFSIZE,
&cbRead, NULL))
{
if (0 == cbRead)
{
break;
}
bResult = CryptHashData(hHash, rgbFile, cbRead, 0);
CHECK_NULL_RET(bResult);
}
}
else
{
bResult = CryptHashData(hHash, pszFilePath, strlen((CHAR *)pszFilePath), 0);
CHECK_NULL_RET(bResult);
}
cbHash = MD5LEN;
if (bResult = CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
{
TCHAR szTmpBuff[3] ;
for (DWORD i = 0; i < cbHash; i++)
{
sprintf (szTmpBuff, TEXT("%c%c"), rgbDigits[rgbHash[i]>>4],
rgbDigits[rgbHash[i] & 0xf]);
lstrcat(szResult, szTmpBuff);
}
bResult = TRUE;
}
Exit0:
dwStatus = GetLastError();
CryptDestroyHash(hHash);
CryptReleaseContext(hProv, 0);
CloseHandle(hFile);
lstrcpy(pszResult, szResult);
return bResult;
}
int main(int argc, char* argv[])
{
DWORD dwStatus = 0;
TCHAR szResult[MD5LEN*2+1] = {0};
TCHAR szFilePath[] = TEXT("C:123.dll");
CHAR szContent[] = "explorer.exe";
GetContentMD5((BYTE *)szFilePath,
TRUE, TRUE, szResult, dwStatus);
MessageBox(NULL, szResult, TEXT("该文件的MD5"), MB_OK);
return 0;
}
加密过程:
1、打开源文件
2、取得密钥容器(CSP)句柄
3、根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件加密)
4、加密数据文件
5、清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。
解密过程:
1、打开源文件
2、取得密钥容器(CSP)句柄
3、根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件解密,
这里要求用户输入的密码与加密时输入的密码相同。在实际应用中,这个所谓用户输入的“密码”其实只是一个产生密钥的种子,一旦产生完会话密钥,则用户完全 可以忘记当初输入的“密码”,接收方可以使用传过来的密钥直接对加密文件进行解密,而不用再重复一次“生成密钥”的过程。)
4、解密数据文件
5、清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。