6

I have an issue with creating/using RSA keys created and used in PHP. Problem is, that the (public AND private) keys should be exchanged between different servers (e.g. when a user account is moved).

Now, the openssl-lib of PHP does not provide any detailed info on in what format the keys are created. The latest documentation at http://php.net/manual/en/function.openssl-pkey-export.php just states, that it is "in PEM format", but it does not say whether it is in PKCS#1 or PKCS#8

Additionally, the headers and trailers of the private key PEM differ between PHP versions as the following code demonstrates:

<?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();
?>

will output different stuff:

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----- 
"

So the private key header/trailer changes based on what PHP version you use. This wouldn't be a real problem, but as it turns out, a system which would create a key header WITH "RSA" will not be able to user key WITHOUT "RSA" for e.g., openssl_sign(): You'll get an error stating that the "provided key couldn't be coerced into a private key"... And this is where it gets messy.

According to https://tls.mbed.org/kb/cryptography/asn1-key-structures-in-der-and-pem, there should be a distinction between Private key PEM formats with the "RSA" in the header an those without, being PKCS#1 and PKCS#8, i.e. with additinal info about the algorithm etc. or not.

I am running into serious problems due to this. The same software written for PHP5.6 cannot run on PHP5.5. On could use a workaround by replacing the 5.5-headers with 5.6-compatible ones manually, but this would just be a "dirty hack". Is there a "good" way how to deal with this?

At setup, this replacement-code would attempt to create a private key, extract the header and "remember it". All keys used at runtime would then be checked. If the headers found would not fit to the "local" ones, they would need to be exchanged.

But I guess there is some kind of configuration option (which I haven't managed to find yet) where I can configure which format is used?

Next, more interesting question: What format is created exactly by openssl? PKCS#1? PKCS#8? Is this also configurable?

Xenonite
  • 1,823
  • 4
  • 26
  • 39
  • 1
    Unfortunately, I just ran a test with PHP v5.6.9, and in this case it also outputs headers and trailers containing the "RSA" – Xenonite Aug 11 '15 at 09:41

1 Answers1

2

No configuration (unfortunatelly). Just PHP + OpenSSL version issue. BEGIN RSA PRIVATE KEY indicates PKCS#1 format. Without RSA it's PKCS#8.

Generate private key RSA with PKCS1 (my older post to the same problem)

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

You can try phpsec library or call openssl from command line (exec()). I know it does not help you but it seems there is not good solution yet.

Edit

I altered your test script a little bit and tested private key format on my 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-----

These results reproduce default behaviour of openssl according to its changelog.

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]

Community
  • 1
  • 1
kba
  • 4,190
  • 2
  • 15
  • 24
  • but why is sometimes the same version of PHP (e.g. 5.6) using different headers on different installations? where is this specified? – Xenonite Aug 17 '15 at 13:23
  • Hard to say. What OS do you use? Are you using default PHP or self-compiled? What version of OpenSSL library do you see in phpinfo? For example, I get PKCS#8 (BEGIN PRIVATE KEY) on my windows machine with PHP 5.5.18 (OpenSSL 1.0.1i). – kba Aug 17 '15 at 13:54
  • Checked this on: PHP 5.4 (Debian wheezy), PHP 5.6 (Debian wheezy with PHP 5.6 from "http://packages.dotdeb.org"), PHP 5.6 (MacOS X 10.10, PHP installed via brew), PHP 5.5 (MacOS X 10.10, source unknown), PHP 5.5 (Ubuntu) – Xenonite Aug 18 '15 at 12:47
  • 1
    And openssl versions used by PHP? [If you look to openssl changelog you can see they changed default private key format from PKCS#1 to PKCS#8 in 1.0.0](https://github.com/openssl/openssl/blob/master/CHANGES#L2066). I think it is the root of problem with PHP behaviour. But PHP 5.6 should use openssl 1.0+ :-/. I will test it a little but further at home. – kba Aug 18 '15 at 14:22
  • ok we might be getting to the core of the problem :) Yet on my MacOS X 10.10 with openssl 0.9.8zf with PHP 5.6 I get the -----BEGIN PRIVATE KEY------ headers... Let me guess - thats an apple issue ? ;) I tried this: http://apple.stackexchange.com/questions/126830/how-to-upgrade-openssl-in-os-x and got now 1.0.2d_1 - I'll let you know how this affects the key headers... – Xenonite Aug 18 '15 at 14:41
  • wow gee thanks! that obviously deserves the bounty !!! :) the openssl 1.0.2d with php 5.6.12 also outputs no "RSA" in the header... Last question from my side would be how to use a key created with the RSA in the headers (e.g. openssl 0.9.8zf) with an openssl version that has no RSA in the headers by default (e.g. openssl 1.0.1p). This fails badly atm -.- – Xenonite Aug 19 '15 at 07:38
  • Yes, it could be a problem. Because lack of settings private key format. If it really fails, you should convert private key before using in PHP to required format. You can try openssl directly or phpsec library. – kba Aug 20 '15 at 09:49