views:

342

answers:

1

I have RSA public key in OpenSSH format:

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

How to convert in PHP this key into format suitable for openssl_pkey_get_public()?

It is easy to extract both RSA public key numbers (n and e), because second part of OpenSSH string is just base64 encoded key format described in RFC4253. So in fact the question is how to encode these numbers into PEM RSA public key format?

A: 

Solution:

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";
}

Resources: http://tools.ietf.org/html/rfc3447#appendix-A.1, http://luca.ntop.org/Teaching/Appunti/asn1.html

Jakub Kulhan