2011-04-02 101 views
7

我一直在尝试使用PHP的openssl扩展生成RSA密钥对,并将结果保存为OpenSSH兼容密钥对 - 意味着私钥是PEM编码(很简单)和公共密钥存储在以下形式的OpenSSH的特定格式:将OpenSSL生成的RSA公钥转换为OpenSSH格式(PHP)

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

至于我可以收集这些格式包括:

  • 关键型清晰文字,后面跟空格(即“openssh-rsa”)
  • 表示以下数据A base64编码串:(在此情况下7)编码为32比特的无符号长大端
  • 算法名称,在这种情况下“
    • 算法名称的字节长度SSH- RSA E”
    • 的RSA的长度‘’中的字节数,被编码为32位无符号长大端
    • 的RSA‘e’的数量
    • 的RSA的长度‘N’数以字节为单位的编码作为32位无符号长大端编号
    • RSA'n'编号

我尝试实施这一使用PHP的包()函数,但无论我怎么努力的结果绝不会是相当于什么,我从使用上所产生的同RSA私钥的ssh-keygen -y -f命令得到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-凯基?

回答

9

好的,我刚刚通过仔细看看Convert pem key to ssh-rsa format(之前做过但显然我错过了一些重要的东西)的C实现参考来解决了我自己的问题。我需要AND与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); 
} 
?> 
+0

请注意'sshEncodeBuffer'需要字节长度。如果你有'mbstring。func_overload'设置为重载字符串函数,用'mb_strlen($ buffer,'8bit')替换'strlen($ buffer)';' – bishop 2016-02-17 16:02:29

+0

@shevron它完美适用于RSA密钥。但它不适用于DSA密钥。我如何转换DSA公钥? – Harikrishnan 2016-06-03 04:42:54

1

很多简单的方法,使用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); 
?> 

输出:

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

来源:

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

相关问题