本文记录了初次接触OpenSSL中的大数模块,重温了RSA加密流程,使用OpenSSL的接口包装成自用RSA加密接口,而且利用本身的接口演示了Alice与Bob经过RSA加密进行通信的一个示例。html
本身的库中须要包含的功能有:git
2^65536-1
)(n,e)
和私钥d
80
位,128
位,256
位,512
位对称密钥因为macOS
自带openssl
(不自带也能够brew
安装)github
$ brew info openssl
openssl: stable 1.0.2k (bottled) [keg-only]
SSL/TLS cryptography library
https://openssl.org/
/usr/local/Cellar/openssl/1.0.2k (1,696 files, 12MB)
Poured from bottle on 2017-04-18 at 10:34:32
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/openssl.rb
==> Dependencies
Build: makedepend ✘
==> Options
--without-test
Skip build-time tests (not recommended)
==> Caveats
A CA file has been bootstrapped using certificates from the SystemRoots
keychain. To add additional certificates (e.g. the certificates added in
the System keychain), place .pem files in
/usr/local/etc/openssl/certs
and run
/usr/local/opt/openssl/bin/c_rehash
This formula is keg-only, which means it was not symlinked into /usr/local,
because Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries.
If you need to have this software first in your PATH run:
echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.zshrc
For compilers to find this software you may need to set:
LDFLAGS: -L/usr/local/opt/openssl/lib
CPPFLAGS: -I/usr/local/opt/openssl/include
For pkg-config to find this software you may need to set:
PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
测试库,书写Makefile
算法
CC = gcc
LDFLAGS = -L/usr/local/opt/openssl/lib
CPPFLAGS = -I/usr/local/opt/openssl/include
all:test
test:test.c
$(CC) test.c -o test -lssl -lcrypto $(LDFLAGS) $(CPPFLAGS)
clean:
rm test
能够成功编译,环境配成完成。shell
2^65536-1
)构造和对一个大整数对象赋值 (至少支持到2^65536-1
)bootstrap
OpenSSL
库中全部的大数对象均在bn.h
中进行定义。数组
OpenSSL
中的大数结构体以下ruby
# define BN_ULONG unsigned int
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;
};
很容易发现,OpenSSL
中对于大数的处理是用一个BN_ULONG
的数组来存的(不过这也是最正常不过的想法)。可是这样的大数是倒放的。markdown
因为是不定长的,因此要一个top
的数来代表大数的长度。session
dmax
保存着最大长度。
flags
用来标记一些属性
# define BN_FLG_MALLOCED 0x01
# define BN_FLG_STATIC_DATA 0x02
OpenSSL
提供的一些大数的工具备:
生成随机数
int BN_rand(BIGNUM *rnd, int bits, int top, int bottom);
int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom);
int BN_rand_range(BIGNUM *rnd, const BIGNUM *range);
int BN_pseudo_rand_range(BIGNUM *rnd, const BIGNUM *range);
大数复制
BIGNUM *BN_dup(const BIGNUM *a);
生成素数
BIGNUM *BN_generate_prime(BIGNUM *ret, int bits, int safe,
const BIGNUM *add, const BIGNUM *rem,
void (*callback) (int, int, void *), void *cb_arg);
将内存中的数据转换为大数,为内存地址,len为数据长度,ret为返回值。
BIGNUM *BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
BN_bin2bn() converts the positive integer in big-endian form of length len at s into a BIGNUM and places it in ret. If ret is NULL, a new BIGNUM is created.
FROM : OpenSSL Manuel
将大数放回内存中
int BN_bn2bin(const BIGNUM *a, unsigned char *to);
BN_bn2bin() converts the absolute value of a into big-endian form and stores it at to. to must point to BN_num_bytes(a) bytes of memory.
FROM : OpenSSL Manuel
2^65536-1
因为网上的Manuel
没有找到对应的条目,理论上内存够大应该能实现。下面实际测试可否支持这么大的数。
书写测试代码:
#include <openssl/bn.h>
#include <stdio.h>
#include <string.h>
int main(){
int ret;
int i;
BIGNUM *a;
BIGNUM *b;
unsigned char num[8192];
num[0] = 0x1;
num[8190] = 0x01;
a = BN_bin2bn(num,8192,NULL);
b = BN_bin2bn(num,8192,NULL);
BN_add(b,a,b);
printf("%d\n",a->dmax);
num[0] = 0x0;
num[8190] = 0x00;
ret = BN_bn2bin(b,num);
printf("0x%x 0x%x 0x%x\n",num[0],num[8190],num[8191]);
printf("%d\n",ret);
return 0;
}
测试输出
$ ./test
1024
0x3 0x2 0x0
正常存储,因此就限定个人库最大支持的长度为8192
的char
类型数据。
ascii
转成大数的函数在了解了BN
的存储定义,这里很容就能够写出直接赋值的函数。
注意,这里str_bn
是16
进制的数字
char ascii2hex(char ascii){
if(ascii >= '0' && ascii <= '9')
return ascii - '0';
else if(ascii >= 'a' && ascii <= 'f')
return ascii - 'a' + 10;
else
return NOT_HEX;
}
/* BN_str2bn * */
BIGNUM *BN_str2bn(char *str_bn){
BIGNUM* result = BN_new();
unsigned char* bin_bn;
int str_bn_len = strlen(str_bn);
int bin_bn_len = str_bn_len%2?str_bn_len/2+1:str_bn_len/2;
int i = 0;
char tmp;
bin_bn = malloc(sizeof(char)*bin_bn_len);
#ifdef debug
printf("\n%d %d\n",str_bn_len,bin_bn_len);
#endif
if(str_bn_len>=65536){ BN_error(ERROR_OVERFLOW); return NULL; }
if(str_bn_len%2){
if((tmp = ascii2hex(str_bn[0])) == NOT_HEX){BN_error(EROOR_NOT_HEX); return NULL;}
bin_bn[0] = tmp&0xf;
for(i = 1; i < str_bn_len ;i+=2){
bin_bn[i/2+1] = 0;
if((tmp = ascii2hex(str_bn[i])) == NOT_HEX){BN_error(EROOR_NOT_HEX); return NULL;}
bin_bn[i/2+1] |= (tmp<<4)&0xf0;
if((tmp = ascii2hex(str_bn[i+1])) == NOT_HEX){BN_error(EROOR_NOT_HEX); return NULL;}
bin_bn[i/2+1] |= tmp&0x0f;
}
}
else{
for(i = 0; i < str_bn_len ;i+=2){
//clear bin_bn
bin_bn[i/2] = 0;
if((tmp = ascii2hex(str_bn[i])) == NOT_HEX){BN_error(EROOR_NOT_HEX); return NULL;}
bin_bn[i/2] |= (tmp<<4)&0xf0;
if((tmp = ascii2hex(str_bn[i+1])) == NOT_HEX){BN_error(EROOR_NOT_HEX); return NULL;}
bin_bn[i/2] |= tmp&0x0f;
}
}
#ifdef debug
for(i = 0; i< bin_bn_len;i++){
printf("0x%x ",bin_bn[i]);
}
#endif
result = BN_bin2bn(bin_bn,bin_bn_len,NULL);
free(bin_bn);
return result;
}
为了之后方便测试,这里同时书写了在屏幕上显示大数的工具
LEN
单位是BN_ULONG
,前面部分截图用的是这个
void BN_print_screen(BIGNUM* bn){
int bn_len = (bn->top*sizeof(BN_ULONG))/sizeof(char);
printf("Len:%5d ",bn_len*2);
BN_print_fp(stdout,bn);
printf("\n");
}
LEN
单位是bits
,后面部分截图用的是这个
void BN_print_screen(BIGNUM* bn){
printf("Len:%5d Value: ",BN_num_bits(bn));
BN_print_fp(stdout,bn);
printf("\n");
}
测试上面写的两个功能是否正确
int main(){
int ret;
BIGNUM *a;
BIGNUM *b;
BIGNUM *c = BN_new();
a = BN_str2bn("123456789123456789123456789");
b = BN_str2bn("100000001100000001100000001");
printf(" a = ");
BN_print_screen(a);
printf(" b = ");
BN_print_screen(b);
BN_sub(c,a,b);
printf("a-b = ");
BN_print_screen(c);
BN_free(a);
BN_free(b);
BN_free(c);
return 0;
}
这几个运算OpenSSL
已经打包的很是容易使用了,这里就不进一步抽象。
BN_add() adds a and b and places the result in r (
r=a+b
). r may be the same BIGNUM as a or b.BN_sub() subtracts b from a and places the result in r (
r=a-b
). r may be the same BIGNUM as a or b.BN_mul() multiplies a and b and places the result in r (
r=a*b
). r may be the same BIGNUM as a or b. For multiplication by powers of 2, use BN_lshift(3).BN_sqr() takes the square of a and places the result in r (
r=a^2
). r and a may be the same BIGNUM. This function is faster than BN_mul(r,a,a).BN_div() divides a by d and places the result in dv and the remainder in rem (
dv=a/d, rem=a%d
). Either of dv and rem may be NULL, in which case the respective value is not returned. The result is rounded towards zero; thus if a is negative, the remainder will be zero or negative. For division by powers of 2, use BN_rshift(3).BN_mod() corresponds to BN_div() with dv set to NULL.
BN_nnmod() reduces a modulo m and places the non-negative remainder in r.
BN_mod_add() adds a to b modulo m and places the non-negative result in r.
BN_mod_sub() subtracts b from a modulo m and places the non-negative result in r.
BN_mod_mul() multiplies a by b and finds the non-negative remainder respective to modulus m (
r=(a*b) mod m
). r may be the same BIGNUM as a or b. For more efficient algorithms for repeated computations using the same modulus, see BN_mod_mul_montgomery(3) and BN_mod_mul_reciprocal(3).BN_mod_sqr() takes the square of a modulo m and places the result in r.
BN_exp() raises a to the p-th power and places the result in r (
r=a^p
). This function is faster than repeated applications of BN_mul().BN_mod_exp() computes a to the p-th power modulo m (
r=a^p % m
). This function uses less time and space than BN_exp().BN_gcd() computes the greatest common divisor of a and b and places the result in r. r may be the same BIGNUM as a or b.
For all functions, ctx is a previously allocated BN_CTX used for temporary variables; see BN_CTX_new(3).
Unless noted otherwise, the result BIGNUM must be different from the arguments.
FROM : OpenSSL Manuel
下面做各个功能的基本演示:
int main(){
int ret;
BIGNUM *a;
BIGNUM *b;
BN_CTX *ctx = BN_CTX_new();
BIGNUM *c = BN_new();
BIGNUM *rem = BN_new();
a = BN_str2bn("123456789123456789123456789");
b = BN_str2bn("1100000001100000001");
printf(" a = ");
BN_print_screen(a);
printf(" b = ");
BN_print_screen(b);
BN_add(c,a,b);
printf("a+b = ");
BN_print_screen(c);
BN_sub(c,a,b);
printf("a-b = ");
BN_print_screen(c);
BN_mul(c,a,b,ctx);
printf("a*b = ");
BN_print_screen(c);
printf("a/b = ");
BN_div(c,rem,a,b,ctx);
BN_print_screen(c);
printf("rem = ");
BN_print_screen(rem);
BN_mod(rem,a,b,ctx);
printf("a%%b = ");
BN_print_screen(rem);
c = BN_str2bn("3");
BN_exp(c,a,c,ctx);
printf("a^3 = ");
BN_print_screen(c);
BN_free(a);
BN_free(b);
BN_free(c);
BN_free(rem);
return 0;
}
其中乘方的实现方法为:
int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
{
int i, bits, ret = 0;
BIGNUM *v, *rr;
if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
/* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
BNerr(BN_F_BN_EXP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
BN_CTX_start(ctx);
if ((r == a) || (r == p))
rr = BN_CTX_get(ctx);
else
rr = r;
v = BN_CTX_get(ctx);
if (rr == NULL || v == NULL)
goto err;
if (BN_copy(v, a) == NULL)
goto err;
bits = BN_num_bits(p);
if (BN_is_odd(p)) {
if (BN_copy(rr, a) == NULL)
goto err;
} else {
if (!BN_one(rr))
goto err;
}
//快速乘方OpenSSL实现
for (i = 1; i < bits; i++) {
//v = v^2
if (!BN_sqr(v, v, ctx))
goto err;
// if(p_i == 1)
if (BN_is_bit_set(p, i)) {
// rr = rr*v
if (!BN_mul(rr, rr, v, ctx))
goto err;
}
}
if (r != rr && BN_copy(r, rr) == NULL)
goto err;
ret = 1;
err:
BN_CTX_end(ctx);
bn_check_top(r);
return (ret);
}
OpenSSL
实现方法int BN_is_prime_ex(const BIGNUM *p, int nchecks, BN_CTX *ctx, BN_GENCB *cb);
BN_is_prime_ex(), BN_is_prime_fasttest_ex(), BN_is_prime() and BN_is_prime_fasttest() return 0 if the number is composite, 1 if it is prime with an error probability of less than 0.25^n checks, and -1 on error.
关于其中的检查个数,在bn.h
中有这样的定义:
# define BN_prime_checks 0 /* default: select number of iterations based
* on the size of the number */
/* * number of Miller-Rabin iterations for an error rate of less than 2^-80 for * random 'b'-bit input, b >= 100 (taken from table 4.4 in the Handbook of * Applied Cryptography [Menezes, van Oorschot, Vanstone; CRC Press 1996]; * original paper: Damgaard, Landrock, Pomerance: Average case error * estimates for the strong probable prime test. -- Math. Comp. 61 (1993) * 177-194) */
# define BN_prime_checks_for_size(b) ((b) >= 1300 ? 2 : \
(b) >= 850 ? 3 : \
(b) >= 650 ? 4 : \
(b) >= 550 ? 5 : \
(b) >= 450 ? 6 : \
(b) >= 400 ? 7 : \
(b) >= 350 ? 8 : \
(b) >= 300 ? 9 : \
(b) >= 250 ? 12 : \
(b) >= 200 ? 15 : \
(b) >= 150 ? 18 : \
/* b >= 100 */ 27)
而对于一个bn
,有int BN_num_bits(const BIGNUM *a);
能够用来获取其bits
数。
OpenSSL
还提供了生成质数的函数,定义以下:
int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add, const BIGNUM *rem, BN_GENCB *cb);
BN_generate_prime_ex() generates a pseudo-random prime number of at least bit length bits. If ret is not NULL, it will be used to store the number.
If cb is not NULL, it is used as follows:
- BN_GENCB_call(cb, 0, i) is called after generating the i-th potential prime number.
- While the number is being tested for primality, BN_GENCB_call(cb, 1, j) is called as described below.
- When a prime has been found, BN_GENCB_call(cb, 2, i) is called.
The prime may have to fulfill additional requirements for use in Diffie-Hellman key exchange:
If add is not NULL, the prime will fulfill the condition p % add == rem (p % add == 1 if rem == NULL) in order to suit a given generator.
If safe is true, it will be a safe prime (i.e. a prime p so that (p-1)/2 is also prime).
The PRNG must be seeded prior to calling BN_generate_prime_ex(). The prime number generation has a negligible error probability.
FROM : OpenSSL Manuel
打包成本身的库函数:
/* BN_is_prime_auto_n_check * */
int BN_is_prime_auto_n_check(BIGNUM* bn){
int ret;
BN_CTX *ctx = BN_CTX_new();
ret = BN_is_prime_ex(bn, BN_prime_checks_for_size(BN_num_bits(bn)), ctx, NULL);
BN_CTX_free(ctx);
return ret;
}
因而写下面的测试程序。
void prim_bn_test(){
int ret;
BIGNUM *a;
BIGNUM *b = BN_new();
BN_CTX *ctx = BN_CTX_new();
a = BN_str2bn("123456789123456789123456789");
BN_generate_prime_ex(b, 256 , 1 , NULL, NULL, NULL);
ret = BN_is_prime_auto_n_check(a);
printf("%s",ret?"It is prime with an error probability of less than 0.25^n checks\n":"The number is composite\n");
BN_print_screen(b);
ret = BN_is_prime_auto_n_check(b);
printf("%s",ret?"It is prime with an error probability of less than 0.25^n checks\n":"The number is composite\n");
BN_free(a);
BN_free(b);
BN_CTX_free(ctx);
}
(n,e)
和私钥d
此后修改了LEN的单位为bit
根据RSA的生成原理:
#define RSA_e "10001"
void gen_RSA(BIGNUM *n,BIGNUM *d,BIGNUM *e,int RSA_bits){
BIGNUM *p;
BIGNUM *q;
BIGNUM *e;
BIGNUM *tmp = BN_new();
BIGNUM *one = BN_new();
BIGNUM *Phin = BN_new();
BN_CTX *ctx = BN_CTX_new();
BIGNUM *gcd_result = BN_new();
BN_one(one);
p = BN_gen_safe_prime(RSA_bits/2);
q = BN_gen_safe_prime(RSA_bits/2);
#ifdef debug
printf("p = ");
BN_print_screen(p);
printf("q = ");
BN_print_screen(q);
#endif
BN_mul(n,p,q,ctx);
BN_sub(p,p,one);
BN_sub(q,q,one);
BN_mul(Phin,p,q,ctx);
BN_mod_inverse(d,e,Phin,ctx);
#ifdef debug
printf("n = ");
BN_print_screen(n);
printf("Phin = ");
BN_print_screen(Phin);
printf("e = ");
BN_print_screen(e);
printf("gcd(e,Phin)=");
BN_gcd(gcd_result,e,Phin,ctx);
BN_print_screen(gcd_result);
printf("d = ");
BN_print_screen(d);
#endif
BN_free(p);
BN_free(q);
BN_CTX_free(ctx);
BN_free(tmp);
BN_free(one);
BN_free(Phin);
BN_free(gcd_result);
}
80
位,128
位,256
位,512
位对称密钥利用库函数BN_rand
,打包成一个的生成最高位为1的随机数的函数。
BIGNUM *BN_gen_rand(int size){
BIGNUM *rand = BN_new();
BN_rand(rand,size,BN_RAND_TOP_ANY,BN_RAND_BOTTOM_ANY);
return rand;
}
测试80
位 128
位 256
位 512
位
int main(int argc,char* argv[]){
BIGNUM *n;
n = BN_gen_rand(80);
BN_print_screen(n);
BN_free(n);
n = BN_gen_rand(128);
BN_print_screen(n);
BN_free(n);
n = BN_gen_rand(256);
BN_print_screen(n);
BN_free(n);
n = BN_gen_rand(512);
BN_print_screen(n);
BN_free(n);
return 0;
}
输出随机产生的key
以下
下面分为本身写RSA
(肖老师不推荐) 和直接用库
BIGNUM *my_rsa_encrypt(BIGNUM *x,BIGNUM *n,BIGNUM *e){
BIGNUM *y = BN_new();
BN_CTX *ctx = BN_CTX_new();
BN_mod_exp(y , x, e, n, ctx);
BN_CTX_free(ctx);
return y;
}
BIGNUM *my_rsa_decrypt(BIGNUM *y,BIGNUM *n,BIGNUM *d){
BIGNUM *x = BN_new();
BN_CTX *ctx = BN_CTX_new();
BN_mod_exp(x, y, d, n, ctx);
BN_CTX_free(ctx);
return x;
}
测试加密解密
void my_rsa_test(){
BIGNUM *n = BN_new();
BIGNUM *y = BN_new();
BIGNUM *x;
BIGNUM *d = BN_new();
BIGNUM *e;
BN_CTX *ctx = BN_CTX_new();
e = BN_str2bn(RSA_e);
gen_RSA(n,d,e,512);
x = BN_gen_rand(80);
printf("Before : ");
BN_print_screen(x);
y = my_rsa_encrypt(x,n,e);
printf("Encrypt : ");
BN_print_screen(y);
BN_free(x);
x = my_rsa_decrypt(y,n,d);
printf("Decrypt : ");
BN_print_screen(x);
BN_free(n);
BN_free(y);
BN_free(x);
BN_free(d);
BN_free(e);
BN_CTX_free(ctx);
}
测试结果
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;
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;
};
这里直接用官方提供的接口生成RSA
考虑不一样的padding
模式:RSA加密经常使用的填充方式有下面3种:
RSA_PKCS1_PADDING
填充模式,最经常使用的模式
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是RSA_size(rsa) - 11
若是输入的明文过长,必须切割,而后填充。
输出:和modulus同样长
根据这个要求,对于512bit的密钥,
RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41
输出:和modulus
同样长
RSA_NO_PADDING
不填充
输入:能够和RSA
钥模长同样长,若是输入的明文过长,必须切割,而后填充。
输出:和modulus
同样长
填充方式:
RSA_padding_add_PKCS1_type_1 //私钥加密填充. 标志: 0x01. 填充:0xFF
RSA_padding_add_PKCS1_type_2 //公钥加密填充. 标志: 0x02. 填充:非零随机数
RSA_padding_add_none //无填充其实就是在高位填充 0x00.
DESCRIPTION
RSA_public_encrypt() encrypts the flen bytes at from (usually a session key) using the public key rsa and stores the ciphertext in to. to must point to RSA_size(rsa) bytes of memory.
padding denotes one of the following modes:
RSA_PKCS1_PADDING
PKCS #1 v1.5 padding. This currently is the most widely used mode.
RSA_PKCS1_OAEP_PADDING
EME-OAEP as defined in PKCS #1 v2.0 with SHA-1, MGF1 and an empty encoding parameter. This mode is recommended for all new applications.
RSA_SSLV23_PADDING
PKCS #1 v1.5 padding with an SSL-specific modification that denotes that the server is SSL3 capable.
RSA_NO_PADDING
Raw RSA encryption. This mode should only be used to implement cryptographically sound padding modes in the application code. Encrypting user data directly with RSA is insecure.
flen must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5 based padding modes
less than RSA_size(rsa) - 41 for RSA_PKCS1_OAEP_PADDING
exactly RSA_size(rsa) for RSA_NO_PADDING.
The random number generator must be seeded prior to calling RSA_public_encrypt().
RSA_private_decrypt() decrypts the flen bytes at from using the private key rsa and stores the plaintext in to. to must point to a memory section large enough to hold the decrypted data (which is smaller than RSA_size(rsa)). padding is the padding mode that was used to encrypt the data.
RETURN VALUES
RSA_public_encrypt() returns the size of the encrypted data (i.e., RSA_size(rsa)). RSA_private_decrypt() returns the size of the recovered plaintext.
FROM : OpenSSL Manuel
flen must be
必定要主要padding
的这几个条件
简单的,未拆分的OpenSSL RSA with different padding method
测试
void rsaTest(char *test){
RSA *rsa;
BIGNUM *e;
int i = 0;
int ret = 0;
unsigned char *encryptedtest;
unsigned char *decryptedtest;
int rsa_len;
int flen;
int modulus_len;
puts(test);
e = BN_str2bn(RSA_e);
rsa = RSA_new();
ret = RSA_generate_key_ex(rsa,1024,e,NULL);
if(ret!=1){
printf("RSA_generate_key_ex err!/n");
}
printf("rsa:n");
BN_print_screen(rsa->n);
printf("rsa:d");
BN_print_screen(rsa->d);
printf("rsa:e");
BN_print_screen(rsa->e);
flen=strlen(test);
//RSA_NO_PADDING
printf("===padding method:RSA_NO_PADDING===\n");
rsa_len=RSA_size(rsa);
modulus_len = rsa_len;
printf("RSA_LEN = %d PAD_LEN = %d\n",rsa_len,modulus_len);
encryptedtest=(unsigned char *)malloc(rsa_len+1);
decryptedtest=(unsigned char *)malloc(rsa_len+1);
memset(encryptedtest,0,rsa_len+1);
memset(decryptedtest,0,rsa_len+1);
ret = RSA_public_encrypt(modulus_len,(unsigned char *)test,(unsigned char*)encryptedtest,rsa,RSA_NO_PADDING);
printf("ret = %d Encryped msg\n %s\n",ret,encryptedtest);
ret = RSA_private_decrypt(rsa_len,(unsigned char *)encryptedtest,(unsigned char*)decryptedtest,rsa,RSA_NO_PADDING);
printf("ret = %d Decryped msg\n %s\n",ret,decryptedtest);
printf("===================================\n\n");
//RSA_PKCS1_PADDING
printf("===padding method:RSA_PKCS1_PADDING===\n");
modulus_len = flen;
printf("RSA_LEN = %d Less than %d PAD_LEN = %d\n",rsa_len,rsa_len-11,modulus_len);
memset(encryptedtest,0,rsa_len+1);
memset(decryptedtest,0,rsa_len+1);
ret = RSA_public_encrypt(modulus_len,(unsigned char *)test,(unsigned char*)encryptedtest,rsa,RSA_PKCS1_PADDING);
printf("ret = %d Encryped msg\n %s\n",ret,encryptedtest);
//这里要注意,这里解密仍是rsa_len
ret = RSA_private_decrypt(rsa_len,(unsigned char *)encryptedtest,(unsigned char*)decryptedtest,rsa,RSA_PKCS1_PADDING);
printf("ret = %d Decryped msg\n %s\n",ret,decryptedtest);
printf("=====================================\n\n");
//RSA_PKCS1_OAEP_PADDING
printf("===padding method:RSA_PKCS1_OAEP_PADDING===\n");
modulus_len = flen;
printf("RSA_LEN = %d Less than %d Pad_len = %d\n",rsa_len,rsa_len-41,modulus_len);
memset(encryptedtest,0,rsa_len+1);
memset(decryptedtest,0,rsa_len+1);
ret = RSA_public_encrypt(modulus_len,(unsigned char *)test,(unsigned char*)encryptedtest,rsa,RSA_PKCS1_OAEP_PADDING);
printf("ret = %d Encryped msg\n %s\n",ret,encryptedtest);
ret = RSA_private_decrypt(rsa_len,(unsigned char *)encryptedtest,(unsigned char*)decryptedtest,rsa,RSA_PKCS1_OAEP_PADDING);
printf("ret = %d Decryped msg\n %s\n",ret,decryptedtest);
printf("=====================================\n\n");
free(decryptedtest);
free(encryptedtest);
BN_free(e);
RSA_free(rsa);
}
注意到RSA 官方的wiki上面这样的一句话 RSA_PKCS1_OAEP_PADDING: recommended for all new applications.
因而选择RSA_PKCS1_OAEP_PADDING
做为个人库中加密默认的pedding
模式
书写本身的RSA
密钥加密解密库
char *encrypt_key(char* key,RSA *rsa,int key_len,int *ret){
int rsa_len = RSA_size(rsa);
int pad_len = rsa_len - 42;
int str_index = 0;
char *encrypted_key;
int encrypted_key_len = 0;
int encrypt_round = key_len/pad_len;
if(key_len % pad_len != 0) encrypt_round++;
encrypted_key_len = (encrypt_round+1)*rsa_len;
encrypted_key = (char *)malloc(encrypted_key_len * sizeof(char));
#ifdef debug
printf("Round = %d\nPad_len = %d\nencrypted_key_len = %d\n",
encrypt_round,pad_len,encrypted_key_len);
#endif
while(str_index < encrypt_round-1){
*ret = RSA_public_encrypt(pad_len,(unsigned char *)(key + str_index*pad_len),
(unsigned char*)(encrypted_key + str_index*rsa_len),rsa,RSA_PKCS1_OAEP_PADDING);
str_index ++;
if(*ret == -1) {printf("Error:");return NULL;}
#ifdef debug
printf("\n\n\nLoop Round\ntext_offset = %d\nencrypted_test_offset = %d\n\n\n",
str_index*pad_len,str_index*rsa_len);
#endif
}
#ifdef debug
printf("last_round_len = %d\ntext_offset = %d\nencrypted_test_offset = %d\n",
key_len%pad_len,str_index*pad_len,str_index*rsa_len);
#endif
*ret += RSA_public_encrypt(key_len%pad_len,(unsigned char *)(key + str_index*pad_len),
(unsigned char*)(encrypted_key + str_index*rsa_len),rsa,RSA_PKCS1_OAEP_PADDING);
str_index ++;
if(*ret == -1) {printf("Error:");return NULL;}
return encrypted_key;
}
char *decrypt_key(char* encrypted_key,RSA *rsa,int key_len,int *ret){
int encrypted_key_len;
int rsa_len = RSA_size(rsa);
int str_index = 0;
int pad_len = rsa_len - 42;
char *decrypted_key;
int encrypt_round = key_len/pad_len;
if(key_len % pad_len != 0) encrypt_round++;
encrypted_key_len = (encrypt_round)*rsa_len;
printf("\n\n\n%d\n\n",encrypted_key_len);
if(encrypted_key_len % rsa_len != 0) {printf("Len Error!"); return 0;}
decrypted_key = (char *)malloc(encrypted_key_len * sizeof(char));
#ifdef debug
printf("Round = %d\nPad_len = %d\nencrypted_key_len = %d\n",
encrypt_round,pad_len,encrypted_key_len);
#endif
while(str_index < encrypt_round){
*ret = RSA_private_decrypt(rsa_len,(unsigned char *)encrypted_key+str_index*rsa_len,(unsigned char*)decrypted_key+str_index*pad_len,rsa,RSA_PKCS1_OAEP_PADDING);
str_index ++;
if(*ret == -1) {printf("Error:Decrypted error!\n");return NULL;}
#ifdef debug
printf("\n\n\n Round\nencryptedtext_offset = %d\ndecrypted_test_offset = %d\n\n\n",
str_index*rsa_len,str_index*pad_len);
#endif
}
return decrypted_key;
}
在这里实现了一个完整的AES + RSA
的一套流程,其中有这些地方须要注意
OpenSSL
库中的工具实现,上面已经展现了我本身的实现,这里利用库仍是严谨考虑。AES
因为只是示意,这里采用直接拆分分组加密,没有采用CCBint main(int argc,char* argv[]){
BIGNUM *n;
int aes_index = 0;
int aes_round = 0;
AES_KEY Alice_AES_key;
AES_KEY Bob_AES_key;
unsigned char* trans_key;
unsigned char* Bob_AES_key_plain;
unsigned char Alice_AES_key_plain[AES_type/8];
int ret = 0;
RSA *bob_rsa;
RSA *alice_rsa;
BIGNUM *e;
unsigned char Alice_msg[] = {"What about go out tonight?:)"};
unsigned char Crypted_msg[1000];
unsigned char Bob_msg[1000];
//Bob Gen rsa pub/priv key
printf("Bob Gen RSA Pub/Priv Key...");
e = BN_str2bn((unsigned char *)RSA_e);
bob_rsa = RSA_new();
ret = RSA_generate_key_ex(bob_rsa,RSA_type,e,NULL);
if(ret!=1){ printf("RSA_generate_key_ex err!/n"); return 1;}
printf("...DONE\n");
//Give alice the publickey
printf("Give Public key to Alice...");
alice_rsa = RSAPublicKey_dup(bob_rsa);
printf("...DONE\n");
//Alice Gen AES Key
printf("Alice Gen AES 256 Key...");
n = BN_gen_rand(AES_type);
ret = BN_bn2bin(n,Alice_AES_key_plain);
if(ret == -1) return 1;
//Save Alice AES internal Key
ret = AES_set_encrypt_key(Alice_AES_key_plain, AES_type, &Alice_AES_key);
if(ret == -1) return 1;
printf("...DONE\n");
//Alice Encrypt this key using Bob's pub key
printf("Alice Encrypt AES 256 Key using Bob's public key...");
trans_key = encrypt_key((unsigned char *)Alice_AES_key_plain,alice_rsa,AES_type,&ret);
if(ret == -1) {printf("Gen trans key fail");return 1;}
printf("...DONE\n");
//Bob get this key
printf("Bob get this msg and Decrypt AES 256 Key using his private key...");
Bob_AES_key_plain = decrypt_key(trans_key,bob_rsa,AES_type,&ret);
if(ret == -1) {printf("Decrypt AES key fail");return 1;}
AES_set_decrypt_key(Bob_AES_key_plain,AES_type,&Bob_AES_key);
printf("...DONE\n");
//Finish AES trans
printf("Alice Send:\n");
printf("%s\n",Alice_msg);
aes_round = strlen((char*)Alice_msg) /16;
if(strlen((char*)Alice_msg) % 16) aes_round++;
aes_index = 0;
while(aes_index<aes_round){
AES_encrypt(Alice_msg+aes_index*16,Crypted_msg+aes_index*16,&Alice_AES_key);
aes_index++;
}
printf("After %d round encrypted:\n",aes_round);
printf("%s\n",Crypted_msg);
//Bob Decrpt msg
aes_round = strlen((char*)Crypted_msg) /16;
if(strlen((char*)Crypted_msg) % 16) aes_round++;
aes_index = 0;
while(aes_index<aes_round){
AES_decrypt(Crypted_msg+aes_index*16,Bob_msg+aes_index*16,&Bob_AES_key);
aes_index++;
}
printf("Bob After %d round decrypted:\n",aes_round);
printf("%s\n",Bob_msg);
return 0;
}
上面AES对于过长分组直接作了简单的拆分,其实能够用CBC或者更加有效的算法来避免替换攻击。重点不在这里,就很少写了。
测试集输出
$ ./test_set ===============================================
=== OpenSSL & My OpenSSL simple API test set===
===============================================
Big Number Calculation test
a = Len: 105 Value: 123456789123456789123456789
b = Len: 73 Value: 1100000001100000001
a+b = Len: 105 Value: 12345678A22345678A22345678A
a-b = Len: 105 Value: 123456788023456788023456788
a*b = Len: 177 Value: 13579BE01B6AF37C0358E38E38C458BF258AA23456789
a/b = Len: 33 Value: 112233444
rem = Len: 72 Value: C00000000C11223345
a%b = Len: 72 Value: C00000000C11223345
a^3 = Len: 313 Value: 1790FC50EB1EF18B6B881A6AC16535FFD7E3D20B65A14D91C627A9AF45E8FCD963C6473FB900159
===============================================
BN_is_prime_auto_n_check test The number is composite Len: 256 Value: E9B305EB693A7533125CFB9C042245FD5034BAF393986886E5FAC379A3D5B62F It is prime with an error probability of less than 0.25^n checks =============================================== My version of RSA test Before : Len: 80 Value: EBB12B24EFFACCBE78CC Encrypt : Len: 510 Value: 21D418B27523864340D4CF344BDC2C3528FE48CAC724F5C18C807FA631514C606907A97E946DA76A8FFB3968DE62984D42D9237024743ECCD309CB520D9184D4 Decrypt : Len: 80 Value: EBB12B24EFFACCBE78CC =============================================== Different Padding mathed and my Simple RSA API test rsa:nLen: 512 Value: D758654DB802831516D0AB120921D483D00178FB02F9BD914487E90D8CA6DEDFACBC40589C50FBFB5A928A49E95F68FC8290516B3F72D5ABEBD76ABC6D7D1765 rsa:dLen: 512 Value: BEF0F4322B5C9EDA0E36CBD8DC1C1111275886EB1AC25262023FF8573945A50B51C10C4F734F7078741196440E33D53A2EE7B2810E9FC1CB396E101A767012E9 rsa:eLen: 17 Value: 10001 ===padding method:RSA_NO_PADDING=== RSA_LEN = 64 PAD_LEN = 64 ret = 64 Encryped msg R��@3�A9�� ���J ret = 64 Decryped msg ABCDEFGHIJKLMNOPQRSTUVWXYZ =================================== ===padding method:RSA_PKCS1_PADDING===
RSA_LEN = 64 Less than 53 PAD_LEN = 26
ret = 64 Encryped msg
+�eP��6�.PR�
|Z��袙��xh����*��j]w`��L�2���W�h����g��� ret = 26 Decryped msg ABCDEFGHIJKLMNOPQRSTUVWXYZ ===================================== ===padding method:RSA_PKCS1_OAEP_PADDING=== RSA_LEN = 64 Less than 23 PAD_LEN = 22 ret = 64 Encryped msg &��DeU��HN����R������|��v��]?�>�m ret = 22 Decryped msg ABCDEFGHIJKLMNOPQRSTUV ===================================== ===My pkted Encryped & Decryped test====
ret = 128 Encryped msg
�u�I����c!�Ӿ"�k�M�sL�n�?w9緱�����o��Ci[d�f�5�i��k��?
��^5�4�E��q)�7{K8����r��M��-�]�
ret = 4 Decryped msg
ABCDEFGHIJKLMNOPQRSTUVWXYZ =====================================
===============================================
alice & Bob
输出
$ ./Alice_Bob
Bob Gen RSA Pub/Priv Key......DONE
Give Public key to Alice......DONE
Alice Gen AES 256 Key......DONE
Alice Encrypt AES 256 Key using Bob's public key......DONE Bob get this msg and Decrypt AES 256 Key using his private key......DONE Alice Send: What about go out tonight?:) After 2 round encrypted: �%����i�,߳�uٴ� V��4B�.�FY Bob After 2 round decrypted: What about go out tonight?:)
具体使用方法见Readme
Manuel
要仔细读,不少细节,如上面的less
就容易错。