org.apache.ws.security.handler.WSHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wss4j Show documentation
Show all versions of wss4j Show documentation
The Apache WSS4J project provides a Java implementation of the primary security standards
for Web Services, namely the OASIS Web Services Security (WS-Security) specifications
from the OASIS Web Services Security TC.
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.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.TimeZone;
import java.util.Vector;
/**
* Extracted from WSDoAllReceiver and WSDoAllSender
* Extended to all passwordless UsernameTokens and configurable identities.
*
* @author Davanum Srinivas ([email protected]).
* @author Werner Dittmann ([email protected]).
* @author Marcel Ammerlaan ([email protected]).
*/
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 = secEngine.getWssConfig();
}
boolean enableSigConf = decodeEnableSignatureConfirmation(reqData);
wssConfig.setEnableSignatureConfirmation(
enableSigConf || ((doAction & WSConstants.SC) != 0)
);
wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(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 may need 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 = reqData.getWssConfig();
if (wssConfig == null) {
wssConfig = secEngine.getWssConfig();
}
boolean enableSigConf = decodeEnableSignatureConfirmation(reqData);
wssConfig.setEnableSignatureConfirmation(
enableSigConf || ((doAction & WSConstants.SC) != 0)
);
wssConfig.setTimeStampStrict(decodeTimestampStrict(reqData));
wssConfig.setHandleCustomPasswordTypes(decodeCustomPasswordTypes(reqData));
wssConfig.setPasswordsAreEncoded(decodeUseEncodedPasswords(reqData));
wssConfig.setAllowNamespaceQualifiedPasswordTypes(
decodeNamespaceQualifiedPasswordTypes(reqData)
);
wssConfig.setSecretKeyLength(reqData.getSecretKeyLength());
reqData.setWssConfig(wssConfig);
if ((doAction & WSConstants.SIGN) == WSConstants.SIGN) {
decodeSignatureParameter2(reqData);
}
if ((doAction & WSConstants.ST_SIGNED) == WSConstants.ST_SIGNED) {
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 boolean checkReceiverResultsAnyOrder(Vector wsResult, Vector actions) {
java.util.List recordedActions = new Vector(actions.size());
for (int i = 0; i < actions.size(); i++) {
Integer action = (Integer)actions.get(i);
recordedActions.add(action);
}
for (int i = 0; i < wsResult.size(); 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 (!recordedActions.remove(actInt)) {
return false;
}
}
if (!recordedActions.isEmpty()) {
return false;
}
return true;
}
protected void checkSignatureConfirmation(RequestData reqData,
Vector wsResult) throws WSSecurityException{
if (doDebug) {
log.debug("Check Signature confirmation");
}
/*
* First get all Signature values 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 stored 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(ii);
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);
}
}
}
}
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 if (reqData.getSigCrypto() != null) {
//
// Default to the signature crypto
//
crypto = reqData.getSigCrypto();
}
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, ' '));
}
String derived = getString(WSHandlerConstants.USE_DERIVED_KEY, mc);
boolean useDerivedKey = Boolean.valueOf(derived).booleanValue();
if (useDerivedKey) {
reqData.setUseDerivedKey(useDerivedKey);
}
String derivedMAC = getString(WSHandlerConstants.USE_DERIVED_KEY, mc);
boolean useDerivedKeyForMAC = Boolean.valueOf(derivedMAC).booleanValue();
if (useDerivedKeyForMAC) {
reqData.setUseDerivedKeyForMAC(useDerivedKeyForMAC);
}
String iterations = getString(WSHandlerConstants.DERIVED_KEY_ITERATIONS, mc);
if (iterations != null) {
int iIterations = Integer.parseInt(iterations);
reqData.setDerivedKeyIterations(iIterations);
}
}
protected void decodeSignatureParameter(RequestData reqData)
throws WSSecurityException {
Object mc = reqData.getMsgContext();
String signatureUser = getString(WSHandlerConstants.SIGNATURE_USER, mc);
if (signatureUser != null) {
reqData.setSignatureUser(signatureUser);
} else {
reqData.setSignatureUser(reqData.getUsername());
}
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
|| tmp == WSConstants.ENCRYPTED_KEY_SHA1_IDENTIFIER)) {
throw new WSSecurityException(
"WSHandler: Signature: illegal key identification"
);
}
reqData.setSigKeyId(tmp);
}
String algo = getString(WSHandlerConstants.SIG_ALGO, mc);
reqData.setSigAlgorithm(algo);
String digestAlgo = getString(WSHandlerConstants.SIG_DIGEST_ALGO, mc);
reqData.setSigDigestAlgorithm(digestAlgo);
String parts = getString(WSHandlerConstants.SIGNATURE_PARTS, mc);
if (parts != null) {
splitEncParts(parts, reqData.getSignatureParts(), reqData);
}
String secretKeyLength = getString(WSHandlerConstants.WSE_SECRET_KEY_LENGTH, mc);
if (secretKeyLength != null) {
int iSecretKeyLength = Integer.parseInt(secretKeyLength);
reqData.setSecretKeyLength(iSecretKeyLength);
}
boolean useSingleCert = decodeUseSingleCertificate(reqData);
reqData.setUseSingleCert(useSingleCert);
}
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
|| tmp == WSConstants.ENCRYPTED_KEY_SHA1_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 encSymEncKey = getString(WSHandlerConstants.ENC_SYM_ENC_KEY, mc);
if (encSymEncKey != null) {
boolean encSymEndKeyBoolean = Boolean.valueOf(encSymEncKey).booleanValue();
reqData.setEncryptSymmetricEncryptionKey(encSymEndKeyBoolean);
}
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 decodeUseEncodedPasswords(RequestData reqData)
throws WSSecurityException {
String value = getString(
WSHandlerConstants.USE_ENCODED_PASSWORDS,
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 useEncodedPasswords parameter"
);
}
protected boolean decodeNamespaceQualifiedPasswordTypes(RequestData reqData)
throws WSSecurityException {
String value = getString(
WSHandlerConstants.ALLOW_NAMESPACE_QUALIFIED_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 allowNamespaceQualifiedPasswordTypes 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"
);
}
protected boolean decodeUseSingleCertificate(RequestData reqData)
throws WSSecurityException {
String useSingleCert =
getString(WSHandlerConstants.USE_SINGLE_CERTIFICATE, reqData.getMsgContext());
if (useSingleCert == null) {
return true;
}
if ("0".equals(useSingleCert) || "false".equals(useSingleCert)) {
return false;
}
if ("1".equals(useSingleCert) || "true".equals(useSingleCert)) {
return true;
}
throw new WSSecurityException(
"WSHandler: illegal useSingleCert parameter"
);
}
/**
* Get a password to construct a UsernameToken or sign a message.
*
* 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;
Object mc = reqData.getMsgContext();
String callback = getString(clsProp, mc);
if (callback != null) {
// we have a password callback class
pwCb = readPwViaCallbackClass(callback, username, doAction, reqData);
} else {
// Try to obtain a password callback class from the message context or handler options
CallbackHandler cbHandler = (CallbackHandler) getOption(refProp);
if (cbHandler == null) {
cbHandler = (CallbackHandler) getProperty(mc, refProp);
}
if (cbHandler != 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) {
String err = "provided null or empty password";
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.
*
* 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 if (reqData.getSigCrypto() != null) {
//
// Default to the signature crypto
//
crypto = reqData.getSigCrypto();
}
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
*
*/
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.
*
* 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.getSubjectX500Principal().getName();
String issuerString = cert.getIssuerX500Principal().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
);
}
}
if (doDebug) {
log.debug(
"WSHandler: Certificate path could not be verified for "
+ "certificate with subject " + subjectString
);
}
return false;
}
/**
* Evaluate whether the given certificate chain should be trusted.
*
* @param certificates the certificate chain that should be validated against the keystore
* @return true if the certificate chain is trusted, false if not
* @throws WSSecurityException
*/
protected boolean verifyTrust(X509Certificate[] certificates, RequestData reqData)
throws WSSecurityException {
// If no certificate was transmitted, do not trust the signature
if (certificates == null) {
return false;
}
String subjectString = certificates[0].getSubjectX500Principal().getName();
//
// 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 (certificates != null && certificates.length > 1
&& reqData.getSigCrypto().validateCertPath(certificates)) {
if (log.isDebugEnabled()) {
log.debug(
"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
);
}
if (log.isDebugEnabled()) {
log.debug(
"Certificate path could not be verified for certificate with subject "
+ subjectString
);
}
return false;
}
/**
* Evaluate whether a timestamp is considered valid on the receivers' side. 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 the receivers' side, that 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 {
return verifyTimestamp(timestamp, timeToLive, 60);
}
/**
* Evaluate whether a timestamp is considered valid on the receivers' side. 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 the receivers' side, that the timestamp is validated against
* @param futureTimeToLive
* the value in seconds for the future validity of the Created time
* @return true if the timestamp is before (now-timeToLive), false otherwise
* @throws WSSecurityException
*/
protected boolean verifyTimestamp(
Timestamp timestamp,
int timeToLive,
int futureTimeToLive
) throws WSSecurityException {
// Calculate the time that is allowed for the message to travel
Calendar validCreation = Calendar.getInstance();
validCreation.setTimeZone(TimeZone.getTimeZone("UTC"));
long currentTime = validCreation.getTime().getTime();
Calendar cre = timestamp.getCreated();
if (futureTimeToLive > 0) {
validCreation.setTime(new Date(currentTime + futureTimeToLive * 1000));
}
if (cre != null && cre.after(validCreation)) {
if (doDebug) {
log.debug("Validation of Timestamp: The message was created in the future!");
}
return false;
}
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)) {
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 key
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 name
.
*
* @param key the non-null key of the option.
* @return the option on key
if key
* 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
* @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);
}