Преобразование открытого ключа открытого ключа RSA в формат OpenSSH (PHP)

Я пытался какое-то время создать пару ключей RSA с расширением openssl PHP и сохранить результат в качестве совместимой с OpenSSH ключевой пары – это означает, что закрытый ключ закодирован в PEM (что легко), а открытый ключ хранится в специальном формате OpenSSH следующего вида:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABA...more base64 encoded stuff... 

Насколько я мог понять, этот формат состоит из:

  • тип ключа в открытом тексте, за которым следует пробел (т. е. «openssh-rsa»)
  • Строка с кодировкой base64, представляющая следующие данные:
    • длина имени алгоритма в байтах (в данном случае 7), закодированная как 32-битный беззнаковый длинный большой endian
    • имя алгоритма, в данном случае 'ssh-rsa'
    • длина RSA 'e' в байтах, закодированная как 32-битный беззнаковый длинный большой endian
    • номер RSA 'e'
    • длина RSA 'n' числа в байтах, закодированная как 32-битный беззнаковый длинный большой endian
    • номер RSA 'n'

Я попытался реализовать это с помощью функции pack () PHP, но независимо от того, что я пробую, результат не эквивалентен тому, что я получаю от использования команды ssh-keygen -y -f в том же секретном ключе RSA, сгенерированном openssl.

Вот упрощенная версия моего кода:

 <?php // generate private key $privKey = openssl_pkey_new(array( 'private_key_bits' => 1024, 'private_key_type' => OPENSSL_KEYTYPE_RSA )); // convert public key to OpenSSH format $keyInfo = openssl_pkey_get_details($privKey); $data = pack("Na*", 7, 'ssh-rsa'); $data .= pack("Na*", strlen($keyInfo['rsa']['e']), $keyInfo['rsa']['e']); $data .= pack("Na*", strlen($keyInfo['rsa']['n']), $keyInfo['rsa']['n']); $pubKey = "ssh-rsa " . base64_encode($data); echo "PHP generated RSA public key:\n$pubKey\n\n"; // For comparison, generate public key using ssh-keygen openssl_pkey_export($privKey, $pem); $umask = umask(0066); // this is needed for ssh-keygen to work properly file_put_contents('/tmp/ssh-keygen-test', $pem); umask($umask); exec('ssh-keygen -y -f /tmp/ssh-keygen-test', $out, $ret); $otherPubKey = $out[0]; echo "ssh-keygen generated RSA public key:\n$otherPubKey\n\n"; echo ($pubKey == $otherPubKey ? "yes! they are the same\n" : "FAIL! they are different\n"); ?> 

Любые советы о том, как я могу это сделать, не полагаясь на ssh-keygen?

Хорошо, я только что решил свою проблему, более подробно рассмотрев ссылки на реализацию C из Convert pem key в формат ssh-rsa (что я делал раньше, но, видимо, я пропустил некоторые важные вещи). Мне нужно было И первый символ N и e с 0x80, и если он соответствует, добавьте еще один символ NULL в начале номера и увеличьте размер на 1 соответственно.

Я не уверен, почему это делается (я не нашел в этом ссылки никаких ссылок в онлайновом поиске), но он работает.

Я только сделал базовые тесты, но, похоже, он работает хорошо, и вот мой код:

 <?php $privKey = openssl_pkey_get_private($rsaKey); $pubKey = sshEncodePublicKey($privKey); echo "PHP generated RSA public key:\n$pubKey\n\n"; function sshEncodePublicKey($privKey) { $keyInfo = openssl_pkey_get_details($privKey); $buffer = pack("N", 7) . "ssh-rsa" . sshEncodeBuffer($keyInfo['rsa']['e']) . sshEncodeBuffer($keyInfo['rsa']['n']); return "ssh-rsa " . base64_encode($buffer); } function sshEncodeBuffer($buffer) { $len = strlen($buffer); if (ord($buffer[0]) & 0x80) { $len++; $buffer = "\x00" . $buffer; } return pack("Na*", $len, $buffer); } ?> 

Гораздо более простой метод, использующий phpseclib, реализацию RSA с чистым PHP :

 <?php include('Crypt/RSA.php'); $rsa = new Crypt_RSA(); $rsa->loadKey('-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0 FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/ 3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB -----END PUBLIC KEY-----'); $rsa->setPublicKey(); $publickey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH); ?> в <?php include('Crypt/RSA.php'); $rsa = new Crypt_RSA(); $rsa->loadKey('-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0 FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/ 3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB -----END PUBLIC KEY-----'); $rsa->setPublicKey(); $publickey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH); ?> 

Вывод:

 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== phpseclib-generated-key 

Источник:

http://phpseclib.sourceforge.net/rsa/examples.html#public,openssh