| /* |
| * 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.KeyStore; |
| 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)); |
| } |
| } |
| } |
| } else { |
| if (rpd.isSignatureConfirmation()) { |
| WSEncryptionPart part = new WSEncryptionPart(WSConstants.SIGNATURE_CONFIRMATION_LN, |
| WSConstants.WSSE11_NS, |
| "Element"); |
| signatureParts.add(part); |
| } |
| } |
| |
| 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"); |
| } |
| |
| } else if (token instanceof IssuedToken) { |
| //TODO is is enough to check for ST_UNSIGNED results ?? |
| WSSecurityEngineResult 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; |
| |
| } |
| |
| |
| } |