PHOENIX-5778 Remove the dependency of KeyStoreTestUtil
Co-authored-by: Josh Elser <elserj@apache.org>
Closes #22, closes #41
diff --git a/phoenix-queryserver-it/pom.xml b/phoenix-queryserver-it/pom.xml
index 6979f79..961d65b 100644
--- a/phoenix-queryserver-it/pom.xml
+++ b/phoenix-queryserver-it/pom.xml
@@ -207,6 +207,11 @@
<artifactId>hadoop-common</artifactId>
<type>test-jar</type>
<scope>test</scope>
- </dependency>
+ </dependency>
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcprov-jdk15</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerEnvironment.java b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerEnvironment.java
index 0d1c55f..7bb6c9e 100644
--- a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerEnvironment.java
+++ b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/QueryServerEnvironment.java
@@ -33,7 +33,6 @@
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.LocalHBaseCluster;
-import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
import org.apache.hadoop.hbase.util.FSUtils;
import org.apache.hadoop.hdfs.DFSConfigKeys;
@@ -179,8 +178,8 @@
// Generate SSL certs
File keystoresDir = new File(UTIL.getDataTestDir("keystore").toUri().getPath());
keystoresDir.mkdirs();
- String sslConfDir = KeyStoreTestUtil.getClasspathDir(QueryServerEnvironment.class);
- KeyStoreTestUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false);
+ String sslConfDir = TlsUtil.getClasspathDir(QueryServerEnvironment.class);
+ TlsUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false);
// Magic flag to tell hdfs to not fail on using ports above 1024
conf.setBoolean("ignore.secure.ports.for.testing", true);
diff --git a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/SecureQueryServerPhoenixDBIT.java b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/SecureQueryServerPhoenixDBIT.java
index 2be195a..bab190c 100644
--- a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/SecureQueryServerPhoenixDBIT.java
+++ b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/SecureQueryServerPhoenixDBIT.java
@@ -50,7 +50,6 @@
import org.apache.hadoop.hbase.LocalHBaseCluster;
import org.apache.hadoop.hbase.client.TestHCM.SleepAndFailFirstTime;
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
-import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
import org.apache.hadoop.hbase.security.token.TokenProvider;
import org.apache.hadoop.hbase.util.FSUtils;
@@ -160,8 +159,8 @@
// Generate SSL certs
File keystoresDir = new File(UTIL.getDataTestDir("keystore").toUri().getPath());
keystoresDir.mkdirs();
- String sslConfDir = KeyStoreTestUtil.getClasspathDir(SecureQueryServerPhoenixDBIT.class);
- KeyStoreTestUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false);
+ String sslConfDir = TlsUtil.getClasspathDir(SecureQueryServerPhoenixDBIT.class);
+ TlsUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false);
// Magic flag to tell hdfs to not fail on using ports above 1024
conf.setBoolean("ignore.secure.ports.for.testing", true);
diff --git a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/TlsUtil.java b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/TlsUtil.java
index de757b8..ef4fe9c 100644
--- a/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/TlsUtil.java
+++ b/phoenix-queryserver-it/src/it/java/org/apache/phoenix/end2end/TlsUtil.java
@@ -11,15 +11,38 @@
package org.apache.phoenix.end2end;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.math.BigInteger;
+import java.net.URL;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import javax.security.auth.x500.X500Principal;
-import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory;
+import org.apache.hadoop.security.ssl.SSLFactory;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+
public class TlsUtil {
protected static final String KEYSTORE_PASSWORD = "avaticasecret";
@@ -54,11 +77,11 @@
setupTls();
} catch (Exception e) {
LOG.error("could not set upt TLS for HTTPS tests", e);
+ throw new RuntimeException(e);
}
}
/**
- * This is simplified from org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil.setupSSLConfig()
* Performs setup of SSL configuration in preparation for testing an SSLFactory. This includes
* keys, certs, keystores, truststores.
*/
@@ -76,18 +99,245 @@
// may not exist
}
- KeyPair sKP = KeyStoreTestUtil.generateKeyPair("RSA");
+ KeyPair sKP = generateKeyPair("RSA");
X509Certificate sCert =
- KeyStoreTestUtil.generateCertificate("CN=localhost, O=server", sKP, 30,
- "SHA1withRSA");
- KeyStoreTestUtil.createKeyStore(KEYSTORE.getCanonicalPath(), KEYSTORE_PASSWORD, "server",
- sKP.getPrivate(), sCert);
+ generateCertificate("CN=localhost, O=server", sKP, 30, "SHA1withRSA");
+ createKeyStore(KEYSTORE.getCanonicalPath(), KEYSTORE_PASSWORD, "server", sKP.getPrivate(),
+ sCert);
Map<String, X509Certificate> certs = new HashMap<String, X509Certificate>();
certs.put("server", sCert);
- KeyStoreTestUtil.createTrustStore(TRUSTSTORE.getCanonicalPath(), TRUSTSTORE_PASSWORD,
- certs);
+ createTrustStore(TRUSTSTORE.getCanonicalPath(), TRUSTSTORE_PASSWORD, certs);
}
+ /**
+ * Create a self-signed X.509 Certificate.
+ *
+ * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB"
+ * @param pair the KeyPair
+ * @param days how many days from now the Certificate is valid for
+ * @param algorithm the signing algorithm, eg "SHA1withRSA"
+ * @return the self-signed certificate
+ */
+ private static X509Certificate generateCertificate(String dn, KeyPair pair, int days,
+ String algorithm)
+ throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
+ NoSuchProviderException, NoSuchAlgorithmException, SignatureException {
+ Date from = new Date();
+ Date to = new Date(from.getTime() + days * 86400000L);
+ BigInteger sn = new BigInteger(64, new SecureRandom());
+ KeyPair keyPair = pair;
+ X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ X500Principal dnName = new X500Principal(dn);
+
+ certGen.setSerialNumber(sn);
+ certGen.setIssuerDN(dnName);
+ certGen.setNotBefore(from);
+ certGen.setNotAfter(to);
+ certGen.setSubjectDN(dnName);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm(algorithm);
+ X509Certificate cert = certGen.generate(pair.getPrivate());
+ return cert;
+ }
+
+ public static String getClasspathDir(Class<?> klass) throws Exception {
+ String file = klass.getName();
+ file = file.replace('.', '/') + ".class";
+ URL url = Thread.currentThread().getContextClassLoader().getResource(file);
+ String baseDir = url.toURI().getPath();
+ baseDir = baseDir.substring(0, baseDir.length() - file.length() - 1);
+ return baseDir;
+ }
+
+ /**
+ * Performs complete setup of SSL configuration in preparation for testing an
+ * SSLFactory. This includes keys, certs, keystores, truststores, the server
+ * SSL configuration file, the client SSL configuration file, and the master
+ * configuration file read by the SSLFactory.
+ *
+ * @param keystoresDir String directory to save keystores
+ * @param sslConfDir String directory to save SSL configuration files
+ * @param conf Configuration master configuration to be used by an SSLFactory,
+ * which will be mutated by this method
+ * @param useClientCert boolean true to make the client present a cert in the
+ * SSL handshake
+ */
+ public static void setupSSLConfig(String keystoresDir, String sslConfDir, Configuration conf,
+ boolean useClientCert) throws Exception {
+ String clientKS = keystoresDir + "/clientKS.jks";
+ String clientPassword = "clientP";
+ String serverKS = keystoresDir + "/serverKS.jks";
+ String serverPassword = "serverP";
+ String trustKS = keystoresDir + "/trustKS.jks";
+ String trustPassword = "trustP";
+
+ File sslClientConfFile = new File(sslConfDir + "/ssl-client.xml");
+ File sslServerConfFile = new File(sslConfDir + "/ssl-server.xml");
+
+ Map<String, X509Certificate> certs = new HashMap<>();
+
+ if (useClientCert) {
+ KeyPair cKP = generateKeyPair("RSA");
+ X509Certificate cCert =
+ generateCertificate("CN=localhost, O=client", cKP, 30, "SHA1withRSA");
+ createKeyStore(clientKS, clientPassword, "client", cKP.getPrivate(), cCert);
+ certs.put("client", cCert);
+ }
+
+ KeyPair sKP = generateKeyPair("RSA");
+ X509Certificate sCert =
+ generateCertificate("CN=localhost, O=server", sKP, 30, "SHA1withRSA");
+ createKeyStore(serverKS, serverPassword, "server", sKP.getPrivate(), sCert);
+ certs.put("server", sCert);
+
+ createTrustStore(trustKS, trustPassword, certs);
+
+ Configuration clientSSLConf =
+ createClientSSLConfig(clientKS, clientPassword, clientPassword, trustKS);
+ Configuration serverSSLConf =
+ createServerSSLConfig(serverKS, serverPassword, serverPassword, trustKS);
+
+ saveConfig(sslClientConfFile, clientSSLConf);
+ saveConfig(sslServerConfFile, serverSSLConf);
+
+ conf.set(SSLFactory.SSL_HOSTNAME_VERIFIER_KEY, "ALLOW_ALL");
+ conf.set(SSLFactory.SSL_CLIENT_CONF_KEY, sslClientConfFile.getName());
+ conf.set(SSLFactory.SSL_SERVER_CONF_KEY, sslServerConfFile.getName());
+ conf.setBoolean(SSLFactory.SSL_REQUIRE_CLIENT_CERT_KEY, useClientCert);
+ }
+
+ /**
+ * Creates SSL configuration for a client.
+ *
+ * @param clientKS String client keystore file
+ * @param password String store password, or null to avoid setting store
+ * password
+ * @param keyPassword String key password, or null to avoid setting key
+ * password
+ * @param trustKS String truststore file
+ * @return Configuration for client SSL
+ */
+ private static Configuration createClientSSLConfig(String clientKS, String password,
+ String keyPassword, String trustKS) {
+ Configuration clientSSLConf =
+ createSSLConfig(SSLFactory.Mode.CLIENT, clientKS, password, keyPassword, trustKS);
+ return clientSSLConf;
+ }
+
+ /**
+ * Creates SSL configuration for a server.
+ *
+ * @param serverKS String server keystore file
+ * @param password String store password, or null to avoid setting store
+ * password
+ * @param keyPassword String key password, or null to avoid setting key
+ * password
+ * @param trustKS String truststore file
+ * @return Configuration for server SSL
+ */
+ private static Configuration createServerSSLConfig(String serverKS, String password,
+ String keyPassword, String trustKS) throws IOException {
+ Configuration serverSSLConf =
+ createSSLConfig(SSLFactory.Mode.SERVER, serverKS, password, keyPassword, trustKS);
+ return serverSSLConf;
+ }
+
+ /**
+ * Creates SSL configuration.
+ *
+ * @param mode SSLFactory.Mode mode to configure
+ * @param keystore String keystore file
+ * @param password String store password, or null to avoid setting store
+ * password
+ * @param keyPassword String key password, or null to avoid setting key
+ * password
+ * @param trustKS String truststore file
+ * @return Configuration for SSL
+ */
+ private static Configuration createSSLConfig(SSLFactory.Mode mode, String keystore,
+ String password, String keyPassword, String trustKS) {
+ String trustPassword = "trustP";
+
+ Configuration sslConf = new Configuration(false);
+ if (keystore != null) {
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_KEYSTORE_LOCATION_TPL_KEY), keystore);
+ }
+ if (password != null) {
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_KEYSTORE_PASSWORD_TPL_KEY), password);
+ }
+ if (keyPassword != null) {
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_KEYSTORE_KEYPASSWORD_TPL_KEY), keyPassword);
+ }
+ if (trustKS != null) {
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_TRUSTSTORE_LOCATION_TPL_KEY), trustKS);
+ }
+ if (trustPassword != null) {
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_TRUSTSTORE_PASSWORD_TPL_KEY), trustPassword);
+ }
+ sslConf.set(FileBasedKeyStoresFactory.resolvePropertyName(mode,
+ FileBasedKeyStoresFactory.SSL_TRUSTSTORE_RELOAD_INTERVAL_TPL_KEY), "1000");
+
+ return sslConf;
+ }
+
+ /**
+ * Saves configuration to a file.
+ *
+ * @param file File to save
+ * @param conf Configuration contents to write to file
+ * @throws IOException if there is an I/O error saving the file
+ */
+ private static void saveConfig(File file, Configuration conf) throws IOException {
+ Writer writer = new FileWriter(file);
+ try {
+ conf.writeXml(writer);
+ } finally {
+ writer.close();
+ }
+ }
+
+ private static KeyPair generateKeyPair(String algorithm) throws NoSuchAlgorithmException {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm);
+ keyGen.initialize(1024);
+ return keyGen.genKeyPair();
+ }
+
+ private static KeyStore createEmptyKeyStore() throws GeneralSecurityException, IOException {
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, null); // initialize
+ return ks;
+ }
+
+ private static void saveKeyStore(KeyStore ks, String filename, String password)
+ throws GeneralSecurityException, IOException {
+ FileOutputStream out = new FileOutputStream(filename);
+ try {
+ ks.store(out, password.toCharArray());
+ } finally {
+ out.close();
+ }
+ }
+
+ private static void createKeyStore(String filename, String password, String alias,
+ Key privateKey, Certificate cert) throws GeneralSecurityException, IOException {
+ KeyStore ks = createEmptyKeyStore();
+ ks.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[] { cert });
+ saveKeyStore(ks, filename, password);
+ }
+
+ private static <T extends Certificate> void createTrustStore(String filename, String password,
+ Map<String, T> certs) throws GeneralSecurityException, IOException {
+ KeyStore ks = createEmptyKeyStore();
+ for (Map.Entry<String, T> cert : certs.entrySet()) {
+ ks.setCertificateEntry(cert.getKey(), cert.getValue());
+ }
+ saveKeyStore(ks, filename, password);
+ }
}
diff --git a/pom.xml b/pom.xml
index f7a0cd9..d8a37a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -592,6 +592,12 @@
<version>${hadoop-two.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>bouncycastle</groupId>
+ <artifactId>bcprov-jdk15</artifactId>
+ <version>140</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
<profiles>