我有一个Certificates.p12
文件,我希望转换为包含PKCS#1格式的未加密私钥的certificates.pem
。我以前能够通过运行:
openssl pkcs12 -in Certificates.p12 -out certificates.pem -nodes -clcerts
生成的certificates.pem
文件具有PRIVATE KEY
PEM块,如预期的那样。但是,the library I'm using无法理解此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
}
我的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...
上述格式是什么? The documentation for openssl pkcs12
只是模糊地说它的输出是“以PEM格式编写的”。我需要更强有力的保证私钥PEM块是PKCS#1格式。
奇怪的是openssl rsa
理解“坏”私钥的奇怪格式,并且可以将其转换为正确的PKCS#1结构:
openssl rsa -in badprivatekey.pem -out goodprivatekey.pem
虽然openssl rsa
了解输入文件,但该工具似乎无法告诉我为什么,即输入文件的格式是什么。
openssl pkcs12
的输出格式是什么?具体来说,它的私钥块的格式是什么?如何让openssl pkcs12
输出正确的PKCS#1私钥?
答案 0 :(得分:2)
哇,那个库假设任何以PRIVATE KEY
结尾的PEM必须是PKCS1 ??这是非常错误的。 有几种 xx PRIVATE KEY
格式,其中只有一个是PKCS1,见下文。
OpenSSL支持RSA私钥的四种不同PEM格式:
'传统'或'传统'未加密,这是您想要的PKC1格式(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-type
和DEK-info
PKC8标准/通用未加密(https://tools.ietf.org/html/rfc5208#section-5),PEM类型为PRIVATE KEY
;这是一个简单的ASN.1包装器,包含算法的标识符(即RSA的OID)加上包含算法相关部分的OCTET STRING,RSA是PKCS1
PKC8标准/通用加密(https://tools.ietf.org/html/rfc5208#section-6),PEM类型为ENCRYPTED PRIVATE KEY
;这会加密ASN.1中的PKCS8数据,其算法通常至少默认为PBKDF2-SHA1-2048
因为PKCS8更灵活,并且是标准的(并且相当常用,例如Java),并且具有更好的加密,所以通常是优选的;请参阅the manpage for the PEM_read/write functions for keys and some but not all other things的注释部分。
读取PEM私钥的所有OpenSSL函数都可以读取其中任何一个(必要时给定正确的密码)但是它们编写的函数取决于函数和范围选项。正如您注意到pkcs12 (import)
(当前)写入PKCS8,但rsa
(始终)写入传统/ PKCS1。
您的选择是:
使用rsa
(正如您所做)或在1.1.0 pkey -traditional
中转换为传统
在1.0.0之前的版本中使用pkcs12
,如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}'
或
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}'