Konwertuj klucz PEM do formatu ssh-rsa

142

Mam certyfikat w derformacie, z niego tym poleceniem generuję klucz publiczny:

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

Co powoduje to:

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

Jak mogę uzyskać taki klucz publiczny? Czy z certyfikatu, czy z tego klucza publicznego?

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

Uzyskano to za pomocą tego polecenia:

ssh-keygen -y -f private_key1.pem > public_key1.pub
Adrya
źródło
14
Sposób, w jaki pisałeś w sekcji „Uzyskano to za pomocą tego polecenia”, działał dla mnie lepiej niż którakolwiek z poniższych odpowiedzi.
Yoav Shapira,
7
@YoavShipra. Tak, ale całe pytanie polega na tym, że chce on konwertować za pomocą tylko klucza publicznego. Może nie ma klucza prywatnego, a tylko klucz publiczny i chce przekonwertować z formatu PEM na format ssh-rsa.
deltamind106
10
Biorąc pod uwagę .pem z AWS, polecenie, które podałeś powyżej, ssh-keygen -y -f private_key1.pem > public_key1.pubdziałało świetnie dla mnie.
Kzqai
1
Wszystkie złe odpowiedzi. To jest poprawne: ssh-keygen -i -m PKCS8 -f public-key.pem
Boeboe
3
Piękno tkwi w oku patrzącego . Musimy zauważyć, że klucz pem może zawierać klucz publiczny lub prywatny, lub oba; zaszyfrowane, a może nie; plus w różnych formatach. Również znaczenie opcji -mjest inne dla -i/ -e. Więc moi przyjaciele, upewnijcie się, że wiecie, czego chcecie i co macie . :-)
ryenus

Odpowiedzi:

129

Nie ma potrzeby kompilowania rzeczy. Możesz zrobić to samo z ssh-keygen:

ssh-keygen -f pub1key.pub -i

odczyta klucz publiczny w formacie openssl z pub1key.pubi wyprowadzi go w formacie OpenSSH.

Uwaga : w niektórych przypadkach konieczne będzie określenie formatu wejściowego:

ssh-keygen -f pub1key.pub -i -mPKCS8

Z dokumentacji ssh-keygen (From man ssh-keygen):

-m format_klucza Określa format klucza dla opcji konwersji -i (import) lub -e (eksport). Obsługiwane formaty kluczy to: „RFC4716” (klucz publiczny lub prywatny RFC 4716 / SSH2), „PKCS8” (klucz publiczny PEM PKCS8) lub „PEM” (klucz publiczny PEM). Domyślnym formatem konwersji jest „RFC4716”.

Victor Mataré
źródło
3
ssh-keygen: nielegalna opcja - m
mbonnin
1
Pytanie idzie w drugą stronę.
131
4
W przypadku przyszłych wyszukiwarek internetowych, jeśli to nie zadziała, komentarze w pierwotnym pytaniu zadziałały.
kristopolous
17
W moim przypadku -m PKCS8było to konieczne
Ian Hunter
1
$ ssh-keygen -f mykey.pub -i key_from_blob: invalid format decode blob failed.
Bastian Voigt
53

Nie potrzeba skryptów ani innych „sztuczek”: openssli ssh-keygenwystarczą. Zakładam, że nie ma hasła do kluczy (co jest złe).

Wygeneruj parę RSA

Wszystkie poniższe metody dają parę kluczy RSA w tym samym formacie

  1. Z openssl ( man genrsa )

    openssl genrsa -out dummy-genrsa.pem 2048
    

    W OpenSSL wersja 1.0.1 genrsa została zastąpiona przez, genpkeywięc jest to nowy sposób na zrobienie tego ( man genpkey ):

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

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

Konwersja DER do PEM

Jeśli masz parę kluczy RSA w formacie DER, możesz przekonwertować ją na PEM, aby umożliwić konwersję formatu poniżej:

Pokolenie:

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

Konwersja:

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

Wyodrębnij klucz publiczny z pary RSA w formacie PEM

  1. w formacie PEM:

    openssl rsa -in dummy-xxx.pem -pubout
    
  2. w formacie OpenSSH v2 zobacz :

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

Uwagi

Wersja systemu operacyjnego i oprogramowania:

[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

Bibliografia:

Tomasz
źródło
//, Czy to faktycznie generuje klucz w ssh-rsaformacie? Dobre odniesienie, przy okazji.
Nathan Basanese
@NathanBasanese, tak (patrz „Wyodrębnianie klucza publicznego z pary RSA w formacie PEM”, punkt 2): po uzyskaniu certyfikatu w formacie pem: ssh-keygen -y -f dummy-xxx.pemtworzy ssh-rsa AAAA[...]==dopasowanie do authorized_keyspliku ssh .
Thomas,
Dobry utwór informacyjny ... ale nie sądzę, aby tak naprawdę odpowiadał na pytanie, podobnie jak powyższy, znacznie krótszy kawałek.
Ogre Codes
23

Odpowiadając na własne pytanie, po zamieszczeniu na liście mailingowej openssl otrzymałem to:

Oto kod C do konwersji z klucza publicznego OpenSSL na klucz publiczny OpenSSH. Możesz pobrać kod z tego linku i skompilować go samodzielnie:

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;
}
Adrya
źródło
2
Na wypadek, gdyby ktoś się zastanawiał, jak to skompilować (byłem), oto wywołanie kompilatora: gcc -o pubkey2ssh pubkey2ssh.c -lcrypto
Andreas Gohr
gdzie robi na get argv [2] (ssh_key_description) z ... po prostu mieć ----- BEGIN RSA PUBLIC KEY ----- MIGJAoGBAMC62xWiOZYlhUhmk + JESy5eZunwGoG9kSHUMn67iBNZLEsR2qN44J1B TOtZRuEsSAKxu7alFlJVu5aSGbUvin3DusYAsl5sZjTf9VZgJHsVycOrtChC1tUi WMAWfv2BLTmK4zBEC33riEBLeX8Trphp3YbIMtzqV81ZrzHZbSnrAgMBAAE = ----- END RSA PUBLIC KEY-- --- nie ma opisu
braden
@braden. Zwykle jest to tylko adres e-mail właściciela klucza. Ale możesz umieścić w opisie, co chcesz.
deltamind106
Implementacja php otwiera się tutaj github.com/131/yks/blob/master/class/stds/crypt.php#L346
131
Odpowiedź z @mkalkov poniżej dotyczy konwersji za pomocą narzędzi wiersza poleceń Linuksa. Potrzebuje tylko pliku pem klucza publicznego z usuniętymi nagłówkami i scalonymi wierszami jako dane wejściowe.
aleksandroid
13
ssh-keygen -i -m PKCS8 -f public-key.pem
Boeboe
źródło
3
U mnie nie działa: „do_convert_from_pkcs8: key.pem nie jest rozpoznawanym formatem klucza publicznego”. To, co zadziałało, to "ssh-keygen -y -f key.pem", które wypisuje tekst ssh-rsa potrzebny do autoryzowanych_kluczy.
Curt
1
To nie działado_convert_from_pkcs8: TEST.pem is not a recognised public key format
Jinna Balu
Pracował dla mnie po openssl genrsa -out newkey.pem 2048iopenssl rsa -in newkey.pem -outform PEM -pubout -out newkeypublic.pem
xirix
12
ssh-keygen -f private.pem -y > public.pub
zkilnbqi
źródło
6

Zrobiłem z

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

Kredyt jest tutaj

periklis
źródło
1
Dlaczego nie przypisałeś uznania Victorowi powyżej? Wydał ci to samo polecenie prawie 8 miesięcy wcześniej.
jww
1
@jww Z dziennika edycji odpowiedzi Victora możesz zauważyć, że pierwotnie odpowiedź była nieco inna, zakładam, że to jest powód
periklis
4

Poniższy skrypt uzyskałby certyfikat klucza publicznego ci.jenkins-ci.org w formacie DER z kodowaniem base64 i przekonwertowałby go na plik klucza publicznego OpenSSH. Ten kod zakłada, że ​​używany jest 2048-bitowy klucz RSA i wiele czerpie z odpowiedzi Iana Boyda . Wyjaśniłem nieco więcej, jak to działa w komentarzach do tego artykułu na wiki Jenkinsa.

echo -n "ssh-rsa " > jenkins.pub
curl -sfI https://ci.jenkins-ci.org/ | grep 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
mkalkov
źródło
OMG, to najlepsza odpowiedź! I to działa! (Musiałem tylko zamienić status = none na status = noxfer). Po prostu użyj drugiego polecenia zaczynającego się od „base64” i daj mu plik PEM na wejściu z usuniętymi nagłówkami i wszystkimi wierszami połączonymi w jeden. Dziękuję @mkalkov!
alexandroid
Zwróć uwagę, że powyższe polecenia zakładają klucz 2048-bitowy i nie będą działać poprawnie, jeśli otrzymają klucz o innym rozmiarze.
aleksandroid
1

FWIW, ten skrypt BASH weźmie certyfikat X.509 w formacie PEM lub DER lub plik klucza publicznego OpenSSL (również format PEM) jako pierwszy argument i wyłączy klucz publiczny OpenSSH RSA. To jest rozwinięciem odpowiedzi @ mkalkov powyżej. Wymagania te cat, grep, tr, dd, xxd, sed, xargs, file, uuidgen, base64, openssl(1.0+) i, oczywiście bash. Wszystko z wyjątkiem openssl(zawiera base64) jest prawie gwarantowane jako część instalacji podstawowej w każdym nowoczesnym systemie Linux, z wyjątkiem być może xxd(które Fedora pokazuje w vim-commonpakiecie). Jeśli ktoś chce to posprzątać i upiększyć, zastrzegam lektora.

#!/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_
źródło