OpenSSL库由许多不同的包组成。 一些较低级别的软件包可以独立使用,而较高级别的软件包可以使用几个较低级别的软件包。 要有效地使用OpenSSL库,重要的是要了解我们已经介绍的密码学的基本概念,并熟悉更重要的补充软件包产品.
4.1 Multithread Support(多线程支持)
大多数现代操作系统都支持多线程应用程序,并且应用程序利用这种支持变得越来越普遍。 OpenSSL当然可以在多线程环境中使用; 然而,它要求开发人员做一些工作,以使程序线程安全。 许多开发人员在OpenSSL中犯的常见错误是他们认为库是线程安全的,不需要在应用程序中做任何特殊的事情。 这当然是一个不正确的假设,并且在多线程环境中设置OpenSSL失败会导致不可预知的行为,并且看起来随机的崩溃是非常难以调试的。
应用程序有两种不同的回调方式可以在多线程环境中安全地运行。 静态锁提供了固定数量的可供OpenSSL使用的互斥锁。 动态锁定允许OpenSSL根据需要创建互斥锁。 OpenSSL目前不使用动态锁,但保留在将来这样做的权利。 如果您希望您的应用程序将来继续以最小的工作量继续工作,我们建议您现在实施静态和动态锁定。
4.1.1 Static Locking Callbacks (静态锁回调)
函数名称
|
void locking_function(int mode, int n, const char *file, int line);
|
功能
|
下一个回调函数用于获取调用线程的唯一标识符。
|
参数
|
意义
|
mode
|
确定锁定功能应采取的操作。
|
n
|
应该获得或释放的锁的数量。
|
file
|
请求锁定操作发生的源文件的名称。
|
line
|
请求锁定操作发生的源代码行号。
|
4.1.2 Dynamic Locking Callbacks(动态锁回调)
步骤:创建锁->添加或删除锁->释放锁
结构成员:
struct CRYPTO_dynlock_value
{
MUTEX_TYPE mutex;
};
函数名称
|
struct CRYPTO_dynlock_value *dyn_create_function(const char *file,int line);
|
功能
|
我们需要定义的第一个回调函数用于创建一个新的互斥体,OpenSSL将能够使用这个互斥体来保护数据结构
|
参数
|
意义
|
file
|
请求创建互斥锁的源文件的名称。
|
line
|
请求创建互斥锁的源代码行号。
|
函数名称
|
void dyn_lock_function(int mode, struct CRYPTO_dynlock_value*mutex, const char *file, int line);
|
功能
|
下一个回调函数用于获取或释放互斥锁。
|
参数
|
意义
|
mode
|
确定锁定功能应采取的操作。
|
mutex
|
应该被获得或释放的互斥体。
|
file
|
请求创建互斥锁的源文件的名称。
|
line
|
请求创建互斥锁的源代码行号。
|
函数名称
|
void dyn_destroy_function(struct CRYPTO_dynlock_value *mutex,const char *file, int line);
|
功能
|
销毁OpenSSL不再需要的互斥体。
|
参数
|
意义
|
mutex
|
应该销毁的互斥体
|
file
|
请求创建互斥锁的源文件的名称。
|
line
|
请求创建互斥锁的源代码行号。
|
4.2 Internal Error Handling
4.2.1 Manipulating Error Queues
当OpenSSL库发生错误时,会记录大量的信息。 有些信息可能会自动尝试从错误中恢复,但其中大部分信息用于调试错误并将错误报告给用户。
ERR包提供了六个基本功能,这些功能对于从错误队列中获取信息非常有用。 每个函数总是从队列中检索最旧的信息,以便按照生成的顺序返回错误。 记录的最基本的信息是错误代码,它描述发生的错误。 错误代码是一个32位整数,仅对OpenSSL有意义。 也就是说,OpenSSL为它可能遇到的任何错误条件定义了自己的唯一错误代码。 它不依赖于任何其他库(包括标准C运行库)定义的错误代码。 对于六个基本函数中的每一个,这个错误代码是函数的返回值。 如果队列中没有错误,则任何一个的返回都将是0,这也告诉我们0从来不是有效的错误代码。
函数名称
|
unsigned long ERR_get_error(void);
|
功能
|
错误码
|
函数名称
|
unsigned long ERR_peek_error(void);
|
功能
|
第二个函数也只从错误队列中检索错误代码,但是它不会被删除
来自队列的错误报告,所以下一次调用将检索到相同的错误:
|
参数
|
意义
|
mutex
|
应该销毁的互斥体
|
file
|
请求创建互斥锁的源文件的名称。
|
line
|
请求创建互斥锁的源代码行号。
|
函数名称
|
unsigned long ERR_get_error_line(const char **file, int *line);
|
功能
|
第三个函数建立在由ERR_get_error和ERR_peek_error返回的信息上。 除了返回错误代码之外,
它还会返回生成错误的源文件和源代码行的名称。 像ERR_get_error一样,它也从队列中删除错误报告.
|
参数
|
意义
|
file
|
接收生成错误的源文件的名称。
|
line
|
接收生成错误的源代码行号。
|
函数名称
|
unsigned long ERR_peek_error_line(const char **file, int *line);
|
功能
| |
参数
|
意义
|
file
|
接收生成错误的源文件的名称。
|
line
|
接收生成错误的源代码行号。
|
函数名称
|
unsigned long ERR_get_error_line_data(const char **file, int *line,const char **data, int *flags);
|
功能
|
获取错误行号和错误数据,并从队列中删除
|
参数
|
意义
|
file
|
接收生成错误的源文件的名称。
|
line
|
接收生成错误的源代码行号。
|
data
|
接收指向错误报告中附带的额外数据的指针。
|
flags
|
接收一组定义额外数据属性的标志。
|
函数名称
|
unsigned long ERR_peek_error_line_data(const char **file, int *line,const char **data, int *flags);
|
功能
|
获取错误行号和错误数据
|
参数
|
意义
|
file
|
接收生成错误的源文件的名称。
|
line
|
接收生成错误的源代码行号。
|
data
|
接收指向错误报告中附带的额外数据的指针。
|
flags
|
接收一组定义额外数据属性的标志。
|
函数名称
|
void ERR_clear_error(void);
|
功能
|
还有最后一个队列操作函数,我们将在这里讨论:清除函数
错误队列。
|
参数
|
意义
|
最后还有一个队列操作函数,我们将在这里讨论:清除错误队列的函数。 它将删除当前队列中的所有错误。 一般情况下,除非我们试图重置当前线程的错误状态,而不关心队列中的其他错误,否则不需要调用这个函数。 一旦被调用,没有办法恢复以前的错误,所以明智地使用它.
4.3 Abstract Input/Output
OpenSSL库提供了多种功能,用于创建和销毁BIO,将它们链接在一起以及读取或写入数据。 需要注意的是,BIO包是一个低级包,因此您必须小心使用它。 许多功能将允许您执行操作,以后可能导致不可预知的行为甚至崩溃。
函数
|
功能
|
The BIO *BIO_new(BIO_METHOD *type);
|
初始化BIO
|
int BIO_set(BIO *bio, BIO_METHOD *type);
|
|
int BIO_free(BIO *bio);
|
当一个BIO不再需要时,它应该被销毁。
|
void BIO_vfree(BIO *bio);
|
BIO_vfree函数与BIO_free相同,只是它不返回值
|
void BIO_free_all(BIO *bio);
|
BIO_free_all函数可用于释放整个BIO链。
|
BIO *BIO_push(BIO *bio, BIO *append);
|
BIO_push函数会将BIO附加到BIO,从而创建或延长BIO链。
|
BIO *BIO_pop(BIO *bio);
|
BIO_pop函数将从指定的BIO链中删除指定的BIO并返回
链中的下一个BIO或者如果没有下一个BIO,则为NULL。
|
int BIO_read(BIO *bio, void *buf, int len);
|
BIO_read的行为与C运行时函数读取几乎完全相同。
|
int BIO_gets(BIO *bio, char *buf, int len);
|
为从源BIO读取数据提供的另一个函数是BIO_gets,通常与C运行库对应的fgets的行为几乎相同。
|
int BIO_write(BIO *bio, const void *buf, int len);
|
从BIO_write的行为写
|
int BIO_puts(BIO *bio, const char *buf);
|
BIO_puts将指定的缓冲区解释为C风格的字符串,并尝试将其全部写出。
|
我们提到,对于四个读写函数中的每一个,0或-1的返回值可能会或可能不一定表示发生错误。 提供了一套函数,可以让我们确定是否真的发生了错误,以及是否应该重试该操作。
4.4 Random Number Generation
整个OpenSSL库中的许多函数都需要随机数的可用性。 例如,创建会话密钥和生成公钥/私钥对都需要随机数字。 为了满足这个要求,RAND包提供了一个密码强的伪随机数发生器(PRNG)。 这意味着它产生的“随机”数据并不是真正的随机数,但在计算上难以预测。
对于所有其他的随机数要求,由PRNG产生的伪随机数是适合使用。
在许多Unix系统上,/ dev / random和/dev/urandom随机数产生器
参考代码:
int RAND_load_file(const char *filename, long bytes);
int RAND_write_file(const char *filename);
/* Read 1024 bytes from /dev/random and seed the PRNG with it */
RAND_load_file("/dev/random", 1024);
/* Write a seed file */
RAND_write_file("prngseed.dat");
/* Read the seed file in its entirety and print the number of bytes
obtained */
nb = RAND_load_file("prngseed.dat", -1);
printf("Seeded the PRNG with %d byte(s) of data from prngseed.dat.\n",nb);
函数
|
功能
|
RAND_screen
|
可以被周期性地调用以收集熵。
|
RAND_load_file
|
这将使PRNG的文件内容达到指定的字节数,或者如果限制被指定为-1,则将它的全部种子。
|
RAND_write_file
|
显示了该函数及其对应的一些示例用法,
|
int RAND_egd(const char *path);
|
RAND_egd尝试连接到指定的Unix域套接字。
|
int RAND_egd_bytes(const char *path, int bytes);
|
RAND_egd_bytes将尝试连接到指定的Unix域套接字。
|
int RAND_query_egd_bytes(const char *path, unsigned char *buf, int
bytes);
|
|
4.5 Arbitrary Precision Math
4.5.1 The Basics
BN_bn2bin
|
它会返回写入到提供的缓冲区的字节数。
|
BN_bin2bn
|
结果放入第三个指定的BIGNUM中论点,覆盖它以前可能持有的任何价值。
|
BN_bn2hex
|
存储在Cstyle字符串中的十六进制表示。
|
BN_bn2dec
|
函数BN_bn2dec将BIGNUM转换为存储在C-stylestring中的十进制表示形式。...
|
BN_hex2bn
|
将存储在C风格字符串中的数字的十六进制表示转换为BIGNUM。
|
BN_dec2bn
|
将十进制的转换成BIGNUM数据
|
4.5.2 Mathematical Operations
Function
|
Comments
|
BN_add(r, a, b)
|
(r = a + b) r may be the same as a or b.
|
BN_sub(r, a, b)
|
(r = a - b)
|
BN_mul(r, a, b, ctx)
|
(r = a x b) r may be the same as a or b.
|
BN_sqr(r, a, ctx)
|
(r = pow(a, 2)) r may be the same as a. This function is faster than
BN_mul(r, a, a).
|
BN_div(d, r, a, b, ctx)
|
(d = a / b, r = a % b) Neither d nor r may be the same as either a or b.
Either d or r may be NULL.
|
BN_mod(r, a, b, ctx)
|
(r = a % b)
|
BN_nnmod(r, a, b, ctx)
|
(r = abs(a % b))
|
BN_mod_add(r, a, b, m,
ctx)
|
(r = abs((a + b) % m))
|
BN mod sub(r, a, b, m,ctx)
|
(r = abs((a - b) % m))
|
BN_mod_mul(r, a, b, m,ctx)
|
(r = abs((a x b) % m)) r may be the same as a or b.
|
BN_mod_sqr(r, a, m,ctx)
|
(r = abs(pow(a, 2) % m))
|
BN_exp(r, a, p, ctx)
|
(r = pow(a, p))
|
BN_mod_exp(r, a, p, m,ctx)
|
(r = pow(a, 2) % m)
|
BN_gcd(r, a, b, ctx)
|
Finds the greatest common divisor of a and b. r may be the same as a orb.
|
4.5.3 Generating Prime Numbers(产生质素)
函数
|
BIGNUM *BN_generate_prime(BIGNUM *ret,
int bits,
int safe,
BIGNUM*add,
BIGNUM *rem,
void (*callback)(int,int, void *),
void *cb_arg);
|
功能
|
顾名思义,函数产生素数,更重要的是产生伪随机素数。
|
参数
|
意义
|
ret
|
用于接收生成的素数。 如果指定为NULL,则会创建一个新的BIGNUM,并使用BN_new进行初始化并返回。
|
bits
|
应该用来表示生成的素数的位数
|
safe
|
可以是零或非零,表示生成的素数是否应该是安全的。
|
add
|
用于指定生成的素数应具有的其他属性。
|
rem
|
用于指定生成的素数应具有的其他属性。
|
callback
|
在生成素数期间调用的函数,用于报告操作的状态。
|
cb_arg
|
如果指定了一个值,则仅用于传递给回调函数。
|
ID string
|
Description
|
openssl
|
引擎使用正常的内置函数进行加密操作。
|
openbsd_dev_crypto
|
在OpenBSD操作系统上,这个引擎将使用操作系统内置的内核级加密技术。
|
cswift
|
用于CryptoSwift加速硬件。
|
chil
|
用于nCipher CHIL加速硬件。
|
atalla
|
用于Compaq Atalla加速硬件。
|
nuron
|
用于Nuron加速硬件。
|
ubsec
|
用于Broadcom uBSec加速硬件。
|
aep
|
用于Aep加速硬件。
|
sureware
|
用于SureWare加速硬件。
|
Table 4-3. Flags for ENGINE_set_default
| |
Flag
|
Description
|
ENGINE_METHOD_RSA
|
将引擎使用限制为仅限RSA操作。
|
ENGINE_METHOD_DSA
|
将引擎使用限制为仅DSA操作。
|
ENGINE_METHOD_DH
|
将引擎使用限制为仅限DH操作。
|
ENGINE_METHOD_RAND
|
将引擎使用限制为只有随机数字操作。
|
ENGINE_METHOD_CIPHERS
|
将引擎使用限制为仅对称密码操作。
|
ENGINE_METHOD_DIGESTS
|
将引擎使用限制为仅消化操作。
|
ENGINE_METHOD_ALL
|
允许OpenSSL使用任何上述实现。
|