blob: 7850e8750215f598b8e8f91995d573c1227c952b [file] [log] [blame]
/*
* Copyright 2004,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.rampart.builder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.Map.Entry;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.axiom.om.OMElement;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.TrustException;
import org.apache.rampart.RampartException;
import org.apache.rampart.RampartMessageData;
import org.apache.rampart.policy.RampartPolicyData;
import org.apache.rampart.util.RampartUtil;
import org.apache.ws.secpolicy.Constants;
import org.apache.ws.secpolicy.model.IssuedToken;
import org.apache.ws.secpolicy.model.SecureConversationToken;
import org.apache.ws.secpolicy.model.SupportingToken;
import org.apache.ws.secpolicy.model.Token;
import org.apache.ws.secpolicy.model.UsernameToken;
import org.apache.ws.secpolicy.model.X509Token;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.conversation.ConversationException;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.handler.WSHandlerResult;
import org.apache.ws.security.message.WSSecDKSign;
import org.apache.ws.security.message.WSSecEncryptedKey;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecSignatureConfirmation;
import org.apache.ws.security.message.WSSecTimestamp;
import org.apache.ws.security.message.WSSecUsernameToken;
import org.apache.ws.security.util.WSSecurityUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public abstract class BindingBuilder {
private static Log log = LogFactory.getLog(BindingBuilder.class);
private Element insertionLocation;
protected String mainSigId = null;
protected Element timestampElement;
/**
* @param rmd
*/
protected void addTimestamp(RampartMessageData rmd) {
log.debug("Adding timestamp");
WSSecTimestamp timestampBuilder = new WSSecTimestamp();
timestampBuilder.setWsConfig(rmd.getConfig());
timestampBuilder.setTimeToLive(RampartUtil.getTimeToLive(rmd));
// add the Timestamp to the SOAP Enevelope
timestampBuilder.build(rmd.getDocument(), rmd
.getSecHeader());
log.debug("Timestamp id: " + timestampBuilder.getId());
rmd.setTimestampId(timestampBuilder.getId());
this.timestampElement = timestampBuilder.getElement();
log.debug("Adding timestamp: DONE");
}
/**
* Add a UsernameToken to the security header
* @param rmd
* @return The <code>WSSecUsernameToken</code> instance
* @throws RampartException
*/
protected WSSecUsernameToken addUsernameToken(RampartMessageData rmd) throws RampartException {
log.debug("Adding a UsernameToken");
RampartPolicyData rpd = rmd.getPolicyData();
//Get the user
String user = rpd.getRampartConfig().getUser();
if(user != null && !"".equals(user)) {
log.debug("User : " + user);
//Get the password
CallbackHandler handler = RampartUtil.getPasswordCB(rmd);
if(handler == null) {
//If the callback handler is missing
throw new RampartException("cbHandlerMissing");
}
WSPasswordCallback[] cb = { new WSPasswordCallback(user,
WSPasswordCallback.USERNAME_TOKEN) };
try {
handler.handle(cb);
//get the password
String password = cb[0].getPassword();
log.debug("Password : " + password);
if(password != null && !"".equals(password)) {
//If the password is available then build the token
WSSecUsernameToken utBuilder = new WSSecUsernameToken();
//TODO Get the UT type, only WS-SX spec supports this
utBuilder.setUserInfo(user, password);
return utBuilder;
} else {
//If there's no password then throw an exception
throw new RampartException("noPasswordForUser",
new String[]{user});
}
} catch (IOException e) {
throw new RampartException("errorInGettingPasswordForUser",
new String[]{user}, e);
} catch (UnsupportedCallbackException e) {
throw new RampartException("errorInGettingPasswordForUser",
new String[]{user}, e);
}
} else {
log.debug("No user value specified in the configuration");
throw new RampartException("userMissing");
}
}
/**
* @param rmd
* @param token
* @return
* @throws WSSecurityException
* @throws RampartException
*/
protected WSSecEncryptedKey getEncryptedKeyBuilder(RampartMessageData rmd, Token token) throws RampartException {
RampartPolicyData rpd = rmd.getPolicyData();
Document doc = rmd.getDocument();
WSSecEncryptedKey encrKey = new WSSecEncryptedKey();
if(token.getInclusion().equals(Constants.INCLUDE_NEVER)) {
if(rpd.getWss11() != null) {
//Use thumbprint
encrKey.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
} else {
//Use SKI
encrKey.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
}
} else {
encrKey.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
}
try {
RampartUtil.setEncryptionUser(rmd, encrKey);
encrKey.setKeySize(rpd.getAlgorithmSuite().getMaximumSymmetricKeyLength());
encrKey.setKeyEncAlgo(rpd.getAlgorithmSuite().getAsymmetricKeyWrap());
encrKey.prepare(doc, RampartUtil.getEncryptionCrypto(rpd.getRampartConfig(), rmd.getCustomClassLoader()));
return encrKey;
} catch (WSSecurityException e) {
throw new RampartException("errorCreatingEncryptedKey", e);
}
}
protected WSSecSignature getSignatureBuider(RampartMessageData rmd, Token token) throws RampartException {
RampartPolicyData rpd = rmd.getPolicyData();
WSSecSignature sig = new WSSecSignature();
sig.setWsConfig(rmd.getConfig());
log.debug("Token inclusion: " + token.getInclusion());
if(token.getInclusion().equals(Constants.INCLUDE_NEVER) ||
(!rmd.isInitiator() && token.getInclusion().equals(Constants.INCLUDE_ALWAYS_TO_RECIPIENT))) {
if(rpd.getWss11() != null) {
//Use thumbprint
sig.setKeyIdentifierType(WSConstants.THUMBPRINT_IDENTIFIER);
} else {
//Use SKI
sig.setKeyIdentifierType(WSConstants.SKI_KEY_IDENTIFIER);
}
} else {
sig.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
}
//Get the user
String user = rpd.getRampartConfig().getUser();
String password = null;
if(user != null && !"".equals(user)) {
log.debug("User : " + user);
//Get the password
CallbackHandler handler = RampartUtil.getPasswordCB(rmd);
if(handler == null) {
//If the callback handler is missing
throw new RampartException("cbHandlerMissing");
}
WSPasswordCallback[] cb = { new WSPasswordCallback(user,
WSPasswordCallback.SIGNATURE) };
try {
handler.handle(cb);
if(cb[0].getPassword() != null && !"".equals(cb[0].getPassword())) {
password = cb[0].getPassword();
log.debug("Password : " + password);
} else {
//If there's no password then throw an exception
throw new RampartException("noPasswordForUser",
new String[]{user});
}
} catch (IOException e) {
throw new RampartException("errorInGettingPasswordForUser",
new String[]{user}, e);
} catch (UnsupportedCallbackException e) {
throw new RampartException("errorInGettingPasswordForUser",
new String[]{user}, e);
}
} else {
log.debug("No user value specified in the configuration");
throw new RampartException("userMissing");
}
sig.setUserInfo(user, password);
sig.setSignatureAlgorithm(rpd.getAlgorithmSuite().getAsymmetricSignature());
sig.setSigCanonicalization(rpd.getAlgorithmSuite().getInclusiveC14n());
try {
sig.prepare(rmd.getDocument(), RampartUtil.getSignatureCrypto(rpd
.getRampartConfig(), rmd.getCustomClassLoader()),
rmd.getSecHeader());
} catch (WSSecurityException e) {
throw new RampartException("errorInSignatureWithX509Token", e);
}
return sig;
}
/**
* @param rmd
* @param suppTokens
* @throws RampartException
*/
protected HashMap handleSupportingTokens(RampartMessageData rmd, SupportingToken suppTokens)
throws RampartException {
//Create the list to hold the tokens
HashMap endSuppTokMap = new HashMap();
if(suppTokens != null && suppTokens.getTokens() != null &&
suppTokens.getTokens().size() > 0) {
log.debug("Processing supporting tokens");
ArrayList tokens = suppTokens.getTokens();
for (Iterator iter = tokens.iterator(); iter.hasNext();) {
Token token = (Token) iter.next();
org.apache.rahas.Token endSuppTok = null;
if(token instanceof IssuedToken && rmd.isInitiator()){
String id = RampartUtil.getIssuedToken(rmd, (IssuedToken)token);
try {
endSuppTok = rmd.getTokenStorage().getToken(id);
} catch (TrustException e) {
throw new RampartException("errorInRetrievingTokenId",
new String[]{id}, e);
}
if(endSuppTok == null) {
throw new RampartException("errorInRetrievingTokenId",
new String[]{id});
}
//Add the token to the header
Element siblingElem = RampartUtil
.insertSiblingAfter(rmd, this.getInsertionLocation(),
(Element) endSuppTok.getToken());
this.setInsertionLocation(siblingElem);
//Add the extracted token
endSuppTokMap.put(token, endSuppTok);
} else if(token instanceof X509Token) {
//Get the to be added
if(token.isDerivedKeys()) {
//We have to use an EncryptedKey
try {
WSSecEncryptedKey encrKey = this
.getEncryptedKeyBuilder(rmd, token);
Element bstElem = encrKey.getBinarySecurityTokenElement();
if(bstElem != null) {
Element siblingElem = RampartUtil
.insertSiblingAfter(rmd, this.getInsertionLocation(),
bstElem);
this.setInsertionLocation(siblingElem);
}
Element siblingElem = RampartUtil
.insertSiblingAfter(rmd,
this.getInsertionLocation(),
encrKey.getEncryptedKeyElement());
this.setInsertionLocation(siblingElem);
Date now = new Date();
endSuppTok =
new org.apache.rahas.Token(encrKey.getId(),
(OMElement)encrKey.getEncryptedKeyElement(),
now, new Date(now.getTime() + 300000));
endSuppTokMap.put(token, endSuppTok);
} catch (TrustException e) {
throw new RampartException("errorCreatingRahasToken", e);
}
} else {
//We have to use a cert
//Prepare X509 signature
WSSecSignature sig = this.getSignatureBuider(rmd, token);
Element bstElem = sig.getBinarySecurityTokenElement();
if(bstElem != null) {
bstElem = RampartUtil.insertSiblingAfter(rmd,
this.getInsertionLocation(), bstElem);
this.setInsertionLocation(bstElem);
}
endSuppTokMap.put(token, sig);
}
} else if(token instanceof UsernameToken) {
WSSecUsernameToken utBuilder = addUsernameToken(rmd);
utBuilder.prepare(rmd.getDocument());
//Add the UT
Element elem = utBuilder.getUsernameTokenElement();
RampartUtil.insertSiblingAfter(rmd, this.getInsertionLocation(), elem);
//Move the insert location to th enext element
this.setInsertionLocation(elem);
Date now = new Date();
try {
org.apache.rahas.Token tempTok = new org.apache.rahas.Token(
utBuilder.getId(), (OMElement) elem, now,
new Date(now.getTime() + 300000));
endSuppTokMap.put(token, tempTok);
} catch (TrustException e) {
throw new RampartException("errorCreatingRahasToken", e);
}
}
}
}
return endSuppTokMap;
}
/**
* @param tokenMap
* @param sigParts
* @throws RampartException
*/
protected Vector addSignatureParts(HashMap tokenMap, Vector sigParts) throws RampartException {
Set entrySet = tokenMap.entrySet();
for (Iterator iter = entrySet.iterator(); iter.hasNext();) {
Object tempTok = ((Entry)iter.next()).getValue();
WSEncryptionPart part = null;
if(tempTok instanceof org.apache.rahas.Token) {
part = new WSEncryptionPart(
((org.apache.rahas.Token) tempTok).getId());
} else if(tempTok instanceof WSSecSignature) {
WSSecSignature tempSig = (WSSecSignature) tempTok;
if(tempSig.getBSTTokenId() != null) {
part = new WSEncryptionPart(tempSig.getBSTTokenId());
}
} else {
throw new RampartException("UnsupportedTokenInSupportingToken");
}
sigParts.add(part);
}
return sigParts;
}
public Element getInsertionLocation() {
return insertionLocation;
}
public void setInsertionLocation(Element insertionLocation) {
this.insertionLocation = insertionLocation;
}
protected Vector doEndorsedSignatures(RampartMessageData rmd, HashMap tokenMap) throws RampartException {
Set tokenSet = tokenMap.keySet();
Vector sigValues = new Vector();
for (Iterator iter = tokenSet.iterator(); iter.hasNext();) {
Token token = (Token)iter.next();
Object tempTok = tokenMap.get(token);
Vector sigParts = new Vector();
sigParts.add(new WSEncryptionPart(this.mainSigId));
if (tempTok instanceof org.apache.rahas.Token) {
org.apache.rahas.Token tok = (org.apache.rahas.Token)tempTok;
if(rmd.getPolicyData().isTokenProtection()) {
sigParts.add(new WSEncryptionPart(tok.getId()));
}
this.doSymmSignature(rmd, token, (org.apache.rahas.Token)tempTok, sigParts);
} else if (tempTok instanceof WSSecSignature) {
WSSecSignature sig = (WSSecSignature)tempTok;
if(rmd.getPolicyData().isTokenProtection() &&
sig.getBSTTokenId() != null) {
sigParts.add(new WSEncryptionPart(sig.getBSTTokenId()));
}
try {
sig.addReferencesToSign(sigParts, rmd.getSecHeader());
sig.computeSignature();
this.setInsertionLocation(RampartUtil.insertSiblingAfter(
rmd,
this.getInsertionLocation(),
sig.getSignatureElement()));
} catch (WSSecurityException e) {
throw new RampartException("errorInSignatureWithX509Token", e);
}
sigValues.add(sig.getSignatureValue());
}
}
return sigValues;
}
protected byte[] doSymmSignature(RampartMessageData rmd, Token policyToken, org.apache.rahas.Token tok, Vector sigParts) throws RampartException {
Document doc = rmd.getDocument();
RampartPolicyData rpd = rmd.getPolicyData();
if(policyToken.isDerivedKeys() || policyToken instanceof SecureConversationToken) {
try {
WSSecDKSign dkSign = new WSSecDKSign();
OMElement ref = tok.getAttachedReference();
if(ref == null) {
ref = tok.getUnattachedReference();
}
if(ref != null) {
dkSign.setExternalKey(tok.getSecret(), (Element)
doc.importNode((Element) ref, true));
} else {
dkSign.setExternalKey(tok.getSecret(), tok.getId());
}
//Set the algo info
dkSign.setSignatureAlgorithm(rpd.getAlgorithmSuite().getSymmetricSignature());
dkSign.setDerivedKeyLength(rpd.getAlgorithmSuite().getMinimumSymmetricKeyLength()/8);
dkSign.prepare(doc, rmd.getSecHeader());
if(rpd.isTokenProtection()) {
sigParts.add(new WSEncryptionPart(tok.getId()));
}
dkSign.setParts(sigParts);
dkSign.addReferencesToSign(sigParts, rmd.getSecHeader());
//Do signature
dkSign.computeSignature();
//Add elements to header
this.setInsertionLocation(RampartUtil
.insertSiblingAfter(rmd,
this.getInsertionLocation(),
dkSign.getdktElement()));
this.setInsertionLocation(RampartUtil.insertSiblingAfter(
rmd,
this.getInsertionLocation(),
dkSign.getSignatureElement()));
return dkSign.getSignatureValue();
} catch (ConversationException e) {
throw new RampartException(
"errorInDerivedKeyTokenSignature", e);
} catch (WSSecurityException e) {
throw new RampartException(
"errorInDerivedKeyTokenSignature", e);
}
} else {
//TODO : Example SAMLTOken Signature
throw new UnsupportedOperationException("TODO");
}
}
/**
* Get hold of the token from the token storage
* @param rmd
* @param tokenId
* @return token from the token storage
* @throws RampartException
*/
protected org.apache.rahas.Token getToken(RampartMessageData rmd,
String tokenId) throws RampartException {
org.apache.rahas.Token tok = null;
try {
tok = rmd.getTokenStorage().getToken(tokenId);
} catch (TrustException e) {
throw new RampartException("errorInRetrievingTokenId",
new String[]{tokenId}, e);
}
if(tok == null) {
throw new RampartException("errorInRetrievingTokenId",
new String[]{tokenId});
}
return tok;
}
protected void addSignatureConfirmation(RampartMessageData rmd, Vector sigParts) {
if(!rmd.getPolicyData().isSignatureConfirmation()) {
//If we don't require sig confirmation simply go back :-)
return;
}
Document doc = rmd.getDocument();
Vector results = (Vector)rmd.getMsgContext().getProperty(WSHandlerConstants.RECV_RESULTS);
/*
* loop over all results gathered by all handlers in the chain. For each
* handler result get the various actions. After that loop we have all
* signature results in the signatureActions vector
*/
Vector signatureActions = new Vector();
for (int i = 0; i < results.size(); i++) {
WSHandlerResult wshResult = (WSHandlerResult) results.get(i);
WSSecurityUtil.fetchAllActionResults(wshResult.getResults(),
WSConstants.SIGN, signatureActions);
WSSecurityUtil.fetchAllActionResults(wshResult.getResults(),
WSConstants.ST_SIGNED, signatureActions);
WSSecurityUtil.fetchAllActionResults(wshResult.getResults(),
WSConstants.UT_SIGN, signatureActions);
}
// prepare a SignatureConfirmation token
WSSecSignatureConfirmation wsc = new WSSecSignatureConfirmation();
if (signatureActions.size() > 0) {
if (log.isDebugEnabled()) {
log.debug("Signature Confirmation: number of Signature results: "
+ signatureActions.size());
}
for (int i = 0; i < signatureActions.size(); i++) {
WSSecurityEngineResult wsr = (WSSecurityEngineResult) signatureActions
.get(i);
byte[] sigVal = wsr.getSignatureValue();
wsc.setSignatureValue(sigVal);
wsc.prepare(doc);
RampartUtil.appendChildToSecHeader(rmd, wsc.getSignatureConfirmationElement());
if(sigParts != null) {
sigParts.add(new WSEncryptionPart(wsc.getId()));
}
}
} else {
//No Sig value
wsc.prepare(doc);
RampartUtil.appendChildToSecHeader(rmd, wsc.getSignatureConfirmationElement());
if(sigParts != null) {
sigParts.add(new WSEncryptionPart(wsc.getId()));
}
}
}
}