ssl: populate storages through RMI calls

GitOrigin-RevId: e89a0fd55dd2b6aecb67b76b4003c4d4d3d2ee6f
This commit is contained in:
Alexander.Kass
2023-04-28 12:59:40 +00:00
committed by intellij-monorepo-bot
parent 7ee469273a
commit b65849248d
5 changed files with 136 additions and 83 deletions
@@ -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);
}
}
@@ -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<String> 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);
@@ -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<PrivateKey, X509Certificate> ourAutoAdded = new LinkedHashMap<>();
private static final List<KeyEntry> 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<X509Certificate> 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<PrivateKey, X509Certificate> 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;
}
}
}
@@ -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<Pair<String, ? extends Certificate>> 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<String, ? extends Certificate> 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<X509Certificate> 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);
}
}
}
}
@@ -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<X509Certificate> loadCertificates(@NotNull String caCertPath)
throws IOException, CertificateException {
String string = FileUtilRt.loadFile(new File(caCertPath));
String[] tokens = string.split(END_CERTIFICATE);
List<X509Certificate> certs = new ArrayList<>(tokens.length);
List<X509Certificate> certs = new ArrayList<>();
List<String> 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<String> splitBundle(@NotNull String string) {
int idx = string.indexOf(BEGIN_MARK);
if (idx == -1) {
return Collections.singletonList(string);
}
List<String> 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));