├── .travis.yml ├── LICENSE ├── Module.php ├── README.md ├── autoload_classmap.php ├── autoload_function.php ├── autoload_register.php ├── bin └── update-ca-bundle ├── composer.json ├── data └── Equifax_Secure_CA.pem ├── src └── Sslurp │ ├── AbstractCaRootData.php │ ├── CaRootPemBundle.php │ ├── MozillaCertData.php │ ├── Sslurp.php │ └── X509Certificate.php └── test ├── .gitignore ├── SslurpTest ├── CaRootPemBundleTest.php ├── MozillaCertDataTest.php ├── SslurpTest.php ├── TestAsset │ ├── MockCaRootPemBundle.php │ ├── MockMozillaCertData.php │ ├── MockMozillaCertDataInvalidPin.php │ └── MockMozillaCertDataInvalidPinAndExp.php ├── X509CertificateTest.php └── _files │ ├── ca-bundle.pem │ ├── certdata.txt │ └── mxr.mozilla.org.pem ├── bootstrap.php ├── coverage-checker ├── cs-checker └── phpunit.xml /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | - 5.5 7 | - 5.6 8 | 9 | before_install: 10 | - wget -q http://cs.sensiolabs.org/get/php-cs-fixer.phar 11 | 12 | script: 13 | - phpunit -c test/phpunit.xml 14 | - ./test/coverage-checker test/clover.xml 100 15 | - ./test/cs-checker 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Evan Coury 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Module.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | class Module 14 | { 15 | public function getAutoloaderConfig() 16 | { 17 | return array( 18 | 'Zend\Loader\ClassMapAutoloader' => array( 19 | __DIR__ . '/autoload_classmap.php', 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sslurp 2 | 3 | v1.0 by Evan Coury 4 | 5 | [![Build Status](https://secure.travis-ci.org/EvanDotPro/Sslurp.png?branch=master)](http://travis-ci.org/EvanDotPro/Sslurp) 6 | 7 | ## Introduction 8 | 9 | Dealing with SSL properly in PHP is a pain in the ass and completely insecure by default. Sslurp aims to make it easier to use SSL in PHP safely and securely. Sslurp can be used as a stand-alone library, CLI tool, or a ZF2 module. 10 | 11 | **Note:** Sslurp requires PHP with OpenSSL support. This is standard in most Linux distributions' PHP packages, otherwise you need to compile PHP using --with-openssl[=DIR]. 12 | 13 | ## Features / Usage 14 | 15 | ### Root CA bundle management 16 | 17 | Sslurp provides CLI and OOP interfaces for generating a trusted root Certificate Authority (CA) bundle using [certdata.txt](http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1) from the source tree of Mozilla's [Network Security Services (NSS) libraries](https://www.mozilla.org/projects/security/pki/nss/) and keeping it up-to-date. The resulting root CA bundle includes the certificates vetted according to the [Mozilla Root Certificate Program](http://www.mozilla.org/projects/security/certs/policy/) — the same root CA bundle trusted by cURL, Firefox, Chrome, and many other applications, libraries, languages, and operating systems. 18 | 19 | Sslurp takes additional steps to protect against MITM attacks while fetching certdata.txt from Mozilla's source tree, ensuring that the generated bundle is truly authentic. When connecting to Mozilla's mxr.mozilla.org domain to fetch the updated certdata.txt, Sslurp forces the use of verified SSL. Sslurp uses the following process to establish the initial trust of the SSL certificate on mxr.mozilla.org: 20 | 21 | * Check the SSL\_CERT\_FILE environment variable (used by OpenSSL). If the value is the path to a readable file and valid certificate bundle, Sslurp will use it. 22 | * If the SSL\_CERT\_FILE is not set or points to a non-existent / invalid certificate bundle, Sslurp will search several known/expected locations for the root CA bundle and use the first valid bundle found. 23 | * If a valid bundle is not found in any of the expected paths, Sslurp will finally fall back to using a bundled, pre-verified copy of the root CA's public key which established trust for the mxr.mozilla.org certificate (Equifax Secure Certificate Authority at least until November 2013). 24 | 25 | As if that's not enough, Sslurp _additionally_ makes use of [public key pinning](http://tools.ietf.org/html/draft-ietf-websec-key-pinning-02) to further authenticate the authenticity of communications with Mozilla's mxr.mozilla.org domain. If the public key pin for mxr.mozilla.org changes before the expiration date of the current certificate, Sslurp will being to throw an exception, and refuse to update the root CA bundle. If the public key pin changes within the final month or after the expiration date of their current certificate (November, 2013), Sslurp will begin throwing a PHP notice encouraging you to update your copy of Sslurp to get the latest pin. 26 | 27 | **You are STRONGLY ENCOURAGED to be using the latest version of Sslurp at all times.** 28 | 29 | ### CLI root CA bundle updater 30 | 31 | [./bin/update-ca-bundle](https://github.com/EvanDotPro/Sslurp/blob/master/bin/update-ca-bundle) is a handy command-line tool for fetching, building, and subsequently updating a root CA bundle in PEM format for use with PHP's OpenSSL support, curl, libcurl, php\_curl, etc. The output generated is fully compatible with the [mk-ca-bundle.pl](https://github.com/bagder/curl/blob/master/lib/mk-ca-bundle.pl) which is used to [generate cURL's trusted bundle](http://curl.haxx.se/docs/caextract.html). 32 | 33 | ``` 34 | Sslurp Root CA Bundle Updater 35 | 36 | Usage: 37 | bin/update-ca-bundle [--stdout] 38 | bin/update-ca-bundle -o[output_file] 39 | 40 | Options 41 | -o Path/filename to the file to (over)write the updated root CA bundle. Defaults to ca-bundle.pem 42 | --stdout Do not write file, send output to stdout instead. 43 | ``` 44 | 45 | ### Using Sslurp as a library 46 | 47 | In addition to the CLI tool, Sslurp can be used as a library through the OOP 48 | interface. The [source](https://github.com/EvanDotPro/Sslurp/tree/master/src/Sslurp) _is_ the API documentation. 49 | 50 | ```php 51 | isLatest()) { 57 | echo 'Your CA root bundle is up to date!' . PHP_EOL; 58 | } else { 59 | echo 'WARNING! Your CA root bundle is out of date!' . PHP_EOL 60 | . 'Local CA root bundle is version ' . $bundle->getVersion() . '. ' 61 | . 'Latest version available from Mozilla is ' . $bundle->getMozillaCertData()->getVersion() . '.' . PHP_EOL; 62 | 63 | echo 'Updating...'; 64 | $bundle->update(); 65 | echo "\tDone!" . PHP_EOL; 66 | } 67 | ``` 68 | 69 | ## Installation 70 | 71 | ### Composer / Packagist 72 | 73 | ``` 74 | ./composer.phar require evandotpro/sslurp 75 | ``` 76 | 77 | ### Normal 78 | 79 | The `./bin/update-ca-bundle` CLI tool will "just work" out of the box. 80 | 81 | Sslurp can _easily_ be used in any existing project, framework, or library. 82 | 83 | To use Sslurp as a library in your project, the easiest method is to simply 84 | include the `autoload_register.php` file: 85 | 86 | ```php 87 | require_once 'vendor/Sslurp/autoload_register.php'; 88 | ``` 89 | 90 | Alternatively, if you project supports loading classmap arrays, you may fetch 91 | the classmap without registering an additional SPL autoloader: 92 | 93 | ```php 94 | $classmap = include 'vendor/Sslurp/autoload_classmap.php'; 95 | // Register $classmap with your project's existing classmap autoloader 96 | ``` 97 | 98 | If you have an existing SPL autoloader that allows adding a callable to a stack 99 | instead of directly registering the classmap array, you have the option of 100 | simply getting a closure which can autoload the Sslurp classes: 101 | 102 | ```php 103 | $sslurpLoader = include 'vendor/Sslurp/autoload_function.php'; 104 | // $sslurpLoader is a closure that can be registered with an existing autoloader 105 | ``` 106 | 107 | ## To-Do 108 | 109 | * **Paranoia level 1000** - Test environment for the ability to call the OpenSSL executable, and if possible, make use of OCSP to _further_ verify the validity of the mxr.mozilla.org domain. 110 | 111 | ## License 112 | 113 | Sslurp is released under the BSD license. See the included LICENSE file. 114 | 115 | The generated root CA bundle file is simply a converted version of the original and as such, it is licensed under the same licenses as the Mozilla source: MPL v2.0, GPL v2.0 or LGPL 2.1. See [nss/COPYING](http://mxr.mozilla.org/mozilla/source/security/nss/COPYING?raw=1) for details. 116 | -------------------------------------------------------------------------------- /autoload_classmap.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/Module.php', 5 | 'Sslurp\Sslurp' => __DIR__ . '/src/Sslurp/Sslurp.php', 6 | 'Sslurp\AbstractCaRootData' => __DIR__ . '/src/Sslurp/AbstractCaRootData.php', 7 | 'Sslurp\MozillaCertData' => __DIR__ . '/src/Sslurp/MozillaCertData.php', 8 | 'Sslurp\CaRootPemBundle' => __DIR__ . '/src/Sslurp/CaRootPemBundle.php', 9 | 'Sslurp\X509Certificate' => __DIR__ . '/src/Sslurp/X509Certificate.php', 10 | ); 11 | -------------------------------------------------------------------------------- /autoload_function.php: -------------------------------------------------------------------------------- 1 | getUpdatedCaRootBundle(); 38 | } catch (Exception $e) {} 39 | 40 | if (empty($caBundle)) { 41 | echo "Sorry, there was an error building the latest root CA bundle.\n"; 42 | if (!empty($e)) { 43 | echo $e->getMessage() . "\n"; 44 | } 45 | exit(1); 46 | } 47 | 48 | if (isset($opts['stdout'])) { 49 | echo $caBundle; 50 | } else { 51 | echo "Updated root CA bundle written to {$opts['o']}\n"; 52 | } 53 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "evandotpro/sslurp", 3 | "description": "Sslurp is a simple library which aims to make properly dealing with SSL in PHP suck less.", 4 | "type": "library", 5 | "keywords": [ 6 | "security", 7 | "ssl" 8 | ], 9 | "homepage": "https://github.com/EvanDotPro/Sslurp", 10 | "authors": [ 11 | { 12 | "name": "Evan Coury", 13 | "email": "me@evancoury.com", 14 | "homepage": "http://blog.evan.pro/" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.3", 19 | "ext-openssl": "*" 20 | }, 21 | "autoload": { 22 | "psr-0": { 23 | "Sslurp": "src/" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /data/Equifax_Secure_CA.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV 3 | UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy 4 | dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 5 | MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx 6 | dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B 7 | AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f 8 | BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A 9 | cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC 10 | AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ 11 | MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm 12 | aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw 13 | ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj 14 | IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF 15 | MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA 16 | A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 17 | 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh 18 | 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /src/Sslurp/AbstractCaRootData.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | use DateTime; 14 | use DateTimeZone; 15 | 16 | abstract class AbstractCaRootData 17 | { 18 | /** 19 | * The CVS version ID 20 | * 21 | * @var string 22 | */ 23 | protected $version = null; 24 | 25 | /** 26 | * The date/time of the certdataversion 27 | * 28 | * @var DateTime 29 | */ 30 | protected $dateTime = null; 31 | 32 | /** 33 | * Get the version number according to CVS. 34 | * 35 | * @return string 36 | */ 37 | public function getVersion() 38 | { 39 | if ($this->version === null) { 40 | if (preg_match('/^.*\$Revision: ([\d\.]+)/m', $this->getContent('Revision:'), $match)) { 41 | $this->version = $match[1]; 42 | } else { 43 | throw new \RuntimeException('Unable to detect revision ID.'); 44 | } 45 | } 46 | 47 | return $this->version; 48 | } 49 | 50 | /** 51 | * Get the date/time the certdata was modified by Mozilla according to CVS. 52 | * 53 | * @return DateTime 54 | */ 55 | public function getDateTime() 56 | { 57 | if ($this->dateTime === null) { 58 | if (preg_match('/^.*\$Date: ([\d\/-]+\s+[\d:]+)/m', $this->getContent('Date:'), $match)) { 59 | $this->dateTime = new DateTime($match[1], new DateTimeZone('UTC')); 60 | } else { 61 | throw new \RuntimeException('Unable to detect revision date.'); 62 | } 63 | } 64 | 65 | return $this->dateTime; 66 | } 67 | 68 | abstract public function getContent($until = false); 69 | } 70 | -------------------------------------------------------------------------------- /src/Sslurp/CaRootPemBundle.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | use SplFileObject; 14 | 15 | class CaRootPemBundle extends AbstractCaRootData 16 | { 17 | /** 18 | * @var SplFileObject 19 | */ 20 | protected $fileObject = null; 21 | 22 | /** 23 | * @var string 24 | */ 25 | protected $pemContent = null; 26 | 27 | /** 28 | * @var MozillaCertData 29 | */ 30 | protected $mozCertData = null; 31 | 32 | public function __construct($filename, MozillaCertData $mozCertData = null) 33 | { 34 | if (!file_exists($filename)) { 35 | touch($filename); 36 | } 37 | $this->fileObject = new SplFileObject($filename, 'r+'); 38 | $this->mozCertData = $mozCertData ?: new MozillaCertData(); 39 | } 40 | 41 | /** 42 | * Return the content of the PEM bundle 43 | */ 44 | public function getContent($until = false) 45 | { 46 | if ($this->pemContent === null) { 47 | $this->pemContent = ''; 48 | while (!$this->fileObject->eof()) $this->pemContent .= $this->fileObject->fgets(); 49 | //$this->pemContent = $this->fileObject->getUpdatedCaRootBundle(); 50 | } 51 | 52 | if ($until) { 53 | return substr($this->pemContent, 0, strpos($this->pemContent, "\n", strpos($this->pemContent, $until))); 54 | } 55 | 56 | return $this->pemContent; 57 | } 58 | 59 | public function getMozillaCertData() 60 | { 61 | return $this->mozCertData; 62 | } 63 | 64 | public function isLatest() 65 | { 66 | return $this->getVersion() === $this->mozCertData->getVersion(); 67 | } 68 | 69 | public function getUpdatedCaRootBundle() 70 | { 71 | return $this->buildBundle($this->mozCertData->getContent()); 72 | } 73 | 74 | protected function buildBundle($rawCertData) 75 | { 76 | $rawCertData = explode("\n", $rawCertData); 77 | $caBundle = <<buildPemString($caName, $data); 131 | } 132 | } 133 | 134 | return $caBundle; 135 | } 136 | 137 | protected function buildPemString($caName, $data) 138 | { 139 | return "\n{$caName}\n" 140 | . str_repeat('=', strlen($caName)) . "\n" 141 | . "-----BEGIN CERTIFICATE-----\n" 142 | . chunk_split(base64_encode($data), 76, "\n") 143 | . "-----END CERTIFICATE-----\n"; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Sslurp/MozillaCertData.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | class MozillaCertData extends AbstractCaRootData 14 | { 15 | // mxr.mozilla.org cert expires Mar 25th, 2015 16 | const MOZILLA_MXR_SSL_PIN = 'b4e6e7a3d911b5a09a9835f525122acfa1442a3b'; 17 | const MOZILLA_MXR_SSL_EXP = 1425186000; // Mar 1st, 2015 18 | 19 | /** 20 | * certdata.txt contents 21 | * 22 | * @var string 23 | */ 24 | protected $certData = null; 25 | 26 | /** 27 | * Stream context 28 | * 29 | * @var resource 30 | */ 31 | protected $context = null; 32 | 33 | /** 34 | * Get the raw certdata.txt contents from mxr.mozilla.org 35 | * 36 | * @return string 37 | */ 38 | public function getContent($until = false) 39 | { 40 | if ($until) { 41 | // don't cache the partial fetch for version check 42 | if ($this->certData !== null) { 43 | return substr($this->certData, 0, strpos($this->certData, "\n", strpos($this->certData, $until))); 44 | } 45 | 46 | return $this->fetchLatestCertData($until); 47 | } 48 | 49 | if ($this->certData === null) { 50 | $this->certData = $this->fetchLatestCertData(); 51 | } 52 | 53 | return $this->certData; 54 | } 55 | 56 | /** 57 | * Get the stream context for the TCP connection to the server. 58 | * 59 | * If no stream context is set, will create a default one. 60 | * 61 | * @return resource 62 | */ 63 | public function getStreamContext() 64 | { 65 | if (!$this->context) { 66 | $this->context = stream_context_create(array('ssl' => array( 67 | 'capture_peer_cert' => true, 68 | 'verify_peer' => true, 69 | 'allow_self_signed' => false, 70 | 'cafile' => $this->getRootCaBundlePath(), 71 | 'CN_match' => 'mxr.mozilla.org', 72 | ))); 73 | } 74 | 75 | return $this->context; 76 | } 77 | 78 | protected function fetchLatestCertData($until = false) 79 | { 80 | $ctx = $this->getStreamContext(); 81 | 82 | set_error_handler(function ($code, $message, $filename, $lineno, $context) { 83 | throw new \ErrorException(sprintf('%s: %s in %s line %d', $code, $message, $filename, $lineno), $code, 0, $filename, $lineno); 84 | }); 85 | 86 | try { 87 | $fp = stream_socket_client('ssl://mxr.mozilla.org:443', $errNo, $errStr, 30, STREAM_CLIENT_CONNECT, $ctx); 88 | } catch (\ErrorException $e) { 89 | restore_error_handler(); 90 | throw new \RuntimeException($errStr, $errNo, $e); 91 | } 92 | 93 | restore_error_handler(); 94 | 95 | $headers = "GET /mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 HTTP/1.1\r\n"; 96 | $headers .= "Host: mxr.mozilla.org\r\n"; 97 | $headers .= "Connection: close\r\n"; 98 | $headers .= "Accept: */*\r\n"; 99 | fwrite($fp, "{$headers}\r\n"); // send request 100 | 101 | $response = ''; 102 | while (!feof($fp)) { 103 | $response .= fgets($fp); 104 | if ($until && strpos($response, $until) !== false) { 105 | break; 106 | } 107 | } 108 | fclose($fp); 109 | 110 | $params = stream_context_get_params($ctx); 111 | $cert = new X509Certificate($params['options']['ssl']['peer_certificate']); 112 | $pin = $cert->getPin(); 113 | 114 | if ($pin !== static::MOZILLA_MXR_SSL_PIN) { 115 | if (time() < static::MOZILLA_MXR_SSL_EXP) { 116 | throw new \RuntimeException(sprintf( 117 | 'ERROR: Certificate pin for mxr.mozilla.org did NOT match expected value! ' . 118 | 'Expected: %s Received: %s', static::MOZILLA_MXR_SSL_PIN, $pin 119 | )); 120 | } 121 | trigger_error('WARNING: mxr.mozilla.org certificate pin may be out of date. ' . 122 | 'If you continue to see this message after updating Sslurp, please ' . 123 | 'file an issue at https://github.com/EvanDotPro/Sslurp/issues'); 124 | } 125 | 126 | return $this->decodeChunkedString($this->getResponseBody($response)); 127 | } 128 | 129 | protected function decodeChunkedString($string) 130 | { 131 | $result = ''; 132 | $currentPos = 0; 133 | $stringLength = strlen($string); 134 | 135 | while ($currentPos < $stringLength) { 136 | $position = strpos($string, "\r\n", $currentPos); 137 | $length = hexdec(substr($string, $currentPos, $position - $currentPos)); 138 | $result .= substr($string, $position + 2, $length); 139 | $currentPos = $position + 2; 140 | } 141 | 142 | return $result; 143 | } 144 | 145 | protected function getResponseBody($string) 146 | { 147 | $lines = explode("\r\n", $string); 148 | 149 | $isHeader = true; 150 | $headers = $content = array(); 151 | 152 | while ($lines) { 153 | $nextLine = array_shift($lines); 154 | 155 | if ($isHeader && $nextLine == '') { 156 | $isHeader = false; 157 | continue; 158 | } 159 | if ($isHeader) { 160 | $headers[] = $nextLine; 161 | } else { 162 | $content[] = $nextLine; 163 | } 164 | } 165 | 166 | return implode("\r\n", $content); 167 | } 168 | 169 | protected function getRootCaBundlePath() 170 | { 171 | return Sslurp::getSystemCaRootBundlePath() ?: __DIR__ . '/../../data/Equifax_Secure_Ca.pem'; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/Sslurp/Sslurp.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | class Sslurp 14 | { 15 | /** 16 | * Sslurp version number 17 | */ 18 | const VERSION = '1.0-dev'; 19 | 20 | /** 21 | * Locate the system root CA bundle. 22 | * 23 | * @return string 24 | */ 25 | public static function getSystemCaRootBundlePath() 26 | { 27 | // If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that. 28 | // This mimics how OpenSSL uses the SSL_CERT_FILE env variable. 29 | $envCertFile = getenv('SSL_CERT_FILE'); 30 | if ($envCertFile && is_readable($envCertFile) && openssl_x509_parse(file_get_contents($envCertFile))) { 31 | // Possibly throw exception instead of ignoring SSL_CERT_FILE if it's invalid? 32 | return $envCertFile; 33 | } 34 | 35 | $caBundlePaths = array( 36 | '/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package) 37 | '/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package) 38 | '/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package) 39 | '/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package) 40 | '/usr/ssl/certs/ca-bundle.crt', // Cygwin 41 | '/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package 42 | '/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option) 43 | '/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat? 44 | ); 45 | 46 | $found = false; 47 | foreach ($caBundlePaths as $caBundle) { 48 | if (is_readable($caBundle) && openssl_x509_parse(file_get_contents($caBundle))) { 49 | $found = true; 50 | break; 51 | } 52 | } 53 | 54 | return $found ? $caBundle : false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Sslurp/X509Certificate.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | namespace Sslurp; 12 | 13 | /** 14 | * This class simply wraps an X.509 resource and exposes some handy stuff via 15 | * a friendly OOP interface. 16 | */ 17 | class X509Certificate 18 | { 19 | /** 20 | * @var X.509 resource 21 | */ 22 | protected $certificate; 23 | 24 | /** 25 | * @var string 26 | */ 27 | protected $pin; 28 | 29 | /** 30 | * @var resource 31 | */ 32 | protected $publicKey; 33 | 34 | /** 35 | * @var array 36 | */ 37 | protected $publicKeyDetails; 38 | 39 | /** 40 | * @param $certificate mixed X.509 resource, X.509 certificate string, or path to X.509 certificate file. 41 | */ 42 | public function __construct($certificate) 43 | { 44 | if (is_string($certificate)) { 45 | if (is_readable($certificate)) { 46 | $certificate = file_get_contents($certificate); 47 | } 48 | // We're surpressing errors here in favor of the more verbose exception below. 49 | $certificate = @openssl_x509_read($certificate); 50 | } 51 | 52 | if (@get_resource_type($certificate) !== 'OpenSSL X.509') { 53 | throw new \InvalidArgumentException('Argument passed to constructor' 54 | . ' of %s must be an X.509 resource, X.509 certificate string, or' 55 | . ' valid path to an X.509 certificate.'); 56 | } 57 | 58 | $this->certificate = $certificate; 59 | } 60 | 61 | /** 62 | * Get the certificate pin. 63 | * 64 | * By Kevin McArthur of StormTide Digital Studios Inc. 65 | * @KevinSMcArthur / https://github.com/StormTide 66 | * 67 | * See http://tools.ietf.org/html/draft-ietf-websec-key-pinning-02 68 | * 69 | * @return string 70 | */ 71 | public function getPin() 72 | { 73 | if ($this->pin === null) { 74 | $pubkeydetails = $this->getPublicKeyDetails(); 75 | $pubkeypem = $pubkeydetails['key']; 76 | //Convert PEM to DER before SHA1'ing 77 | $start = '-----BEGIN PUBLIC KEY-----'; 78 | $end = '-----END PUBLIC KEY-----'; 79 | $pemtrim = substr($pubkeypem, (strpos($pubkeypem, $start) + strlen($start)), (strlen($pubkeypem) - strpos($pubkeypem, $end)) * (-1)); 80 | $der = base64_decode($pemtrim); 81 | $this->pin = sha1($der); 82 | } 83 | 84 | return $this->pin; 85 | } 86 | 87 | /** 88 | * Extracts the public key from certificate and prepares it for use by other functions. 89 | * OOP alias for openssl_pkey_get_public / openssl_get_publickey. 90 | * 91 | * @return resource 'OpenSSL key' 92 | */ 93 | public function getPublicKey() 94 | { 95 | if ($this->publicKey === null) { 96 | $this->publicKey = openssl_get_publickey($this->certificate); 97 | } 98 | 99 | return $this->publicKey; 100 | } 101 | 102 | /** 103 | * This function returns the key details (bits, key, type). 104 | * 105 | * @return array 106 | */ 107 | public function getPublicKeyDetails() 108 | { 109 | if ($this->publicKeyDetails === null) { 110 | $this->publicKeyDetails = openssl_pkey_get_details($this->getPublicKey()); 111 | } 112 | 113 | return $this->publicKeyDetails; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | clover.xml 2 | -------------------------------------------------------------------------------- /test/SslurpTest/CaRootPemBundleTest.php: -------------------------------------------------------------------------------- 1 | bundle = new CaRootPemBundle(__DIR__ . '/_files/ca-bundle.pem', new MozillaCertData); 15 | } 16 | 17 | public function testBuildsCaRootBundleProperly() 18 | { 19 | $result = $this->bundle->getUpdatedCaRootBundle(); 20 | $this->assertStringEqualsFile(__DIR__ . '/_files/ca-bundle.pem', $result); 21 | } 22 | 23 | public function testCanParseVersionAndDateDataProperly() 24 | { 25 | $dateTime = new DateTime('2012/06/28 13:50:18', new DateTimeZone('UTC')); 26 | $this->assertEquals($dateTime, $this->bundle->getDateTime()); 27 | $this->assertSame('1.85', $this->bundle->getVersion()); 28 | } 29 | 30 | public function testIsLatestMethodWorks() 31 | { 32 | $this->assertTrue($this->bundle->isLatest()); 33 | $this->bundle = new CaRootPemBundle(__DIR__ . '/_files/ca-bundle.pem', new MozillaCertData); 34 | $this->bundle->setPemContent(str_replace('1.85', '1.86', $this->bundle->getContent())); 35 | $this->assertFalse($this->bundle->isLatest()); 36 | } 37 | 38 | public function testWillFetchMozillaCertData() 39 | { 40 | $this->assertNotNull($this->bundle->getContent()); 41 | $this->assertInstanceOf('Sslurp\MozillaCertData', $this->bundle->getMozillaCertData()); 42 | } 43 | 44 | public function testWillCreateBundleFileIfItDoesNotExist() 45 | { 46 | $file = sys_get_temp_dir() . '/ca-bundle.pem'; 47 | $this->assertFileNotExists($file); 48 | $this->bundle = new CaRootPemBundle($file, new MozillaCertData); 49 | $this->assertFileExists($file); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/SslurpTest/MozillaCertDataTest.php: -------------------------------------------------------------------------------- 1 | mozCertData = new MozillaCertData(); 17 | } 18 | 19 | public function tearDown() 20 | { 21 | putenv('SSL_CERT_FILE'); 22 | MozillaCertData::$allowOnlineTest = false; 23 | } 24 | 25 | public function certData() 26 | { 27 | return file_get_contents(__DIR__ . '/_files/certdata.txt'); 28 | } 29 | 30 | public function canDoOnlineTest() 31 | { 32 | if (!($fp = @stream_socket_client('tcp://mxr.mozilla.org:80', $errNo, $errStr, 1))) { 33 | $this->markTestSkipped('Could not reach mxr.mozilla.org for online test.'); 34 | } 35 | MozillaCertData::$allowOnlineTest = true; 36 | fclose($fp); 37 | } 38 | 39 | public function testContextOptionsAreSecureDefaults() 40 | { 41 | $context = $this->mozCertData->getStreamContext(); 42 | $opts = stream_context_get_options($context); 43 | $this->assertSame(true, $opts['ssl']['capture_peer_cert']); 44 | $this->assertSame(true, $opts['ssl']['verify_peer']); 45 | $this->assertSame(false, $opts['ssl']['allow_self_signed']); 46 | $this->assertRegExp('/^.+\.(pem|crt)$/', $opts['ssl']['cafile']); 47 | $this->assertSame('mxr.mozilla.org', $opts['ssl']['CN_match']); 48 | } 49 | 50 | public function testCanParseVersionAndDateDataProperly() 51 | { 52 | $dateTime = new DateTime('2012/06/28 13:50:18', new DateTimeZone('UTC')); 53 | $this->assertEquals($dateTime, $this->mozCertData->getDateTime()); 54 | $this->assertSame('1.85', $this->mozCertData->getVersion()); 55 | } 56 | 57 | public function testExpectedCertDataReturnedFromGetContent() 58 | { 59 | $this->assertEquals($this->certData(), $this->mozCertData->getContent()); 60 | } 61 | 62 | public function testExceptionThrownIfCertDataIsInvalidWhenFetchingVersion() 63 | { 64 | $this->setExpectedException('RuntimeException'); 65 | $this->mozCertData->setCertData('foo'); 66 | $this->mozCertData->getVersion(); 67 | } 68 | 69 | public function testExceptionThrownIfCertDataIsInvalidWhenFetchingDateTime() 70 | { 71 | $this->setExpectedException('RuntimeException'); 72 | $this->mozCertData->setCertData('foo'); 73 | $this->mozCertData->getDateTime(); 74 | } 75 | 76 | public function testMozillaCertDataOnlineCheck() 77 | { 78 | $this->canDoOnlineTest(); 79 | $this->assertRegExp('/^\d+\.\d+$/', $this->mozCertData->getVersion()); 80 | } 81 | 82 | public function testMozillaCertDataOnlineCheckWithInvalidPinThrowsException() 83 | { 84 | $this->canDoOnlineTest(); 85 | $this->mozCertData = new MozillaCertDataInvalidPin; 86 | $this->setExpectedException('RuntimeException'); 87 | $this->mozCertData->getVersion(); 88 | } 89 | 90 | public function testMozillaCertDataOnlineCheckWithNewCertificateShowsWarningMessage() 91 | { 92 | $this->canDoOnlineTest(); 93 | $this->mozCertData = new MozillaCertDataInvalidPinAndExp; 94 | $this->setExpectedException('PHPUnit_Framework_Error_Notice'); 95 | $this->mozCertData->getVersion(); 96 | } 97 | 98 | public function testMozillaCertDataOnlineCheckWithNewCertificateStillReturnsVersion() 99 | { 100 | $this->canDoOnlineTest(); 101 | $this->mozCertData = new MozillaCertDataInvalidPinAndExp; 102 | $this->assertRegExp('/^\d+\.\d+$/', @$this->mozCertData->getVersion()); 103 | } 104 | 105 | public function testMozillaCertDataOnlineFailsIfNoCaRootBundleFound() 106 | { 107 | $this->canDoOnlineTest(); 108 | putenv('SSL_CERT_FILE=' . __DIR__ . '/_files/mxr.mozilla.org.pem'); 109 | $this->setExpectedException('RuntimeException'); 110 | $this->mozCertData->getVersion(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/SslurpTest/SslurpTest.php: -------------------------------------------------------------------------------- 1 | assertRegExp('/^.+\.(pem|crt)$/', $caBundlePath); 15 | 16 | // Check that we can override it with some other valid bundle 17 | putenv('SSL_CERT_FILE=' . __DIR__ . '/../../data/Equifax_Secure_CA.pem'); 18 | $caBundlePath = Sslurp::getSystemCaRootBundlePath(); 19 | $this->assertSame(__DIR__ . '/../../data/Equifax_Secure_CA.pem', $caBundlePath); 20 | putenv('SSL_CERT_FILE'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/SslurpTest/TestAsset/MockCaRootPemBundle.php: -------------------------------------------------------------------------------- 1 | pemContent = $pemContent; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/SslurpTest/TestAsset/MockMozillaCertData.php: -------------------------------------------------------------------------------- 1 | certData = $certData; 14 | } 15 | 16 | protected function fetchLatestCertData($until = false) 17 | { 18 | if (static::$allowOnlineTest) { 19 | return parent::fetchLatestCertData($until); 20 | } 21 | $return = $this->certData ?: file_get_contents(__DIR__ . '/../_files/certdata.txt'); 22 | if ($until) { 23 | return substr($return, 0, strpos($return, "\n", strpos($return, $until))); 24 | } 25 | 26 | return $return; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/SslurpTest/TestAsset/MockMozillaCertDataInvalidPin.php: -------------------------------------------------------------------------------- 1 | assertSame($cert->getPin(), $expectedPin); 18 | 19 | $cert = new X509Certificate($certString); 20 | $this->assertSame($cert->getPin(), $expectedPin); 21 | 22 | $cert = new X509Certificate($certResource); 23 | $this->assertSame($cert->getPin(), $expectedPin); 24 | } 25 | 26 | public function testInvalidConstructorArgumentThrowsException() 27 | { 28 | $this->setExpectedException('InvalidArgumentException'); 29 | $cert = new X509Certificate(123); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/SslurpTest/_files/ca-bundle.pem: -------------------------------------------------------------------------------- 1 | ## 2 | ## Bundle of CA Root Certificates 3 | ## 4 | ## Generated with Sslurp (https://github.com/EvanDotPro/Sslurp) 5 | ## 6 | ## This is a bundle of X.509 certificates of public Certificate Authorities. 7 | ## These were automatically extracted from Mozilla's root certificates 8 | ## file (certdata.txt). This file can be found in the Mozilla source tree: 9 | ## /mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt 10 | ## 11 | ## http://www.mozilla.org/projects/security/certs/policy/ 12 | ## http://www.mozilla.org/projects/security/pki/nss/ 13 | ## 14 | ## This file contains the certificates in PEM format and therefore 15 | ## can be directly used with curl / libcurl / php_curl, or with 16 | ## an Apache+mod_ssl webserver for SSL client authentication. 17 | ## Just configure this file as the SSLCACertificateFile. 18 | ## 19 | # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.85 $ $Date: 2012/06/28 13:50:18 $ 20 | 21 | GTE CyberTrust Global Root 22 | ========================== 23 | -----BEGIN CERTIFICATE----- 24 | MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg 25 | Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG 26 | A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz 27 | MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL 28 | Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 29 | IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u 30 | sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql 31 | HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID 32 | AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW 33 | M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF 34 | NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ 35 | -----END CERTIFICATE----- 36 | 37 | Thawte Server CA 38 | ================ 39 | -----BEGIN CERTIFICATE----- 40 | MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT 41 | DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs 42 | dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE 43 | AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j 44 | b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV 45 | BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u 46 | c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG 47 | A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 48 | ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl 49 | /Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 50 | 1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR 51 | MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J 52 | GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ 53 | GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= 54 | -----END CERTIFICATE----- 55 | -------------------------------------------------------------------------------- /test/SslurpTest/_files/certdata.txt: -------------------------------------------------------------------------------- 1 | # 2 | # This Source Code Form is subject to the terms of the Mozilla Public 3 | # License, v. 2.0. If a copy of the MPL was not distributed with this 4 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | CVS_ID "@(#) $RCSfile: certdata.txt,v $ $Revision: 1.85 $ $Date: 2012/06/28 13:50:18 $" 6 | 7 | # 8 | # certdata.txt 9 | # 10 | # This file contains the object definitions for the certs and other 11 | # information "built into" NSS. 12 | # 13 | # Object definitions: 14 | # 15 | # Certificates 16 | # 17 | # -- Attribute -- -- type -- -- value -- 18 | # CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE 19 | # CKA_TOKEN CK_BBOOL CK_TRUE 20 | # CKA_PRIVATE CK_BBOOL CK_FALSE 21 | # CKA_MODIFIABLE CK_BBOOL CK_FALSE 22 | # CKA_LABEL UTF8 (varies) 23 | # CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 24 | # CKA_SUBJECT DER+base64 (varies) 25 | # CKA_ID byte array (varies) 26 | # CKA_ISSUER DER+base64 (varies) 27 | # CKA_SERIAL_NUMBER DER+base64 (varies) 28 | # CKA_VALUE DER+base64 (varies) 29 | # CKA_NSS_EMAIL ASCII7 (unused here) 30 | # 31 | # Trust 32 | # 33 | # -- Attribute -- -- type -- -- value -- 34 | # CKA_CLASS CK_OBJECT_CLASS CKO_TRUST 35 | # CKA_TOKEN CK_BBOOL CK_TRUE 36 | # CKA_PRIVATE CK_BBOOL CK_FALSE 37 | # CKA_MODIFIABLE CK_BBOOL CK_FALSE 38 | # CKA_LABEL UTF8 (varies) 39 | # CKA_ISSUER DER+base64 (varies) 40 | # CKA_SERIAL_NUMBER DER+base64 (varies) 41 | # CKA_CERT_HASH binary+base64 (varies) 42 | # CKA_EXPIRES CK_DATE (not used here) 43 | # CKA_TRUST_DIGITAL_SIGNATURE CK_TRUST (varies) 44 | # CKA_TRUST_NON_REPUDIATION CK_TRUST (varies) 45 | # CKA_TRUST_KEY_ENCIPHERMENT CK_TRUST (varies) 46 | # CKA_TRUST_DATA_ENCIPHERMENT CK_TRUST (varies) 47 | # CKA_TRUST_KEY_AGREEMENT CK_TRUST (varies) 48 | # CKA_TRUST_KEY_CERT_SIGN CK_TRUST (varies) 49 | # CKA_TRUST_CRL_SIGN CK_TRUST (varies) 50 | # CKA_TRUST_SERVER_AUTH CK_TRUST (varies) 51 | # CKA_TRUST_CLIENT_AUTH CK_TRUST (varies) 52 | # CKA_TRUST_CODE_SIGNING CK_TRUST (varies) 53 | # CKA_TRUST_EMAIL_PROTECTION CK_TRUST (varies) 54 | # CKA_TRUST_IPSEC_END_SYSTEM CK_TRUST (varies) 55 | # CKA_TRUST_IPSEC_TUNNEL CK_TRUST (varies) 56 | # CKA_TRUST_IPSEC_USER CK_TRUST (varies) 57 | # CKA_TRUST_TIME_STAMPING CK_TRUST (varies) 58 | # CKA_TRUST_STEP_UP_APPROVED CK_BBOOL (varies) 59 | # (other trust attributes can be defined) 60 | # 61 | 62 | # 63 | # The object to tell NSS that this is a root list and we don't 64 | # have to go looking for others. 65 | # 66 | BEGINDATA 67 | CKA_CLASS CK_OBJECT_CLASS CKO_NSS_BUILTIN_ROOT_LIST 68 | CKA_TOKEN CK_BBOOL CK_TRUE 69 | CKA_PRIVATE CK_BBOOL CK_FALSE 70 | CKA_MODIFIABLE CK_BBOOL CK_FALSE 71 | CKA_LABEL UTF8 "Mozilla Builtin Roots" 72 | 73 | # 74 | # Certificate "GTE CyberTrust Global Root" 75 | # 76 | CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE 77 | CKA_TOKEN CK_BBOOL CK_TRUE 78 | CKA_PRIVATE CK_BBOOL CK_FALSE 79 | CKA_MODIFIABLE CK_BBOOL CK_FALSE 80 | CKA_LABEL UTF8 "GTE CyberTrust Global Root" 81 | CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 82 | CKA_SUBJECT MULTILINE_OCTAL 83 | \060\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061 84 | \030\060\026\006\003\125\004\012\023\017\107\124\105\040\103\157 85 | \162\160\157\162\141\164\151\157\156\061\047\060\045\006\003\125 86 | \004\013\023\036\107\124\105\040\103\171\142\145\162\124\162\165 87 | \163\164\040\123\157\154\165\164\151\157\156\163\054\040\111\156 88 | \143\056\061\043\060\041\006\003\125\004\003\023\032\107\124\105 89 | \040\103\171\142\145\162\124\162\165\163\164\040\107\154\157\142 90 | \141\154\040\122\157\157\164 91 | END 92 | CKA_ID UTF8 "0" 93 | CKA_ISSUER MULTILINE_OCTAL 94 | \060\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061 95 | \030\060\026\006\003\125\004\012\023\017\107\124\105\040\103\157 96 | \162\160\157\162\141\164\151\157\156\061\047\060\045\006\003\125 97 | \004\013\023\036\107\124\105\040\103\171\142\145\162\124\162\165 98 | \163\164\040\123\157\154\165\164\151\157\156\163\054\040\111\156 99 | \143\056\061\043\060\041\006\003\125\004\003\023\032\107\124\105 100 | \040\103\171\142\145\162\124\162\165\163\164\040\107\154\157\142 101 | \141\154\040\122\157\157\164 102 | END 103 | CKA_SERIAL_NUMBER MULTILINE_OCTAL 104 | \002\002\001\245 105 | END 106 | CKA_VALUE MULTILINE_OCTAL 107 | \060\202\002\132\060\202\001\303\002\002\001\245\060\015\006\011 108 | \052\206\110\206\367\015\001\001\004\005\000\060\165\061\013\060 109 | \011\006\003\125\004\006\023\002\125\123\061\030\060\026\006\003 110 | \125\004\012\023\017\107\124\105\040\103\157\162\160\157\162\141 111 | \164\151\157\156\061\047\060\045\006\003\125\004\013\023\036\107 112 | \124\105\040\103\171\142\145\162\124\162\165\163\164\040\123\157 113 | \154\165\164\151\157\156\163\054\040\111\156\143\056\061\043\060 114 | \041\006\003\125\004\003\023\032\107\124\105\040\103\171\142\145 115 | \162\124\162\165\163\164\040\107\154\157\142\141\154\040\122\157 116 | \157\164\060\036\027\015\071\070\060\070\061\063\060\060\062\071 117 | \060\060\132\027\015\061\070\060\070\061\063\062\063\065\071\060 118 | \060\132\060\165\061\013\060\011\006\003\125\004\006\023\002\125 119 | \123\061\030\060\026\006\003\125\004\012\023\017\107\124\105\040 120 | \103\157\162\160\157\162\141\164\151\157\156\061\047\060\045\006 121 | \003\125\004\013\023\036\107\124\105\040\103\171\142\145\162\124 122 | \162\165\163\164\040\123\157\154\165\164\151\157\156\163\054\040 123 | \111\156\143\056\061\043\060\041\006\003\125\004\003\023\032\107 124 | \124\105\040\103\171\142\145\162\124\162\165\163\164\040\107\154 125 | \157\142\141\154\040\122\157\157\164\060\201\237\060\015\006\011 126 | \052\206\110\206\367\015\001\001\001\005\000\003\201\215\000\060 127 | \201\211\002\201\201\000\225\017\240\266\360\120\234\350\172\307 128 | \210\315\335\027\016\056\260\224\320\033\075\016\366\224\300\212 129 | \224\307\006\310\220\227\310\270\144\032\172\176\154\074\123\341 130 | \067\050\163\140\177\262\227\123\007\237\123\371\155\130\224\322 131 | \257\215\155\210\147\200\346\355\262\225\317\162\061\312\245\034 132 | \162\272\134\002\347\144\102\347\371\251\054\326\072\015\254\215 133 | \102\252\044\001\071\346\234\077\001\205\127\015\130\207\105\370 134 | \323\205\252\223\151\046\205\160\110\200\077\022\025\307\171\264 135 | \037\005\057\073\142\231\002\003\001\000\001\060\015\006\011\052 136 | \206\110\206\367\015\001\001\004\005\000\003\201\201\000\155\353 137 | \033\011\351\136\331\121\333\147\042\141\244\052\074\110\167\343 138 | \240\174\246\336\163\242\024\003\205\075\373\253\016\060\305\203 139 | \026\063\201\023\010\236\173\064\116\337\100\310\164\327\271\175 140 | \334\364\166\125\175\233\143\124\030\351\360\352\363\134\261\331 141 | \213\102\036\271\300\225\116\272\372\325\342\174\365\150\141\277 142 | \216\354\005\227\137\133\260\327\243\205\064\304\044\247\015\017 143 | \225\223\357\313\224\330\236\037\235\134\205\155\307\252\256\117 144 | \037\042\265\315\225\255\272\247\314\371\253\013\172\177 145 | END 146 | 147 | # Trust for Certificate "GTE CyberTrust Global Root" 148 | CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST 149 | CKA_TOKEN CK_BBOOL CK_TRUE 150 | CKA_PRIVATE CK_BBOOL CK_FALSE 151 | CKA_MODIFIABLE CK_BBOOL CK_FALSE 152 | CKA_LABEL UTF8 "GTE CyberTrust Global Root" 153 | CKA_CERT_SHA1_HASH MULTILINE_OCTAL 154 | \227\201\171\120\330\034\226\160\314\064\330\011\317\171\104\061 155 | \066\176\364\164 156 | END 157 | CKA_CERT_MD5_HASH MULTILINE_OCTAL 158 | \312\075\323\150\361\003\134\320\062\372\270\053\131\350\132\333 159 | END 160 | CKA_ISSUER MULTILINE_OCTAL 161 | \060\165\061\013\060\011\006\003\125\004\006\023\002\125\123\061 162 | \030\060\026\006\003\125\004\012\023\017\107\124\105\040\103\157 163 | \162\160\157\162\141\164\151\157\156\061\047\060\045\006\003\125 164 | \004\013\023\036\107\124\105\040\103\171\142\145\162\124\162\165 165 | \163\164\040\123\157\154\165\164\151\157\156\163\054\040\111\156 166 | \143\056\061\043\060\041\006\003\125\004\003\023\032\107\124\105 167 | \040\103\171\142\145\162\124\162\165\163\164\040\107\154\157\142 168 | \141\154\040\122\157\157\164 169 | END 170 | CKA_SERIAL_NUMBER MULTILINE_OCTAL 171 | \002\002\001\245 172 | END 173 | CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR 174 | CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR 175 | CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_TRUSTED_DELEGATOR 176 | CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE 177 | 178 | # 179 | # Certificate "Thawte Server CA" 180 | # 181 | CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE 182 | CKA_TOKEN CK_BBOOL CK_TRUE 183 | CKA_PRIVATE CK_BBOOL CK_FALSE 184 | CKA_MODIFIABLE CK_BBOOL CK_FALSE 185 | CKA_LABEL UTF8 "Thawte Server CA" 186 | CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 187 | CKA_SUBJECT MULTILINE_OCTAL 188 | \060\201\304\061\013\060\011\006\003\125\004\006\023\002\132\101 189 | \061\025\060\023\006\003\125\004\010\023\014\127\145\163\164\145 190 | \162\156\040\103\141\160\145\061\022\060\020\006\003\125\004\007 191 | \023\011\103\141\160\145\040\124\157\167\156\061\035\060\033\006 192 | \003\125\004\012\023\024\124\150\141\167\164\145\040\103\157\156 193 | \163\165\154\164\151\156\147\040\143\143\061\050\060\046\006\003 194 | \125\004\013\023\037\103\145\162\164\151\146\151\143\141\164\151 195 | \157\156\040\123\145\162\166\151\143\145\163\040\104\151\166\151 196 | \163\151\157\156\061\031\060\027\006\003\125\004\003\023\020\124 197 | \150\141\167\164\145\040\123\145\162\166\145\162\040\103\101\061 198 | \046\060\044\006\011\052\206\110\206\367\015\001\011\001\026\027 199 | \163\145\162\166\145\162\055\143\145\162\164\163\100\164\150\141 200 | \167\164\145\056\143\157\155 201 | END 202 | CKA_ID UTF8 "0" 203 | CKA_ISSUER MULTILINE_OCTAL 204 | \060\201\304\061\013\060\011\006\003\125\004\006\023\002\132\101 205 | \061\025\060\023\006\003\125\004\010\023\014\127\145\163\164\145 206 | \162\156\040\103\141\160\145\061\022\060\020\006\003\125\004\007 207 | \023\011\103\141\160\145\040\124\157\167\156\061\035\060\033\006 208 | \003\125\004\012\023\024\124\150\141\167\164\145\040\103\157\156 209 | \163\165\154\164\151\156\147\040\143\143\061\050\060\046\006\003 210 | \125\004\013\023\037\103\145\162\164\151\146\151\143\141\164\151 211 | \157\156\040\123\145\162\166\151\143\145\163\040\104\151\166\151 212 | \163\151\157\156\061\031\060\027\006\003\125\004\003\023\020\124 213 | \150\141\167\164\145\040\123\145\162\166\145\162\040\103\101\061 214 | \046\060\044\006\011\052\206\110\206\367\015\001\011\001\026\027 215 | \163\145\162\166\145\162\055\143\145\162\164\163\100\164\150\141 216 | \167\164\145\056\143\157\155 217 | END 218 | CKA_SERIAL_NUMBER MULTILINE_OCTAL 219 | \002\001\001 220 | END 221 | CKA_VALUE MULTILINE_OCTAL 222 | \060\202\003\023\060\202\002\174\240\003\002\001\002\002\001\001 223 | \060\015\006\011\052\206\110\206\367\015\001\001\004\005\000\060 224 | \201\304\061\013\060\011\006\003\125\004\006\023\002\132\101\061 225 | \025\060\023\006\003\125\004\010\023\014\127\145\163\164\145\162 226 | \156\040\103\141\160\145\061\022\060\020\006\003\125\004\007\023 227 | \011\103\141\160\145\040\124\157\167\156\061\035\060\033\006\003 228 | \125\004\012\023\024\124\150\141\167\164\145\040\103\157\156\163 229 | \165\154\164\151\156\147\040\143\143\061\050\060\046\006\003\125 230 | \004\013\023\037\103\145\162\164\151\146\151\143\141\164\151\157 231 | \156\040\123\145\162\166\151\143\145\163\040\104\151\166\151\163 232 | \151\157\156\061\031\060\027\006\003\125\004\003\023\020\124\150 233 | \141\167\164\145\040\123\145\162\166\145\162\040\103\101\061\046 234 | \060\044\006\011\052\206\110\206\367\015\001\011\001\026\027\163 235 | \145\162\166\145\162\055\143\145\162\164\163\100\164\150\141\167 236 | \164\145\056\143\157\155\060\036\027\015\071\066\060\070\060\061 237 | \060\060\060\060\060\060\132\027\015\062\060\061\062\063\061\062 238 | \063\065\071\065\071\132\060\201\304\061\013\060\011\006\003\125 239 | \004\006\023\002\132\101\061\025\060\023\006\003\125\004\010\023 240 | \014\127\145\163\164\145\162\156\040\103\141\160\145\061\022\060 241 | \020\006\003\125\004\007\023\011\103\141\160\145\040\124\157\167 242 | \156\061\035\060\033\006\003\125\004\012\023\024\124\150\141\167 243 | \164\145\040\103\157\156\163\165\154\164\151\156\147\040\143\143 244 | \061\050\060\046\006\003\125\004\013\023\037\103\145\162\164\151 245 | \146\151\143\141\164\151\157\156\040\123\145\162\166\151\143\145 246 | \163\040\104\151\166\151\163\151\157\156\061\031\060\027\006\003 247 | \125\004\003\023\020\124\150\141\167\164\145\040\123\145\162\166 248 | \145\162\040\103\101\061\046\060\044\006\011\052\206\110\206\367 249 | \015\001\011\001\026\027\163\145\162\166\145\162\055\143\145\162 250 | \164\163\100\164\150\141\167\164\145\056\143\157\155\060\201\237 251 | \060\015\006\011\052\206\110\206\367\015\001\001\001\005\000\003 252 | \201\215\000\060\201\211\002\201\201\000\323\244\120\156\310\377 253 | \126\153\346\317\135\266\352\014\150\165\107\242\252\302\332\204 254 | \045\374\250\364\107\121\332\205\265\040\164\224\206\036\017\165 255 | \311\351\010\141\365\006\155\060\156\025\031\002\351\122\300\142 256 | \333\115\231\236\342\152\014\104\070\315\376\276\343\144\011\160 257 | \305\376\261\153\051\266\057\111\310\073\324\047\004\045\020\227 258 | \057\347\220\155\300\050\102\231\327\114\103\336\303\365\041\155 259 | \124\237\135\303\130\341\300\344\331\133\260\270\334\264\173\337 260 | \066\072\302\265\146\042\022\326\207\015\002\003\001\000\001\243 261 | \023\060\021\060\017\006\003\125\035\023\001\001\377\004\005\060 262 | \003\001\001\377\060\015\006\011\052\206\110\206\367\015\001\001 263 | \004\005\000\003\201\201\000\007\372\114\151\134\373\225\314\106 264 | \356\205\203\115\041\060\216\312\331\250\157\111\032\346\332\121 265 | \343\140\160\154\204\141\021\241\032\310\110\076\131\103\175\117 266 | \225\075\241\213\267\013\142\230\172\165\212\335\210\116\116\236 267 | \100\333\250\314\062\164\271\157\015\306\343\263\104\013\331\212 268 | \157\232\051\233\231\030\050\073\321\343\100\050\232\132\074\325 269 | \265\347\040\033\213\312\244\253\215\351\121\331\342\114\054\131 270 | \251\332\271\262\165\033\366\102\362\357\307\362\030\371\211\274 271 | \243\377\212\043\056\160\107 272 | END 273 | -------------------------------------------------------------------------------- /test/SslurpTest/_files/mxr.mozilla.org.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEizCCA3OgAwIBAgIDAQp9MA0GCSqGSIb3DQEBBQUAMEAxCzAJBgNVBAYTAlVT 3 | MRcwFQYDVQQKEw5HZW9UcnVzdCwgSW5jLjEYMBYGA1UEAxMPR2VvVHJ1c3QgU1NM 4 | IENBMB4XDTExMTEyNzA2MTMwMloXDTEzMTEyODIxMDkwMVowgaoxKTAnBgNVBAUT 5 | IGxlYVlSUXZCMXk3VFNCdjQtbDJnREliMC1JNXJlaFVqMQswCQYDVQQGEwJVUzET 6 | MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEcMBoG 7 | A1UEChMTTW96aWxsYSBDb3Jwb3JhdGlvbjELMAkGA1UECxMCSVQxGDAWBgNVBAMT 8 | D214ci5tb3ppbGxhLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 9 | AL4hXTKNh/MvxAaPLMNEbDjeEynZthMbvsIwD6zELD9H8pjgPnRMkhE9saHH2nG3 10 | ImVxZG9qlR0tEKBrK2p58EZiaHHQzVXbghxBdT94SQy0dMbPezgwSrxF70dD/4zG 11 | 23YFKTSjxyoJ8bOopaql8MOuk5OdwJ3czNKUREXTZuZ1f/5w81ImJApmOjQ1gsfr 12 | muTMiOf7snxIIQVtmxTJzy9iU7lNujkobM9MhhXLx8jr+ulBXfUOBeYO/m7FTVob 13 | VV6FbeaokvsVW0lFGEUtR5E4oh1kciRcYP9pQz8qK/TRv1d9fauG/AVdaDuM8vR3 14 | M91owf7bJ3IIyrVTcsxwGmcCAwEAAaOCASEwggEdMB8GA1UdIwQYMBaAFEJ5VBth 15 | zVUrPmPVPEhX9Z/7Rc5KMA4GA1UdDwEB/wQEAwIEsDAdBgNVHSUEFjAUBggrBgEF 16 | BQcDAQYIKwYBBQUHAwIwGgYDVR0RBBMwEYIPbXhyLm1vemlsbGEub3JnMD0GA1Ud 17 | HwQ2MDQwMqAwoC6GLGh0dHA6Ly9ndHNzbC1jcmwuZ2VvdHJ1c3QuY29tL2NybHMv 18 | Z3Rzc2wuY3JsMB0GA1UdDgQWBBQol5biPAbmyEjlbzJ72UIvr39IzTAMBgNVHRMB 19 | Af8EAjAAMEMGCCsGAQUFBwEBBDcwNTAzBggrBgEFBQcwAoYnaHR0cDovL2d0c3Ns 20 | LWFpYS5nZW90cnVzdC5jb20vZ3Rzc2wuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQBs 21 | yNOUJuIzmdsbWTMNS4+wWxqOMn2fTjDJWcGMVD9zH6ja4EYiKKEVi0DUpmTnyNz0 22 | vcMrk4+i/RdmF7djdtBurHGr/ZYDX7kZvrpfymR62lvZWsqq+mS/btLIINkdBBJU 23 | 5RHytqMYCPDgi6dJD83N2k6L2C/PD3s173LscznW/oSDbCqV6tw4L8Hp5CODa9Xa 24 | /IDx74PZEbIIcBx0FWvXcdDlrfAIO1AKZDgCsWXYcsmB3/obF2IigzZ1HDFq7Fj+ 25 | wKxfe9mUaPfebD6Sm6L3qQMPAEOOh/bJFb+drgHASQmQcYpDbulzKv4phCiRB3Ka 26 | usZ29MD/RSr1jOjIVLtB 27 | -----END CERTIFICATE----- 28 | -------------------------------------------------------------------------------- /test/bootstrap.php: -------------------------------------------------------------------------------- 1 | 26 | * 27 | * @author Marco Pivetta 28 | * Modified for colorized output by Evan Coury 29 | */ 30 | 31 | $inputFile = $argv[1]; 32 | $percentage = min(100, max(0, (int) $argv[2])); 33 | 34 | if (!file_exists($inputFile)) { 35 | throw new InvalidArgumentException('Invalid input file provided'); 36 | } 37 | 38 | if (!$percentage) { 39 | throw new InvalidArgumentException('An integer checked percentage must be given as second parameter'); 40 | } 41 | 42 | $xml = new SimpleXMLElement(file_get_contents($inputFile)); 43 | /* @var $metrics SimpleXMLElement[] */ 44 | $metrics = $xml->xpath('//metrics'); 45 | 46 | $totalElements = 0; 47 | $checkedElements = 0; 48 | 49 | foreach ($metrics as $metric) { 50 | $totalElements += (int) $metric['elements']; 51 | $checkedElements += (int) $metric['coveredelements']; 52 | } 53 | 54 | $coverage = round(($checkedElements / $totalElements) * 100); 55 | 56 | echo PHP_EOL; 57 | 58 | if ($coverage < $percentage) { 59 | echo "\x1b[37;41m"; 60 | echo "Code coverage is {$coverage}%, which is below the accepted {$percentage}%"; 61 | echo "\x1b[0m" . PHP_EOL . PHP_EOL; 62 | exit(1); 63 | } 64 | 65 | echo "\x1b[30;42m"; 66 | echo "Code coverage is {$coverage}% - OK!"; 67 | echo "\x1b[0m" . PHP_EOL . PHP_EOL; 68 | -------------------------------------------------------------------------------- /test/cs-checker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | output=$(php php-cs-fixer.phar fix -v --no-interaction --dry-run --level=all .) 3 | echo "" 4 | if [[ $output ]]; then 5 | while read -r line 6 | do 7 | echo -e "\e[37;41m$line\e[00m" 8 | done <<< "$output" 9 | false 10 | else 11 | echo -e "\e[30;42mCoding standards are OK\e[0m" 12 | fi 13 | -------------------------------------------------------------------------------- /test/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./ 4 | 5 | 6 | 7 | 8 | ../src/ 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------