From e34fb937d155f6c0542dd7aca359c4449b6ce51a Mon Sep 17 00:00:00 2001 From: Jamin Hitchcock Date: Wed, 6 Apr 2016 20:50:32 -0500 Subject: [PATCH] SWS-955 - Add method for configuring SAML callback. --- .../wss4j2/Wss4jSecurityInterceptor.java | 17 ++- .../AxiomWss4jMessageInterceptorSamlTest.java | 5 + .../SaajWss4jMessageInterceptorSamlTest.java | 5 + .../Wss4jMessageInterceptorSamlTestCase.java | 111 ++++++++++++++++++ .../soap/security/wss4j2/Wss4jTestCase.java | 1 + 5 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/AxiomWss4jMessageInterceptorSamlTest.java create mode 100644 spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSamlTest.java create mode 100644 spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSamlTestCase.java diff --git a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java index 9c05300c..5edb143d 100644 --- a/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java +++ b/spring-ws-security/src/main/java/org/springframework/ws/soap/security/wss4j2/Wss4jSecurityInterceptor.java @@ -143,6 +143,8 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl private boolean securementUseDerivedKey; + private CallbackHandler samlCallbackHandler; + // Allow RSA 15 to maintain default behavior private boolean allowRSA15KeyTransportAlgorithm = true; @@ -373,6 +375,15 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl public void setSecurementUseDerivedKey(boolean securementUseDerivedKey) { this.securementUseDerivedKey = securementUseDerivedKey; } + + /** + * Sets the SAML Callback used for generating SAML tokens. + * + * @param samlCallback + */ + public void setSecurementSamlCallbackHandler(CallbackHandler samlCallbackHandler) { + this.samlCallbackHandler = samlCallbackHandler; + } /** Sets the server-side time to live */ public void setValidationTimeToLive(int validationTimeToLive) { @@ -595,7 +606,11 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl requestData.setWssConfig(wssConfig); messageContext.setProperty(WSHandlerConstants.TTL_TIMESTAMP, Integer.toString(securementTimeToLive)); - + + if (this.samlCallbackHandler != null) { + messageContext.setProperty(WSHandlerConstants.SAML_CALLBACK_REF, this.samlCallbackHandler); + } + // allow for qualified password types for .Net interoperability requestData.setAllowNamespaceQualifiedPasswordTypes(true); diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/AxiomWss4jMessageInterceptorSamlTest.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/AxiomWss4jMessageInterceptorSamlTest.java new file mode 100644 index 00000000..0f2bba4f --- /dev/null +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/AxiomWss4jMessageInterceptorSamlTest.java @@ -0,0 +1,5 @@ +package org.springframework.ws.soap.security.wss4j2; + +public class AxiomWss4jMessageInterceptorSamlTest extends Wss4jMessageInterceptorSamlTestCase { + +} diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSamlTest.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSamlTest.java new file mode 100644 index 00000000..3f77aede --- /dev/null +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/SaajWss4jMessageInterceptorSamlTest.java @@ -0,0 +1,5 @@ +package org.springframework.ws.soap.security.wss4j2; + +public class SaajWss4jMessageInterceptorSamlTest extends Wss4jMessageInterceptorSamlTestCase { + +} diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSamlTestCase.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSamlTestCase.java new file mode 100644 index 00000000..419e02f7 --- /dev/null +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jMessageInterceptorSamlTestCase.java @@ -0,0 +1,111 @@ +package org.springframework.ws.soap.security.wss4j2; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.apache.wss4j.common.crypto.Crypto; +import org.apache.wss4j.common.crypto.CryptoType; +import org.apache.wss4j.common.crypto.Merlin; +import org.apache.wss4j.common.saml.SAMLCallback; +import org.apache.wss4j.common.saml.bean.KeyInfoBean; +import org.apache.wss4j.common.saml.bean.SubjectBean; +import org.apache.wss4j.common.saml.bean.Version; +import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.soap.SoapMessage; +import org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean; +import org.w3c.dom.Document; + +public abstract class Wss4jMessageInterceptorSamlTestCase extends Wss4jTestCase { + + protected Wss4jSecurityInterceptor interceptor; + + @Override + protected void onSetup() throws Exception { + interceptor = new Wss4jSecurityInterceptor(); + interceptor.setSecurementActions("SAMLTokenSigned"); + interceptor.setValidationActions("SAMLTokenSigned Signature"); + CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean(); + cryptoFactoryBean.setCryptoProvider(Merlin.class); + cryptoFactoryBean.setKeyStoreType("jceks"); + cryptoFactoryBean.setKeyStorePassword("123456"); + cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("private.jks")); + cryptoFactoryBean.afterPropertiesSet(); + Crypto crypto = cryptoFactoryBean.getObject(); + + CryptoType type = new CryptoType(CryptoType.TYPE.ALIAS); + type.setAlias("rsaKey"); + X509Certificate userCertificate = crypto.getX509Certificates(type)[0]; + + interceptor.setSecurementSignatureCrypto(crypto); + interceptor.setValidationSignatureCrypto(crypto); + interceptor.setSecurementSamlCallbackHandler(getSamlCalbackHandler(crypto, userCertificate)); + interceptor.afterPropertiesSet(); + + } + + @Test + public void testAddSAML() throws Exception + { + interceptor.setSecurementPassword("123456"); + interceptor.setSecurementUsername("rsaKey"); + SoapMessage message = loadSoap11Message("empty-soap.xml"); + MessageContext messageContext = getSoap11MessageContext(message); + + interceptor.secureMessage(message, messageContext); + Document document = getDocument(message); + + assertXpathExists("Absent SAML Assertion element", + "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/saml:Assertion", document); + + // lets verify the signature that we've just generated + interceptor.validateMessage(message, messageContext); + } + + protected CallbackHandler getSamlCalbackHandler(Crypto crypto, X509Certificate userCert) + { + return new SamlCallbackHandler(crypto, userCert); + } + + private class SamlCallbackHandler implements CallbackHandler { + + private Crypto crypto; + + private X509Certificate userCertificate; + + public SamlCallbackHandler(Crypto crypto, X509Certificate userCertificate) + { + this.crypto = crypto; + this.userCertificate = userCertificate; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof SAMLCallback) { + SAMLCallback callback = (SAMLCallback) callbacks[i]; + callback.setSamlVersion(Version.SAML_20); + callback.setIssuerCrypto(crypto); + callback.setIssuerKeyName("rsaKey"); + callback.setIssuerKeyPassword("123456"); + callback.setIssuer("test-issuer"); + SubjectBean subject = new SubjectBean("test-subject", "", SAML2Constants.CONF_BEARER); + KeyInfoBean keyInfo = new KeyInfoBean(); + keyInfo.setCertificate(userCertificate); + subject.setKeyInfo(keyInfo); + callback.setSubject(subject); + callback.setSignAssertion(true); + } + } + } + + } + +} diff --git a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jTestCase.java b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jTestCase.java index 9df9ad7c..8f363ea9 100644 --- a/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jTestCase.java +++ b/spring-ws-security/src/test/java/org/springframework/ws/soap/security/wss4j2/Wss4jTestCase.java @@ -79,6 +79,7 @@ public abstract class Wss4jTestCase { namespaces.put("echo", "http://www.springframework.org/spring-ws/samples/echo"); namespaces.put("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); + namespaces.put("saml", "urn:oasis:names:tc:SAML:2.0:assertion"); namespaces.put("test", "http://test"); xpathTemplate.setNamespaces(namespaces); onSetup();