openssl 非对称加密 RSA 加密解密以及签名验证签名

时间:2021-08-25 16:47:16

1. 简介

openssl  rsa.h 提供了密码学中公钥加密体系的一些接口,

本文主要讨论利用rsa.h接口开发以下功能

  • 公钥私钥的生成
  • 公钥加密,私钥解密
  • 私钥加密,公钥解密
  • 签名:私钥签名
  • 验证签名:公钥验签

2. 生成公钥私钥对

主要接口,

/* Deprecated version */
DEPRECATEDIN_0_9_8(RSA *RSA_generate_key(int bits, unsigned long e, void
(*callback) (int, int, void *),
void *cb_arg)) /* New version */
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);

接口调用需要先生成一个大数,如下生成密钥对示例

    //生成密钥对
RSA *r = RSA_new();
int bits = ;
BIGNUM *e = BN_new();
BN_set_word(e, );
RSA_generate_key_ex(r, bits, e, NULL);
//打印密钥
RSA_print_fp(stdout, r, );

打印的密钥对结果:

Private-Key: ( bit)
modulus:
:c0::6c:::ed:4e::bb::ec:be:d6::
:bf:9b:be:4f:8b:fb::ae:f2::9c:e7:b8::
a2::9c::cc:4a:a2::1d:::c8:f6:e0::
3a:::c8:1a:d4:b7::::4c:3b:2a::0b:
:::4f:f9
publicExponent: (0x10001)
privateExponent:
:8f::9e:ca:8f:9f::3a:ed:eb:ec:5a::a0:
c1:2f:::::4c::6a:6e:b8:4a:ab:2c::
:e2:3e:c8:aa::bb::9e:e5:::b4:8f::
::dc:::::::ac::f8:fe:4d::
e1:e2:bf:fd:
prime1:
:fd::4d:f0::a0::5e:d1:c9:0e:b8::f9:
ce:0a:ef::e7:a4:::d8:fd:dd:e6:c4:::
dd:e6:
prime2:
:c2::a9:7b:c8:::::f0::9a::a2:
0b::3b::c0::6d:c6:c7:d1:a1::1d:d3:7d:
:cd:
exponent1:
6c::d8:2a:6b:4f::dd:::::f7:b5:c7:
ad:f2::5b:f7:7b:ca:::0c:eb:d3::f9:ac:
:f5
exponent2:
::e2:5a:::db:1e::2a::3c:6a:e7::
ac:e2:d7:a5::5f::c3:4d:cf::d8::7f::
:9d
coefficient:
:d7:0d:9b:e8:2f:3c::::a0:b2:8b::1d:
e2:b9:0f:9f:ca:b2:::ea:c8:9d:5e::e5:e3:
::aa

3. 公钥加密,私钥解密

主要接口

int RSA_public_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);

由于较长数据需要分组加密,如下封装了一层

//公钥加密
int kkrsa_public_encrypt(char *inStr,char *outData,RSA *r)
{
int encRet = ;
unsigned long inLen = strlen(inStr);
int pdBlock = RSA_size(r)-;
unsigned int eCount = (inLen / pdBlock) +;
//分组加密,可以看出outData最大不超过malloc[eCount*pdBlock]
for (int i=; i < eCount; i++) {
RSA_public_encrypt(inLen > pdBlock?pdBlock:inLen, inStr, outData, r, RSA_PKCS1_PADDING);
inStr += pdBlock;
outData+=RSA_size(r);
encRet+=RSA_size(r);
inLen -= pdBlock;
}
return encRet;
}
//私钥解密
int kkrsa_private_decrypt(char *inStr,char *outData,RSA *r)
{
int decRet = ;
unsigned long inLen = strlen(inStr);
int pdBlock = RSA_size(r);
unsigned int dCount = inLen / pdBlock;
//分组解密
for (int i=; i < dCount; i++) {
int ret = RSA_private_decrypt(pdBlock, inStr, outData, r, RSA_PKCS1_PADDING);
inStr += pdBlock;
outData+=ret;
decRet+=ret;
}
return decRet;
}

测试例子,例子中的r,就是上面生成的RSA密钥对,

    //测试一
printf("block:%d \n",RSA_size(r));
char *src = "this is test encrypt data use RSA_PKCS1_PADDING";
printf("src:%s len=%d\n",src,strlen(src));
char *encDat = malloc();
//公钥加密
int encRet = kkrsa_public_encrypt(src, encDat, r);
printf("enc:%d\n",encRet); char *decDat = malloc();
//私钥解密
int decRet = kkrsa_private_decrypt(encDat, decDat, r);
printf("dec:%s len=%d\n",decDat,decRet); free(encDat);
free(decDat);

打印结果:

block:
src:this is test encrypt data use RSA_PKCS1_PADDING len=
enc:
dec:this is test encrypt data use RSA_PKCS1_PADDING len= test2
src:this is test private encrypt data use RSA_PKCS1_PADDING len=
enc:
dec:this is test private encrypt data use RSA_PKCS1_PADDI len=

4. 私钥加密,公钥解密

主要接口

int RSA_private_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);

同样如果数据较长需要进行分组加密,如下简单封装的接口

//私钥加密
int kkrsa_private_encrypt(char *inStr,char *outData,RSA *r)
{
int encRet = ;
unsigned long inLen = strlen(inStr);
int pdBlock = RSA_size(r)-;
unsigned int eCount = (inLen / pdBlock) +;
//分组加密,可以看出outData最大不超过malloc[eCount*pdBlock]
for (int i=; i < eCount; i++) {
RSA_private_encrypt(inLen > pdBlock?pdBlock:inLen, inStr, outData, r, RSA_PKCS1_PADDING);
inStr += pdBlock;
outData+=RSA_size(r);
encRet+=RSA_size(r);
inLen -= pdBlock;
}
return encRet;
}
//公钥解密
int kkrsa_public_decrypt(char *inStr,char *outData,RSA *r)
{
int decRet = ;
unsigned long inLen = strlen(inStr);
int pdBlock = RSA_size(r);
unsigned int dCount = inLen / pdBlock;
//分组解密
for (int i=; i < dCount; i++) {
int ret = RSA_public_decrypt(pdBlock, inStr, outData, r, RSA_PKCS1_PADDING);
inStr += pdBlock;
outData+=ret;
decRet+=ret;
}
return decRet;
}

调用示例:需要上面生成的密钥对RSA r

  //测试二
printf("\ntest2\n");
char *src2 = "this is test private encrypt data use RSA_PKCS1_PADDING";
printf("src:%s len=%d\n",src2,strlen(src2));
char *encDat2 = malloc();
//私钥加密
int encRet2 = kkrsa_private_encrypt(src2, encDat2, r);
printf("enc:%d\n",encRet2); char *decDat2 = malloc();
//公钥解密
int decRet2 = kkrsa_public_decrypt(encDat2, decDat2, r);
printf("dec:%s len=%d\n",decDat2,decRet2); free(encDat2);
free(decDat2);

测试结果:

test2
src:this is test private encrypt data use RSA_PKCS1_PADDING len=
enc:
dec:this is test private encrypt data use RSA_PKCS1_PADDING\\\375۷GO\ len=

5. 签名与验证签名

主要接口

int RSA_sign(int type, const unsigned char *m, unsigned int m_length,
unsigned char *sigret, unsigned int *siglen, RSA *rsa);
int RSA_verify(int type, const unsigned char *m, unsigned int m_length,
const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);

测试示例,同样需要上面生成的RSA密钥对

    //签名
printf("\ntest sign and verify\n");
char *msg = "";
char *sinDat = malloc(RSA_size(r));
int sinLen = ;
RSA_sign(NID_sha1, msg,strlen(msg),sinDat,&sinLen, r); int vret = RSA_verify(NID_sha1, msg, strlen(msg), sinDat, sinLen, r);
printf("sign_verify=%d\n",vret);

打印结果

test sign and verify
sign_verify=

6. 总结

上述RSA分组加密中使用了RSA_PKCS1_PADDING 的补位方式;当然还有如下

不同的补位方式,在进行分组加密时,需要注意分组块的处理

# define RSA_PKCS1_PADDING
# define RSA_SSLV23_PADDING
# define RSA_NO_PADDING
# define RSA_PKCS1_OAEP_PADDING
# define RSA_X931_PADDING
/* EVP_PKEY_ only */
# define RSA_PKCS1_PSS_PADDING # define RSA_PKCS1_PADDING_SIZE

测试使用 openssl 1.1.0c

参考:https://www.openssl.org/docs/man1.0.2/crypto/RSA_public_encrypt.html

https://www.openssl.org/docs/manmaster/man3/RSA_verify.html