package org.apache.logging.log4j.core.net.ssl;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.status.StatusLogger;

@Plugin(
   name = "Ssl",
   category = "Core",
   printObject = true
)
public class SslConfiguration {
   private static final StatusLogger LOGGER = StatusLogger.getLogger();
   private final KeyStoreConfiguration keyStoreConfig;
   private final TrustStoreConfiguration trustStoreConfig;
   private final SSLContext sslContext;
   private final String protocol;

   private SslConfiguration(String protocol, KeyStoreConfiguration keyStoreConfig, TrustStoreConfiguration trustStoreConfig) {
      this.keyStoreConfig = keyStoreConfig;
      this.trustStoreConfig = trustStoreConfig;
      this.protocol = protocol == null ? "SSL" : protocol;
      this.sslContext = this.createSslContext();
   }

   public SSLSocketFactory getSslSocketFactory() {
      return this.sslContext.getSocketFactory();
   }

   public SSLServerSocketFactory getSslServerSocketFactory() {
      return this.sslContext.getServerSocketFactory();
   }

   private SSLContext createSslContext() {
      SSLContext context = null;

      try {
         context = this.createSslContextBasedOnConfiguration();
         LOGGER.debug("Creating SSLContext with the given parameters");
      } catch (TrustStoreConfigurationException var3) {
         context = this.createSslContextWithTrustStoreFailure();
      } catch (KeyStoreConfigurationException var4) {
         context = this.createSslContextWithKeyStoreFailure();
      }

      return context;
   }

   private SSLContext createSslContextWithTrustStoreFailure() {
      SSLContext context;
      try {
         context = this.createSslContextWithDefaultTrustManagerFactory();
         LOGGER.debug("Creating SSLContext with default truststore");
      } catch (KeyStoreConfigurationException var3) {
         context = this.createDefaultSslContext();
         LOGGER.debug("Creating SSLContext with default configuration");
      }

      return context;
   }

   private SSLContext createSslContextWithKeyStoreFailure() {
      SSLContext context;
      try {
         context = this.createSslContextWithDefaultKeyManagerFactory();
         LOGGER.debug("Creating SSLContext with default keystore");
      } catch (TrustStoreConfigurationException var3) {
         context = this.createDefaultSslContext();
         LOGGER.debug("Creating SSLContext with default configuration");
      }

      return context;
   }

   private SSLContext createSslContextBasedOnConfiguration() throws KeyStoreConfigurationException, TrustStoreConfigurationException {
      return this.createSslContext(false, false);
   }

   private SSLContext createSslContextWithDefaultKeyManagerFactory() throws TrustStoreConfigurationException {
      try {
         return this.createSslContext(true, false);
      } catch (KeyStoreConfigurationException var2) {
         LOGGER.debug("Exception occured while using default keystore. This should be a BUG");
         return null;
      }
   }

   private SSLContext createSslContextWithDefaultTrustManagerFactory() throws KeyStoreConfigurationException {
      try {
         return this.createSslContext(false, true);
      } catch (TrustStoreConfigurationException var2) {
         LOGGER.debug("Exception occured while using default truststore. This should be a BUG");
         return null;
      }
   }

   private SSLContext createDefaultSslContext() {
      try {
         return SSLContext.getDefault();
      } catch (NoSuchAlgorithmException var2) {
         LOGGER.error("Failed to create an SSLContext with default configuration");
         return null;
      }
   }

   private SSLContext createSslContext(boolean loadDefaultKeyManagerFactory, boolean loadDefaultTrustManagerFactory) throws KeyStoreConfigurationException, TrustStoreConfigurationException {
      try {
         KeyManager[] kManagers = null;
         TrustManager[] tManagers = null;
         SSLContext newSslContext = SSLContext.getInstance(this.protocol);
         if (!loadDefaultKeyManagerFactory) {
            KeyManagerFactory kmFactory = this.loadKeyManagerFactory();
            kManagers = kmFactory.getKeyManagers();
         }

         if (!loadDefaultTrustManagerFactory) {
            TrustManagerFactory tmFactory = this.loadTrustManagerFactory();
            tManagers = tmFactory.getTrustManagers();
         }

         newSslContext.init(kManagers, tManagers, (SecureRandom)null);
         return newSslContext;
      } catch (NoSuchAlgorithmException var7) {
         LOGGER.error("No Provider supports a TrustManagerFactorySpi implementation for the specified protocol");
         throw new TrustStoreConfigurationException(var7);
      } catch (KeyManagementException var8) {
         LOGGER.error("Failed to initialize the SSLContext");
         throw new KeyStoreConfigurationException(var8);
      }
   }

   private TrustManagerFactory loadTrustManagerFactory() throws TrustStoreConfigurationException {
      if (this.trustStoreConfig == null) {
         throw new TrustStoreConfigurationException(new Exception("The trustStoreConfiguration is null"));
      } else {
         try {
            return this.trustStoreConfig.initTrustManagerFactory();
         } catch (NoSuchAlgorithmException var2) {
            LOGGER.error("The specified algorithm is not available from the specified provider");
            throw new TrustStoreConfigurationException(var2);
         } catch (KeyStoreException var3) {
            LOGGER.error("Failed to initialize the TrustManagerFactory");
            throw new TrustStoreConfigurationException(var3);
         }
      }
   }

   private KeyManagerFactory loadKeyManagerFactory() throws KeyStoreConfigurationException {
      if (this.keyStoreConfig == null) {
         throw new KeyStoreConfigurationException(new Exception("The keyStoreConfiguration is null"));
      } else {
         try {
            return this.keyStoreConfig.initKeyManagerFactory();
         } catch (NoSuchAlgorithmException var2) {
            LOGGER.error("The specified algorithm is not available from the specified provider");
            throw new KeyStoreConfigurationException(var2);
         } catch (KeyStoreException var3) {
            LOGGER.error("Failed to initialize the TrustManagerFactory");
            throw new KeyStoreConfigurationException(var3);
         } catch (UnrecoverableKeyException var4) {
            LOGGER.error("The key cannot be recovered (e.g. the given password is wrong)");
            throw new KeyStoreConfigurationException(var4);
         }
      }
   }

   public boolean equals(SslConfiguration config) {
      if (config == null) {
         return false;
      } else {
         boolean keyStoreEquals = false;
         boolean trustStoreEquals = false;
         if (this.keyStoreConfig != null) {
            keyStoreEquals = this.keyStoreConfig.equals(config.keyStoreConfig);
         } else {
            keyStoreEquals = this.keyStoreConfig == config.keyStoreConfig;
         }

         if (this.trustStoreConfig != null) {
            trustStoreEquals = this.trustStoreConfig.equals(config.trustStoreConfig);
         } else {
            trustStoreEquals = this.trustStoreConfig == config.trustStoreConfig;
         }

         return keyStoreEquals && trustStoreEquals;
      }
   }

   @PluginFactory
   public static SslConfiguration createSSLConfiguration(@PluginAttribute("protocol") String protocol, @PluginElement("KeyStore") KeyStoreConfiguration keyStoreConfig, @PluginElement("TrustStore") TrustStoreConfiguration trustStoreConfig) {
      return new SslConfiguration(protocol, keyStoreConfig, trustStoreConfig);
   }
}
