作者:ARM-WinCE
今天介绍一下WinCE和Linux之间如何彼此验证并解密对方的数据。我在做这个项目的时候,曾遇到过一些问题,网上搜索了一下,发现很多人遇到和我一样的问题,就是在WinCE和Linux之间,由于平台之间的不同,往往不能彼此验证签名或者解密对方的数据,遗憾的是,网上能够搜索到的都是提问而没有回答(当然可能是我没找到,呵呵),所以介绍一下。
实际上,WinCE和Linux之间彼此验证签名或者解密对方数据,都符合PKI标准,WinCE使用CryptoAPI函数集,Linux则支持CryptoAPI和Openssl,一般在Linux下都会使用Openssl。想在WinCE和Linux之间玩转PKI,要注意以下几点:
A. X509证书
X509证书有PEM和DER两种编码格式,WinCE支持DER格式,Linux支持PEM和DER格式,所以X509证书要选用DER格式,否则在WinCE系统中无法导入到证书库当中。
B. 私钥
私钥就是Private Key,在WinCE下使用pvk文件格式,在Linux可以是pvk,pem或者der格式。
C. 字序
生成的签名或者是加密的数据,在WinCE和Linux下的字序是相反的,也就是说在WinCE下生成的签名和加密的数据,要进行反转,然后在Linux下才能被验证和解密,同理在Linux下也是一样的。
D. 算法一致
确认在WinCE和Linux之间所使用的各种算法是一致的,否则肯定是解不出来的。
搭建环境很简单,一个WinXP安装了WinCE开发环境,去www.openssl.org 上面下载Windows版本的Openssl安装在PC上面,网上搜索一个叫pvktool的工具,用于将私钥从PEM格式转换成PVK格式。
1. Openssl中验证WinCE文件签名
(1) Windows版本的Openssl是基于命令行的,首先在Openssl中创建一个私钥,然后从私钥当中导出公钥,命令如下:
genrsa -out private.pem 1024
创建1024bits的PEM格式的私钥private.pem
rsa -pubout -in private.pem -out public.pem
基于私钥private.pem导出PEM格式的公钥public.pem
(2) 转换私钥为PVK格式,这样可以被WinCE系统使用,命令如下:
rsa -in private.pem -outform PVK -pvk-strong -out private.pvk
将private.pem私钥转换为PVK格式的私钥private.pvk
(3) 将私钥文件拷贝到WinCE系统当中,然后导入私钥,关于如何导入,在前几篇Blog中介绍过,这里不再重复了。
(4) 创建一个文件,然后对文件进行签名,签名的Hash算法这里使用SHA256,当然也可以用别的,这个在前面的Blog中也介绍过了,这里不再重复,签名成功后,将签名数据保存成文件。
(5) 将原文件和签名文件拷贝到Openssl的安装目录下面,然后在命令行模式下调用Openssl命令通过公钥对签名进行验证,命令如下:
dgst –sha256 -verify public.pem -signature signature.bin data.txt
其中public.pem是PEM格式的公钥,signature.bin是WinCE签名后生成的签名文件,data.txt是原文件。
(6) 如果验证成功,会显示Verified或者OK之类的提示,否则会显示错误。
2. WinCE中验证Openssl的签名
(1) 首先在Openssl中创建一个私钥,然后从私钥当中导出DER格式的公钥,命令如下:
genrsa -out private.pem 1024
创建1024bits的PEM格式的私钥private.pem
rsa -in private.pem -pubout -outform DER -out public.der
基于私钥private.pem导出DER格式的公钥public.der
(2) 创建一个文件,然后在Openssl中对文件进行签名,命令行如下:
dgst –sha256 -sign private.pem -out signature.bin data.txt
这里Hash算法使用SHA256,私钥为private.pem,被签名文件是data.txt,生成的签名文件是signature.bin。
(3) 将被签名文件,签名文件还有DER格式的公钥拷贝到WinCE系统当中。
(4) 导入公钥,读取被签名文件和签名文件,然后调用CryptVerifySignature函数对签名进行验证,在上一篇Blog中介绍过,不重复了,如果验证成功,函数返回成功,否则失败。
3. WinCE解密Openssl加密的数据
(1) 首先在Openssl中创建一个私钥,然后从私钥当中导出公钥,命令如下:
genrsa -out private.pem 1024
创建1024bits的PEM格式的私钥private.pem
rsa -pubout -in private.pem -out public.pem
基于私钥private.pem导出PEM格式的公钥public.pem
(2) 转换私钥为PVK格式,这样可以被WinCE系统使用,命令如下:
rsa -in private.pem -outform PVK -pvk-strong -out private.pvk
将private.pem私钥转换为PVK格式的私钥private.pvk
(3) 将私钥文件拷贝到WinCE系统当中,然后导入私钥,关于如何导入,在前几篇Blog中介绍过,这里不再重复了。
(4) 在Openssl命令行下通过公钥对数据文件进行加密,命令行如下:
rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl
通过PEM格式的公钥public.pem对文件file.txt加密,生成file.ssl
(5) 将file.ssl拷贝到WinCE系统中,读取文件数据,然后对文件进行解密,解密函数如下:
BOOL CryptDecrypt(HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final, DWORD dwFlags, BYTE* pbData, DWORD* pdwDataLen)
hKey: 导入私钥的句柄
hHash: Hash对象的句柄,这里设置为0
Final: 是否是最后要被解密的数据
dwFlags: 设置为0
pbData: 作为输入,表示要被解密的数据,作为输出,是解密后的数据
pdwDataLen: 作为输入,表示被解密数据的长度,作为输出,表示解密后数据的长度
函数调用成功,返回TRUE。
4. Openssl解密WinCE加密的数据
(1) 在Openssl中创建一个私钥,然后从私钥当中导出DER格式的公钥,命令如下:
genrsa -out private.pem 1024
创建1024bits的PEM格式的私钥private.pem
rsa -in private.pem -pubout -outform DER -out public.der
基于私钥private.pem导出DER格式的公钥public.der
(2) 在WinCE中导入公钥,并通过公钥对文件进行加密,生成加密文件,函数如下:
/********************************************************************************
* FUNCTION NAME: Cert_Encrypt
*
* DESCRIPTION: Use the public key to encrypt the data
*
* INPUT PARAMETERS:
* puc_PubKey: The public key
* dw_Len: The length of public key
* puc_Random: The random data to be encrypted
* dw_RanLen: The length of random data
*
* OUTPUT PARAMETERS:
* puc_Encrypted: The encrypted data
*
* RETURN VALUE:
* TRUE: Success
* Others: Failed
*
* SPECIAL CONSIDERATIONS:
* <none>
*********************************************************************************/
DWORD Cert_Encrypt(BYTE* puc_PubKey, DWORD dw_Len, BYTE* puc_Random, DWORD dw_RanLen, BYTE* puc_Encrypted)
{
CERT_PUBLIC_KEY_INFO keyinfo;
HCRYPTKEY hPubKey;
HCRYPTPROV hProv;
unsigned char uc_FileBuf[1024];
DWORD dw_Loop, dw_RealSize;
BOOL b_Ret;
b_Ret = FALSE;
// Get handle to the default provider.
if (CryptAcquireContext(&hProv, gtc_KeyContainer, NULL, PROV_RSA_AES, 0) == TRUE)
{
keyinfo.Algorithm.pszObjId = OID_PUBLICKEY;
keyinfo.Algorithm.Parameters.cbData = 0;
keyinfo.Algorithm.Parameters.pbData = NULL;
keyinfo.PublicKey.pbData = puc_PubKey;
keyinfo.PublicKey.cbData = dw_Len;
if (CryptImportPublicKeyInfoEx(hProv, X509_ASN_ENCODING, &keyinfo, 0, 0, NULL, &hPubKey) == TRUE)
{
memcpy(uc_FileBuf, puc_Random, dw_RanLen);
dw_RealSize = dw_RanLen;
if (CryptEncrypt(hPubKey, NULL, TRUE, 0, uc_FileBuf, &dw_RealSize, sizeof(uc_FileBuf)) == TRUE)
{
for (dw_Loop = 0; dw_Loop < dw_RealSize; dw_Loop ++)
{
puc_Encrypted[dw_RealSize - 1 - dw_Loop] = uc_FileBuf[dw_Loop];
}
b_Ret = TRUE;
}
}
}
CryptReleaseContext(hProv, 0);
return b_Ret;
}
(3) 将加密后的数据生成文件file.ssl,然后拷贝到Openssl目录下,调用命令对加密文件进行解密,命令如下:
rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt
用PEM格式的私钥private.pem对加密文件file.ssl进行解密,并生成decrypted.txt文件。
这里介绍了WinCE和Linux之间签名和加解密数据的验证,只是一个例子,可以用于程序的调试。在真正的系统中,WinCE和Linux都应该彼此拥有自己的私钥,并将公钥发给对方,用私钥对文件签名,用公钥验证签名,用公钥加密数据,用私钥解密数据。关于PKI基础知识,这里给个连接:
http://www.youdzone.com/signature.html
多说一句,公钥是不能直接发送给接收方的,因为接收方凭什么相信你呢?所以首先你要向CA提交证书请求,证书中包含公钥,CA对你的请求进行认证后会对你的证书进行签名,然后你可以将这个被CA签名的证书发给接收方,接收方拥有CA的证书,接收方会从CA的证书中取出公钥,然后对接收到的证书进行验证签名,如果验证成功,表示这个证书被CA签过名,是可信任的,然后才会信任发送方,从而从中获得发送方的公钥。接下来就可以验证发送方的签名,并加密数据了。