Gnupg gives "get_key failed" despite using a valid key - php

I want to integrate PGP encryption into my web application, after looking for what to use(extensions, libraries, etc.) I decided to go with the gnupg extension for php. Now, I do have a PGP key in one of my desktop folders and I've tried to use it's fingerprint as a string for addencryptkey, the error I receive is get_key failed which I don't understand why, my PGP key is valid.
There are two very similar questions on SO:
php gnupg get_key failed error ,
gnupg get_key failed in php ,
Based on these, I've updated my code somewhat to no success, here's what it currently looks like:
putenv("GNUPGHOME=/home/user/Desktop/Keys/.gnupg/");
$pgp = new gnupg();
$pgp->addencryptkey("F0E2DF9C82ECE67935171F4939D8599A923820D9");
echo $pgp->geterror();
In the folder specified in putenv, I have my public key saved in a .asc file. I can't see what the problem really is, unless it only works with keys stored on the server?

I just wanted to share my fix for this issue. Given that this is one of the more recent questions on this topic I thought it best to share it here.
At the time I was able to encrypt messages fine (PHP 7.4 with the GNUPG PECL extension).
To address the get_key_failed error, after setting up/importing my keys I copied my entire .gnupg directory to the root of my webserver (/var/www/html in my case) and updated its permissions so that it was accessible by the webserver.
putenv("GNUPGHOME=/var/www/html/.gnupg");
I assumed that this would fix it, however I then encountered a new error when attemping to decrypt a message:
Uncaught Exception: decrypt failed
The only way I could resolve this was by ensuring my key pair did not have a passphrase. Some comments on the PHP GNUPG docs suggest that that passphrase which is the second argument on adddecryptkey() is ignored regardless. However, in my case decryption only worked with a private key that didn't have a passphrase set.
This worked on my local instance (Ubuntu 18) and when deployed to an EC2 instance running Amazon Linux 2.

Related

PHP access to webservice with .cer and .p7b certificates

I'm trying to access a webservice from the prefecture that consolidate commercial invoices, the website provides those 2 certificates .cer and .p7b, I've tried both curl and soap clients with several different options in the params array, but still only get 403 or error on loading key.
Can someone explain me which of the certificates I should use, or if both, which goes in which option and others params needed?
Using curl I at least get a 403 answer (which is something expected without the certificate), but on soap client it only gives SOAP-ERROR: Parsing WSDL even it's the very same URL.
Trying loading the keys leads to:
.CER: "unable to set private key file ... type PEM"
.P7B: "could not load PEM client certificate, OpenSSL error:02001002:system library:fopen:No such file or directory, (no key found, wrong pass phrase, or wrong file format?)"
though the path seems to be correct.
Get same errors using cmd line curl with --cert options (though I tried each cert file individually, is there a way to use both simultaneously?)

How to connect to a WebSphere MQ Queue using SSL authentification in PHP

I want to connect to a WebSphere MQ Queue using SSL authentification from a PHP application (script).
Queue Manager Version is 7.0 or 7.5
MQ Client Version is 8.0
PHP Version is 7.0 (docker)
Using PHP mqseries pecl extension v0.15 (with custom fixes)
So far I was able to connect without SSL connection.
Here are the steps I have to done so far:
I have installed the WebSphere MQ Client v8 for Linux (CentOS in my case)
I have downloaded the PECL 0.15 version of the mqseries php extension. (There as a small bug in the extension, I had to recompile it to make it work properly. I used to get a segmentation fault on MQGET).
I linked the the mqseries.so to php and enabled the extension.
I succesfully (without SSL)
Connect to the queue manager
Open the queue for reading
Get messages on the queue
Close the connection
When I set the USE_SSL in my script to true, I get the error code 2393 that means "An MQCONN or MQCONNX call was issued with SSL configuration options specified, but an error occurred during the initialization of the SSL environment." This error message is very generic and does not help me pin point where is the problem.
The MQ_KEYSTORE is set as /path/to/my/key and my filename is key.kdb and has at the same level key.sth as suggested by this documentation
The MQ_SSL_CIPHER_SPEC is the same in the script than specified by on the queue manager for the specified MQ_CHANNEL_SSL. Checked multiple times. These are NOT the Cipher Suite used with JMS connections
The security cache has been refreshed on the queue manager.
On the server side, I checked the error logs for the queue manager and didn't seem to see my channel name. I say "seem" because there was a lot of noise and there were a few ??? channel name in the lot. So I feel like it did not reach the queue manager for some reason.
I also used the "amqssslc" command found in the MQ Client installation bin folder to test my ssl config. I get the same error than using the PHP script.
I also used WireShark to sniff packets on the corresponding MQ_PORT. The content of the packages contained certificate information. So there is something that looks like a SSL hand shake going on.
I am now out of ideas as of how to debug the case. Does anyone has an idea of what to check next? Is there connection logs on my MQ Client installation that I should check?
Here is an example of a connection using SSL in PHP
Here is a simplified version of my MQ script (I removed the outputs). Some of the constants are not disclosed for security purposes.
All MQSERIES_* constants are defined in the extension
All MQ_* are hardcoded parameters to test my script but their definition does not appear in the script excerpt.
<?php
// Constants defined here
$options = [
'Version' => MQSERIES_MQCNO_VERSION_4,
'Options' => MQSERIES_MQCNO_STANDARD_BINDING,
'MQCD' => [
'Version' => 7,
'ChannelName' => MQ_CHANNEL,
'ConnectionName' => sprintf('%s(%s)', MQ_HOST, MQ_PORT),
'TransportType' => MQSERIES_MQXPT_TCP,
]
];
if (USE_SSL) {
$options['MQSCO'] = [
'KeyRepository' => MQ_KEYSTORE
];
$options['MQCD']['ChannelName'] = MQ_CHANNEL_SSL;
$options['MQCD']['SSLCipherSpec'] = MQ_SSL_CIPHER_SPEC;
}
mqseries_connx(MQ_QUEUE_MANAGER, $options, $conn, $comp_code, $reason );
$mqods2 = [
'ObjectType' => MQSERIES_MQOT_Q,
'ObjectName' => MQ_QUEUE
];
mqseries_open($conn, $mqods2, MQSERIES_MQOO_INPUT_AS_Q_DEF | MQSERIES_MQOO_FAIL_IF_QUIESCING, $obj, $comp_code, $reason);
$gmd = [];
$gmo = [
'Options' => MQSERIES_MQGMO_FAIL_IF_QUIESCING | MQSERIES_MQGMO_WAIT, 'WaitInterval' => 3000
];
$msg = "";
$data_length = "";
for ($i = 0; $i < 1000; $i++) {
mqseries_get($conn, $obj, $gmd, $gmo, 10000, $msg, $data_length, $comp_code, $reason);
if ($reason === 2033) {
printf("No more messages to process\n");
break;
}
// Business logic
}
mqseries_disc($conn, $comp_code, $reason);
?>
UPDATE
Using the client side logs that I didn't know existed. We finally found out that our server was not using a proper certificate. The server's certificate was actually self-signed, thus not granting us access because our .kdb file didn't have it's public key. We added the server's own public key to the .kdb file making that step work out.
As suggested in the comments from JoshMC, for our next problem, we suspect the label is wrong in the keystore file. We did not specify a specific label from the .kdb file. From the docs "For queue managers and clients respectively, the following sources are searched in sequence for a non-empty value. The first non-empty value determines the certificate label. The certificate label must exist in the key repository. If no matching certificate in the correct case and format is found that matches a label, an error occurs and the SSL or TLS handshake fails."
And it also states that "The certificate label cannot contain spaces.".
I will try again tomorrow with proper label naming and sending a specific label name. I will try to see if the name convention ibmwebspheremq<user_that_runs_the_php_process> actually impacts the validity.
UPDATE 2
It finally worked out. As mentioned by JoshMC, the private key needs to have the specific label ibmwebspheremq. I didn't try the CertificateLabel yet. I might dig into that next week.
At first we were using the GUI (ikeyman) from ibm to generate the .kdb file. But we found out it had quite a few bugs in it. For instance, it imports twice the private key with the same label (auto-generated label from the certificate). Editing the label name was not possible (hence the connection problems). To solve that part we used the command line tool ikeycmd that basically offers the same features on the command line (minus the bugs). To run the command, you need the IBM jre (Not verified, needs to be checked out) and run it as java com.ibm.gsk.ikeyman.ikeycmd. From here, there is a whole world of documentation on IBM web site about how to create a certificate, rename labels, check details, etc. Lots and lots of fun!
With a full IBM MQ client install, client side errors will be logged to the directory: /var/mqm/errors
The errors if any will be logged to the file AMQERR01.LOG (this gets rotated with two other files ending in 02 and 03. If the error is something that MQ is not expecting it may also create a file that ended in .FDC with additional details.
The Queue Manager will log a channel name of ??? if the connection has failed during the channel negotiating prior to the client the sending the channel name. If the IBM MQ the queue manager is using v7.5 or lower the channel name is not exchanged until after the TLS handshake is complete.
With v8 and later clients connecting to v8 and later queue managers, MQ will use TLS SNI to exchange the channel name during TLS negotiation, however I am unsure if MQ has also been enhanced to log this channel name where in prior versions it logged ???. Note that IBM MQ Classes for Java and IBM MQ Classes for JMS client even at v8 and later do not support the SNI feature and do not send the channel name until after the TLS handshake is complete.
Check that your client key.kdb has a private key. There are three ways which MQ uses to identify which private key to use.
With MQ Client v7.5 and earlier the label of the private key must be:
ibmwebspheremq<user_that_runs_the_php_process>
With MQ client v7.5 and earlier an alternative is to set the private key as the default certificate and make sure the following environment variable is set in the environment which executes the program. Note this also works with 8.0.0.7/9.0.0.1 but I prefer the next method over this one.
AMQ_SSL_ALLOW_DEFAULT_CERT=1
With MQ Client v8 and later you can use CertificateLabel=anylabelvalue in the SSL stanza of the mqclient.ini. Example below:
SSL:
CertificateLabel=anylabelvalue
The CertificateLabel setting is documented in the IBM MQ v8 KC page "SSL
stanza of the client configuration file".
A template for mqclient.ini can be copied from /var/mqm/mqclient.ini and placed in the same directory where the application executes. The location can also be specified via a environment variable and MQ will look in a few other locations for the file if it the variable is not set and it does not find it in the same directory where the application executes. The various ways in which MQ will look for this file is documented in the IBM MQ v8 KC page "Location of the client configuration file"

Can't ssh to server using keys authentication

Since a few days now I've been creating ssh keys using the phpseclib library (Branch 2.0) like I've done before..copying the public key over to my server thus connecting without the use of a password....but for some reason I just can't connect like this anymore. When I report the errors using $ssh->getLastError(), it displays:
SSH_MSG_USERAUTH_FAILURE: publickey,password
Does anyone know what could be the problem?
Ensure that your private key is added to authorized_keys on the server. Without that, you'll always get this error. More specifically, in my case it was:
SSH_MSG_USERAUTH_FAILURE:
publickey,gssapi-keyex,gssapi-with-mic,password

Satis http basic auth - pass credentials

I have set up satis private composer packet manager.
Satis is running on "packages.asc.company", I protected the site by apache2 http basic authentication and can open it in browser by entering http basic auth credentials.
Now my question: How can I pass composer the credentials to access the satis site in the best and most secure manner when running e.g. "composer update"?
Currently I registered only one user with a password in apache .htpasswd file and need to pass its credentials somewhere to be able to connect from composer to satis.
There are two cases where I need to connect from:
1) From the project during development
2) From jenkins during continuous integration process.
3) Edit: SSL
I am trying to use openssl now to secure the credentials when logging in. On my linux, where the apache runs, I created a private key and a .crt file (see: Apache SSL . On my linux, I can now open the satis packages page with https, and I even redirect http to https, all working (Im using my own certificate generating with openssl because its an internal application and I don't need a trusted ca).
Now, when I switch to my Windows from my Linux vm (here I am coding), and I try to run composer update, I get the following error message:
(hosts file is configured correct)
[Composer\Downloader\TransportException]
The "https://packages.asc.company/packages.json" file could not be downloaded: SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
Failed to enable crypto
failed to open stream: operation failed
What did I miss? I'm pretty new to ssl, but read the whole day now information about it and can't get it to work.
From getcomposer satis site, I have this information but don't know how to use it.
{
"repositories": [
{
"type": "composer",
"url": "https://example.org",
"options": {
"ssl": {
"local_cert": "/home/composer/.ssl/composer.pem"
}
}
}
]
}
Regards.
There is a documentation page for this.
Composer will work with adding user names into the Satis URL. Works for me, I just wanted to get around the useless default passworded server in the local network. There's a read only account, and I used it.
Additionally: Every developer in the company has an account on the repository server, and there isn't much use in protecting downloaded ZIP files with even more security. Composer itself currently doesn't support any code signing methods or hash comparison, so there is no way to know if a package has been tampered with either where stored or during transmission.
According to the docs, not giving credentials in the URL will make Composer ask for them, or you can add them to auth.json. On the other hand: Saving clear text passwords in a dedicated file doesn't sound like the best idea, and transmitting them without using HTTPS is even worse.
You have to define what kind of security you want to have. What is the goal or the threat scenario you want to protect against?

How to check if SSL bundle and domain certificates are made from existing private key

If I have pkey, csr (generated from pkey), bundle certificate and domain certificate files. How can I validate if both certificates are made for pkey?
Also is that the right way to validate ssl certificates. Any suggestions?
I would like to avoid using openssl cli tool and use php openssl library or any third party php library.
#HannoBinder was close but it was a bit trickier. Apparently in PHP openssl one must get resource variables from certificate (tested also on certificate bundle file containing domain certificate for nginx) and private key file.
You can get certificate resource with openssl_pkey_get_public. But for private key you must get it with openssl_pkey_get_private. Its not to clearly written in documentation since probably these functions are quite universal.
Also i could not at first understand how to get public key from those resources. Apparently openssl_pkey_get_details can do that.
So in the end I resolved it this way.
$certPubKeyResource = openssl_pkey_get_public(file_get_contents($cert));
$publicKey1 = trim(openssl_pkey_get_details($certPubKeyResource)['key']);
$private_key = openssl_pkey_get_private(file_get_contents($pkey));
$publicKey2 = trim(openssl_pkey_get_details($private_key)['key']);
echo (!strcmp($publicKey1, $publicKey2) ? 'OK' : 'FAIL') . PHP_EOL;
I hope someone will have use for this since I could not find this particular case.
Also If someone will give more in depth answer with maybe some references to useful materials about SSL in PHP I could mark it as a correct answer instead of mine since im quite interested into getting full picture of this topic.

Categories