6

我在创建/使用 PHP 中创建和使用的 RSA 密钥时遇到问题。问题是,(公共和私人)密钥应该在不同的服务器之间交换(例如,当用户帐户移动时)。

现在,PHP 的 openssl-lib 没有提供任何有关创建密钥格式的详细信息。http://php.net/manual/en/function.openssl-pkey-export.php上的最新文档只是声明它是“PEM 格式”,但没有说明它是 PKCS#1 还是PKCS#8

此外,私钥 PEM 的标头和尾标在 PHP 版本之间有所不同,如以下代码所示:

<?php
$config = array(
    "digest_alg" => 'sha512',
        "private_key_bits" => 4096,
    "private_key_type" => OPENSSL_KEYTYPE_RSA
);
$keyPair = openssl_pkey_new($config);
$privateKey = NULL;
openssl_pkey_export($keyPair, $privateKey);
var_dump($privateKey);
$keyDetails = openssl_pkey_get_details($keyPair);
$publicKey = $keyDetails['key'];
var_dump($publicKey);
die();
?>

将输出不同的东西:

PHP v 5.4:

string(3272) "-----BEGIN PRIVATE KEY-----
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqA//...
-----END PRIVATE KEY-----
"

string(800) "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAO//...
-----END PUBLIC KEY-----
"

PHP v 5.5:

string(3272) "-----BEGIN RSA PRIVATE KEY-----
MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqA//...
-----END RSA PRIVATE KEY-----
"

string(800) "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAO//...
-----END PUBLIC KEY-----
"

PHP v 5.6:

string(3272) "-----BEGIN PRIVATE KEY----- 
MIIJQgIBADANBggsdisdVUCJDSQCjqgl2XqzR+bSv//...
-----END PRIVATE KEY-----
"

string(800) "-----BEGIN PUBLIC KEY----- 
MIICIjANBgkqhkiG9w0BsdvQEFAAOdfAg8AMIICFAgEAo6oJdl6s0fm0r7QlaN/U//...
-----END PUBLIC KEY----- 
"

因此,私钥头/尾随您使用的 PHP 版本而变化。这不会是一个真正的问题,但事实证明,一个将创建一个带有“RSA”的密钥头的系统将无法使用没有“RSA”的密钥,例如,openssl_sign():你会得到一个错误指出“提供的密钥不能被强制转换为私钥”......这就是它变得混乱的地方。

根据https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem,标头中带有“RSA”的私钥 PEM 格式和那些没有,是 PKCS#1 和 PKCS#8,即是否有关于算法等的附加信息。

因此,我遇到了严重的问题。为 PHP5.6 编写的相同软件无法在 PHP5.5 上运行。On 可以通过手动将 5.5 标题替换为 5.6 兼容的标题来使用解决方法,但这只是一个“肮脏的黑客”。有没有一种“好”的方法来处理这个问题?

在设置时,这个替换代码会尝试创建一个私钥,提取标题并“记住它”。然后将检查运行时使用的所有密钥。如果找到的标头不适合“本地”标头,则需要交换它们。

但我想有某种配置选项(我还没有找到)可以配置使用哪种格式?

接下来,更有趣的问题:openssl 究竟创建了什么格式?PKCS#1?PKCS#8?这也是可配置的吗?

4

1 回答 1

2

没有配置(不幸的是)。只是 PHP + OpenSSL 版本问题。BEGIN RSA PRIVATE KEY表示 PKCS#1 格式。没有RSA它是 PKCS#8。

使用 PKCS1 生成私钥 RSA(我较早的帖子针对相同的问题)

"BEGIN RSA PRIVATE KEY" 和 "BEGIN PRIVATE KEY" 有什么区别

您可以尝试使用 phpsec 库或从命令行 ( exec()) 调用 openssl。我知道这对您没有帮助,但似乎还没有好的解决方案。

编辑

我稍微更改了您的测试脚本并在我的 Windows 7 上测试了私钥格式。

<?php

$keyPair = openssl_pkey_new(array(
    "digest_alg" => 'sha512',
    "private_key_bits" => 4096,
    "private_key_type" => OPENSSL_KEYTYPE_RSA
));
$privateKey = null;

openssl_pkey_export($keyPair, $privateKey);

echo sprintf("PHP: %s\n", phpversion());
echo sprintf("OpenSSL: %s\n", OPENSSL_VERSION_TEXT);
echo sprintf("Private key header: %s\n", current(explode("\n", $privateKey)));

 

PHP: 5.4.44
OpenSSL: OpenSSL 0.9.8zf 19 Mar 2015
Private key header: -----BEGIN RSA PRIVATE KEY-----

PHP: 5.5.28
OpenSSL: OpenSSL 1.0.1p 9 Jul 2015
Private key header: -----BEGIN PRIVATE KEY-----

PHP: 5.6.12
OpenSSL: OpenSSL 1.0.1p 9 Jul 2015
Private key header: -----BEGIN PRIVATE KEY-----

这些结果根据其更改日志重现了 openssl 的默认行为。

0.9.8n 和 1.0.0 之间的变化 [2010 年 3 月 29 日]

使 PKCS#8 成为私钥的默认写入格式,取代传统格式。这种形式是标准化的、更安全的,并且不包含隐式 MD5 依赖项。[史蒂夫汉森]

于 2015-08-17T13:12:26.167 回答