I've been trying for a while to generate an RSA keypair using PHP's openssl extension and save the result as an OpenSSH compatible keypair - meaning the private key is PEM encoded (which is easy) and the public key is stored in an OpenSSH specific format of the following form:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABA...more base64 encoded stuff...
As far as I could gather this format consists of:
I've tried implementing this using PHP's pack() function, but no matter what I try the result is never equivalent to what I get from using the ssh-keygen -y -f
command on the same RSA private key generated by openssl.
Here is a simplified version of my code:
<?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");
?>
Any tips on how I can do this without relying on ssh-keygen?
Convert the public key format from SSH2 to OpenSSH Try to find the original SSH2 public key that was provided from the user. Otherwise, use the sshldap command to output the SSH2 public key. You may need to manually insert line-breaks at the appropriate places. Save the SSH2 public key to a file (e.g. ssh2.
To convert a SSH client key to an OpenSSH format: Install the OpenSSH tool set, available under a BSD-style license: http://www.openssh.com/ The ssh-keygen utility is used to covert SSH keys between the different formats required by MessageWay or any other secure file transfer application.
An SSH2 public key in OpenSSH format will start with "ssh-rsa". The idea behind all of this is that once you have keys on the remote server and your local host, access will be simpler since the server will only grant access to someone who has the matching private key.
Ok, I have just solved my own problem by taking a closer look at the C implementation references from Convert pem key to ssh-rsa format (which I did before but apparently I missed some important stuff). I needed to AND the first character of N and e with 0x80 and if it matches add another NULL character at the beginning of the number and increase the size by 1 respectively.
I am not sure why this is done (I didn't find any reference to this in on-line searching I did) but it works.
I've only done basic tests on this but it seems to work well, and here my code:
<?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);
}
?>
Much simpler method, using phpseclib, a pure-PHP RSA implementation:
<?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);
?>
Output:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== phpseclib-generated-key
Source:
http://phpseclib.sourceforge.net/rsa/examples.html#public,openssh
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With