2021SC@SDUSC
11 大数
11.1 介绍
大数一般指的是位数很多的数。 计算机表示的数的大小是有限的,精度也是有限的,它不能支持大数运算。 密码学中采用了很多大数计算,为了让计算机实现大数运算,用户需要定义自己的大数表示方式并及实现各种大数运算。 Openssl为我们提供了这些功能,主要用于非对称算法。
11.2 openssl大数表示
crypto/bn.h中定义了大数的表示方式,如下:
struct bignum_st
{
BN_ULONG *d;
int top;
int dmax;
int neg;
int flags;
};
/*
各项意义如下:
d :BN_ULONG(应系统而异,win32下为4个字节)数组指针首地址,
大数就存放在这里面,不过是倒放的。
比如,用户要存放的大数为 12345678000(通过BN_bin2bn放入),
则 d 的内容如下:
0x30 0x30 0x30 0x38 0x37 0x36 0x35 0x34 0x33 0x32 0x31 ;
top :用来指明大数占多少个BN_ULONG空间,上例中top为3。
dmax:d数组的大小。
neg :是否为负数, 如果 为1,则是负数,为0,则为正数。
flags:用于存放一些标记,
比如flags
含有 BN_FLG_STATIC_DATA 时, d 的内存是静态分配的;
含有 BN_FLG_MALLOCED 时,d 的内存是动态分配的。
*/
11.3 大数函数
大数函数一般都能根据函数名字知道其实现的功能。下面简单介绍了几个函数。
1)
int BN_rand(BIGNUM *rnd, int bits, int top, int bottom);
int BN_priv_rand(BIGNUM *rnd, int bits, int top, int bottom);
int BN_pseudo_rand(BIGNUM *rnd, int bits, int top, int bottom);
生成一个随机的大数。
2)
int BN_rand_range(BIGNUM *rnd, BIGNUM *range);
int BN_priv_rand_range(BIGNUM *rnd, BIGNUM *range);
int BN_pseudo_rand_range(BIGNUM *rnd, BIGNUM *range);
生成随机数,但是给出了随机数的范围。
3)
BIGNUM *BN_copy(BIGNUM *to, const BIGNUM *from);
BIGNUM *BN_dup(const BIGNUM *from);
void BN_with_flags(BIGNUM *dest, const BIGNUM *b, int flags);
大数复制。
int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add,
const BIGNUM *rem, BN_GENCB *cb);
#if OPENSSL_API_COMPAT < 0x00908000L
BIGNUM *BN_generate_prime(BIGNUM *ret, int num, int safe, BIGNUM *add,
BIGNUM *rem, void (*callback)(int, int, void *),
void *cb_arg);
生成素数。
5)
int BN_add_word(BIGNUM *a, BN_ULONG w)
给大数a加上w,
BN_add_word() adds w to a ("a+=w").
BN_sub_word() subtracts w from a ("a-=w").
BN_mul_word() multiplies a and w ("a*=w").
BN_div_word() divides a by w ("a/=w") and returns the remainder.
BN_mod_word() returns the remainder of a divided by w ("a%w").
For BN_div_word() and BN_mod_word(), w must not be 0.
return 1 for success, 0 on error
- 转换
6.1
BIGNUM * BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret)
将 s 中的换为大数,
入参
s : 为内存地址,
len : 为数据长度,
出参:
ret : 为返回值。
6.2
int BN_bn2bin(const BIGNUM *a, unsigned char *to)
将大数转换为内存形式。
入参:
a : 大数,
to : 为输出缓冲区地址,缓冲区需要预先分配,可以为NULL,
return : 返回冲区的长度。
BN_bn2bin() returns the length of the big-endian number placed at to.
BN_bin2bn() returns the BIGNUM, NULL on error
6.3
char *BN_bn2dec(const BIGNUM *a)
将大数转换成整数字符串。
return : 返回值中存放 整数字符串,它由内部分配空间,
用户必须在外部用OPENSSL_free函数释放该空间。
int BN_dec2bn(BIGNUM **a, const char *str); //将整数字符串转换成大数。
6.4
char *BN_bn2hex(const BIGNUM *a)
将大数转换为十六进制字符串。
return: 返回值为生成的十六进制字符串,
外部需要用OPENSSL_free函数释放
int BN_hex2bn(BIGNUM **a, const char *str); // 将十六进制字符串转换为大数
BN_bn2hex() and BN_bn2dec() return a null-terminated string, or NULL on error.
BN_hex2bn() and BN_dec2bn() return the number of characters used in parsing,
or 0 on error, in which case no new BIGNUM will be created.
- 比较
int BN_cmp(BIGNUM *a, BIGNUM *b); 比较两个大数。
int BN_ucmp(BIGNUM *a, BIGNUM *b); 比较两个大数绝对值。
int BN_is_zero(BIGNUM *a);
int BN_is_one(BIGNUM *a);
int BN_is_word(BIGNUM *a, BN_ULONG w);
int BN_is_odd(BIGNUM *a);
11)
BIGNUM *BN_mod_inverse( BIGNUM *in,
const BIGNUM *a,
const BIGNUM *n,
BN_CTX *ctx)
计算ax=1(mod n)。
用户使用openssl函数编程时, 一般用不着进行大数运算。
BN_bin2bn、BN_hex2bn、BN_dec2bn、BN_bin2bn、BN_bn2bin、BN_bn2hex和BN_bn2dec比较常用。
比如给定RSA密钥的内存形式,用户可以调用BN_bin2bn来构造RSA密钥的大数元素来进行RSA运算,
或者已经生成了RSA密钥,用户调用BN_bn2bin将RSA各个元素导出到内存中再写入密钥文件。
#include <openssl/bio.h>
#include <openssl/bn.h>
int main()
{
int ret;
BIGNUM *a;
BN_ULONG w;
a=BN_new();
//BN_zero(a);
//BN_one(a);
//BN_set_word(a,16);
//BN_set_word(a,256);
w=2685550010;
//w=0x2685550010;
ret=BN_add_word(a,w);
if(ret!=1) {
printf("a+=w err!\n");
BN_free(a);
return -1;
}
BIO *bio_out;
bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
//int BN_print(BIO *fp, const BIGNUM *a);
BIO_printf(bio_out, "-------------------\n");
BN_print(bio_out, a);
BIO_printf(bio_out, "\n-------------------\n");
int bits = BN_num_bits(a);
BIO_printf(bio_out, "bits = %d \n" ,bits);
bits = BN_num_bytes(a);
BIO_printf(bio_out, "bytes = %d \n" ,bits);
BN_free(a);
return 0;
}
#include <openssl/bio.h>
#include <openssl/bn.h>
int main()
{
BIGNUM *ret1,*ret2;
ret1=BN_new();
ret1=BN_bin2bn("242424ab",8, ret1);
ret2=BN_bin2bn("242424ab",8, NULL);
// printf
BIO *bio_out;
bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
BN_print(bio_out, ret1); // 16进制打印
BIO_printf(bio_out, "\n");
BN_print(bio_out, ret2);
BIO_printf(bio_out, "\n");
BIO_free(bio_out);
// free
BN_free(ret1);
BN_free(ret2);
return 0;
}
#include <openssl/bio.h>
#include <openssl/bn.h>
int main()
{
BIGNUM *ret = NULL;
char bin[50]={
's'}, *buf =