Как конвертировать формат файла открытого ключа OpenSSH в формат PEM


У меня есть открытый ключ RSA в формате OpenSSH:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9xmJumsHeLEDcJwf3LYONZholP3+pDHJYen4w+gm8o1r7t6oq825Gmjr7pjsQ+ZDxWivkI4vMW9RyFevPg09ljW+V7lZInBpRtB6v1s8PdmV9YVk4R3S0e7sPMPXuM7ocPLh5yKZ9f7JZwQlpp4ww/RE7blbXywjwCxngT7+G+J6HJB0UcR8xR8t6z8qDrDTAJA7pFFFNliw9M+I8tbrFl8HmoyudOFsGsYOd5hjemy4ivW88XcXzfHJdKnmD9FHVZv/GUXgErVMHS25xLcJfPalm5R8BFQrgl8SiqXj9i2vEVct9ZGydG0/Zyh2eX98D82pJhgIBmpJC4JUGv+Mt user@host

Как преобразовать в PHP этот ключ в формат, подходящий для openssl_pkey_get_public()?

Легко извлечь оба номера открытых ключей RSA (n и e), потому что вторая часть строки OpenSSH - это просто формат ключа в кодировке base64, описанный в RFC4253. Так что на самом деле вопрос в том, как закодировать эти числа в формат открытого ключа PEM RSA?

Author: Jakub Kulhan, 2010-07-21

2 answers

Решение:

function len($s)
{
    $len = strlen($s);

    if ($len < 0x80) {
        return chr($len);
    }

    $data = dechex($len);
    $data = pack('H*', (strlen($data) & 1 ? '0' : '') . $data);
    return chr(strlen($data) | 0x80) . $data;
}

function openssh2pem($file)
{
    list(,$data) = explode(' ', trim(file_get_contents($file)), 3);
    $data = base64_decode($data);

    list(,$alg_len) = unpack('N', substr($data, 0, 4));
    $alg = substr($data, 4, $alg_len);

    if ($alg !== 'ssh-rsa') {
        return FALSE;
    }

    list(,$e_len) = unpack('N', substr($data, 4 + strlen($alg), 4));
    $e = substr($data, 4 + strlen($alg) + 4, $e_len);
    list(,$n_len) = unpack('N', substr($data, 4 + strlen($alg) + 4 + strlen($e), 4));
    $n = substr($data, 4 + strlen($alg) + 4 + strlen($e) + 4, $n_len);

    $algid = pack('H*', '06092a864886f70d0101010500');                // algorithm identifier (id, null)
    $algid = pack('Ca*a*', 0x30, len($algid), $algid);                // wrap it into sequence
    $data = pack('Ca*a*Ca*a*', 0x02, len($n), $n, 0x02, len($e), $e); // numbers
    $data = pack('Ca*a*', 0x30, len($data), $data);                   // wrap it into sequence
    $data = "\x00" . $data;                                           // don't know why, but needed
    $data = pack('Ca*a*', 0x03, len($data), $data);                   // wrap it into bitstring
    $data = $algid . $data;                                           // prepend algid
    $data = pack('Ca*a*', 0x30, len($data), $data);                   // wrap it into sequence

    return "-----BEGIN PUBLIC KEY-----\n" .
           chunk_split(base64_encode($data), 64, "\n") .
           "-----END PUBLIC KEY-----\n";
}

Ресурсы: http://tools.ietf.org/html/rfc3447#appendix-A.1, http://luca.ntop.org/Teaching/Appunti/asn1.html

 2
Author: Jakub Kulhan, 2010-07-21 19:39:40

Это старый вопрос... но я только что сам выполнил это упражнение, и я задокументировал свое решение здесь:

Там есть пример кода для создания открытого ключа RSA PKCS#1 из открытого ключа OpenSSH, который немного отличается от решения Якуба (которое создает открытый ключ x.509). Я попытался включить пояснения и ссылки на дополнительную документацию в запись.

Я добавляю это здесь в первую очередь потому, что этот вопрос постоянно возникал в моих поисках дополнительной информации, поэтому я надеюсь, что кто-то найдет полезной мою попытку обобщить ситуацию.

 0
Author: larsks, 2014-08-04 17:43:16