diff --git a/platform/util-rt/src/com/intellij/execution/rmi/RemoteServer.java b/platform/util-rt/src/com/intellij/execution/rmi/RemoteServer.java index 8791dfcb007d..633e17339a27 100644 --- a/platform/util-rt/src/com/intellij/execution/rmi/RemoteServer.java +++ b/platform/util-rt/src/com/intellij/execution/rmi/RemoteServer.java @@ -175,19 +175,28 @@ public class RemoteServer { private static void setupSSL() { setupDisabledAlgorithms(); - boolean caCert = System.getProperty(SslUtil.SSL_CA_CERT_PATH) != null; - boolean clientCert = System.getProperty(SslUtil.SSL_CLIENT_CERT_PATH) != null; - boolean clientKey = System.getProperty(SslUtil.SSL_CLIENT_KEY_PATH) != null; + String caCertPath = System.getProperty(SslUtil.SSL_CA_CERT_PATH); + boolean caCert = caCertPath != null; + String clientCertPath = System.getProperty(SslUtil.SSL_CLIENT_CERT_PATH); + String clientKeyPath = System.getProperty(SslUtil.SSL_CLIENT_KEY_PATH); + boolean clientKey = clientKeyPath != null; boolean deferred = "true".equals(System.getProperty(SslKeyStore.SSL_DEFERRED_KEY_LOADING)); + boolean deferredCa = "true".equals(System.getProperty(SslKeyStore.SSL_DEFERRED_CA_LOADING)); boolean useFactory = "true".equals(System.getProperty(SslUtil.SSL_USE_FACTORY)); if (useFactory) { - if (caCert || clientCert && clientKey) { + if (caCert || clientKey) { Security.setProperty("ssl.SocketFactory.provider", SslSocketFactory.class.getName()); } } else { - if (caCert) SslTrustStore.setDefault(); - if (clientCert && clientKey || deferred) SslKeyStore.setDefault(); + if (caCert || deferredCa) SslTrustStore.setDefault(); + if (clientKey || deferred) SslKeyStore.setDefault(); + } + if (caCert) { + SslTrustStore.appendUserCert("user-provided-ca", caCertPath); + } + if (clientKey) { + SslKeyStore.loadKey("user-provided-key", clientKeyPath, clientCertPath, null); } } diff --git a/platform/util-rt/src/com/intellij/execution/rmi/ssl/DelegateKeyStore.java b/platform/util-rt/src/com/intellij/execution/rmi/ssl/DelegateKeyStore.java index 3814ea6d588d..7aae1a8b3f5a 100644 --- a/platform/util-rt/src/com/intellij/execution/rmi/ssl/DelegateKeyStore.java +++ b/platform/util-rt/src/com/intellij/execution/rmi/ssl/DelegateKeyStore.java @@ -16,8 +16,16 @@ public class DelegateKeyStore extends KeyStoreSpi { Security.addProvider(this); }}; - protected final KeyStore delegate; + private final KeyStore delegate; + protected KeyStore getDelegate() { + validate(delegate); + return delegate; + } + + protected void validate(KeyStore keyStore) { + + } @SuppressWarnings("SpellCheckingInspection") static String getDefaultKeyStorePath() { @@ -42,7 +50,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { try { - return delegate.getKey(alias, password); + return getDelegate().getKey(alias, password); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -52,7 +60,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public java.security.cert.Certificate[] engineGetCertificateChain(String alias) { try { - return delegate.getCertificateChain(alias); + return getDelegate().getCertificateChain(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -62,7 +70,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public java.security.cert.Certificate engineGetCertificate(String alias) { try { - return delegate.getCertificate(alias); + return getDelegate().getCertificate(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -72,7 +80,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public Date engineGetCreationDate(String alias) { try { - return delegate.getCreationDate(alias); + return getDelegate().getCreationDate(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -81,28 +89,28 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public void engineSetKeyEntry(String alias, Key key, char[] password, java.security.cert.Certificate[] chain) throws KeyStoreException { - delegate.setKeyEntry(alias, key, password, chain); + getDelegate().setKeyEntry(alias, key, password, chain); } @Override public void engineSetKeyEntry(String alias, byte[] key, java.security.cert.Certificate[] chain) throws KeyStoreException { - delegate.setKeyEntry(alias, key, chain); + getDelegate().setKeyEntry(alias, key, chain); } @Override public void engineSetCertificateEntry(String alias, java.security.cert.Certificate cert) throws KeyStoreException { - delegate.setCertificateEntry(alias, cert); + getDelegate().setCertificateEntry(alias, cert); } @Override public void engineDeleteEntry(String alias) throws KeyStoreException { - delegate.deleteEntry(alias); + getDelegate().deleteEntry(alias); } @Override public Enumeration engineAliases() { try { - return delegate.aliases(); + return getDelegate().aliases(); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -112,7 +120,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public boolean engineContainsAlias(String alias) { try { - return delegate.containsAlias(alias); + return getDelegate().containsAlias(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -122,7 +130,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public int engineSize() { try { - return delegate.size(); + return getDelegate().size(); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -132,7 +140,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public boolean engineIsKeyEntry(String alias) { try { - return delegate.isKeyEntry(alias); + return getDelegate().isKeyEntry(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -142,7 +150,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public boolean engineIsCertificateEntry(String alias) { try { - return delegate.isCertificateEntry(alias); + return getDelegate().isCertificateEntry(alias); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -152,7 +160,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public String engineGetCertificateAlias(Certificate cert) { try { - return delegate.getCertificateAlias(cert); + return getDelegate().getCertificateAlias(cert); } catch (KeyStoreException e) { throw new IllegalStateException(e); @@ -162,7 +170,7 @@ public class DelegateKeyStore extends KeyStoreSpi { @Override public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { try { - delegate.store(stream, password); + getDelegate().store(stream, password); } catch (KeyStoreException e) { throw new IllegalStateException(e); diff --git a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslKeyStore.java b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslKeyStore.java index 447d35ff4789..094651f876b0 100644 --- a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslKeyStore.java +++ b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslKeyStore.java @@ -6,26 +6,28 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; +import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.ArrayList; +import java.util.List; public final class SslKeyStore extends DelegateKeyStore { public static final String SSL_DEFERRED_KEY_LOADING = "sslDeferredKeyLoading"; + public static final String SSL_DEFERRED_CA_LOADING = "sslDeferredCaLoading"; public static final String NAME = "idea-key-store"; - private static final Map ourAutoAdded = new LinkedHashMap<>(); + private static final List ourAdded = new ArrayList<>(); + private int myAdded; static { ourProvider.setProperty("KeyStore." + NAME, SslKeyStore.class.getName()); } public SslKeyStore() { super("PKCS12"); - loadUserCert(); } public static void setDefault() { @@ -35,40 +37,57 @@ public final class SslKeyStore extends DelegateKeyStore { } } - private static void loadUserCert() { - String certPath = System.getProperty(SslUtil.SSL_CLIENT_CERT_PATH); - String keyPath = System.getProperty(SslUtil.SSL_CLIENT_KEY_PATH); - if (certPath != null && keyPath != null) { - try { - loadKey(certPath, keyPath, null); - } - catch (Exception e) { - throw new IllegalStateException(e); - } + @Nullable + public static PrivateKey getUserKey() { + return ourAdded.isEmpty() ? null : ourAdded.get(0).key; + } + + public static void loadKey(@NotNull String alias, + @NotNull String clientKeyPath, + @Nullable String clientCertPath, + @Nullable char[] password) { + try { + PrivateKey key = SslUtil.readPrivateKey(clientKeyPath, password); + List certificates = clientCertPath == null ? null : SslUtil.loadCertificates(clientCertPath); + ourAdded.add(new KeyEntry(alias, key, certificates == null ? null : certificates.toArray(new Certificate[0]))); + } + catch (Exception e) { + throw new IllegalStateException(e); } } - public static void loadKey(@NotNull String clientCertPath, - @NotNull String clientKeyPath, - @Nullable char[] password) throws CertificateException, IOException { - PrivateKey key = SslUtil.readPrivateKey(clientKeyPath, password); - if (ourAutoAdded.containsKey(key)) return; - X509Certificate cert = SslUtil.readCertificate(clientCertPath); - ourAutoAdded.put(key, cert); + + @Override + protected void validate(KeyStore keyStore) { + super.validate(keyStore); + for (int i = myAdded, sz = ourAdded.size(); i < sz; ++i) { + KeyEntry entry = ourAdded.get(i); + if (entry.certChain != null) { + try { + keyStore.setKeyEntry(entry.alias, entry.key, null, entry.certChain); + } + catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + } + myAdded = i + 1; + } } @Override public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { - delegate.load(null, null); - int i = 0; - for (Map.Entry entry : ourAutoAdded.entrySet()) { - try { - delegate.setKeyEntry("user-provided-key" + (i == 0 ? "" : "#" + i), entry.getKey(), null, new Certificate[]{entry.getValue()}); - } - catch (KeyStoreException e) { - throw new IllegalStateException(); - } - ++i; + super.engineLoad(null, null); + } + + private static class KeyEntry { + private final String alias; + private final PrivateKey key; + private final Certificate[] certChain; + + private KeyEntry(@NotNull String alias, @NotNull PrivateKey key, @Nullable Certificate[] certChain) { + this.alias = alias; + this.key = key; + this.certChain = certChain; } } } diff --git a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslTrustStore.java b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslTrustStore.java index 461c5008292f..eb5258fa8cb8 100644 --- a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslTrustStore.java +++ b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslTrustStore.java @@ -1,17 +1,20 @@ // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.execution.rmi.ssl; -import com.intellij.openapi.util.text.StringUtilRt; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Enumeration; import java.util.List; @@ -21,6 +24,9 @@ public final class SslTrustStore extends DelegateKeyStore { ourProvider.setProperty("KeyStore." + NAME, SslTrustStore.class.getName()); } + private static final List> ourAdded = new ArrayList<>(); + private int myAdded; + public SslTrustStore() { super(KeyStore.getDefaultType()); } @@ -34,15 +40,29 @@ public final class SslTrustStore extends DelegateKeyStore { @Override public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { - delegate.load(null, null); - appendUserItems(delegate); + super.engineLoad(null, null); } - private static void appendUserCert(KeyStore keyStore, String alias, String path) { + @Override + protected void validate(KeyStore keyStore) { + super.validate(keyStore); + for (int i = myAdded, sz = ourAdded.size(); i < sz; ++i) { + Pair cert = ourAdded.get(i); + try { + keyStore.setCertificateEntry(cert.first, cert.second); + } + catch (KeyStoreException e) { + throw new IllegalStateException(e); + } + myAdded = i + 1; + } + } + + public static void appendUserCert(@NotNull String alias, @NotNull String path) { try { List certs = SslUtil.loadCertificates(path); for (int i = 0; i < certs.size(); i++) { - keyStore.setCertificateEntry(i == 0 ? alias : alias + "-" + i, certs.get(i)); + ourAdded.add(Pair.create(i == 0 ? alias : alias + "-" + i, certs.get(i))); } } catch (Exception e) { @@ -50,7 +70,7 @@ public final class SslTrustStore extends DelegateKeyStore { } } - private static void appendUserTrustStore(KeyStore keyStore, String path, char[] password) { + public static void appendUserTrustStore(@NotNull String path, char[] password) { try { File file = new File(path); if (!file.exists()) return; @@ -62,28 +82,11 @@ public final class SslTrustStore extends DelegateKeyStore { String alias = aliases.nextElement(); Certificate certificate = tmpStore.getCertificate(alias); if (certificate == null) continue; - keyStore.setCertificateEntry(alias, certificate); + ourAdded.add(Pair.create(alias, certificate));; } } catch (Exception e) { throw new IllegalStateException(e); } } - - private static void appendUserItems(KeyStore keyStore) { - String userCert = System.getProperty(SslUtil.SSL_CA_CERT_PATH); - if (!StringUtilRt.isEmpty(userCert)) { - appendUserCert(keyStore, "user-provided-ca", userCert); - } - String trustStores = System.getProperty(SslUtil.SSL_TRUST_STORE_PATHS); - if (!StringUtilRt.isEmpty(trustStores)) { - String[] storesPaths = trustStores.split(File.pathSeparator); - for (String path : storesPaths) { - String pass = "changeit"; - char[] chars = new char[pass.length()]; - pass.getChars(0, pass.length(), chars, 0); - appendUserTrustStore(keyStore, path, chars); - } - } - } } diff --git a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslUtil.java b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslUtil.java index d4d5c5355f1f..ce4b9bce123b 100644 --- a/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslUtil.java +++ b/platform/util-rt/src/com/intellij/execution/rmi/ssl/SslUtil.java @@ -13,30 +13,44 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public final class SslUtil { - public static final String SSL_TRUST_STORE_PATHS = "sslTrustStorePaths"; public static final String SSL_CA_CERT_PATH = "sslCaCertPath"; 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"; public static final String SSL_USE_FACTORY = "sslUseFactory"; - private static final String END_CERTIFICATE = "-----END CERTIFICATE-----"; + private static final String BEGIN_MARK = "-----BEGIN"; @NotNull public static List loadCertificates(@NotNull String caCertPath) throws IOException, CertificateException { String string = FileUtilRt.loadFile(new File(caCertPath)); - String[] tokens = string.split(END_CERTIFICATE); - List certs = new ArrayList<>(tokens.length); + List certs = new ArrayList<>(); + List tokens = splitBundle(string); for (String token : tokens) { - if (token == null || token.trim().length() == 0) continue; - certs.add(readCertificate(stringStream(token + END_CERTIFICATE))); + if (token == null || token.trim().isEmpty()) continue; + certs.add(readCertificate(stringStream(token))); } return certs; } + private static List splitBundle(@NotNull String string) { + int idx = string.indexOf(BEGIN_MARK); + if (idx == -1) { + return Collections.singletonList(string); + } + List res = new ArrayList<>(); + while (idx != -1) { + int endIdx = string.indexOf(BEGIN_MARK, idx + BEGIN_MARK.length()); + res.add(string.substring(idx, endIdx == -1 ? string.length() : endIdx)); + idx = endIdx; + } + return res; + } + @NotNull public static InputStream stringStream(@NotNull String str) { return new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));