Pegues Pegues - 1 year ago 153
reST (reStructuredText) Question

How to connect to API using PHP with a PFX file and password?

I need to connect to a company API using a PFX file and password using oauth2.

I'm not very familiar with using PFX files and not sure how to connect to an API using a PFX file and password. I looked here at SO but didn't find much to help me get started. I searched google, but found a few documents and sample code, none of which worked. I found the following code, but it isn't working for me. Can someone please help me get this working?

Code I've found and am currently trying to use is as follows:

<?php

$url = "https://myaccounts.domain.com/auth/oauth/v2/token";
$cert_file = 'my_auth.pfx';
$cert_password = '1234567890';

$ch = curl_init();

$options = array(
CURLOPT_RETURNTRANSFER => true,

//CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false,

CURLOPT_USERAGENT => 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)',

//CURLOPT_VERBOSE => true,
CURLOPT_URL => $url ,
CURLOPT_SSLCERT => $cert_file ,
CURLOPT_SSLCERTPASSWD => $cert_password ,
);

curl_setopt_array($ch , $options);

$output = curl_exec($ch);

if(!$output)
{
echo "Curl Error : " . curl_error($ch);
}
else
{
echo htmlentities($output);
}
?>


The above code gives me the following error:

Curl Error: could not load PEM client certificate, OpenSSL error error:02001002:system library:fopen:No such file or directory, (no key found, wrong pass phrase, or wrong file format?)


The key is in the same directory, so I'm not sure why it isn't finding it. Perhaps I'm approaching the use of a PFX file wrong.

Answer Source

I was approaching this all wrong. From what I've read and the information I've gathered, it's best to convert the PFX file to a PEM file. I did this using cygwin with all the necessary packages and openssl. Once the PFX file was converted to PEM, I then used a curl command with necessary credentials to connect to the API I need to pull data from. The command I ran from a bash shell is the following:

curl -i -XPOST -u username:password -k https://myaccounts.domain.com/auth/oauth/v2/token -v --cert my_auth.pem

I received the following response:

* timeout on name lookup is not supported
*   Trying 123.123.123.123...
* TCP_NODELAY set
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0* Connected to myaccounts.domain.com (123.123.123.123) port 111 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [87 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [3880 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [333 bytes data]
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
{ [903 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
} [1291 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [70 bytes data]
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
} [264 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=US; ST=My Location; L=ThankYou; O=Automatic Data Processing, Inc.; OU=Testing Labs; CN=myaccounts.domain.com
*  start date: Aug  4 00:00:00 2001 GMT
*  expire date: Oct 23 01:01:01 2017 GMT
*  issuer: C=US; O=My Corporation; OU=My Trust Network; CN=My Class 3 Secure Server CA - G4
*  SSL certificate verify ok.
* Server auth using Basic with user '123456'
} [5 bytes data]
> POST /auth/oauth/v2/token HTTP/1.1
> Host: myaccounts.domain.com
> Authorization: Basic veryveryveryveryverylongstringthatwillgoherebecauseitisveryverylong==
> User-Agent: curl/6.12.0
> Accept: */*
>
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0{ [5 bytes data]
< HTTP/1.1 200 OK
< MY-CorrelationID: 123456789-adda-1234-a123-1a12345abcde
< Pragma: no-cache
< Cache-Control: no-store, no-cache, private
< Content-Type: application/json;charset=UTF-8
< Content-Length: 127
< Date: Thu, 02 Feb 2017 23:05:46 GMT
< Server: My Accounts
<
{ [127 bytes data]
100   127  100   127    0     0     75      0  0:00:01  0:00:01 --:--:--    77* Curl_http_done: called premature == 0
100   127  100   127    0     0     75      0  0:00:01  0:00:01 --:--:--    77HTTP/1.1 200 OK
MY-CorrelationID: 123456789-adda-1234-a123-1a12345abcde
Pragma: no-cache
Cache-Control: no-store, no-cache, private
Content-Type: application/json;charset=UTF-8
Content-Length: 127
Date: Thu, 02 Feb 2017 23:05:46 GMT
Server: My Accounts

{
  "access_token":"123456789-1234-1234-1234-12345678901234",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"api"
}
* Connection #0 to host myaccounts.domain.com left intact

I was also able to verify this connection using postman, and I get the same response consistently.

I further development the solution for my needs based on research I've done. The PHP solution using cURL is below. Below are two functions and an if condition. The if condition fires the appropriate function based on if the access token has already been added to the session or not. If not added to the session, it will fetch it based on the credentials that needs to be added. If already added to the session, then proceed with getting the data needed.

I used the php curl documentation for expanding on my OP: http://php.net/manual/en/book.curl.php

<?php

session_start();

function getAccessCode(){

    $curl = curl_init();

    // Variables
    $apiGrantType = 'client_credentials';
    $apiScopes = array('scope1','scope2','scope3');             // Currently not used
    $apiUrl = "myaccounts.domain.com/auth/oauth/v2/token?grant_type=" . $apiGrantType;
    $authPath = '/var/www/html/domain.com/clients/test/';
    $cliendId = 'username';                                     // Client ID
    $clientSecret = 'password';                                 // Client Secret
    $certUserPwd = $cliendId . ":" . $clientSecret;             // Client ID:Client Secret
    $certFile = $authPath . 'my_auth.pem';                      // Private Cert
    $certPassword = 'cert-password';                            // Cert Password

    $apiPost = array(
        "grant_type"    => $apiGrantType,
        "client_id"     => $cliendId,
        "client_secret" => $clientSecret
    );
    $apiPostQuery = http_build_query($apiPost);

    $apiHeader = array();

    // $header Content Length
    $apiHeader[] = 'Content-length: 0';

    // $header Content Type
    $apiHeader[] = 'Content-type: application/json';

    // $header 'Client ID:Client Secret' Base64 Encoded
    $apiHeader[] = "Authorization: Basic " . base64_encode($cliendId . ":" . $clientSecret); // OAuth,Basic

    // cURL Options
    $options = array(

        CURLOPT_URL                 => $apiUrl,

        CURLOPT_RETURNTRANSFER      => true,

        CURLOPT_HEADER              => false, // true to show header information
        CURLINFO_HEADER_OUT         => true,
        CURLOPT_HTTPGET             => false,
        CURLOPT_POST                => true,
        CURLOPT_FOLLOWLOCATION      => false,
        CURLOPT_VERBOSE             => true,
        CURLOPT_FOLLOWLOCATION      => true,

        CURLOPT_SSL_VERIFYHOST      => false, // true in production
        CURLOPT_SSL_VERIFYPEER      => false, // true in production

        CURLOPT_TIMEOUT             => 30,
        CURLOPT_MAXREDIRS           => 2,

        CURLOPT_HTTPHEADER          => $apiHeader,
        CURLOPT_USERAGENT           => 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)',

        CURLOPT_HTTPAUTH            => CURLAUTH_ANYSAFE, // CURLAUTH_BASIC
        CURLOPT_POSTFIELDS          => $apiPostQuery,

        CURLOPT_USERPWD             => $certUserPwd,
        CURLOPT_SSLCERTTYPE         => 'PEM',
        CURLOPT_SSLCERT             => $certFile,
        CURLOPT_SSLCERTPASSWD       => $certPassword
    );

    curl_setopt_array($curl , $options);
    $output = curl_exec($curl);
    $json = json_decode($output);

    return $json->access_token;
}


function getJobApps($access_token) {

    echo '<pre>' . print_r($_SESSION, TRUE) . '</pre>';

    /**
     * Get Job Applications Data from DOMAIN
     */
    $curl = curl_init();

    $apiUrl = "https://myaccounts.domain.com/aaaaa/bbbbb";

    // $header Authorization
    $apiHeader = array('Authorization', 'Bearer ' . $access_token);

    $options = array(
        CURLOPT_URL             => $apiUrl,
        CURLOPT_HTTPHEADER      => $apiHeader,
        CURLOPT_RETURNTRANSFER  => true,
        CURLOPT_POST            => true
    );

    curl_setopt_array($curl , $options);
    $output = curl_exec($curl);
    $json = json_decode($output);

    echo '<pre>';
    print_r($json);
    echo '</pre>';
}


// Init Loop
if(isset($_SESSION['access_token'])) {

    // Job Applications
    $apiData = getJobApps($_SESSION['access_token']);

    echo $apiData;

} else {
    $access_token = getAccessCode();
    $_SESSION['access_token'] = $access_token;

    echo '<pre>' . print_r($_SESSION, TRUE) . '</pre>';

    header(sprintf("Location: %s", 'http://mywebsite.com/clients/test/test.php'));
    die();
}

?>
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download