How can I transform between the two styles of public key format, one "BEGIN RSA PUBLIC KEY", the other is "BEGIN PUBLIC KEY"
Asked Answered
B

5

168

How can I transform between the two styles of public key format, one format is:

-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----

the other format is:

-----BEGIN RSA PUBLIC KEY-----
...
-----END RSA PUBLIC KEY-----

for example I generated id_rsa/id_rsa.pub pair using ssh-keygen command, I calculated the public key from id_rsa using:

openssl rsa -in id_rsa -pubout -out pub2 

then again I calculated the public key from id_rsa.pub using :

ssh-keygen -f id_rsa.pub -e -m pem > pub1

the content is pub1 is :

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----

and the content of pub2 is :

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB
-----END PUBLIC KEY-----

According to my understanding, pub1 and pub2 contain the same public key information, but they are in different format, I wonder how can I transform between the two format? Can anyone show me some concise introduction on the tow formats?

Baylor answered 4/8, 2013 at 3:17 Comment(1)
Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See What topics can I ask about here in the Help Center. Perhaps Super User or Unix & Linux Stack Exchange would be a better place to ask.Phenol
C
14

Using phpseclib, a pure PHP RSA implementation...

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey('-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB
-----END PUBLIC KEY-----');
$rsa->setPublicKey();

echo $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW);

The base64-encoded stuff appears to match even though the header says BEGIN PUBLIC KEY and not BEGIN RSA PUBLIC KEY. So maybe just use str_replace to fix that and you should be good to go!

Christal answered 5/8, 2013 at 15:33 Comment(1)
I've got no idea why that function reads that PEM, as it clearly describes a SubjectPublicKeyInfo that is described in X.509 and not in PKCS#1 whatsoever, and the binary contents are consistent with that. Yes, that structure contains a PKCS#1 encoded RSA private key, but if this function works correctly then it is permissive to a fault.Hygiene
T
471

I wanted to help explain what's going on here.

An RSA "Public Key" consists of two numbers:

  • the modulus (e.g. a 2,048 bit number)
  • the exponent (usually 65,537)

Using your RSA public key as an example, the two numbers are:

  • Modulus: 297,056,429,939,040,947,991,047,334,197,581,225,628,107,021,573,849,359,042,679,698,093,131,908,015,712,695,688,944,173,317,630,555,849,768,647,118,986,535,684,992,447,654,339,728,777,985,990,170,679,511,111,819,558,063,246,667,855,023,730,127,805,401,069,042,322,764,200,545,883,378,826,983,730,553,730,138,478,384,327,116,513,143,842,816,383,440,639,376,515,039,682,874,046,227,217,032,079,079,790,098,143,158,087,443,017,552,531,393,264,852,461,292,775,129,262,080,851,633,535,934,010,704,122,673,027,067,442,627,059,982,393,297,716,922,243,940,155,855,127,430,302,323,883,824,137,412,883,916,794,359,982,603,439,112,095,116,831,297,809,626,059,569,444,750,808,699,678,211,904,501,083,183,234,323,797,142,810,155,862,553,705,570,600,021,649,944,369,726,123,996,534,870,137,000,784,980,673,984,909,570,977,377,882,585,701
  • Exponent: 65,537

The question then becomes how do we want to store these numbers in a computer. First we convert both to hexadecimal:

  • Modulus: EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
  • Exponent: 010001

RSA invented the first format

RSA invented a format first:

RSAPublicKey ::= SEQUENCE {
   modulus           INTEGER,  -- n
   publicExponent    INTEGER   -- e
}

They chose to use the DER flavor of the ASN.1 binary encoding standard to represent the two numbers [1]:

SEQUENCE (2 elements)
   INTEGER (2048 bit): EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
   INTEGER (24 bit): 010001

The final binary encoding in ASN.1 is:

30 82 01 0A      ;sequence (0x10A bytes long)
   02 82 01 01   ;integer (0x101 bytes long)
      00 EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
   02 03         ;integer (3 bytes long)
      010001

If you then run all those bytes together and Base64 encode it, you get:

MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB

RSA labs then said add a header and trailer:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----

Five hyphens, and the words BEGIN RSA PUBLIC KEY. That is your PEM DER ASN.1 PKCS#1 RSA Public key

  • PEM: synonym for base64
  • DER: a flavor of ASN.1 encoding
  • ASN.1: the binary encoding scheme used
  • PKCS#1: The formal specification that dictates representing a public key as structure that consists of modulus followed by an exponent
  • RSA public key: the public key algorithm being used

Not just RSA

After that, other forms of public key cryptography came along:

  • Diffie-Hellman
  • Ellicptic Curve

When it came time to create a standard for how to represent the parameters of those encryption algorithms, people adopted a lot of the same ideas that RSA originally defined:

  • use ASN.1 binary encoding
  • base64 it
  • wrap it with five hyphens
  • and the words BEGIN PUBLIC KEY

But rather than using:

  • -----BEGIN RSA PUBLIC KEY-----
  • -----BEGIN DH PUBLIC KEY-----
  • -----BEGIN EC PUBLIC KEY-----

They decided instead to include the Object Identifier (OID) of what is to follow. In the case of an RSA public key, that is:

  • RSA PKCS#1: 1.2.840.113549.1.1.1

So for RSA public key it was essentially:

public struct RSAPublicKey {
   INTEGER modulus,
   INTEGER publicExponent 
}

Now they created SubjectPublicKeyInfo which is basically:

public struct SubjectPublicKeyInfo {
   AlgorithmIdentifier algorithm,
   RSAPublicKey subjectPublicKey
}

In actual DER ASN.1 definition is:

SubjectPublicKeyInfo  ::=  SEQUENCE  {
    algorithm  ::=  SEQUENCE  {
        algorithm               OBJECT IDENTIFIER, -- 1.2.840.113549.1.1.1 rsaEncryption (PKCS#1 1)
        parameters              ANY DEFINED BY algorithm OPTIONAL  },
    subjectPublicKey     BIT STRING {
        RSAPublicKey ::= SEQUENCE {
            modulus            INTEGER,    -- n
            publicExponent     INTEGER     -- e
        }
}

That gives you an ASN.1 of:

SEQUENCE (2 elements)
   SEQUENCE (2 elements)
      OBJECT IDENTIFIER 1.2.840.113549.1.1.1
      NULL
   BIT STRING (1 element)
      SEQUENCE (2 elements)
         INTEGER (2048 bit): EB506399F5C612F5A67A09C1192B92FAB53DB28520D859CE0EF6B7D83D40AA1C1DCE2C0720D15A0F531595CAD81BA5D129F91CC6769719F1435872C4BCD0521150A0263B470066489B918BFCA03CE8A0E9FC2C0314C4B096EA30717C03C28CA29E678E63D78ACA1E9A63BDB1261EE7A0B041AB53746D68B57B68BEF37B71382838C95DA8557841A3CA58109F0B4F77A5E929B1A25DC2D6814C55DC0F81CD2F4E5DB95EE70C706FC02C4FCA358EA9A82D8043A47611195580F89458E3DAB5592DEFE06CDE1E516A6C61ED78C13977AE9660A9192CA75CD72967FD3AFAFA1F1A2FF6325A5064D847028F1E6B2329E8572F36E708A549DDA355FC74A32FDD8DBA65
         INTEGER (24 bit): 010001

The final binary encoding in ASN.1 is:

30 82 01 22          ;SEQUENCE (0x122 bytes = 290 bytes)
|  30 0D             ;SEQUENCE (0x0d bytes = 13 bytes) 
|  |  06 09          ;OBJECT IDENTIFIER (0x09 = 9 bytes)
|  |  2A 86 48 86   
|  |  F7 0D 01 01 01 ;hex encoding of 1.2.840.113549.1.1
|  |  05 00          ;NULL (0 bytes)
|  03 82 01 0F 00    ;BIT STRING  (0x10f = 271 bytes)
|  |  30 82 01 0A       ;SEQUENCE (0x10a = 266 bytes)
|  |  |  02 82 01 01    ;INTEGER  (0x101 = 257 bytes)
|  |  |  |  00             ;leading zero of INTEGER
|  |  |  |  EB 50 63 99 F5 C6 12 F5  A6 7A 09 C1 19 2B 92 FA 
|  |  |  |  B5 3D B2 85 20 D8 59 CE  0E F6 B7 D8 3D 40 AA 1C 
|  |  |  |  1D CE 2C 07 20 D1 5A 0F  53 15 95 CA D8 1B A5 D1 
|  |  |  |  29 F9 1C C6 76 97 19 F1  43 58 72 C4 BC D0 52 11 
|  |  |  |  50 A0 26 3B 47 00 66 48  9B 91 8B FC A0 3C E8 A0
|  |  |  |  E9 FC 2C 03 14 C4 B0 96  EA 30 71 7C 03 C2 8C A2  
|  |  |  |  9E 67 8E 63 D7 8A CA 1E  9A 63 BD B1 26 1E E7 A0  
|  |  |  |  B0 41 AB 53 74 6D 68 B5  7B 68 BE F3 7B 71 38 28
|  |  |  |  38 C9 5D A8 55 78 41 A3  CA 58 10 9F 0B 4F 77 A5
|  |  |  |  E9 29 B1 A2 5D C2 D6 81  4C 55 DC 0F 81 CD 2F 4E 
|  |  |  |  5D B9 5E E7 0C 70 6F C0  2C 4F CA 35 8E A9 A8 2D 
|  |  |  |  80 43 A4 76 11 19 55 80  F8 94 58 E3 DA B5 59 2D
|  |  |  |  EF E0 6C DE 1E 51 6A 6C  61 ED 78 C1 39 77 AE 96 
|  |  |  |  60 A9 19 2C A7 5C D7 29  67 FD 3A FA FA 1F 1A 2F 
|  |  |  |  F6 32 5A 50 64 D8 47 02  8F 1E 6B 23 29 E8 57 2F 
|  |  |  |  36 E7 08 A5 49 DD A3 55  FC 74 A3 2F DD 8D BA 65
|  |  |  02 03          ;INTEGER (03 = 3 bytes)
|  |  |  |  010001
   

And as before, you take all those bytes, Base64 encode them, you end up with your second example:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB   

Add the slightly different header and trailer, and you get:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB   
-----END PUBLIC KEY-----

And this is your X.509 SubjectPublicKeyInfo/OpenSSL PEM public key [2].

Do it right, or hack it

Now that you know that the encoding isn't magic, you can write all the pieces needed to parse out the RSA modulus and exponent. Or you can recognize that the first 24 bytes are just added new stuff on top of the original PKCS#1 standard

30 82 01 22          ;SEQUENCE (0x122 bytes = 290 bytes)
|  30 0D             ;SEQUENCE (0x0d bytes = 13 bytes) 
|  |  06 09          ;OBJECT IDENTIFIER (0x09 = 9 bytes)
|  |  2A 86 48 86   
|  |  F7 0D 01 01 01 ;hex encoding of 1.2.840.113549.1.1
|  |  05 00          ;NULL (0 bytes)
|  03 82 01 0F 00    ;BIT STRING  (0x10f = 271 bytes)
|  |  ...

Those first 24-bytes are "new" stuff added:

30 82 01 22 30 0D 06 09 2A 86 48 86 F7 0D 01 01 01 05 00 03 82 01 0F 00

And due to an extraordinary coincidence of fortune and good luck:

24 bytes happens to correspond exactly to 32 base64 encoded characters

Because in Base64: 3-bytes becomes four characters:

30 82 01  22 30 0D  06 09 2A  86 48 86  F7 0D 01  01 01 05  00 03 82  01 0F 00
\______/  \______/  \______/  \______/  \______/  \______/  \______/  \______/
    |         |         |         |         |         |         |         |
  MIIB      IjAN      Bgkq      hkiG      9w0B      AQEF      AAOC      AQ8A

That means if you take your second X.509 public key, the first 32 characters corresponds only to newly added stuff:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END PUBLIC KEY-----

If you remove the first 32 characters, and change it to BEGIN RSA PUBLIC KEY:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA61BjmfXGEvWmegnBGSuS+rU9soUg2FnODva32D1AqhwdziwHINFa
D1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBSEVCgJjtHAGZIm5GL/KA86KDp/CwDFMSw
luowcXwDwoyinmeOY9eKyh6aY72xJh7noLBBq1N0bWi1e2i+83txOCg4yV2oVXhB
o8pYEJ8LT3el6Smxol3C1oFMVdwPgc0vTl25XucMcG/ALE/KNY6pqC2AQ6R2ERlV
gPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeulmCpGSynXNcpZ/06+vofGi/2MlpQZNhH
Ao8eayMp6FcvNucIpUndo1X8dKMv3Y26ZQIDAQAB
-----END RSA PUBLIC KEY-----

You have exactly what you wanted - the older RSA PUBLIC KEY format.

Tangleberry answered 17/4, 2015 at 18:49 Comment(14)
What tool did you use to print out the hex structure of the files?Strychnine
@Strychnine I used the excellent, excellent, ASN.1 JavaScript decoder. That and TRANSLATOR, BINARY are two excellent tools to have in your toolbox of tricks.Tangleberry
Any chance you could explain the certificate process as well?Mungovan
The start of the modulus has an extra "1" character. It should begin like this... 297,056,429,939,040,947,991,047,334,197,581,225,628,107,02,573... but NOT this... 297,056,429,939,040,947,991,047,334,197,581,225,628,107,021,573... hope that helps someones from getting angry at their hex conversion.Discomfit
jsFiddle version of ASN.1 js. It's also on githubTangleberry
INTEGER (12 bit): 010001: shouldn't it be 24? 010001 is 24 bits, 65537 doesn't fit 12 bits.Smoothbore
@GeorgeSovetov You're right; i accidentally did 3 bytes * 4 bits per nibble = 12 bitsTangleberry
Sorry for the nitpick, but PEM isn't a synonym for base64. It has a complicated and not very well defined history, but PEM is in itself a data format. Generally now it is accepted to be the header (hyphens with BEGIN XYZ), footer, and optional number of headers under the header and then the base64 encoded data.Martita
Another tool is ASN.1 Editor, a native Windows application.Harassed
Amazing!, can I do the same with BEGIN RSA PRIVATE KEY? just remove the first 32 base64 bytes?Kurt
whoa. I'm so much grateful for this first so clear & detailed explanation I found after dozen of hours spent in research on the internet. Many, many, many thanks !Mitchmitchael
@nmesarco Oh i have no idea; i doubt it. You have to repeat the same exercise, this time with RSAPrivateKeyTangleberry
Oh, that is amaizing explanation! Thank you! One question though: did not you mix terms when you put examples of DER ASN.1 and ASN.1, if you search by "In actual DER ASN.1 definition is:" and "That gives you an ASN.1 of:" - is not they vice versa?Preuss
ASN.1 is a standard binary format. But ASN.1 has different flavors (what is allowed, what isn't), and that is the "Distinguished Encoding Rules" (DER) flavor of ASN.1. Probably like "all C++ code is valid C code, but not vice versa"Tangleberry
H
75

I found this website to be a good technical explanation of the different formats: https://polarssl.org/kb/cryptography/asn1-key-structures-in-der-and-pem

"BEGIN RSA PUBLIC KEY" is PKCS#1, which can only contain RSA keys.

"BEGIN PUBLIC KEY" is a SPKI (Subject Public Key Info) key (part of X.509), which can contain a variety of formats.

If you just want to convert them with the command-line, "openssl rsa" is good for this.

To convert from SPKI to PKCS#1:

openssl rsa -pubin -in <filename> -RSAPublicKey_out

To convert from PKCS#1 to SPKI:

openssl rsa -RSAPublicKey_in -in <filename> -pubout
Headwaiter answered 13/1, 2015 at 20:15 Comment(20)
I can't find anything about public key in PKCS#8 (RFC 5208).Harassed
Doesn't work on MacOS: unknown option -RSAPublicKey_inLustick
@FranklinYu: yes PKCS8 is privatekey only and polarssl is wrong on that point. The generic form of publickey is defined by X.509 and specifically the type SubjectPublicKeyInfo, as correctly stated in Ian Boyd's (long!) answer; this info is (more conveniently) duplicated in RFC5280 plus other RFCs depending on algorithm, with 'basic' RSA in RFC3279.Din
@nakajuice: You need OpenSSL version 1.0.0 (released 2010) or higher. AIUI Apple stopped supporting OpenSSL on OS(X), so you may need a version from brew or similar.Din
This got me on the right direction for converting from OpenSSH format. I ended up using ssh-keygen like this: ssh-keygen -i -f ~/.ssh/id_rsa.pub -e -m PKCS8 > ~/.ssh/id_rsa.pub.pemMnemosyne
This answer is misleading, as others have pointed out - PKCS#8 only sets a standard for private keys.Barrack
@Din There seems to be much confusion about this on the internet, but multiple sources show successful conversion from the X509 SubjectPublicKeyInfo (SPKI) format into PKCS 1. It looks like PKCS 1 was indeed originally designed for private keys but later utilized also for public keys. See here for example: #53924826 . It is a similar story with PKCS 8 which was designed for Private Keys but later could take OIDs for Public Keys. Documentation needs to be updated to clarify the confusing situation there.Tripterous
@Din "Because RSA is not used exclusively inside X509 and SSL/TLS, a more generic key format is available in the form of PKCS#8, that identifies the type of public key and contains the relevant data." tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pemTripterous
@Barrack PKCS#8 according to the standard, yes, was for private keys but it now used for a variety of things including public keys. Not to say it is the primary format for Public Keys but that a successful conversion to PKCS 8 for public keys is possible. There are OIDs for public keys in PKCS 8 format I believe.Tripterous
@SeligkeitIstInGott: mbed(tls) is just the new name of polarssl, which as I said before was wrong then and remains wrong now. Look at PKCS8 (RFC5208) and that (publickey) structure is not present anywhere; look at X.509/PKIX (currently RFC5280) and it is in section 4.1. Also look at RFC7468 sections 10 11 13. You can convert (or extract) a publickey from PKCS8 because (for all schemes now used) you can convert a publickey from any privatekey and PKCS8 is a privatekey, but there is no PKCS8 anywhere that is not a privatekey, period, full stop.Din
@Din Which standard generates the base64 ASCII readable "BEGIN RSA PUBLIC KEY" heading? I do not believe that fits for the the X509 certificate format which rather says BEGIN CERTIFICATE. The SPKI spec in 4.1.2.7 in RFC5280 doesn't specify a standalone PUBLIC KEY format like that so far as I can tell. So it must be some form of PKCS format I'm thinking. Both PKCS 1 and 8 say they are for private keys. Yet both BEGIN RSA PUBLIC KEY and BEGIN PUBLIC KEY must come from some PKCS format. So which RFC specifies those names and how are they implemented in terms of format?Tripterous
The PEM format BEGIN/END PUBLIC KEY (without RSA) is defined in RFC7468 and uses (as I said) SPKI from X.509/PKIX (RFC5280, previously 3280 and 2459); this is an Internet (and ITU) standard not a PKCS. The widely used but AFAIK not official practice of BEGIN/END CERTIFICATE or X.509 CERTIFICATE is also formalized by 7468. There are no standards for OpenSSL's PEM-format algorithm-specific BEGIN/END {RSA,DSA,EC} PUBLIC KEY (and PRIVATE also) although for the underlying DER the RSA cases use PKCS1; DSA uses a format Eric invented and EC uses SEC1 from SECG, which builds on X9.62.) ...Din
... PKCS1 is mostly about the RSA algorithm and incidentally defines both public and private keys FOR RSA ONLY. PKCS8 is entirely about privatekeys for all algorithms, and similarly X.509/PKIX is mostly about publickeys for all algorithms.Din
Ah, your comment about RFC7468 is clearer now. I'll take a look. Thanks.Tripterous
@Din Going back to my very first comment above, you have failed to address RFC8017. People should stop saying PKCS#1 is only for private keys. That is no longer the case in v2.2 of PKCS1. Sec 1: "The recommendations are intended to be compatible with the standards IEEE 1363... and ANSI X9.44. This document supersedes PKCS #1 version 2.1 [RFC3447] but includes compatible techniques." Sec 3: "Two key types are employed in the primitives and schemes defined in this document: RSA public key and RSA private key. Together, an RSA public key and an RSA private key form an RSA key pair."Tripterous
See also: hackernoon.com/public-key-cryptography-rsa-keys-izda3ylvTripterous
You are right about BEGIN PUBLIC KEY being from the X509 Cert Spec, though I still don't understand that, since that spec if for an entire certificate. It's like they ripped out and isolated just one portion of it for certain implementations which have nothing to do with certificates (like SSH or something). PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY) || X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)Tripterous
@SeligkeitIstInGott: this is not a change, PKCS1 has always defined both public and private keys but always for RSA only. PKCS8 has always been only for privatekeys but all algorithms, and X.509/PKIX/SPKI has always been only for publickeys but all algorithms, and for RSA only the algorithm-specific part of either PKCS8 or X.509/PKIX/SPKI is the formats from PKCS1. I never said PKCS1 is privatekey only and I didn't see anyone say so either, I said PKCS8 is privatekey only, and that was and is correct. ...Din
... Your own reference 'hackernoon' correctly says that for RSA (not other algorithms) PKCS8 uses PrivateKey (not PublicKey) from PKCS1, although it seems to suggest that PKCS1 is an earlier 'version' of PKCS8 which is completely wrong. Also it is out of date about OpenSSH; since 2018 OpenSSH no longer uses for privatekey the OpenSSL formats ('traditional'=PKCS1+adhoc+SEC1 or PKCS8) but instead its own 'new' format. This is covered by numerous existing Qs on several stacks which you should refer to if interested, since it is not relevant to this Q.Din
Looks like there are multiple formats of public key, and PKIX encoding takes care of that. But why multiple formats of private key is not considered?Masbate
C
14

Using phpseclib, a pure PHP RSA implementation...

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey('-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA61BjmfXGEvWmegnBGSuS
+rU9soUg2FnODva32D1AqhwdziwHINFaD1MVlcrYG6XRKfkcxnaXGfFDWHLEvNBS
EVCgJjtHAGZIm5GL/KA86KDp/CwDFMSwluowcXwDwoyinmeOY9eKyh6aY72xJh7n
oLBBq1N0bWi1e2i+83txOCg4yV2oVXhBo8pYEJ8LT3el6Smxol3C1oFMVdwPgc0v
Tl25XucMcG/ALE/KNY6pqC2AQ6R2ERlVgPiUWOPatVkt7+Bs3h5Ramxh7XjBOXeu
lmCpGSynXNcpZ/06+vofGi/2MlpQZNhHAo8eayMp6FcvNucIpUndo1X8dKMv3Y26
ZQIDAQAB
-----END PUBLIC KEY-----');
$rsa->setPublicKey();

echo $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW);

The base64-encoded stuff appears to match even though the header says BEGIN PUBLIC KEY and not BEGIN RSA PUBLIC KEY. So maybe just use str_replace to fix that and you should be good to go!

Christal answered 5/8, 2013 at 15:33 Comment(1)
I've got no idea why that function reads that PEM, as it clearly describes a SubjectPublicKeyInfo that is described in X.509 and not in PKCS#1 whatsoever, and the binary contents are consistent with that. Yes, that structure contains a PKCS#1 encoded RSA private key, but if this function works correctly then it is permissive to a fault.Hygiene
B
10

The only difference between your pub1 and pub2, besides the header/footer, is this additional string in pub2: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A. If you remove that, the Base 64 is identical to that in pub1.

The extra string corresponds to the algorithm identifier according to this Answer.

Burne answered 6/8, 2013 at 5:54 Comment(0)
F
1

To convert to a third style of public key (OpenSSH public key format):

Input file: C:\mc_pubkey.txt: (in SSH2 Public Key format)

---- BEGIN SSH2 PUBLIC KEY ----
Comment: "imported-openssh-key"
AAAAB3NzaC1yc2EAAAADAQABAAABAQCb4oUsXZ51L9DmH3UqSnwOAUr9w6AOZa8b
YH8qsJAhypAjH9YvQteVXXQEY0ybaVE1cmpIKEyC2jmC/jOJ4b7bivlo2hgnLOrj
3FPkDWO2yNNio9RnPXREBx7+iIi9pcaozUmiPMoPxaEyfpGQAvfdFzt+n6+o/hoO
72Zv5RSo0rGr76sLUjx9Mi5TnuI2rT4s9FGMZ9xkgUu3z11UcDhSXqkLEgEEDKBq
bft3VuiQm6Blggzk9tk5L1LAdKM7udhby9dOwXCYPnCKTymYTqi/2FTwKZDj5TJH
V8r6c2Sz/qEdgMkw7RD3ice9m9GYHi2Burgyvjw+Sla+yu0n9sBh
---- END SSH2 PUBLIC KEY ----

To convert this to OpenSSH public key format, use the ssh-keygen.exe utility. It's probably already on your computer, since it's packaged with Git. So in Powershell:

> cd C:\Program Files\Git\usr\bin

> .\ssh-keygen.exe -i -f "C:\mc_pubkey.txt"
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCb4oUsXZ51L9DmH3UqSnwOAUr9w6AOZa8bYH8qsJAhypAjH9YvQteVXXQEY0ybaVE1cmpIKEyC2jmC/jOJ4b7bivlo2hgnLOrj3FPkDWO2yNNio9RnPXREBx7+iIi9pcaozUmiPMoPxaEyfpGQAvfdFzt+n6+o/hoO72Zv5RSo0rGr76sLUjx9Mi5TnuI2rT4s9FGMZ9xkgUu3z11UcDhSXqkLEgEEDKBqbft3VuiQm6Blggzk9tk5L1LAdKM7udhby9dOwXCYPnCKTymYTqi/2FTwKZDj5TJHV8r6c2Sz/qEdgMkw7RD3ice9m9GYHi2Burgyvjw+Sla+yu0n9sBh

This is the output you want! Copy and paste and save into a file and you are done.

Fencing answered 12/1, 2023 at 5:55 Comment(1)
a good point @MichaelCurrie but let me emphasize many differences - details : SSH2 in header and footer which are delimited by 4 hyphens (not 5) and extra spaces - notice the "data" begins by AAAA not MIIB I'm sure experts will explain why - my POV : the most important: these keys are for different usage (mostly SSH/Rsync/SCP/SFTP connexions) than SSL/TLS... maybe i'm wrong - what you call "conversion" i would call that "output style/format" and notice that the data are stored in .ssh/authorized_keys with this "one line" formatUpon

© 2022 - 2024 — McMap. All rights reserved.