OpenSSL 中 RSA 加密解密实现源码分析

1、RSA 公钥和私钥的组成,以及加密和解密的公式:

2、模指数运算:

先做指数运算,再做模运算,如 5^3 mod 7 = 125 mod 7 = 6

3、RSA加密算法流程:

    1. 选择一对不同的、并且足够大的素数 p 和 q
    2. 计算 n = p * q
    3. 计算欧拉函数 f(n) = (p-1) * (q-1),p 和 q 需要保密
    4. 寻找与 f(n) 互质的数 e,并且 1 < e < f(n)
    5. 计算 d,使得 d * e ≡ 1 mod f(n)
    6. 公钥 KU = (e , n)   私钥 KR = (d , n)
    7. 加密时,将明文变换成 0 ~ n-1 的一个整数 M。明文较长可以分割。设密文为 C,则加密过程是:C ≡ M^e mod n
    8. 解密时,M ≡ C^d mod n

其中 ≡ 表示同余,就是符号两边模运算结果相同。

因为符号右边结果恒为 1

所以要找到符合条件的 d 使得 d * e mod f(n) = 1

4、OpenSSL中RSA加密解密实现

(1)BN_MONT_CTX
/* Used for montgomery multiplication */
struct bn_mont_ctx_st
     {
     int ri;              /* number of bits in R */
     BIGNUM RR;           /* used to convert to montgomery form */
     BIGNUM N;            /* The modulus */
     BIGNUM Ni;           /* R*(1/R mod N) - N*Ni = 1
                           * (Ni is only stored for bignum algorithm) */
     BN_ULONG n0[2];      /* least significant word(s) of Ni;
                                      (type changed with 0.9.9, was "BN_ULONG n0;" before) */
     int flags;
     };
(2)BIGNUM

struct bignum_st

{

BN_ULONG *d;     /* Pointer to an array of ‘BN_BITS2‘ bit chunks. */

int top;                 /* Index of last used d +1. */

/* The next are internal book keeping for bn_expand. */

int dmax;             /* Size of the d array. */

int neg;                /* one if the number is negative */

int flags;

};

d:BN_ULONG (应系统而异,win32 下为4 个字节) 数组指针首地址,大数就存放在这里面,不过是倒放的。比如,用户要存放的大数为 12345678000(通过BN_bin2bn 放入),则d 的内容如下: 0x30
0x30 0x30 0x38 0x37 0x36 0x35 0x34 0x33 0x32 0x31 ;(注意这里是以ASCII码存放的,他是字符转 bignum )

top:用来指明大数占多少个 BN_ULONG 空间,上例中top 为 3。

dmax:d 数组的大小。

neg:是否为负数,如果为1,则是负数,为 0,则为正数。

flags:用于存放一些标记,比如 flags 含有BN_FLG_STATIC_DATA 时,表明d 的内存

是静态分配的;含有 BN_FLG_MALLOCED 时,d 的内存是动态分配的。

(3)RSA
struct rsa_st

{

/* The first parameter is used to pickup errors where

* this is passed instead of aEVP_PKEY, it is set to 0 */

int pad;

long version;

/*此处的 method 方法指针比较重要,通过指向不同的函数,可以调用自己定义的处理函数,也就是这个指针指向各种运算函数的地址*/

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;

/* be careful using this if the RSA structure is shared */

CRYPTO_EX_DATA ex_data;

int 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;

};

(4)RSA_METHOD
struct rsa_meth_st

{

const char *name;

/*与(6)中RSA_eay_public_encrypt函数对应*/

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,

BN_CTX *ctx);           /* Can be null */

/*与(6)中BN_mod_exp_mont 函数对应*/

     int (*bn_mod_exp)(BIGNUM *r,

const BIGNUM *a, const BIGNUM *p,

const BIGNUM *m, BN_CTX *ctx,

BN_MONT_CTX *m_ctx);      /* Can be null */

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! */

/* 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. Note: for backwards

*   compatibility this functionality is only enabled if the RSA_FLAG_SIGN_VER

*   option is set in ‘flags‘.

*/

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);

};

用户可实现自己的 RSA_METHOD 来替换openssl 提供的默认方法。

(5)rsa_crpt.c 具体实现了以下几个函数
int     RSA_public_encrypt(int flen,

const unsigned char *from,

unsigned char *to,

RSA *rsa,

                                           int padding);

int     RSA_private_encrypt(int flen,

const unsigned char *from,

unsigned char *to,

RSA *rsa,

int padding);

int     RSA_public_decrypt(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);

RSA_public_encrypt 函数体内部,真正调用的是如下函数:

return(rsa->meth->rsa_pub_enc(flen, from, to, rsa, padding));

也就是 RSA_public_encrypt 调用的是(4)中 rsa_pub_enc 这个函数。

在 rsa.h中有如下说明:

/* these are the actual SSLeay RSA functions */

const RSA_METHOD *RSA_PKCS1_SSLeay(void);

const RSA_METHOD *RSA_null_method(void);

(6)rsa_esy.c 中有如下定义:

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,              /* XXX probably we should not use Montgomery if  e == 3 */

RSA_eay_init,

RSA_eay_finish,

0, /* flags */

NULL,

0,                                               /* rsa_sign */

0,                                               /* rsa_verify */

NULL                                         /* rsa_keygen */

};

 这个结构体完成了函数地址映射的功能。如:

 RSA_public_encrypt 最终调用的是 RSA_eay_public_encrypt 这个函数

 bn_mod_exp 最终调用的是 BN_mod_exp_mont这个函数


(7)RSA_eay_public_encrypt 在 rsa_esy.c 中实现
static int RSA_eay_public_encrypt(int flen,

const unsigned char *from,

unsigned char *to,

RSA *rsa,

int padding)

{

 ..............

/*核心调用在此,其实调用的是(6)中 BN_mod_exp_mont 函数*/

if (!rsa->meth->bn_mod_exp(ret,f,rsa->e,rsa->n,ctx,

rsa->_method_mod_n)) goto err;

..............

}

(8)BN_mod_exp_mont 在 bn_exp.c 中实现

int BN_mod_exp_mont(BIGNUM *rr,

const BIGNUM *a,

const BIGNUM *p,

const BIGNUM *m,

BN_CTX *ctx,

BN_MONT_CTX *in_mont)

{

..........

if (!BN_to_montgomery(val[0],aa,mont,ctx)) goto err; /* 1 */

window = BN_window_bits_for_exponent_size(bits);

if (window > 1)

{

if (!BN_mod_mul_montgomery(d,val[0],val[0],mont,ctx)) goto err; /* 2 */

j=1<<(window-1);

for (i=1; i<j; i++)

{

if(((val[i] = BN_CTX_get(ctx)) == NULL) ||

!BN_mod_mul_montgomery(val[i],val[i-1],

d,mont,ctx))

goto err;

}

}

..........

}

BN_mod_exp_mont 调用 BN_mod_mul_montgomery 函数实现了核心功能

(9)BN_mod_mul_montgomery 在 bn_mont.c 中实现

int BN_mod_mul_montgomery(BIGNUM *r,

const BIGNUM *a,

const BIGNUM *b,

BN_MONT_CTX *mont,

BN_CTX *ctx)

{

..........

if (num>1 && a->top==num && b->top==num)

{

if (bn_wexpand(r,num) == NULL) return(0);

if (bn_mul_mont(r->d,a->d,b->d,mont->N.d,mont->n0,num))

{

r->neg = a->neg^b->neg;

r->top = num;

bn_correct_top(r);

return(1);

}

}

..........

}

BN_mod_mul_montgomery 调用 bn_mul_mont 函数实现了核心功能

(10)bn_mul_mont 在 bn_asm.c 中实现,即实现蒙哥马利模乘

如下是 bn_mul_mont 函数的具体实现:

int bn_mul_mont(BN_ULONG *rp,

const BN_ULONG *ap,

const BN_ULONG *bp,

const BN_ULONG *np,

const BN_ULONG *n0p,

int num)

{

BN_ULONG c0,c1,*tp,n0=*n0p;

volatile BN_ULONG *vp;

int i=0,j;

vp = tp = alloca((num+2)*sizeof(BN_ULONG));

for(i=0;i<=num;i++)     tp[i]=0;

for(i=0;i<num;i++)

{

/* t = a * b */

c0         = bn_mul_add_words(tp,ap,num,bp[i]);

c1         = (tp[num] + c0)&BN_MASK2;

tp[num]    = c1;

tp[num+1]  = (c1<c0?1:0);

/* u = (t + (t*n0 mod r) * n) / r */

c0         = bn_mul_add_words(tp,np,num,tp[0]*n0);

c1         = (tp[num] + c0)&BN_MASK2;

tp[num]    = c1;

tp[num+1] += (c1<c0?1:0);

for(j=0;j<=num;j++)     tp[j]=tp[j+1];

}

/* return u>=n ? u-n : u */

if (tp[num]!=0 || tp[num-1]>=np[num-1])

{

c0 = bn_sub_words(rp,tp,np,num);

if (tp[num]!=0 || c0==0)

{

for(i=0;i<num+2;i++)     vp[i] = 0;

return 1;

}

}

for(i=0;i<num;i++)     rp[i] = tp[i],     vp[i] = 0;

vp[num]   = 0;

vp[num+1] = 0;

return 1;

}


bn_mul_add_words 在 bn_asm.c 中定义实现。可以通过如下部分代码知道具体的实现方法:

num  -->  表示大数占用的 BN_ULONG 的个数,也就是 BIGNUM 中的 top 成员

rp     -->  表示输出结果的指针

ap    -->  表示输入大数的指针

w     -->  表示输入word

c1    -->  表示输出进位

BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w)

{

......

while (num)

{

mul_add(rp[0],ap[0],w,c1);

ap++; rp++; num--;

}

......

}

bn_mul_add_words 函数实现了将一个大数 ap 与字 w 乘累加,并且得到结果保存到大数 rp ,进位保存到 c1 中。实现了如下表达式:

(c1, rp) = ap * w + rp + c1

乘法的实现方式如下:


bn_sub_words 在 bn_asm.c 中定义实现。可以通过如下部分代码知道具体的实现方法:

n      -->  表示大数占用的 BN_ULONG 的个数,也就是 BIGNUM 中的 top 成员

a      -->  表示输入被减数

b      -->  表示输入减数

c      -->  表示输出借位

BN_ULONG bn_sub_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, int n)

{

......

while (n)

{

t1=a[0]; t2=b[0];

r[0]=(t1-t2-c)&BN_MASK2;

if (t1 != t2) c=(t1 < t2);

a++; b++; r++; n--;

}

......

}

实现了两个大数 a 和 b 的减法运算,其中 c 是借位,实现如下表达式:

(r, c) = a - b


bn_lcl.h 中定义了如下几个宏:实现乘累加运算

#define Lw(t)     (((BN_ULONG)(t))&BN_MASK2)

#define Hw(t)    (((BN_ULONG)((t)>>BN_BITS2))&BN_MASK2)

#define mul_add(r,a,w,c) { \

BN_ULLONG t; \

t=(BN_ULLONG)w * (a) + (r) + (c); \

(r)= Lw(t); \

(c)= Hw(t); \

}

mul_add 函数实现了乘累加的功能。实现如下的表达式:

(c, r) = a * w + r + c

OpenSSL 中 RSA 加密解密实现源码分析

时间: 2024-12-29 11:28:59

OpenSSL 中 RSA 加密解密实现源码分析的相关文章

OpenSSL 中 RSA 加密解密实现源代码分析

1.RSA 公钥和私钥的组成.以及加密和解密的公式: 2.模指数运算: 先做指数运算,再做模运算.如 5^3 mod 7 = 125 mod 7 = 6 3.RSA加密算法流程: 选择一对不同的.而且足够大的素数 p 和 q 计算 n = p * q 计算欧拉函数 f(n) = (p-1) * (q-1),p 和 q 须要保密 寻找与 f(n) 互质的数 e.而且 1 < e < f(n) 计算 d,使得 d * e ≡ 1 mod f(n) 公钥 KU = (e , n)   私钥 KR =

基于新唐M0的XXTEA加密解密算法源码

源:基于新唐M0的XXTEA加密解密算法源码 /*--------------------------------------------------------------------------------------------------------- 在数据的加解密领域,算法分为对称密钥与非对称密钥两种.对称密钥与非对称密钥由于各自的特点,所应用的领域是不尽相 同的.对称密钥加密算法由于其速度快,一般用于整体数据的加密,而非对称密钥加密算法的安全性能佳,在数字签名领域得到广 泛的应用.

C#中RSA加密解密和签名与验证的实现

RSA加密算法是一种非对称加密算法.在公钥加密标准和电子商业中RSA被广泛使用.RSA是1977年由罗纳德•李维斯特(Ron Rivest).阿迪•萨莫尔(Adi Shamir)和伦纳德•阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院工作.RSA就是他们三人姓氏开头字母拼在一起组成的..Net的推出,我们能够利用.Net Framework中的类提供的加密服务来保证数据安全.目前应用较为广泛的加密方法是使用RSA算法进行加密.在.Net Framework中与R

caffe中HingeLossLayer层原理以及源码分析

输入: bottom[0]: NxKx1x1维,N为样本个数,K为类别数.是预测值. bottom[1]: Nx1x1x1维, N为样本个数,类别为K时,每个元素的取值范围为[0,1,2,-,K-1].是groundTruth. 输出: top[0]: 1x1x1x1维, 求得是hingeLoss. 关于HingeLoss: p: 范数,默认是L1范数,可以在配置中设置为L1或者L2范数. :指示函数,如果第n个样本的真实label为k,则为,否则为-1. tnk: bottom[0]中第n个样

Android 中View的绘制机制源码分析 三

到目前为止,measure过程已经讲解完了,今天开始我们就来学习layout过程,不过在学习layout过程之前,大家有没有发现我换了编辑器,哈哈,终于下定决心从Html编辑器切换为markdown编辑器,这里之所以使用"下定决心"这个词,是因为毕竟Html编辑器使用好几年了,很多习惯都已经养成了,要改变多年的习惯确实不易,相信这也是还有很多人坚持使用Html编辑器的原因.这也反应了一个现象,当人对某一事物非常熟悉时,一旦出现了新的事物想取代老的事物时,人们都有一种抵触的情绪,做技术的

Android 中View的绘制机制源码分析 二

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要讲解了View的measure过程,今天我们就来学习ViewGroup的measure过程,由于ViewGroup只是一个抽象类,所以我们需要以一个具体的布局来分析measure过程,正如我上篇文章说的,我打算使用LinearLayout为例讲解measure过程,如果你还没有读过上篇文章,那么建议你先

利用openssl进行RSA加密解密

openssl是一个功能强大的工具包,它集成了众多密码算法及实用工具.我们即可以利用它提供的命令台工具生成密钥.证书来加密解密文件,也可以在利用其提供的API接口在代码中对传输信息进行加密. RSA是一个非对称加密算法.简单说来,非对称加密算法就是说加密解密一个文件需要有两个密钥,一个用来加密,为公钥,一个用来解密,为私钥.证书可以用来授权公钥的使用. 今天小研究了下openssl的rsa加密,其中主要涉及利用公钥和密钥加解密文件,没有涉及对证书的操作.想要集体了解的可以去: http://ww

openssl evp RSA 加密解密

可以直接使用RSA.h 提供的接口 如下测试使用EVP提供的RSA接口 1. EVP提供的RSA 加密解密 主要接口: int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *ctx); int EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); int EVP_PKEY_decrypt_init(EV

Android中AsyncTask基本用法与源码分析(API 23)

原文链接 http://sparkyuan.github.io/2016/03/23/AsyncTask源码剖析(API 23)/ 转载请注明出处 Android的UI是线程不安全的,想在子线程中更新UI就必须使用Android的异步操作机制,直接在主线程中更新UI会导致程序崩溃. Android的异步操作主要有两种,AsyncTask和Handler.AsyncTask是一个轻量的异步类,简单.可控.本文主要结合API 23的源码讲解一下AsyncTask到底是什么. 基本用法 声明:Andr