Java tutorial
/* * 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.synapse.transport.nhttp; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import org.apache.http.config.ConnectionConfig; import org.apache.http.nio.NHttpClientEventHandler; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.IOSession; import org.apache.http.nio.reactor.ssl.SSLSetupHandler; import org.apache.axis2.AxisFault; import org.apache.axis2.description.Parameter; import org.apache.axis2.description.TransportOutDescription; import org.apache.axis2.transport.base.ParamUtils; import org.apache.axiom.om.OMElement; import org.apache.synapse.transport.utils.logging.LoggingUtils; import org.apache.synapse.transport.utils.sslcert.CertificateVerificationConfig; import org.apache.synapse.transport.utils.sslcert.CertificateVerificationException; import org.apache.synapse.transport.utils.sslcert.adaptor.SynapseAdaptorForOcspCrl; import javax.net.ssl.*; import javax.xml.namespace.QName; import java.util.Map; import java.util.Iterator; import java.util.HashMap; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyStore; import java.security.GeneralSecurityException; import java.net.SocketAddress; import java.net.InetSocketAddress; public class HttpCoreNIOSSLSender extends HttpCoreNIOSender { private static final Log log = LogFactory.getLog(HttpCoreNIOSSLSender.class); private static final SynapseAdaptorForOcspCrl ocspCrl = new SynapseAdaptorForOcspCrl(); protected IOEventDispatch getEventDispatch(NHttpClientEventHandler handler, SSLContext sslContext, SSLSetupHandler sslSetupHandler, ConnectionConfig config, TransportOutDescription transportOut) throws AxisFault { return LoggingUtils.getClientIODispatch(handler, config, sslContext, sslSetupHandler, getCustomSSLContexts(transportOut)); } /** * Create the SSLContext to be used by this sender * * @param transportOut the Axis2 transport configuration * @return the SSLContext to be used */ protected SSLContext getSSLContext(TransportOutDescription transportOut) throws AxisFault { Parameter keyParam = transportOut.getParameter("keystore"); Parameter trustParam = transportOut.getParameter("truststore"); OMElement ksEle = null; OMElement tsEle = null; if (keyParam != null) { ksEle = keyParam.getParameterElement().getFirstElement(); } boolean novalidatecert = ParamUtils.getOptionalParamBoolean(transportOut, "novalidatecert", false); if (trustParam != null) { if (novalidatecert) { log.warn("Ignoring novalidatecert parameter since a truststore has been specified"); } tsEle = trustParam.getParameterElement().getFirstElement(); } return createSSLContext(ksEle, tsEle, novalidatecert); } /** * Create the SSLIOSessionHandler to initialize the host name verification at the following * levels, through an Axis2 transport configuration parameter as follows: * HostnameVerifier - Default, DefaultAndLocalhost, Strict, AllowAll * * @param transportOut the Axis2 transport configuration * @return the SSLIOSessionHandler to be used * @throws AxisFault if a configuration error occurs */ protected SSLSetupHandler getSSLSetupHandler(TransportOutDescription transportOut) throws AxisFault { Parameter hostnameVerifier = transportOut.getParameter("HostnameVerifier"); String hostnameVerifierValue = hostnameVerifier != null ? hostnameVerifier.getValue().toString() : null; Parameter revocationVerifierParam = transportOut.getParameter("CertificateRevocationVerifier"); CertificateVerificationConfig cvConfig = revocationVerifierParam != null ? new CertificateVerificationConfig(revocationVerifierParam) : null; return createSSLSetupHandler(hostnameVerifierValue, cvConfig); } /** * Looks for a transport parameter named customSSLProfiles and initializes zero or more * custom SSLContext instances. The syntax for defining custom SSL profiles is as follows. * * <parameter name="customSSLProfiles> * <profile> * <servers>www.test.org:80, www.test2.com:9763</servers> * <KeyStore> * <Location>/path/to/identity/store</Location> * <Type>JKS</Type> * <Password>password</Password> * <KeyPassword>password</KeyPassword> * </KeyStore> * <TrustStore> * <Location>path/tp/trust/store</Location> * <Type>JKS</Type> * <Password>password</Password> * </TrustStore> * </profile> * </parameter> * * Any number of profiles can be defined under the customSSLProfiles parameter. * * @param transportOut transport out description * @return a map of server addresses and SSL contexts * @throws AxisFault if at least on SSL profile is not properly configured */ private Map<String, SSLContext> getCustomSSLContexts(TransportOutDescription transportOut) throws AxisFault { if (log.isDebugEnabled()) { log.info("Loading custom SSL profiles for the HTTPS sender"); } Parameter customProfilesParam = transportOut.getParameter("customSSLProfiles"); if (customProfilesParam == null) { return null; } OMElement customProfilesElt = customProfilesParam.getParameterElement(); Iterator profiles = customProfilesElt.getChildrenWithName(new QName("profile")); Map<String, SSLContext> contextMap = new HashMap<String, SSLContext>(); while (profiles.hasNext()) { OMElement profile = (OMElement) profiles.next(); OMElement serversElt = profile.getFirstChildWithName(new QName("servers")); if (serversElt == null || serversElt.getText() == null) { String msg = "Each custom SSL profile must define at least one host:port " + "pair under the servers element"; log.error(msg); throw new AxisFault(msg); } String[] servers = serversElt.getText().split(","); OMElement ksElt = profile.getFirstChildWithName(new QName("KeyStore")); OMElement trElt = profile.getFirstChildWithName(new QName("TrustStore")); String noValCert = profile.getAttributeValue(new QName("novalidatecert")); boolean novalidatecert = "true".equals(noValCert); SSLContext sslContext = createSSLContext(ksElt, trElt, novalidatecert); for (String server : servers) { server = server.trim(); if (!contextMap.containsKey(server)) { contextMap.put(server, sslContext); } else { log.warn("Multiple SSL profiles were found for the server : " + server + ". " + "Ignoring the excessive profiles."); } } } if (contextMap.size() > 0) { log.info("Custom SSL profiles initialized for " + contextMap.size() + " servers"); return contextMap; } return null; } private SSLContext createSSLContext(OMElement keyStoreElt, OMElement trustStoreElt, boolean novalidatecert) throws AxisFault { KeyManager[] keymanagers = null; TrustManager[] trustManagers = null; if (keyStoreElt != null) { String location = keyStoreElt.getFirstChildWithName(new QName("Location")).getText(); String type = keyStoreElt.getFirstChildWithName(new QName("Type")).getText(); String storePassword = keyStoreElt.getFirstChildWithName(new QName("Password")).getText(); String keyPassword = keyStoreElt.getFirstChildWithName(new QName("KeyPassword")).getText(); FileInputStream fis = null; try { KeyStore keyStore = KeyStore.getInstance(type); fis = new FileInputStream(location); log.info("Loading Identity Keystore from : " + location); keyStore.load(fis, storePassword.toCharArray()); KeyManagerFactory kmfactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmfactory.init(keyStore, keyPassword.toCharArray()); keymanagers = kmfactory.getKeyManagers(); } catch (GeneralSecurityException gse) { log.error("Error loading Keystore : " + location, gse); throw new AxisFault("Error loading Keystore : " + location, gse); } catch (IOException ioe) { log.error("Error opening Keystore : " + location, ioe); throw new AxisFault("Error opening Keystore : " + location, ioe); } finally { if (fis != null) { try { fis.close(); } catch (IOException ignore) { } } } } if (trustStoreElt != null) { if (novalidatecert) { log.warn("Ignoring novalidatecert parameter since a truststore has been specified"); } String location = trustStoreElt.getFirstChildWithName(new QName("Location")).getText(); String type = trustStoreElt.getFirstChildWithName(new QName("Type")).getText(); String storePassword = trustStoreElt.getFirstChildWithName(new QName("Password")).getText(); FileInputStream fis = null; try { KeyStore trustStore = KeyStore.getInstance(type); fis = new FileInputStream(location); log.info("Loading Trust Keystore from : " + location); trustStore.load(fis, storePassword.toCharArray()); TrustManagerFactory trustManagerfactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerfactory.init(trustStore); trustManagers = trustManagerfactory.getTrustManagers(); } catch (GeneralSecurityException gse) { log.error("Error loading Key store : " + location, gse); throw new AxisFault("Error loading Key store : " + location, gse); } catch (IOException ioe) { log.error("Error opening Key store : " + location, ioe); throw new AxisFault("Error opening Key store : " + location, ioe); } finally { if (fis != null) { try { fis.close(); } catch (IOException ignore) { } } } } else if (novalidatecert) { log.warn("Server certificate validation (trust) has been disabled. " + "DO NOT USE IN PRODUCTION!"); trustManagers = new TrustManager[] { new NoValidateCertTrustManager() }; } try { SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(keymanagers, trustManagers, null); return sslcontext; } catch (GeneralSecurityException gse) { log.error("Unable to create SSL context with the given configuration", gse); throw new AxisFault("Unable to create SSL context with the given configuration", gse); } } private SSLSetupHandler createSSLSetupHandler(final String hostnameVerifier, final CertificateVerificationConfig cvConfig) throws AxisFault { return new SSLSetupHandler() { public void initalize(SSLEngine sslengine) { } public void verify(IOSession ioSession, SSLSession session) throws SSLException { SocketAddress remoteAddress = ioSession.getRemoteAddress(); String address; if (remoteAddress instanceof InetSocketAddress) { address = ((InetSocketAddress) remoteAddress).getHostName(); } else { address = remoteAddress.toString(); } boolean valid = false; //Do HostName verification. if (hostnameVerifier != null) { if ("Strict".equals(hostnameVerifier)) { valid = HostnameVerifier.STRICT.verify(address, session); } else if ("AllowAll".equals(hostnameVerifier)) { valid = HostnameVerifier.ALLOW_ALL.verify(address, session); } else if ("DefaultAndLocalhost".equals(hostnameVerifier)) { valid = HostnameVerifier.DEFAULT_AND_LOCALHOST.verify(address, session); } } else { valid = HostnameVerifier.DEFAULT.verify(address, session); } if (!valid) { throw new SSLException("Host name verification failed for host : " + address); } if (cvConfig != null) { try { ocspCrl.verifyRevocationStatus(session.getPeerCertificateChain(), cvConfig.getCacheSize(), cvConfig.getCacheDuration()); } catch (CertificateVerificationException e) { throw new SSLException("Certificate chain validation failed for host : " + address, e); } } } }; } }