Skip to content

Commit dc6b149

Browse files
committed
Done implementing SWS-207
1 parent fca4cd3 commit dc6b149

25 files changed

+969
-434
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.soap.security.callback;
18+
19+
import java.io.IOException;
20+
import javax.security.auth.callback.Callback;
21+
import javax.security.auth.callback.CallbackHandler;
22+
import javax.security.auth.callback.UnsupportedCallbackException;
23+
24+
/**
25+
* Represents a chain of <code>CallbackHandler</code>s. For each callback, each of the handlers is called in term. If a
26+
* handler throws a <code>UnsupportedCallbackException</code>, the next handler is tried.
27+
*
28+
* @author Arjen Poutsma
29+
* @since 1.5.0
30+
*/
31+
public class CallbackHandlerChain extends AbstractCallbackHandler {
32+
33+
private final CallbackHandler[] callbackHandlers;
34+
35+
public CallbackHandlerChain(CallbackHandler[] callbackHandlers) {
36+
this.callbackHandlers = callbackHandlers;
37+
}
38+
39+
public CallbackHandler[] getCallbackHandlers() {
40+
return callbackHandlers;
41+
}
42+
43+
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
44+
boolean allUnsupported = true;
45+
for (int i = 0; i < callbackHandlers.length; i++) {
46+
CallbackHandler callbackHandler = callbackHandlers[i];
47+
try {
48+
callbackHandler.handle(new Callback[]{callback});
49+
allUnsupported = false;
50+
}
51+
catch (UnsupportedCallbackException ex) {
52+
// if an UnsupportedCallbackException occurs, go to the next handler
53+
}
54+
}
55+
if (allUnsupported) {
56+
throw new UnsupportedCallbackException(callback);
57+
}
58+
}
59+
}

security/src/main/java/org/springframework/ws/soap/security/support/KeyStoreFactoryBean.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public void setType(String type) {
8787
this.type = type;
8888
}
8989

90-
public Object getObject() throws Exception {
90+
public Object getObject() {
9191
return keyStore;
9292
}
9393

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright 2008 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.ws.soap.security.support;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.security.GeneralSecurityException;
22+
import java.security.KeyStore;
23+
24+
import org.springframework.core.io.FileSystemResource;
25+
import org.springframework.core.io.Resource;
26+
import org.springframework.util.StringUtils;
27+
28+
/**
29+
* Generic utility methods for dealing with {@link KeyStore} objects.
30+
*
31+
* @author Arjen Poutsma
32+
* @since 1.5.0
33+
*/
34+
public abstract class KeyStoreUtils {
35+
36+
/**
37+
* Loads the key store indicated by system properties. This method tries to load a key store by consulting the
38+
* following system properties:<code>javax.net.ssl.keyStore</code>, <code>javax.net.ssl.keyStorePassword</code>, and
39+
* <code>javax.net.ssl.keyStoreType</code>.
40+
* <p/>
41+
* If these properties specify a file with an appropriate password, the factory uses this file for the key store. If
42+
* that file does not exist, then a default, empty keystore is created.
43+
* <p/>
44+
* This behavior corresponds to the standard J2SDK behavior for SSL key stores.
45+
*
46+
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509KeyManager">The
47+
* standard J2SDK SSL key store mechanism</a>
48+
*/
49+
public static KeyStore loadDefaultKeyStore() throws GeneralSecurityException, IOException {
50+
Resource location = null;
51+
String type = null;
52+
String password = null;
53+
String locationProperty = System.getProperty("javax.net.ssl.keyStore");
54+
if (StringUtils.hasLength(locationProperty)) {
55+
File f = new File(locationProperty);
56+
if (f.exists() && f.isFile() && f.canRead()) {
57+
location = new FileSystemResource(f);
58+
}
59+
String passwordProperty = System.getProperty("javax.net.ssl.keyStorePassword");
60+
if (StringUtils.hasLength(passwordProperty)) {
61+
password = passwordProperty;
62+
}
63+
type = System.getProperty("javax.net.ssl.trustStore");
64+
}
65+
// use the factory bean here, easier to setup
66+
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
67+
factoryBean.setLocation(location);
68+
factoryBean.setPassword(password);
69+
factoryBean.setType(type);
70+
factoryBean.afterPropertiesSet();
71+
return (KeyStore) factoryBean.getObject();
72+
}
73+
74+
/**
75+
* Loads a default trust store. This method uses the following algorithm: <ol> <li> If the system property
76+
* <code>javax.net.ssl.trustStore</code> is defined, its value is loaded. If the
77+
* <code>javax.net.ssl.trustStorePassword</code> system property is also defined, its value is used as a password.
78+
* If the <code>javax.net.ssl.trustStoreType</code> system property is defined, its value is used as a key store
79+
* type.
80+
* <p/>
81+
* If <code>javax.net.ssl.trustStore</code> is defined but the specified file does not exist, then a default, empty
82+
* trust store is created. </li> <li> If the <code>javax.net.ssl.trustStore</code> system property was not
83+
* specified, but if the file <code>$JAVA_HOME/lib/security/jssecacerts</code> exists, that file is used. </li>
84+
* Otherwise, <li>If the file <code>$JAVA_HOME/lib/security/cacerts</code> exists, that file is used. </ol>
85+
* <p/>
86+
* This behavior corresponds to the standard J2SDK behavior for SSL trust stores.
87+
*
88+
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
89+
* standard J2SDK SSL trust store mechanism</a>
90+
*/
91+
public static KeyStore loadDefaultTrustStore() throws GeneralSecurityException, IOException {
92+
Resource location = null;
93+
String type = null;
94+
String password = null;
95+
String locationProperty = System.getProperty("javax.net.ssl.trustStore");
96+
if (StringUtils.hasLength(locationProperty)) {
97+
File f = new File(locationProperty);
98+
if (f.exists() && f.isFile() && f.canRead()) {
99+
location = new FileSystemResource(f);
100+
}
101+
String passwordProperty = System.getProperty("javax.net.ssl.trustStorePassword");
102+
if (StringUtils.hasLength(passwordProperty)) {
103+
password = passwordProperty;
104+
}
105+
type = System.getProperty("javax.net.ssl.trustStoreType");
106+
}
107+
else {
108+
String javaHome = System.getProperty("java.home");
109+
location = new FileSystemResource(javaHome + "/lib/security/jssecacerts");
110+
if (!location.exists()) {
111+
location = new FileSystemResource(javaHome + "/lib/security/cacerts");
112+
}
113+
}
114+
// use the factory bean here, easier to setup
115+
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
116+
factoryBean.setLocation(location);
117+
factoryBean.setPassword(password);
118+
factoryBean.setType(type);
119+
factoryBean.afterPropertiesSet();
120+
return (KeyStore) factoryBean.getObject();
121+
}
122+
123+
}

security/src/main/java/org/springframework/ws/soap/security/wss4j/Wss4jSecurityInterceptor.java

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -46,39 +46,31 @@
4646
import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
4747
import org.springframework.ws.soap.security.WsSecuritySecurementException;
4848
import org.springframework.ws.soap.security.WsSecurityValidationException;
49+
import org.springframework.ws.soap.security.callback.CallbackHandlerChain;
4950

5051
/**
5152
* A WS-Security endpoint interceptor based on Apache's WSS4J. This inteceptor supports messages created by the {@link
5253
* org.springframework.ws.soap.axiom.AxiomSoapMessageFactory} and the {@link org.springframework.ws.soap.saaj.SaajSoapMessageFactory}.
5354
* <p/>
54-
* The validation and securement actions executed by this interceptor are configured via <code>validationActions</code> and
55-
* <code>securementActions</code> properties, respectively. Actions should be passed as a space-separated strings.
55+
* The validation and securement actions executed by this interceptor are configured via <code>validationActions</code>
56+
* and <code>securementActions</code> properties, respectively. Actions should be passed as a space-separated strings.
5657
* <p/>
5758
* Valid <strong>validation</strong> actions are:
58-
*
59-
* <blockquote><table>
60-
* <tr><th>Validation action</th><th>Description</th></tr>
61-
* <tr><td><code>UsernameToken</code></td><td>Validates username token</td></tr>
62-
* <tr><td><code>Timestamp</code></td><td>Validates the timestamp</td></tr>
63-
* <tr><td><code>Encrypt</code></td><td>Decrypts the message</td></tr>
64-
* <tr><td><code>Signature</code></td><td>Validates the signature</td></tr>
65-
* <tr><td><code>NoSecurity</code></td><td>No action performed</td></tr>
66-
* </table></blockquote>
6759
* <p/>
68-
* <strong>Securement</strong> actions are:
69-
* <blockquote><table>
70-
* <tr><th>Securement action</th><th>Description</th></tr>
71-
* <tr><td><code>UsernameToken</td></code><td>Adds a username token</td></tr>
72-
* <tr><td><code>UsernameTokenSignature</td></code><td>Adds a username token and a signature username token secrect key</td></tr>
73-
* <tr><td><code>Timestamp</td></code><td>Adds a timestamp</td></tr>
74-
* <tr><td><code>Encrypt</td></code><td>Encrypts the response</td></tr>
75-
* <tr><td><code>Signature</td></code><td>Signs the response</td></tr>
76-
* <tr><td><code>NoSecurity</td></code><td>No action performed</td></tr>
77-
* </table></blockquote>
60+
* <blockquote><table> <tr><th>Validation action</th><th>Description</th></tr> <tr><td><code>UsernameToken</code></td><td>Validates
61+
* username token</td></tr> <tr><td><code>Timestamp</code></td><td>Validates the timestamp</td></tr>
62+
* <tr><td><code>Encrypt</code></td><td>Decrypts the message</td></tr> <tr><td><code>Signature</code></td><td>Validates
63+
* the signature</td></tr> <tr><td><code>NoSecurity</code></td><td>No action performed</td></tr> </table></blockquote>
7864
* <p/>
79-
* The order of the actions that the client performed to secure the messages is significant and is
80-
* enforced by the interceptor.
81-
*
65+
* <strong>Securement</strong> actions are: <blockquote><table> <tr><th>Securement action</th><th>Description</th></tr>
66+
* <tr><td><code>UsernameToken</td></code><td>Adds a username token</td></tr> <tr><td><code>UsernameTokenSignature</td></code><td>Adds
67+
* a username token and a signature username token secrect key</td></tr> <tr><td><code>Timestamp</td></code><td>Adds a
68+
* timestamp</td></tr> <tr><td><code>Encrypt</td></code><td>Encrypts the response</td></tr>
69+
* <tr><td><code>Signature</td></code><td>Signs the response</td></tr> <tr><td><code>NoSecurity</td></code><td>No action
70+
* performed</td></tr> </table></blockquote>
71+
* <p/>
72+
* The order of the actions that the client performed to secure the messages is significant and is enforced by the
73+
* interceptor.
8274
*
8375
* @author Tareq Abed Rabbo
8476
* @author Arjen Poutsma
@@ -89,8 +81,6 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
8981

9082
public static final String SECUREMENT_USER_PROPERTY_NAME = "Wss4jSecurityInterceptor.securementUser";
9183

92-
private CallbackHandler validationCallbackHandler;
93-
9484
private int securementAction;
9585

9686
private String securementActions;
@@ -99,9 +89,7 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
9989

10090
private String securementUsername;
10191

102-
private boolean timestampStrict = true;
103-
104-
private int timeToLive = 300;
92+
private CallbackHandler validationCallbackHandler;
10593

10694
private int validationAction;
10795

@@ -115,10 +103,14 @@ public class Wss4jSecurityInterceptor extends AbstractWsSecurityInterceptor impl
115103

116104
private Crypto validationSignatureCrypto;
117105

118-
private Wss4jHandler handler = new Wss4jHandler();
106+
private boolean timestampStrict = true;
119107

120108
private boolean enableSignatureConfirmation;
121109

110+
private int timeToLive = 300;
111+
112+
private Wss4jHandler handler = new Wss4jHandler();
113+
122114
public void setSecurementActions(String securementActions) {
123115
this.securementActions = securementActions;
124116
securementActionsVector = new Vector();
@@ -141,10 +133,24 @@ public void setSecurementActor(String securementActor) {
141133
handler.setOption(WSHandlerConstants.ACTOR, securementActor);
142134
}
143135

136+
/**
137+
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handler to use when securing messages.
138+
*
139+
* @see #setSecurementCallbackHandlers(CallbackHandler[])
140+
*/
144141
public void setSecurementCallbackHandler(CallbackHandler securementCallbackHandler) {
145142
handler.setSecurementCallbackHandler(securementCallbackHandler);
146143
}
147144

145+
/**
146+
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handlers to use when securing messages.
147+
*
148+
* @see #setSecurementCallbackHandler(CallbackHandler)
149+
*/
150+
public void setSecurementCallbackHandlers(CallbackHandler[] securementCallbackHandler) {
151+
handler.setSecurementCallbackHandler(new CallbackHandlerChain(securementCallbackHandler));
152+
}
153+
148154
public void setSecurementEncryptionCrypto(Crypto securementEncryptionCrypto) {
149155
handler.setSecurementEncryptionCrypto(securementEncryptionCrypto);
150156
}
@@ -333,10 +339,24 @@ public void setValidationActor(String validationActor) {
333339
this.validationActor = validationActor;
334340
}
335341

342+
/**
343+
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handler to use when validating messages.
344+
*
345+
* @see #setValidationCallbackHandlers(CallbackHandler[])
346+
*/
336347
public void setValidationCallbackHandler(CallbackHandler callbackHandler) {
337348
this.validationCallbackHandler = callbackHandler;
338349
}
339350

351+
/**
352+
* Sets the {@link org.apache.ws.security.WSPasswordCallback} handlers to use when validating messages.
353+
*
354+
* @see #setValidationCallbackHandler(CallbackHandler)
355+
*/
356+
public void setValidationCallbackHandlers(CallbackHandler[] callbackHandler) {
357+
this.validationCallbackHandler = new CallbackHandlerChain(callbackHandler);
358+
}
359+
340360
/** Sets the Crypto to use to decrypt incoming messages */
341361
public void setValidationDecryptionCrypto(Crypto decryptionCrypto) {
342362
this.validationDecryptionCrypto = decryptionCrypto;
@@ -410,6 +430,9 @@ protected void secureMessage(SoapMessage soapMessage, MessageContext messageCont
410430
if (securementAction == WSConstants.NO_SECURITY && !enableSignatureConfirmation) {
411431
return;
412432
}
433+
if (logger.isDebugEnabled()) {
434+
logger.debug("Securing message [" + soapMessage + "] with actions [" + securementActions + "]");
435+
}
413436
RequestData requestData = initializeRequestData(messageContext);
414437

415438
Document envelopeAsDocument = toDocument(soapMessage);
@@ -450,7 +473,7 @@ private RequestData initializeRequestData(MessageContext messageContext) {
450473
protected void validateMessage(SoapMessage soapMessage, MessageContext messageContext)
451474
throws WsSecurityValidationException {
452475
if (logger.isDebugEnabled()) {
453-
logger.debug("Validating message [" + soapMessage + "] with actions " + validationActions);
476+
logger.debug("Validating message [" + soapMessage + "] with actions [" + validationActions + "]");
454477
}
455478

456479
if (validationAction == WSConstants.NO_SECURITY) {
@@ -508,11 +531,7 @@ private void updateContextWithResults(MessageContext messageContext, Vector resu
508531
messageContext.setProperty(WSHandlerConstants.RECV_RESULTS, handlerResults);
509532
}
510533

511-
/**
512-
* Verifies the trust of a certificate.
513-
* @param results
514-
* @throws WSSecurityException
515-
*/
534+
/** Verifies the trust of a certificate. */
516535
protected void verifyCertificateTrust(Vector results) throws WSSecurityException {
517536
RequestData requestData = new RequestData();
518537
requestData.setSigCrypto(validationSignatureCrypto);

0 commit comments

Comments
 (0)