我有一个问题,创建/使用在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();
?>
将输出不同的内容:
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-----
"
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,私钥PEM格式在报头中有"RSA"和没有"RSA"的格式之间应该有区别,即pkcs# 1和pkcs# 8,即是否有关于算法等的附加信息。
因此我遇到了严重的问题。为PHP5.6编写的相同软件不能在PHP5.5上运行。我可以使用一个变通方法,用5.6兼容的手工替换5.5头,但这只是一个"肮脏的hack"。有没有一个"好"的方法来处理这个问题?
在设置时,这个替换代码将尝试创建一个私钥,提取标题并"记住它"。然后将检查运行时使用的所有键。如果找到的标头不适合"本地"标头,则需要交换它们。
但我猜有某种配置选项(我还没有设法找到)在哪里我可以配置使用哪种格式?
下一个更有趣的问题:openssl到底创建了什么格式?PKCS # 1 ?PKCS # 8 ?这也是可配置的吗?
没有配置(很遗憾)。只是PHP + OpenSSL版本问题。BEGIN RSA PRIVATE KEY
表示pkcs# 1格式。没有RSA
,它是pkcs# 8。
生成私钥RSA与PKCS1(我以前的帖子相同的问题)
"BEGIN RSA PRIVATE KEY"and "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的变更日志重现openssl的默认行为。
0.9.8n和1.0.0的变化[29 Mar 2010]
将pkcs# 8作为私钥的默认写格式,取代传统的格式。这种形式是标准化的,更安全,而且不会包含隐式MD5依赖项。(史蒂夫·亨森)