How to connect PHP to MySQL using mysqli and SSL [duplicate] - php

Environment:
Ubuntu 14.04, PHP 5.5.9, MYSQL 5.6.30
From time to time I get an error
mysqli::real_connect(): SSL operation failed with code 1. OpenSSL
Error message:
error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
Front/nginx is on one server and mysql is on a different server.
The error is not continuous, it happens randomly and I find those error messages in error log so I cannot really do full debug.
Sample:
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$this->objMySqli = mysqli_init();
mysqli_options ($this->objMySqli, MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, true);
$this->objMySqli->ssl_set($this->SslKey, $this->SslCertificate, $this->SslCACertificate, null, null);
$link = $this->objMySqli->real_connect($this->Server, $this->Username, $this->Password, $this->Database, $this->Port);
if (!$link) {
throw new QMySqliDatabaseException("Unable to connect to Database", -1, null);
}
SSL Cipher:
It fails on this line:
$link = $this->objMySqli->real_connect($this->Server, $this->Username, $this->Password, $this->Database, $this->Port);

It is problem with mysql version 5.6.30,
I have updated to 5.6.32 and now this error is not happening anymore.

Upgrading didn't fix my problem, but I did find a new solution.
TLDR; Convert your PKCS#8 format keys to PKCS#1 format keys
openssl rsa -in example.com.server.key.pem -out example.com.server.key.pkcs1.pem
I ran into these bugs:
MySQL Bug 71271
MySQL Bug 64870
Essentially, if your private key is in PKCS#8 format instead of PKCS#1 format, MySQL will not "find" the private key.
A lot of Stack Overflow answers say rename the header and footer to include RSA in the header and footer like so:
From (PKCS#8 format)
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
To (PKCS#1 format)
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
However, this does not change the private key data between the header and footer. When using my PKCS#8 format key with the modified header and footer, no error occured at MySQL startup, but when I tried to connect with mysqli I got
error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version number
By converting my private keys to PKCS#1 format, the keys worked and I could connect fine:
openssl rsa -in example.com.server.key.pem -out example.com.server.key.pkcs1.pem
Note that newer OpenSSL versions, at least 1.0.2j (what I was using) output PKCS#8 keys when using
openssl req -config "openssl.cnf" \
-keyout "private/example.com.server.key.pem" -new -sha256 \
-days 3652 -nodes -out "csr/example.com.server.csr.pem"
but using different options may output PKCS#1 keys (possibly -newkey rsa:2048, I haven't tested). Which would explain why only some people are experiencing this when using openssl generated private keys.
This problem occurred form me in MySQL 5.6.17 and 5.7.14, so it seems like the bugs are not fixed.

Related

remote SSL Connection to AWS(Keypairs) using PHP

I am trying to connect to an AWS RDS instance using SSL but I keep getting this error:
mysqli_real_connect(): Unable to set private key file
What im doing is this:
Generate key pairs in AWS, keep private key.
Use OpenSSL to get the cert.pem using this command and the private key that I got from AWS: openssl req -newkey rsa:2048 -nodes -keyout private-key-generated-by-AWS.pem -x509 -days 365 -out certificate.pem
I downloaded the cacert.pem from this website: https://curl.haxx.se/docs/caextract.html
Here is my code:
mysqli_ssl_set($con,"sshconn.pem","certificate.pem","cacert.pem",NULL,NULL);
if (!mysqli_real_connect($con,"myAWSendpoint","username","password","DBname"))
{
die("Connect Error: " . mysqli_connect_error());
}
mysqli_close($con);
?>
I am pretty sure I am not setting my private key correctly but I don't know what I'm doing wrong, any suggestions? Thank you!
AWS RDS uses server side authentication, not client side. You'll need to the master password once you setup the SSL connection or whatever users you have provisioned inside the DB or IAM users.
https://dev.mysql.com/doc/refman/5.6/en/using-encrypted-connections.html
A root certificate that works for all regions can be downloaded at https://s3.amazonaws.com/rds-downloads/rds-ca-2015-root.pem
Intermediates are here: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html
mysql -h myinstance.c9akciq32.rds-us-east-1.amazonaws.com
--ssl-ca=[full path]rds-combined-ca-bundle.pem --ssl-verify-server-cert
https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MySQL.html#MySQL.Concepts.SSLSupport

Certificate not accepted. Unable to set private key file

I try to make a connection through SoapClient. I need a certificate for this. I received a .pfx certificate. I used the following command to create a .pem file.
openssl pkcs12 -in cert.pfx -out cert.pem -nodes
There is a password in the certificate so I need to enter it before I get the cert.pem file. So far so good, I think.
Now I try to connect to the WSDL service.
$url = "https://test.website.com/webservices/transfer.asmx?WSDL";
$cert = '/path/to/cert.pem';
$passphrase = "12345678";
$soapClient = new SoapClient($url, array('local_cert'=>$cert,'passphrase'=>$passphrase));
I get the following error:
(Warning) SoapClient::SoapClient(): Unable to set private key file `/var/www/vhosts/............./cert.pem'
I think the problem is the certificate. Is the way that I converted the .pfx to a .pem the correct way?
The problem you're running into is that a .pem certificate is always supposed to be an encrypted file. According to the OpenSSL docs for the pkcs12 command when you used -nodes it didn't encrypt anything, rather put each node into plain text, which caused the .pem certificate to be invalid and your SoapClient couldn't parse the invalid file.
To fix this, hopefully you haven't deleted the original cert.pfx, just re-convert it using this line:
openssl pkcs12 -in cert.pfx -out cert.pem -clcerts
and your cert.pem file will be correct.
Today I had this problem with an invalid Cert/Private combination, meaning the cert wasn't belonging to the specified key.
You can verify this problem using:
openssl rsa -noout -modulus -in server.key | openssl md5
openssl x509 -noout -modulus -in server.crt | openssl md5
key and cert should return the same checksum. If not, somebody has mixed up some files.
The same procedure works for CSRs as well:
# and for a CSR
openssl req -noout -modulus -in server.csr | openssl md5

Why different private key strings under Linux or Windows?

When I'm creating private key strings with the following PHP code (and same config-parameter), they are enclosed between different strings:
$configs = array('config' => 'OpenSSL.cnf',
'digest_alg' => 'sha1',
'x509_extensions' => 'v3_ca',
'req_extensions' => 'v3_req',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'encrypt_key' => false,
'encrypt_key_cipher' => OPENSSL_CIPHER_3DES);
$privateKeyResourceId = openssl_pkey_new($this->configs);
openssl_pkey_export($privateKeyResourceId, $privateKeyString);
On Linux the $privateKeyString looks like this:
-----BEGIN PRIVATE KEY-----NBgkqhkiG9w0BAQE....ASDFasjkfa-----END PRIVATE KEY-----
On Windows the $privateKeyString looks like this:
-----BEGIN RSA PRIVATE KEY-----NBgkqhkiG9E....ASDFasjkfa-----END RSA PRIVATE KEY-----
When I copy the Windows private key string to Linux it works until I remove the 'RSA' from the start/end (same behavior vice versa). Why is this?
This is a differece between openssl versions not PHP. The following openssl command creates different key headers/footers between openssl versions 0.9.x and 1.0.0x:
openssl req -new -keyout mykey.key -out mycertreq.csr -nodes -sha1 -newkey rsa:2048
For version 0.9.x, the key header/footer is:
-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----
For version 1.0.0x, the key header/footer is:
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
For the later version of openssl, I have to run the key file through the following command to make it compatible with the older default:
openssl rsa -in mykey.key -text > mykey.pem
The "mykey.pem" file then has the header/footers (and format) that is compatible with AWS and like services.
According to a user note php.net this is a known issue:
Please take note that older versions of PHP/OpenSSL exports the RSA private key with '-----BEGIN RSA PRIVATE KEY-----' PEM tag, which includes just the privateKey field, thus omitting the version and privateKeyAlgorithm fields.
The effect of that would be that if you're converting it to DER, and
then back to PEM, but using '-----BEGIN PRIVATE KEY-----' PEM tag,
that the openssl_pkey_get_privatekey() function will fail!Senthryl's
code can be used to prefix the PEM encoded data with the version and
privateKeyAlgorithm fields again.
The newer PHP/OpenSSL versions exports the RSA private key with
'-----BEGIN PRIVATE KEY-----' PEM tag, which includes the version and
privateKeyAlgorithm fields.
I noticed these differences between my two servers:
PHP Version 5.3.3 (OpenSSL 1.0.0a-fips 1 Jun 2010) on Fedora Core 12 x64
PHP Version 5.2.9 (OpenSSL 0.9.8g 19 Oct 2007) on Fedora Core 10 x64

openssl_seal problems

I'm having some problems loading in a public key for encrypting using openssl_seal function in PHP...
I've created the public & private keys using the openSSL command line tool:
openssl genrsa -des3 -out private.pem 1024
openssl rsa -in private.pem -out public.pem -outform PEM -pubout
However, when I run it in my PHP code I get the following errors:
openssl_seal() [function.openssl-seal]: Don't know how to get public key from this private key
openssl_seal() [function.openssl-seal]: not a public key (1th member of pubkeys)
When I verify the public key using: openssl verify public.pem, I get:
unable to load certificate
1876:error:0906D06C:PEM routines:PEM_read_bio:no start line:.\crypto\pem\pem_lib
.c:648:Expecting: TRUSTED CERTIFICATE
Anyone know why this is happening?
PHP Code:
public function encrypt($valueToEncrypt, $publicKeyFile)
{
$pk = file_get_contents($publicKeyFile);
$publicKey = openssl_pkey_get_public($pk);
$encrypted = '';
$a_envelope = array();
$a_key = array($publicKey);
if (openssl_seal($valueToEncrypt, $encrypted, $a_envelope, $a_key) === FALSE)
{
while ($msg = openssl_error_string())
echo $msg . "<br />\n";
die('Failed to encrypt data!');
}
openssl_free_key($publicKey);
....
For anyone experiencing the same problem, the issue related to an install of xampp on windows. Running on the command line on a fresh install of just php worked fine.
In addition to StuffandBlah's answer: It is in fact related to the different OpenSSL versions in Apache and PHP in XAMPP on Windows. Here's a post of how to solve the problem (copying DLLs).

OpenSSL PHP Function doesn't work

I'm looking for days how to translate this command to OpenSLL php function:
$ openssl pkcs8 -inform DER -in aaa010101aaa_CSD_01.key -out AAA010101AAA.key.pem
Enter Password: a0123456789
This work perfect, but I can't (I don't know) use the correct function of PHP OpenSSL
http://www.php.net/manual/es/ref.openssl.php
I have made this for .cer working perfect:
command : openssl x509 -inform DER -outform PEM -in aaa010101aaa_CSD_01.cer -out AAA010101AAA.cer.pem
PHP equivalent:
function der2pem($der_data) {
$pem = chunk_split(base64_encode($der_data), 64, "\n");
$pem = "-----BEGIN CERTIFICATE-----\n".$pem."-----END CERTIFICATE-----\n";
return $pem;
}
$fp = fopen("llaves/aaa010101aaa_CSD_01.cer", "r");
$priv_key = fread($fp, 8192);
fclose($fp);
echo der2pem($priv_key);
It's perfect, giving it to me the PEM. But doesn't work for the .key, just for the .cer, I obtain on OpenSSL this for .key using the funcion on PHP:
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDpmiW1q9gyzCFtMcbaFDJexk2IpLoTdNXg4ToGRZ/f+hIjmj3N
6ODWX1ARNFGYocEHf113GpW5Oe/mj6UqhBpiH4JRTNR4Udb8myJTArIlODynVHuI
UuyhKo7gbMbDdXjilTAYY2XWQuQ7aDtWwntUmNg4vAC/F3OtRz3+y9wM5QIDAQAB
AoGAfNkHomqvZ6a1jrh1wIPez8xID+mKEW/2BvQYoNWBNqFeJG0A7xWxZKEYF7nQ
ijSZB7rIZylsL8yJLL5E1c44koc+2+S6OF6gcWujcLR5UFRIZscxo0e1ro30wSTy
MBcdBeWASbaEy7+7MF46W0hAhBE7b49JUmduz1fBjtNNeoECQQD3VbNAsbf/90Vw
ZVexUXWNquwPsAZjmdpL1Te1RdC5txj2EbUdDPaaYC1cCXXjblf9rsyyViUowsNt
cnh92wSRAkEA8clIHObESiZZEndtmYuRlgLsX2Gr/qo30uAUurH7p8Q07SWOZCJk
OPJUhh2qanYZsnsOYHW+9Br5U6gDknxdFQJBALoua9nWLdDjnQTHdKSI0jmLIVmJ
wrV1GgsdfGrbHAzAlGye1bwBhxycK2jtwi0qYdgXngTcreop+hxIIAV1OdECQQC7
v7rZhnBhy8lax5Y5puEUBY0au7Nc+zyB6TLvjgmGSpt2krUxGGuOtM3hnuOX68Ek
kN2nFYeD8fYtecfcVenJAkEAxM+QzoVdJpw1/ijh+IfZ5eNhvMK2bj4De+/2Sx5K
ZZ9SiI7bRaOjBfvaI7AkeH0LpihURW12Zt7hZv8kC058Jg==
-----END RSA PRIVATE KEY-----
The same function on php give me:
-----BEGIN-----
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIgkLCyAP9+nQCAggA MBQGCCqGSIb3DQMHBAgTJc/0zgL+tASCAoBHumtbuHsY5IevnUwr1Ha5P+S3RpVS 6iYvoOvKs5L6bPE+fjCkLxset0e68NcyXDw3WO/qzfGkFVGBnC90gekWUiS7/2pf ltPeKjo8Fw7T4CgVWhtnVdPZmJWihY52FuIQ2HuyQYzH8K/SnjYP4GBkJKpdPCjy JNzjspfJPoOlfdOkj7URwvKH5RjsLGYkopjtdaqxS2pFfz6PPCSiFMn+Jo9Vkjwv 3d1b3X2SoYuYhV2g3XgHWHBQNqoas8Blhcw8OYNlntxcaNnx3Eb9YcCWSmNj7l+c pibvosXzEqVoat6PsJyUVzJQZBao15PnI438qomkprsCy6EvFSSXuIunMH7nVIGH gcS0TRxYpXPHY3cdQOdsJtxb4Ny1aCJqLQkHs2jbiLZ5mu+rmQUil3tLxfRd4Bje AYA3T1e5VVVha97BmNQVTbNfxj9wWvkmrearxJXTvvnML4f0ma830S/1AGmCGLb/ A2KlqHq9RxqJn1SdOuYQbcQTeGe/JRVOaJVGUKxJ2vbwjB/35cmGNLKYj4faZZgy jYpC3BqVeDcPGlPuMnoNkgrBfLDgDX+JV6tqqT2uMo76Wp6xYKmM41jkO1IAWpHR /R7d3aG2psL0wKkFSEXWxhX3SRLdurVcFLv6E6bEPHqHJR4QrvF2OUwpDhOZz9F/ Lz935gz5MQNRuVRtAVBAa2ZmjRbdCboL/qiL2MKibZLNRmKy3dqRdPOmnlKKBR8d cYQc4YwRm5dWuObO2tY68R4H43g7shw+POKSzvSPhAkzRRGExONVDJ6Zg/0iAUNe 0xYlsqKJyCJ1fg/b1AuFyyAnOhCO9ywUGiK7t92pZRgmwxEfVBDRNcbA
-----END -----
Any ideas?
There's a library that allows just that. It is called Chilkat2. I used it to do the same in Python without console.
Right now I'm trying to remove this dependency in my library because it's closed source and it can't be installed through package managers, it must be installed manually with the website instructions.
It works pretty well, however, so here's how you would do it, assuming you have the .key file, the password, and Chilkat2 PHP Extension installed:
include("chilkat_9_5_0.php"); //whatever version you download
privateKey = new CkPrivateKey();
privateKey.LoadPkcs8EncryptedFile('path/to/private.key',password)
pemString = privateKey.getPkcs8Pem()
Note: I created this code with the documentation, but I have never used Chilkat2 with PHP.

Categories