From 727de7bb11052abfa85a8e9d71bf9c65efdc2e64 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 00:52:56 +1000 Subject: [PATCH 01/10] Initial support for parsing common/alt names This appears to follow both the specification and browser behaviour, but this should be double-checked before merge. --- library/Requests/Transport/fsockopen.php | 77 ++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index 30e5a50a8..f1a0bb383 100755 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -47,6 +47,7 @@ public function request($url, $headers = array(), $data = array(), $options = ar $url_parts = parse_url($url); $host = $url_parts['host']; $context = stream_context_create(); + $verifyname = false; // HTTPS support if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { @@ -55,7 +56,10 @@ public function request($url, $headers = array(), $data = array(), $options = ar $context_options = array( 'verify_peer' => true, + // 'CN_match' => $host, + 'capture_peer_cert' => true ); + $verifyname = true; // SNI, if enabled (OpenSSL >=0.9.8j) if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) { @@ -73,6 +77,10 @@ public function request($url, $headers = array(), $data = array(), $options = ar } } + if (isset($options['verifyname']) && $options['verifyname'] === false) { + $verifyname = false; + } + stream_context_set_option($context, array('ssl' => $context_options)); } else { @@ -88,6 +96,10 @@ public function request($url, $headers = array(), $data = array(), $options = ar $fp = stream_socket_client($remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context); restore_error_handler(); + if ($verifyname) { + $this->verify_certificate_from_context($host, $context); + } + if (!$fp) { if ($errno === 0) { // Connection issue @@ -316,6 +328,71 @@ public function connect_error_handler($errno, $errstr) { return true; } + /** + * Verify the certificate against common name and subject alternative names + * + * Unfortunately, PHP doesn't check the certificate against the alternative + * names, leading things like 'https://www.github.com/' to be invalid. + * Instead + * + * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 + * @param [type] $host [description] + * @param [type] $context [description] + * @return [type] [description] + */ + public function verify_certificate_from_context($host, $context) { + $meta = stream_context_get_params($context); + if (empty($meta['options']) || empty($meta['options']['ssl']) || empty($meta['options']['ssl']['peer_certificate'])) { + throw new Requests_Exception('SSL certificate was not given; check that the host is valid', 'fsockopen.ssl.no_params'); + } + + $cert = openssl_x509_parse($meta['options']['ssl']['peer_certificate']); + + // Calculate the valid wildcard match if the host is not an IP address + $parts = explode('.', $host); + if (ip2long($host) === false) { + $parts[0] = '*'; + } + $wildcard = implode('.', $parts); + + $has_dns_alt = false; + + // Check the subjectAltName + if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) { + $altnames = explode(',', $cert['extensions']['subjectAltName']); + foreach ($altnames as $altname) { + $altname = trim($altname); + if (strpos($altname, 'DNS:') !== 0) + continue; + + $has_dns_alt = true; + + // Strip the 'DNS:' prefix and trim whitespace + $altname = trim(substr($altname, 4)); + + if ($host === $altname) { + return true; + } + elseif ($wildcard === $altname) { + return true; + } + } + } + + // Fall back to checking the common name if we didn't get any dNSName + // alt names, as per RFC2818 + if (!$has_dns_alt && !empty($cert['subject']['CN'])) { + if ($host === $cert['subject']['CN']) { + return true; + } + elseif ($wildcard === $cert['subject']['CN']) { + return true; + } + } + + throw new Requests_Exception('SSL certificate did not match the requested domain name', 'fsockopen.ssl.no_match'); + } + /** * Whether this transport is valid * From d72e19ca1c4ad91f1dc42a5d850bca5edb87bb7e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 01:27:22 +1000 Subject: [PATCH 02/10] Use stream_context_get_options() for 5.2 support --- library/Requests/Transport/fsockopen.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index f1a0bb383..4b65128ef 100755 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -341,12 +341,15 @@ public function connect_error_handler($errno, $errstr) { * @return [type] [description] */ public function verify_certificate_from_context($host, $context) { - $meta = stream_context_get_params($context); - if (empty($meta['options']) || empty($meta['options']['ssl']) || empty($meta['options']['ssl']['peer_certificate'])) { + $meta = stream_context_get_options($context); + + // If we don't have SSL options, then we couldn't make the connection at + // all + if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { throw new Requests_Exception('SSL certificate was not given; check that the host is valid', 'fsockopen.ssl.no_params'); } - $cert = openssl_x509_parse($meta['options']['ssl']['peer_certificate']); + $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); // Calculate the valid wildcard match if the host is not an IP address $parts = explode('.', $host); From 59c01405db040d6453221b7b18c34f2b39371148 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 01:27:48 +1000 Subject: [PATCH 03/10] Use a more descriptive SSL failure error --- library/Requests/Transport/fsockopen.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index 4b65128ef..c125174f7 100755 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -346,7 +346,7 @@ public function verify_certificate_from_context($host, $context) { // If we don't have SSL options, then we couldn't make the connection at // all if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { - throw new Requests_Exception('SSL certificate was not given; check that the host is valid', 'fsockopen.ssl.no_params'); + throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.ssl.connect_error'); } $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); From af873c1872847f9d3e00ea96bcaa6787217c1342 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 01:28:15 +1000 Subject: [PATCH 04/10] Test expired and revoked SSL certificates --- tests/Transport/Base.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Transport/Base.php b/tests/Transport/Base.php index d140019eb..96a6d7d7e 100755 --- a/tests/Transport/Base.php +++ b/tests/Transport/Base.php @@ -410,6 +410,20 @@ public function testHTTPS() { $this->assertEmpty($result['args']); } + /** + * @expectedException Requests_Exception + */ + public function testExpiredHTTPS() { + $request = Requests::get('https://testssl-expire.disig.sk/index.en.html', array(), $this->getOptions()); + } + + /** + * @expectedException Requests_Exception + */ + public function testRevokedHTTPS() { + $request = Requests::get('https://testssl-revoked.disig.sk/index.en.html', array(), $this->getOptions()); + } + /** * @expectedException Requests_Exception */ From 9087c6b4e40e6b9a0576e3a33463d0d5ea27d46e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 01:38:07 +1000 Subject: [PATCH 05/10] Document certificate verification method params --- library/Requests/Transport/fsockopen.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Requests/Transport/fsockopen.php b/library/Requests/Transport/fsockopen.php index c125174f7..b3706a3d5 100755 --- a/library/Requests/Transport/fsockopen.php +++ b/library/Requests/Transport/fsockopen.php @@ -336,9 +336,12 @@ public function connect_error_handler($errno, $errstr) { * Instead * * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 - * @param [type] $host [description] - * @param [type] $context [description] - * @return [type] [description] + * + * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) + * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) + * @param string $host Host name to verify against + * @param resource $context Stream context + * @return bool */ public function verify_certificate_from_context($host, $context) { $meta = stream_context_get_options($context); From 7c3c9676b54c565d258f5d57c4b993773183e465 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 02:08:14 +1000 Subject: [PATCH 06/10] Add SNI, SAN and invalid domain tests --- tests/Transport/Base.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/Transport/Base.php b/tests/Transport/Base.php index 96a6d7d7e..f6b3f7cfd 100755 --- a/tests/Transport/Base.php +++ b/tests/Transport/Base.php @@ -424,6 +424,43 @@ public function testRevokedHTTPS() { $request = Requests::get('https://testssl-revoked.disig.sk/index.en.html', array(), $this->getOptions()); } + /** + * Test that SSL fails with a bad certificate + * + * This is defined as invalid by + * https://onlinessl.netlock.hu/en/test-center/invalid-ssl-certificate.html + * and is used in testing in PhantomJS. That said, expect this to break. + * + * @expectedException Requests_Exception + */ + public function testBadDomain() { + $request = Requests::get('https://tv.eurosport.com/', array(), $this->getOptions()); + } + + /** + * Test that the transport supports Server Name Indication with HTTPS + * + * sni.velox.ch is used for SNI testing, and the common name is set to + * `*.sni.velox.ch` as such. Without alternate name support, this will fail + * as `sni.velox.ch` is only in the alternate name + */ + public function testAlternateNameSupport() { + $request = Requests::get('https://sni.velox.ch/', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + } + + /** + * Test that the transport supports Server Name Indication with HTTPS + * + * sni.velox.ch is used for SNI testing, and the common name is set to + * `*.sni.velox.ch` as such. Without SNI support, this will fail. Also tests + * our wildcard support. + */ + public function testSNISupport() { + $request = Requests::get('https://abc.sni.velox.ch/', array(), $this->getOptions()); + $this->assertEquals(200, $request->status_code); + } + /** * @expectedException Requests_Exception */ From 4502755b2886c4a1aa533f90ffb96312d681012b Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Mon, 12 Aug 2013 02:26:23 +1000 Subject: [PATCH 07/10] Mark SSL tests as skipped if we don't have support --- tests/Transport/Base.php | 32 ++++++++++++++++++++++++++++++++ tests/Transport/fsockopen.php | 7 +++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/tests/Transport/Base.php b/tests/Transport/Base.php index f6b3f7cfd..dbda8e2a7 100755 --- a/tests/Transport/Base.php +++ b/tests/Transport/Base.php @@ -1,6 +1,8 @@ $this->transport @@ -402,6 +404,11 @@ public function testBadIP() { } public function testHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://httpbin.org/get', array(), $this->getOptions()); $this->assertEquals(200, $request->status_code); @@ -414,6 +421,11 @@ public function testHTTPS() { * @expectedException Requests_Exception */ public function testExpiredHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://testssl-expire.disig.sk/index.en.html', array(), $this->getOptions()); } @@ -421,6 +433,11 @@ public function testExpiredHTTPS() { * @expectedException Requests_Exception */ public function testRevokedHTTPS() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://testssl-revoked.disig.sk/index.en.html', array(), $this->getOptions()); } @@ -434,6 +451,11 @@ public function testRevokedHTTPS() { * @expectedException Requests_Exception */ public function testBadDomain() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://tv.eurosport.com/', array(), $this->getOptions()); } @@ -445,6 +467,11 @@ public function testBadDomain() { * as `sni.velox.ch` is only in the alternate name */ public function testAlternateNameSupport() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://sni.velox.ch/', array(), $this->getOptions()); $this->assertEquals(200, $request->status_code); } @@ -457,6 +484,11 @@ public function testAlternateNameSupport() { * our wildcard support. */ public function testSNISupport() { + if ($this->skip_https) { + $this->markTestSkipped('SSL support is not available.'); + return; + } + $request = Requests::get('https://abc.sni.velox.ch/', array(), $this->getOptions()); $this->assertEquals(200, $request->status_code); } diff --git a/tests/Transport/fsockopen.php b/tests/Transport/fsockopen.php index 24fa33b28..55a717a4c 100755 --- a/tests/Transport/fsockopen.php +++ b/tests/Transport/fsockopen.php @@ -3,12 +3,11 @@ class RequestsTest_Transport_fsockopen extends RequestsTest_Transport_Base { protected $transport = 'Requests_Transport_fsockopen'; - public function testHTTPS() { + protected $skip_https = false; + protected function setUp() { // If OpenSSL isn't loaded, this should fail if (!defined('OPENSSL_VERSION_NUMBER')) { - $this->setExpectedException('Requests_Exception'); + $this->skip_https = true; } - - return parent::testHTTPS(); } } From 0c2fe6e24e7ec34117b3bbd3d227046920c92c86 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Fri, 4 Oct 2013 17:17:24 +1000 Subject: [PATCH 08/10] Split SSL handling into a separate class This makes it easier to test, as it means we don't need to test against live APIs. --- library/Requests.php | 21 ++++ library/Requests/SSL.php | 151 +++++++++++++++++++++++ library/Requests/Transport/fsockopen.php | 50 +------- 3 files changed, 177 insertions(+), 45 deletions(-) create mode 100755 library/Requests/SSL.php diff --git a/library/Requests.php b/library/Requests.php index 7a7626908..275b84479 100755 --- a/library/Requests.php +++ b/library/Requests.php @@ -728,4 +728,25 @@ protected static function compatible_gzinflate($gzData) { return false; } } + + public static function match_domain($host, $reference) { + // Check for a direct match + if ($host === $reference) { + return true; + } + + // Calculate the valid wildcard match if the host is not an IP address + // Also validates that the host has 3 parts or more, as per Firefox's + // ruleset. + $parts = explode('.', $host); + if (ip2long($host) === false && count($parts) >= 3) { + $parts[0] = '*'; + $wildcard = implode('.', $parts); + if ($wildcard === $reference) { + return true; + } + } + + return false; + } } diff --git a/library/Requests/SSL.php b/library/Requests/SSL.php new file mode 100755 index 000000000..1ddd894ab --- /dev/null +++ b/library/Requests/SSL.php @@ -0,0 +1,151 @@ +verify_certificate_from_context($host, $context); + if (!$this->verify_certificate_from_context($host, $context)) { + throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); + } } if (!$fp) { @@ -349,54 +351,12 @@ public function verify_certificate_from_context($host, $context) { // If we don't have SSL options, then we couldn't make the connection at // all if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { - throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.ssl.connect_error'); + throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error'); } $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); - // Calculate the valid wildcard match if the host is not an IP address - $parts = explode('.', $host); - if (ip2long($host) === false) { - $parts[0] = '*'; - } - $wildcard = implode('.', $parts); - - $has_dns_alt = false; - - // Check the subjectAltName - if (!empty($cert['extensions']) && !empty($cert['extensions']['subjectAltName'])) { - $altnames = explode(',', $cert['extensions']['subjectAltName']); - foreach ($altnames as $altname) { - $altname = trim($altname); - if (strpos($altname, 'DNS:') !== 0) - continue; - - $has_dns_alt = true; - - // Strip the 'DNS:' prefix and trim whitespace - $altname = trim(substr($altname, 4)); - - if ($host === $altname) { - return true; - } - elseif ($wildcard === $altname) { - return true; - } - } - } - - // Fall back to checking the common name if we didn't get any dNSName - // alt names, as per RFC2818 - if (!$has_dns_alt && !empty($cert['subject']['CN'])) { - if ($host === $cert['subject']['CN']) { - return true; - } - elseif ($wildcard === $cert['subject']['CN']) { - return true; - } - } - - throw new Requests_Exception('SSL certificate did not match the requested domain name', 'fsockopen.ssl.no_match'); + return Requests_SSL::verify_certificate($host, $cert); } /** From 13281c68edef7c0ea59e61bf2833990e07db319e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Fri, 4 Oct 2013 17:18:35 +1000 Subject: [PATCH 09/10] Add tests for SSL handling --- tests/SSL.php | 108 +++++++++++++++++++++++++++++++++++++++++ tests/phpunit.xml.dist | 1 + 2 files changed, 109 insertions(+) create mode 100755 tests/SSL.php diff --git a/tests/SSL.php b/tests/SSL.php new file mode 100755 index 000000000..62bb998df --- /dev/null +++ b/tests/SSL.php @@ -0,0 +1,108 @@ +assertTrue(Requests_SSL::match_domain($base, $dnsname)); + } + + /** + * @dataProvider domainNoMatchProvider + */ + public function testNoMatch($base, $dnsname) { + $this->assertFalse(Requests_SSL::match_domain($base, $dnsname)); + } + + protected function fakeCertificate($dnsname, $with_san = true) { + $certificate = array( + 'subject' => array( + 'CN' => $dnsname + ), + ); + + if ($with_san !== false) { + // If SAN is set to true, default it to the dNSName + if ($with_san === true) { + $with_san = $dnsname; + } + $certificate['extensions'] = array( + 'subjectAltName' => 'DNS: ' . $with_san, + ); + } + + return $certificate; + } + + /** + * @dataProvider domainMatchProvider + */ + public function testMatchViaCertificate($base, $dnsname) { + $certificate = $this->fakeCertificate($dnsname); + $this->assertTrue(Requests_SSL::verify_certificate($base, $certificate)); + } + + /** + * @dataProvider domainNoMatchProvider + */ + public function testNoMatchViaCertificate($base, $dnsname) { + $certificate = $this->fakeCertificate($dnsname); + $this->assertFalse(Requests_SSL::verify_certificate($base, $certificate)); + } + + public function testCNFallback() { + $certificate = $this->fakeCertificate('example.com', false); + $this->assertTrue(Requests_SSL::verify_certificate('example.com', $certificate)); + } + + public function testInvalidCNFallback() { + $certificate = $this->fakeCertificate('example.com', false); + $this->assertFalse(Requests_SSL::verify_certificate('example.net', $certificate)); + } + + /** + * Test a certificate with both CN and SAN fields + * + * As per RFC2818, if the SAN field exists, we should parse that and ignore + * the value of the CN field. + * + * @link http://tools.ietf.org/html/rfc2818#section-3.1 + */ + public function testIgnoreCNWithSAN() { + $certificate = $this->fakeCertificate('example.net', 'example.com'); + + $this->assertTrue(Requests_SSL::verify_certificate('example.com', $certificate), 'Checking SAN validation'); + $this->assertFalse(Requests_SSL::verify_certificate('example.net', $certificate), 'Checking CN non-validation'); + } +} diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 3236d04ec..3242b765b 100755 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -13,6 +13,7 @@ IRI.php Requests.php Response/Headers.php + SSL.php From e91c06b8da8a6bdd7859b40adf236c67f8244ec3 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Sun, 6 Oct 2013 19:53:11 +1000 Subject: [PATCH 10/10] Move EE root CA to the top of the file Fixes an obscure bug with OpenSSL 0.9.8j and lower. Props @dd32, see http://core.trac.wordpress.org/changeset/25569 for prior art. --- library/Requests/Transport/cacert.pem | 48 +++++++++++++-------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/library/Requests/Transport/cacert.pem b/library/Requests/Transport/cacert.pem index 3cf964e8b..56ece1a03 100755 --- a/library/Requests/Transport/cacert.pem +++ b/library/Requests/Transport/cacert.pem @@ -16,6 +16,30 @@ # @(#) $RCSfile: certdata.txt,v $ $Revision: 1.87 $ $Date: 2012/12/29 16:32:45 $ +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + GTE CyberTrust Global Root ========================== -----BEGIN CERTIFICATE----- @@ -3528,27 +3552,3 @@ ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== -----END CERTIFICATE----- - -EE Certification Centre Root CA -=============================== ------BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG -EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy -dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw -MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB -UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy -ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM -TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 -rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw -93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN -P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ -MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF -BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj -xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM -lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u -uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU -3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM -dcGWxZ0= ------END CERTIFICATE-----