Main Page
 The gatekeeper of reality is
 quantified imagination.

Stay notified when site changes by adding your email address:

Your Email:

Bookmark and Share
Email Notification
Project PCI Compliance & TLS 1.2
Purpose
The purpose of this project is to show how to test the web environment to determine if backend systems (primarily those interacting with remote API systems such as Authorize.Net) are capable of using TLS 1.2. PCI compliance requires (by June 2018) that systems use TLS 1.2. Luckily, API testing is simple with Authorize.Net's sandbox environment; Authorize.Net is moving to TLS 1.2 for their production API in September 2017. What has been done with this page is to show the testing process (all in Php) as you may already have parts in-hand and may only need some reference. You can download the entire testing project here.

<?php
/*
  This is a check to see if TLS 1.2 is operable on the web server.  If it is then the next step is to see if curl is able to connect to
  Authorize.net using TLS 1.2 in their test environment as, according to documentation, they have disabled all protocols with the exception
  of TLS 1.2 that will be going live on SEPTEMBER 2017 (meaning prior versions will be disabled).  In broader scope for PCI and a website in
  general, TLS 1.1 is to be used no later than June 2016 and by June 30, 2018 TLS 1.2 must be used.
  Reference: https://community.developer.authorize.net/t5/The-Authorize-Net-Developer-Blog/Request-for-Comments-API-Best-Practices/ba-p/53668
  
  Step 1 of the test is to see if you are using the following (or later versions):
  OpenSSL 1.0.1
  cURL 7.34.0 (7.38 preferred as it will have newer ciphers, SSL/TLS beyond what 7.34 has)
  PHP 5.6

  STEP 2 of the text is to submit a request to "howsmyssl.com" to see what a 3rd party detects.  You may or may not want to complete this step
  if you have other means at your disposal.

  STEP 3 of the test is to submit a test transaction to the Authorize.net server using TLS 1.2.  You will need to customize the connection information
  for your account.

  ADDITIONAL PCI INFORMATION:
  https://blog.pcisecuritystandards.org/migrating-from-ssl-and-early-tls?utm_campaign=Merchant%202nd%20TLS%20Notice%20v2.html&utm_medium=email&utm_source=Eloqua
  ADDITIONAL AUTHORIZE.NET INFORMATION:
  https://support.authorize.net/authkb/index?page=content&id=A1623&actp=LIST

  NOTES:
  With the Authorize.Net sandbox requiring TLS 1.2, if you attempt to connect without having TLS 1.2, you should get an error like "Unknown SSL protocol error in connection to apitest.authorize.net:443".
  If you need a certificate bundle for some reason (perhaps you have an old bundle that results in getting an error like 
  "SSL certificate problem, verify that the CA cert is OK" when SSL_VERIFYPEER is on), try downloading a more recent cacert.pem bundle from
  https://curl.haxx.se/docs/caextract.html
  If you are using a bundle under "old school" php/curl you will need to change the file extension from pem to crt.
  Other Php/TLS information:
  http://php.net/manual/en/migration56.openssl.php
*/

/* Define step to perform */
$test_perform = 1; /* Set to 1 to test for version information,set to 2 to perform a SSL test against "howsmyssl.com", set to 3 to submit a test transaction to authorize.net */
$ciphers = Array(); /* Do not change */
$cipher_aliases = Array(); /* Do not change */
$digests = Array(); /* Do not change */
$digest_aliases = Array(); /* Do not change */
$response_curlerror = ""; /* Do not change */
$response_curl = ""; /* Do not change */
$data = ""; /* Do not change */
$result = array(); /* Do not change */

/* (STEP 1) Get version and cipher information */
if ($test_perform === 1) {
	if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
		$ciphers = openssl_get_cipher_methods();
		$ciphers_and_aliases = openssl_get_cipher_methods(true);
		$cipher_aliases = array_diff($ciphers_and_aliases, $ciphers);
		$digests = openssl_get_md_methods();
		$digests_and_aliases = openssl_get_md_methods(true);
		$digest_aliases = array_diff($digests_and_aliases, $digests);
	}
	else {
		/* Php version too low to use */
		$ciphers[0] = "Php version less than 5.3.0.  Upgrade needed to determine ciphers.";
		$ciphers_and_aliases[0] = "Php version less than 5.3.0.  Upgrade needed to determine aliases.";
		$digests[0] = "Php version less than 5.3.0.  Upgrade needed to determine digests.";
		$digest_aliases[0] = "Php version less than 5.3.0.  Upgrade needed to determine aliases.";
	}
}

/* (STEP 2) Define "howsmyssl.com" for remote SSL test */
if ($test_perform === 2) {
	$ch = curl_init('https://www.howsmyssl.com/a/check');
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	$data = curl_exec($ch);
	curl_close($ch);
	$result = json_decode($data, true);
}

/* (STEP 3) Define the Authorize.Net Merchant Information */
if ($test_perform === 3) {
	//$ch = curl_init("https://api2.authorize.net/xml/v1/request.api"); /* Live */
	$ch = curl_init("https://apitest.authorize.net/xml/v1/request.api"); /* Test */

	$gateway_login = "your-merchant-id";	/* Change this for your test account */
	$gateway_key = "your-merchant-key";	/* Change this for your test account */

	$payload = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
	$payload = $payload . "<authenticateTestRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">";
	$payload = $payload . "<merchantAuthentication>";
	$payload = $payload . "<name>" . $gateway_login . "</name>";
	$payload = $payload . "<transactionKey>" . $gateway_key . "</transactionKey>";
	$payload = $payload . "</merchantAuthentication>";
	$payload = $payload . "</authenticateTestRequest>";

	curl_setopt($ch, CURLOPT_HEADER, 0); /* Set to 1 to see HTTP headers, otherwise 0 or XML reading will not work */
	curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-Type: text/xml"));
	curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
	curl_setopt($ch, CURLOPT_PORT, 443);
	//curl_setopt($ch, CURLOPT_SSLVERSION, 6);	/* Use TLS 1.2.  According to php documentation you should not need to specify this manually; instead let Curl choose.  Authorize.net sandbox will require TLS 1.2 until September 2017 - after which point it will also be required on live */
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
	curl_setopt($ch, CURLOPT_POST, 1);
	curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
	curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . "/cacert.crt"); /* Location in same folder as this file */
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

	$response_curl = curl_exec($ch);
	if (curl_errno($ch)) {
		$response_curlerror = curl_error($ch);
	}
	curl_close($ch);
}

/* Generate some output */
print "<html><body>\n";
if ($test_perform === 1) {
	/* STEP 1 */
	/* Get version information */
	$verinfo_curl = curl_version();
	$verinfo_php = phpversion();
	/* Show version details */
	print "OpenSSL version (minimum should be 1.0.1): " . $verinfo_curl['ssl_version'] . "<br />";
	print "cURL version (minimum should be 7.34.0): " . $verinfo_curl['version'] . "<br />";
	print "PHP version (minimum should be 5.6): " . $verinfo_php . "<br />";
	print "<hr />Ciphers:<hr />";
	print "<pre>";
	print_r($ciphers);
	print "</pre><br />";
	print "<hr />Cipher Aliases:<hr />";
	print "<pre>";
	print_r($cipher_aliases);
	print "</pre><br />";
	print "<hr />Digests:<hr />";
	print "<pre>";
	print_r($digests);
	print "</pre><br />";
	print "<hr />Digest Aliases:<hr />";
	print "<pre>";
	print_r($digest_aliases);
	print "</pre><br />";
}
else if ($test_perform === 2) {
	/* STEP 2 */
	print "<b>Highest TLS Version Detected:</b> " . $result['tls_version'] . "<br />";
	print "Full Response:<br />";
	print_r($result);
}
else if ($test_perform === 3) {
	/* STEP 3 */
	/* Show results of submitting request using TLS 1.2 */
	print "Result of Submission:<br />";
	if (strlen($response_curlerror) > 0) { print "Curl ERROR: " . $response_curlerror . "<br />"; }
	else { print $response_curl . "<br />"; }
}
else {
	/* Something is afoot */
	print "Nothing appears to have been setup.";
}
print "</body></html>\n";

?>


Discovering Version Information
Discovering version information (other than the script code above) can be helpful in determining what needs to be updated and/or upgraded. For example, the Internet is full of articles on getting PHP to 5.6 (minimum) from 5.5 on Ubuntu. One of the distinctions that nobody made in that regard, as an example, is you can have 5.5 on Ubuntu 12.04 (precise) and 14.04 (trusty). If you were unfortunate enough to be on 12.04 then it was highly likely NONE of the articles would work because the PPA, while it contained a distribution of Php5.6 it did not have a distribution for 12.04. Alas, an upgrade of the OS to 14.04 is required before you can start thinking about upgrading Php (to ultimately update mods such as cURL (from 7.22 to 7.34) that can handle TLS 1.2). Below you will find a variety of commands that may come in handy for determining the versions are of assorted services on your Ubuntu system. These are assumed to be entered from the terminal unless otherwise noted.

[Get OS version]: lsb_release -a
[Get Apache version]: apache2 -v
[Get openssl version]: openssl version
[Get Php version]: php -v
[Get Php mod packages installed]: dpkg --get-selections | grep -v deinstall | grep php NOTE: If you feel adventureous you can also simply go to where Php is currently installed such as /etc/php5/mods-available or /etc/php/5.5/mods-available to get a list.
[Get Php cURL version]: <?php phpinfo(); ?> NOTE: This is meant to be run from a php page and rendered in a web browser. Keep in mind that while you can have a cURL installed on the system, that is NOT usually the one used by Php.
[Get list of Repositories (PPA) installed]: apt-cache policy | grep http | awk '{print $2 $3}' | sort -u

Brief Note About Upgrading Php and Mod Packages:
By default, when you install a new version of Php (such as Php5.6) with the generic "sudo apt-get install php5.6" it will not necessarily install all of the mod packages you may current have. In most cases you will have to specify the packages unique to your system with syntax such as "sudo apt-get install php5.6 php5.6-curl". That is why it is important to get a list of mod packages installed on your system that were not part of the standard installation of your current version of Php.

Example Upgrade From Ubuntu 12.04 (precise):
This example shows how to upgrade Ubuntu from 12.04 to 14.04 so that you can ultimately get Php5.6 installed (and, thus, the newer version of openssl and cURL used by Php to allow you to use TLS1.2).

sudo do-release-upgrade [NOTE: This upgrades the system to 14.04. There will be a variety of prompts but generally you may want to retain existing configurations when explicitly prompted. Even if you select to answer "Yes" to all prompts to make installation/upgrade faster, it will drop out and wait for your input with the more critical ones.]

After the system has restarted and you get connected then you'll focus on Apache and Php next:

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt-get update
sudo apt-get upgrade [this upgrades apache2]
sudo apt-get install php5.6 php5.6-curl php5.6-mcrypt php5.6-gd php5.6-json php5.6-mbstring php5.6-mysql php5.6-common php5.6-opcache php-imagick php-memcache
sudo service apache2 restart
sudo a2dismod php5 [this formally disables php5.5; you may need to do this if phpinfo() still says you are running Php5.5 which then means you will likely need to run the following command as well ]
sudo a2enmod php5.6 [this formally enables php5.6]
sudo service apache2 restart

At this point you should trying accessing your website's assorted pages for final testing and version checking.

About Joe