1

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);
?>
imclickingmaniac
  • 1,467
  • 1
  • 16
  • 28
  • What code do you have to use said certificate in CURL request that does not work? – Daniel Protopopov Apr 19 '18 at 10:43
  • Inserted some code & results. I should done that in first place :) – imclickingmaniac Apr 19 '18 at 13:07
  • Seen and tried this? Seems you're mixing command arguments - https://stackoverflow.com/questions/27062639/php-soap-client-with-certificates-over-ssl – Daniel Protopopov Apr 19 '18 at 19:27
  • 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. – imclickingmaniac Apr 19 '18 at 20:54
  • 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. – Daniel Protopopov Apr 20 '18 at 08:06
  • 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. – imclickingmaniac Apr 20 '18 at 09:46
  • 1
    https://stackoverflow.com/questions/21187946/curl-error-60-ssl-certificate-issue-self-signed-certificate-in-certificate-cha# ? It seems you need to pay more attention and inspect certificate chain as it tells you to, rather than try working around it. – Daniel Protopopov Apr 20 '18 at 14:16
  • Thank you. Now I have full working connection. I will post full solution as answer later. – imclickingmaniac Apr 21 '18 at 21:06
  • That must be a really long solution... – NicklasF Feb 14 '20 at 14:45
  • Indeed. I forgot to share solution. Here you go. – imclickingmaniac Feb 17 '20 at 09:37

1 Answers1

1

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.

imclickingmaniac
  • 1,467
  • 1
  • 16
  • 28