/**
* 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.cxf.systest.sts.stsclient;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
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 org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.endpoint.EndpointException;
import org.apache.cxf.endpoint.EndpointImpl;
import org.apache.cxf.ext.logging.LoggingInInterceptor;
import org.apache.cxf.ext.logging.LoggingOutInterceptor;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.ExchangeImpl;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.service.Service;
import org.apache.cxf.service.ServiceImpl;
import org.apache.cxf.service.model.BindingInfo;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.service.model.ServiceInfo;
import org.apache.cxf.systest.sts.common.SecurityTestUtil;
import org.apache.cxf.systest.sts.deployment.STSServer;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.policy.interceptors.STSTokenOutInterceptor;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.trust.STSAuthParams;
import org.apache.cxf.ws.security.trust.STSAuthParams.AuthMode;
import org.apache.cxf.ws.security.trust.STSClient;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Some tests for STSClient configuration.
*/
public class STSTokenOutInterceptorTest extends AbstractBusClientServerTestBase {
static final String STSPORT = allocatePort(STSServer.class);
static final String STSPORT2 = allocatePort(STSServer.class, 2);
private static final String STS_SERVICE_NAME =
"{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}SecurityTokenService";
private static final String TOKEN_TYPE_SAML_2_0 =
"http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
private static final String SERVICE_ENDPOINT_ASSYMETRIC =
"http://localhost:8081/doubleit/services/doubleitasymmetric";
private static final String STS_X509_WSDL_LOCATION_RELATIVE = "/SecurityTokenService/X509?wsdl";
private static final String STS_X509_ENDPOINT_NAME = "{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}X509_Port";
private static final String KEY_TYPE_X509 = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey";
private static final String SERVICE_ENDPOINT_TRANSPORT =
"https://localhost:8081/doubleit/services/doubleittransportsaml1";
private static final String STS_TRANSPORT_WSDL_LOCATION_RELATIVE = "/SecurityTokenService/Transport?wsdl";
private static final String STS_TRANSPORT_ENDPOINT_NAME =
"{http://docs.oasis-open.org/ws-sx/ws-trust/200512/}Transport_Port";
private static final String CLIENTSTORE = "/keys/clientstore.jks";
private static final String KEYSTORE_PASS = "cspass";
private static final String KEY_PASS = "ckpass";
@BeforeClass
public static void startServers() throws Exception {
STSServer stsServer = new STSServer();
stsServer.setContext("cxf-transport.xml");
assertTrue(launchServer(stsServer));
stsServer = new STSServer();
stsServer.setContext("cxf-x509.xml");
assertTrue(launchServer(stsServer));
}
@AfterClass
public static void cleanup() throws Exception {
SecurityTestUtil.cleanup();
stopAllServers();
}
@Test
public void testBasicAsymmetricBinding() throws Exception {
Bus bus = BusFactory.getThreadDefaultBus();
STSAuthParams authParams = new STSAuthParams(
AuthMode.X509_ASSYMETRIC,
null,
"org.apache.cxf.systest.sts.common.CommonCallbackHandler",
"mystskey",
"clientKeystore.properties");
STSTokenOutInterceptor interceptor = new STSTokenOutInterceptor(
authParams,
"http://localhost:" + STSPORT2 + STS_X509_WSDL_LOCATION_RELATIVE,
bus);
MessageImpl message = prepareMessage(bus, null, SERVICE_ENDPOINT_ASSYMETRIC);
interceptor.handleMessage(message);
SecurityToken token = (SecurityToken)message.getExchange().get(SecurityConstants.TOKEN);
validateSecurityToken(token);
}
@Test
public void testBasicTransportBinding() throws Exception {
// Setup HttpsURLConnection to get STS WSDL
configureDefaultHttpsConnection();
Bus bus = BusFactory.getThreadDefaultBus();
STSAuthParams authParams = new STSAuthParams(
AuthMode.UT_TRANSPORT,
"alice",
"org.apache.cxf.systest.sts.common.CommonCallbackHandler",
null,
null);
STSTokenOutInterceptor interceptor = new STSTokenOutInterceptor(
authParams,
"https://localhost:" + STSPORT + STS_TRANSPORT_WSDL_LOCATION_RELATIVE,
bus);
TLSClientParameters tlsParams = prepareTLSParams();
STSClient stsClient = interceptor.getSTSClient();
((HTTPConduit)stsClient.getClient().getConduit()).setTlsClientParameters(tlsParams);
MessageImpl message = prepareMessage(bus, null, SERVICE_ENDPOINT_TRANSPORT);
interceptor.handleMessage(message);
SecurityToken token = (SecurityToken)message.getExchange().get(SecurityConstants.TOKEN);
validateSecurityToken(token);
}
@Test
public void testSTSClientAsymmetricBinding() throws Exception {
Bus bus = BusFactory.getThreadDefaultBus();
STSClient stsClient = initStsClientAsymmeticBinding(bus);
STSTokenOutInterceptor interceptor = new STSTokenOutInterceptor(stsClient);
MessageImpl message = prepareMessage(bus, null, SERVICE_ENDPOINT_ASSYMETRIC);
interceptor.handleMessage(message);
SecurityToken token = (SecurityToken)message.getExchange().get(SecurityConstants.TOKEN);
validateSecurityToken(token);
}
@Test
public void testSTSClientTransportBinding() throws Exception {
// Setup HttpsURLConnection to get STS WSDL
configureDefaultHttpsConnection();
Bus bus = BusFactory.getThreadDefaultBus();
STSClient stsClient = initStsClientTransportBinding(bus);
STSTokenOutInterceptor interceptor = new STSTokenOutInterceptor(stsClient);
TLSClientParameters tlsParams = prepareTLSParams();
((HTTPConduit)stsClient.getClient().getConduit()).setTlsClientParameters(tlsParams);
MessageImpl message = prepareMessage(bus, null, SERVICE_ENDPOINT_TRANSPORT);
interceptor.handleMessage(message);
SecurityToken token = (SecurityToken)message.getExchange().get(SecurityConstants.TOKEN);
validateSecurityToken(token);
}
private STSClient initStsClientAsymmeticBinding(Bus bus) {
bus.getInInterceptors().add(new LoggingOutInterceptor());
bus.getOutInterceptors().add(new LoggingInInterceptor());
bus.getOutFaultInterceptors().add(new LoggingInInterceptor());
STSClient stsClient = new STSClient(bus);
stsClient.setWsdlLocation("http://localhost:" + STSPORT2 + STS_X509_WSDL_LOCATION_RELATIVE);
stsClient.setServiceName(STS_SERVICE_NAME);
stsClient.setEndpointName(STS_X509_ENDPOINT_NAME);
stsClient.setTokenType(TOKEN_TYPE_SAML_2_0);
stsClient.setKeyType(KEY_TYPE_X509);
stsClient.setAllowRenewingAfterExpiry(true);
stsClient.setEnableLifetime(true);
Map<String, Object> props = new HashMap<>();
props.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler");
props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey");
props.put(SecurityConstants.ENCRYPT_PROPERTIES, "clientKeystore.properties");
props.put(SecurityConstants.SIGNATURE_PROPERTIES, "clientKeystore.properties");
props.put(SecurityConstants.STS_TOKEN_USERNAME, "mystskey");
props.put(SecurityConstants.STS_TOKEN_PROPERTIES, "clientKeystore.properties");
props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true");
stsClient.setProperties(props);
return stsClient;
}
private STSClient initStsClientTransportBinding(Bus bus) {
bus.getInInterceptors().add(new LoggingOutInterceptor());
bus.getOutInterceptors().add(new LoggingInInterceptor());
bus.getOutFaultInterceptors().add(new LoggingInInterceptor());
STSClient stsClient = new STSClient(bus);
stsClient.setWsdlLocation("https://localhost:" + STSPORT + STS_TRANSPORT_WSDL_LOCATION_RELATIVE);
stsClient.setServiceName(STS_SERVICE_NAME);
stsClient.setEndpointName(STS_TRANSPORT_ENDPOINT_NAME);
stsClient.setTokenType(TOKEN_TYPE_SAML_2_0);
stsClient.setAllowRenewingAfterExpiry(true);
stsClient.setEnableLifetime(true);
Map<String, Object> props = new HashMap<>();
props.put(SecurityConstants.USERNAME, "alice");
props.put(SecurityConstants.CALLBACK_HANDLER, "org.apache.cxf.systest.sts.common.CommonCallbackHandler");
stsClient.setProperties(props);
return stsClient;
}
private MessageImpl prepareMessage(Bus bus, STSClient stsClient, String serviceAddress) throws EndpointException {
MessageImpl message = new MessageImpl();
message.put(SecurityConstants.STS_CLIENT, stsClient);
message.put(Message.ENDPOINT_ADDRESS, serviceAddress);
Exchange exchange = new ExchangeImpl();
ServiceInfo si = new ServiceInfo();
Service s = new ServiceImpl(si);
EndpointInfo ei = new EndpointInfo();
Endpoint ep = new EndpointImpl(bus, s, ei);
ei.setBinding(new BindingInfo(si, null));
message.setExchange(exchange);
exchange.put(Endpoint.class, ep);
return message;
}
private void configureDefaultHttpsConnection() throws NoSuchAlgorithmException, KeyStoreException,
CertificateException, IOException, KeyManagementException {
// For localhost testing only
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {
public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) {
return "localhost".equals(hostname);
}
});
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
KeyStore keyStore = loadClientKeystore();
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustManagers, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Needed to prevent test failure using IBM JDK
if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
System.setProperty("https.protocols", "TLSv1");
}
}
private TLSClientParameters prepareTLSParams() throws KeyStoreException, IOException,
NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
TLSClientParameters tlsParams = new TLSClientParameters();
tlsParams.setDisableCNCheck(true);
KeyStore trustStore = loadClientKeystore();
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory
.getDefaultAlgorithm());
trustFactory.init(trustStore);
TrustManager[] tm = trustFactory.getTrustManagers();
tlsParams.setTrustManagers(tm);
KeyStore keyStore = loadClientKeystore();
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, KEY_PASS.toCharArray());
KeyManager[] km = keyFactory.getKeyManagers();
tlsParams.setKeyManagers(km);
return tlsParams;
}
private KeyStore loadClientKeystore() throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream keystoreStream = STSTokenOutInterceptorTest.class.getResourceAsStream(CLIENTSTORE);
try {
keystore.load(keystoreStream, KEYSTORE_PASS.toCharArray());
} finally {
keystoreStream.close();
}
return keystore;
}
private void validateSecurityToken(SecurityToken token) {
Assert.assertNotNull(token);
Assert.assertEquals(TOKEN_TYPE_SAML_2_0, token.getTokenType());
Assert.assertNotNull(token.getId());
Assert.assertTrue(token.getExpires().isAfter(Instant.now()));
Assert.assertEquals("Assertion", token.getToken().getLocalName());
}
}