openssl 非对称加密 RSA 加密解密以及签名验证签名
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 = 512; BIGNUM *e = BN_new(); BN_set_word(e, 65537); RSA_generate_key_ex(r, bits, e, NULL); //打印密钥 RSA_print_fp(stdout, r, 0);
打印的密钥对结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Private-Key: (512 bit) modulus: 00:c0:53:6c:46:57:ed:4e:33:bb:71:ec:be:d6:21: 78:bf:9b:be:4f:8b:fb:32:ae:f2:83:9c:e7:b8:63: a2:34:9c:98:cc:4a:a2:17:1d:31:03:c8:f6:e0:13: 3a:29:40:c8:1a:d4:b7:88:38:07:4c:3b:2a:01:0b: 17:45:36:4f:f9 publicExponent: 65537 (0x10001) privateExponent: 00:8f:59:9e:ca:8f:9f:01:3a:ed:eb:ec:5a:11:a0: c1:2f:90:16:39:94:4c:97:6a:6e:b8:4a:ab:2c:74: 96:e2:3e:c8:aa:34:bb:99:9e:e5:60:86:b4:8f:55: 49:80:dc:26:06:74:13:64:49:ac:31:f8:fe:4d:80: e1:e2:bf:fd:41 prime1: 00:fd:95:4d:f0:25:a0:87:5e:d1:c9:0e:b8:96:f9: ce:0a:ef:59:e7:a4:57:49:d8:fd:dd:e6:c4:59:24: dd:e6:33 prime2: 00:c2:28:a9:7b:c8:98:97:33:32:f0:52:9a:26:a2: 0b:50:3b:86:c0:55:6d:c6:c7:d1:a1:43:1d:d3:7d: 53:cd:23 exponent1: 6c:80:d8:2a:6b:4f:36:dd:21:92:90:13:f7:b5:c7: ad:f2:20:5b:f7:7b:ca:03:69:0c:eb:d3:13:f9:ac: 60:f5 exponent2: 55:44:e2:5a:18:98:db:1e:83:2a:84:3c:6a:e7:13: ac:e2:d7:a5:34:5f:87:c3:4d:cf:52:d8:90:7f:24: 04:9d coefficient: 00:d7:0d:9b:e8:2f:3c:00:86:00:a0:b2:8b:00:1d: e2:b9:0f:9f:ca:b2:75:84:ea:c8:9d:5e:78:e5:e3: 92:46: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 = 0; unsigned long inLen = strlen(inStr); int pdBlock = RSA_size(r)-11; unsigned int eCount = (inLen / pdBlock) +1; //分组加密,可以看出outData最大不超过malloc[eCount*pdBlock] for (int i=0; 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 = 0; unsigned long inLen = strlen(inStr); int pdBlock = RSA_size(r); unsigned int dCount = inLen / pdBlock; //分组解密 for (int i=0; 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(1024); //公钥加密 int encRet = kkrsa_public_encrypt(src, encDat, r); printf("enc:%d\n",encRet); char *decDat = malloc(1024); //私钥解密 int decRet = kkrsa_private_decrypt(encDat, decDat, r); printf("dec:%s len=%d\n",decDat,decRet); free(encDat); free(decDat);
打印结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
block:64 src:this is test encrypt data use RSA_PKCS1_PADDING len=47 enc:64 dec:this is test encrypt data use RSA_PKCS1_PADDING len=47 test2 src:this is test private encrypt data use RSA_PKCS1_PADDING len=55 enc:128 dec:this is test private encrypt data use RSA_PKCS1_PADDI len=53
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 = 0; unsigned long inLen = strlen(inStr); int pdBlock = RSA_size(r)-11; unsigned int eCount = (inLen / pdBlock) +1; //分组加密,可以看出outData最大不超过malloc[eCount*pdBlock] for (int i=0; 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 = 0; unsigned long inLen = strlen(inStr); int pdBlock = RSA_size(r); unsigned int dCount = inLen / pdBlock; //分组解密 for (int i=0; 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(1024); //私钥加密 int encRet2 = kkrsa_private_encrypt(src2, encDat2, r); printf("enc:%d\n",encRet2); char *decDat2 = malloc(1024); //公钥解密 int decRet2 = kkrsa_public_decrypt(encDat2, decDat2, r); printf("dec:%s len=%d\n",decDat2,decRet2); free(encDat2); free(decDat2);
测试结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
test2 src:this is test private encrypt data use RSA_PKCS1_PADDING len=55 enc:128 dec:this is test private encrypt data use RSA_PKCS1_PADDING\372\314\375۷GO\304 len=55
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 = "0123456789"; char *sinDat = malloc(RSA_size(r)); int sinLen = 0; 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);
打印结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
test sign and verify sign_verify=1
6. 总结
上述RSA分组加密中使用了RSA_PKCS1_PADDING 的补位方式;当然还有如下
不同的补位方式,在进行分组加密时,需要注意分组块的处理
# define RSA_PKCS1_PADDING 1 # define RSA_SSLV23_PADDING 2 # define RSA_NO_PADDING 3 # define RSA_PKCS1_OAEP_PADDING 4 # define RSA_X931_PADDING 5 /* EVP_PKEY_ only */ # define RSA_PKCS1_PSS_PADDING 6 # define RSA_PKCS1_PADDING_SIZE 11
测试使用 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