| /* |
| * 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 |
| } |
| } |
| } |