DBE-1202 support combines pem files with multiple CA certificates
authorSergey Ignatov <sergey.ignatov@jetbrains.com>
Tue, 17 Nov 2015 14:31:18 +0000 (17:31 +0300)
committerSergey Ignatov <sergey.ignatov@jetbrains.com>
Tue, 17 Nov 2015 14:33:08 +0000 (17:33 +0300)
platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java
platform/util-rt/src/com/intellij/execution/rmi/ssl/SslSocketFactory.java
platform/util-rt/src/com/intellij/security/CompositeX509TrustManager.java [new file with mode: 0644]

index a64de2e236d789a2183f4b6861afff24db854141..bf733d125ae4e312f7a6da4443c447756455806b 100644 (file)
@@ -37,12 +37,13 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings;
 import com.intellij.openapi.util.BuildNumber;
 import com.intellij.openapi.util.io.FileUtil;
 import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.security.CompositeX509TrustManager;
 import com.intellij.util.Consumer;
 import com.intellij.util.SystemProperties;
-import com.intellij.util.containers.ContainerUtil;
 import com.intellij.util.net.NetUtils;
 import com.intellij.util.net.ssl.CertificateUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.net.ssl.*;
 import java.io.*;
@@ -53,8 +54,12 @@ import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.*;
-import java.util.*;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Map;
 
 /**
  * @author stathik
@@ -65,12 +70,12 @@ public class ITNProxy {
   private static final String NEW_THREAD_POST_URL = "https://ea-report.jetbrains.com/trackerRpc/idea/createScr";
   private static final String ENCODING = "UTF8";
 
-  public static void sendError(Project project,
+  public static void sendError(@Nullable Project project,
                                final String login,
                                final String password,
-                               final ErrorBean error,
-                               final Consumer<Integer> callback,
-                               final Consumer<Exception> errback) {
+                               @NotNull final ErrorBean error,
+                               @NotNull final Consumer<Integer> callback,
+                               @NotNull final Consumer<Exception> errback) {
     if (StringUtil.isEmpty(login)) {
       return;
     }
@@ -96,13 +101,14 @@ public class ITNProxy {
     }
   }
 
+  @NotNull
   public static String getBrowseUrl(int threadId) {
     return NEW_THREAD_VIEW_URL + threadId;
   }
 
   private static SSLContext ourSslContext;
 
-  private static int postNewThread(String login, String password, ErrorBean error) throws Exception {
+  private static int postNewThread(String login, String password, @NotNull ErrorBean error) throws Exception {
     if (ourSslContext == null) {
       ourSslContext = initContext();
     }
@@ -142,7 +148,8 @@ public class ITNProxy {
     }
   }
 
-  private static Multimap<String, String> createParameters(String login, String password, ErrorBean error) {
+  @NotNull
+  private static Multimap<String, String> createParameters(String login, String password, @NotNull ErrorBean error) {
     Multimap<String, String> params = ArrayListMultimap.create(40, 1);
 
     params.put("protocol.version", "1");
@@ -201,11 +208,12 @@ public class ITNProxy {
     return params;
   }
 
-  private static String format(Calendar calendar) {
+  @Nullable
+  private static String format(@Nullable Calendar calendar) {
     return calendar == null ?  null : Long.toString(calendar.getTime().getTime());
   }
 
-  private static byte[] join(Multimap<String, String> params) throws UnsupportedEncodingException {
+  private static byte[] join(@NotNull Multimap<String, String> params) throws UnsupportedEncodingException {
     StringBuilder builder = new StringBuilder();
     for (Map.Entry<String, String> param : params.entries()) {
       if (StringUtil.isEmpty(param.getKey())) {
@@ -221,7 +229,8 @@ public class ITNProxy {
     return builder.toString().getBytes(ENCODING);
   }
 
-  private static HttpURLConnection post(URL url, byte[] bytes) throws IOException {
+  @NotNull
+  private static HttpURLConnection post(@NotNull URL url, @NotNull byte[] bytes) throws IOException {
     HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
 
     connection.setSSLSocketFactory(ourSslContext.getSocketFactory());
@@ -266,7 +275,7 @@ public class ITNProxy {
 
   private static class EaHostnameVerifier implements HostnameVerifier {
     @Override
-    public boolean verify(String hostname, SSLSession session) {
+    public boolean verify(String hostname, @NotNull SSLSession session) {
       try {
         Certificate[] certificates = session.getPeerCertificates();
         if (certificates.length > 1) {
@@ -298,46 +307,6 @@ public class ITNProxy {
     }
   }
 
-  private static class CompositeX509TrustManager implements X509TrustManager {
-    private final List<X509TrustManager> myManagers = ContainerUtil.newArrayList();
-
-    public CompositeX509TrustManager(TrustManager[]... managerSets) {
-      for (TrustManager[] set : managerSets) {
-        for (TrustManager manager : set) {
-          if (manager instanceof X509TrustManager) {
-            myManagers.add((X509TrustManager)manager);
-          }
-        }
-      }
-    }
-
-    @Override
-    public void checkClientTrusted(X509Certificate[] certificates, String s) throws CertificateException {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void checkServerTrusted(X509Certificate[] certificates, String s) throws CertificateException {
-      for (X509TrustManager manager : myManagers) {
-        try {
-          manager.checkServerTrusted(certificates, s);
-          return;
-        }
-        catch (CertificateException ignored) { }
-      }
-      throw new CertificateException("No trusting managers found for " + s);
-    }
-
-    @Override
-    public X509Certificate[] getAcceptedIssuers() {
-      List<X509Certificate> result = ContainerUtil.newArrayList();
-      for (X509TrustManager manager : myManagers) {
-        ContainerUtil.addAll(result, manager.getAcceptedIssuers());
-      }
-      return result.toArray(new X509Certificate[result.size()]);
-    }
-  }
-
   @SuppressWarnings("SpellCheckingInspection") private static final String JB_CA_CERT =
     "-----BEGIN CERTIFICATE-----\n" +
     "MIIFvjCCA6agAwIBAgIQMYHnK1dpIZVCoitWqBwhXjANBgkqhkiG9w0BAQsFADBn\n" +
index dfbdf74068be5f525e84b0870b2b9b24e14c23c8..53280d92c589af2c6d2156ac01f6f52ad75e6684 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2000-2014 JetBrains s.r.o.
+ * Copyright 2000-2015 JetBrains s.r.o.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  */
 package com.intellij.execution.rmi.ssl;
 
+import com.intellij.openapi.util.io.FileUtilRt;
 import com.intellij.openapi.util.text.StringUtilRt;
+import com.intellij.security.CompositeX509TrustManager;
+import com.intellij.util.containers.ContainerUtilRt;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.net.ssl.*;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
 import java.net.InetAddress;
 import java.net.Socket;
+import java.nio.charset.Charset;
 import java.security.*;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.List;
 import java.util.UUID;
 
 public class SslSocketFactory extends SSLSocketFactory {
@@ -35,6 +39,7 @@ public class SslSocketFactory extends SSLSocketFactory {
   public static final String SSL_CLIENT_CERT_PATH = "sslClientCertPath";
   public static final String SSL_CLIENT_KEY_PATH = "sslClientKeyPath";
   public static final String SSL_TRUST_EVERYBODY = "sslTrustEverybody";
+  private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";
   private SSLSocketFactory myFactory;
 
   public SslSocketFactory() throws GeneralSecurityException, IOException {
@@ -49,7 +54,7 @@ public class SslSocketFactory extends SSLSocketFactory {
       boolean trustEverybody = StringUtilRt.parseBoolean(System.getProperty(SSL_TRUST_EVERYBODY), false);
 
       tms = trustEverybody ? new TrustManager[]{new MyTrustEverybodyManager()} :
-            caCertPath == null ? new TrustManager[]{} : new TrustManager[]{new MyTrustManager(caCertPath)};
+            caCertPath == null ? new TrustManager[]{} : createTrustManagers(caCertPath);
       kms = clientCertPath != null && clientKeyPath != null
             ? new KeyManager[]{new MyKeyManager(clientCertPath, clientKeyPath)}
             : new KeyManager[]{};
@@ -62,6 +67,23 @@ public class SslSocketFactory extends SSLSocketFactory {
     myFactory = ctx.getSocketFactory();
   }
 
+  @NotNull
+  public static TrustManager[] createTrustManagers(@NotNull String caCertPath) throws Exception {
+    String string = FileUtilRt.loadFile(new File(caCertPath));
+    String[] tokens = string.split(END_CERTIFICATE);
+    List<TrustManager> result = ContainerUtilRt.newArrayListWithCapacity(tokens.length);
+    for (String token : tokens) {
+      if (token == null || token.trim().isEmpty()) continue;
+      result.add(new MyTrustManager(readCertificate(stringStream(token + END_CERTIFICATE))));
+    }
+    return new TrustManager[]{new CompositeX509TrustManager(result.toArray(new TrustManager[result.size()]))};
+  }
+
+  @NotNull
+  public static InputStream stringStream(@NotNull String str) {
+    return new ByteArrayInputStream(str.getBytes(Charset.forName("UTF-8")));
+  }
+
   @NotNull
   public Socket createSocket(InetAddress host, int port) throws IOException {
     return myFactory.createSocket(host, port);
@@ -98,11 +120,14 @@ public class SslSocketFactory extends SSLSocketFactory {
 
   @NotNull
   public static X509Certificate readCertificate(@NotNull String filePath) throws CertificateException, IOException {
-    CertificateFactory factory = CertificateFactory.getInstance("X.509");
-    InputStream inStream = new FileInputStream(filePath);
-    X509Certificate ca = (X509Certificate)factory.generateCertificate(inStream);
-    inStream.close();
-    return ca;
+    return readCertificate(new FileInputStream(filePath));
+  }
+
+  @NotNull
+  public static X509Certificate readCertificate(@NotNull InputStream stream) throws CertificateException, IOException {
+    X509Certificate certificate = (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(stream);
+    stream.close();
+    return certificate;
   }
 
   @NotNull
@@ -111,14 +136,11 @@ public class SslSocketFactory extends SSLSocketFactory {
   }
 
   private static class MyTrustManager implements X509TrustManager {
-    @NotNull private final String myCaCertPath;
     private X509TrustManager trustManager;
 
-    private MyTrustManager(@NotNull String caCertPath) throws Exception {
-      myCaCertPath = caCertPath;
-      KeyStore ks = createStore();
+    private MyTrustManager(@NotNull X509Certificate caCertPath) throws Exception {
       TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-      tmf.init(ks);
+      tmf.init(createStore(caCertPath));
       for (TrustManager tm : tmf.getTrustManagers()) {
         if (tm instanceof X509TrustManager) {
           trustManager = (X509TrustManager)tm;
@@ -131,11 +153,10 @@ public class SslSocketFactory extends SSLSocketFactory {
     }
 
     @NotNull
-    public KeyStore createStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException {
+    private static KeyStore createStore(@NotNull X509Certificate certificate) throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException {
       KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
       ks.load(null);
-      X509Certificate caCert = readCertificate(myCaCertPath);
-      ks.setCertificateEntry(UUID.randomUUID().toString(), caCert);
+      ks.setCertificateEntry(UUID.randomUUID().toString(), certificate);
       return ks;
     }
 
@@ -168,14 +189,15 @@ public class SslSocketFactory extends SSLSocketFactory {
 
   private static class MyKeyManager extends X509ExtendedKeyManager {
     private final String myAlias = UUID.randomUUID().toString();
-    private final X509Certificate[] myCertificates;
-    private final PrivateKey myPrivateKey;
+    @NotNull private final X509Certificate[] myCertificates;
+    @NotNull private final PrivateKey myPrivateKey;
 
     private MyKeyManager(@NotNull String certPath, @NotNull String keyPath) throws Exception {
       myCertificates = new X509Certificate[]{readCertificate(certPath)};
       myPrivateKey = readPrivateKey(keyPath);
     }
 
+    @NotNull
     public String[] getClientAliases(String s, Principal[] principals) {
       return new String[]{};
     }
@@ -184,18 +206,22 @@ public class SslSocketFactory extends SSLSocketFactory {
       return myAlias;
     }
 
+    @NotNull
     public String[] getServerAliases(String s, Principal[] principals) {
       return new String[]{};
     }
 
+    @Nullable
     public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
       return null;
     }
 
+    @NotNull
     public X509Certificate[] getCertificateChain(String s) {
       return myCertificates;
     }
 
+    @NotNull
     public PrivateKey getPrivateKey(String s) {
       return myPrivateKey;
     }
diff --git a/platform/util-rt/src/com/intellij/security/CompositeX509TrustManager.java b/platform/util-rt/src/com/intellij/security/CompositeX509TrustManager.java
new file mode 100644 (file)
index 0000000..0734768
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.security;
+
+import com.intellij.util.containers.ContainerUtilRt;
+import org.jetbrains.annotations.NotNull;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+public class CompositeX509TrustManager implements X509TrustManager {
+  private final List<X509TrustManager> myManagers = ContainerUtilRt.newArrayList();
+
+  public CompositeX509TrustManager(@NotNull TrustManager[]... managerSets) {
+    for (TrustManager[] set : managerSets) {
+      for (TrustManager manager : set) {
+        if (manager instanceof X509TrustManager) {
+          myManagers.add((X509TrustManager)manager);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void checkClientTrusted(X509Certificate[] certificates, String s) throws CertificateException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void checkServerTrusted(X509Certificate[] certificates, String s) throws CertificateException {
+    for (X509TrustManager manager : myManagers) {
+      try {
+        manager.checkServerTrusted(certificates, s);
+        return;
+      }
+      catch (CertificateException ignored) { }
+    }
+    throw new CertificateException("No trusting managers found for " + s);
+  }
+
+  @NotNull
+  @Override
+  public X509Certificate[] getAcceptedIssuers() {
+    return new X509Certificate[0];
+  }
+}