Commit 9f987b1a authored by Mustafa Emre Acer's avatar Mustafa Emre Acer Committed by Commit Bot

[Lookalikes] Ignore signed exchange cache URLs if they are lookalikes

This CL detects and allows signed exchange cache URLs even if they are
lookalikes.

Signed Exchange cache URLs (the initial URL that redirects to the
SXG) serve application/signed-exchange Content-Type response header.
In this case, the navigation will always either fail or redirect to
the primary URL (getting the content either from inside the SXG or the
network depending on whether the validations succeed).

Bug: 1110151
Change-Id: I5a15c0248402e563bb34b9643c483d80770bd121
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2441276
Commit-Queue: Mustafa Emre Acer <meacer@chromium.org>
Reviewed-by: default avatarJoe DeBlasio <jdeblasio@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818052}
parent 8c487e7e
......@@ -30,6 +30,7 @@ LookalikeUrlBlockingPage::LookalikeUrlBlockingPage(
const GURL& request_url,
ukm::SourceId source_id,
LookalikeUrlMatchType match_type,
bool is_signed_exchange,
std::unique_ptr<
security_interstitials::SecurityInterstitialControllerClient>
controller_client)
......@@ -39,7 +40,8 @@ LookalikeUrlBlockingPage::LookalikeUrlBlockingPage(
std::move(controller_client)),
safe_url_(safe_url),
source_id_(source_id),
match_type_(match_type) {
match_type_(match_type),
is_signed_exchange_(is_signed_exchange) {
controller()->metrics_helper()->RecordUserDecision(MetricsHelper::SHOW);
controller()->metrics_helper()->RecordUserInteraction(
MetricsHelper::TOTAL_VISITS);
......
......@@ -32,6 +32,7 @@ class LookalikeUrlBlockingPage
const GURL& request_url,
ukm::SourceId source_id,
LookalikeUrlMatchType match_type,
bool is_signed_exchange,
std::unique_ptr<
security_interstitials::SecurityInterstitialControllerClient>
controller);
......@@ -42,6 +43,8 @@ class LookalikeUrlBlockingPage
security_interstitials::SecurityInterstitialPage::TypeID GetTypeForTesting()
override;
bool is_signed_exchange_for_testing() const { return is_signed_exchange_; }
protected:
// SecurityInterstitialPage implementation:
void CommandReceived(const std::string& command) override;
......@@ -59,6 +62,9 @@ class LookalikeUrlBlockingPage
const GURL safe_url_;
ukm::SourceId source_id_;
LookalikeUrlMatchType match_type_;
// True if the throttle encountered a response with
// is_signed_exchange_inner_response flag. Only checked in tests.
const bool is_signed_exchange_;
DISALLOW_COPY_AND_ASSIGN(LookalikeUrlBlockingPage);
};
......
......@@ -143,8 +143,9 @@ ThrottleCheckResult LookalikeUrlNavigationThrottle::ShowInterstitial(
web_contents, url, safe_url);
std::unique_ptr<LookalikeUrlBlockingPage> blocking_page(
new LookalikeUrlBlockingPage(web_contents, safe_url, url, source_id,
match_type, std::move(controller)));
new LookalikeUrlBlockingPage(
web_contents, safe_url, url, source_id, match_type,
handle->IsSignedExchangeInnerResponse(), std::move(controller)));
base::Optional<std::string> error_page_contents =
blocking_page->GetHTMLContents();
......@@ -225,6 +226,28 @@ ThrottleCheckResult LookalikeUrlNavigationThrottle::PerformChecks(
first_is_lookalike = false;
}
// Allow signed exchange cache URLs such as
// https://example-com.site.test/package.sxg.
// Navigation throttles see signed exchanges as a redirect chain where
// Url 0: Cache URL (i.e. outer URL)
// Url 1: URL of the sgx package
// Url 2: Inner URL (the URL whose contents the sgx package contains)
//
// We want to allow lookalike cache URLs but not lookalike inner URLs, so we
// make an exception for this condition.
// TODO(meacer): Confirm that the assumption about cache URL being the 1st
// and inner URL being the last URL in the redirect chain is correct.
//
// Note that the signed exchange logic can still redirect the initial
// navigation to the fallback URL even if SGX checks fail (invalid cert,
// missing headers etc, see crbug.com/874323 for an example). Such navigations
// are not considered SGX navigations and IsSignedExchangeInnerResponse()
// will return false. We treat such navigations as simple redirects.
if (first_is_lookalike &&
navigation_handle()->IsSignedExchangeInnerResponse()) {
first_is_lookalike = false;
}
if (!first_is_lookalike && !last_is_lookalike) {
return content::NavigationThrottle::PROCEED;
}
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/bind.h"
#include "base/path_service.h"
#include "base/strings/pattern.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
......@@ -28,20 +29,26 @@
#include "chrome/test/base/ui_test_utils.h"
#include "components/lookalikes/core/features.h"
#include "components/lookalikes/core/lookalike_url_util.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/security_interstitials/content/security_interstitial_page.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "components/security_interstitials/core/metrics_helper.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_mock_cert_verifier.h"
#include "content/public/test/signed_exchange_browser_test_helper.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/cert/mock_cert_verifier.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/cert_test_util.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/network/public/cpp/network_switches.h"
#include "ui/base/window_open_disposition.h"
namespace {
......@@ -281,7 +288,8 @@ class LookalikeUrlNavigationThrottleBrowserTest
Browser* browser,
const GURL& navigated_url,
const GURL& expected_suggested_url,
NavigationSuggestionEvent expected_event) {
NavigationSuggestionEvent expected_event,
bool expect_signed_exchange = false) {
base::HistogramTester histograms;
history::HistoryService* const history_service =
......@@ -290,6 +298,14 @@ class LookalikeUrlNavigationThrottleBrowserTest
ui_test_utils::WaitForHistoryToLoad(history_service);
LoadAndCheckInterstitialAt(browser, navigated_url);
if (expect_signed_exchange) {
LookalikeUrlBlockingPage* interstitial =
static_cast<LookalikeUrlBlockingPage*>(GetCurrentInterstitial(
browser->tab_strip_model()->GetActiveWebContents()));
EXPECT_TRUE(interstitial->is_signed_exchange_for_testing());
}
SendInterstitialCommandSync(browser,
SecurityInterstitialCommand::CMD_DONT_PROCEED);
EXPECT_EQ(expected_suggested_url,
......@@ -1293,18 +1309,56 @@ IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest,
embedded_test_server()->GetURL("example.net", "/"));
}
scoped_refptr<net::X509Certificate> LoadCertificate() {
constexpr char kCertFileName[] = "prime256v1-sha256-google-com.public.pem";
base::ScopedAllowBlockingForTesting allow_io;
base::FilePath dir_path;
base::PathService::Get(content::DIR_TEST_DATA, &dir_path);
dir_path = dir_path.Append(FILE_PATH_LITERAL("sxg"));
return net::CreateCertificateChainFromFile(
dir_path, kCertFileName, net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
}
// Tests for Signed Exchanges.
class LookalikeUrlNavigationThrottleSignedExchangeBrowserTest
: public LookalikeUrlNavigationThrottleBrowserTest {
public:
void SetUpOnMainThread() override {
LookalikeUrlNavigationThrottleSignedExchangeBrowserTest() {
net::EmbeddedTestServer::RegisterTestCerts();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// HTTPS server only serves a valid cert for localhost, so this is needed
// to load pages from other hosts without an error.
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
mock_cert_verifier_.SetUpCommandLine(command_line);
}
void SetUpInProcessBrowserTestFixture() override {
mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
}
void TearDownInProcessBrowserTestFixture() override {
mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
}
void SetUp() override {
sxg_test_helper_.SetUp();
LookalikeUrlNavigationThrottleBrowserTest::SetUp();
}
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
embedded_test_server()->RegisterRequestMonitor(base::BindRepeating(
void SetUpOnMainThread() override {
https_server_.AddDefaultHandlers(
base::FilePath(FILE_PATH_LITERAL("content/test/data")));
https_server_.ServeFilesFromSourceDirectory("content/test/data");
https_server_.RegisterRequestMonitor(base::BindRepeating(
&LookalikeUrlNavigationThrottleSignedExchangeBrowserTest::
MonitorRequest,
base::Unretained(this)));
ASSERT_TRUE(https_server_.Start());
LookalikeUrlNavigationThrottleBrowserTest::SetUpOnMainThread();
}
......@@ -1319,8 +1373,34 @@ class LookalikeUrlNavigationThrottleSignedExchangeBrowserTest
return it->second.find("application/signed-exchange") != std::string::npos;
}
void InstallMockCert() {
sxg_test_helper_.InstallMockCert(mock_cert_verifier_.mock_cert_verifier());
// Make the MockCertVerifier treat the certificate
// "prime256v1-sha256-google-com.public.pem" as valid for
// "google-com.example.org".
scoped_refptr<net::X509Certificate> original_cert = LoadCertificate();
net::CertVerifyResult dummy_result;
dummy_result.verified_cert = original_cert;
dummy_result.cert_status = net::OK;
dummy_result.ocsp_result.response_status = net::OCSPVerifyResult::PROVIDED;
dummy_result.ocsp_result.revocation_status =
net::OCSPRevocationStatus::GOOD;
mock_cert_verifier_.mock_cert_verifier()->AddResultForCertAndHost(
original_cert, "google-com.example.org", dummy_result, net::OK);
}
void InstallMockCertChainInterceptor() {
sxg_test_helper_.InstallMockCertChainInterceptor();
sxg_test_helper_.InstallUrlInterceptor(
GURL("https://google-com.example.org/cert.msg"),
"content/test/data/sxg/google-com.example.org.public.pem.cbor");
}
protected:
net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
content::SignedExchangeBrowserTestHelper sxg_test_helper_;
content::ContentMockCertVerifier mock_cert_verifier_;
private:
void MonitorRequest(const net::test_server::HttpRequest& request) {
......@@ -1337,7 +1417,8 @@ class LookalikeUrlNavigationThrottleSignedExchangeBrowserTest
INSTANTIATE_TEST_SUITE_P(
All,
LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
testing::Combine(testing::Bool(), testing::Bool()));
testing::Combine(testing::Bool() /* target_embedding_enabled */,
testing::Bool() /* punycode_interstitial_enabled */));
// Navigates to a 127.0.0.1 URL that serves a signed exchange for
// google-com.example.org. This navigation should be blocked by the target
......@@ -1346,47 +1427,117 @@ INSTANTIATE_TEST_SUITE_P(
// code). Testing an ETLD+1 such as googlé.com would require generating a custom
// cert.
IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
SignedExchange_ShouldBlockTarget) {
InnerUrlIsLookalike_ShouldBlock) {
if (!target_embedding_enabled()) {
return;
}
InstallMockCert();
InstallMockCertChainInterceptor();
sxg_test_helper_.InstallUrlInterceptor(
GURL("https://google-com.example.org/test/"),
"content/test/data/sxg/fallback.html");
const GURL kNavigatedUrl =
embedded_test_server()->GetURL("/sxg/google-com.example.org_test.sxg");
https_server_.GetURL("/sxg/google-com.example.org_test.sxg");
const GURL kExpectedSuggestedUrl("https://google.com");
TestMetricsRecordedAndInterstitialShown(
browser(), kNavigatedUrl, kExpectedSuggestedUrl,
NavigationSuggestionEvent::kMatchTargetEmbedding);
NavigationSuggestionEvent::kMatchTargetEmbedding,
true /* expect_signed_exchange */);
// Check that the SXG file was handled as a Signed Exchange.
ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kNavigatedUrl));
}
// Navigates to a lookalike URL that serves a signed exchange for
// test.example.org. This should also be blocked by the lookalike interstitial,
// even though the URL that serves the signed exchange is never visible to
// the user.
// Navigates to a lookalike URL (google-com.test.com) that serves a signed
// exchange for test.example.org. This should not be blocked.
IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
SignedExchange_ShouldBlockCacheUrl) {
OuterUrlIsLookalike_ShouldNotBlock) {
if (!target_embedding_enabled()) {
return;
}
InstallMockCert();
InstallMockCertChainInterceptor();
const GURL kSgxTargetUrl("https://test.example.org/test/");
sxg_test_helper_.InstallUrlInterceptor(kSgxTargetUrl,
"content/test/data/sxg/fallback.html");
const GURL kNavigatedUrl = embedded_test_server()->GetURL(
const GURL kNavigatedUrl = https_server_.GetURL(
"google-com.test.com", "/sxg/test.example.org_test.sxg");
const GURL kExpectedSuggestedUrl =
embedded_test_server()->GetURL("google.com", "/");
TestInterstitialNotShown(browser(), kNavigatedUrl);
// Check that the SXG file was handled as a Signed Exchange.
// MonitorRequest() sees kNavigatedUrl with an IP address instead of
// domain name, so check it instead.
const GURL kResolvedNavigatedUrl =
https_server_.GetURL("/sxg/test.example.org_test.sxg");
ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
}
// Navigates to a lookalike URL (google-com.test.com) that serves a signed
// exchange for test.example.org. This should not be blocked.
IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
OuterUrlIsLookalikeButNotSignedExchange_ShouldNotBlock) {
if (!target_embedding_enabled()) {
return;
}
InstallMockCert();
InstallMockCertChainInterceptor();
const GURL kSgxTargetUrl("https://test.example.org/test/");
sxg_test_helper_.InstallUrlInterceptor(kSgxTargetUrl,
"content/test/data/sxg/fallback.html");
const GURL kSgxCacheUrl = https_server_.GetURL(
"google-com.test.com", "/sxg/test.example.org_test.sxg");
const GURL kNavigatedUrl = embedded_test_server()->GetURL(
"apple-com.site.com", "/server-redirect?" + kSgxCacheUrl.spec());
TestInterstitialNotShown(browser(), kNavigatedUrl);
// Check that the SXG file was handled as a Signed Exchange.
// MonitorRequest() sees kNavigatedUrl with an IP address instead of
// domain name, so check it instead.
const GURL kResolvedNavigatedUrl =
https_server_.GetURL("/sxg/test.example.org_test.sxg");
ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
}
// Navigates to a lookalike URL (google-com.test.com) that serves a signed
// exchange for google-com.example.org.
// Both the outer URL (i.e. cache) and the inner URL are lookalikes so this
// should be blocked.
IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleSignedExchangeBrowserTest,
InnerAndOuterUrlsAreLookalikes_ShouldBlock) {
if (!target_embedding_enabled()) {
return;
}
InstallMockCert();
InstallMockCertChainInterceptor();
sxg_test_helper_.InstallUrlInterceptor(
GURL("https://google-com.example.org/test/"),
"content/test/data/sxg/fallback.html");
const GURL kNavigatedUrl = https_server_.GetURL(
"google-com.test.com", "/sxg/google-com.example.org_test.sxg");
const GURL kExpectedSuggestedUrl("https://google.com");
TestMetricsRecordedAndInterstitialShown(
browser(), kNavigatedUrl, kExpectedSuggestedUrl,
NavigationSuggestionEvent::kMatchTargetEmbedding);
NavigationSuggestionEvent::kMatchTargetEmbedding,
true /* expect_signed_exchange */);
// Check that no SXG response was handled.
ASSERT_FALSE(HadSignedExchangeInAcceptHeader(kNavigatedUrl));
ASSERT_FALSE(HadSignedExchangeInAcceptHeader(kSgxTargetUrl));
// Check that the SXG file was handled as a Signed Exchange.
// MonitorRequest() sees kNavigatedUrl with an IP address instead of
// domain name, so check it instead.
const GURL kResolvedNavigatedUrl =
https_server_.GetURL("/sxg/google-com.example.org_test.sxg");
ASSERT_TRUE(HadSignedExchangeInAcceptHeader(kResolvedNavigatedUrl));
}
// TODO(meacer): Add a test for a failed SGX response. It should be treated
// as a normal redirect. In fact, InnerAndOuterUrlsLookalikes_ShouldBlock
// is actually testing this right now, fix it.
......@@ -273,7 +273,7 @@ std::unique_ptr<LookalikeUrlBlockingPage> CreateLookalikeInterstitialPage(
}
return std::make_unique<LookalikeUrlBlockingPage>(
web_contents, safe_url, request_url, ukm::kInvalidSourceId,
LookalikeUrlMatchType::kNone,
LookalikeUrlMatchType::kNone, false,
std::make_unique<LookalikeUrlControllerClient>(web_contents, request_url,
safe_url));
}
......
......@@ -24,6 +24,11 @@ openssl ecparam -out prime256v1.key -name prime256v1 -genkey
openssl req -new -sha256 -key prime256v1.key -out prime256v1-sha256.csr \
-subj '/CN=test.example.org/O=Test/C=US'
openssl req -new -sha256 -key prime256v1.key -out \
prime256v1-sha256-google-com.csr \
-subj '/CN=google-com.example.org/O=Test/C=US'
# Generate a certificate whose validity period starts at 2019-06-01 and
# valid for 90 days.
openssl ca -batch \
......@@ -34,6 +39,17 @@ openssl ca -batch \
-in prime256v1-sha256.csr \
-out prime256v1-sha256.public.pem
# Generate a certificate whose validity period starts at 2019-06-01 and
# valid for 90 days. Same as above, but for google-com.example.org.
openssl ca -batch \
-config google-com-ca.cnf \
-extensions sxg_cert \
-startdate 190601000000Z \
-enddate 190830000000Z \
-in prime256v1-sha256-google-com.csr \
-out prime256v1-sha256-google-com.public.pem
# Generate a certificate without CanSignHttpExchangesDraft extension.
openssl ca -batch \
-config ca.cnf \
......
......@@ -33,6 +33,11 @@ echo -n OCSP >$tmpdir/ocsp; echo -n SCT >$sctdir/dummy.sct
gen-certurl -pem prime256v1-sha256.public.pem \
-ocsp $tmpdir/ocsp -sctDir $sctdir > test.example.org.public.pem.cbor
# Same as above, but for google-com.example.org.
gen-certurl -pem prime256v1-sha256-google-com.public.pem \
-ocsp $tmpdir/ocsp -sctDir $sctdir > google-com.example.org.public.pem.cbor
# Generate the certificate chain of "*.example.org", whose validity period is
# more than 90 days.
gen-certurl -pem prime256v1-sha256-validity-too-long.public.pem \
......@@ -72,8 +77,8 @@ gen-signedexchange \
-uri https://google-com.example.org/test/ \
-status 200 \
-content test.html \
-certificate prime256v1-sha256.public.pem \
-certUrl https://cert.example.org/cert.msg \
-certificate prime256v1-sha256-google-com.public.pem \
-certUrl https://google-com.example.org/cert.msg \
-validityUrl https://google-com.example.org/resource.validity.msg \
-privateKey prime256v1.key \
-date $signature_date \
......
# This file is same as ca.cnf with the exception of subjectAltName field.
[ca]
default_ca = CA_root
preserve = yes
[CA_root]
dir = out
new_certs_dir = $dir
database = $dir/index.txt
serial = $dir/serial
certificate = ../../../../net/data/ssl/certificates/root_ca_cert.pem
private_key = ../../../../net/data/ssl/certificates/root_ca_cert.pem
default_md = sha256
unique_subject = no
policy = policy_anything
[sxg_cert]
basicConstraints = CA:FALSE
# OID required for sxg since d54c469
1.3.6.1.4.1.11129.2.1.22 = ASN1:NULL
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
subjectAltName=DNS:google-com.example.org
[policy_anything]
# Default signing policy
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = optional
emailAddress = optional
HTTP/1.1 200 OK
Content-Type: application/cert-chain+cbor
-----BEGIN CERTIFICATE REQUEST-----
MIH4MIGfAgEAMD0xHzAdBgNVBAMMFmdvb2dsZS1jb20uZXhhbXBsZS5vcmcxDTAL
BgNVBAoMBFRlc3QxCzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
QgAElFfdVfWVnYyDSfXmIkjMokHtKd1SLpMcJiV1gzYsTABuyq5PUhaghqpelLbG
vTBUEYH8BupWh2DdIF01fR3Kw6AAMAoGCCqGSM49BAMCA0gAMEUCIQC4wI2Lo4CN
/8MDiD14faGKqizZZURk1wNln5qQ7ZhLngIgZ3XkkcNcemHTc5srzamFFNKJU2L3
zpJjorgaC+H3rn8=
-----END CERTIFICATE REQUEST-----
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, ST=California, L=Mountain View, O=Test CA, CN=Test Root CA
Validity
Not Before: Jun 1 00:00:00 2019 GMT
Not After : Aug 30 00:00:00 2019 GMT
Subject: CN=google-com.example.org, O=Test, C=US
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:94:57:dd:55:f5:95:9d:8c:83:49:f5:e6:22:48:
cc:a2:41:ed:29:dd:52:2e:93:1c:26:25:75:83:36:
2c:4c:00:6e:ca:ae:4f:52:16:a0:86:aa:5e:94:b6:
c6:bd:30:54:11:81:fc:06:ea:56:87:60:dd:20:5d:
35:7d:1d:ca:c3
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
1.3.6.1.4.1.11129.2.1.22:
..
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Subject Key Identifier:
CF:65:B9:9B:EE:0E:EC:8E:A6:B9:69:96:82:20:F4:F7:8B:8A:B2:FA
X509v3 Authority Key Identifier:
keyid:9B:26:0B:8A:98:A9:BB:1D:B9:1F:1C:E3:1A:40:33:ED:8E:17:88:AB
X509v3 Subject Alternative Name:
DNS:google-com.example.org
Signature Algorithm: sha256WithRSAEncryption
ae:16:c3:1d:a9:b1:53:86:22:ee:90:e6:07:e6:03:c1:a1:10:
79:98:32:d9:61:31:f2:27:5c:6a:a2:80:c6:0b:d1:40:16:06:
65:5e:b9:72:e1:77:e1:a9:02:bf:b9:18:56:8a:24:0f:b7:84:
94:d7:53:51:e6:9f:51:fd:7e:fe:d2:64:9e:73:d0:97:2d:2f:
ad:64:41:28:ef:9e:e7:86:ca:18:04:39:7d:7d:b1:9f:3c:20:
f4:44:5b:a0:d4:00:28:bf:8c:c2:50:c9:c8:5e:cf:d5:1c:37:
98:8e:2e:d8:81:71:43:79:77:60:6b:85:01:34:14:70:73:33:
1d:df:6e:2e:30:b5:99:ff:0c:ac:82:b5:23:c2:f4:8c:8a:e0:
53:f2:f4:3f:cb:18:78:3c:b3:f4:f9:41:e3:d4:83:75:24:c8:
b6:16:15:d7:36:d1:06:a3:9a:0b:59:6b:cd:e4:05:8e:25:d8:
1f:44:bf:30:20:3b:92:dd:66:74:3b:e6:d2:91:0c:5a:81:ac:
d1:9d:3f:9e:fe:cd:31:a0:36:40:58:6f:01:01:f5:9c:f7:ab:
81:91:3f:d3:f1:3c:29:a3:47:a0:60:71:55:86:8c:15:3e:9e:
0c:54:45:04:4c:10:33:36:09:c5:88:56:3a:8e:78:3b:dc:5d:
43:5a:cd:84
-----BEGIN CERTIFICATE-----
MIIC9jCCAd6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzET
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
A1UECgwHVGVzdCBDQTEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTE5MDYwMTAw
MDAwMFoXDTE5MDgzMDAwMDAwMFowPTEfMB0GA1UEAwwWZ29vZ2xlLWNvbS5leGFt
cGxlLm9yZzENMAsGA1UECgwEVGVzdDELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIB
BggqhkjOPQMBBwNCAASUV91V9ZWdjINJ9eYiSMyiQe0p3VIukxwmJXWDNixMAG7K
rk9SFqCGql6Utsa9MFQRgfwG6laHYN0gXTV9HcrDo4GlMIGiMAkGA1UdEwQCMAAw
EAYKKwYBBAHWeQIBFgQCBQAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMB0GA1UdDgQWBBTPZbmb7g7sjqa5aZaCIPT3i4qy+jAfBgNVHSMEGDAWgBSb
JguKmKm7HbkfHOMaQDPtjheIqzAhBgNVHREEGjAYghZnb29nbGUtY29tLmV4YW1w
bGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQCuFsMdqbFThiLukOYH5gPBoRB5mDLZ
YTHyJ1xqooDGC9FAFgZlXrly4XfhqQK/uRhWiiQPt4SU11NR5p9R/X7+0mSec9CX
LS+tZEEo757nhsoYBDl9fbGfPCD0RFug1AAov4zCUMnIXs/VHDeYji7YgXFDeXdg
a4UBNBRwczMd324uMLWZ/wysgrUjwvSMiuBT8vQ/yxh4PLP0+UHj1IN1JMi2FhXX
NtEGo5oLWWvN5AWOJdgfRL8wIDuS3WZ0O+bSkQxagazRnT+e/s0xoDZAWG8BAfWc
96uBkT/T8Twpo0egYHFVhowVPp4MVEUETBAzNgnFiFY6jng73F1DWs2E
-----END CERTIFICATE-----
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment