blob: 9dd16d9184e18fb6fe414626ad28948d93724511 [file] [log] [blame]
/*
* 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.tomcat.util.net;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Locale;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.authenticator.SSLAuthenticator;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
public final class TesterSupport {
public static final String SSL_DIR = "test/org/apache/tomcat/util/net/";
public static final String CA_ALIAS = "ca";
public static final String CA_JKS = SSL_DIR + CA_ALIAS + ".jks";
public static final String CLIENT_ALIAS = "user1";
public static final String CLIENT_JKS = SSL_DIR + CLIENT_ALIAS + ".jks";
public static final String LOCALHOST_JKS = SSL_DIR + "localhost.jks";
public static final String LOCALHOST_KEYPASS_JKS = SSL_DIR + "localhost-copy1.jks";
public static final String JKS_PASS = "changeit";
public static final String JKS_KEY_PASS = "tomcatpass";
public static final String LOCALHOST_CERT_PEM = SSL_DIR + "localhost-cert.pem";
public static final String LOCALHOST_KEY_PEM = SSL_DIR + "localhost-key.pem";
public static final String ROLE = "testrole";
public static void initSsl(Tomcat tomcat) {
initSsl(tomcat, LOCALHOST_JKS, null, null);
}
protected static void initSsl(Tomcat tomcat, String keystore,
String keystorePass, String keyPass) {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.indexOf("Apr") == -1) {
Connector connector = tomcat.getConnector();
connector.setProperty("sslProtocol", "tls");
File keystoreFile =
new File(keystore);
connector.setAttribute("keystoreFile",
keystoreFile.getAbsolutePath());
File truststoreFile = new File(CA_JKS);
connector.setAttribute("truststoreFile",
truststoreFile.getAbsolutePath());
if (keystorePass != null) {
connector.setAttribute("keystorePass", keystorePass);
}
if (keyPass != null) {
connector.setAttribute("keyPass", keyPass);
}
} else {
File keystoreFile = new File(
LOCALHOST_CERT_PEM);
tomcat.getConnector().setAttribute("SSLCertificateFile",
keystoreFile.getAbsolutePath());
keystoreFile = new File(
LOCALHOST_KEY_PEM);
tomcat.getConnector().setAttribute("SSLCertificateKeyFile",
keystoreFile.getAbsolutePath());
}
tomcat.getConnector().setSecure(true);
tomcat.getConnector().setProperty("SSLEnabled", "true");
// OpenSSL before 1.0.1 only supports TLSv1.
// Our default SSLProtocol setting "all" includes unsupported TLSv1.1 and 1.2
// and would produce an error during init.
// Trigger loading of the native library and choose old protocol
// if we use old OpenSSL.
if (AprLifecycleListener.isAprAvailable() && SSL.version() < 0x10001000L) {
tomcat.getConnector().setProperty("SSLProtocol", Constants.SSL_PROTO_TLSv1);
}
}
protected static KeyManager[] getUser1KeyManagers() throws Exception {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(getKeyStore(CLIENT_JKS), JKS_PASS.toCharArray());
return kmf.getKeyManagers();
}
protected static TrustManager[] getTrustManagers() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(getKeyStore(CA_JKS));
return tmf.getTrustManagers();
}
protected static void configureClientSsl() {
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(TesterSupport.getUser1KeyManagers(),
TesterSupport.getTrustManagers(),
null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
private static KeyStore getKeyStore(String keystore) throws Exception {
File keystoreFile = new File(keystore);
KeyStore ks = KeyStore.getInstance("JKS");
try (InputStream is = new FileInputStream(keystoreFile)) {
ks.load(is, JKS_PASS.toCharArray());
}
return ks;
}
protected static boolean isMacOs() {
return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("mac os x");
}
protected static boolean isRenegotiationSupported(Tomcat tomcat) {
String protocol = tomcat.getConnector().getProtocolHandlerClassName();
if (protocol.contains("Apr")) {
// Disabled by default in 1.1.20 windows binary (2010-07-27)
return false;
}
if (protocol.contains("NioProtocol") || (protocol.contains("Nio2Protocol") && isMacOs())) {
// Doesn't work on all platforms - see BZ 56448.
return false;
}
return true;
}
protected static void configureClientCertContext(Tomcat tomcat) {
TesterSupport.initSsl(tomcat);
// Need a web application with a protected and unprotected URL
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "simple", new SimpleServlet());
ctx.addServletMappingDecoded("/unprotected", "simple");
ctx.addServletMappingDecoded("/protected", "simple");
// Security constraints
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded("/protected");
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctx.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
String cn = "NOTFOUND";
try {
KeyStore ks = getKeyStore(CLIENT_JKS);
X509Certificate cert = (X509Certificate)ks.getCertificate(CLIENT_ALIAS);
cn = cert.getSubjectDN().getName();
} catch (Exception ex) {
// Ignore
}
realm.addUser(cn, "not used");
realm.addUserRole(cn, ROLE);
ctx.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod("CLIENT-CERT");
ctx.setLoginConfig(lc);
ctx.getPipeline().addValve(new SSLAuthenticator());
}
public static final byte DATA = (byte)33;
public static class SimpleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.getWriter().print("OK");
if (req.isUserInRole(ROLE)) {
resp.getWriter().print("-" + ROLE);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Swallow any request body
int read = 0;
int len = 0;
byte[] buffer = new byte[4096];
InputStream is = req.getInputStream();
boolean contentOK = true;
while (len > -1) {
len = is.read(buffer);
read = read + len;
for (int i=0; i<len && contentOK; i++) {
contentOK = (buffer[i] == DATA);
}
}
// len will have been -1 on last iteration
read++;
// Report the number of bytes read
resp.setContentType("text/plain");
if (contentOK)
resp.getWriter().print("OK-" + read);
else
resp.getWriter().print("CONTENT-MISMATCH-" + read);
}
}
public static class TrustAllCerts implements X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs,
String authType) {
// NOOP - Trust everything
}
@Override
public void checkServerTrusted(X509Certificate[] certs,
String authType) {
// NOOP - Trust everything
}
}
}