curl / soap request with .p12 SSL certificate
Asked Answered
N

1

2

I need to SOAP request https://address?wsdl which require .p12 certificate using PHP7.2.

After hours of reasearch only thing I was able to do is making this request from bash:

$ curl -k -E cert.crt.pem --key cert.key.pem https://address?wsdl

which retured WSDL. But I had to split .p12 to separate files and use -k option which makes all this stuff not secure. Split done by this commands:

openssl pkcs12 -in mycert.p12 -out cert.key.pem -nocerts -nodes
openssl pkcs12 -in mycert.p12 -out cert.crt.pem -clcerts -nokeys

The question is: How to request this WSDL using cURL from PHP or how to configure new \SoapClient() so it will work?

Is this possible having only .p12 file & password? Or I have to convert it?


Hope this describe what I already was able to do:

<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_VERBOSE, true);

/**
 * cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
 * $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
 *
 * Result:
 *
 * This works. But:
 * - I don't have peer verification
 * - Is such file safe? It has encrypted pkey & certificate (I think not encrypted).
 *   I don't know much about that topic. Maybe someone with more experience will be able to tell more.
 *   Maybe some better solution to output this. Maybe as 2 separate files?
 */
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); // DO NOT VERIFY!
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
//curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pass); // This is not required :/
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);

/**
 * cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
 * $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
 *
 * Result:
 *
 * TCP_NODELAY set
 * Connected to XXX
 * ALPN, offering http/1.1
 * SSL certificate problem: self signed certificate in certificate chain
 * stopped the pause stream!
 * Closing connection 0
 */
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,true);
curl_setopt($ch,CURLOPT_SSLCERT,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEY,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_SSLKEYPASSWD, $pass);

/**
 * cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
 * $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
 *
 * Result:
 *
 * TCP_NODELAY set
 * Connected to XXX
 * ALPN, offering http/1.1
 * ignoring certificate verify locations due to disabled peer verification
 * error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
 * stopped the pause stream!
 * Closing connection 0
 */
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);

/**
 * cert.p12 (with password) -> cert.pem (contains encrypted PKey & client ?unencrypted? cert)
 * $ openssl pkcs12 -in cert.p12 -out cert.pem -clcerts
 *
 * Result:
 *
 * TCP_NODELAY set
 * Connected to XXX
 * ALPN, offering http/1.1
 * successfully set certificate verify locations:
 *   CAfile: /www/soap/cert.pem
 *   CApath: /www/soap
 * SSL certificate problem: self signed certificate in certificate chain
 * stopped the pause stream!
 * Closing connection 0
 */
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch,CURLOPT_CAINFO,__DIR__ . '/cert.pem');
curl_setopt($ch,CURLOPT_CAPATH,__DIR__);
curl_setopt($ch,CURLOPT_KEYPASSWD,$pass);

$data = curl_exec($ch);

$error = curl_error($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

curl_close($ch);

var_dump($data, $httpcode, $error);
?>
Nevis answered 19/4, 2018 at 10:2 Comment(10)
What code do you have to use said certificate in CURL request that does not work?Dash
Inserted some code & results. I should done that in first place :)Nevis
Seen and tried this? Seems you're mixing command arguments - #27063139Dash
Yes, I did. There is a lot misleading answers everywhere, including curl not accepting p12 certificates. Both curl & soap are working for me. SOAP using stream_context_create which allow me to set allow_self_signed=true. So one last problem that I really have is this: SSL certificate problem: self signed certificate in certificate chain. Now I have to find out how to omit this problem (not I just have workaround). After that I will post how I solve all .p12 problems for both curl & soap. I will be glad if you will be able to help me out with this self signed cert.Nevis
You may need to check your AV software to ensure that it does not check trusted/secure connections, as sometimes they may tamper with certificates, substituting them with their own, although in this case the certificate wouldn't be self-signed... would it? Also, if it is self-signed, how would server validate it? Your setting allow_self_signed is for the client to be able to send such certificate to the server. I believe you would need to import this certificate and let server know that you will be using it.Dash
I'm not much into certificates stuff but this p12 certificate was delivered to me by 3rd party system. When converted to pem subject & issuer are different so I guess its not self-signed. When I add this certificate to the system I can easily access wsdl from browser (it ask me to select proper certificate). I don't know browser behavior, but it works. Problem is only when trying to use it from php.Nevis
#21188446 ? It seems you need to pay more attention and inspect certificate chain as it tells you to, rather than try working around it.Dash
Thank you. Now I have full working connection. I will post full solution as answer later.Nevis
That must be a really long solution...Broderick
Indeed. I forgot to share solution. Here you go.Nevis
N
2

I was not able to use .p12, .pfx, pkcs12 directly is SOAP. First we need to read p12 file and convert it to .pem. Later you can cache result ($pemCertContent) to avoid conversion each request or store it as file to use as filepaths.

    $readSuccessful = openssl_pkcs12_read($content, $certData, $password);
    if (!$readSuccessful) {
        $msg = sprintf('Could not read pkcs12 file `%s`: `%s`.', $filePath, openssl_error_string());
        throw new \Exception($msg);
    }

    $pemCertContent = '';

    if (isset($certData['pkey'])) {
        openssl_pkey_export($certData['pkey'], $pem, $pemPassword);
        $pemCertContent .= $pem;
    }
    if (isset($certData['cert'])) {
        openssl_x509_export($certData['cert'], $cert);
        $pemCertContent .= $cert;
    }

    $pemCertContent; // This is your .pem certificate content. I store it as a file.

Depending on need you can use it like this when creating SOAP client:

    $soapOptions = [
        /* In some cases, when you have 2 certificates you may need to use stream context instead of 'local_cert'
        'stream_context' => stream_context_create([
            'ssl' => [
                'cafile' => $caCert->getFilePath(), // CA Certificate
                'local_cert' => $tlsCertificate->getFilePath(), // PEM certificate
                'passphrase' => $tlsCertificatePass, // Pass for PEM certificate
            ],
        ]),
         */
        'local_cert' => $tlsCertificate->getFilePath(),
        'passphrase' => $tlsCertificatePass,

        'trace' => true,
        'exceptions' => true,
    ];

This allows to connect to SoapServer to get wsdl and do request. In my case I also have to sign whole request body with WSSE. To do it I use this with .p12 cert opened by openssl_pkcs12_read function:

RobRichards\WsePhp\WSSESoap;
RobRichards\XMLSecLibs\XMLSecurityKey;

But this is another story.

Nevis answered 17/2, 2020 at 9:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.