197

I have a certificate in der format, from it with this command I generate a public key:

openssl x509 -inform der -in ejbcacert.cer -noout -pubkey > pub1key.pub

Which results in this:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7vbqajDw4o6gJy8UtmIbkcpnk
O3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2
eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1
QWPdspTBKcxeFbccDwIDAQAB
-----END PUBLIC KEY-----

How can I obtain a public key like this? Either from certificate or from this public key?

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7vbqajDw4o6gJy8UtmIbkcpnkO3Kwc4qsEnSZp/TR+fQi62F79RHWmwKOtFmwteURgLbj7D/WGuNLGOfa/2vse3G2eHnHl5CB8ruRX9fBl/KgwCVr2JaEuUm66bBQeP5XeBotdR4cvX38uPYivCDdPjJ1QWPdspTBKcxeFbccDw==

This was obtained with this command:

ssh-keygen -y -f private_key1.pem > public_key1.pub
Caleb
  • 5,084
  • 1
  • 46
  • 65
Adrya
  • 3,427
  • 8
  • 31
  • 29
  • 17
    The way you posted in the "This was obtained with this command" worked for me better than any of the answers below. – Yoav Shapira Nov 20 '12 at 21:21
  • Ditto @YoavShapira. Extra step if coming from an openssl PKCS12 private key: "openssl pkcs12 -in private_key1.p12 -out private_key1.pem" – SwiftArchitect Jan 01 '14 at 22:15
  • 11
    @YoavShipra. Yes but the whole question is that he wants to convert using only the public key. Maybe he doesn't have the private key and he only has the public key and wants to convert from PEM format to ssh-rsa format. – deltamind106 Mar 23 '15 at 14:44
  • 13
    Given a .pem from AWS, the command you give above `ssh-keygen -y -f private_key1.pem > public_key1.pub` worked great for me. – Kzqai Apr 21 '15 at 20:03
  • 4
    All wrong answers. This is the correct one: `ssh-keygen -i -m PKCS8 -f public-key.pem` – Boeboe Mar 28 '18 at 14:55
  • 4
    **Beauty is in the eye of the beholder**. We need to note that a **pem** key could container either public key or private key, or both; encrypted or maybe not; plus with various format. Also the meaning of option `-m` is different for `-i`/`-e`. So my friends, **please make sure you know what you want and what you have**. :-) – ryenus Sep 04 '19 at 03:19
  • 1
    The command: `ssh-keygen -y -f private_key1.pem >public_key1.pub` works well on my Mac. It will read a PEM encoded OpenSSH Private (not Public) key file and convert it to the format necessary for an `id_rsa.pub` or `~/.ssh/authorized_keys` file. I confirmed this by generating an SSH key in Azure, downloading the private key, converting it with the above command, and then comparing the output to the public key displayed on Azure. They matched. – Speeddymon Jan 27 '22 at 00:15
  • @Speeddymon, can `ssh-keygen` generate PEM encoded SSH private keys? Instead of downloading the SSH PEM private key, why didn't you convert the private key to a public key in your Linux VM on Azure and download the public key? – Derek Mahar Jan 18 '23 at 13:58
  • In regards to your first question @DerekMahar I'm not sure but I would assume it probably can. In regards to the second question, I don't have a VM in Azure, I used the Azure portal. This also was only done for testing purpose so the key file was never used anywhere and has been deleted. – Speeddymon Jan 19 '23 at 14:06

12 Answers12

178

No need to compile stuff. You can do the same with ssh-keygen:

ssh-keygen -f pub1key.pub -i

will read the public key in openssl format from pub1key.pub and output it in OpenSSH format.

Note: In some cases you will need to specify the input format:

ssh-keygen -f pub1key.pub -i -m PKCS8

From the ssh-keygen docs (From man ssh-keygen):

-m key_format Specify a key format for the -i (import) or -e (export) conversion options. The supported key formats are: “RFC4716” (RFC 4716/SSH2 public or private key), “PKCS8” (PEM PKCS8 public key) or “PEM” (PEM public key). The default conversion format is “RFC4716”.

Covich
  • 2,544
  • 4
  • 26
  • 37
Victor Mataré
  • 2,446
  • 2
  • 16
  • 20
72

No need for scripts or other 'tricks': openssl and ssh-keygen are enough. I'm assuming no password for the keys (which is bad).

Generate an RSA pair

All the following methods give an RSA key pair in the same format

  1. With openssl (man genrsa)

    openssl genrsa -out dummy-genrsa.pem 2048
    

    In OpenSSL v1.0.1 genrsa is superseded by genpkey so this is the new way to do it (man genpkey):

    openssl genpkey -algorithm RSA -out dummy-genpkey.pem -pkeyopt rsa_keygen_bits:2048
    
  2. With ssh-keygen

    ssh-keygen -t rsa -b 2048 -f dummy-ssh-keygen.pem -N '' -C "Test Key"
    

Converting DER to PEM

If you have an RSA key pair in DER format, you may want to convert it to PEM to allow the format conversion below:

Generation:

openssl genpkey -algorithm RSA -out genpkey-dummy.cer -outform DER -pkeyopt rsa_keygen_bits:2048

Conversion:

openssl rsa -inform DER -outform PEM -in genpkey-dummy.cer -out dummy-der2pem.pem

Extract the public key from the PEM formatted RSA pair

  1. in PEM format:

    openssl rsa -in dummy-xxx.pem -pubout
    
  2. in OpenSSH v2 format see:

    ssh-keygen -y -f dummy-xxx.pem
    

Notes

OS and software version:

[user@test1 ~]# cat /etc/redhat-release ; uname -a ; openssl version
CentOS release 6.5 (Final)
Linux test1.example.local 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 03:15:09 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
OpenSSL 1.0.1e-fips 11 Feb 2013

References:

Thomas BDX
  • 2,632
  • 2
  • 27
  • 31
  • // , Does this actually generate a key in the `ssh-rsa` format? Good reference, btw. – Nathan Basanese Nov 17 '16 at 05:05
  • @NathanBasanese, yes (see "Extract the public key from the PEM formatted RSA pair", point 2): once one has the certificate in pem format: `ssh-keygen -y -f dummy-xxx.pem` produces an `ssh-rsa AAAA[...]==` fit for ssh's `authorized_keys` file. – Thomas BDX Feb 09 '17 at 14:54
  • Good informative piece... but I don't think it really answers the question as well as the above much shorter piece. – Ogre Codes Aug 23 '17 at 21:48
  • By converting it shows: unable to load Private Key 10828:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:.\crypto\a sn1\tasn_dec.c:1200: – Max Kleiner Sep 29 '21 at 12:08
  • Write out: OpenSSL> rsa -in genpkeySSH.pem -pubout -out genpubkeySSH.pem – Max Kleiner Sep 29 '21 at 12:27
30

To answer my own question, after posting on openssl mailing list got this:

Here is C code to convert from an OpenSSL public key to an OpenSSH public key. You can grab the code from this link and compile it yourself:

static unsigned char pSshHeader[11] = { 0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2D, 0x72, 0x73, 0x61};

static int SshEncodeBuffer(unsigned char *pEncoding, int bufferLen, unsigned char* pBuffer)
{
   int adjustedLen = bufferLen, index;
   if (*pBuffer & 0x80)
   {
      adjustedLen++;
      pEncoding[4] = 0;
      index = 5;
   }
   else
   {
      index = 4;
   }
   pEncoding[0] = (unsigned char) (adjustedLen >> 24);
   pEncoding[1] = (unsigned char) (adjustedLen >> 16);
   pEncoding[2] = (unsigned char) (adjustedLen >>  8);
   pEncoding[3] = (unsigned char) (adjustedLen      );
   memcpy(&pEncoding[index], pBuffer, bufferLen);
   return index + bufferLen;
}

int main(int argc, char**  argv)
{
   int iRet = 0;
   int nLen = 0, eLen = 0;
   int encodingLength = 0;
   int index = 0;
   unsigned char *nBytes = NULL, *eBytes = NULL;
   unsigned char* pEncoding = NULL;
   FILE* pFile = NULL;
   EVP_PKEY *pPubKey = NULL;
   RSA* pRsa = NULL;
   BIO *bio, *b64;

   ERR_load_crypto_strings(); 
   OpenSSL_add_all_algorithms();

   if (argc != 3)
   {
      printf("usage: %s public_key_file_name ssh_key_description\n", argv[0]);
      iRet = 1;
      goto error;
   }

   pFile = fopen(argv[1], "rt");
   if (!pFile)
   {
      printf("Failed to open the given file\n");
      iRet = 2;
      goto error;
   }

   pPubKey = PEM_read_PUBKEY(pFile, NULL, NULL, NULL);
   if (!pPubKey)
   {
      printf("Unable to decode public key from the given file: %s\n", ERR_error_string(ERR_get_error(), NULL));
      iRet = 3;
      goto error;
   }

   if (EVP_PKEY_type(pPubKey->type) != EVP_PKEY_RSA)
   {
      printf("Only RSA public keys are currently supported\n");
      iRet = 4;
      goto error;
   }

   pRsa = EVP_PKEY_get1_RSA(pPubKey);
   if (!pRsa)
   {
      printf("Failed to get RSA public key : %s\n", ERR_error_string(ERR_get_error(), NULL));
      iRet = 5;
      goto error;
   }

   // reading the modulus
   nLen = BN_num_bytes(pRsa->n);
   nBytes = (unsigned char*) malloc(nLen);
   BN_bn2bin(pRsa->n, nBytes);

   // reading the public exponent
   eLen = BN_num_bytes(pRsa->e);
   eBytes = (unsigned char*) malloc(eLen);
   BN_bn2bin(pRsa->e, eBytes);

   encodingLength = 11 + 4 + eLen + 4 + nLen;
   // correct depending on the MSB of e and N
   if (eBytes[0] & 0x80)
      encodingLength++;
   if (nBytes[0] & 0x80)
      encodingLength++;

   pEncoding = (unsigned char*) malloc(encodingLength);
   memcpy(pEncoding, pSshHeader, 11);

   index = SshEncodeBuffer(&pEncoding[11], eLen, eBytes);
   index = SshEncodeBuffer(&pEncoding[11 + index], nLen, nBytes);

   b64 = BIO_new(BIO_f_base64());
   BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
   bio = BIO_new_fp(stdout, BIO_NOCLOSE);
   BIO_printf(bio, "ssh-rsa ");
   bio = BIO_push(b64, bio);
   BIO_write(bio, pEncoding, encodingLength);
   BIO_flush(bio);
   bio = BIO_pop(b64);
   BIO_printf(bio, " %s\n", argv[2]);
   BIO_flush(bio);
   BIO_free_all(bio);
   BIO_free(b64);

error:
   if (pFile)
      fclose(pFile);
   if (pRsa)
      RSA_free(pRsa);
   if (pPubKey)
      EVP_PKEY_free(pPubKey);
   if (nBytes)
      free(nBytes);
   if (eBytes)
      free(eBytes);
   if (pEncoding)
      free(pEncoding);

   EVP_cleanup();
   ERR_free_strings();
   return iRet;
}
jww
  • 97,681
  • 90
  • 411
  • 885
Adrya
  • 3,427
  • 8
  • 31
  • 29
  • 5
    In case someone is wondering how to compile this (I was), here's the compiler call: gcc -o pubkey2ssh pubkey2ssh.c -lcrypto – Andreas Gohr Feb 20 '11 at 14:10
  • where does on get argv[2] (ssh_key_description) from... I just have a -----BEGIN RSA PUBLIC KEY----- MIGJAoGBAMC62xWiOZYlhUhmk+JESy5eZunwGoG9kSHUMn67iBNZLEsR2qN44J1B TOtZRuEsSAKxu7alFlJVu5aSGbUvin3DusYAsl5sZjTf9VZgJHsVycOrtChC1tUi WMAWfv2BLTmK4zBEC33riEBLeX8Trphp3YbIMtzqV81ZrzHZbSnrAgMBAAE= -----END RSA PUBLIC KEY----- it doesn't have a description – braden Sep 18 '13 at 19:24
  • @braden. Usually it is just the email address of the key's owner. But you can put whatever you want int he description. – deltamind106 Mar 23 '15 at 14:45
  • A php implementation opensshtopem here https://github.com/131/yks/blob/master/class/stds/crypt.php#L346 – 131 Jul 20 '15 at 21:53
  • The answer from @mkalkov below does the conversion using Linux command line tools. It just needs public key pem file with headers removed and lines merged as an input. – alexandroid Jan 22 '16 at 19:01
22
ssh-keygen -i -m PKCS8 -f public-key.pem
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Boeboe
  • 2,070
  • 1
  • 17
  • 21
  • 5
    Did not work for me: "do_convert_from_pkcs8: key.pem is not a recognised public key format". What did work was "ssh-keygen -y -f key.pem" which prints out the ssh-rsa text needed for authorized_keys. – Curt Feb 04 '19 at 21:00
  • 1
    This is not working `do_convert_from_pkcs8: TEST.pem is not a recognised public key format` – Jinna Balu Apr 04 '19 at 13:44
  • Worked for me after `openssl genrsa -out newkey.pem 2048` and `openssl rsa -in newkey.pem -outform PEM -pubout -out newkeypublic.pem` – xirix Aug 09 '19 at 18:34
17
ssh-keygen -f private.pem -y > public.pub
zkilnbqi
  • 1,141
  • 11
  • 23
8

The following script would obtain the ci.jenkins-ci.org public key certificate in base64-encoded DER format and convert it to an OpenSSH public key file. This code assumes that a 2048-bit RSA key is used and draws a lot from this Ian Boyd's answer. I've explained a bit more how it works in comments to this article in Jenkins wiki.

echo -n "ssh-rsa " > jenkins.pub
curl -sfI https://ci.jenkins-ci.org/ | grep -i X-Instance-Identity | tr -d \\r | cut -d\  -f2 | base64 -d | dd bs=1 skip=32 count=257 status=none | xxd -p -c257 | sed s/^/00000007\ 7373682d727361\ 00000003\ 010001\ 00000101\ / | xxd -p -r | base64 -w0 >> jenkins.pub
echo >> jenkins.pub
hanshenrik
  • 19,904
  • 4
  • 43
  • 89
mkalkov
  • 1,364
  • 1
  • 11
  • 12
  • 1
    OMG this is the best answer! And it works! (I only had to replace status=none with status=noxfer). Just use the second command starting with "base64" and give it a PEM file on input with headers stripped out and all lines concatenated into one. Thank you @mkalkov! – alexandroid Jan 22 '16 at 18:59
  • Note the commands above assume 2048-bit key and will not work correctly if given a key of a different size. – alexandroid Jan 27 '16 at 02:00
7

I did with

ssh-keygen -i -f $sshkeysfile >> authorized_keys

Credit goes here

Community
  • 1
  • 1
periklis
  • 10,102
  • 6
  • 60
  • 68
  • 1
    Why did you not give credit to Victor above? He gave you the same command almost 8 months earlier. – jww May 27 '15 at 22:16
  • 1
    @jww From the edit log of Victor's reply you may see that originally the answer was a little different, I assume this is the reason – periklis May 28 '15 at 05:46
4

FWIW, this BASH script will take a PEM- or DER-format X.509 certificate or OpenSSL public key file (also PEM format) as the first argument and disgorge an OpenSSH RSA public key. This expands upon @mkalkov's answer above. Requirements are cat, grep, tr, dd, xxd, sed, xargs, file, uuidgen, base64, openssl (1.0+), and of course bash. All except openssl (contains base64) are pretty much guaranteed to be part of the base install on any modern Linux system, except maybe xxd (which Fedora shows in the vim-common package). If anyone wants to clean it up and make it nicer, caveat lector.

#!/bin/bash
#
# Extract a valid SSH format public key from an X509 public certificate.
#

# Variables:
pubFile=$1
fileType="no"
pkEightTypeFile="$pubFile"
tmpFile="/tmp/`uuidgen`-pkEightTypeFile.pk8"

# See if a file was passed:
[ ! -f "$pubFile" ] && echo "Error, bad or no input file $pubFile." && exit 1

# If it is a PEM format X.509 public cert, set $fileType appropriately:
pemCertType="X$(file $pubFile | grep 'PEM certificate')"
[ "$pemCertType" != "X" ] && fileType="PEM"

# If it is an OpenSSL PEM-format PKCS#8-style public key, set $fileType appropriately:
pkEightType="X$(grep -e '-BEGIN PUBLIC KEY-' $pubFile)"
[ "$pkEightType" != "X" ] && fileType="PKCS"

# If this is a file we can't recognise, try to decode a (binary) DER-format X.509 cert:
if [ "$fileType" = "no" ]; then
        openssl x509 -in $pubFile -inform DER -noout
        derResult=$(echo $?)
        [ "$derResult" = "0" ] && fileType="DER"
fi

# Exit if not detected as a file we can use:
[ "$fileType" = "no" ] && echo "Error, input file not of type X.509 public certificate or OpenSSL PKCS#8-style public key (not encrypted)." && exit 1

# Convert the X.509 public cert to an OpenSSL PEM-format PKCS#8-style public key:
if [ "$fileType" = "PEM" -o "$fileType" = "DER" ]; then
        openssl x509 -in $pubFile -inform $fileType -noout -pubkey > $tmpFile
        pkEightTypeFile="$tmpFile"
fi

# Build the string:
# Front matter:
frontString="$(echo -en 'ssh-rsa ')"

# Encoded modulus and exponent, with appropriate pointers:
encodedModulus="$(cat $pkEightTypeFile | grep -v -e "----" | tr -d '\n' | base64 -d | dd bs=1 skip=32 count=257 status=none | xxd -p -c257 | sed s/^/00000007\ 7373682d727361\ 00000003\ 010001\ 00000101\ / | xxd -p -r | base64 -w0 )"

# Add a comment string based on the filename, just to be nice:
commentString=" $(echo $pubFile | xargs basename | sed -e 's/\.crt\|\.cer\|\.pem\|\.pk8\|\.der//')"

# Give the user a string:
echo $frontString $encodedModulus $commentString

# cleanup:
rm -f $tmpFile
db_
  • 51
  • 4
2

This is what worked for me, since i only had access to the Public Key:

  1. Convert the PEM public key to a PKCS8 compatible Public Key
    openssl x509 -pubkey -noout -in pubcertkey.pem > pubcertkey.pub
  1. Convert the PKCS8 Public Key to a ssh-rsa key
    ssh-keygen -i -mPKCS8 -f pubcertkey.pub > pubcertkey-ssh-rsa.pub
0

Please be aware that even current Win32-OpenSSH builds seem to have a bug preventing this conversion from happening, as referenced here on this dedicated GitHub issue page.

Issue can be confirmed with this behaviour :

  • Output from the command is blank (but no error printed) : Blank output
  • An error can be found in Windows event viewer under the "Windows Logs/Applications" journal : Error in "Applications" journal

Only alternative seems to not use Win32-OpenSSH for this specific task.

SonicGold
  • 35
  • 1
  • 7
0

Tested for Oracle Cloud Instance public key request:

Private key generation (with passphrase):

openssl genrsa -des3 -out private.pem 4096

Public key extraction:

openssl rsa -in private.pem -pubout -out public.pem

Public key conversion in "ssh-rsa" format:

ssh-keygen -i -m PKCS8 -f public.pem > public.pub

Ensure that the permissions for the SSH folder and keys are as follows (public keys must be 644, private keys must be 400):

chmod 400 private.pem
chmod 644 public.pem
chmod 644 public.pub
Riccardo Volpe
  • 1,471
  • 1
  • 16
  • 30
0

You can also use phpseclib library

$keyObj = phpseclib3\Crypt\PublicKeyLoader::load($key, $passphrase)
$keyString = $keyObj->getPublicKey()->toString('OpenSSH');

$key can be a public or private string. If it is a file then just wrap it in file_get_contents($key)

fred
  • 1,146
  • 11
  • 16