PHP RSA 密钥创建

PHP RSA key creation

我对在 PHP 中创建和使用的 creating/using RSA 密钥有疑问。问题是,(public AND private)密钥应该在不同的服务器之间交换(例如,当用户帐户被移动时)。

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

此外,私钥 PEM 的 headers 和尾部在 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----- 
"

因此私钥 header/trailer 会根据您使用的 PHP 版本而变化。这不会是一个真正的问题,但事实证明,一个系统将创建一个密钥 header WITH "RSA" 将无法在没有 "RSA" 的情况下使用密钥,例如,openssl_sign():你会收到一条错误消息,指出 "provided key couldn't be coerced into a private key"... 这就是它变得混乱的地方。

根据 https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem,在 header 中带有 "RSA" 的私钥 PEM 格式和没有 PKCS#1 和 PKCS#8 的私钥 PEM 格式应该有所区别,即是否包含有关算法等的附加信息。

因此,我 运行 遇到了严重的问题。为 PHP5.6 编写的同一软件不能在 PHP5.5 上 运行。 On 可以通过手动将 5.5-header 替换为 5.6 兼容的版本来使用变通方法,但这只是一个 "dirty hack"。有没有 "good" 的方法来处理这个问题?

在设置时,这个 replacement-code 会尝试创建私钥,提取 header 和 "remember it"。然后将检查在 运行 时间使用的所有密钥。如果找到的 header 不适合 "local" 的,则需要交换它们。

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

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

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

(我的老post同样的问题)

what is the differences between "BEGIN RSA PRIVATE KEY" and "BEGIN PRIVATE KEY".

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

编辑

我稍微修改了你的测试脚本并在我的 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 的默认行为。

Changes between 0.9.8n and 1.0.0 [29 Mar 2010]

Make PKCS#8 the default write format for private keys, replacing the traditional format. This form is standardised, more secure and doesn't include an implicit MD5 dependency. [Steve Henson]