1.? 此代码的功能:先使用MD5摘要,摘要的结果使用私钥签名,签名结果用base64编码输出
#include <stdio.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/md5.h>
//将二进制流转换成base64编码
char * base64_encode(const unsigned char * bindata, char * base64, int binlength)
{
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int i, j;
unsigned char current;
for (i = 0, j = 0; i < binlength; i += 3)
{
current = (bindata[i] >> 2);
current &= (unsigned char)0x3F;
base64[j++] = base64char[(int)current];
current = ((unsigned char)(bindata[i] << 4)) & ((unsigned char)0x30);
if (i + 1 >= binlength)
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
base64[j++] = '=';
break;
}
current |= ((unsigned char)(bindata[i + 1] >> 4)) & ((unsigned char)0x0F);
base64[j++] = base64char[(int)current];
current = ((unsigned char)(bindata[i + 1] << 2)) & ((unsigned char)0x3C);
if (i + 2 >= binlength)
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
break;
}
current |= ((unsigned char)(bindata[i + 2] >> 6)) & ((unsigned char)0x03);
base64[j++] = base64char[(int)current];
current = ((unsigned char)bindata[i + 2]) & ((unsigned char)0x3F);
base64[j++] = base64char[(int)current];
}
base64[j] = '\0';
return base64;
}
int my_sign(const char *psrc, BIGNUM *signret, const char *pri_key_path, char *SignOut)
{
/* RSA签名过程如下:
1) 对用户数据进行摘要;
2) 构造X509_SIG结构并DER编码,其中包括了摘要算法以及摘要结果。
3) 对2)的结果进行填充,填满RSA密钥长度字节数。比如1024位RSA密钥必须填满128字节。具体的填充方式由用户指定。
4) 对3)的结果用RSA私钥加密。
RSA_eay_private_encrypt函数实现了3)和4)过程。 */
RSA *p_rsa = NULL;
FILE *file = NULL;
unsigned char signstr[512+1];
unsigned char MD5Str[128+1] = {0}; //MD5长度一般为128位
int nid = NID_md5;
int signlen;
//int i = 0,bit = 1024;
int ret = 0;
//int pad = RSA_PKCS1_PADDING;
//nid = NID_md5;
file = fopen(pri_key_path, "rb");
if(!file)
{
ret = -1;
return ret;
}
if((p_rsa = PEM_read_RSAPrivateKey(file, NULL,NULL,NULL )) == NULL)
{
ret = -2;
fclose(file);
return ret;
}
fclose(file);
//. MD5摘要
MD5((const unsigned char *)psrc, strlen(psrc), MD5Str);
//. 签名
ret = RSA_sign(nid, MD5Str, strlen(MD5Str), signstr, &signlen, p_rsa);
if(ret != 1)
{
ret = -3;
RSA_free(p_rsa);
printf("RSA_sign err!\n");
return ret;
}
RSA_free(p_rsa);
//签名成功,转换成base64
base64_encode(signstr, SignOut, signlen);
return 0;
}
int main(int argc, char**argv)
{
BIGNUM *dst = BN_new();
int ret;
char SignOut[2048+1]; //签名后输出的内容
char *psrc = "hello world"; //待签名的源字符串
if(argc >= 2)
{
memset(dst, 0x00, sizeof(dst));
ret = my_sign(psrc, dst, argv[1], SignOut);
if(ret)
{
fprintf(stderr, "Error\n");
}
else
{
printf("base64_SignOut:\n%s\n", SignOut);
}
}
BN_free(dst);
return ret;
}
2.编译(已经下载openssl源码并交叉编译过):
arm-linux-gnueabihf-gcc example.c -o example -I./include -L./lib -lssl -lcrypto
3.验证:
?3.1 首先,使用在线工具生成密钥, 将私钥复制保存为文件testkey.pem
(如果项目中提供了私钥,按照的格式私钥编写pem文件)
?3.2 代码结果与在线签名结果对比(注:截图中的test.key? 就是3.1中的testkey.pem。因为后来觉得用pem后缀显得规范点就改成testkey.pem,截图懒得更换了。秘钥文件与文件名无关)
4.拓展知识
4.1 PKCS#1 和?PKCS#8 格式的区分:有RSA的是PKCS#1
?
4.2 OpenSSL密钥相关命令
1. 生成密钥
openssl genrsa -out key.pem 1024
-out 指定生成文件,此文件包含公钥和私钥两部分,所以即可以加密,也可以解密
1024 生成密钥的长度
2. 提取PEM格式公钥
openssl rsa -in key.pem -pubout -out pubkey.pem
-in 指定输入的密钥文件
-out 指定提取生成公钥的文件(PEM公钥格式)
3. 提取PEM RSAPublicKey格式公钥
openssl rsa -in key.pem -RSAPublicKey_out -out pubkey.pem
-in 指定输入的密钥文件
-out 指定提取生成公钥的文件(PEM RSAPublicKey格式)
4. 公钥加密文件
openssl rsautl -encrypt -in input.file -inkey pubkey.pem -pubin -out output.file
-in 指定被加密的文件
-inkey 指定加密公钥文件
-pubin 表面是用纯公钥文件加密
-out 指定加密后的文件
5. 私钥解密文件
openssl rsautl -decrypt -in input.file -inkey key.pem -out output.file
-in 指定需要解密的文件
-inkey 指定私钥文件
-out 指定解密后的文件
|