主要介绍了openssl之rsa相关函数,这个对学习和实现rsa算法比较有帮助。
rsa基 本结构
struct
{
int pad;
long version;
const rsa_method *meth;
engine *engine;
bignum *n; n=p*q
bignum *e; 公开的加密指数,经常为65537(ox10001)
bignum *d; 私钥
bignum *p; 大素数p
bignum *q; 大素数q
bignum *dmp1; d mod (p-1)
bignum *dmq1; d mod (q-1)
bignum *iqmp; (inverse of q) mod p
int references;
int flags;
// ...
}rsa;
.初始化函数
rsa * rsa_new(void);初始化一个rsa结构
void rsa_free(rsa *rsa);释放一个rsa结构
.rsa私 钥产生函数
rsa *rsa_generate_key(int num, unsigned long e,void (*callback)(int,int,void *), void *cb_arg);产生一个模为num位的密钥对,e为公开的加密指数,一般为65537(ox10001),假如后两个参数不为null,将有些调用。在产生密钥对之前,一般需要指定随机数种子
.判断位数函 数
int rsa_size(const rsa *rsa);返回rsa模的位数,他用来判断需要给加密值分配空间的大小
int rsa_check_key(rsa *rsa);他测试p,q是否为素数,n=p*q,d*e = 1 mod (p-1*q-1), dmp1, dmq1, iqmp是否均设置正确了。
.rsa的rsa_method函 数
了解rsa的运算那就必须了解rsa_method,下面我们先看看rsa_method结构
typedef struct rsa_meth_st
{
const 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);
int (*rsa_mod_exp)(bignum *r0,const bignum *i,rsa *rsa); int (*bn_mod_exp)(bignum *r, const bignum *a, const bignum *p,
const bignum *m, bn_ctx *ctx,bn_mont_ctx *m_ctx);
int (*init)(rsa *rsa); /* called at new */
int (*finish)(rsa *rsa); /* called at free */
int flags; /* rsa_method_flag_* things */
char *app_data; /* may be needed! */
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,unsigned char *sigbuf, unsigned int siglen, const rsa *rsa);
} rsa_method;
const rsa_method *rsa_pkcs1_ssleay(void);
const rsa_method *rsa_null_method(void);
主要有上面两个函数。第二个函数是定义了rsa_null才会调用,其实要调用这个函数以后几乎什么都不能干,只是输出错误信息。第一个是常用的method,下面我们看看它的定义
const rsa_method *rsa_pkcs1_ssleay(void)
{
return(&rsa_pkcs1_eay_meth);
}
static rsa_method rsa_pkcs1_eay_meth={
"eric young's pkcs#1 rsa",
rsa_eay_public_encrypt,
rsa_eay_public_decrypt, /* signature verification */
rsa_eay_private_encrypt, /* signing */
rsa_eay_private_decrypt,
rsa_eay_mod_exp,
bn_mod_exp_mont,
rsa_eay_init,
rsa_eay_finish,
0, /* flags */
null,
0, /* rsa_sign */
0 /* rsa_verify */
};
由此可以看出,一般rsa->meth-> rsa_pub_enc对应于rsa_eay_public_encrypt,刚开始看openssl的时候最难得就是这个指向函数的指针,根本不知道rsa->meth-> rsa_pub_enc对应于哪里。在openssl里面这种指针很多,到以后也能够看到。下面是设置meth的一些函数应该都很容易理解
void rsa_set_default_method(const rsa_method *meth);
const rsa_method *rsa_get_default_method(void);
int rsa_set_method(rsa *rsa, const rsa_method *meth);
const rsa_method *rsa_get_method(const rsa *rsa);
int rsa_flags(const rsa *rsa);
rsa *rsa_new_method(engine *engine);
.加解密函数
int rsa_public_encrypt(int flen, unsigned char *from,
unsigned char *to, rsa *rsa, int padding);
int rsa_private_decrypt(int flen, unsigned char *from,
unsigned char *to, rsa *rsa, int padding);
int rsa_private_encrypt(int flen, unsigned char *from,
unsigned char *to, rsa *rsa,int padding);
int rsa_public_decrypt(int flen, unsigned char *from,
unsigned char *to, rsa *rsa,int padding);
有了第4节的基础,那理解这些加解密函数就容易了,假如
rsa_set_method(rsa, rsa_pkcs1_ssleay())的话,那rsa_public_encrypt对应于rsa_eay_public_encrypt,这样我们就可以调试公钥加密的过程了。flen为要加密信息的长度,from为需要加密的信息,to为加密后的信息,一般to至少要申请bn_num_bytes(rsa->n)大的空间。padding是采取的加解密方案。pkcs#1中主要提供了两种加密方案,rsaex-oaep和psaes-pkcs1-v1_5(反正就是两种加密过程了,有点复杂,它主要是先对先对需要加密的数据进行了编码,比如rsaes-oaep采用eme-oaep编码,再进行加密或解密)。openssl中已经编好了编码的函数:
case rsa_pkcs1_padding:
i=rsa_padding_add_pkcs1_type_2(buf,num,from,flen);
#ifndef openssl_no_sha
case rsa_pkcs1_oaep_padding: i=rsa_padding_add_pkcs1_oaep(buf,num,from,flen,null,0);
#endif
case rsa_sslv23_padding:
i=rsa_padding_add_sslv23(buf,num,from,flen);
case rsa_no_padding:
i=rsa_padding_add_none(buf,num,from,flen);
等上面编好码后,就调用bn_mod_exp_mont来进行模幂了。最后得出值,这也就是具体的加密和解密过程。在这里还可以发现,加密时输入的rsa有两种方式,一是p,q,...为null,只有rsa->d,和rsa->n不为空,这样就直接用rsa->d和rsa->n进行模幂计算,假如p,q.....都不为空的话,他会调用中国剩余定理来进行加密。
.签名函数
int rsa_sign(int type, unsigned char *m, unsigned int m_len,
unsigned char *sigret, unsigned int *siglen, rsa *rsa);
int rsa_verify(int type, unsigned char *m, unsigned int m_len,
unsigned char *sigbuf, unsigned int siglen, rsa *rsa);
其实签名其实和用私钥加密差不多是一回事,所以签名函数最终调用的就是私钥加密的函数,在openssl中这个签名函数很少单独拿出来用的,都是为了给evp_signfinal来调用的。所以假如是利用rsa进行签名的话,rsa_private_encrypt,bn_mod_exp_mont是最基本的,所有的都需要调用他,区别无非就在于在需要签名的信息上做了一下处理(一般将需要签名的信息求取摘要值得到m)
.写入文件函 数
int rsa_print(bio *bp, rsa *x, int offset);
int rsa_print_fp(file *fp, rsa *x, int offset);offset是为了调整输出格式的,随意一个数都可以(例如2,12,16。。)
.其他
int rsa_blinding_on(rsa *rsa, bn_ctx *ctx);
void rsa_blinding_off(rsa *rsa);
为了防止时间攻击,openssl还在签名的时候产生一个随机因子,附加在私钥上。
int rsa_sign_asn1_octet_string(int dummy, unsigned char *m,unsigned int m_len, unsigned char *sigret, unsigned int *siglen,rsa *rsa);
int rsa_verify_asn1_octet_string(int dummy, unsigned char *m,unsigned int m_len, unsigned char *sigbuf, unsigned int siglen,rsa *rsa);
用私钥对八元组串进行签名,原理同rsa_sign
openssl有关大数运算函数介绍- -
主要介绍openssl中的有关大数运算函数,这个对于以后的rsa研究和实现比较有价值
.初始化函数
bignum *bn_new(void); 新生成一个bignum结构
void bn_free(bignum *a); 释放一个bignum结构,释放完后a=null;
void bn_init(bignum *); 初始化所有项均为0,一般为bn_ init(&c)
void bn_clear(bignum *a); 将a中所有项均赋值为0,但是内存并没有释放
void bn_clear_free(bignum *a); 相当与将bn_free和bn_clear综合,要不就赋值0,要不就释放空间。
.上下文情景函数,存储计算中的中间过程
bn_ctx *bn_ctx_new(void);申请一个新的上下文结构
void bn_ctx_init(bn_ctx *c);将所有的项赋值为0,一般bn_ctx_init(&c)
void bn_ctx_free(bn_ctx *c);释放上下文结构,释放完后c=null;
.复制以及交换函数
bignum *bn_copy(bignum *a, const bignum *b);将b复制给a,正确返回a,错误返回null
bignum *bn_dup(const bignum *a);新建一个bignum结构,将a复制给新建结构返回,错误返回null
bignum *bn_swap(bignum *a, bignum *b);交换a,b
.取位函数
. 产生素数函数
bignum *bn_generate_prime(bignum *ret, int bits,int safe, bignum *add,
bignum *rem, void (*callback)(int, int, void *), void *cb_arg);产生一个bits位的素数,后面几个参数都可以为null
int bn_is_prime(const bignum *p, int nchecks,
void (*callback)(int, int, void *), bn_ctx *ctx, void *cb_arg);
判断是否为素数,返回0表示成功,1表示错误概率小于0。25,-1表示错误
.位数函数
int bn_set_bit(bignum *a, int n);将a中的第n位设置为1,假如a小于n位将扩展
int bn_clear_bit(bignum *a, int n);将a中的第n为设置为0,假如a小于n位将出错
int bn_is_bit_set(const bignum *a, int n);测试是否已经设置,1表示已设置
int bn_mask_bits(bignum *a, int n);将a截断至n位,假如a小于n位将出错
int bn_lshift(bignum *r, const bignum *a, int n);a左移n位,结果存于r
int bn_lshift1(bignum *r, bignum *a); a左移1位,结果存于r
int bn_rshift(bignum *r, bignum *a, int n); a右移n位,结果存于r
int bn_rshift1(bignum *r, bignum *a); a左移1位,结果存于r
.与字符串 的转换函数
int bn_bn2bin(const bignum *a, unsigned char *to);将abs(a)转化为字符串存入to,to的空间必须大于bn_num_bytes(a)
bignum *bn_bin2bn(const unsigned char *s, int len, bignum *ret);将s中的len位的正整数转化为大数
char *bn_bn2hex(const bignum *a);转化为16进制字符串
char *bn_bn2dec(const bignum *a);转化为10进制字符串
int bn_hex2bn(bignum **a, const char *str);同上理
int bn_dec2bn(bignum **a, const char *str);同上理
int bn_print(bio *fp, const bignum *a);将大数16进制形式写入内存中
int bn_print_fp(file *fp, const bignum *a); 将大数16进制形式写入文件
int bn_bn2mpi(const bignum *a, unsigned char *to);
bignum *bn_mpi2bn(unsigned char *s, int len, bignum *ret);
.其他函数
下面函数可以进行更有效率的模乘和模除,假如在重复在同一模下重复进行模乘和模除计算,计算r=(a*b)%m 利用了recp=1/m
bn_recp_ctx *bn_recp_ctx_new(void);
void bn_recp_ctx_init(bn_recp_ctx *recp);
void bn_recp_ctx_free(bn_recp_ctx *recp);
int bn_recp_ctx_set(bn_recp_ctx *recp, const bignum *m, bn_ctx *ctx);
int bn_mod_mul_reciprocal(bignum *r, bignum *a, bignum *b,
bn_recp_ctx *recp, bn_ctx *ctx);
下面函数采用蒙哥马利算法进行模幂计算,可以提高效率,他也主要应用于在同一模下进行多次幂运算
bn_mont_ctx *bn_mont_ctx_new(void);
void bn_mont_ctx_init(bn_mont_ctx *ctx);
void bn_mont_ctx_free(bn_mont_ctx *mont);
int bn_mont_ctx_set(bn_mont_ctx *mont, const bignum *m, bn_ctx *ctx);
bn_mont_ctx *bn_mont_ctx_copy(bn_mont_ctx *to, bn_mont_ctx *from);
int bn_mod_mul_montgomery(bignum *r, bignum *a, bignum *b,
bn_mont_ctx *mont, bn_ctx *ctx);
int bn_from_montgomery(bignum *r, bignum *a, bn_mont_ctx *mont,
bn_ctx *ctx);
int bn_to_montgomery(bignum *r, bignum *a, bn_mont_ctx *mont,