blob: ae1634037586e3a0c5f099d2b95b39f3dec62f23 [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;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.om.xpath.AXIOMXPath;
import org.apache.axiom.om.OMNamespace;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rampart.policy.RampartPolicyData;
import org.apache.rampart.policy.SupportingPolicyData;
import org.apache.rampart.util.RampartUtil;
import org.apache.ws.secpolicy.SPConstants;
import org.apache.ws.secpolicy.model.*;
import org.apache.ws.security.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoType;
import org.apache.ws.security.message.token.Timestamp;
import org.apache.ws.security.util.WSSecurityUtil;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.jaxen.XPath;
import org.jaxen.JaxenException;
import javax.xml.namespace.QName;
import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.util.*;
public class PolicyBasedResultsValidator implements ExtendedPolicyValidatorCallbackHandler {
private static Log log = LogFactory.getLog(PolicyBasedResultsValidator.class);
public void validate(ValidatorData data, Vector results)
throws RampartException {
List<WSSecurityEngineResult> resultsList = new ArrayList<WSSecurityEngineResult>(results);
this.validate(data, resultsList);
}
/**
* {@inheritDoc}
*/
public void validate(ValidatorData data, List<WSSecurityEngineResult> results)
throws RampartException {
RampartMessageData rmd = data.getRampartMessageData();
RampartPolicyData rpd = rmd.getPolicyData();
//If there's Security policy present and no results
//then we should throw an error
if(rpd != null && results == null) {
throw new RampartException("noSecurityResults");
}
//Check presence of timestamp
WSSecurityEngineResult tsResult = null;
if(rpd != null && rpd.isIncludeTimestamp()) {
tsResult =
WSSecurityUtil.fetchActionResult(results, WSConstants.TS);
if(tsResult == null && !rpd.isIncludeTimestampOptional()) {
throw new RampartException("timestampMissing");
}
}
//sig/encr
List<WSEncryptionPart> encryptedParts = RampartUtil.getEncryptedParts(rmd);
if(rpd != null && rpd.isSignatureProtection() && isSignatureRequired(rmd)) {
String sigId = RampartUtil.getSigElementId(rmd);
encryptedParts.add(RampartUtil.createEncryptionPart(WSConstants.SIG_LN, sigId, WSConstants.SIG_NS,
RampartConstants.XML_ENCRYPTION_MODIFIER_ELEMENT));
}
List<WSEncryptionPart> signatureParts = RampartUtil.getSignedParts(rmd);
//Timestamp is not included in sig parts
if (rpd != null) {
if (tsResult != null || !rpd.isIncludeTimestampOptional()) {
if (rpd.isIncludeTimestamp()
&& !rpd.isTransportBinding()) {
signatureParts.add(RampartUtil.createEncryptionPart(WSConstants.TIMESTAMP_TOKEN_LN, "timestamp"));
}
}
}
if(!rmd.isInitiator()) {
//Just an indicator for EndorsingSupportingToken signature
SupportingToken endSupportingToken = null;
if (rpd != null) {
endSupportingToken = rpd.getEndorsingSupportingTokens();
}
if(endSupportingToken != null && !endSupportingToken.isOptional()) {
SignedEncryptedParts endSignedParts = endSupportingToken.getSignedParts();
if((endSignedParts != null && !endSignedParts.isOptional() &&
(endSignedParts.isBody() ||
endSignedParts.getHeaders().size() > 0)) ||
rpd.isIncludeTimestamp()) {
signatureParts.add(RampartUtil.createEncryptionPart("EndorsingSupportingTokens",
"EndorsingSupportingTokens"));
}
}
//Just an indicator for SignedEndorsingSupportingToken signature
SupportingToken sgndEndSupportingToken = null;
if (rpd != null) {
sgndEndSupportingToken = rpd.getSignedEndorsingSupportingTokens();
}
if(sgndEndSupportingToken != null && !sgndEndSupportingToken.isOptional()) {
SignedEncryptedParts sgndEndSignedParts = sgndEndSupportingToken.getSignedParts();
if((sgndEndSignedParts != null && !sgndEndSignedParts.isOptional() &&
(sgndEndSignedParts.isBody() ||
sgndEndSignedParts.getHeaders().size() > 0)) ||
rpd.isIncludeTimestamp()) {
signatureParts.add(RampartUtil.createEncryptionPart("SignedEndorsingSupportingTokens",
"SignedEndorsingSupportingTokens"));
}
}
if (rpd != null) {
List<SupportingToken> supportingToks = rpd.getSupportingTokensList();
for (SupportingToken supportingToken : supportingToks) {
if (supportingToken != null && !supportingToken.isOptional()) {
SupportingPolicyData policyData = new SupportingPolicyData();
policyData.build(supportingToken);
encryptedParts.addAll(RampartUtil.getSupportingEncryptedParts(rmd, policyData));
signatureParts.addAll(RampartUtil.getSupportingSignedParts(rmd, policyData));
}
}
}
}
validateEncrSig(data,encryptedParts, signatureParts, results);
if(rpd != null && !rpd.isTransportBinding()) {
validateProtectionOrder(data, results);
}
validateEncryptedParts(data, encryptedParts, results);
validateSignedPartsHeaders(data, signatureParts, results);
validateRequiredElements(data);
//Supporting tokens
if(!rmd.isInitiator()) {
validateSupportingTokens(data, results);
}
/*
* Now we can check the certificate used to sign the message. In the
* following implementation the certificate is only trusted if either it
* itself or the certificate of the issuer is installed in the keystore.
*
* Note: the method verifyTrust(X509Certificate) allows custom
* implementations with other validation algorithms for subclasses.
*/
// Extract the signature action result from the action vector
WSSecurityEngineResult actionResult = WSSecurityUtil.fetchActionResult(
results, WSConstants.SIGN);
if (actionResult != null) {
X509Certificate returnCert = (X509Certificate) actionResult
.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE);
if (returnCert != null) {
if (!verifyTrust(returnCert, rmd)) {
throw new RampartException ("trustVerificationError");
}
}
}
/*
* Perform further checks on the timestamp that was transmitted in the
* header.
* In the following implementation the timestamp is valid if :
* Timestamp->Created < 'now' < Timestamp->Expires.
* (Last test handled by WSS4J also if timeStampStrict enabled)
*
* Note: the method verifyTimestamp(Timestamp) allows custom
* implementations with other validation algorithms for subclasses.
*/
// Extract the timestamp action result from the action vector
actionResult = WSSecurityUtil.fetchActionResult(results, WSConstants.TS);
if (actionResult != null) {
Timestamp timestamp = (Timestamp) actionResult
.get(WSSecurityEngineResult.TAG_TIMESTAMP);
if (timestamp != null) {
if (!verifyTimestamp(timestamp, rmd)) {
throw new RampartException("cannotValidateTimestamp");
}
}
}
}
/**
* @param encryptedParts
* @param signatureParts
*/
protected void validateEncrSig(ValidatorData data,List<WSEncryptionPart> encryptedParts,
List<WSEncryptionPart> signatureParts, List<WSSecurityEngineResult> results)
throws RampartException {
List<Integer> actions = getSigEncrActions(results);
boolean sig = false;
boolean encr = false;
for (Object action : actions) {
Integer act = (Integer) action;
if (act == WSConstants.SIGN) {
sig = true;
} else if (act == WSConstants.ENCR) {
encr = true;
}
}
RampartPolicyData rpd = data.getRampartMessageData().getPolicyData();
SupportingToken sgndSupTokens = rpd.getSignedSupportingTokens();
SupportingToken sgndEndorSupTokens = rpd.getSignedEndorsingSupportingTokens();
if(sig && signatureParts.size() == 0
&& (sgndSupTokens == null || sgndSupTokens.getTokens().size() == 0)
&& (sgndEndorSupTokens == null || sgndEndorSupTokens.getTokens().size() == 0)) {
//Unexpected signature
throw new RampartException("unexprectedSignature");
} else if(!sig && signatureParts.size() > 0) {
//required signature missing
throw new RampartException("signatureMissing");
}
if(encr && encryptedParts.size() == 0) {
//Check whether its just an encrypted key
List<WSSecurityEngineResult> list = this.getResults(results, WSConstants.ENCR);
boolean encrDataFound = false;
for (WSSecurityEngineResult result : list) {
ArrayList dataRefURIs = (ArrayList) result.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
if (dataRefURIs != null && dataRefURIs.size() != 0) {
encrDataFound = true;
}
}
//TODO check whether the encrptedDataFound is an UsernameToken
if(encrDataFound && !isUsernameTokenPresent(data)) {
//Unexpected encryption
throw new RampartException("unexprectedEncryptedPart");
}
} else if(!encr && encryptedParts.size() > 0) {
//required signature missing
throw new RampartException("encryptionMissing");
}
}
/**
* @param data
* @param results
*/
protected void validateSupportingTokens(ValidatorData data, List<WSSecurityEngineResult> results)
throws RampartException {
//Check for UsernameToken
RampartPolicyData rpd = data.getRampartMessageData().getPolicyData();
List<SupportingToken> supportingTokens = rpd.getSupportingTokensList();
for (SupportingToken suppTok : supportingTokens) {
handleSupportingTokens(results, suppTok);
}
SupportingToken signedSuppToken = rpd.getSignedSupportingTokens();
handleSupportingTokens(results, signedSuppToken);
SupportingToken signedEndSuppToken = rpd.getSignedEndorsingSupportingTokens();
handleSupportingTokens(results, signedEndSuppToken);
SupportingToken endSuppToken = rpd.getEndorsingSupportingTokens();
handleSupportingTokens(results, endSuppToken);
}
/**
* @param results
* @param suppTok
* @throws RampartException
*/
protected void handleSupportingTokens(List<WSSecurityEngineResult> results, SupportingToken suppTok) throws RampartException {
if(suppTok == null) {
return;
}
ArrayList tokens = suppTok.getTokens();
for (Object objectToken : tokens) {
Token token = (Token) objectToken;
if (token instanceof UsernameToken) {
UsernameToken ut = (UsernameToken) token;
//Check presence of a UsernameToken
WSSecurityEngineResult utResult = WSSecurityUtil.fetchActionResult(results, WSConstants.UT);
if (utResult == null && !ut.isOptional()) {
throw new RampartException("usernameTokenMissing");
}
org.apache.ws.security.message.token.UsernameToken wssUt =
(org.apache.ws.security.message.token.UsernameToken) utResult.get(WSSecurityEngineResult.TAG_USERNAME_TOKEN);
if(ut.isNoPassword() && wssUt.getPassword() != null) {
throw new RampartException("invalidUsernameTokenType");
}
if(ut.isHashPassword() && !wssUt.isHashed()) {
throw new RampartException("invalidUsernameTokenType");
} else if (!ut.isHashPassword() && (wssUt.getPassword() == null ||
!wssUt.getPasswordType().equals(WSConstants.PASSWORD_TEXT))) {
throw new RampartException("invalidUsernameTokenType");
}
} else if (token instanceof IssuedToken) {
WSSecurityEngineResult samlResult = WSSecurityUtil.fetchActionResult(results, WSConstants.ST_SIGNED);
// Then check for unsigned saml tokens
if (samlResult == null) {
log.debug("No signed SAMLToken found. Looking for unsigned SAMLTokens");
samlResult = WSSecurityUtil.fetchActionResult(results, WSConstants.ST_UNSIGNED);
}
if (samlResult == null) {
throw new RampartException("samlTokenMissing");
}
} else if (token instanceof X509Token) {
X509Token x509Token = (X509Token) token;
WSSecurityEngineResult x509Result = WSSecurityUtil.fetchActionResult(results, WSConstants.BST);
if (x509Result == null && !x509Token.isOptional()) {
throw new RampartException("binaryTokenMissing");
}
}
}
}
/**
* @param data
* @param results
*/
protected void validateProtectionOrder(ValidatorData data, List<WSSecurityEngineResult> results)
throws RampartException {
String protectionOrder = data.getRampartMessageData().getPolicyData().getProtectionOrder();
List<Integer> sigEncrActions = this.getSigEncrActions(results);
if(sigEncrActions.size() < 2) {
//There are no results to COMPARE
return;
}
boolean sigNotPresent = true;
boolean encrNotPresent = true;
for (Object sigEncrAction : sigEncrActions) {
Integer act = (Integer) sigEncrAction;
if (act == WSConstants.SIGN) {
sigNotPresent = false;
} else if (act == WSConstants.ENCR) {
encrNotPresent = false;
}
}
// Only one action is present, so there is no order to check
if ( sigNotPresent || encrNotPresent ) {
return;
}
boolean done = false;
if(SPConstants.SIGN_BEFORE_ENCRYPTING.equals(protectionOrder)) {
boolean sigFound = false;
for (Iterator iter = sigEncrActions.iterator();
iter.hasNext() || !done;) {
Integer act = (Integer) iter.next();
if(act == WSConstants.ENCR && ! sigFound ) {
// We found ENCR and SIGN has not been found - break and fail
break;
}
if(act == WSConstants.SIGN) {
sigFound = true;
} else if(sigFound) {
//We have an ENCR action after sig
done = true;
}
}
} else {
boolean encrFound = false;
for (Object sigEncrAction : sigEncrActions) {
Integer act = (Integer) sigEncrAction;
if (act == WSConstants.SIGN && !encrFound) {
// We found SIGN and ENCR has not been found - break and fail
break;
}
if (act == WSConstants.ENCR) {
encrFound = true;
} else if (encrFound) {
//We have an ENCR action after sig
done = true;
}
}
}
if(!done) {
throw new RampartException("protectionOrderMismatch");
}
}
protected List<Integer> getSigEncrActions(List<WSSecurityEngineResult> results) {
List<Integer> sigEncrActions = new ArrayList<Integer>();
for (WSSecurityEngineResult result : results) {
Integer action = (Integer) (result)
.get(WSSecurityEngineResult.TAG_ACTION);
if (WSConstants.SIGN == action || WSConstants.ENCR == action) {
sigEncrActions.add(action);
}
}
return sigEncrActions;
}
protected void validateEncryptedParts(ValidatorData data,
List<WSEncryptionPart> encryptedParts, List<WSSecurityEngineResult> results)
throws RampartException {
RampartMessageData rmd = data.getRampartMessageData();
ArrayList encrRefs = getEncryptedReferences(results);
RampartPolicyData rpd = rmd.getPolicyData();
// build the list of encrypted nodes based on the dataRefs xpath expressions
SOAPEnvelope envelope = rmd.getMsgContext().getEnvelope();
Set namespaces = RampartUtil.findAllPrefixNamespaces(envelope,
rpd.getDeclaredNamespaces());
Map decryptedElements = new HashMap();
for (Object encrRef : encrRefs) {
WSDataRef dataRef = (WSDataRef) encrRef;
if (dataRef == null || dataRef.getXpath() == null) {
continue;
}
try {
XPath xp = new AXIOMXPath(dataRef.getXpath());
for (Object namespaceObject : namespaces) {
OMNamespace tmpNs = (OMNamespace) namespaceObject;
xp.addNamespace(tmpNs.getPrefix(), tmpNs.getNamespaceURI());
}
for (Object o : xp.selectNodes(envelope)) {
decryptedElements.put(o, dataRef.isContent());
}
} catch (JaxenException e) {
// This has to be changed to propagate an instance of a RampartException up
throw new RampartException("An error occurred while searching for decrypted elements.", e);
}
}
//Check for encrypted body
if(rpd.isEncryptBody()&& !rpd.isEncryptBodyOptional()) {
if( !isRefIdPresent(encrRefs, data.getBodyEncrDataId())){
throw new RampartException("encryptedPartMissing",
new String[]{data.getBodyEncrDataId()});
}
}
for (WSEncryptionPart encryptedPart : encryptedParts) {
//This is the encrypted Body and we already checked encrypted body
if (encryptedPart.getName().equals(WSConstants.ELEM_BODY)) {
continue;
}
if ((WSConstants.SIG_LN.equals(encryptedPart.getName()) &&
WSConstants.SIG_NS.equals(encryptedPart.getNamespace()))
|| encryptedPart.getEncModifier().equals(WSConstants.ELEM_HEADER)) {
if (!isRefIdPresent(encrRefs, new QName(encryptedPart.getNamespace(), encryptedPart.getName()))) {
throw new RampartException("encryptedPartMissing",
new String[]{encryptedPart.getNamespace() + ":" + encryptedPart.getName()});
}
continue;
}
// it is not a header or body part... verify encrypted xpath elements
String xpath = encryptedPart.getXpath();
boolean found = false;
try {
XPath xp = new AXIOMXPath(xpath);
for (Object namespaceObject : namespaces) {
OMNamespace tmpNs = (OMNamespace) namespaceObject;
xp.addNamespace(tmpNs.getPrefix(), tmpNs.getNamespaceURI());
}
for (Object o : xp.selectNodes(envelope)) {
Object result = decryptedElements.get(o);
if (result != null &&
("Element".equals(encryptedPart.getEncModifier())
^ (Boolean) result)) {
found = true;
break;
}
}
if (!found) {
throw new RampartException("encryptedPartMissing",
new String[]{xpath});
}
} catch (JaxenException e) {
// This has to be changed to propagate an instance of a RampartException up
throw new RampartException("An error occurred while searching for decrypted elements.", e);
}
}
}
public void validateRequiredElements(ValidatorData data) throws RampartException {
RampartMessageData rmd = data.getRampartMessageData();
RampartPolicyData rpd = rmd.getPolicyData();
SOAPEnvelope envelope = rmd.getMsgContext().getEnvelope();
for (String expression : rpd.getRequiredElements()) {
if (!RampartUtil.checkRequiredElements(envelope, rpd.getDeclaredNamespaces(), expression)) {
throw new RampartException("requiredElementsMissing", new String[]{expression});
}
}
}
protected void validateSignedPartsHeaders(ValidatorData data, List<WSEncryptionPart> signatureParts,
List<WSSecurityEngineResult> results)
throws RampartException {
RampartMessageData rmd = data.getRampartMessageData();
Node envelope = rmd.getDocument().getFirstChild();
WSSecurityEngineResult[] actionResults = fetchActionResults(results, WSConstants.SIGN);
// Find elements that are signed
List<QName> actuallySigned = new ArrayList<QName>();
if (actionResults != null) {
for (WSSecurityEngineResult actionResult : actionResults) {
List wsDataRefs = (List) actionResult.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
// if header was encrypted before it was signed, protected
// element is 'EncryptedHeader.' the actual element is
// first child element
for (Object objectDataReference : wsDataRefs) {
WSDataRef wsDataRef = (WSDataRef) objectDataReference;
Element protectedElement = wsDataRef.getProtectedElement();
if (protectedElement.getLocalName().equals("EncryptedHeader")) {
NodeList nodeList = protectedElement.getChildNodes();
for (int x = 0; x < nodeList.getLength(); x++) {
if (nodeList.item(x).getNodeType() == Node.ELEMENT_NODE) {
String ns = (nodeList.item(x)).getNamespaceURI();
String ln = (nodeList.item(x)).getLocalName();
actuallySigned.add(new QName(ns, ln));
break;
}
}
} else {
String ns = protectedElement.getNamespaceURI();
String ln = protectedElement.getLocalName();
actuallySigned.add(new QName(ns, ln));
}
}
}
}
for (WSEncryptionPart wsep : signatureParts) {
if (wsep.getName().equals(WSConstants.ELEM_BODY)) {
QName bodyQName;
if (WSConstants.URI_SOAP11_ENV.equals(envelope.getNamespaceURI())) {
bodyQName = new SOAP11Constants().getBodyQName();
} else {
bodyQName = new SOAP12Constants().getBodyQName();
}
if (!actuallySigned.contains(bodyQName) && !rmd.getPolicyData().isSignBodyOptional()) {
// soap body is not signed
throw new RampartException("bodyNotSigned");
}
} else if (wsep.getName().equals(WSConstants.ELEM_HEADER) ||
wsep.getXpath() != null) {
// TODO earlier this was wsep.getType() == WSConstants.PART_TYPE_ELEMENT
// This means that encrypted element of an XPath expression type. Therefore we are checking
// now whether an XPath expression exists. - Verify
Element element = WSSecurityUtil.findElement(
envelope, wsep.getName(), wsep.getNamespace());
if (element == null) {
// The signedpart header or element we are checking is not present in
// soap envelope - this is allowed
continue;
}
// header or the element present in soap envelope - verify that it is part of signature
if (actuallySigned.contains(new QName(element.getNamespaceURI(), element.getLocalName()))) {
continue;
}
String msg = wsep.getXpath() != null ?
"signedPartHeaderNotSigned" : "signedElementNotSigned";
// header or the element defined in policy is present but not signed
throw new RampartException(msg, new String[]{wsep.getNamespace() + ":" + wsep.getName()});
}
}
}
protected boolean isSignatureRequired(RampartMessageData rmd) {
RampartPolicyData rpd = rmd.getPolicyData();
return (rpd.isSymmetricBinding() && rpd.getSignatureToken() != null) ||
(!rpd.isSymmetricBinding() && !rpd.isTransportBinding() &&
((rpd.getInitiatorToken() != null && rmd.isInitiator())
|| rpd.getRecipientToken() != null && !rmd.isInitiator()));
}
/*
* Verify whether timestamp of the message is valid.
* If timeStampStrict is enabled in rampartConfig; testing of timestamp has not expired
* ('now' is before ts->Expires) is also handled earlier by WSS4J without timeskew.
* TODO must write unit tests
*/
protected boolean verifyTimestamp(Timestamp timestamp, RampartMessageData rmd) throws RampartException {
long maxSkew = RampartUtil.getTimestampMaxSkew(rmd);
//Verify that ts->Created is before 'now'
Date createdTime = timestamp.getCreated();
if (createdTime != null) {
long now = Calendar.getInstance().getTimeInMillis();
//calculate the tolerance limit for timeskew of the 'Created' in timestamp
if (maxSkew > 0) {
now += (maxSkew * 1000);
}
// fail if ts->Created is after 'now'
if (createdTime.getTime() > now) {
return false;
}
}
//Verify that ts->Expires is after now.
Date expires = timestamp.getExpires();
if (expires != null) {
long now = Calendar.getInstance().getTimeInMillis();
//calculate the tolerance limit for timeskew of the 'Expires' in timestamp
if (maxSkew > 0) {
now -= (maxSkew * 1000);
}
//fail if ts->Expires is before 'now'
if (expires.getTime() < now) {
return false;
}
}
return true;
}
/**
* 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
* @param rmd To get signature keystore information.
* @return true if the certificate is trusted, false if not (AxisFault is thrown for exceptions during CertPathValidation)
* @throws RampartException If an error occurred during validation.
*/
protected boolean verifyTrust(X509Certificate cert, RampartMessageData rmd) throws RampartException {
// If no certificate was transmitted, do not trust the signature
if (cert == null) {
return false;
}
Crypto crypto = RampartUtil.getSignatureCrypto(
rmd.getPolicyData().getRampartConfig(),
rmd.getCustomClassLoader());
// TODO removing this with WSS4J 1.6 migration. We do not have a way to get alias
// Therefore cannot set alias to message context. What will be affected from this ?
// rmd.getMsgContext().setProperty(RampartMessageData.SIGNATURE_CERT_ALIAS, alias);
// TODO this validation we are doing in SignatureProcessor.handleToken (WSS4J) So why we need to do again ?
// investigate
return isCertificateTrusted(cert, crypto);
}
/**
* TODO - This is directly copied from WSS4J (SignatureTrustValidator).
* We need to use to Validators instead of following code. REFACTOR later.
*
* Evaluate whether a given certificate should be trusted.
*
* 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
* @param crypto A crypto instance to use for trust validation
* @return true if the certificate is trusted, false if not
* @throws RampartException If an error occurred during validation.
*/
protected boolean isCertificateTrusted(
X509Certificate cert,
Crypto crypto
) throws RampartException {
String subjectString = cert.getSubjectX500Principal().getName();
String issuerString = cert.getIssuerX500Principal().getName();
BigInteger issuerSerial = cert.getSerialNumber();
if (log.isDebugEnabled()) {
log.debug("Transmitted certificate has subject " + subjectString);
log.debug(
"Transmitted certificate has issuer " + issuerString + " (serial "
+ issuerSerial + ")"
);
}
//
// FIRST step - Search the keystore for the transmitted certificate
//
if (isCertificateInKeyStore(crypto, cert)) {
return true;
}
//
// SECOND step - Search for the issuer cert (chain) of the transmitted certificate in the
// keystore or the truststore
//
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.SUBJECT_DN);
cryptoType.setSubjectDN(issuerString);
X509Certificate[] foundCerts = new X509Certificate[0];
try {
foundCerts = crypto.getX509Certificates(cryptoType);
} catch (WSSecurityException e) {
throw new RampartException("noCertForSubject", e);
}
// If the certs have not been found, the issuer is not in the keystore/truststore
// As a direct result, do not trust the transmitted certificate
if (foundCerts == null || foundCerts.length < 1) {
if (log.isDebugEnabled()) {
log.debug(
"No certs found in keystore for issuer " + issuerString
+ " of certificate for " + subjectString
);
}
return false;
}
//
// THIRD step
// Check the certificate trust path for the issuer cert chain
//
if (log.isDebugEnabled()) {
log.debug(
"Preparing to validate certificate path for issuer " + issuerString
);
}
//
// Form a certificate chain from the transmitted certificate
// and the certificate(s) of the issuer from the keystore/truststore
//
X509Certificate[] x509certs = new X509Certificate[foundCerts.length + 1];
x509certs[0] = cert;
for (int j = 0; j < foundCerts.length; j++) {
x509certs[j + 1] = (X509Certificate)foundCerts[j];
}
//
// Use the validation method from the crypto to check whether the subjects'
// certificate was really signed by the issuer stated in the certificate
//
// TODO we need to configure enable revocation ...
try {
if (crypto.verifyTrust(x509certs, false)) {
if (log.isDebugEnabled()) {
log.debug(
"Certificate path has been verified for certificate with subject "
+ subjectString
);
}
return true;
}
} catch (WSSecurityException e) {
throw new RampartException("certPathVerificationFailed", e);
}
if (log.isDebugEnabled()) {
log.debug(
"Certificate path could not be verified for certificate with subject "
+ subjectString
);
}
return false;
}
/**
* Check to see if the certificate argument is in the keystore
* TODO Directly copied from WSS4J (SignatureTrustValidator) - Optimize later
* @param crypto A Crypto instance to use for trust validation
* @param cert The certificate to check
* @return true if cert is in the keystore
* @throws RampartException If certificates are not found for given issuer and serial number.
*/
protected boolean isCertificateInKeyStore(
Crypto crypto,
X509Certificate cert
) throws RampartException {
String issuerString = cert.getIssuerX500Principal().getName();
BigInteger issuerSerial = cert.getSerialNumber();
CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
cryptoType.setIssuerSerial(issuerString, issuerSerial);
X509Certificate[] foundCerts = new X509Certificate[0];
try {
foundCerts = crypto.getX509Certificates(cryptoType);
} catch (WSSecurityException e) {
throw new RampartException("noCertificatesForIssuer", new String[]{issuerString,
issuerSerial.toString()}, e);
}
//
// If a certificate has been found, the certificates must be compared
// to ensure against phony DNs (compare encoded form including signature)
//
if (foundCerts != null && foundCerts[0] != null && foundCerts[0].equals(cert)) {
if (log.isDebugEnabled()) {
log.debug(
"Direct trust for certificate with " + cert.getSubjectX500Principal().getName()
);
}
return true;
}
if (log.isDebugEnabled()) {
log.debug(
"No certificate found for subject from issuer with " + issuerString
+ " (serial " + issuerSerial + ")"
);
}
return false;
}
protected ArrayList getEncryptedReferences(List<WSSecurityEngineResult> results) {
//there can be multiple ref lists
List<WSSecurityEngineResult> encrResults = getResults(results, WSConstants.ENCR);
ArrayList refs = new ArrayList();
for (WSSecurityEngineResult engineResult : encrResults) {
ArrayList dataRefUris = (ArrayList) engineResult
.get(WSSecurityEngineResult.TAG_DATA_REF_URIS);
//take only the ref list processing results
if (dataRefUris != null) {
for (Iterator iterator = dataRefUris.iterator(); iterator
.hasNext(); ) {
WSDataRef uri = (WSDataRef) iterator.next();
refs.add(uri);
}
}
}
return refs;
}
protected List<WSSecurityEngineResult> getResults(List<WSSecurityEngineResult> results, int action) {
List<WSSecurityEngineResult> list = new ArrayList<WSSecurityEngineResult>();
for (WSSecurityEngineResult result : results) {
// Check the result of every action whether it matches the given
// action
Integer actInt = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
if (actInt == action) {
list.add(result);
}
}
return list;
}
protected boolean isUsernameTokenPresent(ValidatorData data) {
//TODO This can be integrated with supporting token processing
// which also checks whether Username Tokens present
RampartPolicyData rpd = data.getRampartMessageData().getPolicyData();
List<SupportingToken> supportingToks = rpd.getSupportingTokensList();
for (SupportingToken suppTok : supportingToks) {
if (isUsernameTokenPresent(suppTok)) {
return true;
}
}
SupportingToken signedSuppToken = rpd.getSignedSupportingTokens();
if(isUsernameTokenPresent(signedSuppToken)) {
return true;
}
SupportingToken signedEndSuppToken = rpd.getSignedEndorsingSupportingTokens();
if(isUsernameTokenPresent(signedEndSuppToken)) {
return true;
}
SupportingToken endSuppToken = rpd.getEndorsingSupportingTokens();
return isUsernameTokenPresent(endSuppToken);
}
protected boolean isUsernameTokenPresent(SupportingToken suppTok) {
if(suppTok == null) {
return false;
}
ArrayList tokens = suppTok.getTokens();
for (Iterator iter = tokens.iterator(); iter.hasNext();) {
Token token = (Token) iter.next();
if(token instanceof UsernameToken) {
return true;
}
}
return false;
}
private boolean isRefIdPresent(ArrayList refList , String id) {
if(id != null && id.charAt(0) == '#') {
id = id.substring(1);
}
for (Object aRefList : refList) {
WSDataRef dataRef = (WSDataRef) aRefList;
//ArrayList can contain null elements
if (dataRef == null) {
continue;
}
//Try to get the wsuId of the decrypted element
String dataRefUri = dataRef.getWsuId();
//If not found, try the reference Id of encrypted element ( we set the same Id when we
// decrypted element in WSS4J)
// TODO wsu id must present. We need to find the scenario where it is not set
// if (dataRefUri == null) {
// dataRefUri = dataRef.getProtectedElement().getAttribute("Id"); // TODO check whether this is correct
// earlier it was dataRefUri = dataRef.getDataref();
//}
if (dataRefUri != null && dataRefUri.equals(id)) {
return true;
}
}
return false;
}
public static WSSecurityEngineResult[] fetchActionResults(List<WSSecurityEngineResult> wsSecurityEngineResults, int action) {
List<WSSecurityEngineResult> wsResult = new ArrayList<WSSecurityEngineResult>();
// Find the part of the security result that matches the given action
for (WSSecurityEngineResult wsSecurityEngineResult : wsSecurityEngineResults) {
// Check the result of every action whether it matches the given action
WSSecurityEngineResult result = (WSSecurityEngineResult) wsSecurityEngineResult;
int resultAction = (Integer) result.get(WSSecurityEngineResult.TAG_ACTION);
if (resultAction == action) {
wsResult.add(wsSecurityEngineResult);
}
}
return wsResult.toArray(new WSSecurityEngineResult[wsResult
.size()]);
}
private boolean isRefIdPresent(ArrayList refList , QName qname) {
for (Object aRefList : refList) {
WSDataRef dataRef = (WSDataRef) aRefList;
//ArrayList can contain null elements
if (dataRef == null) {
continue;
}
//QName of the decrypted element
QName dataRefQName = dataRef.getName();
if (dataRefQName != null && dataRefQName.equals(qname)) {
return true;
}
}
return false;
}
}