如何将 .p12 转换为包含未加密的 PKCS#1 私钥块的 .pem?

jam*_*her 4 ssl openssl pkcs#12 pkcs#1

我有一个Certificates.p12文件,我希望将其转换为certificates.pem包含 PKCS#1 格式的未加密私钥的文件。我以前可以通过运行来做到这一点:

openssl pkcs12 -in Certificates.p12 -out certificates.pem -nodes -clcerts
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,生成的certificates.pem文件有一个PRIVATE KEYPEM 块。但是,我使用的库不理解这个 PEM 块,因为它期望它是一个 PKCS#1 私钥。PKCS#1 私钥的 ASN.1 结构由RFC 3447定义为:

RSAPrivateKey ::= SEQUENCE {
    version           Version,
    modulus           INTEGER,  -- n
    publicExponent    INTEGER,  -- e
    privateExponent   INTEGER,  -- d
    prime1            INTEGER,  -- p
    prime2            INTEGER,  -- q
    exponent1         INTEGER,  -- d mod (p-1)
    exponent2         INTEGER,  -- d mod (q-1)
    coefficient       INTEGER,  -- (inverse of q) mod p
    otherPrimeInfos   OtherPrimeInfos OPTIONAL
}
Run Code Online (Sandbox Code Playgroud)

我的坏私钥块certificates.pem没有这个 PKCS#1 结构!相反,它的 ASN.1 结构如下所示:

$ openssl asn1parse -i -in badprivatekey.pem
    0:d=0  hl=4 l=1212 cons: SEQUENCE
    4:d=1  hl=2 l=   1 prim:  INTEGER           :00
    7:d=1  hl=2 l=  13 cons:  SEQUENCE
    9:d=2  hl=2 l=   9 prim:   OBJECT            :rsaEncryption
   20:d=2  hl=2 l=   0 prim:   NULL
   22:d=1  hl=4 l=1190 prim:  OCTET STRING      [HEX DUMP]:308204A...very long hex...
Run Code Online (Sandbox Code Playgroud)

上面的格式是什么?的文档openssl pkcs12只是模糊地说它的输出是“以 PEM 格式编写的”。我需要更有力的保证私钥 PEM 块是 PKCS#1 格式。

奇怪的是,它openssl rsa理解“坏”私钥的奇怪格式,并且可以将其转换为正确的 PKCS#1 结构:

openssl rsa -in badprivatekey.pem -out goodprivatekey.pem
Run Code Online (Sandbox Code Playgroud)

虽然openssl rsa理解输入文件,但该工具似乎无法告诉我为什么,即输入文件的格式是什么。

的输出格式是openssl pkcs12什么?具体它的私钥块的格式是什么?如何openssl pkcs12输出正确的 PKCS#1 私钥?

dav*_*085 5

哇,那个库假设任何以 PEM 结尾的 PEM 都PRIVATE KEY必须是 PKCS1?? 那是非常错误的。有多种 xx PRIVATE KEY格式,其中只有一种是 PKCS1,见下文。

Meta:我认为这是一个骗局,但我找不到它,所以无论如何都要回答。

OpenSSL 支持四种不同的 RSA 私钥 PEM 格式:

  • “传统”或“传统”未加密,这是您想要的 PKCS1 格式(https://tools.ietf.org/html/rfc8017#appendix-A.1.2)与 PEM 类型RSA PRIVATE KEY(不仅仅是PRIVATE KEY

  • 使用 OpenSSL(SSLeay 的)自定义方案在 PEM 级别加密的“传统”或“传统”,该方案使用非常差(且无法修复)的 PBKDF;这具有相同的 PEM 类型,RSA PRIVATE KEY但添加了标头Proc-typeDEK-info

  • PKCS8 标准/通用未加密 ( https://tools.ietf.org/html/rfc5208#section-5 ) 与 PEM 类型PRIVATE KEY;这是一个简单的 ASN.1 包装器,包含算法的标识符(即 RSA 的 OID)加上一个 OCTET STRING 包含算法相关部分,对于 RSA 是 PKCS1

  • PKCS8 标准/通用加密 ( https://tools.ietf.org/html/rfc5208#section-6 ) 与 PEM 类型ENCRYPTED PRIVATE KEY;这使用一种算法加密 ASN.1 中的 PKCS8 数据,该算法通常默认至少为 PBKDF2-SHA1-2048,这是不错的

因为 PKCS8 更灵活,并且是标准的(并且相当常用,例如 Java),并且具有更好的加密,所以它通常是首选;有关键的 PEM_read/write 函数和一些但不是所有其他内容,请参阅联机帮助页的注释部分。

所有读取 PEM 私钥的 OpenSSL 函数都可以读取其中的任何一个(必要时提供正确的密码),但它们写入的内容因函数和范围选项而异。正如您所注意到的pkcs12 (import)(当前)编写 PKCS8,但rsa(始终)编写传统/PKCS1。

您的选择是:

  • 使用rsa(如您一样)或在 1.1.0pkey -traditional中转换为传统

  • pkcs12在 1.0.0 之前的版本中使用,例如 0.9.8,当它编写传统格式时(适用于多种算法,而不仅仅是 RSA)。当然,使用过时且不受支持的版本可能会使您暴露在后续版本中修复的缺陷甚至漏洞中。

  • 从(未加密或解密的)PKCS8 中提取 PKCS1 部分。OpenSSL 使用 DER 作为键,这意味着依赖于算法的 blob,即最后一组中最后一个字段的值,始终是数据的连续最后部分。举个例子:

    $ openssl pkcs12 -in SO47599544.p12 -passin pass:sekrit -nocerts -nodes MAC verified OK Bag Attributes friendlyName: mykey localKeyID: 54 69 6D 65 20 31 35 31 32 31 37 30 38 39 39 33 33 37 Key Attributes: -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnWIGH4p1FHOP2 wtVbxuyvSHpBGd2v+7AyVHG4EJ/9WRoWN4+aGYUOGxzdTyDxk9e7DCTjuY6ciNTh Ph74LADfQQ0B8yGkRtKer3vO1Dg7+6ErCcIGgsfrhZqpuUfod4nSU/LnHXoAAgN5 07LVvohrJMSRTA55jn356EDv31sz/dew1ThzHjYTShGbXh0/baqLxpmm4e8OixAL YV1glHnIdr4h6wrwkJ6TtNexc3xTLwtRf9k7pJPvg+viGh7RTqhbUcGSV+dLelNe 653LbElHtAByz4Ti9e4vUKFuzxqsaWaSYGpzxF/pdthQawV/fTa9CjLGJFFrnVqc KT3TSJ19AgMBAAECggEAOmmubRwxAVrgR9YiW3LIUzbdVbQNqcwU6LyJJVLIRcrA TFkAiy21QAM+xBFG0oxklSncBpFSslkg1a61aLMTatpuC+wuJgWCp1lhwgRZzLY8 v6UcUOF9nzx3jB7cdsyjEwOymfG0ECSjyfaXSfzD6YJgCsedldijKIRlhlVUpIS4 YvdPPGQMXxLr9K8dkQ9o5yTQCrpey1/dNEo7lS17/uMV++dxmka5J+/dRcm2AAIc 7dk6OX9MpGKPFODUyvFjdrWPR2qK25cmVW6hrhJuaPih+1eSd78UkR7OdoHBQEbJ 5MoXSO0eTV4rhid+dX+ynwXA2OvaZbxcr7rlZXjaAQKBgQDybaKW32RHVmjKQDXC xyTdQTMJV7JClBKeXjqJxbgKDhKFTiapn7kNVbJ594n7twuxmTxxoN35gamsbe7q HjEesZZvb2vgLTXnqSvSXcl32CEi554VjlNP6+jZ5JBu/Gw7qObKuWBt+/gkrtCx d09xQllZlD354RyfS3+9jzdEXQKBgQCwttL+Gw2WEm4dPQrfxbasXKQ5hNSE42j+ i0W26xv8o1lKQFip0A4YWidfI+Cvued944ZqCTvnPv6Z+JQzidHFjg6DXWgs1GK/ olsh5xO0hoxAj1azx+11ZHKSb7Kni+jSJsz40U35mWE805HFijxzzQz45unuPZGr d8oqXIcroQKBgQCKuU32w7JgWAPy6DdbVBW2Pl70E6jADHdzBDy/JdMgfdj/Sy84 lVuRU96jiJD+50nbwPIjm4gqBJaRQv8aHVjCVaDd94Zla7mS7O1UnbJxz812ac++ SglGjJpcRTyZJfzRTt9yVg3mIe9nHlnxk3J0PyFd70Rfvv9f8BYS5OcdSQKBgBnb xug0ITrSm5ZftlWkYuS58bYQ/+AqPtTwoFTx9nhzlr9MxyyiK03Y82XypBBSzdMY FjUyALgH+c2iGF2qTy3vaaRDaNkWgxSzt04wuCt0fNV9pBxOpyrEdheDjMsDqCAI WXoXdqeNkDMMaopTfiEb4kgR0i1wiP5kWwrz2zvBAoGBAPELu0IH3jtvo849KeXW O6U1QlxdmWS6h/La1iVRHoE2U3pxAj39IDx4P6GMrgc9VLqRKLTO1Cu9giimO2jH 8iryT5VTlrrINL3M4vXAFjSN/xwVsrLaw/mAQPOKBwNlDwxcCrlxnANnYXdrhk8n uNmZ2VH8flBFRpSbm9aisgMr -----END PRIVATE KEY----- $ openssl pkcs12 -in SO47599544.p12 -passin pass:sekrit -nocerts -nodes \ | sed -e 1,/-BEGIN/d -e /-END/,\$d | openssl asn1parse MAC verified OK 0:d=0 hl=4 l=1214 cons: SEQUENCE 4:d=1 hl=2 l= 1 prim: INTEGER :00 7:d=1 hl=2 l= 13 cons: SEQUENCE 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption 20:d=2 hl=2 l= 0 prim: NULL 22:d=1 hl=4 l=1192 prim: OCTET STRING [HEX DUMP]:308204A4[rest snipped] [that's PKCS8 with algo-dependent part in the OCTET STRING at offset 22] [with a tag-length header length of 4 (the hl=4)]

    您可以使用asn1parse -strparse以下方法提取 PKCS1 部分: $ openssl pkcs12 -in SO47599544.p12 -passin pass:sekrit -nocerts -nodes \ | sed -e 1,/-BEGIN/d -e /-END/,\$d \ | openssl asn1parse -strparse 22 -out SO47599544.raw -noout MAC verified OK $ openssl asn1parse -in SO47599544.raw -inform der 0:d=0 hl=4 l=1188 cons: SEQUENCE 4:d=1 hl=2 l= 1 prim: INTEGER :00 7:d=1 hl=4 l= 257 prim: INTEGER :[snipped] 268:d=1 hl=2 l= 3 prim: INTEGER :010001 273:d=1 hl=4 l= 256 prim: INTEGER :[snipped] [rest snipped -- that's your PKCS1 format but in der so convert to pem] $ (echo -----BEGIN RSA PRIVATE KEY-----; openssl base64 -in SO47599544.raw; \ echo -----END RSA PRIVATE KEY-----) | tee SO47599544.pem -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAp1iBh+KdRRzj9sLVW8bsr0h6QRndr/uwMlRxuBCf/VkaFjeP mhmFDhsc3U8g8ZPXuwwk47mOnIjU4T4e+CwA30ENAfMhpEbSnq97ztQ4O/uhKwnC BoLH64WaqblH6HeJ0lPy5x16AAIDedOy1b6IayTEkUwOeY59+ehA799bM/3XsNU4 cx42E0oRm14dP22qi8aZpuHvDosQC2FdYJR5yHa+IesK8JCek7TXsXN8Uy8LUX/Z O6ST74Pr4hoe0U6oW1HBklfnS3pTXuudy2xJR7QAcs+E4vXuL1Chbs8arGlmkmBq c8Rf6XbYUGsFf302vQoyxiRRa51anCk900idfQIDAQABAoIBADpprm0cMQFa4EfW IltyyFM23VW0DanMFOi8iSVSyEXKwExZAIsttUADPsQRRtKMZJUp3AaRUrJZINWu tWizE2rabgvsLiYFgqdZYcIEWcy2PL+lHFDhfZ88d4we3HbMoxMDspnxtBAko8n2 l0n8w+mCYArHnZXYoyiEZYZVVKSEuGL3TzxkDF8S6/SvHZEPaOck0Aq6Xstf3TRK O5Ute/7jFfvncZpGuSfv3UXJtgACHO3ZOjl/TKRijxTg1MrxY3a1j0dqituXJlVu oa4Sbmj4oftXkne/FJEeznaBwUBGyeTKF0jtHk1eK4YnfnV/sp8FwNjr2mW8XK+6 5WV42gECgYEA8m2ilt9kR1ZoykA1wsck3UEzCVeyQpQSnl46icW4Cg4ShU4mqZ+5 DVWyefeJ+7cLsZk8caDd+YGprG3u6h4xHrGWb29r4C0156kr0l3Jd9ghIueeFY5T T+vo2eSQbvxsO6jmyrlgbfv4JK7QsXdPcUJZWZQ9+eEcn0t/vY83RF0CgYEAsLbS /hsNlhJuHT0K38W2rFykOYTUhONo/otFtusb/KNZSkBYqdAOGFonXyPgr7nnfeOG agk75z7+mfiUM4nRxY4Og11oLNRiv6JbIecTtIaMQI9Ws8ftdWRykm+yp4vo0ibM +NFN+ZlhPNORxYo8c80M+Obp7j2Rq3fKKlyHK6ECgYEAirlN9sOyYFgD8ug3W1QV tj5e9BOowAx3cwQ8vyXTIH3Y/0svOJVbkVPeo4iQ/udJ28DyI5uIKgSWkUL/Gh1Y wlWg3feGZWu5kuztVJ2ycc/NdmnPvkoJRoyaXEU8mSX80U7fclYN5iHvZx5Z8ZNy dD8hXe9EX77/X/AWEuTnHUkCgYAZ28boNCE60puWX7ZVpGLkufG2EP/gKj7U8KBU 8fZ4c5a/TMcsoitN2PNl8qQQUs3TGBY1MgC4B/nNohhdqk8t72mkQ2jZFoMUs7dO MLgrdHzVfaQcTqcqxHYXg4zLA6ggCFl6F3anjZAzDGqKU34hG+JIEdItcIj+ZFsK 89s7wQKBgQDxC7tCB947b6POPSnl1julNUJcXZlkuofy2tYlUR6BNlN6cQI9/SA8 eD+hjK4HPVS6kSi0ztQrvYIopjtox/Iq8k+VU5a6yDS9zOL1wBY0jf8cFbKy2sP5 gEDzigcDZQ8MXAq5cZwDZ2F3a4ZPJ7jZmdlR/H5QRUaUm5vWorIDKw== -----END RSA PRIVATE KEY-----

    或者,您可以将 PKCS8 主体转换为二进制文件,然后丢弃前 22+4=26 个字节(因为上面第一个 asn1parse 的标头 len hl=4): $ openssl pkcs12 -in SO47599544.p12 -passin pass:sekrit -nocerts -nodes \ | sed -e 1,/-BEGIN/d -e /-END/,\$d \ | openssl base64 -d | dd bs=1 skip=26 >SO47599544.raw MAC verified OK 1192+0 records in 1192+0 records out 1192 bytes (1.2 kB) copied, 0.00892462 s, 134 kB/s [then convert to PEM with echo BEGIN;base64(encode);echo END as above]

PS:如果只读取 PKCS12 一次很重要,例如为了避免重新输入密码,您可以使用 awk 之类的

openssl pkcs12 -in file.p12 | \
awk '/BEGIN PRIVATE/,/END PRIVATE/{t=t $0 RS;next}1; \
  END{process t as the whole PRIVATE KEY PEM}'
Run Code Online (Sandbox Code Playgroud)

或者

openssl pkcs12 -in file.p12 | \
awk '/BEGIN PRIVATE/{f=1;next}/END PRIVATE/{f=0;next}f{t=t $0 RS;next}1; \
    END{process t as just the base64 body}'
Run Code Online (Sandbox Code Playgroud)