RSA公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制 。在公开密钥密码体制中,加密密钥(即公开密钥)PK是公开信息,而解密密钥(即秘密密钥)SK是需要保密的。加密算法E和解密算法D也都是公开的。虽然解密密钥SK是由公开密钥PK决定的,但却不能根据PK计算出SK 。
正是基于这种理论,1978年出现了著名的RSA算法,它通常是先生成一对RSA密钥,其中之一是保密密钥,由用户保存;另一个为公开密钥,可对外公开,甚至可在网络服务器中注册。为提高保密强度,RSA密钥至少为500位长,一般推荐使用1024位。这就使加密的计算量很大。为减少计算量,在传送信息时,常采用传统加密方法与公开密钥加密方法相结合的方式,即信息采用改进的DES或IDEA对话密钥加密,然后使用RSA密钥加密对话密钥和信息摘要。对方收到信息后,用不同的密钥解密并可核对信息摘要 。
RSA算法是一个广泛使用的公钥算法。其密钥包括公钥和私钥。它能用于数字签名、身份认证以及密钥交换。RSA密钥长度一般使用1024位或者更高。RSA密钥信息主要包括:
n:模数
e:公钥指数
d:私钥指数
p:最初的大素数
q:最初的大素数
其中,公钥为n和e;私钥为n和d。
本文假设你已经安装好了OpenSSL,并且持有一份1.1.1的源码。
RSA相关的头文件在rsa.h中、源文件在crypto/rsa目录中。
struct rsa_st {
/*
* The first parameter is used to pickup errors where this is passed
* instead of an EVP_PKEY, it is set to 0
*/
int pad;
int32_t version;
const RSA_METHOD *meth;
/* functional reference if 'meth' is ENGINE-provided */
ENGINE *engine;
BIGNUM *n;
BIGNUM *e;
BIGNUM *d;
BIGNUM *p;
BIGNUM *q;
BIGNUM *dmp1;
BIGNUM *dmq1;
BIGNUM *iqmp;
/* for multi-prime RSA, defined in RFC 8017 */
STACK_OF(RSA_PRIME_INFO) *prime_infos;
/* If a PSS only key this contains the parameter restrictions */
RSA_PSS_PARAMS *pss;
/* be careful using this if the RSA structure is shared */
CRYPTO_EX_DATA ex_data;
CRYPTO_REF_COUNT references;
int flags;
/* Used to cache montgomery values */
BN_MONT_CTX *_method_mod_n;
BN_MONT_CTX *_method_mod_p;
BN_MONT_CTX *_method_mod_q;
/*
* all BIGNUM values are actually in the following data, if it is not
* NULL
*/
char *bignum_data;
BN_BLINDING *blinding;
BN_BLINDING *mt_blinding;
CRYPTO_RWLOCK *lock;
};
typedef struct rsa_st RSA;
这个结构定义了RSA内部数据信息。主要字段含义:
version —— 版本。
meth —— RSA运算抽象方法集合。
n,e,d,p,q,dmp1,dmq1,iqmp —— 密钥相关的大数。
struct rsa_meth_st {
char *name;
int (*rsa_pub_enc) (int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int (*rsa_pub_dec) (int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int (*rsa_priv_enc) (int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int (*rsa_priv_dec) (int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
/* Can be null */
int (*rsa_mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
/* Can be null */
int (*bn_mod_exp) (BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx);
/* called at new */
int (*init) (RSA *rsa);
/* called at free */
int (*finish) (RSA *rsa);
/* RSA_METHOD_FLAG_* things */
int flags;
/* may be needed! */
char *app_data;
/*
* New sign and verify functions: some libraries don't allow arbitrary
* data to be signed/verified: this allows them to be used. Note: for
* this to work the RSA_public_decrypt() and RSA_private_encrypt() should
* *NOT* be used RSA_sign(), RSA_verify() should be used instead.
*/
int (*rsa_sign) (int type,
const unsigned char *m, unsigned int m_length,
unsigned char *sigret, unsigned int *siglen,
const RSA *rsa);
int (*rsa_verify) (int dtype, const unsigned char *m,
unsigned int m_length, const unsigned char *sigbuf,
unsigned int siglen, const RSA *rsa);
/*
* If this callback is NULL, the builtin software RSA key-gen will be
* used. This is for behavioural compatibility whilst the code gets
* rewired, but one day it would be nice to assume there are no such
* things as "builtin software" implementations.
*/
int (*rsa_keygen) (RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
int (*rsa_multi_prime_keygen) (RSA *rsa, int bits, int primes,
BIGNUM *e, BN_GENCB *cb);
};
typedef struct rsa_meth_st RSA_METHOD;
这个结构定义了RSA内部各种运算抽象方法集合。主要字段含义:
name —— 名称描述。
rsa_pub_enc —— 公钥加密方法。
rsa_pub_dec —— 公钥解密方法。
rsa_priv_enc —— 私钥加密方法。
rsa_priv_dec —— 公钥解密方法。
rsa_sign —— 签名方法。
rsa_verify —— 验签方法。
rsa_keygen —— 生成密钥对方法。
在1.1.1中,大多数的数据结构已经不再向使用者开放,从封装的角度来看,这是更合理的。如果你在头文件中找不到结构定义,不妨去源码中搜一搜。
RSA *RSA_new(void);
生成一个RSA密钥结构,采用默认的rsa_pkcs1_ossl_meth方法。
void RSA_free(RSA *r);
释放RSA结构。
RSA *RSA_generate_key(int bits, unsigned long e, void (*callback) (int, int, void *), void *cb_arg);
生成RSA密钥(旧版本)。
bits为密钥位数,e为公钥指数。
callback为密钥生成过程中的干预回调函数,通常传入NULL。cb_arg为回调参数。
成功返回RSA指针,失败返回NULL。
int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
生成RSA密钥(新版本)。
rsa为RSA对象指针。bits为密钥位置,e为公钥指数的大数形式指针。cb为干预回调函数,通常传入NULL。
成功返回1,失败返回0。
关于公钥指数e,主要有两个取值:
# define RSA_3 0x3L
# define RSA_F4 0x10001L
RSA *RSAPublicKey_dup(RSA *rsa);
复制RSA公钥部分。
成功返回RSA指针,失败返回NULL。
RSA *RSAPrivateKey_dup(RSA *rsa);
复制RSA私钥部分。
成功返回RSA指针,失败返回NULL。
int RSA_bits(const RSA *rsa);
获取RSA密钥位数。
int RSA_size(const RSA *rsa);
获取RSA密钥长度。
int RSA_check_key(const RSA *);
int RSA_check_key_ex(const RSA *, BN_GENCB *cb);
检查RSA的有效性,必须为完整的密钥对。
成功返回1,失败返回0。
int RSA_print(BIO *bp, const RSA *r, int offset);
int RSA_print_fp(FILE *fp, const RSA *r, int offset);
将RSA信息输出到bp/fp中,off为输出信息在bp/fp中的偏移量,比如是屏幕bp/fp,则表示打印信息的位置离左边屏幕边缘的距离。
int RSA_public_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
RSA公钥加密。
成功返回密文的长度,失败返回-1。
int RSA_public_decrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
RSA公钥解密。
成功返回明文的长度,失败返回-1。
int RSA_private_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
RSA私钥加密。
成功返回密文的长度,失败返回-1。
int RSA_private_decrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
RSA私钥解密。
成功返回明文的长度,失败返回-1。
关于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
其中PKCS1填充大小为11字节,所以加密明文长度必须不大于(密钥大小-11字节)。
# define RSA_PKCS1_PADDING_SIZE 11
int RSA_sign(int type, const unsigned char *m, unsigned int m_length,
unsigned char *sigret, unsigned int *siglen, RSA *rsa);
对数据m生成RSA签名,生成的签名长度与key的长度相同,如512位密钥生成64字节签名。
type指定摘要算法的NID,如NID_sha1。
成功返回1,失败返回0。
int RSA_verify(int type, const unsigned char *m, unsigned int m_length,
const unsigned char *sigbuf, unsigned int siglen, RSA *rsa);
对数据m验证RSA签名。
type指定摘要算法的NID,如NID_sha1。
成功返回1,失败返回0。
以下函数在x509.h中定义:
int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
{
return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa);
}
将RSA公钥转换为DER编码,并写入到bp抽象IO中。
成功返回1,失败返回0。
RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
{
return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPrivateKey), bp, rsa);
}
从bp抽象IO中读取DER编码,并转换为rsa结构私钥。
成功返回有效指定,失败返回NULL。
int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
{
return ASN1_item_i2d_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa);
}
将RSA公钥转换为DER编码,并写入到bp抽象IO中。
成功返回1,失败返回0。
RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
{
return ASN1_item_d2i_bio(ASN1_ITEM_rptr(RSAPublicKey), bp, rsa);
}
从bp抽象IO中读取DER编码,并转换为rsa结构公钥。
成功返回有效指定,失败返回NULL。
下面这个例子演示了RSA密钥对的生成、公私钥的复制、公钥加密和私钥解密的用法。
#include
#include
#include
#include
#include
#include
namespace dakuang {}
int main(int argc, char* argv[])
{
ERR_load_RSA_strings();
RSA* pRSA = RSA_generate_key(512, RSA_3, NULL, NULL);
RSA_print_fp(stdout, pRSA , 0);
RSA_free(pRSA);
pRSA = RSA_new();
BIGNUM* pBNe = BN_new();
BN_set_word(pBNe, RSA_3);
int ret = RSA_generate_key_ex(pRSA, 512, pBNe, NULL);
printf("RSA_generate_key_ex() ret:%d \n", ret);
BN_free(pBNe);
RSA_print_fp(stdout, pRSA , 0);
RSA* pRSApub = RSAPublicKey_dup(pRSA);
printf("copy pub key:%p \n", pRSApub);
RSA_print_fp(stdout, pRSApub , 0);
RSA_free(pRSApub);
RSA* pRSApri = RSAPrivateKey_dup(pRSA);
printf("copy pri key:%p \n", pRSApri);
RSA_print_fp(stdout, pRSApri , 0);
RSA_free(pRSApri);
int bits = RSA_bits(pRSA);
printf("bits:%d \n", bits);
int bytes = RSA_size(pRSA);
printf("bytes:%d \n", bytes);
ret = RSA_check_key(pRSA);
printf("RSA_check_key() ret:%d \n", ret);
char sText[] = "1234567890";
unsigned char* pCipher = (unsigned char*)malloc(bytes);
ret = RSA_public_encrypt(strlen(sText), (const unsigned char*)sText, pCipher, pRSA, RSA_PKCS1_PADDING);
printf("RSA_public_encrypt() ret:%d \n", ret);
if (ret <= 0)
{
unsigned long err = ERR_get_error();
printf("err:[%ld] \n", err);
char sBuf[128] = {0};
char* pErrStr = ERR_error_string(err, sBuf);
printf("errstr:[%s] \n", pErrStr);
}
unsigned char* pText = (unsigned char*)malloc(bytes);
ret = RSA_private_decrypt(ret, (const unsigned char*)pCipher, pText, pRSA, RSA_PKCS1_PADDING);
printf("RSA_private_decrypt() ret:%d \n", ret);
if (ret > 0)
{
printf("text:[%s] \n", pText);
}
free(pCipher);
free(pText);
RSA_free(pRSA);
return 0;
}
输出:
RSA Private-Key: (512 bit, 2 primes)
modulus:
00:9f:d4:b7:53:c9:22:30:8d:52:31:13:67:61:ce:
12:bc:25:85:44:94:0e:87:e2:b9:8c:87:dd:ca:d0:
df:1c:84:51:c7:ac:ec:a3:3f:2b:48:81:74:18:22:
9a:c2:a8:11:03:8c:19:b6:a6:45:2d:81:d4:18:65:
bb:90:59:e6:d5
publicExponent: 3 (0x3)
privateExponent:
6a:8d:cf:8d:30:c1:75:b3:8c:20:b7:9a:41:34:0c:
7d:6e:58:d8:62:b4:5a:97:26:5d:af:e9:31:e0:94:
bd:ac:7d:e2:9e:a3:b3:4c:e9:23:41:bf:3c:ca:4d:
3e:ee:b6:f5:36:1c:7a:9c:2d:70:e3:20:94:53:b6:
e9:0a:13:eb
prime1:
00:d3:69:55:a1:b4:24:1d:11:56:18:3e:b4:bf:06:
23:05:69:d2:12:78:1e:b5:45:47:8c:3f:8a:6e:02:
48:d5:4d
prime2:
00:c1:8a:69:55:62:27:b0:82:48:bc:fe:3e:67:de:
1e:f9:29:e8:dc:86:9d:4b:be:e5:96:fa:5d:bb:30:
81:f3:a9
exponent1:
00:8c:f0:e3:c1:22:c2:be:0b:8e:ba:d4:78:7f:59:
6c:ae:46:8c:0c:50:14:78:d8:da:5d:7f:b1:9e:ac:
30:8e:33
exponent2:
00:81:06:f0:e3:96:c5:20:56:db:28:a9:7e:ef:e9:
69:fb:71:45:e8:59:be:32:7f:43:b9:fc:3e:7c:cb:
01:4d:1b
coefficient:
00:cb:09:a1:95:b6:64:0c:d0:50:50:58:cb:20:fd:
bc:7c:e6:fb:56:22:73:75:34:7b:44:6d:a5:94:87:
59:8f:58
RSA_generate_key_ex() ret:1
RSA Private-Key: (512 bit, 2 primes)
modulus:
00:ac:3b:05:ad:53:48:42:fb:8e:a0:52:53:29:71:
3b:30:a6:47:74:99:7b:9f:94:48:93:92:1f:57:78:
01:07:6c:db:bd:34:f0:c9:35:42:7e:20:c1:ec:94:
7d:0b:cc:c7:70:56:52:87:cc:04:66:30:4b:06:fb:
49:ee:78:eb:af
publicExponent: 3 (0x3)
privateExponent:
72:d2:03:c8:e2:30:2c:a7:b4:6a:e1:8c:c6:4b:7c:
cb:19:84:f8:66:52:6a:62:db:0d:0c:14:e4:fa:ab:
5a:47:7a:64:2a:7b:1c:27:0b:c2:37:a0:7f:52:fc:
53:17:3e:ba:9f:cb:0b:4d:5b:94:0f:38:f1:97:67:
eb:d7:a3:ab
prime1:
00:d8:79:81:07:43:58:8f:a7:ab:db:04:bf:e5:a0:
03:35:7c:13:76:5b:22:2c:de:e0:c7:29:4b:66:65:
e3:aa:ef
prime2:
00:cb:ad:74:30:db:a2:21:33:21:76:28:d8:1c:ef:
26:b3:dc:53:2b:9b:b5:ce:29:38:ae:73:4c:c7:a6:
d1:cb:41
exponent1:
00:90:51:00:af:82:3b:0a:6f:c7:e7:58:7f:ee:6a:
ac:ce:52:b7:a4:3c:c1:73:3f:40:84:c6:32:44:43:
ed:1c:9f
exponent2:
00:87:c8:f8:20:92:6c:16:22:16:4e:c5:e5:68:9f:
6f:22:92:e2:1d:12:79:34:1b:7b:1e:f7:88:85:19:
e1:32:2b
coefficient:
00:bd:69:e1:82:71:0e:e3:e6:bb:c3:55:2a:10:f7:
b1:26:1e:c2:9a:d7:44:9f:88:be:aa:3e:7d:d1:7c:
c0:94:ba
copy pub key:0x203f770
RSA Public-Key: (512 bit)
Modulus:
00:ac:3b:05:ad:53:48:42:fb:8e:a0:52:53:29:71:
3b:30:a6:47:74:99:7b:9f:94:48:93:92:1f:57:78:
01:07:6c:db:bd:34:f0:c9:35:42:7e:20:c1:ec:94:
7d:0b:cc:c7:70:56:52:87:cc:04:66:30:4b:06:fb:
49:ee:78:eb:af
Exponent: 3 (0x3)
copy pri key:0x203f770
RSA Private-Key: (512 bit, 2 primes)
modulus:
00:ac:3b:05:ad:53:48:42:fb:8e:a0:52:53:29:71:
3b:30:a6:47:74:99:7b:9f:94:48:93:92:1f:57:78:
01:07:6c:db:bd:34:f0:c9:35:42:7e:20:c1:ec:94:
7d:0b:cc:c7:70:56:52:87:cc:04:66:30:4b:06:fb:
49:ee:78:eb:af
publicExponent: 3 (0x3)
privateExponent:
72:d2:03:c8:e2:30:2c:a7:b4:6a:e1:8c:c6:4b:7c:
cb:19:84:f8:66:52:6a:62:db:0d:0c:14:e4:fa:ab:
5a:47:7a:64:2a:7b:1c:27:0b:c2:37:a0:7f:52:fc:
53:17:3e:ba:9f:cb:0b:4d:5b:94:0f:38:f1:97:67:
eb:d7:a3:ab
prime1:
00:d8:79:81:07:43:58:8f:a7:ab:db:04:bf:e5:a0:
03:35:7c:13:76:5b:22:2c:de:e0:c7:29:4b:66:65:
e3:aa:ef
prime2:
00:cb:ad:74:30:db:a2:21:33:21:76:28:d8:1c:ef:
26:b3:dc:53:2b:9b:b5:ce:29:38:ae:73:4c:c7:a6:
d1:cb:41
exponent1:
00:90:51:00:af:82:3b:0a:6f:c7:e7:58:7f:ee:6a:
ac:ce:52:b7:a4:3c:c1:73:3f:40:84:c6:32:44:43:
ed:1c:9f
exponent2:
00:87:c8:f8:20:92:6c:16:22:16:4e:c5:e5:68:9f:
6f:22:92:e2:1d:12:79:34:1b:7b:1e:f7:88:85:19:
e1:32:2b
coefficient:
00:bd:69:e1:82:71:0e:e3:e6:bb:c3:55:2a:10:f7:
b1:26:1e:c2:9a:d7:44:9f:88:be:aa:3e:7d:d1:7c:
c0:94:ba
bits:512
bytes:64
RSA_check_key() ret:1
RSA_public_encrypt() ret:64
RSA_private_decrypt() ret:10
text:[1234567890]
下面这个例子演示了公私钥的分开保存、读取,以及使用公私钥加解密。
#include
#include
#include
#include
#include
#include
#include
namespace dakuang {}
int main(int argc, char* argv[])
{
ERR_load_RSA_strings();
RSA* pRSA = RSA_new();
BIGNUM* pBNe = BN_new();
BN_set_word(pBNe, RSA_3);
int ret = RSA_generate_key_ex(pRSA, 512, pBNe, NULL);
printf("RSA_generate_key_ex() ret:%d \n", ret);
BN_free(pBNe);
BIO* pBIOpri = BIO_new_file("rsa.key","w");
BIO* pBIOpub = BIO_new_file("rsa.pub","w");
ret = i2d_RSAPrivateKey_bio(pBIOpri, pRSA);
printf("i2d_RSAPrivateKey_bio() ret:%d \n", ret);
ret = i2d_RSAPublicKey_bio(pBIOpub, pRSA);
printf("i2d_RSAPublicKey_bio() ret:%d \n", ret);
BIO_free(pBIOpri);
BIO_free(pBIOpub);
RSA* pRSApri = RSA_new();
RSA* pRSApub = RSA_new();
BIO* pBIOpri2 = BIO_new_file("rsa.key","r");
BIO* pBIOpub2 = BIO_new_file("rsa.pub","r");
d2i_RSAPrivateKey_bio(pBIOpri2, &pRSApri);
d2i_RSAPublicKey_bio(pBIOpub2, &pRSApub);
BIO_free(pBIOpri2);
BIO_free(pBIOpub2);
int bytes = RSA_size(pRSApri);
printf("copy' private key size:%d \n", bytes);
int bytes2 = RSA_size(pRSApub);
printf("copy' public key size:%d \n", bytes2);
char sText[] = "1234567890";
unsigned char* pCipher = (unsigned char*)malloc(bytes);
ret = RSA_public_encrypt(strlen(sText), (const unsigned char*)sText, pCipher, pRSApub, RSA_PKCS1_PADDING);
printf("RSA_public_encrypt() ret:%d \n", ret);
if (ret <= 0)
{
unsigned long err = ERR_get_error();
printf("err:[%ld] \n", err);
char sBuf[128] = {0};
char* pErrStr = ERR_error_string(err, sBuf);
printf("errstr:[%s] \n", pErrStr);
}
unsigned char* pText = (unsigned char*)malloc(bytes);
ret = RSA_private_decrypt(ret, (const unsigned char*)pCipher, pText, pRSApri, RSA_PKCS1_PADDING);
printf("RSA_private_decrypt() ret:%d \n", ret);
if (ret > 0)
{
printf("text:[%s] \n", pText);
}
free(pCipher);
free(pText);
RSA_free(pRSApri);
RSA_free(pRSApub);
RSA_free(pRSA);
return 0;
}
输出:
RSA_generate_key_ex() ret:1
i2d_RSAPrivateKey_bio() ret:1
i2d_RSAPublicKey_bio() ret:1
copy' private key size:64
copy' public key size:64
RSA_public_encrypt() ret:64
RSA_private_decrypt() ret:10
text:[1234567890]
下面这个例子演示了签名的生成和验证操作。
#include
#include
#include
#include
#include
#include
#include
namespace dakuang {}
void printHex(const unsigned char* pBuf, int nLen)
{
for (int i = 0; i < nLen; ++i)
{
printf("%02x", pBuf[i]);
}
printf("\n");
}
int main(int argc, char* argv[])
{
ERR_load_RSA_strings();
RSA* pRSA = RSA_new();
BIGNUM* pBNe = BN_new();
BN_set_word(pBNe, RSA_3);
int ret = RSA_generate_key_ex(pRSA, 512, pBNe, NULL);
printf("ret:%d \n", ret);
BN_free(pBNe);
char sData[] = "1234567890";
char sSign[512] = {0};
unsigned int nSignLen = 512;
ret = RSA_sign(NID_sha1, (unsigned char *)sData, 10, (unsigned char *)sSign, &nSignLen, pRSA);
printf("RSA_sign() ret:%d \n", ret);
printf("sign %d \n", nSignLen);
printHex((const unsigned char*)sSign, nSignLen);
//sSign[0] = 0;
ret = RSA_verify(NID_sha1, (unsigned char *)sData, 10, (unsigned char *)sSign, nSignLen, pRSA);
printf("RSA_verify() ret:%d \n", ret);
RSA_free(pRSA);
return 0;
}
输出:
ret:1
RSA_sign() ret:1
sign 64
4ec0af099c49646b72fda88a4fb11e8deb3898da9c3f611a5f25f05d9d005631858239bbb732cd5060dbc975363fc1b9cdfdc5a04554115a916f06f98163189f
RSA_verify() ret:1