openssl rsautl";无法加载公钥";

openssl rsautl";无法加载公钥";,openssl,pkcs#1,Openssl,Pkcs#1,我以PKCS#1RSAPublicKey格式生成了一个RSA公钥。我想使用此密钥用openssl rsautl加密一些数据,如下所示: $ openssl genrsa -out private_key.pem 512 Generating RSA private key, 512 bit long modulus ................++++++++++++ ...++++++++++++ e is 65537 (0x10001) $ openssl rsa -in private

我以PKCS#1
RSAPublicKey
格式生成了一个RSA公钥。我想使用此密钥用
openssl rsautl
加密一些数据,如下所示:

$ openssl genrsa -out private_key.pem 512
Generating RSA private key, 512 bit long modulus
................++++++++++++
...++++++++++++
e is 65537 (0x10001)
$ openssl rsa -in private_key.pem -RSAPublicKey_out -out public_key.pem
writing RSA key
$ echo "this is the cleartext" | openssl rsautl -encrypt -out encrypted_with_pub_key -pubin -inkey public_key.pem
unable to load Public Key

这是怎么回事?为什么
openssl
无法读取它自己生成的密钥?它能读什么格式?更一般地说,这是为什么?

TL;DR:OpenSSL支持几种不推荐使用的旧格式。当输入是rsa私钥对时,
openssl rsa-pubout
似乎有一个特殊情况。最后,我给出了两个命令序列,让OP的加密(和相应的解密)成功;一种是使用DER而不是PEM,另一种是首先发出DER,然后将其转换为PEM…奇怪的是,使用
openssl rsa
,当输入是DER编码的公钥时,它会输出现代格式。。。不管怎么说,下面是OP最初尝试失败原因的详细描述。至于“为什么文档这么差”…欢迎使用OpenSSL。好好学习源目录结构,因为它会加快你的搜索速度。:)

详细解释(详情摘自OpenSSL 1.0.2m)

OpenSSL rsautl应用程序将
-inform
参数(传入密钥表示)默认为
format=format\u PEM

然后,它使用以下格式为传入密钥选择读取器:

else if (format == FORMAT_PEMRSA) {
    RSA *rsa;
    rsa = PEM_read_bio_RSAPublicKey(key, NULL,
                                    (pem_password_cb *)password_callback,
                                    &cb_data);
    /* ... */
}
else if (format == FORMAT_PEM) {
    pkey = PEM_read_bio_PUBKEY(key, NULL,
                               (pem_password_cb *)password_callback,
                               &cb_data);
因此,我们将使用
PEM\u read\u bio\u PUBKEY
。注意上面的
FORMAT\u PEMRSA
条目……我们将同时开发通向
PEM\u read\u bio\r公钥的路径。这两个读取器都是通过几个宏定义的

crypto/pem/pem_all.c:427:IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
crypto/pem/pem_all.c:241:IMPLEMENT_PEM_rw_const(RSAPublicKey, RSA, PEM_STRING_RSA_PUBLIC, RSAPublicKey)
扩展到

# define IMPLEMENT_PEM_rw(name, type, str, asn1) \
    IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_write(name, type, str, asn1)

# define IMPLEMENT_PEM_rw_const(name, type, str, asn1) \
    IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_write_const(name, type, str, asn1)    
反过来使用

# define IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
    IMPLEMENT_PEM_read_fp(name, type, str, asn1)
它将实际定义构建为:

# define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
{ \
  return PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
}
需要特别注意的是第三个参数,
str
,对于
PEM\u read\u bio\u publickey
版本,它作为
PEM\u STRING\u RSA\u PUBLIC
传递,对于
PEM\u read\u bio\r publickey
版本,它作为
PEM\u STRING\u RSA\u PUBLIC
传递。这些字符串分别是

./crypto/pem/pem.h:122:# define PEM_STRING_PUBLIC       "PUBLIC KEY"
./crypto/pem/pem.h:124:# define PEM_STRING_RSA_PUBLIC   "RSA PUBLIC KEY"
查看
PEM\u ASN1\u read\u bio
的实现,我们看到它调用
PEM\u bytes\u read\u bio

void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
                        pem_password_cb *cb, void *u)
{
    const unsigned char *p = NULL;
    unsigned char *data = NULL;
    long len;
    char *ret = NULL;

    if (!PEM_bytes_read_bio(&data, &len, NULL, name, bp, cb, u))
        return NULL;
    /* ... */
PEM\u bytes\u read\u bio
读取密钥文件,并将其分成若干部分
nm
获取
----
秒之间的标记,例如
----BEGIN RSA公钥---
,然后由
check\u pem
检查

int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm,
                       const char *name, BIO *bp, pem_password_cb *cb,
                       void *u)
{
    EVP_CIPHER_INFO cipher;
    char *nm = NULL, *header = NULL;
    unsigned char *data = NULL;
    long len;
    int ret = 0;

    for (;;) {
        if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
            if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE)
                ERR_add_error_data(2, "Expecting: ", name);
            return 0;
        }
        if (check_pem(nm, name))
check\u pem
检查如下,其中
nm
是在文件中找到的字符串,
name
是从
pem\u read\u bio\u XXX
函数中硬编码的位置传递过来的

static int check_pem(const char *nm, const char *name)
{
    /* Normal matching nm and name */
    if (!strcmp(nm, name))
        return 1;           

    /* special cases for
      PKCS8 format (BEGIN PRIVATE KEY or BEGIN ENCRYPTED PRIVATE KEY)
      Various things ending in PARAMETERS
      Various X509 related files
      PKCS7 format (BEGIN PKCS7 or BEGIN PKCS7 SIGNED DATA)
      CMS things (BEGIN CMS) */

    /* ... */
它只是在
name
nm
之间执行一个
strcmp
。因此,要使用
PEM\u read\u bio\u PUBKEY
成功读取RSA公钥,需要从
----BEGIN public key------
开始。但是查看OP指令生成的密钥,我们发现
----BEGIN RSA public key------
。但这是对应于PEM\u read\u bio\u RsaPublicKey的字符串。。。也许我们可以使用
-inform
来选择
format=format\u PEMRSA
,然后让
rsautl
以这种方式读取我们的公钥
rsautl
使用名为
str2fmt
的函数来解析
-inform
参数。让我们来看一看:

int str2fmt(char *s)
{
    if (s == NULL)
        return FORMAT_UNDEF;
    if ((*s == 'D') || (*s == 'd'))
        return (FORMAT_ASN1);
    else if ((*s == 'T') || (*s == 't'))
        return (FORMAT_TEXT);
    else if ((*s == 'N') || (*s == 'n'))
        return (FORMAT_NETSCAPE);
    else if ((*s == 'S') || (*s == 's'))
        return (FORMAT_SMIME);
    else if ((*s == 'M') || (*s == 'm'))
        return (FORMAT_MSBLOB);
    else if ((*s == '1')
             || (strcmp(s, "PKCS12") == 0) || (strcmp(s, "pkcs12") == 0)
             || (strcmp(s, "P12") == 0) || (strcmp(s, "p12") == 0))
        return (FORMAT_PKCS12);
    else if ((*s == 'E') || (*s == 'e'))
        return (FORMAT_ENGINE);
    else if ((*s == 'H') || (*s == 'h'))
        return FORMAT_HTTP;
    else if ((*s == 'P') || (*s == 'p')) {
        if (s[1] == 'V' || s[1] == 'v')
            return FORMAT_PVK;
        else
            return (FORMAT_PEM);
    } else
        return (FORMAT_UNDEF);
}
没有。没有办法让它返回格式

那么,如果我们编辑公钥文件,让标记改为说
----BEGIN public key---

$ sed -e "s/RSA PUBLIC/PUBLIC/" public_key.pem > public_key_mod.pem             
$ echo "this is the cleartext" | openssl rsautl -encrypt -out encrypted_with_pub_key -pubin -inkey public_key_mod.pem
unable to load Public Key
没有

如果我们将公钥提取到DER而不是PEM呢

$ openssl rsa -in private_key.pem -out public_key.der -outform DER -pubout
writing RSA key
$ echo "this is the cleartext" | openssl rsautl -encrypt -out encrypted_with_pub_key -pubin -inkey public_key.der -keyform DER

$ openssl rsautl  -decrypt -in encrypted_with_pub_key -inkey private_key.pem
this is the cleartext
成功!也许我们可以使用OpenSSL将DER密钥转换为与
rsautl

$ openssl rsa -in public_key.der -inform DER -pubin -out test.pem
writing RSA key
$ cat test.pem
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAM3uGdU6YtwI5S8K+GgddW8KhrzmSFVI
6cvBT+XqOuSVo+n8VyUfADHw4rPxjy/dWDpyOxzWdTg8VZ77Vs06af8CAwEAAQ==
-----END PUBLIC KEY-----
$ echo "this is the cleartext" | openssl rsautl -encrypt -out encrypted_with_pub_key -pubin -inkey test.pem

$ openssl rsautl  -decrypt -in encrypted_with_pub_key -inkey private_key.pem
this is the cleartext

因此,是的……它看起来像是在将
BEGIN rsa私钥作为输入时,只写入过时的
BEGIN rsa公钥;DR:OpenSSL支持几种不推荐使用的旧格式。当输入是rsa私钥对时,
openssl rsa-pubout
似乎有一个特殊情况。最后,我给出了两个命令序列,让OP的加密(和相应的解密)成功;一种是使用DER而不是PEM,另一种是首先发出DER,然后将其转换为PEM…奇怪的是,使用
openssl rsa
,当输入是DER编码的公钥时,它会输出现代格式。。。不管怎么说,下面是OP最初尝试失败原因的详细描述。至于“为什么文档这么差”…欢迎使用OpenSSL。好好学习源目录结构,因为它会加快你的搜索速度。:)

详细解释(详情摘自OpenSSL 1.0.2m)

OpenSSL rsautl应用程序将
-inform
参数(传入密钥表示)默认为
format=format\u PEM

然后,它使用以下格式为传入密钥选择读取器:

else if (format == FORMAT_PEMRSA) {
    RSA *rsa;
    rsa = PEM_read_bio_RSAPublicKey(key, NULL,
                                    (pem_password_cb *)password_callback,
                                    &cb_data);
    /* ... */
}
else if (format == FORMAT_PEM) {
    pkey = PEM_read_bio_PUBKEY(key, NULL,
                               (pem_password_cb *)password_callback,
                               &cb_data);
因此,我们将使用
PEM\u read\u bio\u PUBKEY
。注意上面的
FORMAT\u PEMRSA
条目……我们将同时开发通向
PEM\u read\u bio\r公钥的路径。这两个读取器都是通过几个宏定义的

crypto/pem/pem_all.c:427:IMPLEMENT_PEM_rw(PUBKEY, EVP_PKEY, PEM_STRING_PUBLIC, PUBKEY)
crypto/pem/pem_all.c:241:IMPLEMENT_PEM_rw_const(RSAPublicKey, RSA, PEM_STRING_RSA_PUBLIC, RSAPublicKey)
扩展到

# define IMPLEMENT_PEM_rw(name, type, str, asn1) \
    IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_write(name, type, str, asn1)

# define IMPLEMENT_PEM_rw_const(name, type, str, asn1) \
    IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_write_const(name, type, str, asn1)    
反过来使用

# define IMPLEMENT_PEM_read(name, type, str, asn1) \
    IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
    IMPLEMENT_PEM_read_fp(name, type, str, asn1)
它将实际定义构建为:

# define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
{ \
  return PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
}
需要特别注意的是第三个参数,
str
,对于
PEM\u read\u bio\u publickey
版本,它作为
PEM\u STRING\u RSA\u PUBLIC
传递,对于
PEM\u read\u bio\r publickey
版本,它作为
PEM\u STRING\u RSA\u PUBLIC
传递。这些字符串分别是

./crypto/pem/pem.h:122:# define PEM_STRING_PUBLIC       "PUBLIC KEY"
./crypto/pem/pem.h:124:# define PEM_STRING_RSA_PUBLIC   "RSA PUBLIC KEY"
查看
PEM\u ASN1\u read\u bio
的实现,我们看到它调用
PEM\u bytes\u read\u bio

void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x,
                        pem_password_cb *cb, void *u)
{
    const unsigned char *p = NULL;
    unsigned char *data = NULL;
    long len;
    char *ret = NULL;

    if (!PEM_bytes_read_bio(&data, &len, NULL, name, bp, cb, u))
        return NULL;
    /* ... */
PEM\u bytes\u read\u bio
读取密钥文件,并将其分成若干部分
nm
获取
----
秒之间的标记,例如,
----BEGIN RSA PUBLI