Moved Spring-WS to separate dir.
This commit is contained in:
6
security/src/etc/test-resources/log4j.properties
Normal file
6
security/src/etc/test-resources/log4j.properties
Normal file
@@ -0,0 +1,6 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
log4j.logger.org.springframework.ws=DEBUG
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
@@ -0,0 +1,6 @@
|
||||
PlainText {
|
||||
org.springframework.ws.soap.security.xwss.callback.jaas.PlainTextLoginModule Required;
|
||||
};
|
||||
Certificate {
|
||||
org.springframework.ws.soap.security.xwss.callback.jaas.CertificateLoginModule Required;
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:RequireEncryption />
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,5 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,5 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:Encrypt>
|
||||
<xwss:X509Token certificateAlias="alias"/>
|
||||
</xwss:Encrypt>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:Encrypt />
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,22 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1"><wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="XWSSGID-1149204674680-1346390970" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">MIICXDCCAcUCBER/bZ8wDQYJKoZIhvcNAQEEBQAwdTELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1Vu
|
||||
a25vd24xEjAQBgNVBAcTCUFtc3RlcmRhbTEUMBIGA1UEChMLSW50ZXJmYWNlMjExEjAQBgNVBAsT
|
||||
CVNwcmluZy1XUzEWMBQGA1UEAxMNQXJqZW4gUG91dHNtYTAeFw0wNjA2MDEyMjQzNDNaFw0wNjA4
|
||||
MzAyMjQzNDNaMHUxCzAJBgNVBAYTAk5MMRAwDgYDVQQIEwdVbmtub3duMRIwEAYDVQQHEwlBbXN0
|
||||
ZXJkYW0xFDASBgNVBAoTC0ludGVyZmFjZTIxMRIwEAYDVQQLEwlTcHJpbmctV1MxFjAUBgNVBAMT
|
||||
DUFyamVuIFBvdXRzbWEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOVGA4N3bkrJhTUq/6ol
|
||||
S85RAueC4QpSKlc7Ppg84f/TQZkMpAEKx/NGZq/SlqUqjYS9OSu+2xVTju/svAYcIarVvxlUfIfL
|
||||
Ua4l/6ETS/bJCxZFiKiHhSjAQ9+CZOjMn4z0IzXMzM+9yqpDlJd1cRilbIVZ4GEsutWR74DoBRYB
|
||||
AgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAEi9JV6Rdq9gYO9l1FjegS78SHTbKBp614fU2V9op0ArB
|
||||
1iUiBjteBfELIpjIbd36lkdwsgHmK8lusNRYiwAFptkQffQPJq4F4xCv5wVLkhr1qkY9vgmCjevJ
|
||||
VQK9W6ggUMHEtwiWCnLuPcUe8k3hnCmqNv5CtcKv4QduvrHPrR8=</wsse:BinarySecurityToken><xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
||||
<wsse:SecurityTokenReference>
|
||||
<wsse:Reference URI="#XWSSGID-1149204674680-1346390970" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"/>
|
||||
</wsse:SecurityTokenReference>
|
||||
</ds:KeyInfo><xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">EaldhRqsjjckrksnrNlmwbco1MmG/cIe5U21zs8e1fOtx2yvLEP//ZeEDyiZb6qQJ/nlbs5XQriv
|
||||
oHnI8BrXnWFADJijWQm3WSNxoSvUn3NOOjTj+qjlM0YG3CW2/19ZZLrj0XRiKJyCZXj0fYM+xODr
|
||||
PY70j2YZcIAaH3dtLAs=</xenc:CipherValue></xenc:CipherData><xenc:ReferenceList xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:DataReference URI="#XWSSGID-1149204675657-83290958"/></xenc:ReferenceList></xenc:EncryptedKey></wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body><xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="XWSSGID-1149204675657-83290958" Type="http://www.w3.org/2001/04/xmlenc#Content"><xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"/><xenc:CipherData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"><xenc:CipherValue xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">pOPYwI954LnKSTn1iHJthTlli3m8grr+AfElSNY3mJus0CtKb+Hr+/yThIA7hkqXGYGYx3ReoKcK
|
||||
J+auHXeC2llvcs8kMTZTRLxZl7/qLwWuWX0dBAmBpwXaJy0RXhx5kNr0CWDl0g6Qm4p1VRlnHWNc
|
||||
YXwt4x70RthlVlX6RczAdcthlLZdXqpEjqKQ11zQ9aXFTPFL+1H8gSvSflTOOUg95fPFPZCR</xenc:CipherValue></xenc:CipherData></xenc:EncryptedData></SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:RequireSignature requireTimestamp="false"/>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:RequireUsernameToken passwordDigestRequired="true" nonceRequired="true"/>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:RequireUsernameToken passwordDigestRequired="false" nonceRequired="false"/>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,5 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:Sign includeTimestamp="false">
|
||||
<xwss:X509Token certificateAlias="alias"/>
|
||||
</xwss:Sign>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:Sign includeTimestamp="false" />
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,20 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1"><wsse:BinarySecurityToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="XWSSGID-1149203416758-1792263386" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">MIICXDCCAcUCBER/bZ8wDQYJKoZIhvcNAQEEBQAwdTELMAkGA1UEBhMCTkwxEDAOBgNVBAgTB1Vu
|
||||
a25vd24xEjAQBgNVBAcTCUFtc3RlcmRhbTEUMBIGA1UEChMLSW50ZXJmYWNlMjExEjAQBgNVBAsT
|
||||
CVNwcmluZy1XUzEWMBQGA1UEAxMNQXJqZW4gUG91dHNtYTAeFw0wNjA2MDEyMjQzNDNaFw0wNjA4
|
||||
MzAyMjQzNDNaMHUxCzAJBgNVBAYTAk5MMRAwDgYDVQQIEwdVbmtub3duMRIwEAYDVQQHEwlBbXN0
|
||||
ZXJkYW0xFDASBgNVBAoTC0ludGVyZmFjZTIxMRIwEAYDVQQLEwlTcHJpbmctV1MxFjAUBgNVBAMT
|
||||
DUFyamVuIFBvdXRzbWEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOVGA4N3bkrJhTUq/6ol
|
||||
S85RAueC4QpSKlc7Ppg84f/TQZkMpAEKx/NGZq/SlqUqjYS9OSu+2xVTju/svAYcIarVvxlUfIfL
|
||||
Ua4l/6ETS/bJCxZFiKiHhSjAQ9+CZOjMn4z0IzXMzM+9yqpDlJd1cRilbIVZ4GEsutWR74DoBRYB
|
||||
AgMBAAEwDQYJKoZIhvcNAQEEBQADgYEAEi9JV6Rdq9gYO9l1FjegS78SHTbKBp614fU2V9op0ArB
|
||||
1iUiBjteBfELIpjIbd36lkdwsgHmK8lusNRYiwAFptkQffQPJq4F4xCv5wVLkhr1qkY9vgmCjevJ
|
||||
VQK9W6ggUMHEtwiWCnLuPcUe8k3hnCmqNv5CtcKv4QduvrHPrR8=</wsse:BinarySecurityToken><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse SOAP-ENV"/></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><ds:Reference URI="#XWSSGID-1149203417054-305519969"><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>/ewlu0ecAQ2DsEJqDcM4AT6oWPE=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>ihZA0zibv/mSMKWb0lc6sXJmA+Tp12ku26mhh2/2QlScm6/eKBopUcskEOlLNVL5LLhvTBa+fNVw
|
||||
XVAQKnYyKY2hG5Bsqc+1/Ec6eoN7H9dCbBbwDOdU/H7qV9NFJprLu98tUtPTbBT4IRtnNV5NWKYT
|
||||
odKKjip/W0n1Hz/ym9I=</ds:SignatureValue><ds:KeyInfo><wsse:SecurityTokenReference xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="XWSSGID-1149203416754902281749">
|
||||
<wsse:Reference URI="#XWSSGID-1149203416758-1792263386" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"/>
|
||||
</wsse:SecurityTokenReference></ds:KeyInfo></ds:Signature></wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="XWSSGID-1149203417054-305519969">
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:UsernameToken digestPassword="true" useNonce="true"/>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,3 @@
|
||||
<xwss:SecurityConfiguration dumpMessages="false" xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
|
||||
<xwss:UsernameToken digestPassword="false" useNonce="false"/>
|
||||
</xwss:SecurityConfiguration>
|
||||
@@ -0,0 +1,6 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1"><wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="XWSSGID-1149205720423-1352053129" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:Username>Bert</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">kwNstEaiFOrI7B31j7GuETYvdgk=</wsse:Password><wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">9mdsYDCrjjYRur0rxzYt2oD7</wsse:Nonce><wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2006-06-01T23:48:42Z</wsu:Created></wsse:UsernameToken></wsse:Security></SOAP-ENV:Header><SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,19 @@
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Header>
|
||||
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
||||
SOAP-ENV:mustUnderstand="1">
|
||||
<wsse:UsernameToken
|
||||
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
||||
wsu:Id="XWSSGID-1149200055993710197275"
|
||||
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
|
||||
<wsse:Username>Bert</wsse:Username>
|
||||
<wsse:Password
|
||||
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
||||
>Ernie</wsse:Password>
|
||||
</wsse:UsernameToken>
|
||||
</wsse:Security>
|
||||
</SOAP-ENV:Header>
|
||||
<SOAP-ENV:Body>
|
||||
<tru:StockSymbol xmlns:tru="http://fabrikam123.com/payloads">QQQ</tru:StockSymbol>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security;
|
||||
|
||||
import java.util.Locale;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.context.MessageContext;
|
||||
import org.springframework.ws.soap.SoapEndpointInterceptor;
|
||||
import org.springframework.ws.soap.SoapHeaderElement;
|
||||
import org.springframework.ws.soap.SoapBody;
|
||||
import org.springframework.ws.soap.context.SoapMessageContext;
|
||||
|
||||
/**
|
||||
* Interceptor base class for interceptors that handle WS-Security.
|
||||
* <p/>
|
||||
* Subclasses of this base class can be configured to validate incoming and secure outgoing messages. By default, both
|
||||
* are on.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractWsSecurityInterceptor implements SoapEndpointInterceptor {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
*/
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean validateRequest = true;
|
||||
|
||||
private boolean secureResponse = true;
|
||||
|
||||
private static final QName WS_SECURITY_NAME =
|
||||
new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security");
|
||||
|
||||
/**
|
||||
* Indicates whether outgoing responsed are to be secured. Defaults to <code>true</code>.
|
||||
*/
|
||||
public void setSecureResponse(boolean secureResponse) {
|
||||
this.secureResponse = secureResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether incoming request are to be validated. Defaults to <code>true</code>.
|
||||
*/
|
||||
public void setValidateRequest(boolean validateRequest) {
|
||||
this.validateRequest = validateRequest;
|
||||
}
|
||||
|
||||
public final boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {
|
||||
if (validateRequest) {
|
||||
Assert.isTrue(messageContext instanceof SoapMessageContext,
|
||||
"WsSecurityInterceptor requires a SoapMessageContext");
|
||||
SoapMessageContext soapMessageContext = (SoapMessageContext) messageContext;
|
||||
try {
|
||||
validateRequest(soapMessageContext);
|
||||
return true;
|
||||
}
|
||||
catch (WsSecurityValidationException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not validate request: " + ex.getMessage());
|
||||
}
|
||||
SoapBody response = soapMessageContext.getSoapResponse().getSoapBody();
|
||||
response.addClientOrSenderFault(ex.getMessage(), Locale.ENGLISH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {
|
||||
if (secureResponse) {
|
||||
Assert.isTrue(messageContext instanceof SoapMessageContext,
|
||||
"WsSecurityInterceptor requires a SoapMessageContext");
|
||||
SoapMessageContext soapMessageContext = (SoapMessageContext) messageContext;
|
||||
try {
|
||||
secureResponse(soapMessageContext);
|
||||
return true;
|
||||
}
|
||||
catch (WsSecuritySecurementException ex) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error("Could not secure response: " + ex.getMessage(), ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method. Subclasses are required to validate the request contained in the given
|
||||
* <code>SoapMessageContext</code>, and replace the original request with the validated version.
|
||||
*
|
||||
* @param messageContext the soap message context
|
||||
* @throws WsSecurityValidationException in case of validation errors
|
||||
*/
|
||||
protected abstract void validateRequest(SoapMessageContext messageContext) throws WsSecurityValidationException;
|
||||
|
||||
/**
|
||||
* Abstract template method. Subclasses are required to secure the response contained in the given
|
||||
* <code>SoapMessageContext</code>, and replace the original response with the secured version.
|
||||
*
|
||||
* @param messageContext the soap message context
|
||||
* @throws WsSecuritySecurementException in case of securement errors
|
||||
*/
|
||||
protected abstract void secureResponse(SoapMessageContext messageContext) throws WsSecuritySecurementException;
|
||||
|
||||
public boolean understands(SoapHeaderElement headerElement) {
|
||||
return WS_SECURITY_NAME.equals(headerElement.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security;
|
||||
|
||||
import org.springframework.ws.WebServiceException;
|
||||
|
||||
/**
|
||||
* Exception indicating that something went wrong during WS-Security executions. Has specific subclasses for securement
|
||||
* and validation.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class WsSecurityException extends WebServiceException {
|
||||
|
||||
public WsSecurityException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public WsSecurityException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security;
|
||||
|
||||
/**
|
||||
* Exception indicating that something went wrong during the securement of a message.
|
||||
* <p/>
|
||||
* This is a checked exception since we want it to be caught, logged and handled rather than cause the application to
|
||||
* fail. Failure to secure a message is usually not a fatal problem.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class WsSecuritySecurementException extends WsSecurityException {
|
||||
|
||||
public WsSecuritySecurementException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public WsSecuritySecurementException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security;
|
||||
|
||||
/**
|
||||
* Exception indicating that something went wrong during the validation of a message.
|
||||
* <p/>
|
||||
* This is a checked exception since we want it to be caught, logged and handled rather than cause the application to
|
||||
* fail. Failure to validate a message is usually not a fatal problem.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class WsSecurityValidationException extends WsSecurityException {
|
||||
|
||||
public WsSecurityValidationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public WsSecurityValidationException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Provided WS-Security implementation classes. Contains the <code>AbstractWsSecurityInterceptor</code> and exceptions.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Spring factory bean for a <code>java.security.KeyStore</code>.
|
||||
* <p/>
|
||||
* To load an existing key store, you must set the <code>location</code> property. If this property is not set, a new,
|
||||
* empty key store is created, which is most likely not what you want.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #setLocation(org.springframework.core.io.Resource)
|
||||
*/
|
||||
public class KeyStoreFactoryBean implements FactoryBean, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(KeyStoreFactoryBean.class);
|
||||
|
||||
private KeyStore keyStore;
|
||||
|
||||
private String type;
|
||||
|
||||
private String provider;
|
||||
|
||||
private Resource location;
|
||||
|
||||
private char[] password;
|
||||
|
||||
/**
|
||||
* Sets the location of the key store to use. If this is not set, a new, empty key store will be used.
|
||||
*
|
||||
* @see KeyStore#load(java.io.InputStream, char[])
|
||||
*/
|
||||
public void setLocation(Resource location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password to use for integrity checking. If this property is not set, then integrity checking is not
|
||||
* performed.
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
if (password != null) {
|
||||
this.password = password.toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the provider of the key store to use. If this is not set, the default is used.
|
||||
*/
|
||||
public void setProvider(String provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of the <code>KeyStore</code> to use. If this is not set, the default is used.
|
||||
*
|
||||
* @see KeyStore#getDefaultType()
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Object getObject() throws Exception {
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return KeyStore.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public final void afterPropertiesSet() throws GeneralSecurityException, IOException {
|
||||
if (StringUtils.hasLength(provider) && StringUtils.hasLength(type)) {
|
||||
keyStore = KeyStore.getInstance(type, provider);
|
||||
}
|
||||
else if (StringUtils.hasLength(type)) {
|
||||
keyStore = KeyStore.getInstance(type);
|
||||
}
|
||||
else {
|
||||
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
}
|
||||
InputStream is = null;
|
||||
try {
|
||||
if (location != null && location.exists()) {
|
||||
is = location.getInputStream();
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading key store from " + location);
|
||||
}
|
||||
}
|
||||
else if (logger.isWarnEnabled()) {
|
||||
logger.warn("Creating empty key store");
|
||||
}
|
||||
keyStore.load(is, password);
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains support classes for handling WS-Security messages.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import com.sun.xml.wss.ProcessingContext;
|
||||
import com.sun.xml.wss.XWSSProcessor;
|
||||
import com.sun.xml.wss.XWSSProcessorFactory;
|
||||
import com.sun.xml.wss.XWSSecurityException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.soap.context.SoapMessageContext;
|
||||
import org.springframework.ws.soap.saaj.SaajSoapMessageContext;
|
||||
import org.springframework.ws.soap.security.AbstractWsSecurityInterceptor;
|
||||
import org.springframework.ws.soap.security.xwss.callback.CallbackHandlerChain;
|
||||
|
||||
/**
|
||||
* WS-Security endpoint interceptor that is based on Sun's XML and Web Services Security package (XWSS). This
|
||||
* WS-Security implementation is part of the Java Web Services Developer Pack (Java WSDP).
|
||||
* <p/>
|
||||
* This interceptor needs a <code>CallbackHandler</code> to operate. This handler is used to retrieve certificates,
|
||||
* private keys, validate user credentials, etc. Refer to the XWSS Javadoc to learn more about the specific
|
||||
* <code>Callback</code>s fired by XWSS. You can also set multiple handlers, each of which will be used in turn.
|
||||
* <p/>
|
||||
* Additionally, you must define a XWSS policy file by setting <code>policyConfiguration</code> property. The format of
|
||||
* the policy file is documented in the <a href="http://java.sun.com/webservices/docs/1.6/tutorial/doc/XWS-SecurityIntro4.html#wp529900">Java
|
||||
* Web Services Tutorial</a>.
|
||||
* <p/>
|
||||
* <b>Note</b> that this interceptor depends on SAAJ, and thus requires <code>SaajSoapMessage</code>s to operate. This
|
||||
* means that you must use a <code>SaajSoapMessageContextFactory</code> to create the SOAP messages.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #setCallbackHandler(javax.security.auth.callback.CallbackHandler)
|
||||
* @see #setPolicyConfiguration(org.springframework.core.io.Resource)
|
||||
* @see com.sun.xml.wss.impl.callback.XWSSCallback
|
||||
* @see org.springframework.ws.soap.saaj.SaajSoapMessageContextFactory
|
||||
* @see <a href="https://xwss.dev.java.net/">XWSS</a>
|
||||
*/
|
||||
public class XwsSecurityInterceptor extends AbstractWsSecurityInterceptor implements InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(XwsSecurityInterceptor.class);
|
||||
|
||||
private XWSSProcessor processor;
|
||||
|
||||
private CallbackHandler callbackHandler;
|
||||
|
||||
private Resource policyConfiguration;
|
||||
|
||||
/**
|
||||
* Sets the handler to resolve XWSS callbacks. Setting either this propery, or <code>callbackHandlers</code>, is
|
||||
* required.
|
||||
*
|
||||
* @see com.sun.xml.wss.impl.callback.XWSSCallback
|
||||
* @see #setCallbackHandlers(javax.security.auth.callback.CallbackHandler[])
|
||||
*/
|
||||
public void setCallbackHandler(CallbackHandler callbackHandler) {
|
||||
this.callbackHandler = callbackHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the handlers to resolve XWSS callbacks. Setting either this propery, or <code>callbackHandlers</code>, is
|
||||
* required.
|
||||
*
|
||||
* @see com.sun.xml.wss.impl.callback.XWSSCallback
|
||||
* @see #setCallbackHandler(javax.security.auth.callback.CallbackHandler)
|
||||
*/
|
||||
public void setCallbackHandlers(CallbackHandler[] callbackHandler) {
|
||||
this.callbackHandler = new CallbackHandlerChain(callbackHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the policy configuration to use for XWSS. Required.
|
||||
*/
|
||||
public void setPolicyConfiguration(Resource policyConfiguration) {
|
||||
this.policyConfiguration = policyConfiguration;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(policyConfiguration, "policyConfiguration is required");
|
||||
Assert.isTrue(policyConfiguration.exists(), "policyConfiguration [" + policyConfiguration + "] does not exist");
|
||||
Assert.notNull(callbackHandler, "callbackHandler is required");
|
||||
XWSSProcessorFactory processorFactory = XWSSProcessorFactory.newInstance();
|
||||
InputStream is = null;
|
||||
try {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading policy configuration from from '" + policyConfiguration.getFilename() + "'");
|
||||
}
|
||||
is = policyConfiguration.getInputStream();
|
||||
processor = processorFactory.createProcessorForSecurityConfiguration(is, callbackHandler);
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void secureResponse(SoapMessageContext soapMessageContext) throws XwsSecuritySecurementException {
|
||||
Assert.isTrue(soapMessageContext instanceof SaajSoapMessageContext,
|
||||
"XwsSecurityInterceptor requires a SaajSoapMessageContext. " +
|
||||
"Use a SaajSoapMessageContextFactory to create the SOAP messages.");
|
||||
SaajSoapMessageContext saajMessageContext = (SaajSoapMessageContext) soapMessageContext;
|
||||
SOAPMessage securedMessage = secureMessage(saajMessageContext.getSaajResponse());
|
||||
saajMessageContext.setSaajResponse(securedMessage);
|
||||
}
|
||||
|
||||
protected void validateRequest(SoapMessageContext soapMessageContext) throws XwsSecurityValidationException {
|
||||
Assert.isTrue(soapMessageContext instanceof SaajSoapMessageContext,
|
||||
"XwsSecurityInterceptor requires a SaajSoapMessageContext" +
|
||||
"Use a SaajSoapMessageContextFactory to create the SOAP messages.");
|
||||
SaajSoapMessageContext saajMessageContext = (SaajSoapMessageContext) soapMessageContext;
|
||||
SOAPMessage validatedMessage = validateMessage(saajMessageContext.getSaajRequest());
|
||||
saajMessageContext.setSaajRequest(validatedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Secures the given SAAJ message in accordance with the defined security policy and returns the secured result.
|
||||
*
|
||||
* @param message the message to be secured
|
||||
* @return the secured message
|
||||
* @throws XwsSecuritySecurementException in case of errors
|
||||
*/
|
||||
protected SOAPMessage secureMessage(SOAPMessage message) throws XwsSecuritySecurementException {
|
||||
try {
|
||||
ProcessingContext context = processor.createProcessingContext(message);
|
||||
return processor.secureOutboundMessage(context);
|
||||
}
|
||||
catch (XWSSecurityException ex) {
|
||||
throw new XwsSecuritySecurementException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given SAAJ message in accordance with the defined security policy and returns the validated
|
||||
* result.
|
||||
*
|
||||
* @param message the message to be validated
|
||||
* @return the validated message
|
||||
* @throws XwsSecurityValidationException in case of errors
|
||||
*/
|
||||
protected SOAPMessage validateMessage(SOAPMessage message) throws XwsSecurityValidationException {
|
||||
try {
|
||||
ProcessingContext context = processor.createProcessingContext(message);
|
||||
return processor.verifyInboundMessage(context);
|
||||
}
|
||||
catch (XWSSecurityException ex) {
|
||||
throw new XwsSecurityValidationException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import org.springframework.ws.soap.security.WsSecuritySecurementException;
|
||||
|
||||
public class XwsSecuritySecurementException extends WsSecuritySecurementException {
|
||||
|
||||
public XwsSecuritySecurementException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public XwsSecuritySecurementException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import org.springframework.ws.soap.security.WsSecurityValidationException;
|
||||
|
||||
public class XwsSecurityValidationException extends WsSecurityValidationException {
|
||||
|
||||
public XwsSecurityValidationException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public XwsSecurityValidationException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a <code>CallbackHandler</code>.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractCallbackHandler implements CallbackHandler {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected AbstractCallbackHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the given callbacks, and calls <code>handleInternal</code> for each of them.
|
||||
*
|
||||
* @param callbacks the callbacks
|
||||
* @see #handleInternal(javax.security.auth.callback.Callback)
|
||||
*/
|
||||
public final void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (int i = 0; i < callbacks.length; i++) {
|
||||
handleInternal(callbacks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that should be implemented by subclasses.
|
||||
*/
|
||||
protected abstract void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException;
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
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 com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
|
||||
|
||||
/**
|
||||
* Represents a chain of <code>CallbackHandler</code>s. For each callback, each of the handlers is called in term. If a
|
||||
* handler throws a <code>UnsupportedCallbackException</code>, the next handler is tried.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class CallbackHandlerChain extends AbstractCallbackHandler {
|
||||
|
||||
private CallbackHandler[] callbackHandlers;
|
||||
|
||||
public CallbackHandlerChain(CallbackHandler[] callbackHandlers) {
|
||||
this.callbackHandlers = callbackHandlers;
|
||||
}
|
||||
|
||||
public void setCallbackHandlers(CallbackHandler[] callbackHandlers) {
|
||||
this.callbackHandlers = callbackHandlers;
|
||||
}
|
||||
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
handleCertificateValidationCallback((CertificateValidationCallback) callback);
|
||||
}
|
||||
else if (callback instanceof PasswordValidationCallback) {
|
||||
handlePasswordValidationCallback((PasswordValidationCallback) callback);
|
||||
}
|
||||
else if (callback instanceof TimestampValidationCallback) {
|
||||
handleTimestampValidationCallback((TimestampValidationCallback) callback);
|
||||
}
|
||||
else {
|
||||
boolean allUnsupported = true;
|
||||
for (int i = 0; i < callbackHandlers.length; i++) {
|
||||
CallbackHandler callbackHandler = callbackHandlers[i];
|
||||
try {
|
||||
callbackHandler.handle(new Callback[]{callback});
|
||||
allUnsupported = false;
|
||||
}
|
||||
catch (UnsupportedCallbackException ex) {
|
||||
// if an UnsupportedCallbackException occurs, go to the next handler
|
||||
}
|
||||
}
|
||||
if (allUnsupported) {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCertificateValidationCallback(CertificateValidationCallback callback) {
|
||||
callback.setValidator(new CertificateValidatorChain(callback));
|
||||
}
|
||||
|
||||
private void handlePasswordValidationCallback(PasswordValidationCallback callback) {
|
||||
callback.setValidator(new PasswordValidatorChain(callback));
|
||||
}
|
||||
|
||||
private void handleTimestampValidationCallback(TimestampValidationCallback callback) {
|
||||
callback.setValidator(new TimestampValidatorChain(callback));
|
||||
}
|
||||
|
||||
private class TimestampValidatorChain implements TimestampValidationCallback.TimestampValidator {
|
||||
|
||||
private TimestampValidationCallback callback;
|
||||
|
||||
private TimestampValidatorChain(TimestampValidationCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void validate(TimestampValidationCallback.Request request)
|
||||
throws TimestampValidationCallback.TimestampValidationException {
|
||||
for (int i = 0; i < callbackHandlers.length; i++) {
|
||||
CallbackHandler callbackHandler = callbackHandlers[i];
|
||||
try {
|
||||
callbackHandler.handle(new Callback[]{callback});
|
||||
callback.getResult();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new TimestampValidationCallback.TimestampValidationException(e);
|
||||
}
|
||||
catch (UnsupportedCallbackException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PasswordValidatorChain implements PasswordValidationCallback.PasswordValidator {
|
||||
|
||||
private PasswordValidationCallback callback;
|
||||
|
||||
private PasswordValidatorChain(PasswordValidationCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
boolean allUnsupported = true;
|
||||
for (int i = 0; i < callbackHandlers.length; i++) {
|
||||
CallbackHandler callbackHandler = callbackHandlers[i];
|
||||
try {
|
||||
callbackHandler.handle(new Callback[]{callback});
|
||||
allUnsupported = false;
|
||||
if (!callback.getResult()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new PasswordValidationCallback.PasswordValidationException(e);
|
||||
}
|
||||
catch (UnsupportedCallbackException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return !allUnsupported;
|
||||
}
|
||||
}
|
||||
|
||||
private class CertificateValidatorChain implements CertificateValidationCallback.CertificateValidator {
|
||||
|
||||
private CertificateValidationCallback callback;
|
||||
|
||||
private CertificateValidatorChain(CertificateValidationCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public boolean validate(X509Certificate certificate)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
boolean allUnsupported = true;
|
||||
for (int i = 0; i < callbackHandlers.length; i++) {
|
||||
CallbackHandler callbackHandler = callbackHandlers[i];
|
||||
try {
|
||||
callbackHandler.handle(new Callback[]{callback});
|
||||
allUnsupported = false;
|
||||
if (!callback.getResult()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new CertificateValidationCallback.CertificateValidationException(e);
|
||||
}
|
||||
catch (UnsupportedCallbackException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return !allUnsupported;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.DecryptionKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback;
|
||||
|
||||
/**
|
||||
* Default callback handler that handles cryptographic callback. This handler determines the exact callback passed, and
|
||||
* calls a template method for it. By default, all template methods throw an <code>UnsupportedCallbackException</code>,
|
||||
* so you only need to override those you need.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class CryptographyCallbackHandler extends AbstractCallbackHandler {
|
||||
|
||||
protected final void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
handleCertificateValidationCallback((CertificateValidationCallback) callback);
|
||||
}
|
||||
else if (callback instanceof DecryptionKeyCallback) {
|
||||
handleDecryptionKeyCallback((DecryptionKeyCallback) callback);
|
||||
}
|
||||
else if (callback instanceof EncryptionKeyCallback) {
|
||||
handleEncryptionKeyCallback((EncryptionKeyCallback) callback);
|
||||
}
|
||||
else if (callback instanceof SignatureKeyCallback) {
|
||||
handleSignatureKeyCallback((SignatureKeyCallback) callback);
|
||||
}
|
||||
else if (callback instanceof SignatureVerificationKeyCallback) {
|
||||
handleSignatureVerificationKeyCallback((SignatureVerificationKeyCallback) callback);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Certificate validation
|
||||
//
|
||||
|
||||
/**
|
||||
* Template method that handles <code>CertificateValidationCallback</code>s. Called from
|
||||
* <code>handleInternal()</code>. Default implementation throws an <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleCertificateValidationCallback(CertificateValidationCallback callback)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
//
|
||||
// Decryption
|
||||
//
|
||||
|
||||
/**
|
||||
* Method that handles <code>DecryptionKeyCallback</code>s. Called from <code>handleInternal()</code>. Default
|
||||
* implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handlePrivateKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.PrivateKeyRequest)
|
||||
* @see #handleSymmetricKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.SymmetricKeyRequest)
|
||||
*/
|
||||
protected final void handleDecryptionKeyCallback(DecryptionKeyCallback callback)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (callback.getRequest() instanceof DecryptionKeyCallback.PrivateKeyRequest) {
|
||||
handlePrivateKeyRequest(callback, (DecryptionKeyCallback.PrivateKeyRequest) callback.getRequest());
|
||||
}
|
||||
else if (callback.getRequest() instanceof DecryptionKeyCallback.SymmetricKeyRequest) {
|
||||
handleSymmetricKeyRequest(callback, (DecryptionKeyCallback.SymmetricKeyRequest) callback.getRequest());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>DecryptionKeyCallback</code>s with <code>PrivateKeyRequest</code> . Called from
|
||||
* <code>handleDecryptionKeyCallback()</code>. Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handlePublicKeyBasedPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
|
||||
* @see #handleX509CertificateBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509CertificateBasedRequest)
|
||||
* @see #handleX509IssuerSerialBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509IssuerSerialBasedRequest)
|
||||
* @see #handleX509SubjectKeyIdentifierBasedRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest)
|
||||
*/
|
||||
protected final void handlePrivateKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.PrivateKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (request instanceof DecryptionKeyCallback.PublicKeyBasedPrivKeyRequest) {
|
||||
handlePublicKeyBasedPrivKeyRequest(callback, (DecryptionKeyCallback.PublicKeyBasedPrivKeyRequest) request);
|
||||
}
|
||||
else if (request instanceof DecryptionKeyCallback.X509CertificateBasedRequest) {
|
||||
handleX509CertificateBasedRequest(callback, (DecryptionKeyCallback.X509CertificateBasedRequest) request);
|
||||
}
|
||||
else if (request instanceof DecryptionKeyCallback.X509IssuerSerialBasedRequest) {
|
||||
handleX509IssuerSerialBasedRequest(callback, (DecryptionKeyCallback.X509IssuerSerialBasedRequest) request);
|
||||
}
|
||||
else if (request instanceof DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest) {
|
||||
handleX509SubjectKeyIdentifierBasedRequest(callback,
|
||||
(DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest) request);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>DecryptionKeyCallback</code>s with <code>PublicKeyBasedPrivKeyRequest</code>s.
|
||||
* Called from <code>handlePrivateKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handlePublicKeyBasedPrivKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.PublicKeyBasedPrivKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>DecryptionKeyCallback</code>s with <code>X509CertificateBasedRequest</code>s.
|
||||
* Called from <code>handlePrivateKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleX509CertificateBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509CertificateBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>DecryptionKeyCallback</code>s with <code>X509IssuerSerialBasedRequest</code>s.
|
||||
* Called from <code>handlePrivateKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleX509IssuerSerialBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509IssuerSerialBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>DecryptionKeyCallback</code>s with <code>X509SubjectKeyIdentifierBasedRequest</code>s.
|
||||
* Called from <code>handlePrivateKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleX509SubjectKeyIdentifierBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>DecryptionKeyCallback</code>s with <code>SymmetricKeyRequest</code> . Called from
|
||||
* <code>handleDecryptionKeyCallback()</code>. Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleAliasSymmetricKeyRequest(com.sun.xml.wss.impl.callback.DecryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.DecryptionKeyCallback.AliasSymmetricKeyRequest)
|
||||
*/
|
||||
protected final void handleSymmetricKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.SymmetricKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (request instanceof DecryptionKeyCallback.AliasSymmetricKeyRequest) {
|
||||
DecryptionKeyCallback.AliasSymmetricKeyRequest aliasSymmetricKeyRequest =
|
||||
(DecryptionKeyCallback.AliasSymmetricKeyRequest) request;
|
||||
handleAliasSymmetricKeyRequest(callback, aliasSymmetricKeyRequest);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>DecryptionKeyCallback</code>s with <code>AliasSymmetricKeyRequest</code>s.
|
||||
* Called from <code>handleSymmetricKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleAliasSymmetricKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.AliasSymmetricKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
//
|
||||
// Encryption
|
||||
//
|
||||
|
||||
/**
|
||||
* Method that handles <code>EncryptionKeyCallback</code>s. Called from <code>handleInternal()</code>. Default
|
||||
* implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleSymmetricKeyRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.SymmetricKeyRequest)
|
||||
* @see #handleX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.X509CertificateRequest)
|
||||
*/
|
||||
protected final void handleEncryptionKeyCallback(EncryptionKeyCallback callback)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (callback.getRequest() instanceof EncryptionKeyCallback.SymmetricKeyRequest) {
|
||||
handleSymmetricKeyRequest(callback, (EncryptionKeyCallback.SymmetricKeyRequest) callback.getRequest());
|
||||
}
|
||||
else if (callback.getRequest() instanceof EncryptionKeyCallback.X509CertificateRequest) {
|
||||
handleX509CertificateRequest(callback,
|
||||
(EncryptionKeyCallback.X509CertificateRequest) callback.getRequest());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>EncryptionKeyCallback</code>s with <code>SymmetricKeyRequest</code> . Called from
|
||||
* <code>handleEncryptionKeyCallback()</code>. Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleAliasSymmetricKeyRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasSymmetricKeyRequest)
|
||||
*/
|
||||
protected final void handleSymmetricKeyRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.SymmetricKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (request instanceof EncryptionKeyCallback.AliasSymmetricKeyRequest) {
|
||||
handleAliasSymmetricKeyRequest(callback, (EncryptionKeyCallback.AliasSymmetricKeyRequest) request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>EncryptionKeyCallback</code>s with <code>AliasSymmetricKeyRequest</code>s.
|
||||
* Called from <code>handleSymmetricKeyRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleAliasSymmetricKeyRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.AliasSymmetricKeyRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>EncryptionKeyCallback</code>s with <code>X509CertificateRequest</code> . Called from
|
||||
* <code>handleEncryptionKeyCallback()</code>. Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleAliasX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.AliasX509CertificateRequest)
|
||||
* @see #handleDefaultX509CertificateRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.DefaultX509CertificateRequest)
|
||||
* @see #handlePublicKeyBasedRequest(com.sun.xml.wss.impl.callback.EncryptionKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.EncryptionKeyCallback.PublicKeyBasedRequest)
|
||||
*/
|
||||
protected final void handleX509CertificateRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.X509CertificateRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (request instanceof EncryptionKeyCallback.AliasX509CertificateRequest) {
|
||||
handleAliasX509CertificateRequest(callback, (EncryptionKeyCallback.AliasX509CertificateRequest) request);
|
||||
}
|
||||
else if (request instanceof EncryptionKeyCallback.DefaultX509CertificateRequest) {
|
||||
handleDefaultX509CertificateRequest(callback,
|
||||
(EncryptionKeyCallback.DefaultX509CertificateRequest) request);
|
||||
}
|
||||
else if (request instanceof EncryptionKeyCallback.PublicKeyBasedRequest) {
|
||||
handlePublicKeyBasedRequest(callback, (EncryptionKeyCallback.PublicKeyBasedRequest) request);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>EncryptionKeyCallback</code>s with <code>AliasX509CertificateRequest</code>s.
|
||||
* Called from <code>handleX509CertificateRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleAliasX509CertificateRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.AliasX509CertificateRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>EncryptionKeyCallback</code>s with <code>DefaultX509CertificateRequest</code>s.
|
||||
* Called from <code>handleX509CertificateRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleDefaultX509CertificateRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.DefaultX509CertificateRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>EncryptionKeyCallback</code>s with <code>PublicKeyBasedRequest</code>s. Called
|
||||
* from <code>handleX509CertificateRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handlePublicKeyBasedRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.PublicKeyBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
//
|
||||
// Signing
|
||||
//
|
||||
|
||||
/**
|
||||
* Method that handles <code>SignatureKeyCallback</code>s. Called from <code>handleInternal()</code>. Default
|
||||
* implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handlePrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureKeyCallback.PrivKeyCertRequest)
|
||||
*/
|
||||
protected final void handleSignatureKeyCallback(SignatureKeyCallback callback)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (callback.getRequest() instanceof SignatureKeyCallback.PrivKeyCertRequest) {
|
||||
handlePrivKeyCertRequest(callback, (SignatureKeyCallback.PrivKeyCertRequest) callback.getRequest());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>SignatureKeyCallback</code>s with <code>PrivKeyCertRequest</code>s. Called from
|
||||
* <code>handleSignatureKeyCallback()</code>. Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleDefaultPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureKeyCallback.DefaultPrivKeyCertRequest)
|
||||
* @see #handleAliasPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureKeyCallback.AliasPrivKeyCertRequest)
|
||||
* @see #handlePublicKeyBasedPrivKeyCertRequest(com.sun.xml.wss.impl.callback.SignatureKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest)
|
||||
*/
|
||||
protected final void handlePrivKeyCertRequest(SignatureKeyCallback cb,
|
||||
SignatureKeyCallback.PrivKeyCertRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
if (request instanceof SignatureKeyCallback.DefaultPrivKeyCertRequest) {
|
||||
handleDefaultPrivKeyCertRequest(cb, (SignatureKeyCallback.DefaultPrivKeyCertRequest) request);
|
||||
}
|
||||
else if (cb.getRequest() instanceof SignatureKeyCallback.AliasPrivKeyCertRequest) {
|
||||
handleAliasPrivKeyCertRequest(cb, (SignatureKeyCallback.AliasPrivKeyCertRequest) request);
|
||||
}
|
||||
else if (cb.getRequest() instanceof SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest) {
|
||||
handlePublicKeyBasedPrivKeyCertRequest(cb, (SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest) request);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>DefaultPrivKeyCertRequest</code>s.
|
||||
* Called from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleDefaultPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.DefaultPrivKeyCertRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>AliasPrivKeyCertRequest</code>s.
|
||||
* Called from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleAliasPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.AliasPrivKeyCertRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>PublicKeyBasedPrivKeyCertRequest</code>s.
|
||||
* Called from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handlePublicKeyBasedPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
//
|
||||
// Signature verification
|
||||
//
|
||||
|
||||
/**
|
||||
* Method that handles <code>SignatureVerificationKeyCallback</code>s. Called from <code>handleInternal()</code>.
|
||||
* Default implementation delegates to specific handling methods.
|
||||
*
|
||||
* @see #handleX509CertificateRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509CertificateRequest)
|
||||
*/
|
||||
protected final void handleSignatureVerificationKeyCallback(SignatureVerificationKeyCallback callback)
|
||||
throws UnsupportedCallbackException, IOException {
|
||||
if (callback.getRequest() instanceof SignatureVerificationKeyCallback.X509CertificateRequest) {
|
||||
handleX509CertificateRequest(callback,
|
||||
(SignatureVerificationKeyCallback.X509CertificateRequest) callback.getRequest());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that handles <code>SignatureVerificationKeyCallback</code>s with <code>X509CertificateRequest</code>s.
|
||||
* Called from <code>handleSignatureVerificationKeyCallback()</code>. Default implementation delegates to specific
|
||||
* handling methods.
|
||||
*
|
||||
* @see #handlePublicKeyBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.PublicKeyBasedRequest)
|
||||
* @see #handleX509IssuerSerialBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest)
|
||||
* @see #handleX509SubjectKeyIdentifierBasedRequest(com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback,
|
||||
* com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest)
|
||||
*/
|
||||
protected final void handleX509CertificateRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.X509CertificateRequest request)
|
||||
throws UnsupportedCallbackException, IOException {
|
||||
if (request instanceof SignatureVerificationKeyCallback.PublicKeyBasedRequest) {
|
||||
handlePublicKeyBasedRequest(callback, (SignatureVerificationKeyCallback.PublicKeyBasedRequest) request);
|
||||
}
|
||||
else if (request instanceof SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest) {
|
||||
handleX509IssuerSerialBasedRequest(callback,
|
||||
(SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest) request);
|
||||
}
|
||||
else if (request instanceof SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest) {
|
||||
handleX509SubjectKeyIdentifierBasedRequest(callback,
|
||||
(SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest) request);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>PublicKeyBasedPrivKeyCertRequest</code>s.
|
||||
* Called from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleX509SubjectKeyIdentifierBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>X509IssuerSerialBasedRequest</code>s.
|
||||
* Called from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handleX509IssuerSerialBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method that handles <code>SignatureKeyCallback</code>s with <code>PublicKeyBasedRequest</code>s. Called
|
||||
* from <code>handlePrivKeyCertRequest()</code>. Default implementation throws an
|
||||
* <code>UnsupportedCallbackException</code>.
|
||||
*/
|
||||
protected void handlePublicKeyBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.PublicKeyBasedRequest request)
|
||||
throws IOException, UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
|
||||
|
||||
/**
|
||||
* A default implementation of a <code>TimestampValidationCallback.TimestampValidator</code>. Based on a version found
|
||||
* in the JWSDP samples.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class DefaultTimestampValidator implements TimestampValidationCallback.TimestampValidator {
|
||||
|
||||
public void validate(TimestampValidationCallback.Request request)
|
||||
throws TimestampValidationCallback.TimestampValidationException {
|
||||
if (request instanceof TimestampValidationCallback.UTCTimestampRequest) {
|
||||
TimestampValidationCallback.UTCTimestampRequest utcRequest =
|
||||
((TimestampValidationCallback.UTCTimestampRequest) request);
|
||||
Date created = parseDate(utcRequest.getCreated());
|
||||
Date expired = parseDate(utcRequest.getExpired());
|
||||
|
||||
validateCreationTime(created, utcRequest.getMaxClockSkew(), utcRequest.getTimestampFreshnessLimit());
|
||||
|
||||
if (expired != null) {
|
||||
validateExpirationTime(expired, utcRequest.getMaxClockSkew());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new TimestampValidationCallback.TimestampValidationException("Unsupport request: [" + request + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private Date getFreshnessAndSkewAdjustedDate(long maxClockSkew, long timestampFreshnessLimit) {
|
||||
Calendar c = new GregorianCalendar();
|
||||
long offset = c.get(Calendar.ZONE_OFFSET);
|
||||
if (c.getTimeZone().inDaylightTime(c.getTime())) {
|
||||
offset += c.getTimeZone().getDSTSavings();
|
||||
}
|
||||
long beforeTime = c.getTimeInMillis();
|
||||
long currentTime = beforeTime - offset;
|
||||
|
||||
long adjustedTime = currentTime - maxClockSkew - timestampFreshnessLimit;
|
||||
c.setTimeInMillis(adjustedTime);
|
||||
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
private Date getGMTDateWithSkewAdjusted(Calendar calendar, long maxClockSkew, boolean addSkew) {
|
||||
long offset = calendar.get(Calendar.ZONE_OFFSET);
|
||||
if (calendar.getTimeZone().inDaylightTime(calendar.getTime())) {
|
||||
offset += calendar.getTimeZone().getDSTSavings();
|
||||
}
|
||||
long beforeTime = calendar.getTimeInMillis();
|
||||
long currentTime = beforeTime - offset;
|
||||
|
||||
if (addSkew) {
|
||||
currentTime = currentTime + maxClockSkew;
|
||||
}
|
||||
else {
|
||||
currentTime = currentTime - maxClockSkew;
|
||||
}
|
||||
|
||||
calendar.setTimeInMillis(currentTime);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
private Date parseDate(String date) throws TimestampValidationCallback.TimestampValidationException {
|
||||
SimpleDateFormat calendarFormatter1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
SimpleDateFormat calendarFormatter2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.'sss'Z'");
|
||||
|
||||
try {
|
||||
try {
|
||||
return calendarFormatter1.parse(date);
|
||||
}
|
||||
catch (ParseException ignored) {
|
||||
return calendarFormatter2.parse(date);
|
||||
}
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new TimestampValidationCallback.TimestampValidationException("Could not parse request date: " + date,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateCreationTime(Date created, long maxClockSkew, long timestampFreshnessLimit)
|
||||
throws TimestampValidationCallback.TimestampValidationException {
|
||||
Date current = getFreshnessAndSkewAdjustedDate(maxClockSkew, timestampFreshnessLimit);
|
||||
|
||||
if (created.before(current)) {
|
||||
throw new TimestampValidationCallback.TimestampValidationException(
|
||||
"The creation time is older than currenttime - timestamp-freshness-limit - max-clock-skew");
|
||||
}
|
||||
|
||||
Date currentTime = getGMTDateWithSkewAdjusted(new GregorianCalendar(), maxClockSkew, true);
|
||||
if (currentTime.before(created)) {
|
||||
throw new TimestampValidationCallback.TimestampValidationException(
|
||||
"The creation time is ahead of the current time.");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExpirationTime(Date expires, long maxClockSkew)
|
||||
throws TimestampValidationCallback.TimestampValidationException {
|
||||
Date currentTime = getGMTDateWithSkewAdjusted(new GregorianCalendar(), maxClockSkew, false);
|
||||
if (expires.before(currentTime)) {
|
||||
throw new TimestampValidationCallback.TimestampValidationException(
|
||||
"The current time is ahead of the expiration time in Timestamp");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertPathBuilder;
|
||||
import java.security.cert.CertPathBuilderException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.PKIXBuilderParameters;
|
||||
import java.security.cert.X509CertSelector;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import com.sun.org.apache.xml.internal.security.utils.RFC2253Parser;
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.DecryptionKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.SignatureVerificationKeyCallback;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.ws.soap.security.support.KeyStoreFactoryBean;
|
||||
|
||||
/**
|
||||
* Callback handler that uses Java Security <code>KeyStore</code>s to handle cryptographic callbacks. Allows for
|
||||
* specific key stores to be set for various cryptographic operations.
|
||||
* <p/>
|
||||
* This handler requires one or more key stores to be set. You can configure them in your application context by using a
|
||||
* <code>KeyStoreFactoryBean</code>. The exact stores to be set depends on the cryptographic operations that are to be
|
||||
* performed by this handler. The table underneath show the key store to be used for each operation: <table border="1">
|
||||
* <tr> <td><strong>Cryptographic operation</strong></td> <td><strong>Key store used</strong></td> </tr> <tr>
|
||||
* <td>Certificate validation</td> <td>first <code>keyStore</code>, then <code>trustStore</code></td> </tr> <tr>
|
||||
* <td>Decryption based on private key</td> <td><code>keyStore</code></td> </tr> <tr> <td>Decryption based on symmetric
|
||||
* key</td> <td><code>symmetricStore</code></td> </tr> <tr> <td>Encryption based on certificate</td>
|
||||
* <td><code>trustStore</code></td> </tr> <tr> <td>Encryption based on symmetric key</td>
|
||||
* <td><code>symmetricStore</code></td> </tr> <tr> <td>Signing</td> <td><code>keyStore</code></td> </tr> <tr>
|
||||
* <td>Signature verification</td> <td><code>trustStore</code></td> </tr> </table>
|
||||
* <p/>
|
||||
* <h3>Default key stores</h3> If the <code>symmetricStore</code> is not set, it will default to the
|
||||
* <code>keyStore</code>. If the key or trust store is not set, this handler will use the standard Java mechanism to
|
||||
* load or create it. See {@link #loadDefaultKeyStore()} and {@link #loadDefaultTrustStore()}.
|
||||
* <p/>
|
||||
* <h3>Examples</h3> For instance, if you want to use the <code>KeyStoreCallbackHandler</code> to validate incoming
|
||||
* certificates or signatures, you would use a trust store, like so:
|
||||
* <pre>
|
||||
* <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
|
||||
* <property name="trustStore" ref="trustStore"/>
|
||||
* </bean>
|
||||
* <p/>
|
||||
* <bean id="trustStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
|
||||
* <property name="location" value="classpath:truststore.jks"/>
|
||||
* <property name="password" value="changeit"/>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* If you want to use it to decrypt incoming certificates or sign outgoing messages, you would use a key store, like
|
||||
* so:
|
||||
* <pre>
|
||||
* <bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
|
||||
* <property name="keyStore" ref="keyStore"/>
|
||||
* <property name="privateKeyPassword" value="changeit"/>
|
||||
* </bean>
|
||||
* <p/>
|
||||
* <bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
|
||||
* <property name="location" value="classpath:keystore.jks"/>
|
||||
* <property name="password" value="changeit"/>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* <p/>
|
||||
* <h3>Handled callbacks</h3> This class handles <code>CertificateValidationCallback</code>s,
|
||||
* <code>DecryptionKeyCallback</code>s, <code>EncryptionKeyCallback</code>s, <code>SignatureKeyCallback</code>s, and
|
||||
* <code>SignatureVerificationKeyCallback</code>s. It throws an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see KeyStore
|
||||
* @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
|
||||
* @see CertificateValidationCallback
|
||||
* @see DecryptionKeyCallback
|
||||
* @see EncryptionKeyCallback
|
||||
* @see SignatureKeyCallback
|
||||
* @see SignatureVerificationKeyCallback
|
||||
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
|
||||
* standard Java trust store mechanism</a>
|
||||
*/
|
||||
public class KeyStoreCallbackHandler extends CryptographyCallbackHandler implements InitializingBean {
|
||||
|
||||
private static final String X_509_CERTIFICATE_TYPE = "X.509";
|
||||
|
||||
private static final String SUBJECT_KEY_IDENTIFIER_OID = "2.5.29.14";
|
||||
|
||||
private KeyStore keyStore;
|
||||
|
||||
private KeyStore symmetricStore;
|
||||
|
||||
private KeyStore trustStore;
|
||||
|
||||
private String defaultAlias;
|
||||
|
||||
private char[] privateKeyPassword;
|
||||
|
||||
private char[] symmetricKeyPassword;
|
||||
|
||||
private static X509Certificate getCertificate(String alias, KeyStore store) throws IOException {
|
||||
try {
|
||||
return (X509Certificate) store.getCertificate(alias);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static X509Certificate getCertificate(PublicKey pk, KeyStore store) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = store.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
Certificate cert = store.getCertificate(alias);
|
||||
if (cert == null || !X_509_CERTIFICATE_TYPE.equals(cert.getType())) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
if (x509Cert.getPublicKey().equals(pk)) {
|
||||
return x509Cert;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key store alias for the default certificate and private key.
|
||||
*/
|
||||
public void setDefaultAlias(String defaultAlias) {
|
||||
this.defaultAlias = defaultAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default key store. This property is required for decription based on private keys, and signing. If this
|
||||
* property is not set, a default key store is loaded.
|
||||
*
|
||||
* @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
|
||||
* @see #loadDefaultTrustStore()
|
||||
*/
|
||||
public void setKeyStore(KeyStore keyStore) {
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password used to retrieve private keys from the keystore. This property is required for decription based
|
||||
* on private keys, and signing.
|
||||
*/
|
||||
public void setPrivateKeyPassword(String privateKeyPassword) {
|
||||
if (privateKeyPassword != null) {
|
||||
this.privateKeyPassword = privateKeyPassword.toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password used to retrieve keys from the symmetric keystore. If this property is not set, it default to
|
||||
* the private key password.
|
||||
*
|
||||
* @see #setPrivateKeyPassword(String)
|
||||
*/
|
||||
public void setSymmetricKeyPassword(String symmetricKeyPassword) {
|
||||
if (symmetricKeyPassword != null) {
|
||||
this.symmetricKeyPassword = symmetricKeyPassword.toCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key store used for encryption and decryption using symmetric keys. If this property is not set, it
|
||||
* defaults to the <code>keyStore</code> property.
|
||||
*
|
||||
* @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
|
||||
* @see #setKeyStore(java.security.KeyStore)
|
||||
*/
|
||||
public void setSymmetricStore(KeyStore symmetricStore) {
|
||||
this.symmetricStore = symmetricStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key store used for signature verifications and encryptions. If this property is not set, a default key
|
||||
* store will be loaded.
|
||||
*
|
||||
* @see org.springframework.ws.soap.security.support.KeyStoreFactoryBean
|
||||
* @see #loadDefaultTrustStore()
|
||||
*/
|
||||
public void setTrustStore(KeyStore trustStore) {
|
||||
this.trustStore = trustStore;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (keyStore == null) {
|
||||
loadDefaultKeyStore();
|
||||
}
|
||||
if (trustStore == null) {
|
||||
loadDefaultTrustStore();
|
||||
}
|
||||
if (symmetricStore == null) {
|
||||
symmetricStore = keyStore;
|
||||
}
|
||||
if (symmetricKeyPassword == null) {
|
||||
symmetricKeyPassword = privateKeyPassword;
|
||||
}
|
||||
}
|
||||
|
||||
protected final void handleAliasPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.AliasPrivKeyCertRequest request)
|
||||
throws IOException {
|
||||
PrivateKey privateKey = getPrivateKey(request.getAlias());
|
||||
X509Certificate certificate = getCertificate(request.getAlias());
|
||||
request.setPrivateKey(privateKey);
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handleAliasSymmetricKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.AliasSymmetricKeyRequest request)
|
||||
throws IOException {
|
||||
SecretKey secretKey = getSymmetricKey(request.getAlias());
|
||||
request.setSymmetricKey(secretKey);
|
||||
}
|
||||
|
||||
//
|
||||
// Encryption
|
||||
//
|
||||
|
||||
protected final void handleAliasSymmetricKeyRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.AliasSymmetricKeyRequest request)
|
||||
throws IOException {
|
||||
SecretKey secretKey = getSymmetricKey(request.getAlias());
|
||||
request.setSymmetricKey(secretKey);
|
||||
}
|
||||
|
||||
protected final void handleAliasX509CertificateRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.AliasX509CertificateRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(request.getAlias());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
//
|
||||
// Certificate validation
|
||||
//
|
||||
|
||||
protected final void handleCertificateValidationCallback(CertificateValidationCallback callback) {
|
||||
callback.setValidator(new KeyStoreCertificateValidator());
|
||||
}
|
||||
|
||||
//
|
||||
// Signing
|
||||
//
|
||||
|
||||
protected final void handleDefaultPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.DefaultPrivKeyCertRequest request)
|
||||
throws IOException {
|
||||
PrivateKey privateKey = getPrivateKey(defaultAlias);
|
||||
X509Certificate certificate = getCertificate(defaultAlias);
|
||||
request.setPrivateKey(privateKey);
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handleDefaultX509CertificateRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.DefaultX509CertificateRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(defaultAlias);
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handlePublicKeyBasedPrivKeyCertRequest(SignatureKeyCallback callback,
|
||||
SignatureKeyCallback.PublicKeyBasedPrivKeyCertRequest request)
|
||||
throws IOException {
|
||||
PrivateKey privateKey = getPrivateKey(request.getPublicKey());
|
||||
X509Certificate certificate = getCertificate(request.getPublicKey());
|
||||
request.setPrivateKey(privateKey);
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
//
|
||||
// Decryption
|
||||
//
|
||||
protected final void handlePublicKeyBasedPrivKeyRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.PublicKeyBasedPrivKeyRequest request)
|
||||
throws IOException {
|
||||
PrivateKey key = getPrivateKey(request.getPublicKey());
|
||||
request.setPrivateKey(key);
|
||||
}
|
||||
|
||||
protected final void handlePublicKeyBasedRequest(EncryptionKeyCallback callback,
|
||||
EncryptionKeyCallback.PublicKeyBasedRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(request.getPublicKey());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handlePublicKeyBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.PublicKeyBasedRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(request.getPublicKey());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handleX509CertificateBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509CertificateBasedRequest request)
|
||||
throws IOException {
|
||||
PrivateKey privKey = getPrivateKey(request.getX509Certificate());
|
||||
request.setPrivateKey(privKey);
|
||||
}
|
||||
|
||||
protected final void handleX509IssuerSerialBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509IssuerSerialBasedRequest request)
|
||||
throws IOException {
|
||||
PrivateKey key = getPrivateKey(request.getIssuerName(), request.getSerialNumber());
|
||||
request.setPrivateKey(key);
|
||||
}
|
||||
|
||||
protected final void handleX509IssuerSerialBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.X509IssuerSerialBasedRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(request.getIssuerName(), request.getSerialNumber());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
protected final void handleX509SubjectKeyIdentifierBasedRequest(DecryptionKeyCallback callback,
|
||||
DecryptionKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
|
||||
throws IOException {
|
||||
PrivateKey key = getPrivateKey(request.getSubjectKeyIdentifier());
|
||||
request.setPrivateKey(key);
|
||||
}
|
||||
|
||||
//
|
||||
// Signature verification
|
||||
//
|
||||
|
||||
protected final void handleX509SubjectKeyIdentifierBasedRequest(SignatureVerificationKeyCallback callback,
|
||||
SignatureVerificationKeyCallback.X509SubjectKeyIdentifierBasedRequest request)
|
||||
throws IOException {
|
||||
X509Certificate certificate = getCertificateFromTrustStore(request.getSubjectKeyIdentifier());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
|
||||
// Certificate methods
|
||||
|
||||
protected X509Certificate getCertificate(String alias) throws IOException {
|
||||
return getCertificate(alias, keyStore);
|
||||
}
|
||||
|
||||
protected X509Certificate getCertificate(PublicKey pk) throws IOException {
|
||||
return getCertificate(pk, keyStore);
|
||||
}
|
||||
|
||||
protected X509Certificate getCertificateFromTrustStore(String alias) throws IOException {
|
||||
return getCertificate(alias, trustStore);
|
||||
}
|
||||
|
||||
protected X509Certificate getCertificateFromTrustStore(byte[] subjectKeyIdentifier) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = trustStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
Certificate cert = trustStore.getCertificate(alias);
|
||||
if (cert == null || !X_509_CERTIFICATE_TYPE.equals(cert.getType())) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
byte[] keyId = getSubjectKeyIdentifier(x509Cert);
|
||||
if (keyId == null) {
|
||||
// Cert does not contain a key identifier
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(subjectKeyIdentifier, keyId)) {
|
||||
return x509Cert;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected X509Certificate getCertificateFromTrustStore(PublicKey pk) throws IOException {
|
||||
return getCertificate(pk, trustStore);
|
||||
}
|
||||
|
||||
protected X509Certificate getCertificateFromTrustStore(String issuerName, BigInteger serialNumber)
|
||||
throws IOException {
|
||||
try {
|
||||
Enumeration aliases = trustStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
Certificate cert = trustStore.getCertificate(alias);
|
||||
if (cert == null || !X_509_CERTIFICATE_TYPE.equals(cert.getType())) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
String thisIssuerName = RFC2253Parser.normalize(x509Cert.getIssuerDN().getName());
|
||||
BigInteger thisSerialNumber = x509Cert.getSerialNumber();
|
||||
if (thisIssuerName.equals(issuerName) && thisSerialNumber.equals(serialNumber)) {
|
||||
return x509Cert;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Private Key methods
|
||||
|
||||
protected PrivateKey getPrivateKey(String alias) throws IOException {
|
||||
try {
|
||||
return (PrivateKey) keyStore.getKey(alias, privateKeyPassword);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected PrivateKey getPrivateKey(PublicKey publicKey) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
if (keyStore.isKeyEntry(alias)) {
|
||||
// Just returning the first one here
|
||||
return (PrivateKey) keyStore.getKey(alias, privateKeyPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PrivateKey getPrivateKey(X509Certificate certificate) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
if (!keyStore.isKeyEntry(alias)) {
|
||||
continue;
|
||||
}
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
if (cert != null && cert.equals(certificate)) {
|
||||
return (PrivateKey) keyStore.getKey(alias, privateKeyPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PrivateKey getPrivateKey(byte[] keyIdentifier) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
if (!keyStore.isKeyEntry(alias)) {
|
||||
continue;
|
||||
}
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
if (cert == null || !"X.509".equals(cert.getType())) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
byte[] keyId = getSubjectKeyIdentifier(x509Cert);
|
||||
if (keyId == null) {
|
||||
// Cert does not contain a key identifier
|
||||
continue;
|
||||
}
|
||||
if (Arrays.equals(keyIdentifier, keyId)) {
|
||||
return (PrivateKey) keyStore.getKey(alias, privateKeyPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PrivateKey getPrivateKey(String issuerName, BigInteger serialNumber) throws IOException {
|
||||
try {
|
||||
Enumeration aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
if (!keyStore.isKeyEntry(alias)) {
|
||||
continue;
|
||||
}
|
||||
Certificate cert = keyStore.getCertificate(alias);
|
||||
if (cert == null || !"X.509".equals(cert.getType())) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate x509Cert = (X509Certificate) cert;
|
||||
String thisIssuerName = RFC2253Parser.normalize(x509Cert.getIssuerDN().getName());
|
||||
BigInteger thisSerialNumber = x509Cert.getSerialNumber();
|
||||
if (thisIssuerName.equals(issuerName) && thisSerialNumber.equals(serialNumber)) {
|
||||
return (PrivateKey) keyStore.getKey(alias, privateKeyPassword);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
protected final byte[] getSubjectKeyIdentifier(X509Certificate cert) {
|
||||
byte[] subjectKeyIdentifier = cert.getExtensionValue(SUBJECT_KEY_IDENTIFIER_OID);
|
||||
if (subjectKeyIdentifier == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] dest = new byte[subjectKeyIdentifier.length - 4];
|
||||
System.arraycopy(subjectKeyIdentifier, 4, dest, 0, subjectKeyIdentifier.length - 4);
|
||||
return dest;
|
||||
}
|
||||
|
||||
//
|
||||
// Symmetric key methods
|
||||
//
|
||||
|
||||
protected SecretKey getSymmetricKey(String alias) throws IOException {
|
||||
try {
|
||||
return (SecretKey) symmetricStore.getKey(alias, symmetricKeyPassword);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the key store indicated by system properties. This method tries to load a key store by consulting the
|
||||
* following system properties:<code>javax.net.ssl.keyStore</code>, <code>javax.net.ssl.keyStorePassword</code>, and
|
||||
* <code>javax.net.ssl.keyStoreType</code>.
|
||||
* <p/>
|
||||
* If these properties specify a file with an appropriate password, the factory uses this file for the key store. If
|
||||
* that file does not exist, then a default, empty keystore is created.
|
||||
* <p/>
|
||||
* This behavior corresponds to the standard J2SDK behavior for SSL key stores.
|
||||
*
|
||||
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509KeyManager">The
|
||||
* standard J2SDK SSL key store mechanism</a>
|
||||
*/
|
||||
protected void loadDefaultKeyStore() {
|
||||
Resource location = null;
|
||||
String type = null;
|
||||
String password = null;
|
||||
String locationProperty = System.getProperty("javax.net.ssl.keyStore");
|
||||
if (StringUtils.hasLength(locationProperty)) {
|
||||
File f = new File(locationProperty);
|
||||
if (f.exists() && f.isFile() && f.canRead()) {
|
||||
location = new FileSystemResource(f);
|
||||
}
|
||||
String passwordProperty = System.getProperty("javax.net.ssl.keyStorePassword");
|
||||
if (StringUtils.hasLength(passwordProperty)) {
|
||||
password = passwordProperty;
|
||||
}
|
||||
type = System.getProperty("javax.net.ssl.trustStore");
|
||||
}
|
||||
// use the factory bean here, easier to setup
|
||||
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
|
||||
factoryBean.setLocation(location);
|
||||
factoryBean.setPassword(password);
|
||||
factoryBean.setType(type);
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
this.trustStore = (KeyStore) factoryBean.getObject();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded default key store");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Could not open default key store", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a default trust store. This method uses the following algorithm: <ol> <li> If the system property
|
||||
* <code>javax.net.ssl.trustStore</code> is defined, its value is loaded. If the
|
||||
* <code>javax.net.ssl.trustStorePassword</code> system property is also defined, its value is used as a password.
|
||||
* If the <code>javax.net.ssl.trustStoreType</code> system property is defined, its value is used as a key store
|
||||
* type.
|
||||
* <p/>
|
||||
* If <code>javax.net.ssl.trustStore</code> is defined but the specified file does not exist, then a default, empty
|
||||
* trust store is created. </li> <li> If the <code>javax.net.ssl.trustStore</code> system property was not
|
||||
* specified, but if the file <code>$JAVA_HOME/lib/security/jssecacerts</code> exists, that file is used. </li>
|
||||
* Otherwise, <li>If the file <code>$JAVA_HOME/lib/security/cacerts</code> exists, that file is used. </ol>
|
||||
* <p/>
|
||||
* This behavior corresponds to the standard J2SDK behavior for SSL trust stores.
|
||||
*
|
||||
* @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager">The
|
||||
* standard J2SDK SSL trust store mechanism</a>
|
||||
*/
|
||||
protected void loadDefaultTrustStore() {
|
||||
Resource location = null;
|
||||
String type = null;
|
||||
String password = null;
|
||||
String locationProperty = System.getProperty("javax.net.ssl.trustStore");
|
||||
if (StringUtils.hasLength(locationProperty)) {
|
||||
File f = new File(locationProperty);
|
||||
if (f.exists() && f.isFile() && f.canRead()) {
|
||||
location = new FileSystemResource(f);
|
||||
}
|
||||
String passwordProperty = System.getProperty("javax.net.ssl.trustStorePassword");
|
||||
if (StringUtils.hasLength(passwordProperty)) {
|
||||
password = passwordProperty;
|
||||
}
|
||||
type = System.getProperty("javax.net.ssl.trustStoreType");
|
||||
}
|
||||
else {
|
||||
String javaHome = System.getProperty("java.home");
|
||||
location = new FileSystemResource(javaHome + "/lib/security/jssecacerts");
|
||||
if (!location.exists()) {
|
||||
location = new FileSystemResource(javaHome + "/lib/security/cacerts");
|
||||
}
|
||||
}
|
||||
// use the factory bean here, easier to setup
|
||||
KeyStoreFactoryBean factoryBean = new KeyStoreFactoryBean();
|
||||
factoryBean.setLocation(location);
|
||||
factoryBean.setPassword(password);
|
||||
factoryBean.setType(type);
|
||||
try {
|
||||
factoryBean.afterPropertiesSet();
|
||||
this.trustStore = (KeyStore) factoryBean.getObject();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Loaded default trust store");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.warn("Could not open default trust store", ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Inner classes
|
||||
//
|
||||
|
||||
private class KeyStoreCertificateValidator implements CertificateValidationCallback.CertificateValidator {
|
||||
|
||||
public boolean validate(X509Certificate certificate)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
if (isOwnedCert(certificate)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Certificate with DN [" + certificate.getSubjectX500Principal().getName() +
|
||||
"] is in private keystore");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (trustStore == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
certificate.checkValidity();
|
||||
}
|
||||
catch (CertificateExpiredException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Certificate with DN [" + certificate.getSubjectX500Principal().getName() +
|
||||
"] has expired");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (CertificateNotYetValidException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Certificate with DN [" + certificate.getSubjectX500Principal().getName() +
|
||||
"] is not yet valid");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
X509CertSelector certSelector = new X509CertSelector();
|
||||
certSelector.setCertificate(certificate);
|
||||
|
||||
PKIXBuilderParameters parameters;
|
||||
CertPathBuilder builder;
|
||||
try {
|
||||
parameters = new PKIXBuilderParameters(trustStore, certSelector);
|
||||
parameters.setRevocationEnabled(false);
|
||||
builder = CertPathBuilder.getInstance("PKIX");
|
||||
}
|
||||
catch (GeneralSecurityException ex) {
|
||||
throw new CertificateValidationCallback.CertificateValidationException(
|
||||
"Could not create PKIX CertPathBuilder", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
builder.build(parameters);
|
||||
}
|
||||
catch (CertPathBuilderException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Certification path of certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] could not be validated");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (InvalidAlgorithmParameterException e) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Algorithm of certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] could not be validated");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Certificate with DN [" + certificate.getSubjectX500Principal().getName() + "] validated");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isOwnedCert(X509Certificate cert)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
if (keyStore == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Enumeration aliases = keyStore.aliases();
|
||||
while (aliases.hasMoreElements()) {
|
||||
String alias = (String) aliases.nextElement();
|
||||
if (keyStore.isKeyEntry(alias)) {
|
||||
X509Certificate x509Cert = (X509Certificate) keyStore.getCertificate(alias);
|
||||
if (x509Cert != null) {
|
||||
if (x509Cert.equals(cert)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
throw new CertificateValidationCallback.CertificateValidationException(
|
||||
"Could not determine whether certificate is contained in main key store", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
|
||||
/**
|
||||
* Mock implementation of of callback handler that accepts all password and certificate validation callbacks.
|
||||
* <p/>
|
||||
* If the <code>valid</code> property is set to <code>true</code> (the default), this handler simply accepts and
|
||||
* validates every password or certificate validation callback that is passed to it.
|
||||
* <p/>
|
||||
* This class handles <code>CertificateValidationCallback</code>s and <code>PasswordValidationCallback</code>s, and
|
||||
* throws an <code>UnsupportedCallbackException</code> for others
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class MockValidationCallbackHandler extends AbstractCallbackHandler {
|
||||
|
||||
private boolean isValid = true;
|
||||
|
||||
public MockValidationCallbackHandler() {
|
||||
}
|
||||
|
||||
public MockValidationCallbackHandler(boolean valid) {
|
||||
isValid = valid;
|
||||
}
|
||||
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
CertificateValidationCallback validationCallback = (CertificateValidationCallback) callback;
|
||||
validationCallback.setValidator(new MockCertificateValidator());
|
||||
}
|
||||
else if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback;
|
||||
validationCallback.setValidator(new MockPasswordValidator());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void setValid(boolean valid) {
|
||||
isValid = valid;
|
||||
}
|
||||
|
||||
private class MockCertificateValidator implements CertificateValidationCallback.CertificateValidator {
|
||||
|
||||
public boolean validate(X509Certificate certificate)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
||||
private class MockPasswordValidator implements PasswordValidationCallback.PasswordValidator {
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple callback handler that validates passwords agains a in-memory <code>Properties</code> object. Password
|
||||
* validation is done on a case-sensitive basis.
|
||||
* <p/>
|
||||
* This class only handles <code>PasswordValidationCallback</code>s, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #setUsers(java.util.Properties)
|
||||
*/
|
||||
public class SimplePasswordValidationCallbackHandler extends AbstractCallbackHandler implements InitializingBean {
|
||||
|
||||
private Properties users = new Properties();
|
||||
|
||||
/**
|
||||
* Sets the users to validate against. Property names are usernames, property values are passwords.
|
||||
*/
|
||||
public void setUsers(Properties users) {
|
||||
this.users = users;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(users, "users is required");
|
||||
}
|
||||
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback passwordCallback = (PasswordValidationCallback) callback;
|
||||
if (passwordCallback.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
|
||||
passwordCallback.setValidator(new SimplePlainTextPasswordValidator());
|
||||
}
|
||||
else if (passwordCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
|
||||
PasswordValidationCallback.DigestPasswordRequest digestPasswordRequest =
|
||||
(PasswordValidationCallback.DigestPasswordRequest) passwordCallback.getRequest();
|
||||
String password = users.getProperty(digestPasswordRequest.getUsername());
|
||||
digestPasswordRequest.setPassword(password);
|
||||
passwordCallback.setValidator(new PasswordValidationCallback.DigestPasswordValidator());
|
||||
}
|
||||
passwordCallback.setValidator(new SimplePlainTextPasswordValidator());
|
||||
}
|
||||
else if (callback instanceof TimestampValidationCallback) {
|
||||
TimestampValidationCallback timestampCallback = (TimestampValidationCallback) callback;
|
||||
timestampCallback.setValidator(new DefaultTimestampValidator());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUsersMap(Map users) {
|
||||
for (Iterator iterator = users.keySet().iterator(); iterator.hasNext();) {
|
||||
String username = (String) iterator.next();
|
||||
String password = (String) users.get(username);
|
||||
this.users.setProperty(username, password);
|
||||
}
|
||||
}
|
||||
|
||||
private class SimplePlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator {
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest plainTextPasswordRequest =
|
||||
(PasswordValidationCallback.PlainTextPasswordRequest) request;
|
||||
String password = users.getProperty(plainTextPasswordRequest.getUsername());
|
||||
return password != null && password.equals(plainTextPasswordRequest.getPassword());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordCallback;
|
||||
import com.sun.xml.wss.impl.callback.UsernameCallback;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple callback handler that supplies a username and password to a username token at runtime.
|
||||
* <p/>
|
||||
* This class handles <code>UsernameCallback</code>s and <code>PasswordCallback</code>s, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #setUsername(String)
|
||||
* @see #setPassword(String)
|
||||
*/
|
||||
public class SimpleUsernamePasswordCallbackHandler extends AbstractCallbackHandler implements InitializingBean {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.hasLength(username, "username must be set");
|
||||
Assert.hasLength(password, "password must be set");
|
||||
}
|
||||
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof UsernameCallback) {
|
||||
UsernameCallback usernameCallback = (UsernameCallback) callback;
|
||||
usernameCallback.setUsername(username);
|
||||
}
|
||||
else if (callback instanceof PasswordCallback) {
|
||||
PasswordCallback passwordCallback = (PasswordCallback) callback;
|
||||
passwordCallback.setPassword(password);
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.x509.X509AuthenticationToken;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
/**
|
||||
* Callback handler that validates a certificate using an Acegi <code>AuthenticationManager</code>. Logic based on
|
||||
* Acegi's <code>X509ProcessingFilter</code>.
|
||||
* <p/>
|
||||
* An Acegi <code>X509AuthenticationToken</code> is created with the certificate as the credentials.
|
||||
* <p/>
|
||||
* The configured authentication manager is expected to supply a provider which can handle this token (usually an
|
||||
* instance of <code>X509AuthenticationProvider</code>).</p>
|
||||
* <p/>
|
||||
* This class only handles <code>CertificateValidationCallback</code>s, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see X509AuthenticationToken
|
||||
* @see org.acegisecurity.providers.x509.X509AuthenticationProvider
|
||||
* @see org.acegisecurity.ui.x509.X509ProcessingFilter
|
||||
* @see CertificateValidationCallback
|
||||
*/
|
||||
public class AcegiCertificateValidationCallbackHandler extends AbstractCallbackHandler {
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
private boolean ignoreFailure = false;
|
||||
|
||||
/**
|
||||
* Sets the Acegi authentication manager. Required.
|
||||
*/
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void setIgnoreFailure(boolean ignoreFailure) {
|
||||
this.ignoreFailure = ignoreFailure;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationManager, "authenticationManager is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles <code>CertificateValidationCallback</code>s, and throws an <code>UnsupportedCallbackException</code> for
|
||||
* others
|
||||
*
|
||||
* @throws UnsupportedCallbackException when the callback is not supported
|
||||
*/
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
((CertificateValidationCallback) callback).setValidator(new AcegiCertificateValidator());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private class AcegiCertificateValidator implements CertificateValidationCallback.CertificateValidator {
|
||||
|
||||
public boolean validate(X509Certificate certificate)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
boolean result;
|
||||
try {
|
||||
Authentication authResult =
|
||||
authenticationManager.authenticate(new X509AuthenticationToken(certificate));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] successful");
|
||||
}
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
return true;
|
||||
}
|
||||
catch (AuthenticationException failed) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] failed: " + failed.toString());
|
||||
}
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
result = ignoreFailure;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.acegisecurity.providers.dao.UserCache;
|
||||
import org.acegisecurity.providers.dao.cache.NullUserCache;
|
||||
import org.acegisecurity.userdetails.UserDetails;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
import org.springframework.ws.soap.security.xwss.callback.DefaultTimestampValidator;
|
||||
|
||||
/**
|
||||
* Callback handler that validates a password digest using an Acegi <code>UserDetailsService</code>. Logic based on
|
||||
* Acegi's <code>DigestProcessingFilter</code>.
|
||||
* <p/>
|
||||
* An Acegi <code>UserDetailService</code> is used to load <code>UserDetails</code> from. The digest of the password
|
||||
* contained in this details object is then compared with the digest in the message.
|
||||
* <p/>
|
||||
* This class only handles <code>PasswordValidationCallback</code>s that contain a <code>DigestPasswordRequest</code>,
|
||||
* and throws an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see UserDetailsService
|
||||
* @see PasswordValidationCallback
|
||||
* @see com.sun.xml.wss.impl.callback.PasswordValidationCallback.DigestPasswordRequest
|
||||
* @see org.acegisecurity.ui.digestauth.DigestProcessingFilter
|
||||
*/
|
||||
public class AcegiDigestPasswordValidationCallbackHandler extends AbstractCallbackHandler {
|
||||
|
||||
private UserCache userCache = new NullUserCache();
|
||||
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
/**
|
||||
* Sets the users cache. Not required, but can benefit performance.
|
||||
*/
|
||||
public void setUserCache(UserCache userCache) {
|
||||
this.userCache = userCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Acegi user details service. Required.
|
||||
*/
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(userDetailsService, "userDetailsService is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles <code>PasswordValidationCallback</code>s that contain a <code>DigestPasswordRequest</code>, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others
|
||||
*
|
||||
* @throws UnsupportedCallbackException when the callback is not supported
|
||||
*/
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback passwordCallback = ((PasswordValidationCallback) callback);
|
||||
if (passwordCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
|
||||
PasswordValidationCallback.DigestPasswordRequest request =
|
||||
(PasswordValidationCallback.DigestPasswordRequest) passwordCallback.getRequest();
|
||||
String username = request.getUsername();
|
||||
UserDetails user = loadUserDetails(username);
|
||||
if (user != null) {
|
||||
request.setPassword(user.getPassword());
|
||||
}
|
||||
AcegiDigestPasswordValidator validator = new AcegiDigestPasswordValidator(user);
|
||||
passwordCallback.setValidator(validator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (callback instanceof TimestampValidationCallback) {
|
||||
TimestampValidationCallback timestampCallback = (TimestampValidationCallback) callback;
|
||||
timestampCallback.setValidator(new DefaultTimestampValidator());
|
||||
|
||||
}
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
private UserDetails loadUserDetails(String username) {
|
||||
UserDetails user = userCache.getUserFromCache(username);
|
||||
|
||||
if (user == null) {
|
||||
try {
|
||||
user = userDetailsService.loadUserByUsername(username);
|
||||
}
|
||||
catch (UsernameNotFoundException notFound) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Username '" + username + "' not found");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
userCache.putUserInCache(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private class AcegiDigestPasswordValidator extends PasswordValidationCallback.DigestPasswordValidator {
|
||||
|
||||
private UserDetails user;
|
||||
|
||||
private AcegiDigestPasswordValidator(UserDetails user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
if (super.validate(request)) {
|
||||
UsernamePasswordAuthenticationToken authRequest =
|
||||
new UsernamePasswordAuthenticationToken(user, user.getPassword());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authRequest.toString());
|
||||
}
|
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authRequest);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationException;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
/**
|
||||
* Callback handler that validates a certificate uses an Acegi <code>AuthenticationManager</code>. Logic based on
|
||||
* Acegi's <code>BasicProcessingFilter</code>.
|
||||
* <p/>
|
||||
* This handler requires an Acegi <code>AuthenticationManager</code> to operate. It can be set using the
|
||||
* <code>authenticationManager</code> property. An Acegi <code>UsernamePasswordAuthenticationToken</code> is created
|
||||
* with the username as principal and password as credentials.
|
||||
* <p/>
|
||||
* This class only handles <code>PasswordValidationCallback</code>s that contain a
|
||||
* <code>PlainTextPasswordRequest</code>, and throws an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see UsernamePasswordAuthenticationToken
|
||||
* @see PasswordValidationCallback
|
||||
* @see com.sun.xml.wss.impl.callback.PasswordValidationCallback.PlainTextPasswordRequest
|
||||
* @see org.acegisecurity.ui.basicauth.BasicProcessingFilter
|
||||
*/
|
||||
public class AcegiPlainTextPasswordValidationCallbackHandler extends AbstractCallbackHandler {
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
private boolean ignoreFailure = false;
|
||||
|
||||
/**
|
||||
* Sets the Acegi authentication manager. Required.
|
||||
*/
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
public void setIgnoreFailure(boolean ignoreFailure) {
|
||||
this.ignoreFailure = ignoreFailure;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(authenticationManager, "authenticationManager is required");
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles <code>PasswordValidationCallback</code>s that contain a <code>PlainTextPasswordRequest</code>, and throws
|
||||
* an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @throws UnsupportedCallbackException when the callback is not supported
|
||||
*/
|
||||
protected void handleInternal(Callback callback) throws IOException, UnsupportedCallbackException {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback;
|
||||
if (validationCallback.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
|
||||
validationCallback.setValidator(new AcegiPlainTextPasswordValidator());
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
private class AcegiPlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator {
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest =
|
||||
(PasswordValidationCallback.PlainTextPasswordRequest) request;
|
||||
try {
|
||||
Authentication authResult = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
|
||||
plainTextRequest.getUsername(), plainTextRequest.getPassword()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication success: " + authResult.toString());
|
||||
}
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
return true;
|
||||
}
|
||||
catch (AuthenticationException failed) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for user '" + plainTextRequest.getUsername() + "' failed: " +
|
||||
failed.toString());
|
||||
}
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
return ignoreFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains <code>CallbackHandler</code> implementations for XWSS that use the <a href="http://www.acegisecurity.org/">Acegi
|
||||
Security System for Spring</a>.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
/**
|
||||
* Abstract base class for integrating with JAAS. Provides a login context name property.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public abstract class AbstractJaasValidationCallbackHandler extends AbstractCallbackHandler
|
||||
implements InitializingBean {
|
||||
|
||||
private String loginContextName;
|
||||
|
||||
protected AbstractJaasValidationCallbackHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the login context name.
|
||||
*/
|
||||
public String getLoginContextName() {
|
||||
return loginContextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the login context name.
|
||||
*/
|
||||
public void setLoginContextName(String loginContextName) {
|
||||
this.loginContextName = loginContextName;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(loginContextName, "loginContextName is required");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
|
||||
/**
|
||||
* Provides basic support for integrating with JAAS and certificates. Requires the <code>loginContextName</code> to be
|
||||
* set.Requires a <code>LoginContext</code> which handles <code>X500Principal</code>s.
|
||||
* <p/>
|
||||
* This class only handles <code>CertificateValidationCallback</code>s, and throws an
|
||||
* <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see javax.security.auth.x500.X500Principal
|
||||
* @see #setLoginContextName(String)
|
||||
*/
|
||||
public class JaasCertificateValidationCallbackHandler extends AbstractJaasValidationCallbackHandler {
|
||||
|
||||
/**
|
||||
* Handles <code>CertificateValidationCallback</code>s, and throws an <code>UnsupportedCallbackException</code> for
|
||||
* others
|
||||
*
|
||||
* @throws UnsupportedCallbackException when the callback is not supported
|
||||
*/
|
||||
protected final void handleInternal(Callback callback) throws UnsupportedCallbackException {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
((CertificateValidationCallback) callback).setValidator(new JaasCertificateValidator());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private class JaasCertificateValidator implements CertificateValidationCallback.CertificateValidator {
|
||||
|
||||
public boolean validate(X509Certificate certificate)
|
||||
throws CertificateValidationCallback.CertificateValidationException {
|
||||
LoginContext loginContext = null;
|
||||
Subject subject = new Subject();
|
||||
subject.getPrincipals().add(certificate.getSubjectX500Principal());
|
||||
try {
|
||||
loginContext = new LoginContext(getLoginContextName(), subject);
|
||||
}
|
||||
catch (LoginException ex) {
|
||||
throw new CertificateValidationCallback.CertificateValidationException(ex);
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
throw new CertificateValidationCallback.CertificateValidationException(ex);
|
||||
}
|
||||
|
||||
try {
|
||||
loginContext.login();
|
||||
Subject subj = loginContext.getSubject();
|
||||
if (!subj.getPrincipals().isEmpty()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] successful");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (LoginException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for certificate with DN [" +
|
||||
certificate.getSubjectX500Principal().getName() + "] failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
/**
|
||||
* Provides basic support for integrating with JAAS and plain text passwords.
|
||||
* <p/>
|
||||
* This class only handles <code>PasswordValidationCallback</code>s that contain a
|
||||
* <code>PlainTextPasswordRequest</code>, and throws an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @see #getLoginContextName()
|
||||
*/
|
||||
public class JaasPlainTextPasswordValidationCallbackHandler extends AbstractJaasValidationCallbackHandler {
|
||||
|
||||
protected JaasPlainTextPasswordValidationCallbackHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles <code>PasswordValidationCallback</code>s that contain a <code>PlainTextPasswordRequest</code>, and throws
|
||||
* an <code>UnsupportedCallbackException</code> for others.
|
||||
*
|
||||
* @throws UnsupportedCallbackException when the callback is not supported
|
||||
*/
|
||||
protected final void handleInternal(Callback callback) throws UnsupportedCallbackException {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback;
|
||||
if (validationCallback.getRequest() instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
|
||||
validationCallback.setValidator(new JaasPlainTextPasswordValidator());
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
|
||||
private class JaasPlainTextPasswordValidator implements PasswordValidationCallback.PasswordValidator {
|
||||
|
||||
public boolean validate(PasswordValidationCallback.Request request)
|
||||
throws PasswordValidationCallback.PasswordValidationException {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest plainTextRequest =
|
||||
(PasswordValidationCallback.PlainTextPasswordRequest) request;
|
||||
|
||||
final String username = plainTextRequest.getUsername();
|
||||
final String password = plainTextRequest.getPassword();
|
||||
|
||||
LoginContext loginContext = null;
|
||||
try {
|
||||
loginContext = new LoginContext(getLoginContextName(), new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) throws UnsupportedCallbackException {
|
||||
if (callback instanceof NameCallback) {
|
||||
((NameCallback) callback).setName(username);
|
||||
}
|
||||
else if (callback instanceof PasswordCallback) {
|
||||
((PasswordCallback) callback).setPassword(password.toCharArray());
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedCallbackException(callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (LoginException ex) {
|
||||
throw new PasswordValidationCallback.PasswordValidationException(ex);
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
throw new PasswordValidationCallback.PasswordValidationException(ex);
|
||||
}
|
||||
|
||||
try {
|
||||
loginContext.login();
|
||||
Subject subject = loginContext.getSubject();
|
||||
if (!subject.getPrincipals().isEmpty()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for user '" + username + "' successful");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for user '" + username + "' failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (LoginException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Authentication request for user '" + username + "' failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains <code>CallbackHandler</code> implementations for XWSS that use the <a
|
||||
href="http://java.sun.com/products/jaas/">Java Authentication and Authorization Service (JAAS)</a>.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains <code>CallbackHandler</code> implementations for XWSS.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Contains classes for using the <a href="https://xwss.dev.java.net/">XML and WebServices Security</a> WS-Security
|
||||
implementation within Spring-WS.
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import javax.xml.soap.MessageFactory;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.springframework.ws.soap.saaj.SaajSoapMessageContext;
|
||||
import org.springframework.ws.transport.TransportRequest;
|
||||
import org.springframework.ws.transport.TransportException;
|
||||
|
||||
public class XwsSecurityInterceptorTest extends TestCase {
|
||||
|
||||
private MessageFactory messageFactory;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
messageFactory = MessageFactory.newInstance();
|
||||
}
|
||||
|
||||
public void testhandleRequest() throws Exception {
|
||||
final SOAPMessage request = messageFactory.createMessage();
|
||||
final SOAPMessage validatedRequest = messageFactory.createMessage();
|
||||
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
|
||||
protected SOAPMessage secureMessage(SOAPMessage message) throws XwsSecuritySecurementException {
|
||||
fail("secure not expected");
|
||||
return null;
|
||||
}
|
||||
|
||||
protected SOAPMessage validateMessage(SOAPMessage message) throws XwsSecurityValidationException {
|
||||
assertEquals("Invalid message", request, message);
|
||||
return validatedRequest;
|
||||
}
|
||||
|
||||
};
|
||||
SaajSoapMessageContext context =
|
||||
new SaajSoapMessageContext(request, new DummyTransportRequest(), messageFactory);
|
||||
interceptor.handleRequest(context, null);
|
||||
assertEquals("Invalid request", validatedRequest, context.getSaajRequest());
|
||||
}
|
||||
|
||||
public void testhandleResponse() throws Exception {
|
||||
final SOAPMessage response = messageFactory.createMessage();
|
||||
final SOAPMessage securedResponse = messageFactory.createMessage();
|
||||
XwsSecurityInterceptor interceptor = new XwsSecurityInterceptor() {
|
||||
protected SOAPMessage secureMessage(SOAPMessage message) throws XwsSecuritySecurementException {
|
||||
assertEquals("Invalid message", response, message);
|
||||
return securedResponse;
|
||||
}
|
||||
|
||||
protected SOAPMessage validateMessage(SOAPMessage message) throws XwsSecurityValidationException {
|
||||
fail("validate not expected");
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
SOAPMessage request = messageFactory.createMessage();
|
||||
SaajSoapMessageContext context =
|
||||
new SaajSoapMessageContext(request, new DummyTransportRequest(), messageFactory);
|
||||
context.setSaajResponse(response);
|
||||
interceptor.handleResponse(context, null);
|
||||
assertEquals("Invalid response", securedResponse, context.getSaajResponse());
|
||||
}
|
||||
|
||||
private static class DummyTransportRequest implements TransportRequest {
|
||||
|
||||
public Iterator getHeaderNames() {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
|
||||
public Iterator getHeaders(String name) {
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
|
||||
public String getUrl() throws TransportException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.DecryptionKeyCallback;
|
||||
import com.sun.xml.wss.impl.callback.EncryptionKeyCallback;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
public class XwssMessageInterceptorEncryptTest extends XwssMessageInterceptorKeyStoreTestCase {
|
||||
|
||||
public void testEncryptDefaultCertificate() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("encrypt-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof EncryptionKeyCallback) {
|
||||
EncryptionKeyCallback keyCallback = (EncryptionKeyCallback) callback;
|
||||
if (keyCallback.getRequest() instanceof EncryptionKeyCallback.AliasX509CertificateRequest) {
|
||||
EncryptionKeyCallback.AliasX509CertificateRequest request =
|
||||
(EncryptionKeyCallback.AliasX509CertificateRequest) keyCallback.getRequest();
|
||||
assertEquals("Invalid alias", "", request.getAlias());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathExists("BinarySecurityToken does not exist",
|
||||
"SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:BinarySecurityToken", result);
|
||||
assertXpathExists("Signature does not exist",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/xenc:EncryptedKey", result);
|
||||
}
|
||||
|
||||
public void testEncryptAlias() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("encrypt-alias-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof EncryptionKeyCallback) {
|
||||
EncryptionKeyCallback keyCallback = (EncryptionKeyCallback) callback;
|
||||
if (keyCallback.getRequest() instanceof EncryptionKeyCallback.AliasX509CertificateRequest) {
|
||||
EncryptionKeyCallback.AliasX509CertificateRequest request =
|
||||
(EncryptionKeyCallback.AliasX509CertificateRequest) keyCallback.getRequest();
|
||||
assertEquals("Invalid alias", "alias", request.getAlias());
|
||||
request.setX509Certificate(certificate);
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathExists("BinarySecurityToken does not exist",
|
||||
"SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:BinarySecurityToken", result);
|
||||
assertXpathExists("Signature does not exist",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/xenc:EncryptedKey", result);
|
||||
}
|
||||
|
||||
public void testDecrypt() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("decrypt-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof DecryptionKeyCallback) {
|
||||
DecryptionKeyCallback keyCallback = (DecryptionKeyCallback) callback;
|
||||
if (keyCallback.getRequest() instanceof DecryptionKeyCallback.X509CertificateBasedRequest) {
|
||||
DecryptionKeyCallback.X509CertificateBasedRequest request =
|
||||
(DecryptionKeyCallback.X509CertificateBasedRequest) keyCallback.getRequest();
|
||||
assertEquals("Invalid certificate", certificate, request.getX509Certificate());
|
||||
request.setPrivateKey(privateKey);
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("encrypted-soap.xml");
|
||||
SOAPMessage result = interceptor.validateMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public abstract class XwssMessageInterceptorKeyStoreTestCase extends XwssMessageInterceptorTestCase {
|
||||
|
||||
protected X509Certificate certificate;
|
||||
|
||||
protected PrivateKey privateKey;
|
||||
|
||||
protected void onSetup() throws Exception {
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = getClass().getResourceAsStream("test-keystore.jks");
|
||||
keyStore.load(is, "password".toCharArray());
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
certificate = (X509Certificate) keyStore.getCertificate("alias");
|
||||
privateKey = (PrivateKey) keyStore.getKey("alias", "password".toCharArray());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.SignatureKeyCallback;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
public class XwssMessageInterceptorSignTest extends XwssMessageInterceptorKeyStoreTestCase {
|
||||
|
||||
public void testSignDefaultCertificate() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("sign-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof SignatureKeyCallback) {
|
||||
SignatureKeyCallback keyCallback = (SignatureKeyCallback) callback;
|
||||
if (keyCallback.getRequest() instanceof SignatureKeyCallback.DefaultPrivKeyCertRequest) {
|
||||
SignatureKeyCallback.DefaultPrivKeyCertRequest request =
|
||||
(SignatureKeyCallback.DefaultPrivKeyCertRequest) keyCallback.getRequest();
|
||||
request.setX509Certificate(certificate);
|
||||
request.setPrivateKey(privateKey);
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathExists("BinarySecurityToken does not exist",
|
||||
"SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:BinarySecurityToken", result);
|
||||
assertXpathExists("Signature does not exist", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature",
|
||||
result);
|
||||
}
|
||||
|
||||
public void testSignAlias() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("sign-alias-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof SignatureKeyCallback) {
|
||||
SignatureKeyCallback keyCallback = (SignatureKeyCallback) callback;
|
||||
if (keyCallback.getRequest() instanceof SignatureKeyCallback.AliasPrivKeyCertRequest) {
|
||||
SignatureKeyCallback.AliasPrivKeyCertRequest request =
|
||||
(SignatureKeyCallback.AliasPrivKeyCertRequest) keyCallback.getRequest();
|
||||
assertEquals("Invalid alias", "alias", request.getAlias());
|
||||
request.setX509Certificate(certificate);
|
||||
request.setPrivateKey(privateKey);
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathExists("BinarySecurityToken does not exist",
|
||||
"SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:BinarySecurityToken", result);
|
||||
assertXpathExists("Signature does not exist", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/ds:Signature",
|
||||
result);
|
||||
}
|
||||
|
||||
public void testValidateCertificate() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("requireSignature-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof CertificateValidationCallback) {
|
||||
CertificateValidationCallback validationCallback = (CertificateValidationCallback) callback;
|
||||
validationCallback.setValidator(new CertificateValidationCallback.CertificateValidator() {
|
||||
public boolean validate(X509Certificate passedCertificate) {
|
||||
assertEquals("Invalid certificate", certificate, passedCertificate);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("signed-soap.xml");
|
||||
SOAPMessage result = interceptor.validateMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.soap.MessageFactory;
|
||||
import javax.xml.soap.MimeHeaders;
|
||||
import javax.xml.soap.SOAPException;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import org.springframework.xml.xpath.XPathExpression;
|
||||
import org.springframework.xml.xpath.XPathExpressionFactory;
|
||||
|
||||
public abstract class XwssMessageInterceptorTestCase extends TestCase {
|
||||
|
||||
protected XwsSecurityInterceptor interceptor;
|
||||
|
||||
private MessageFactory messageFactory;
|
||||
|
||||
private Map namespaces;
|
||||
|
||||
protected final void setUp() throws Exception {
|
||||
interceptor = new XwsSecurityInterceptor();
|
||||
messageFactory = MessageFactory.newInstance();
|
||||
namespaces = new HashMap();
|
||||
namespaces.put("SOAP-ENV", "http://schemas.xmlsoap.org/soap/envelope/");
|
||||
namespaces.put("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
|
||||
namespaces.put("ds", "http://www.w3.org/2000/09/xmldsig#");
|
||||
namespaces.put("xenc", "http://www.w3.org/2001/04/xmlenc#");
|
||||
onSetup();
|
||||
}
|
||||
|
||||
protected void assertXpathEvaluatesTo(String message,
|
||||
String expectedValue,
|
||||
String xpathExpression,
|
||||
SOAPMessage soapMessage) {
|
||||
XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
|
||||
Document document = soapMessage.getSOAPPart();
|
||||
String actualValue = expression.evaluateAsString(document);
|
||||
assertEquals(message, expectedValue, actualValue);
|
||||
}
|
||||
|
||||
protected void assertXpathExists(String message, String xpathExpression, SOAPMessage soapMessage) {
|
||||
XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
|
||||
Document document = soapMessage.getSOAPPart();
|
||||
Node node = expression.evaluateAsNode(document);
|
||||
assertNotNull(message, node);
|
||||
}
|
||||
|
||||
protected void assertXpathNotExists(String message, String xpathExpression, SOAPMessage soapMessage) {
|
||||
XPathExpression expression = XPathExpressionFactory.createXPathExpression(xpathExpression, namespaces);
|
||||
Document document = soapMessage.getSOAPPart();
|
||||
Node node = expression.evaluateAsNode(document);
|
||||
assertNull(message, node);
|
||||
}
|
||||
|
||||
protected SOAPMessage loadSaajMessage(String fileName) throws SOAPException, IOException {
|
||||
MimeHeaders mimeHeaders = new MimeHeaders();
|
||||
mimeHeaders.addHeader("Content-Type", "text/xml");
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = getClass().getResourceAsStream(fileName);
|
||||
assertNotNull("Could not load SAAJ message with name [" + fileName + "]", is);
|
||||
return messageFactory.createMessage(mimeHeaders, is);
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSetup() throws Exception {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.xml.soap.SOAPMessage;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordCallback;
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.TimestampValidationCallback;
|
||||
import com.sun.xml.wss.impl.callback.UsernameCallback;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ws.soap.security.xwss.callback.AbstractCallbackHandler;
|
||||
|
||||
public class XwssMessageInterceptorUsernameTokenTest extends XwssMessageInterceptorTestCase {
|
||||
|
||||
public void testAddUsernameTokenDigest() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("usernameToken-digest-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof UsernameCallback) {
|
||||
((UsernameCallback) callback).setUsername("Bert");
|
||||
}
|
||||
else if (callback instanceof PasswordCallback) {
|
||||
PasswordCallback passwordCallback = (PasswordCallback) callback;
|
||||
passwordCallback.setPassword("Ernie");
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathEvaluatesTo("Invalid Username",
|
||||
"Bert",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Username/text()",
|
||||
result);
|
||||
assertXpathExists("Password does not exist",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Password[@Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest']",
|
||||
result);
|
||||
}
|
||||
|
||||
public void testAddUsernameTokenPlainText() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("usernameToken-plainText-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof UsernameCallback) {
|
||||
((UsernameCallback) callback).setUsername("Bert");
|
||||
}
|
||||
else if (callback instanceof PasswordCallback) {
|
||||
PasswordCallback passwordCallback = (PasswordCallback) callback;
|
||||
passwordCallback.setPassword("Ernie");
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("empty-soap.xml");
|
||||
SOAPMessage result = interceptor.secureMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathEvaluatesTo("Invalid Username",
|
||||
"Bert",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Username/text()",
|
||||
result);
|
||||
assertXpathEvaluatesTo("Invalid Password",
|
||||
"Ernie",
|
||||
"/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security/wsse:UsernameToken/wsse:Password[@Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText']/text()",
|
||||
result);
|
||||
}
|
||||
|
||||
public void testValidateUsernameTokenPlainText() throws Exception {
|
||||
interceptor
|
||||
.setPolicyConfiguration(new ClassPathResource("requireUsernameToken-plainText-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback;
|
||||
validationCallback.setValidator(new PasswordValidationCallback.PasswordValidator() {
|
||||
public boolean validate(PasswordValidationCallback.Request request) {
|
||||
if (request instanceof PasswordValidationCallback.PlainTextPasswordRequest) {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest passwordRequest =
|
||||
(PasswordValidationCallback.PlainTextPasswordRequest) request;
|
||||
assertEquals("Invalid username", "Bert", passwordRequest.getUsername());
|
||||
assertEquals("Invalid password", "Ernie", passwordRequest.getPassword());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("usernameTokenPlainText-soap.xml");
|
||||
SOAPMessage result = interceptor.validateMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
|
||||
}
|
||||
|
||||
public void testValidateUsernameTokenDigest() throws Exception {
|
||||
interceptor.setPolicyConfiguration(new ClassPathResource("requireUsernameToken-digest-config.xml", getClass()));
|
||||
CallbackHandler handler = new AbstractCallbackHandler() {
|
||||
|
||||
protected void handleInternal(Callback callback) {
|
||||
if (callback instanceof PasswordValidationCallback) {
|
||||
PasswordValidationCallback validationCallback = (PasswordValidationCallback) callback;
|
||||
if (validationCallback.getRequest() instanceof PasswordValidationCallback.DigestPasswordRequest) {
|
||||
PasswordValidationCallback.DigestPasswordRequest passwordRequest =
|
||||
(PasswordValidationCallback.DigestPasswordRequest) validationCallback.getRequest();
|
||||
assertEquals("Invalid username", "Bert", passwordRequest.getUsername());
|
||||
passwordRequest.setPassword("Ernie");
|
||||
validationCallback.setValidator(new PasswordValidationCallback.DigestPasswordValidator());
|
||||
}
|
||||
else {
|
||||
fail("Unexpected request");
|
||||
}
|
||||
}
|
||||
else if (callback instanceof TimestampValidationCallback) {
|
||||
TimestampValidationCallback validationCallback = (TimestampValidationCallback) callback;
|
||||
validationCallback.setValidator(new TimestampValidationCallback.TimestampValidator() {
|
||||
public void validate(TimestampValidationCallback.Request request) {
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
fail("Unexpected callback");
|
||||
}
|
||||
}
|
||||
};
|
||||
interceptor.setCallbackHandler(handler);
|
||||
interceptor.afterPropertiesSet();
|
||||
SOAPMessage message = loadSaajMessage("usernameTokenDigest-soap.xml");
|
||||
SOAPMessage result = interceptor.validateMessage(message);
|
||||
assertNotNull("No result returned", result);
|
||||
assertXpathNotExists("Security Header not removed", "/SOAP-ENV:Envelope/SOAP-ENV:Header/wsse:Security", result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class CallbackHandlerChainTest extends TestCase {
|
||||
|
||||
private CallbackHandler supported = new CallbackHandler() {
|
||||
public void handle(Callback[] callbacks) {
|
||||
}
|
||||
};
|
||||
|
||||
private CallbackHandler unsupported = new CallbackHandler() {
|
||||
public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
|
||||
throw new UnsupportedCallbackException(callbacks[0]);
|
||||
}
|
||||
};
|
||||
|
||||
private Callback callback = new Callback() {
|
||||
};
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
}
|
||||
|
||||
public void testSupported() throws Exception {
|
||||
CallbackHandlerChain chain = new CallbackHandlerChain(new CallbackHandler[]{supported});
|
||||
chain.handle(new Callback[]{callback});
|
||||
}
|
||||
|
||||
public void testUnsupportedNormal() throws Exception {
|
||||
CallbackHandlerChain chain = new CallbackHandlerChain(new CallbackHandler[]{unsupported, supported});
|
||||
chain.handle(new Callback[]{callback});
|
||||
}
|
||||
|
||||
public void testUnsupported() throws Exception {
|
||||
CallbackHandlerChain chain = new CallbackHandlerChain(new CallbackHandler[]{unsupported});
|
||||
try {
|
||||
chain.handle(new Callback[]{callback});
|
||||
fail("Expected UnsupportedCallbackException");
|
||||
}
|
||||
catch (UnsupportedCallbackException ex) {
|
||||
// expected behavior
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class KeyStoreCallbackHandlerTest extends TestCase {
|
||||
|
||||
private KeyStoreCallbackHandler handler;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
handler = new KeyStoreCallbackHandler();
|
||||
}
|
||||
|
||||
public void testLoadDefaultTrustStore() throws Exception {
|
||||
System.setProperty("javax.net.ssl.trustStore",
|
||||
"/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/");
|
||||
handler.loadDefaultTrustStore();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class SimplePasswordValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private SimplePasswordValidationCallbackHandler handler;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
handler = new SimplePasswordValidationCallbackHandler();
|
||||
Properties users = new Properties();
|
||||
users.setProperty("Bert", "Ernie");
|
||||
handler.setUsers(users);
|
||||
}
|
||||
|
||||
public void testPlainTextPasswordValid() throws Exception {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest("Bert", "Ernie");
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
handler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
}
|
||||
|
||||
public void testPlainTextPasswordInvalid() throws Exception {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest("Bert", "Big bird");
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
handler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
}
|
||||
|
||||
public void testPlainTextPasswordNoSuchUser() throws Exception {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest("Big bird", "Bert");
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
handler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
}
|
||||
|
||||
public void testDigestPasswordValid() throws Exception {
|
||||
String username = "Bert";
|
||||
String nonce = "9mdsYDCrjjYRur0rxzYt2oD7";
|
||||
String passwordDigest = "kwNstEaiFOrI7B31j7GuETYvdgk=";
|
||||
String creationTime = "2006-06-01T23:48:42Z";
|
||||
PasswordValidationCallback.DigestPasswordRequest request =
|
||||
new PasswordValidationCallback.DigestPasswordRequest(username, passwordDigest, nonce, creationTime);
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
handler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
|
||||
}
|
||||
|
||||
public void testDigestPasswordInvalid() throws Exception {
|
||||
String username = "Bert";
|
||||
String nonce = "9mdsYDCrjjYRur0rxzYt2oD7";
|
||||
String passwordDigest = "kwNstEaiFOrI7B31j7GuETYvdgk";
|
||||
String creationTime = "2006-06-01T23:48:42Z";
|
||||
PasswordValidationCallback.DigestPasswordRequest request =
|
||||
new PasswordValidationCallback.DigestPasswordRequest(username, passwordDigest, nonce, creationTime);
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
handler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordCallback;
|
||||
import com.sun.xml.wss.impl.callback.UsernameCallback;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class SimpleUsernamePasswordCallbackHandlerTest extends TestCase {
|
||||
|
||||
private SimpleUsernamePasswordCallbackHandler handler;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
handler = new SimpleUsernamePasswordCallbackHandler();
|
||||
handler.setUsername("Bert");
|
||||
handler.setPassword("Ernie");
|
||||
}
|
||||
|
||||
public void testUsernameCallback() throws Exception {
|
||||
UsernameCallback usernameCallback = new UsernameCallback();
|
||||
handler.handleInternal(usernameCallback);
|
||||
assertEquals("Invalid username", "Bert", usernameCallback.getUsername());
|
||||
}
|
||||
|
||||
public void testPasswordCallback() throws Exception {
|
||||
PasswordCallback passwordCallback = new PasswordCallback();
|
||||
handler.handleInternal(passwordCallback);
|
||||
assertEquals("Invalid username", "Ernie", passwordCallback.getPassword());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.providers.TestingAuthenticationToken;
|
||||
import org.acegisecurity.providers.x509.X509AuthenticationToken;
|
||||
import org.easymock.MockControl;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
public class AcegiCertificateValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private AcegiCertificateValidationCallbackHandler callbackHandler;
|
||||
|
||||
private MockControl control;
|
||||
|
||||
private AuthenticationManager mock;
|
||||
|
||||
private X509Certificate certificate;
|
||||
|
||||
private CertificateValidationCallback callback;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
callbackHandler = new AcegiCertificateValidationCallbackHandler();
|
||||
control = MockControl.createControl(AuthenticationManager.class);
|
||||
mock = (AuthenticationManager) control.getMock();
|
||||
callbackHandler.setAuthenticationManager(mock);
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new ClassPathResource("/org/springframework/ws/soap/security/xwss/test-keystore.jks").getInputStream();
|
||||
keyStore.load(is, "password".toCharArray());
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
certificate = (X509Certificate) keyStore.getCertificate("alias");
|
||||
callback = new CertificateValidationCallback(certificate);
|
||||
}
|
||||
|
||||
public void testValidateCertificateValid() throws Exception {
|
||||
mock.authenticate(new X509AuthenticationToken(certificate));
|
||||
control.setMatcher(MockControl.ALWAYS_MATCHER);
|
||||
control.setReturnValue(new TestingAuthenticationToken(certificate, null, new GrantedAuthority[0]));
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testValidateCertificateInvalid() throws Exception {
|
||||
mock.authenticate(new X509AuthenticationToken(certificate));
|
||||
control.setMatcher(MockControl.ALWAYS_MATCHER);
|
||||
control.setThrowable(new BadCredentialsException(""));
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.userdetails.User;
|
||||
import org.acegisecurity.userdetails.UserDetailsService;
|
||||
import org.acegisecurity.userdetails.UsernameNotFoundException;
|
||||
import org.easymock.MockControl;
|
||||
|
||||
public class AcegiDigestPasswordValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private AcegiDigestPasswordValidationCallbackHandler callbackHandler;
|
||||
|
||||
private MockControl control;
|
||||
|
||||
private UserDetailsService mock;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private PasswordValidationCallback callback;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
callbackHandler = new AcegiDigestPasswordValidationCallbackHandler();
|
||||
control = MockControl.createControl(UserDetailsService.class);
|
||||
mock = (UserDetailsService) control.getMock();
|
||||
callbackHandler.setUserDetailsService(mock);
|
||||
username = "Bert";
|
||||
password = "Ernie";
|
||||
String nonce = "9mdsYDCrjjYRur0rxzYt2oD7";
|
||||
String passwordDigest = "kwNstEaiFOrI7B31j7GuETYvdgk=";
|
||||
String creationTime = "2006-06-01T23:48:42Z";
|
||||
PasswordValidationCallback.DigestPasswordRequest request =
|
||||
new PasswordValidationCallback.DigestPasswordRequest(username, passwordDigest, nonce, creationTime);
|
||||
callback = new PasswordValidationCallback(request);
|
||||
}
|
||||
|
||||
public void testAuthenticateUserDigestUserNotFound() throws Exception {
|
||||
control.expectAndThrow(mock.loadUserByUsername(username), new UsernameNotFoundException(username));
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testAuthenticateUserDigestValid() throws Exception {
|
||||
User user = new User(username, password, true, true, true, true, new GrantedAuthority[0]);
|
||||
control.expectAndReturn(mock.loadUserByUsername(username), user);
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testAuthenticateUserDigestValidInvalid() throws Exception {
|
||||
User user = new User(username, "Big bird", true, true, true, true, new GrantedAuthority[0]);
|
||||
control.expectAndReturn(mock.loadUserByUsername(username), user);
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.acegi;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.AuthenticationManager;
|
||||
import org.acegisecurity.BadCredentialsException;
|
||||
import org.acegisecurity.GrantedAuthority;
|
||||
import org.acegisecurity.providers.TestingAuthenticationToken;
|
||||
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
|
||||
import org.easymock.MockControl;
|
||||
|
||||
public class AcegiPlainTextPasswordValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private AcegiPlainTextPasswordValidationCallbackHandler callbackHandler;
|
||||
|
||||
private MockControl control;
|
||||
|
||||
private AuthenticationManager mock;
|
||||
|
||||
private PasswordValidationCallback callback;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
callbackHandler = new AcegiPlainTextPasswordValidationCallbackHandler();
|
||||
control = MockControl.createControl(AuthenticationManager.class);
|
||||
mock = (AuthenticationManager) control.getMock();
|
||||
callbackHandler.setAuthenticationManager(mock);
|
||||
username = "Bert";
|
||||
password = "Ernie";
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest(username, password);
|
||||
callback = new PasswordValidationCallback(request);
|
||||
}
|
||||
|
||||
public void testAuthenticateUserPlainTextValid() throws Exception {
|
||||
Authentication authResult = new TestingAuthenticationToken(username, password, new GrantedAuthority[0]);
|
||||
control.expectAndReturn(mock.authenticate(new UsernamePasswordAuthenticationToken(username, password)),
|
||||
authResult);
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testAuthenticateUserPlainTextInvalid() throws Exception {
|
||||
control.expectAndThrow(mock.authenticate(new UsernamePasswordAuthenticationToken(username, password)),
|
||||
new BadCredentialsException(""));
|
||||
control.replay();
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
control.verify();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
public class CertificateLoginModule implements LoginModule {
|
||||
|
||||
private Subject subject;
|
||||
|
||||
private boolean loginSuccessful = false;
|
||||
|
||||
public boolean abort() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean commit() {
|
||||
if (!loginSuccessful) {
|
||||
subject.getPrincipals().clear();
|
||||
subject.getPrivateCredentials().clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
java.util.Map sharedState,
|
||||
java.util.Map options) {
|
||||
this.subject = subject;
|
||||
}
|
||||
|
||||
public boolean login() throws LoginException {
|
||||
if (subject == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = getName(subject);
|
||||
|
||||
loginSuccessful = "CN=Arjen Poutsma,OU=Spring-WS,O=Interface21,L=Amsterdam,ST=Unknown,C=NL".equals(name);
|
||||
return loginSuccessful;
|
||||
}
|
||||
|
||||
public boolean logout() {
|
||||
subject.getPrincipals().clear();
|
||||
subject.getPrivateCredentials().clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getName(Subject subject) {
|
||||
for (Iterator iterator = subject.getPrincipals().iterator(); iterator.hasNext();) {
|
||||
Principal principal = (Principal) iterator.next();
|
||||
if (principal instanceof X500Principal) {
|
||||
return ((X500Principal) principal).getName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.CertificateValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
public class JaasCertificateValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private JaasCertificateValidationCallbackHandler callbackHandler;
|
||||
|
||||
private CertificateValidationCallback callback;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
System.setProperty("java.security.auth.login.config", getClass().getResource("jaas.config").toString());
|
||||
callbackHandler = new JaasCertificateValidationCallbackHandler();
|
||||
callbackHandler.setLoginContextName("Certificate");
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new ClassPathResource("/org/springframework/ws/soap/security/xwss/test-keystore.jks").getInputStream();
|
||||
keyStore.load(is, "password".toCharArray());
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
X509Certificate certificate = (X509Certificate) keyStore.getCertificate("alias");
|
||||
callback = new CertificateValidationCallback(certificate);
|
||||
}
|
||||
|
||||
public void testValidateCertificateValid() throws Exception {
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import com.sun.xml.wss.impl.callback.PasswordValidationCallback;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class JaasPlainTextPasswordValidationCallbackHandlerTest extends TestCase {
|
||||
|
||||
private JaasPlainTextPasswordValidationCallbackHandler callbackHandler;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
System.setProperty("java.security.auth.login.config", getClass().getResource("jaas.config").toString());
|
||||
callbackHandler = new JaasPlainTextPasswordValidationCallbackHandler();
|
||||
callbackHandler.setLoginContextName("PlainText");
|
||||
}
|
||||
|
||||
public void testAuthenticateUserPlainTextValid() throws Exception {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest("Bert", "Ernie");
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertTrue("Not authenticated", authenticated);
|
||||
}
|
||||
|
||||
public void testAuthenticateUserPlainTextInvalid() throws Exception {
|
||||
PasswordValidationCallback.PlainTextPasswordRequest request =
|
||||
new PasswordValidationCallback.PlainTextPasswordRequest("Bert", "Big bird");
|
||||
PasswordValidationCallback callback = new PasswordValidationCallback(request);
|
||||
callbackHandler.handleInternal(callback);
|
||||
boolean authenticated = callback.getResult();
|
||||
assertFalse("Authenticated", authenticated);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
public class PlainTextLoginModule implements LoginModule {
|
||||
|
||||
private Subject subject;
|
||||
|
||||
private CallbackHandler callbackHandler;
|
||||
|
||||
private boolean success;
|
||||
|
||||
private List principals = new ArrayList();
|
||||
|
||||
public boolean abort() {
|
||||
success = false;
|
||||
logout();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean commit() throws LoginException {
|
||||
if (success) {
|
||||
if (subject.isReadOnly()) {
|
||||
throw new LoginException("Subject is read-only");
|
||||
}
|
||||
try {
|
||||
subject.getPrincipals().addAll(principals);
|
||||
principals.clear();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new LoginException(e.getMessage());
|
||||
}
|
||||
}
|
||||
else {
|
||||
principals.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void initialize(Subject subject,
|
||||
CallbackHandler callbackHandler,
|
||||
java.util.Map sharedState,
|
||||
java.util.Map options) {
|
||||
this.subject = subject;
|
||||
this.callbackHandler = callbackHandler;
|
||||
}
|
||||
|
||||
public boolean login() throws LoginException {
|
||||
if (callbackHandler == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
NameCallback nameCallback = new NameCallback("Username: ");
|
||||
PasswordCallback passwordCallback = new PasswordCallback("Password: ", false);
|
||||
Callback[] callbacks = new Callback[]{nameCallback, passwordCallback};
|
||||
|
||||
callbackHandler.handle(callbacks);
|
||||
|
||||
String username = nameCallback.getName();
|
||||
String password = new String(passwordCallback.getPassword());
|
||||
|
||||
((PasswordCallback) callbacks[1]).clearPassword();
|
||||
|
||||
success = validate(username, password);
|
||||
|
||||
callbacks[0] = null;
|
||||
callbacks[1] = null;
|
||||
|
||||
if (!success) {
|
||||
throw new LoginException("Authentication failed: Password does not match");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (LoginException ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
success = false;
|
||||
throw new LoginException(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validate(String username, String password) {
|
||||
if ("Bert".equals(username) && "Ernie".equals(password)) {
|
||||
this.principals.add(new SimplePrincipal(username));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean logout() {
|
||||
principals.clear();
|
||||
|
||||
Iterator iterator = subject.getPrincipals(SimplePrincipal.class).iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SimplePrincipal principal = (SimplePrincipal) iterator.next();
|
||||
subject.getPrincipals().remove(principal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2006 the original author or authors.
|
||||
*
|
||||
* Licensed 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.springframework.ws.soap.security.xwss.callback.jaas;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
public final class SimplePrincipal implements Principal {
|
||||
|
||||
private String name;
|
||||
|
||||
public SimplePrincipal() {
|
||||
name = "";
|
||||
}
|
||||
|
||||
public SimplePrincipal(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof SimplePrincipal)) {
|
||||
return false;
|
||||
}
|
||||
return name.equals(((SimplePrincipal) o).name);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user