diff --git a/community-tests/src/tests/testGroups.properties b/community-tests/src/tests/testGroups.properties
index 96075ed68f28..5941b527969f 100644
--- a/community-tests/src/tests/testGroups.properties
+++ b/community-tests/src/tests/testGroups.properties
@@ -2,3 +2,4 @@
[DISABLED_TESTS]
org.jetbrains.idea.svn.*
org.jetbrains.idea.svn16.*
+com.intellij.util.net.ssl.*
diff --git a/platform/platform-api/src/com/intellij/util/net/ssl/ConfirmingTrustManager.java b/platform/platform-api/src/com/intellij/util/net/ssl/ConfirmingTrustManager.java
index 82dd36e7f41e..bb80da46e476 100644
--- a/platform/platform-api/src/com/intellij/util/net/ssl/ConfirmingTrustManager.java
+++ b/platform/platform-api/src/com/intellij/util/net/ssl/ConfirmingTrustManager.java
@@ -350,6 +350,34 @@ public class ConfirmingTrustManager extends ClientOnlyTrustManager {
}
}
+ /**
+ * Check that underlying trust store contains certificate with specified alias.
+ *
+ * @param alias - certificate's alias to be checked
+ * @return - whether certificate is in storage
+ */
+ public boolean containsCertificate(@NotNull String alias) {
+ myReadLock.lock();
+ try {
+ return myKeyStore.containsAlias(alias);
+ }
+ catch (KeyStoreException e) {
+ LOG.error(e);
+ return false;
+ } finally {
+ myReadLock.unlock();
+ }
+ }
+
+ boolean removeAllCertificates() {
+ for (X509Certificate certificate : getCertificates()) {
+ if (!removeCertificate(certificate)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public void checkServerTrusted(X509Certificate[] certificates, String s) throws CertificateException {
myReadLock.lock();
diff --git a/platform/platform-tests/platform-tests.iml b/platform/platform-tests/platform-tests.iml
index a2fe52638ffd..d1018688da69 100644
--- a/platform/platform-tests/platform-tests.iml
+++ b/platform/platform-tests/platform-tests.iml
@@ -22,6 +22,7 @@
+
diff --git a/platform/platform-tests/testData/certificates/ca.crt b/platform/platform-tests/testData/certificates/ca.crt
new file mode 100644
index 000000000000..d258c4e4009a
--- /dev/null
+++ b/platform/platform-tests/testData/certificates/ca.crt
@@ -0,0 +1,56 @@
+-----BEGIN CERTIFICATE-----
+MIIKGTCCBgGgAwIBAgIJAMs2bK7YyVSbMA0GCSqGSIb3DQEBBQUAMIGiMQswCQYD
+VQQGEwJSVTEPMA0GA1UECAwGUnVzc2lhMRYwFAYDVQQHDA1TdC5QZXRlcnNidXJn
+MRswGQYDVQQKDBJDZXJ0aWZpY2F0ZXMgVGVzdHMxHjAcBgNVBAsMFUNlcnRpZmlj
+YXRlIEF1dGhvcml0eTEtMCsGA1UEAwwkY2VydGlmaWNhdGVzLXRlc3RzLmxhYnMu
+aW50ZWxsaWoubmV0MB4XDTE0MDIxMzE1MzkyOFoXDTI0MDIxMTE1MzkyOFowgaIx
+CzAJBgNVBAYTAlJVMQ8wDQYDVQQIDAZSdXNzaWExFjAUBgNVBAcMDVN0LlBldGVy
+c2J1cmcxGzAZBgNVBAoMEkNlcnRpZmljYXRlcyBUZXN0czEeMBwGA1UECwwVQ2Vy
+dGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDDCRjZXJ0aWZpY2F0ZXMtdGVzdHMu
+bGFicy5pbnRlbGxpai5uZXQwggQiMA0GCSqGSIb3DQEBAQUAA4IEDwAwggQKAoIE
+AQCnFJ7EhraRT/sleDkR9fkHL7UT+ae3DSB5nJcc5sELv+5PirL2rYpwGtbdr4LX
+bgsACCk2iRKlVdCA4RRhMBuPm7XQ88Wtj2cMV8FUcl0xrAhg27vIAViLsLNf5vFV
+An+FUBfjxy++mMlYEFJXXILEE7TyfW1CHMBhn+2S0IBIvBjWDeWacvKrq/WUJ2qm
+BQJrvubRL1MtXaUOt8naYgiLLZRFmcy4/id8rxA+7+nX02hckibQXPfEiEzYKB9c
+zRN4fctYIAsVBY7dx6dDlysRPPzenBkd7ym3EB1ZBDobVJRb8cfct2ysl/C9B71t
+FP9X7gfzSmm/vYdQw3EsIUMJQDWdiEmHAP+Bj9aOftVvi6I8OSuHr9SB7W52vgo6
+o+aYrKmCQGMs98WF8XjOsDa4T0MH4e0lUIZ5YuITlH6rG0/iM8GhuJJKaF9XJSia
+Osj6YdkgUYSoK1ZYZwtLUJ30u2N7AvhQ0xRZbSjZCLFfA7eu3V9B4hBL4q+u97+M
+CuqUje+KurrQKZ4XVnfiPNMjXocx3i1KULP9jW09ZAa1sGyv3sCETkP+dbbhcF5X
+WA2Is+33zj452lX5PoCIkTHZtVTbixnJ64DsBX3YwfZFtwHH/wDwRWtxnwWNXGIq
+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
+-----END CERTIFICATE-----
diff --git a/platform/platform-tests/testSrc/com/intellij/util/net/ssl/CertificateTest.java b/platform/platform-tests/testSrc/com/intellij/util/net/ssl/CertificateTest.java
new file mode 100644
index 000000000000..9a544b7158f8
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/util/net/ssl/CertificateTest.java
@@ -0,0 +1,120 @@
+package com.intellij.util.net.ssl;
+
+import com.intellij.testFramework.PlatformTestCase;
+import com.intellij.testFramework.PlatformTestUtil;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+
+/**
+ * @author Mikhail Golubev
+ */
+public class CertificateTest extends PlatformTestCase {
+ @NonNls private static final String AUTHORITY_CN = "certificates-tests.labs.intellij.net";
+
+ @NonNls private static final String TRUSTED_CERT_CN = "trusted.certificates-tests.labs.intellij.net";
+ @NonNls private static final String EXPIRED_CERT_CN = "expired.certificates-tests.labs.intellij.net";
+ @NonNls private static final String SELF_SIGNED_CERT_CN = "self-signed.certificates-tests.labs.intellij.net";
+
+ // this is the only type of certificates, which 'Common Name' field doesn't match URL of server, where it's located
+ @NonNls private static final String WRONG_HOSTNAME_CERT_CN = "illegal.certificates-tests.labs.intellij.net";
+ @NonNls private static final String WRONG_HOSTNAME_CERT_URL = "https://wrong-hostname.certificates-tests.labs.intellij.net";
+
+ private CloseableHttpClient myClient;
+ private ConfirmingTrustManager.MutableTrustManager myTrustManager;
+
+
+ /**
+ * Test that expired certificate doesn't pass JSSE timestamp check and hence untrusted and added explicitly, although
+ * issued by our test CA.
+ */
+ public void testExpiredCertificate() throws Exception {
+ doTest(EXPIRED_CERT_CN, true);
+ }
+
+ /**
+ * Test that self-signed certificate, that wasn't issued by out test CA, is untrusted and thus added explicitly.
+ */
+ public void testSelfSignedCertificate() throws Exception {
+ doTest(SELF_SIGNED_CERT_CN, true);
+ }
+
+ /**
+ * Hostname validity check (see {@link org.apache.http.conn.ssl.X509HostnameVerifier}) is disabled for now, so
+ * it merely tests that even certificate with illegal CN field (i.e. it doesn't match requested URL).
+ * is trusted, because issued by our test CA.
+ */
+ public void testWrongHostnameCertificate() throws Exception {
+ // wrong hostname doesn't lead to any warning by now, thus it's treated the same as trusted certificate
+ doTest(WRONG_HOSTNAME_CERT_URL, WRONG_HOSTNAME_CERT_CN, false);
+ }
+
+ /**
+ * Test that certificate with correct hostname, validity terms and issued by our test CA is trusted.
+ */
+ public void testTrustedCertificate() throws Exception {
+ doTest(TRUSTED_CERT_CN, false);
+ }
+
+
+ private void doTest(@NonNls String alias, boolean willBeAdded) throws Exception {
+ doTest("https://" + alias, alias, willBeAdded);
+ }
+
+ private void doTest(@NotNull String url, @NotNull String alias, boolean added) throws Exception {
+ CloseableHttpResponse response = myClient.execute(new HttpGet(url));
+ try {
+ assertEquals(response.getStatusLine().getStatusCode(), HttpStatus.SC_OK);
+ }
+ finally {
+ response.close();
+ }
+ if (added) {
+ assertTrue(myTrustManager.containsCertificate(alias));
+ assertEquals(2, myTrustManager.getCertificates().size());
+ }
+ else {
+ // only CA certificate
+ assertEquals(1, myTrustManager.getCertificates().size());
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ CertificatesManager certificatesManager = CertificatesManager.getInstance();
+ myClient = HttpClientBuilder.create()
+ .setSslcontext(certificatesManager.getSslContext())
+ .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
+ .build();
+
+ // add CA certificate
+ myTrustManager = certificatesManager.getCustomTrustManager();
+ assertTrue(myTrustManager.addCertificate(getTestDataPath() + "certificates/ca.crt"));
+ assertTrue(myTrustManager.containsCertificate(AUTHORITY_CN));
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ try {
+ assertTrue(myTrustManager.removeAllCertificates());
+ assertEmpty(myTrustManager.getCertificates());
+ }
+ finally {
+ myClient.close();
+ }
+ super.tearDown();
+ }
+
+ private static String getTestDataPath() {
+ return PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + "/platform/platform-tests/testData/";
+ }
+}