У меня проблема с созданием / использованием ключей RSA, созданных и используемых в PHP. Проблема заключается в том, что (открытый и закрытый) ключи должны быть обменены между различными серверами (например, когда перемещается учетная запись пользователя).
Теперь openssl-lib из PHP не предоставляет никакой подробной информации о том, в каком формате создаются ключи. Последняя документация на 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, должно быть различие между форматами PEM с закрытым ключом с «RSA» в заголовке и без, PKCS # 1 и PKCS # 8, то есть с дополнительной информацией об алгоритме и т. д. или нет.
Я сталкиваюсь с серьезными проблемами из-за этого. То же программное обеспечение, написанное для PHP5.6, не может работать на PHP5.5. On может использовать обходной путь, заменив 5.5-заголовочные файлы на 5.6-совместимые вручную, но это будет просто «грязный хак». Есть ли «хороший» способ как с этим бороться?
При установке этот код замены попытается создать закрытый ключ, извлечь заголовок и «запомнить его». Все ключи, используемые во время выполнения, будут проверены. Если найденные заголовки не подойдут к «местным», их нужно будет заменить.
Но я думаю, что есть какой-то вариант конфигурации (который мне пока не удалось найти), где я могу настроить, какой формат используется?
Далее более интересный вопрос: какой формат создается именно openssl? PKCS # 1? PKCS # 8? Это также настраивается?
Нет конфигурации (к сожалению). Просто вопрос версии PHP + OpenSSL. BEGIN RSA PRIVATE KEY
указывает формат PKCS # 1. Без RSA
это PKCS # 8.
Генерация закрытого ключа RSA с PKCS1 (мой старый пост к той же проблеме)
в чем разница между "НАЧАТЬ RSA ЧАСТНЫЙ КЛЮЧ" а также "НАЧАТЬ ЧАСТНЫЙ КЛЮЧ".
Вы можете попробовать библиотеку 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 по умолчанию в соответствии с его журналом изменений.
Изменения между 0.9.8n и 1.0.0 [29 марта 2010]
Сделать PKCS # 8 форматом записи по умолчанию для закрытых ключей, заменив
традиционный формат. Эта форма стандартизирована, более безопасна и не
включить неявную зависимость MD5.
[Стив Хенсон]
Других решений пока нет …