blob: 656d6f867f56221eba184b52efd37f9173032805 [file] [log] [blame]
/*
* Copyright 2003-2005 The Apache Software Foundation.
*
* 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.apache.ws.security.handler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSConfig;
import org.apache.ws.security.WSSecurityEngine;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.action.Action;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.token.SignatureConfirmation;
import org.apache.ws.security.message.token.Timestamp;
import org.apache.ws.security.util.Loader;
import org.apache.ws.security.util.StringUtil;
import org.apache.ws.security.util.WSSecurityUtil;
import org.apache.ws.security.util.XmlSchemaDateFormat;
import org.w3c.dom.Document;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Vector;
/**
* Extracted from WSDoAllReceiver and WSDoAllSender
* Extended to all passwordless UsernameTokens and configurable identities.
*
* @author Davanum Srinivas (dims@yahoo.com).
* @author Werner Dittmann (Werner.Dittmann@t-online.de).
* @author Marcel Ammerlaan (marcel.ammerlaan@gmail.com).
*/
public abstract class WSHandler {
public static String DONE = "done";
private static Log log = LogFactory.getLog(WSHandler.class.getName());
protected static final WSSecurityEngine secEngine = WSSecurityEngine.getInstance();
protected static Hashtable cryptos = new Hashtable(5);
private boolean doDebug = log.isDebugEnabled();
/**
* Performs all defined security actions to set-up the SOAP request.
*
*
* @param doAction a set defining the actions to do
* @param doc the request as DOM document
* @param reqData a data storage to pass values around between methods
* @param actions a vector holding the actions to do in the order defined
* in the deployment file or property
* @throws WSSecurityException
*/
protected void doSenderAction(
int doAction,
Document doc,
RequestData reqData,
Vector actions,
boolean isRequest
) throws WSSecurityException {
boolean mu = decodeMustUnderstand(reqData);
WSSConfig wssConfig = reqData.getWssConfig();
if (wssConfig == null) {
wssConfig = WSSConfig.getNewInstance();
}
wssConfig
.setEnableSignatureConfirmation(decodeEnableSignatureConfirmation(reqData));
wssConfig
.setPrecisionInMilliSeconds(decodeTimestampPrecision(reqData));
reqData.setWssConfig(wssConfig);
Object mc = reqData.getMsgContext();
String actor = getString(WSHandlerConstants.ACTOR, mc);
reqData.setActor(actor);
WSSecHeader secHeader = new WSSecHeader(actor, mu);
secHeader.insertSecurityHeader(doc);
reqData.setSecHeader(secHeader);
reqData.setSoapConstants(WSSecurityUtil.getSOAPConstants(doc
.getDocumentElement()));
/*
* Here we have action, username, password, and actor, mustUnderstand.
* Now get the action specific parameters.
*/
if ((doAction & WSConstants.UT) == WSConstants.UT) {
decodeUTParameter(reqData);
}
/*
* Here we have action, username, password, and actor, mustUnderstand.
* Now get the action specific parameters.
*/
if ((doAction & WSConstants.UT_SIGN) == WSConstants.UT_SIGN) {
decodeUTParameter(reqData);
decodeSignatureParameter(reqData);
}
/*
* Get and check the Signature specific parameters first because they
* may be used for encryption too.
*/
if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
reqData.setSigCrypto(loadSignatureCrypto(reqData));
decodeSignatureParameter(reqData);
}
/*
* If we need to handle signed SAML token then we need may of the
* Signature parameters. The handle procedure loads the signature crypto
* file on demand, thus don't do it here.
*/
if ((doAction & WSConstants.ST_SIGNED) == WSConstants.ST_SIGNED) {
decodeSignatureParameter(reqData);
}
/*
* Set and check the encryption specific parameters, if necessary take
* over signature parameters username and crypto instance.
*/
if ((doAction & WSConstants.ENCR) == WSConstants.ENCR) {
reqData.setEncCrypto(loadEncryptionCrypto(reqData));
decodeEncryptionParameter(reqData);
}
/*
* If after all the parsing no Signature parts defined, set here a
* default set. This is necessary because we add SignatureConfirmation
* and therefore the default (Body) must be set here. The default setting
* in WSSignEnvelope doesn't work because the vector is not empty anymore.
*/
if (reqData.getSignatureParts().isEmpty()) {
WSEncryptionPart encP = new WSEncryptionPart(reqData.getSoapConstants()
.getBodyQName().getLocalPart(), reqData.getSoapConstants()
.getEnvelopeURI(), "Content");
reqData.getSignatureParts().add(encP);
}
/*
* If SignatureConfirmation is enabled and this is a response then
* insert SignatureConfrmation elements, note their wsu:id in the signature
* parts. They will be signed automatically during a (probably) defined
* SIGN action.
*/
if (wssConfig.isEnableSignatureConfirmation() && !isRequest) {
String done = (String)
getProperty(reqData.getMsgContext(), WSHandlerConstants.SIG_CONF_DONE);
if (!DONE.equals(done)
&& (getProperty(reqData.getMsgContext(), WSHandlerConstants.RECV_RESULTS))
!= null) {
wssConfig.getAction(WSConstants.SC).execute(this, WSConstants.SC, doc, reqData);
}
}
/*
* Here we have all necessary information to perform the requested
* action(s).
*/
for (int i = 0; i < actions.size(); i++) {
int actionToDo = ((Integer) actions.get(i)).intValue();
if (doDebug) {
log.debug("Performing Action: " + actionToDo);
}
switch (actionToDo) {
case WSConstants.UT:
case WSConstants.ENCR:
case WSConstants.SIGN:
case WSConstants.ST_SIGNED:
case WSConstants.ST_UNSIGNED:
case WSConstants.TS:
case WSConstants.UT_SIGN:
wssConfig.getAction(actionToDo).execute(this, actionToDo, doc, reqData);
break;
case WSConstants.NO_SERIALIZE:
reqData.setNoSerialization(true);
break;
//
// Handle any "custom" actions, similarly,
// but to preserve behavior from previous
// versions, consume (but log) action lookup failures.
//
default:
Action doit = null;
try {
doit = wssConfig.getAction(actionToDo);
} catch (final WSSecurityException e) {
log.warn(
"Error trying to locate a custom action (" + actionToDo + ")",
e
);
}
if (doit != null) {
doit.execute(this, actionToDo, doc, reqData);
}
}
}
/*
* If this is a request then store all signature values. Add ours to
* already gathered values because of chained handlers, e.g. for
* other actors.
*/
if (wssConfig.isEnableSignatureConfirmation()
&& isRequest
&& reqData.getSignatureValues().size() > 0) {
Vector sigv = (Vector)
getProperty(reqData.getMsgContext(), WSHandlerConstants.SEND_SIGV);
if (sigv == null) {
sigv = new Vector();
setProperty(reqData.getMsgContext(),
WSHandlerConstants.SEND_SIGV, sigv);
}
// sigv.add(reqData.getSignatureValues());
sigv.addAll(reqData.getSignatureValues());
}
}
protected void doReceiverAction(int doAction, RequestData reqData)
throws WSSecurityException {
WSSConfig wssConfig = WSSConfig.getNewInstance();
wssConfig
.setEnableSignatureConfirmation(decodeEnableSignatureConfirmation(reqData));
wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
reqData.setWssConfig(wssConfig);
if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
decodeSignatureParameter2(reqData);
}
if ((doAction & WSConstants.ENCR) == WSConstants.ENCR) {
decodeDecryptionParameter(reqData);
}
if ((doAction & WSConstants.NO_SERIALIZE) == WSConstants.NO_SERIALIZE) {
reqData.setNoSerialization(true);
}
}
protected boolean checkReceiverResults(Vector wsResult, Vector actions) {
int resultActions = wsResult.size();
int size = actions.size();
int ai = 0;
for (int i = 0; i < resultActions; i++) {
final Integer actInt = (Integer) ((WSSecurityEngineResult) wsResult
.get(i)).get(WSSecurityEngineResult.TAG_ACTION);
int act = actInt.intValue();
if (act == WSConstants.SC || act == WSConstants.BST) {
continue;
}
if (ai >= size || ((Integer) actions.get(ai++)).intValue() != act) {
return false;
}
}
if (ai != size) {
return false;
}
return true;
}
protected void checkSignatureConfirmation(RequestData reqData,
Vector wsResult) throws WSSecurityException{
if (doDebug) {
log.debug("Check Signature confirmation");
}
/*
* First get all Signature value stored during sending the request
*/
Vector sigv = (Vector) getProperty(reqData.getMsgContext(),
WSHandlerConstants.SEND_SIGV);
/*
* Now get all results that hold a SignatureConfirmation element from
* the current run of receiver (we can have more than one run: if we
* have several security header blocks with different actors/roles)
*/
Vector sigConf = new Vector();
WSSecurityUtil.fetchAllActionResults(wsResult, WSConstants.SC, sigConf);
/*
* now loop over all SignatureConfirmation results and check:
* - if there is a signature value and no Signature value generated in request: error
* - if there is a signature value and no matching Signature value found: error
*
* If a matching value found: remove from vector of stored signature values
*/
for (int i = 0; i < sigConf.size(); i++) {
WSSecurityEngineResult result =
(WSSecurityEngineResult)sigConf.get(i);
SignatureConfirmation sc =
(SignatureConfirmation)result.get(WSSecurityEngineResult.TAG_SIGNATURE_CONFIRMATION);
byte[] sigVal = sc.getSignatureValue();
if (sigVal != null) {
if (sigv == null || sigv.size() == 0) {
//If there are no store signature values
if(sigVal.length != 0) {
//If there's no value in the case where there are no
//stored SV it is valid. Therefore if there IS a value
//in the sig confirmation element
throw new WSSecurityException("WSHandler: Check Signature confirmation: got a SC element, but no stored SV");
}
} else {
//If we have stored signature values
boolean found = false;
for (int ii = 0; ii < sigv.size(); ii++) {
byte[] storedValue = (byte[]) sigv.get(i);
if (Arrays.equals(sigVal, storedValue)) {
found = true;
sigv.remove(ii);
break;
}
}
if (!found) {
throw new WSSecurityException(
"WSHandler: Check Signature confirmation: got SC element, but no matching SV");
}
}
}
}
/*
* This indicates this is the last handler: the vector holding the
* stored Signature values must be empty, otherwise we have an error
*/
if (!reqData.isNoSerialization()) {
log.debug("Check Signature confirmation - last handler");
if (sigv != null && !sigv.isEmpty()) {
throw new WSSecurityException("WSHandler: Check Signature confirmation: stored SV vector not empty");
}
}
}
/**
* Hook to allow subclasses to load their Signature Crypto however they see
* fit.
*/
public Crypto loadSignatureCrypto(RequestData reqData)
throws WSSecurityException {
Crypto crypto = null;
/*
* Get crypto property file for signature. If none specified throw
* fault, otherwise get a crypto instance.
*/
String sigPropFile = getString(WSHandlerConstants.SIG_PROP_FILE,
reqData.getMsgContext());
if (sigPropFile != null) {
crypto = (Crypto) cryptos.get(sigPropFile);
if (crypto == null) {
crypto = CryptoFactory.getInstance(sigPropFile, this
.getClassLoader(reqData.getMsgContext()));
cryptos.put(sigPropFile, crypto);
}
} else if (getString(WSHandlerConstants.SIG_PROP_REF_ID, reqData
.getMsgContext()) != null) {
/*
* If the property file is missing then
* look for the Properties object
*/
String refId = getString(WSHandlerConstants.SIG_PROP_REF_ID,
reqData.getMsgContext());
if (refId != null) {
Object propObj = getProperty(reqData.getMsgContext(), refId);
if(propObj instanceof Properties) {
crypto = (Crypto) cryptos.get(refId);
if (crypto == null) {
crypto = CryptoFactory.getInstance((Properties)propObj);
cryptos.put(refId, crypto);
}
} else {
throw new WSSecurityException(
"WSHandler: Signature: signaturePropRefId must hold a "
+ "java.util.Properties object"
);
}
}
} else {
throw new WSSecurityException(
"WSHandler: Signature: no crypto properties"
);
}
return crypto;
}
/**
* Hook to allow subclasses to load their Encryption Crypto however they
* see fit.
*/
protected Crypto loadEncryptionCrypto(RequestData reqData)
throws WSSecurityException {
Crypto crypto = null;
/*
* Get encryption crypto property file. If non specified take crypto
* instance from signature, if that fails: throw fault
*/
String encPropFile = getString(WSHandlerConstants.ENC_PROP_FILE,
reqData.getMsgContext());
if (encPropFile != null) {
crypto = (Crypto) cryptos.get(encPropFile);
if (crypto == null) {
crypto = CryptoFactory.getInstance(encPropFile, this
.getClassLoader(reqData.getMsgContext()));
cryptos.put(encPropFile, crypto);
}
} else if (getString(WSHandlerConstants.ENC_PROP_REF_ID, reqData
.getMsgContext()) != null) {
/*
* If the property file is missing then
* look for the Properties object
*/
String refId = getString(WSHandlerConstants.ENC_PROP_REF_ID,
reqData.getMsgContext());
if(refId != null) {
Object propObj = getProperty(reqData.getMsgContext(), refId);
if(propObj instanceof Properties) {
crypto = (Crypto) cryptos.get(refId);
if (crypto == null) {
crypto = CryptoFactory.getInstance((Properties)propObj);
cryptos.put(refId, crypto);
}
} else {
throw new WSSecurityException(
"WSHandler: Encryption: encryptionPropRefId must hold a"
+ " java.util.Properties object"
);
}
}
} else if ((crypto = reqData.getSigCrypto()) == null) {
throw new WSSecurityException(
"WSHandler: Encryption: no crypto property file"
);
}
return crypto;
}
protected void decodeUTParameter(RequestData reqData)
throws WSSecurityException {
Object mc = reqData.getMsgContext();
String type = getString(WSHandlerConstants.PASSWORD_TYPE, mc);
if (type != null) {
if(WSConstants.PW_TEXT.equals(type)) {
reqData.setPwType(WSConstants.PASSWORD_TEXT);
} else if(WSConstants.PW_DIGEST.equals(type)) {
reqData.setPwType(WSConstants.PASSWORD_DIGEST);
} else if(WSConstants.PW_NONE.equals(type)) {
// No password requested.
reqData.setPwType(null);
} else {
throw new WSSecurityException("Unknown password type encoding: " + type);
}
}
String add = getString(WSHandlerConstants.ADD_UT_ELEMENTS, mc);
if (add != null) {
reqData.setUtElements(StringUtil.split(add, ' '));
}
}
protected void decodeSignatureParameter(RequestData reqData)
throws WSSecurityException {
Object mc = reqData.getMsgContext();
String keyId = getString(WSHandlerConstants.SIG_KEY_ID, mc);
if (keyId != null) {
Integer id = (Integer) WSHandlerConstants.keyIdentifier.get(keyId);
if (id == null) {
throw new WSSecurityException(
"WSHandler: Signature: unknown key identification"
);
}
int tmp = id.intValue();
if (!(tmp == WSConstants.ISSUER_SERIAL
|| tmp == WSConstants.BST_DIRECT_REFERENCE
|| tmp == WSConstants.X509_KEY_IDENTIFIER
|| tmp == WSConstants.SKI_KEY_IDENTIFIER
|| tmp == WSConstants.THUMBPRINT_IDENTIFIER)) {
throw new WSSecurityException(
"WSHandler: Signature: illegal key identification"
);
}
reqData.setSigKeyId(tmp);
}
String algo = getString(WSHandlerConstants.SIG_ALGO, mc);
reqData.setSigAlgorithm(algo);
String parts = getString(WSHandlerConstants.SIGNATURE_PARTS, mc);
if (parts != null) {
splitEncParts(parts, reqData.getSignatureParts(), reqData);
}
}
protected void decodeEncryptionParameter(RequestData reqData)
throws WSSecurityException {
Object mc = reqData.getMsgContext();
String encUser = getString(WSHandlerConstants.ENCRYPTION_USER, mc);
if (encUser != null) {
reqData.setEncUser(encUser);
} else {
reqData.setEncUser(reqData.getUsername());
}
if (reqData.getEncUser() == null) {
throw new WSSecurityException("WSHandler: Encryption: no username");
}
/*
* String msgType = msgContext.getCurrentMessage().getMessageType(); if
* (msgType != null && msgType.equals(Message.RESPONSE)) {
* handleSpecialUser(encUser); }
*/
handleSpecialUser(reqData);
/*
* If the following parameters are no used (they return null) then the
* default values of WSS4J are used.
*/
String encKeyId = getString(WSHandlerConstants.ENC_KEY_ID, mc);
if (encKeyId != null) {
Integer id = (Integer) WSHandlerConstants.keyIdentifier.get(encKeyId);
if (id == null) {
throw new WSSecurityException(
"WSHandler: Encryption: unknown key identification"
);
}
int tmp = id.intValue();
reqData.setEncKeyId(tmp);
if (!(tmp == WSConstants.ISSUER_SERIAL
|| tmp == WSConstants.X509_KEY_IDENTIFIER
|| tmp == WSConstants.SKI_KEY_IDENTIFIER
|| tmp == WSConstants.BST_DIRECT_REFERENCE
|| tmp == WSConstants.EMBEDDED_KEYNAME
|| tmp == WSConstants.THUMBPRINT_IDENTIFIER)) {
throw new WSSecurityException(
"WSHandler: Encryption: illegal key identification"
);
}
}
String encSymAlgo = getString(WSHandlerConstants.ENC_SYM_ALGO, mc);
reqData.setEncSymmAlgo(encSymAlgo);
String encKeyTransport =
getString(WSHandlerConstants.ENC_KEY_TRANSPORT, mc);
reqData.setEncKeyTransport(encKeyTransport);
String encParts = getString(WSHandlerConstants.ENCRYPTION_PARTS, mc);
if (encParts != null) {
splitEncParts(encParts, reqData.getEncryptParts(), reqData);
}
}
protected boolean decodeMustUnderstand(RequestData reqData)
throws WSSecurityException {
String mu =
getString(WSHandlerConstants.MUST_UNDERSTAND, reqData.getMsgContext());
if (mu == null) {return true;}
if ("0".equals(mu) || "false".equals(mu)) {return false;}
if ("1".equals(mu) || "true".equals(mu)) {return true;}
throw new WSSecurityException(
"WSHandler: illegal mustUnderstand parameter"
);
}
public int decodeTimeToLive(RequestData reqData) {
String ttl =
getString(WSHandlerConstants.TTL_TIMESTAMP, reqData.getMsgContext());
int ttl_i = 0;
if (ttl != null) {
try {
ttl_i = Integer.parseInt(ttl);
} catch (NumberFormatException e) {
ttl_i = reqData.getTimeToLive();
}
}
if (ttl_i <= 0) {
ttl_i = reqData.getTimeToLive();
}
return ttl_i;
}
protected boolean decodeEnableSignatureConfirmation(RequestData reqData) throws WSSecurityException {
String value = getString(WSHandlerConstants.ENABLE_SIGNATURE_CONFIRMATION,
reqData.getMsgContext());
if (value == null) {return true;}
if ("0".equals(value) || "false".equals(value)) {return false;}
if ("1".equals(value) || "true".equals(value)) {return true;}
throw new WSSecurityException(
"WSHandler: illegal enableSignatureConfirmation parameter"
);
}
protected boolean decodeTimestampPrecision(RequestData reqData)
throws WSSecurityException {
String value = getString(WSHandlerConstants.TIMESTAMP_PRECISION,
reqData.getMsgContext());
if (value == null) {return true;}
if ("0".equals(value) || "false".equals(value)) {return false;}
if ("1".equals(value) || "true".equals(value)) {return true;}
throw new WSSecurityException(
"WSHandler: illegal precisionInMilliSeconds parameter"
);
}
protected boolean decodeCustomPasswordTypes(RequestData reqData)
throws WSSecurityException {
String value = getString(
WSHandlerConstants.HANDLE_CUSTOM_PASSWORD_TYPES,
reqData.getMsgContext()
);
if (value == null) {return false;}
if ("0".equals(value) || "false".equals(value)) {return false;}
if ("1".equals(value) || "true".equals(value)) {return true;}
throw new WSSecurityException(
"WSHandler: illegal handleCustomPasswordTypes parameter"
);
}
protected boolean decodeTimestampStrict(RequestData reqData)
throws WSSecurityException {
String value = getString(WSHandlerConstants.TIMESTAMP_STRICT,
reqData.getMsgContext());
if (value == null) {return true;}
if ("0".equals(value) || "false".equals(value)) {return false;}
if ("1".equals(value) || "true".equals(value)) {return true;}
throw new WSSecurityException(
"WSHandler: illegal timestampStrict parameter"
);
}
/**
* Get a password to construct a UsernameToken or sign a message.
* <p/>
* Try all possible sources to get a password.
*/
public WSPasswordCallback getPassword(String username,
int doAction,
String clsProp,
String refProp,
RequestData reqData
) throws WSSecurityException {
WSPasswordCallback pwCb = null;
CallbackHandler cbHandler = null;
String err = "provided null or empty password";
Object mc = reqData.getMsgContext();
String callback = getString(clsProp, mc);
if (callback != null) { // we have a password callback class
pwCb = readPwViaCallbackClass(callback, username, doAction, reqData);
// Null passwords are not always a problem: if the callback was called to provide a username instead.
} else if ((cbHandler = (CallbackHandler) getProperty(mc, refProp)) != null) {
pwCb = performCallback(cbHandler, username, doAction);
} else {
//
// If a callback isn't configured then try to get the password
// from the message context
//
String password = getPassword(mc);
if (password == null) {
throw new WSSecurityException("WSHandler: application " + err);
}
pwCb = constructPasswordCallback(username, doAction);
pwCb.setPassword(password);
}
return pwCb;
}
private WSPasswordCallback readPwViaCallbackClass(String callback,
String username,
int doAction,
RequestData requestData
) throws WSSecurityException {
Class cbClass = null;
CallbackHandler cbHandler = null;
try {
cbClass = Loader.loadClass(getClassLoader(requestData
.getMsgContext()), callback);
} catch (ClassNotFoundException e) {
throw new WSSecurityException("WSHandler: cannot load password callback class: "
+ callback,
e);
}
try {
cbHandler = (CallbackHandler) cbClass.newInstance();
} catch (Exception e) {
throw new WSSecurityException("WSHandler: cannot create instance of password callback: "
+ callback,
e);
}
return (performCallback(cbHandler, username, doAction));
}
/**
* Perform a callback to get a password.
* <p/>
* The called back function gets an indication why to provide a password:
* to produce a UsernameToken, Signature, or a password (key) for a given
* name.
*/
private WSPasswordCallback performCallback(CallbackHandler cbHandler,
String username,
int doAction
) throws WSSecurityException {
WSPasswordCallback pwCb = constructPasswordCallback(username, doAction);
Callback[] callbacks = new Callback[1];
callbacks[0] = pwCb;
/*
* Call back the application to get the password
*/
try {
cbHandler.handle(callbacks);
} catch (Exception e) {
throw new WSSecurityException("WSHandler: password callback failed", e);
}
return pwCb;
}
private WSPasswordCallback constructPasswordCallback(
String username,
int doAction
) throws WSSecurityException {
int reason = WSPasswordCallback.UNKNOWN;
switch (doAction) {
case WSConstants.UT:
case WSConstants.UT_SIGN:
reason = WSPasswordCallback.USERNAME_TOKEN;
break;
case WSConstants.SIGN:
reason = WSPasswordCallback.SIGNATURE;
break;
case WSConstants.ENCR:
reason = WSPasswordCallback.KEY_NAME;
break;
}
return new WSPasswordCallback(username, reason);
}
private void splitEncParts(String tmpS, Vector parts, RequestData reqData)
throws WSSecurityException {
WSEncryptionPart encPart = null;
String[] rawParts = StringUtil.split(tmpS, ';');
for (int i = 0; i < rawParts.length; i++) {
String[] partDef = StringUtil.split(rawParts[i], '}');
if (partDef.length == 1) {
if (doDebug) {
log.debug("single partDef: '" + partDef[0] + "'");
}
encPart =
new WSEncryptionPart(partDef[0].trim(),
reqData.getSoapConstants().getEnvelopeURI(),
"Content");
} else if (partDef.length == 3) {
String mode = partDef[0].trim();
if (mode.length() <= 1) {
mode = "Content";
} else {
mode = mode.substring(1);
}
String nmSpace = partDef[1].trim();
if (nmSpace.length() <= 1) {
nmSpace = reqData.getSoapConstants().getEnvelopeURI();
} else {
nmSpace = nmSpace.substring(1);
if (nmSpace.equals(WSConstants.NULL_NS)) {
nmSpace = null;
}
}
String element = partDef[2].trim();
if (doDebug) {
log.debug("partDefs: '"
+ mode
+ "' ,'"
+ nmSpace
+ "' ,'"
+ element
+ "'");
}
encPart = new WSEncryptionPart(element, nmSpace, mode);
} else {
throw new WSSecurityException("WSHandler: wrong part definition: " + tmpS);
}
parts.add(encPart);
}
}
private void handleSpecialUser(RequestData reqData) {
if (!WSHandlerConstants.USE_REQ_SIG_CERT.equals(reqData.getEncUser())) {
return;
}
Vector results =
(Vector) getProperty(reqData.getMsgContext(), WSHandlerConstants.RECV_RESULTS);
if (results == null) {
return;
}
/*
* Scan the results for a matching actor. Use results only if the
* receiving Actor and the sending Actor match.
*/
for (int i = 0; i < results.size(); i++) {
WSHandlerResult rResult =
(WSHandlerResult) results.get(i);
String hActor = rResult.getActor();
if (!WSSecurityUtil.isActorEqual(reqData.getActor(), hActor)) {
continue;
}
Vector wsSecEngineResults = rResult.getResults();
/*
* Scan the results for the first Signature action. Use the
* certificate of this Signature to set the certificate for the
* encryption action :-).
*/
for (int j = 0; j < wsSecEngineResults.size(); j++) {
WSSecurityEngineResult wser =
(WSSecurityEngineResult) wsSecEngineResults.get(j);
int wserAction =
((java.lang.Integer)wser.get(WSSecurityEngineResult.TAG_ACTION)).intValue();
if (wserAction == WSConstants.SIGN) {
X509Certificate cert =
(X509Certificate)wser.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
reqData.setEncCert(cert);
return;
}
}
}
}
/**
* Hook to allow subclasses to load their Decryption Crypto however they
* see fit.
*/
protected Crypto loadDecryptionCrypto(RequestData reqData)
throws WSSecurityException {
Crypto crypto = null;
String decPropFile = getString(WSHandlerConstants.DEC_PROP_FILE,
reqData.getMsgContext());
if (decPropFile != null) {
crypto = (Crypto) cryptos.get(decPropFile);
if (crypto == null) {
crypto = CryptoFactory.getInstance(decPropFile, this
.getClassLoader(reqData.getMsgContext()));
cryptos.put(decPropFile, crypto);
}
} else if (getString(WSHandlerConstants.DEC_PROP_REF_ID, reqData
.getMsgContext()) != null) {
/*
* If the property file is missing then
* look for the Properties object
*/
String refId = getString(WSHandlerConstants.DEC_PROP_REF_ID,
reqData.getMsgContext());
if(refId != null) {
Object propObj = getProperty(reqData.getMsgContext(), refId);
if(propObj instanceof Properties) {
crypto = (Crypto) cryptos.get(refId);
if (crypto == null) {
crypto = CryptoFactory.getInstance((Properties)propObj);
cryptos.put(refId, crypto);
}
} else {
throw new WSSecurityException(
"WSHandler: Decrytion: decryptionPropRefId must hold a"
+ " java.util.Properties object"
);
}
}
} else if ((crypto = reqData.getSigCrypto()) == null) {
throw new WSSecurityException(
"WSHandler: Encryption: no crypto property file"
);
}
return crypto;
}
protected void decodeSignatureParameter2(RequestData reqData)
throws WSSecurityException {
reqData.setSigCrypto(loadSignatureCrypto(reqData));
/* There are currently no other signature parameters that need
* to be handled here, but we call the load crypto hook rather
* than just changing the visibility
* of this method to maintain parity with WSDoAllSender.
*/
}
/*
* Set and check the decryption specific parameters, if necessary
* take over signature crypto instance.
*/
protected void decodeDecryptionParameter(RequestData reqData)
throws WSSecurityException {
reqData.setDecCrypto(loadDecryptionCrypto(reqData));
/* There are currently no other decryption parameters that need
* to be handled here, but we call the load crypto hook rather
* than just changing the visibility
* of this method to maintain parity with WSDoAllSender.
*/
}
/**
* Get the password callback class and get an instance
* <p/>
*/
protected CallbackHandler getPasswordCB(RequestData reqData)
throws WSSecurityException {
Object mc = reqData.getMsgContext();
CallbackHandler cbHandler = null;
String callback = getString(WSHandlerConstants.PW_CALLBACK_CLASS, mc);
if (callback != null) {
Class cbClass = null;
try {
cbClass = Loader.loadClass(getClassLoader(reqData
.getMsgContext()), callback);
} catch (ClassNotFoundException e) {
throw new WSSecurityException(
"WSHandler: cannot load password callback class: "
+ callback, e);
}
try {
cbHandler = (CallbackHandler) cbClass.newInstance();
} catch (java.lang.Exception e) {
throw new WSSecurityException(
"WSHandler: cannot create instance of password callback: "
+ callback, e);
}
} else {
cbHandler = (CallbackHandler) getProperty(mc,
WSHandlerConstants.PW_CALLBACK_REF);
if (cbHandler == null) {
throw new WSSecurityException(
"WSHandler: no reference in callback property");
}
}
return cbHandler;
}
/**
* Evaluate whether a given certificate should be trusted.
* Hook to allow subclasses to implement custom validation methods however they see fit.
* <p/>
* Policy used in this implementation:
* 1. Search the keystore for the transmitted certificate
* 2. Search the keystore for a connection to the transmitted certificate
* (that is, search for certificate(s) of the issuer of the transmitted certificate
* 3. Verify the trust path for those certificates found because the search for the issuer might be fooled by a phony DN (String!)
*
* @param cert the certificate that should be validated against the keystore
* @return true if the certificate is trusted, false if not (AxisFault is thrown for exceptions during CertPathValidation)
* @throws WSSecurityException
*/
protected boolean verifyTrust(X509Certificate cert, RequestData reqData)
throws WSSecurityException {
// If no certificate was transmitted, do not trust the signature
if (cert == null) {
return false;
}
String[] aliases = null;
String alias = null;
X509Certificate[] certs;
String subjectString = cert.getSubjectDN().getName();
String issuerString = cert.getIssuerDN().getName();
BigInteger issuerSerial = cert.getSerialNumber();
if (doDebug) {
log.debug("WSHandler: Transmitted certificate has subject " + subjectString);
log.debug("WSHandler: Transmitted certificate has issuer " + issuerString + " (serial " + issuerSerial + ")");
}
// FIRST step
// Search the keystore for the transmitted certificate
// Search the keystore for the alias of the transmitted certificate
try {
alias = reqData.getSigCrypto().getAliasForX509Cert(issuerString, issuerSerial);
} catch (WSSecurityException ex) {
throw new WSSecurityException("WSHandler: Could not get alias for certificate with " + subjectString, ex);
}
if (alias != null) {
// Retrieve the certificate for the alias from the keystore
try {
certs = reqData.getSigCrypto().getCertificates(alias);
} catch (WSSecurityException ex) {
throw new WSSecurityException("WSHandler: Could not get certificates for alias " + alias, ex);
}
// If certificates have been found, the certificates must be compared
// to ensure against phony DNs (compare encoded form including signature)
if (certs != null && certs.length > 0 && cert.equals(certs[0])) {
if (doDebug) {
log.debug("Direct trust for certificate with " + subjectString);
}
return true;
}
} else {
if (doDebug) {
log.debug("No alias found for subject from issuer with " + issuerString + " (serial " + issuerSerial + ")");
}
}
// SECOND step
// Search for the issuer of the transmitted certificate in the keystore
// Search the keystore for the alias of the transmitted certificates issuer
try {
aliases = reqData.getSigCrypto().getAliasesForDN(issuerString);
} catch (WSSecurityException ex) {
throw new WSSecurityException("WSHandler: Could not get alias for certificate with " + issuerString, ex);
}
// If the alias has not been found, the issuer is not in the keystore
// As a direct result, do not trust the transmitted certificate
if (aliases == null || aliases.length < 1) {
if (doDebug) {
log.debug("No aliases found in keystore for issuer " + issuerString + " of certificate for " + subjectString);
}
return false;
}
// THIRD step
// Check the certificate trust path for every alias of the issuer found in the keystore
for (int i = 0; i < aliases.length; i++) {
alias = aliases[i];
if (doDebug) {
log.debug("Preparing to validate certificate path with alias " + alias + " for issuer " + issuerString);
}
// Retrieve the certificate(s) for the alias from the keystore
try {
certs = reqData.getSigCrypto().getCertificates(alias);
} catch (WSSecurityException ex) {
throw new WSSecurityException("WSHandler: Could not get certificates for alias " + alias, ex);
}
// If no certificates have been found, there has to be an error:
// The keystore can find an alias but no certificate(s)
if (certs == null || certs.length < 1) {
throw new WSSecurityException("WSHandler: Could not get certificates for alias " + alias);
}
// Form a certificate chain from the transmitted certificate
// and the certificate(s) of the issuer from the keystore
// First, create new array
X509Certificate[] x509certs = new X509Certificate[certs.length + 1];
// Then add the first certificate ...
x509certs[0] = cert;
// ... and the other certificates
for (int j = 0; j < certs.length; j++) {
x509certs[j + 1] = certs[j];
}
certs = x509certs;
// Use the validation method from the crypto to check whether the subjects certificate was really signed by the issuer stated in the certificate
try {
if (reqData.getSigCrypto().validateCertPath(certs)) {
if (doDebug) {
log.debug("WSHandler: Certificate path has been verified for certificate with subject " + subjectString);
}
return true;
}
} catch (WSSecurityException ex) {
throw new WSSecurityException("WSHandler: Certificate path verification failed for certificate with subject " + subjectString, ex);
}
}
log.debug("WSHandler: Certificate path could not be verified for certificate with subject " + subjectString);
return false;
}
/**
* Evaluate whether a timestamp is considered valid on receiverside. Hook to
* allow subclasses to implement custom validation methods however they see
* fit.
*
* Policy used in this implementation:
*
* 1. The receiver can set its own time to live (besides from that set on
* sender side)
*
* 2. If the message was created before (now-ttl) the message is rejected
*
* @param timestamp
* the timestamp that is validated
* @param timeToLive
* the limit on receiverside, the timestamp is validated against
* @return true if the timestamp is before (now-timeToLive), false otherwise
* @throws WSSecurityException
*/
protected boolean verifyTimestamp(Timestamp timestamp, int timeToLive) throws WSSecurityException {
// Calculate the time that is allowed for the message to travel
Calendar validCreation = Calendar.getInstance();
long currentTime = validCreation.getTime().getTime();
currentTime -= timeToLive * 1000;
validCreation.setTime(new Date(currentTime));
if (doDebug) {
log.debug("Preparing to verify the timestamp");
DateFormat zulu = new XmlSchemaDateFormat();
log.debug("Validation of Timestamp: Current time is "
+ zulu.format(Calendar.getInstance().getTime()));
log.debug("Validation of Timestamp: Valid creation is "
+ zulu.format(validCreation.getTime()));
if (timestamp.getCreated() != null) {
log.debug("Validation of Timestamp: Timestamp created is "
+ zulu.format(timestamp.getCreated().getTime()));
}
}
// Validate the time it took the message to travel
// if (timestamp.getCreated().before(validCreation) ||
// !timestamp.getCreated().equals(validCreation)) {
Calendar cre = timestamp.getCreated();
if (cre != null && !cre.after(validCreation)) {
if (doDebug) {
log.debug("Validation of Timestamp: The message was created too long ago");
}
return false;
}
if (doDebug) {
log.debug("Validation of Timestamp: Everything is ok");
}
return true;
}
/**
* Looks up key first via {@link #getOption(String)} and if not found
* there, via {@link #getProperty(Object, String)}
*
* @param key the key to search for. May not be null.
* @param mc the message context to search.
* @return the value found.
* @throws IllegalArgumentException if <code>key</code> is null.
*/
public String getString(String key, Object mc) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null");
}
String s = getStringOption(key);
if (s != null) {
return s;
}
if (mc == null) {
throw new IllegalArgumentException("Message context cannot be null");
}
return (String) getProperty(mc, key);
}
/**
* Returns the option on <code>name</code>.
*
* @param key the non-null key of the option.
* @return the option on <code>key</code> if <code>key</code>
* exists and is of type java.lang.String; otherwise null.
*/
public String getStringOption(String key) {
Object o = getOption(key);
if (o instanceof String){
return (String) o;
} else {
return null;
}
}
// /**
// * Returns the classloader to be used for loading the callback class
// *
// * @return class loader
// */
// public ClassLoader getClassLoader() {
// try {
// return Loader.getTCL();
// } catch (Throwable t) {
// return null;
// }
// }
/**
* Returns the classloader to be used for loading the callback class
* @param msgCtx The MessageContext
* @return class loader
*/
public ClassLoader getClassLoader(Object msgCtx) {
try {
return Loader.getTCL();
} catch (Throwable t) {
return null;
}
}
public abstract Object getOption(String key);
public abstract Object getProperty(Object msgContext, String key);
public abstract void setProperty(Object msgContext, String key,
Object value);
public abstract String getPassword(Object msgContext);
public abstract void setPassword(Object msgContext, String password);
}