package org.apache.solr.util;

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

import java.io.File;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.SSLContext;

import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.solr.client.solrj.embedded.SSLConfig;
import org.apache.solr.client.solrj.impl.HttpClientConfigurer;
import org.apache.solr.common.params.SolrParams;
import org.eclipse.jetty.util.security.CertificateUtils;

public class SSLTestConfig extends SSLConfig {
  public static File TEST_KEYSTORE = ExternalPaths.EXAMPLE_HOME == null ? null
      : new File(ExternalPaths.EXAMPLE_HOME, "../etc/solrtest.keystore");
  
  private static String TEST_KEYSTORE_PATH = TEST_KEYSTORE != null
      && TEST_KEYSTORE.exists() ? TEST_KEYSTORE.getAbsolutePath() : null;
  private static String TEST_KEYSTORE_PASSWORD = "secret";
  private static HttpClientConfigurer DEFAULT_CONFIGURER = new HttpClientConfigurer();
  
  public SSLTestConfig() {
    this(false, false);
  }
  
  public SSLTestConfig(boolean useSSL, boolean clientAuth) {
    this(useSSL, clientAuth, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD, TEST_KEYSTORE_PATH, TEST_KEYSTORE_PASSWORD);
  }
 
  public SSLTestConfig(boolean useSSL, boolean clientAuth, String keyStore, String keyStorePassword, String trustStore, String trustStorePassword) {
    super(useSSL, clientAuth, keyStore, keyStorePassword, trustStore, trustStorePassword);
  }
  
  /**
   * Will provide an HttpClientConfigurer for SSL support (adds https and
   * removes http schemes) is SSL is enabled, otherwise return the default
   * configurer
   */
  public HttpClientConfigurer getHttpClientConfigurer() {
    return isSSLMode() ? new SSLHttpClientConfigurer() : DEFAULT_CONFIGURER;
  }

  /**
   * Builds a new SSLContext with the given configuration and allows the uses of
   * self-signed certificates during testing.
   */
  protected SSLContext buildSSLContext() throws KeyManagementException, 
    UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {

    return SSLContexts.custom()
        .loadKeyMaterial(buildKeyStore(getKeyStore(), getKeyStorePassword()), getKeyStorePassword().toCharArray())
        .loadTrustMaterial(buildKeyStore(getTrustStore(), getTrustStorePassword()), new TrustSelfSignedStrategy())
        .setSecureRandom(new NullSecureRandom()).build();
  }
  
  
  protected static KeyStore buildKeyStore(String keyStoreLocation, String password) {
    try {
      return CertificateUtils.getKeyStore(null, keyStoreLocation, "JKS", null, password);
    } catch (Exception ex) {
      throw new IllegalStateException("Unable to build KeyStore from file: " + keyStoreLocation, ex);
    }
  }
  
  private class SSLHttpClientConfigurer extends HttpClientConfigurer {
    @SuppressWarnings("deprecation")
    protected void configure(DefaultHttpClient httpClient, SolrParams config) {
      super.configure(httpClient, config);
      SchemeRegistry registry = httpClient.getConnectionManager().getSchemeRegistry();
      // Make sure no tests cheat by using HTTP
      registry.unregister("http");
      try {
        registry.register(new Scheme("https", 443, new SSLSocketFactory(buildSSLContext())));
      } catch (KeyManagementException | UnrecoverableKeyException
          | NoSuchAlgorithmException | KeyStoreException ex) {
        throw new IllegalStateException("Unable to setup https scheme for HTTPClient to test SSL.", ex);
      }
    }
  }
  
  public static void setSSLSystemProperties() {
    System.setProperty("javax.net.ssl.keyStore", TEST_KEYSTORE_PATH);
    System.setProperty("javax.net.ssl.keyStorePassword", TEST_KEYSTORE_PASSWORD);
    System.setProperty("javax.net.ssl.trustStore", TEST_KEYSTORE_PATH);
    System.setProperty("javax.net.ssl.trustStorePassword", TEST_KEYSTORE_PASSWORD);
  }
  
  public static void clearSSLSystemProperties() {
    System.clearProperty("javax.net.ssl.keyStore");
    System.clearProperty("javax.net.ssl.keyStorePassword");
    System.clearProperty("javax.net.ssl.trustStore");
    System.clearProperty("javax.net.ssl.trustStorePassword");
  }
  
  /**
   * We use this to avoid SecureRandom blocking issues due to too many
   * instances or not enough random entropy. Tests do not need secure SSL.
   */
  private static class NullSecureRandom extends SecureRandom {
    public byte[] generateSeed(int numBytes) {
      return new byte[0];
    }
    
    synchronized public void nextBytes(byte[] bytes) {
    }
    
    synchronized public void setSeed(byte[] seed) {
    }
  }
}
@@ -1853,24 +1853,83 @@
 ystore%22);%0A  
+private static String SECURE_RANDOM_ALGORITHM = %22SHA1PRNG%22;
 %0A  private s
@@ -2690,16 +2690,71 @@
 sword);%0A
+    setSecureRandomAlgorithm(SECURE_RANDOM_ALGORITHM);%0A
   %7D%0A  %0A 
@@ -5719,17 +5719,24 @@
 ew byte%5B
-0
+numBytes
 %5D;%0A    %7D

Set the server side to SHA1PRNG as in Steve's original patch.