本文记录了初次接触OpenSSL中的大数模块,重温了RSA加密流程,使用OpenSSL的接口包装成自用RSA加密接口,并且利用自己的接口演示了Alice与Bob通过RSA加密进行通讯的一个示例。
概览
自己的库中需要包含的功能有:
- 构造和对一个大整数对象赋值 (至少支持到
2^65536-1
) - 大整数的加法、乘法、取模、加速乘方等基本算术运算
- 判断这个大整数是否是素数
- 通过两个大素数构造RSA公钥
(n,e)
和私钥d
- 随机生成
80
位,128
位,256
位,512
位对称密钥 - 对不同长度的对称密钥进行加密(包括padding技术)
- 解密得到不同长度的对称密钥
Part 0 环境准备
由于macOS
自带openssl
(不自带也可以brew
安装)
$ 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
可以成功编译,环境配成完成。
Part 1 构造和对一个大整数对象赋值 (至少支持到2^65536-1
)
构造和对一个大整数对象赋值 (至少支持到2^65536-1
)
OpenSSL
库中所有的大数对象均在bn.h
中进行定义。
OpenSSL
中的大数结构体如下
# 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
的数组来存的(不过这也是最正常不过的想法)。但是这样的大数是倒放的。由于是不定长的,所以要一个
top
的数来表明大数的长度。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
没有找到对应的条目,理论上内存够大应该能实现。下面实际测试能否支持这么大的数。
265536−1=(28)8192
书写测试代码:
#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();