RAND_bytes 不会从同一个种子中给出相同的结果[重复]
Posted
技术标签:
【中文标题】RAND_bytes 不会从同一个种子中给出相同的结果[重复]【英文标题】:RAND_bytes doesn't give the same result from the same seed [duplicate] 【发布时间】:2017-05-08 03:21:35 【问题描述】:我正在尝试使用 OpenSSL 编写自定义 RSA 密钥对生成算法。我已经使用PKCS5_PBKDF2_HMAC_SHA1
函数来生成 PRNG 种子,所以我使用这个种子作为 RAND_seed 输入。
不幸的是,每次我使用相同的种子调用RAND_bytes
时,我都会获得不同的随机数,但这不是预期的行为,因为正如How can one securely generate an asymmetric key pair from a short passphrase? 的答案所说,随机数生成器是确定性的(相同的种子相同的输出)。
下面是测试用例。我也声明了常量种子,但生成永远不是确定性的。
unsigned int seed = 0x00beef00;
unsigned int rnum[5];
RAND_seed(&seed, sizeof(seed));
RAND_bytes((unsigned char *)&rnum[0], sizeof(rnum));
哪里出错了?
【问题讨论】:
假设您使用默认的RAND
引擎,请查看rand_bytes
in md_rand.c
的源代码。另请参阅 Stack Overflow 上的 Making openssl generate deterministic key。简而言之,您必须创建一个 OpenSSL ENGINE
才能做到这一点。
要创建自定义引擎,请参阅 OpenSSL 博客上的 Engine Building Lesson 1: A Minimum Useless Engine 和 Engine Building Lesson 2: An Example MD5 Engine。
【参考方案1】:
这不是错误。 OpenSSL 随机数生成器使用良好的随机性来源自行进行一些播种。
所以在RAND_seed
中使用相同的种子值并不能保证相同的随机数序列。这是一件好事,因为它降低了它们的可预测性,因此更安全。
来自RAND_seed
的手册页:
#include <openssl/rand.h> void RAND_seed(const void *buf, int num); void RAND_add(const void *buf, int num, double entropy); int RAND_status(void); int RAND_event(UINT iMsg, WPARAM wParam, LPARAM lParam); void RAND_screen(void);
RAND_add()
将buf
处的num
字节混合到 PRNG 状态。因此,如果buf
的数据对于对手来说是不可预测的,这增加了 状态的不确定性并使 PRNG 输出更少 可预测的。合适的输入来自用户交互(随机键 按下、鼠标移动)和某些硬件事件。entropy
参数是(的下限)对随机性的估计 包含在 buf 中,以字节为单位。有关来源的详细信息 随机性以及如何估计它们的熵可以在 文学,例如RFC 1750。
RAND_add()
可能会使用敏感数据(例如用户输入)调用 密码。种子值无法从 PRNG 中恢复 输出。OpenSSL 确保 PRNG 状态对于每个线程都是唯一的。 在提供“/dev/urandom”的系统上,随机设备是 用于透明地播种 PRNG。 但是,在所有其他系统上, 应用程序负责通过调用播种 PRNG
RAND_add()
、RAND_egd(3)
或RAND_load_file(3)
。
RAND_seed()
等价于RAND_add()
num == entropy
。
因此,如果您的系统有/dev/urandom
,它将被用作 PRNG 的初始种子。
【讨论】:
您好,感谢您的回复,主题为crypto.stackexchange.com/questions/1662/…,它说“PRNG 是确定性的(相同的种子意味着相同的输出序列)并产生随机位。” @CrescenzoMugione PRNG 是确定性的。这里的不同之处在于,OpenSSL 使用来自/dev/urandom
的随机数据透明地为 PRNG 播种。可以多次调用RAND_seed
或RAND_add
来添加种子数据。因此,当您调用其中一个函数时,给定的种子缓冲区会添加到从/dev/random
提取的数据中。这有助于增加随机性并使任何生成的字节不可预测。【参考方案2】:
Openssl 的int RAND_bytes(unsigned char *buf, int num);
试图让事情尽可能随机。这显然是您不想要的功能,而是在寻找可重复的伪随机序列
但是Openssl也有
int RAND_pseudo_bytes(unsigned char *buf, int num);
这可能是您正在寻找的,伪部分,为您提供可重复的序列。
https://linux.die.net/man/3/rand_pseudo_bytes
Force openssl's RNGs to return a repeatable byte sequence
顺便说一句,如果您正在使用 RSA,那么可重复的随机序列并不是那么好,因为您要寻找的是两个大素数。做一个大的 PRNG 数字,然后测试素数可能是一个坏主意。其中一半可以被 2 整除! :)
如果我没记错的话,您需要选择一个好的起始数字,通过与 1 进行 ORing 使其为奇数,然后测试素数,如果不是,则增加 4 并重试。
一旦找到它们,您可能不想再次使用可重复的 PRNG 字节开始搜索。
虽然我很确定这是一个学习项目,但如果你想要的只是来自 openssl 的 RSA 密钥对,请查看
https://***.com/a/5246045/6047952
【讨论】:
您好,感谢您的回复,我尝试了 RAND_pseudo_bytes,但它总是给我相同种子的随机输出。我需要为相同的输入生成相同的输出,因为我的密钥对对于单个设备必须是唯一的,由一些参数标识。RAND_pseudo_bytes
将 PID、时间等混合到其状态中。分叉也混入新的状态。检查md_rand.c
and rand_bytes
function的源文件。
@jww 我已经尝试了***.com/a/7510354/6047952 的测试程序,我得到了一个可重复的序列。我的版本与您的源代码版本有何不同我不能说。但是可重复的伪随机序列对于诸如蒙特卡洛程序之类的东西很有用,所以如果这种能力已经丢失,我会很惊讶
@CrescenzoMugione。即使你得到了一个可重复的序列,你也得到了一个非常弱的系统。这类似于 Debian 曾经犯的错误,最终导致他们的 openssh 只生成 32K 唯一会话密钥,因为最终他们的种子仅基于 PID。 schneier.com/blog/archives/2008/05/random_number_b.html MAC 地址之类的东西甚至都不是好秘密,因为您子网上的每个人都可以得到它。请记住,约翰·冯·诺依曼曾经说过:“任何考虑用算术方法产生随机数字的人,当然都处于一种犯罪状态。”[以上是关于RAND_bytes 不会从同一个种子中给出相同的结果[重复]的主要内容,如果未能解决你的问题,请参考以下文章