让OpenSSL生成确定性密钥

6
我想让openssl生成确定性的私钥和公钥对,与之相关的设备依赖数据会通过HASH填充到seedbuf中作为种子,但是似乎无法使用该种子进行随机数生成,生成的密钥仍然是随机的。由于多种原因,我不想只生成一次密钥并将其存储,我只想让私钥存在于内存中。
RAND_seed(seedbuf, sizeof(seedbuf));

bne = BN_new();
if (1 != BN_set_word(bne,e)) {
    goto free_stuff;
}

keypair = RSA_new();
if(1 != RSA_generate_key_ex(keypair, KEY_LENGTH, bne, NULL)) {
    goto free_stuff;
}

基本上,我希望“RSA_generate_key_ex”函数在使用相同的输入时每次返回相同的密钥对。
2个回答

6

让openssl生成确定性密钥...

我不认为你可以直接做到这一点。默认情况下,OpenSSL使用md_rand,它会自动进行种子生成。调用rand_seed内部调用rand_add,它会将状态添加到(而不是丢弃/替换)状态中。您可以在crypto/rand/md_rand.c中找到md_rand的源代码。

如果启用了FIPS构建,则可以使用NIST的SP 800-90确定性随机比特生成器。然而,我似乎记得它们的操作方式类似于md_rand。也就是说,您可以添加到内部状态,但无法真正控制它。您可以在crypto/rand/rand_lib.c中找到源代码。

我认为您有一个选择。您可以创建自己的my_rand。将其基于块密码或哈希。在启动时,使用设备相关数据对其进行种子处理,然后返回确定性字节。

现在,要想让像RSA_generate_key_ex这样的函数使用你的PRNG,你需要将它打包进一个OpenSSLENGINE中。 OpenSSL的Richard Levitte在OpenSSL博客上发布了两个不错的系列博客Engine Building Lesson 1: A Minimum Useless EngineEngine Building Lesson 2: An Example MD5 Engine
一旦你将其与引擎打包,就可以像这样使用它。在使用ENGINE_METHOD_RAND设置随机方法后,你将使用你的算法。
ENGINE* eng = ENGINE_by_id("my_rand");
unsigned long err = ERR_get_error();

if(NULL == eng) {
    fprintf(stderr, "ENGINE_by_id failed, err = 0x%lx\n", err);
    abort(); /* failed */
}

int rc = ENGINE_init(eng);
err = ERR_get_error();

if(0 == rc) {
    fprintf(stderr, "ENGINE_init failed, err = 0x%lx\n", err);
    abort(); /* failed */
}

rc = ENGINE_set_default(eng, ENGINE_METHOD_RAND);
err = ERR_get_error();

if(0 == rc) {
    fprintf(stderr, "ENGINE_set_default failed, err = 0x%lx\n", err);
    abort(); /* failed */
}

如果你想查看一个ENGINE的实现,可以查看crypto/engine/eng_rdrand.c中的rdrand引擎。引擎并不复杂,容易进行复制和粘贴。请确保将新引擎添加到crypto/engine/Makefile中的Makefile中。

6

如果您想生成确定性密钥,而不一定使用OpenSSL,GnuTLS 可能能够提供帮助:

certtool --generate-privkey --outfile privkey.pem --key-type=rsa --sec-param=high --seed=0000000000000000000000000000000000000000000000000000000000000000

使用--seed,至少在当前版本中,意味着使用可证明的算法生成(FIPS PUB186-4)--provable
生成的privkey.pem格式为RSA-PSS,您可以使用以下命令将其转换为原始RSA格式:
certtool --to-rsa --load-privkey privkey.pem --outfile privkey.key

如果需要,您可以从任一格式的密钥生成自签名证书:

certtool --generate-self-signed --load-privkey privkey.key --outfile signed.crt --template cert.cfg

如果你想让这一步骤也具有确定性,或避免每次运行命令时回答问题,请在最后一个命令中添加--template cert.cfg参数。使生成变得确定性的cert.cfg文件的最小内容如下:

serial = 1
activation_date = "2019-01-01 00:00:00 UTC"
expiration_date = "2029-01-01 00:00:00 UTC"

在使用4个种子时,这是否有用?我找不到一种将两个随机密钥和两个私有密钥(服务器和客户端)转换为一个主密钥的方法,作为ssl握手过程的最后一步。谢谢。 - Ori David
1
@OriDavid:我不太明白你的问题以及你想要实现什么。在TLS中没有所谓的主密钥。你需要CA证书吗? - user
我想要实现IBM SSL握手概述中的第四步,但是我不知道如何"计算秘密密钥"。假设我拥有必要的密钥,公式是什么? - Ori David
1
@OriDavid:如果您需要那种详细程度的信息,最好直接阅读规范(TLS 1.3参见RFC 8446,以前的版本请参见Wikipedia文章),并且可能需要查看一些TLS库源代码。无论如何,TLS实现细节完全超出了这个问题的范围,因此您应该提出另一个问题,甚至在其他网站上提问,如Crypto.SE - user
在我的系统上,如果种子的长度不符合预期,则密钥不会相同。预期的长度在此处有记录链接 - Alai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接