使用OpenSSL API读取公用密钥的密码回调

时间:2018-09-19 17:06:52

标签: c ssl encryption openssl

使用公钥加密时,通常习惯以加密格式存储私钥,因为它们当然是秘密。这在OpenSSL C API中得到了反映,该API提供了PEM_write_PrivateKey之类的功能,该功能采用了可选的密码来加密密钥(例如AES)作为函数参数。然后,当从磁盘读回加密密钥时,OpenSSL API提供了类似PEM_read_PrivateKey之类的功能,该功能允许用户提供用作回调的函数指针,以便应用程序可以向OpenSSL提供加密密钥的密码。

但是令我困惑的是,OpenSSL API似乎还允许用户在读取 Public 密钥时提供密码回调。例如,用于读取公共密钥的一个API函数签名是:

 EVP_PKEY *PEM_read_PUBKEY(FILE *fp, EVP_PKEY **x,
                                        pem_password_cb *cb, void *u);

那么在读取公共密钥时提供密码回调的目的是什么? AFAIK加密公共密钥是没有意义的,因为根据定义,公共密钥应该对公众可用。那么,为什么OpenSSL API的函数参数在此处需要进行密码回调?

1 个答案:

答案 0 :(得分:1)

this comment中所述,可以对任何PEM编码的数据进行加密。 RFC 1421中定义了隐私增强邮件(PEM)的消息加密,在您的问题上下文中,有趣的是查看示例消息 section 4.6 Summary of Encapsulated Header Fields

-----BEGIN PRIVACY-ENHANCED MESSAGE-----
Proc-Type: 4,ENCRYPTED
Content-Domain: RFC822
DEK-Info: DES-CBC,F8143EDE5960C597
Originator-ID-Symmetric: linn@zendia.enet.dec.com,,
Recipient-ID-Symmetric: linn@zendia.enet.dec.com,ptf-kmc,3
Key-Info: DES-ECB,RSA-MD2,9FD3AAD2F2691B9A,
          B70665BB9BF7CBCDA60195DB94F727D3
Recipient-ID-Symmetric: pem-dev@tis.com,ptf-kmc,4
Key-Info: DES-ECB,RSA-MD2,161A3F75DC82EF26,
          E2EF532C65CBCFF79F83A2658132DB47

LLrHB0eJzyhP+/fSStdW8okeEnv47jxe7SJ/iN72ohNcUk2jHEUSoH1nvNSIWL9M
8tEjmF/zxB+bATMtPjCUWbz8Lr9wloXIkjHUlBLpvXR0UrUzYbkNpk0agV2IzUpk
J6UiRRGcDSvzrsoK+oNvqu6z7Xs5Xfz5rDqUcMlK1Z6720dcBWGGsDLpTpSCnpot
dXd/H5LMDWnonNvPCwQUHt==
-----END PRIVACY-ENHANCED MESSAGE-----

查看OpenSSL的1.1分支,它具有函数PEM_read_bio(),该函数支持读取此类消息并将其拆分为名称(如第一行所示),标头(位于其下的名称/值对)和数据(以base64编码的内容):

 int PEM_read_bio(BIO *in, char **name, char **header,
                  unsigned char **data, long *len);

所有OpenSSL PEM_read_XYZ()函数有时都从PEM_bytes_read_bio()调用它,因为它们都是通过宏扩展以相同的方式实现的。该函数包含以下调用:

PEM_read_bio(bp, &nm, &header, &data, &len)

先分割邮件,然后

PEM_get_EVP_CIPHER_INFO(header, &cipher);

找出在该消息的标题中找到了哪种加密信息,并用它填充EVP_CIPHER_INFO对象,然后

PEM_do_header(&cipher, data, &len, cb, u);

根据找到的密码信息对数据进行解密-必要时再次进行解密。请注意cb参数,该参数代表回调,这是一种在需要时获取任何密码短语输入的机制。

现在可能令人困惑的是,某些私钥格式(例如PKCS#8)也具有自己的独立于PEM编码的存储加密信息的机制。从技术上讲,应该有可能对此类密钥进行两次加密:一次在PEM级别,一次在PKCS#8级别。但是,用于生成或转换为PKCS#8格式密钥的OpenSSL工具似乎没有提供该选项。此外,除非还包括私钥,否则所有工具似乎都不会公开加密生成的任何公钥PEM文件的选项。

您可以检查一些输出以查看它们是否符合我的故事。首先,以PKCS#1格式生成RSA密钥对,未加密:

$ openssl genrsa
Generating RSA private key, 2048 bit long modulus (2 primes)
.................+++++
............+++++
e is 65537 (0x010001)
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAlcnR/w7zPoLrhuqFvcfz5fn8DFb0fEcCKOKSj+x+JJxGth9P
rJbxkt4pRXxbMIL0fX59HN5bRvQh2g59l/kfr30kCOnclap9nRrohWyg2i7720Cw
<truncated>

然后使用相同的命令,但是使用加密,这发生在PEM级别,如标题所示:

$ openssl genrsa -des3
Generating RSA private key, 2048 bit long modulus (2 primes)
.....................+++++
....................+++++
e is 65537 (0x010001)
Enter pass phrase:
Verifying - Enter pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,D90861647707F687

DIupLghCjcvpLenqAAULaJj1EDvUUfc2Xc58YVh7rMTSVgLwZ+9CtnUQJcup4aUQ
a1EdGXTadwBQB2jTtiFJbH67/5D26PHXPnM+YN2rnoReOExVS7hKu3DTq7c4j6a3
<truncated>

最后生成一个与PKCS#8类似的密钥,该密钥具有自己的加密功能,因此不会在PEM级别进行加密。您会看到PEM标头不存在。

$ openssl genpkey -algorithm RSA -des3
.........................................+++++
...........................................................................+++++
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIV0Ih4bsI6egCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECNOim8HAN8j5BIIEyEe05hHtc8HL
<truncated>

如果我的推理全部正确,则提示“输入PEM密码”不正确,因为这不是PEM级别的加密,而是PKCS#8级别的加密。