This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new c1c69dac4 TIKA-3719 (#549)
c1c69dac4 is described below

commit c1c69dac4f5f948f38e0b198c3fdaad61a7d80be
Author: Tim Allison <[email protected]>
AuthorDate: Fri Apr 22 13:47:27 2022 -0400

    TIKA-3719 (#549)
    
    * TIKA-3719 -- add tls to tika-server
---
 CHANGES.txt                                        |   2 +
 .../apache/tika/server/core/TikaServerConfig.java  |   7 +
 .../apache/tika/server/core/TikaServerProcess.java |  54 +++++-
 .../org/apache/tika/server/core/TlsConfig.java     | 171 +++++++++++++++++++
 .../tika/server/core/IntegrationTestBase.java      |   7 +-
 .../tika/server/core/TikaServerConfigTest.java     |  16 ++
 .../server/core/TikaServerIntegrationTest.java     | 181 +++++++++++++++++++++
 .../tika-config-server-tls-one-way-template.xml    |  43 +++++
 .../tika-config-server-tls-two-way-template.xml    |  46 ++++++
 .../resources/configs/tika-config-server-tls.xml   |  45 +++++
 .../src/test/resources/ssl-keys/README.txt         |  28 ++++
 .../resources/ssl-keys/tika-client-keystore.p12    | Bin 0 -> 2505 bytes
 .../resources/ssl-keys/tika-client-truststore.p12  | Bin 0 -> 3429 bytes
 .../src/test/resources/ssl-keys/tika-client.crt    | Bin 0 -> 789 bytes
 .../resources/ssl-keys/tika-server-keystore.p12    | Bin 0 -> 2505 bytes
 .../resources/ssl-keys/tika-server-truststore.p12  | Bin 0 -> 3429 bytes
 .../src/test/resources/ssl-keys/tika-server.crt    | Bin 0 -> 789 bytes
 17 files changed, 595 insertions(+), 5 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index a58dfcd64..2e4f1346f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -6,6 +6,8 @@ Release 2.4.0 - ???
      
https://github.com/apache/tika/blob/main/tika-parsers/tika-parsers-ml/tika-dl/pom.xml
      for the dependencies that must be provided at run-time (TIKA-3676).
 
+   * Add initial TLS encryption option for tika-server (TIKA-3719).
+
    * Allow specification of fetcherName and fetchKey via query parameters
      in request URI in tika-server (TIKA-3714).
 
diff --git 
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java
 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java
index 990ffdf3f..f0afbc6aa 100644
--- 
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java
+++ 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerConfig.java
@@ -130,6 +130,7 @@ public class TikaServerConfig extends ConfigBase {
     private String forkedStatusFile;
     private int numRestarts = 0;
 
+    private TlsConfig tlsConfig = new TlsConfig();
     /**
      * Config with only the defaults
      */
@@ -525,6 +526,12 @@ public class TikaServerConfig extends ConfigBase {
         this.returnStackTrace = returnStackTrace;
     }
 
+    public void setTlsConfig(TlsConfig tlsConfig) {
+        this.tlsConfig = tlsConfig;
+    }
+    public TlsConfig getTlsConfig() {
+        return tlsConfig;
+    }
     public List<String> getEndpoints() {
         return endpoints;
     }
diff --git 
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java
 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java
index 08b1bf9e4..ea25dda93 100644
--- 
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java
+++ 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TikaServerProcess.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.BindException;
 import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -35,15 +36,23 @@ import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.DefaultParser;
 import org.apache.commons.cli.Options;
 import org.apache.cxf.binding.BindingFactoryManager;
+import org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils;
+import org.apache.cxf.configuration.jsse.TLSServerParameters;
+import org.apache.cxf.configuration.security.ClientAuthentication;
+import org.apache.cxf.configuration.security.KeyManagersType;
+import org.apache.cxf.configuration.security.KeyStoreType;
+import org.apache.cxf.configuration.security.TrustManagersType;
 import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.jaxrs.JAXRSBindingFactory;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
 import org.apache.cxf.jaxrs.lifecycle.ResourceProvider;
 import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.utils.JAXRSServerFactoryCustomizationUtils;
 import org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter;
 import org.apache.cxf.service.factory.ServiceConstructionException;
 import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
 import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
+import org.apache.cxf.transport.http_jetty.JettyHTTPServerEngineFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.SAXException;
@@ -248,13 +257,22 @@ public class TikaServerProcess {
         sf.setOutInterceptors(Collections.singletonList(new 
GZIPOutInterceptor()));
         sf.setInInterceptors(Collections.singletonList(new 
GZIPInInterceptor()));
 
-        String url = "http://"; + host + ":" + port + "/";
+        String protocol = tikaServerConfig.getTlsConfig().isActive() ? "https" 
: "http";
+        String url = protocol + "://" + host + ":" + port + "/";
         sf.setAddress(url);
         sf.setResourceComparator(new ProduceTypeResourceComparator());
         BindingFactoryManager manager = 
sf.getBus().getExtension(BindingFactoryManager.class);
-        JAXRSBindingFactory factory = new JAXRSBindingFactory();
-        factory.setBus(sf.getBus());
-        manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, 
factory);
+        if (tikaServerConfig.getTlsConfig().isActive()) {
+            TLSServerParameters tlsParams = 
getTlsParams(tikaServerConfig.getTlsConfig());
+            JettyHTTPServerEngineFactory factory = new 
JettyHTTPServerEngineFactory();
+            factory.setBus(sf.getBus());
+            factory.setTLSServerParametersForPort(host, port, tlsParams);
+            JAXRSServerFactoryCustomizationUtils.customize(sf);
+        } else {
+            JAXRSBindingFactory factory = new JAXRSBindingFactory();
+            factory.setBus(sf.getBus());
+            
manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);
+        }
         ServerDetails details = new ServerDetails();
         details.sf = sf;
         details.url = url;
@@ -263,6 +281,34 @@ public class TikaServerProcess {
         return details;
     }
 
+    private static TLSServerParameters getTlsParams(TlsConfig tlsConfig)
+            throws GeneralSecurityException, IOException {
+        KeyStoreType keyStore = new KeyStoreType();
+        keyStore.setType(tlsConfig.getKeyStoreType());
+        keyStore.setPassword(tlsConfig.getKeyStorePassword());
+        keyStore.setFile(tlsConfig.getKeyStoreFile());
+        KeyManagersType kmt = new KeyManagersType();
+        kmt.setKeyStore(keyStore);
+        kmt.setKeyPassword(tlsConfig.getKeyStorePassword());
+        TLSServerParameters parameters = new TLSServerParameters();
+        parameters.setKeyManagers(TLSParameterJaxBUtils.getKeyManagers(kmt));
+
+        if (tlsConfig.hasTrustStore()) {
+            KeyStoreType trustKeyStore = new KeyStoreType();
+            trustKeyStore.setType(tlsConfig.getTrustStoreType());
+            trustKeyStore.setPassword(tlsConfig.getTrustStorePassword());
+            trustKeyStore.setFile(tlsConfig.getTrustStoreFile());
+            TrustManagersType tmt = new TrustManagersType();
+            tmt.setKeyStore(trustKeyStore);
+            
parameters.setTrustManagers(TLSParameterJaxBUtils.getTrustManagers(tmt, true));
+        }
+        ClientAuthentication clientAuthentication = new ClientAuthentication();
+        
clientAuthentication.setRequired(tlsConfig.isClientAuthenticationRequired());
+        clientAuthentication.setWant(tlsConfig.isClientAuthenticationWanted());
+        parameters.setClientAuthentication(clientAuthentication);
+        return parameters;
+    }
+
     private static void loadAllProviders(TikaServerConfig tikaServerConfig,
                                          ServerStatus serverStatus,
                                          List<ResourceProvider> 
resourceProviders,
diff --git 
a/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TlsConfig.java
 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TlsConfig.java
new file mode 100644
index 000000000..0a18ff4b3
--- /dev/null
+++ 
b/tika-server/tika-server-core/src/main/java/org/apache/tika/server/core/TlsConfig.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+package org.apache.tika.server.core;
+
+import java.util.Map;
+
+import org.apache.tika.config.Initializable;
+import org.apache.tika.config.InitializableProblemHandler;
+import org.apache.tika.config.Param;
+import org.apache.tika.exception.TikaConfigException;
+import org.apache.tika.utils.StringUtils;
+
+public class TlsConfig implements Initializable {
+
+    private boolean active = false;
+    //TODO make this configurable
+    private final boolean passwordsAESEncrypted = false;
+    private String keyStoreType = null;
+    private String keyStorePassword = null;
+    private String keyStoreFile = null;
+    private String trustStoreType = null;
+    private String trustStorePassword = null;
+    private String trustStoreFile = null;
+
+    private boolean clientAuthenticationWanted = false;
+
+    private boolean clientAuthenticationRequired = false;
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+    public boolean isPasswordsAESEncrypted() {
+        return passwordsAESEncrypted;
+    }
+
+    public String getKeyStoreType() {
+        return keyStoreType;
+    }
+
+    public void setKeyStoreType(String keyStoreType) {
+        this.keyStoreType = keyStoreType;
+    }
+
+    public String getKeyStorePassword() {
+        return keyStorePassword;
+    }
+
+    public void setKeyStorePassword(String keyStorePassword) {
+        this.keyStorePassword = keyStorePassword;
+    }
+
+    public String getKeyStoreFile() {
+        return keyStoreFile;
+    }
+
+    public void setKeyStoreFile(String keyStoreFile) {
+        this.keyStoreFile = keyStoreFile;
+    }
+
+    public String getTrustStoreType() {
+        return trustStoreType;
+    }
+
+    public void setTrustStoreType(String trustStoreType) {
+        this.trustStoreType = trustStoreType;
+    }
+
+    public String getTrustStorePassword() {
+        return trustStorePassword;
+    }
+
+    public void setTrustStorePassword(String trustStorePassword) {
+        this.trustStorePassword = trustStorePassword;
+    }
+
+    public String getTrustStoreFile() {
+        return trustStoreFile;
+    }
+
+    public void setTrustStoreFile(String trustStoreFile) {
+        this.trustStoreFile = trustStoreFile;
+    }
+
+    @Override
+    public void initialize(Map<String, Param> params) throws 
TikaConfigException {
+
+    }
+
+    @Override
+    public void checkInitialization(InitializableProblemHandler problemHandler)
+            throws TikaConfigException {
+        if (active) {
+            if (StringUtils.isBlank(keyStoreType)) {
+                throw new TikaConfigException("must initialize keyStoreType");
+            } else if (StringUtils.isBlank(keyStoreFile)) {
+                throw new TikaConfigException("must initialize keyStoreFile");
+            } else if (StringUtils.isBlank(keyStorePassword)) {
+                throw new TikaConfigException("must initialize 
keyStorePassword");
+            }
+            if (hasTrustStore()) {
+                if (StringUtils.isBlank(trustStoreType)) {
+                    throw new TikaConfigException("must initialize 
trustStoreType " +
+                            "if there's any trustStore info");
+                } else if (StringUtils.isBlank(trustStoreFile)) {
+                    throw new TikaConfigException("must initialize 
trustStoreFile " +
+                            "if there's any trustStore info");
+                } else if (StringUtils.isBlank(trustStorePassword)) {
+                    throw new TikaConfigException("must initialize 
trustStorePassword " +
+                            "if there's any trustStore info");
+                }
+            }
+            if (!hasTrustStore() && isClientAuthenticationRequired()) {
+                throw new TikaConfigException("requiring client 
authentication, but no trust " +
+                        "store has been specified?!");
+            }
+        }
+    }
+
+    public boolean isClientAuthenticationWanted() {
+        return clientAuthenticationWanted;
+    }
+
+    public void setClientAuthenticationWanted(boolean 
clientAuthenticationWanted) {
+        this.clientAuthenticationWanted = clientAuthenticationWanted;
+    }
+
+    public boolean isClientAuthenticationRequired() {
+        return clientAuthenticationRequired;
+    }
+
+    public void setClientAuthenticationRequired(boolean 
clientAuthenticationRequired) {
+        this.clientAuthenticationRequired = clientAuthenticationRequired;
+    }
+
+    @Override
+    public String toString() {
+        return "TlsConfig{" + "active=" + active + ", passwordsAESEncrypted=" +
+                passwordsAESEncrypted + ", keyStoreType='" + keyStoreType + 
'\'' +
+                ", keyStorePassword='" + keyStorePassword + '\'' + ", 
keyStoreFile='" +
+                keyStoreFile + '\'' + ", trustStoreType='" + trustStoreType + 
'\'' +
+                ", trustStorePassword='" + trustStorePassword + '\'' + ", 
trustStoreFile='" +
+                trustStoreFile + '\'' + ", clientAuthenticationWanted=" +
+                clientAuthenticationWanted + ", 
isClientAuthenticationRequired=" +
+                clientAuthenticationRequired + '}';
+    }
+
+    public boolean hasTrustStore() {
+        return ! StringUtils.isBlank(trustStoreType) &&
+                ! StringUtils.isBlank(trustStorePassword) &&
+                ! StringUtils.isBlank(trustStoreFile);
+    }
+}
diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
index 8c3112112..90adf0acc 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
@@ -126,9 +126,14 @@ public class IntegrationTestBase extends TikaTest {
     }
 
     void awaitServerStartup() throws Exception {
+        WebClient client = WebClient.create(endPoint + 
"/").accept("text/html");
+        awaitServerStartup(client);
+
+    }
+
+    void awaitServerStartup(WebClient client) throws Exception {
         Instant started = Instant.now();
         long elapsed = Duration.between(started, Instant.now()).toMillis();
-        WebClient client = WebClient.create(endPoint + 
"/").accept("text/html");
         while (elapsed < MAX_WAIT_MS) {
             try {
                 Response response = client.get();
diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java
index 0f9a25c34..57ae3281e 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerConfigTest.java
@@ -98,4 +98,20 @@ public class TikaServerConfigTest {
         assertEquals(9994, ports[0]);
         assertEquals(9999, ports[5]);
     }
+
+    @Test
+    public void testTlsConfig() throws Exception {
+        Set<String> settings = new HashSet<>();
+        CommandLineParser parser = new DefaultParser();
+        CommandLine emptyCommandLine = parser.parse(new Options(), new 
String[]{});
+        Path path = Paths.get(TikaConfigTest.class.getResource(
+                "/configs/tika-config-server-tls.xml").toURI());
+        TikaServerConfig config = TikaServerConfig
+                .load(path,
+                        emptyCommandLine,
+                        settings);
+        TlsConfig tlsConfig = config.getTlsConfig();
+        System.out.println(tlsConfig);
+    }
+
 }
diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
index 83fffdd49..603c5a668 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerIntegrationTest.java
@@ -21,11 +21,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
 import java.util.List;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
@@ -35,7 +39,16 @@ import javax.ws.rs.core.Response;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.io.IOUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.configuration.jsse.TLSParameterJaxBUtils;
+import org.apache.cxf.configuration.security.KeyManagersType;
+import org.apache.cxf.configuration.security.KeyStoreType;
+import org.apache.cxf.configuration.security.TrustManagersType;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
@@ -50,6 +63,37 @@ import org.apache.tika.utils.ProcessUtils;
 public class TikaServerIntegrationTest extends IntegrationTestBase {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(TikaServerIntegrationTest.class);
+    private static Path TLS_KEYS;
+    private static Path TIKA_TLS_ONE_WAY_CONFIG;
+    private static Path TIKA_TLS_TWO_WAY_CONFIG;
+    @BeforeAll
+    public static void setUpSSL() throws Exception {
+        TLS_KEYS =
+                
Paths.get(TikaServerIntegrationTest.class.getResource("/ssl-keys").toURI());
+
+        String xml = IOUtils.resourceToString(
+                "/configs/tika-config-server-tls-two-way-template.xml",
+                UTF_8);
+        xml = xml.replaceAll("\\$\\{SSL_KEYS\\}", 
TLS_KEYS.toAbsolutePath().toString());
+
+        TIKA_TLS_TWO_WAY_CONFIG = Files.createTempFile("tika-config-tls-", 
".xml");
+        Files.write(TIKA_TLS_TWO_WAY_CONFIG, xml.getBytes(UTF_8));
+
+        xml = IOUtils.resourceToString(
+                "/configs/tika-config-server-tls-one-way-template.xml",
+                UTF_8);
+        xml = xml.replaceAll("\\$\\{SSL_KEYS\\}", 
TLS_KEYS.toAbsolutePath().toString());
+
+        TIKA_TLS_ONE_WAY_CONFIG = Files.createTempFile("tika-config-tls-", 
".xml");
+        Files.write(TIKA_TLS_ONE_WAY_CONFIG, xml.getBytes(UTF_8));
+
+    }
+
+    @AfterAll
+    public static void cleanUpSSL() throws IOException {
+        Files.delete(TIKA_TLS_TWO_WAY_CONFIG);
+        Files.delete(TIKA_TLS_ONE_WAY_CONFIG);
+    }
 
     @Test
     public void testBasic() throws Exception {
@@ -260,6 +304,16 @@ public class TikaServerIntegrationTest extends 
IntegrationTestBase {
         }
     }
 
+    private String getSSL(String file) {
+        try {
+            return 
ProcessUtils.escapeCommandLine(Paths.get(TikaServerIntegrationTest.class.
+                    getResource("/ssl-keys/" + 
file).toURI()).toAbsolutePath().toString());
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
     @Test
     public void testStdErrOutBasic() throws Exception {
         startProcess(
@@ -276,6 +330,133 @@ public class TikaServerIntegrationTest extends 
IntegrationTestBase {
 
     }
 
+    @Test
+    public void test1WayTLS() throws Exception {
+        startProcess(
+                new String[]{"-config",
+                        
ProcessUtils.escapeCommandLine(TIKA_TLS_ONE_WAY_CONFIG.toAbsolutePath().toString())});
+
+        String httpsEndpoint = "https://localhost:"; + INTEGRATION_TEST_PORT;
+        WebClient webClient = WebClient.create(httpsEndpoint);
+        configure1WayTLS(webClient);
+
+        awaitServerStartup(webClient);
+
+        webClient.close();
+        webClient = WebClient.create(httpsEndpoint + RMETA_PATH);
+        configure1WayTLS(webClient);
+
+        Response response = webClient.accept("application/json")
+                .put(ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+        Reader reader = new InputStreamReader((InputStream) 
response.getEntity(), UTF_8);
+
+        List<Metadata> metadataList = JsonMetadataList.fromJson(reader);
+        assertEquals(1, metadataList.size());
+        assertEquals("Nikolai Lobachevsky", metadataList.get(0).get("author"));
+        assertContains("hello world", 
metadataList.get(0).get("X-TIKA:content"));
+
+        //now test no tls config
+        webClient = WebClient.create(httpsEndpoint + RMETA_PATH);
+
+        try {
+            response = webClient.accept("application/json").put(
+                    ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+            fail("bad, bad, bad. this should have failed!");
+        } catch (Exception e) {
+            assertContains("javax.net.ssl.SSLHandshakeException", 
e.getMessage());
+        }
+    }
+    @Test
+    public void test2WayTLS() throws Exception {
+        startProcess(
+                new String[]{"-config",
+                        
ProcessUtils.escapeCommandLine(TIKA_TLS_TWO_WAY_CONFIG.toAbsolutePath().toString())});
+
+        String httpsEndpoint = "https://localhost:"; + INTEGRATION_TEST_PORT;
+        WebClient webClient = WebClient.create(httpsEndpoint);
+        configure2WayTLS(webClient);
+
+        awaitServerStartup(webClient);
+
+        webClient.close();
+        webClient = WebClient.create(httpsEndpoint + RMETA_PATH);
+        configure2WayTLS(webClient);
+
+        Response response = webClient.accept("application/json")
+                .put(ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+        Reader reader = new InputStreamReader((InputStream) 
response.getEntity(), UTF_8);
+
+        List<Metadata> metadataList = JsonMetadataList.fromJson(reader);
+        assertEquals(1, metadataList.size());
+        assertEquals("Nikolai Lobachevsky", metadataList.get(0).get("author"));
+        assertContains("hello world", 
metadataList.get(0).get("X-TIKA:content"));
+
+        //now test that no tls config fails
+        webClient = WebClient.create(httpsEndpoint + RMETA_PATH);
+
+        try {
+            response = webClient.accept("application/json").put(
+                    ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+            fail("bad, bad, bad. this should have failed!");
+        } catch (Exception e) {
+            assertContains("javax.net.ssl.SSLHandshakeException", 
e.getMessage());
+        }
+
+        //now test that 1 way fails
+        webClient = WebClient.create(httpsEndpoint + RMETA_PATH);
+        configure1WayTLS(webClient);
+        try {
+            response = webClient.accept("application/json").put(
+                    ClassLoader.getSystemResourceAsStream(TEST_HELLO_WORLD));
+            fail("bad, bad, bad. this should have failed!");
+        } catch (Exception e) {
+            assertContains("readHandshakeRecord", e.getMessage());
+        }
+    }
+
+    private void configure2WayTLS(WebClient webClient) throws 
GeneralSecurityException, IOException {
+        HTTPConduit conduit = WebClient.getConfig(webClient)
+                .getHttpConduit();
+        KeyStoreType keystore = new KeyStoreType();
+        keystore.setType("PKCS12");
+        keystore.setPassword("tika-secret");
+        keystore.setFile(getSSL("tika-client-keystore.p12"));
+        KeyManagersType kmt = new KeyManagersType();
+        kmt.setKeyStore(keystore);
+        kmt.setKeyPassword("tika-secret");
+        TLSClientParameters parameters = new TLSClientParameters();
+        parameters.setKeyManagers(TLSParameterJaxBUtils.getKeyManagers(kmt));
+
+        KeyStoreType trustKeyStore = new KeyStoreType();
+        trustKeyStore.setType("PKCS12");
+        trustKeyStore.setPassword("tika-secret");
+        trustKeyStore.setFile(getSSL("tika-client-truststore.p12"));
+
+        TrustManagersType tmt = new TrustManagersType();
+        tmt.setKeyStore(trustKeyStore);
+        
parameters.setTrustManagers(TLSParameterJaxBUtils.getTrustManagers(tmt, true));
+
+        conduit.setTlsClientParameters(parameters);
+
+    }
+
+    private void configure1WayTLS(WebClient webClient) throws 
GeneralSecurityException,
+            IOException {
+        HTTPConduit conduit = WebClient.getConfig(webClient)
+                .getHttpConduit();
+        TLSClientParameters parameters = new TLSClientParameters();
+
+        KeyStoreType trustKeyStore = new KeyStoreType();
+        trustKeyStore.setType("PKCS12");
+        trustKeyStore.setPassword("tika-secret");
+        trustKeyStore.setFile(getSSL("tika-client-truststore.p12"));
+
+        TrustManagersType tmt = new TrustManagersType();
+        tmt.setKeyStore(trustKeyStore);
+        
parameters.setTrustManagers(TLSParameterJaxBUtils.getTrustManagers(tmt, true));
+        conduit.setTlsClientParameters(parameters);
+    }
+
     @Test
     @Disabled("This works, but prints too much junk to the console.  " +
             "Figure out how to gobble/redirect.")
diff --git 
a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-one-way-template.xml
 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-one-way-template.xml
new file mode 100644
index 000000000..f90acc859
--- /dev/null
+++ 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-one-way-template.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<properties>
+  <server>
+    <params>
+      <port>9999</port>
+      <taskTimeoutMillis>1000000</taskTimeoutMillis>
+      <minimumTimeoutMillis>10000</minimumTimeoutMillis>
+      <maxFiles>10000</maxFiles>
+      <forkedJvmArgs>
+        <arg>-Xmx1g</arg>
+      </forkedJvmArgs>
+      <endpoints>
+        <endpoint>rmeta</endpoint>
+      </endpoints>
+    </params>
+    <tlsConfig>
+      <params>
+        <active>true</active>
+        <keyStoreType>PKCS12</keyStoreType>
+        <keyStorePassword>tika-secret</keyStorePassword>
+        <keyStoreFile>${SSL_KEYS}/tika-server-keystore.p12</keyStoreFile>
+        <clientAuthenticationWanted>false</clientAuthenticationWanted>
+        <clientAuthenticationRequired>false</clientAuthenticationRequired>
+      </params>
+    </tlsConfig>
+  </server>
+</properties>
diff --git 
a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-two-way-template.xml
 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-two-way-template.xml
new file mode 100644
index 000000000..7a8b5dc94
--- /dev/null
+++ 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls-two-way-template.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<properties>
+  <server>
+    <params>
+      <port>9999</port>
+      <taskTimeoutMillis>1000000</taskTimeoutMillis>
+      <minimumTimeoutMillis>10000</minimumTimeoutMillis>
+      <maxFiles>10000</maxFiles>
+      <forkedJvmArgs>
+        <arg>-Xmx1g</arg>
+      </forkedJvmArgs>
+      <endpoints>
+        <endpoint>rmeta</endpoint>
+      </endpoints>
+    </params>
+    <tlsConfig>
+      <params>
+        <active>true</active>
+        <keyStoreType>PKCS12</keyStoreType>
+        <keyStorePassword>tika-secret</keyStorePassword>
+        <keyStoreFile>${SSL_KEYS}/tika-server-keystore.p12</keyStoreFile>
+        <trustStoreType>PKCS12</trustStoreType>
+        <trustStorePassword>tika-secret</trustStorePassword>
+        <trustStoreFile>${SSL_KEYS}/tika-server-truststore.p12</trustStoreFile>
+        <clientAuthenticationWanted>true</clientAuthenticationWanted>
+        <clientAuthenticationRequired>true</clientAuthenticationRequired>
+      </params>
+    </tlsConfig>
+  </server>
+</properties>
diff --git 
a/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.xml
 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.xml
new file mode 100644
index 000000000..a5186eec3
--- /dev/null
+++ 
b/tika-server/tika-server-core/src/test/resources/configs/tika-config-server-tls.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<properties>
+  <server>
+    <params>
+      <port>9999</port>
+      <taskTimeoutMillis>54321</taskTimeoutMillis>
+      <minimumTimeoutMillis>10</minimumTimeoutMillis>
+      <enableUnsecureFeatures>true</enableUnsecureFeatures>
+      <maxFiles>20</maxFiles>
+      <forkedJvmArgs>
+        <arg>-Xmx2g</arg>
+      </forkedJvmArgs>
+      <endpoints>
+        <endpoint>rmeta</endpoint>
+      </endpoints>
+    </params>
+    <tlsConfig>
+      <params>
+        <active>true</active>
+        <keyStoreType>myType</keyStoreType>
+        <keyStorePassword>pass</keyStorePassword>
+        <keyStoreFile>/something/or/other</keyStoreFile>
+        <trustStoreType>myType2</trustStoreType>
+        <trustStorePassword>pass2</trustStorePassword>
+        <trustStoreFile>/something/or/other2</trustStoreFile>
+      </params>
+    </tlsConfig>
+  </server>
+</properties>
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/README.txt 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/README.txt
new file mode 100644
index 000000000..2267cabda
--- /dev/null
+++ b/tika-server/tika-server-core/src/test/resources/ssl-keys/README.txt
@@ -0,0 +1,28 @@
+To generate these, I followed
+https://bhashineen.medium.com/steps-to-create-keystores-and-truststores-to-be-used-in-mutual-ssl-of-a-server-and-a-client-e0b75ca3ea42
+
+1. Create a keystore for the client
+keytool -genkey -alias tika-client -keyalg RSA -keystore 
tika-client-keystore.p12 -keysize 2048 -storeType PKCS12 -validity 9999 -ext 
SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Tika Testing"
+
+2. Export the public cert of the client
+keytool -export -keystore tika-client-keystore.p12 -alias tika-client -file 
tika-client.crt
+
+3. Create a keystore for the server
+keytool -genkey -alias tika-server -keyalg RSA -keystore 
tika-server-keystore.p12 -keysize 2048 -storeType PKCS12 -validity 9999 -ext 
SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Tika Testing"
+
+4. Export the public cert of the server
+keytool -export -keystore tika-server-keystore.p12 -alias tika-server -file 
tika-server.crt
+
+5. Create a truststore for the client
+keytool -genkey -alias tika-client-trust -keyalg RSA -keystore 
tika-client-truststore.p12 -keysize 2048 -storeType PKCS12 -validity 9999 -ext 
SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Tika Testing"
+
+6. Create a truststore for the server
+keytool -genkey -alias tika-server-trust -keyalg RSA -keystore 
tika-server-truststore.p12 -keysize 2048 -storeType PKCS12 -validity 9999 -ext 
SAN=DNS:localhost,IP:127.0.0.1 -dname "CN=localhost, OU=Tika Testing"
+
+7. Import the client public cert into the server truststore
+keytool -import -keystore tika-server-truststore.p12 -alias tika-client -file 
tika-client.crt
+
+8. Import the server public cert into the client truststore
+keytool -import -keystore tika-client-truststore.p12 -alias tika-server -file 
tika-server.crt
+
+NOTE: I did not then delete the private keys because I wanted to leave them in 
in case we needed to do something else.
\ No newline at end of file
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-keystore.p12
 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-keystore.p12
new file mode 100644
index 000000000..44b37fb39
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-keystore.p12
 differ
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-truststore.p12
 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-truststore.p12
new file mode 100644
index 000000000..f020e9205
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client-truststore.p12
 differ
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client.crt 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client.crt
new file mode 100644
index 000000000..fa03cfa39
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-client.crt 
differ
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-keystore.p12
 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-keystore.p12
new file mode 100644
index 000000000..b2d63ae71
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-keystore.p12
 differ
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-truststore.p12
 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-truststore.p12
new file mode 100644
index 000000000..a3ec498ba
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server-truststore.p12
 differ
diff --git 
a/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server.crt 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server.crt
new file mode 100644
index 000000000..41095ef19
Binary files /dev/null and 
b/tika-server/tika-server-core/src/test/resources/ssl-keys/tika-server.crt 
differ

Reply via email to