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_seedRAND_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 不会从同一个种子中给出相同的结果[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Java中Random类

设置种子时的整数是啥意思?

从 libtorrent 中的 url 种子低速下载

种子下载完成后传输将不会运行 shell 脚本

PyTorch DataLoader 对并行运行的批次使用相同的随机种子

火花随机森林:相同种子的不同结果