| /** |
| * 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.wss4j.dom.processor; |
| |
| import java.security.Key; |
| import java.security.NoSuchProviderException; |
| import java.security.Principal; |
| import java.security.Provider; |
| import java.security.PublicKey; |
| import java.security.cert.X509Certificate; |
| import java.security.spec.AlgorithmParameterSpec; |
| import java.time.Duration; |
| import java.time.Instant; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.xml.crypto.Data; |
| import javax.xml.crypto.NodeSetData; |
| import javax.xml.crypto.OctetStreamData; |
| import javax.xml.crypto.dsig.Manifest; |
| import javax.xml.crypto.dsig.Reference; |
| import javax.xml.crypto.dsig.SignedInfo; |
| import javax.xml.crypto.dsig.Transform; |
| import javax.xml.crypto.dsig.XMLObject; |
| import javax.xml.crypto.dsig.XMLSignature; |
| import javax.xml.crypto.dsig.XMLSignatureFactory; |
| import javax.xml.crypto.dsig.XMLValidateContext; |
| import javax.xml.crypto.dsig.dom.DOMValidateContext; |
| import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec; |
| import javax.xml.crypto.dsig.spec.HMACParameterSpec; |
| |
| import org.apache.wss4j.common.bsp.BSPEnforcer; |
| import org.apache.wss4j.common.bsp.BSPRule; |
| import org.apache.wss4j.common.cache.ReplayCache; |
| import org.apache.wss4j.common.crypto.AlgorithmSuite; |
| import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator; |
| import org.apache.wss4j.common.crypto.Crypto; |
| import org.apache.wss4j.common.crypto.CryptoType; |
| import org.apache.wss4j.common.ext.WSSecurityException; |
| import org.apache.wss4j.common.principal.PublicKeyPrincipalImpl; |
| import org.apache.wss4j.common.principal.UsernameTokenPrincipal; |
| import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal; |
| import org.apache.wss4j.common.token.BinarySecurity; |
| import org.apache.wss4j.common.token.SecurityTokenReference; |
| import org.apache.wss4j.common.util.KeyUtils; |
| import org.apache.wss4j.common.util.XMLUtils; |
| import org.apache.wss4j.dom.WSConstants; |
| import org.apache.wss4j.dom.WSDataRef; |
| import org.apache.wss4j.dom.WSDocInfo; |
| import org.apache.wss4j.dom.callback.CallbackLookup; |
| import org.apache.wss4j.dom.engine.WSSecurityEngineResult; |
| import org.apache.wss4j.dom.handler.RequestData; |
| import org.apache.wss4j.dom.message.token.Timestamp; |
| import org.apache.wss4j.dom.str.STRParser; |
| import org.apache.wss4j.dom.str.STRParser.REFERENCE_TYPE; |
| import org.apache.wss4j.dom.str.STRParserParameters; |
| import org.apache.wss4j.dom.str.STRParserResult; |
| import org.apache.wss4j.dom.str.SignatureSTRParser; |
| import org.apache.wss4j.dom.transform.AttachmentContentSignatureTransform; |
| import org.apache.wss4j.dom.transform.STRTransform; |
| import org.apache.wss4j.dom.transform.STRTransformUtil; |
| import org.apache.wss4j.dom.util.EncryptionUtils; |
| import org.apache.wss4j.dom.util.WSSecurityUtil; |
| import org.apache.wss4j.dom.util.X509Util; |
| import org.apache.wss4j.dom.validate.Credential; |
| import org.apache.wss4j.dom.validate.Validator; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| public class SignatureProcessor implements Processor { |
| private static final org.slf4j.Logger LOG = |
| org.slf4j.LoggerFactory.getLogger(SignatureProcessor.class); |
| |
| private XMLSignatureFactory signatureFactory; |
| |
| public SignatureProcessor() { |
| init(null); |
| } |
| |
| public SignatureProcessor(Provider provider) { |
| init(provider); |
| } |
| |
| private void init(Provider provider) { |
| if (provider == null) { |
| // Try to install the Santuario Provider - fall back to the JDK provider if this does |
| // not work |
| try { |
| signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig"); |
| } catch (NoSuchProviderException ex) { |
| signatureFactory = XMLSignatureFactory.getInstance("DOM"); |
| } |
| } else { |
| signatureFactory = XMLSignatureFactory.getInstance("DOM", provider); |
| } |
| } |
| |
| public List<WSSecurityEngineResult> handleToken( |
| Element elem, |
| RequestData data |
| ) throws WSSecurityException { |
| LOG.debug("Found signature element"); |
| Element keyInfoElement = |
| XMLUtils.getDirectChildElement( |
| elem, |
| "KeyInfo", |
| WSConstants.SIG_NS |
| ); |
| X509Certificate[] certs = null; |
| Principal principal = null; |
| PublicKey publicKey = null; |
| byte[] secretKey = null; |
| String signatureMethod = getSignatureMethod(elem); |
| REFERENCE_TYPE referenceType = null; |
| |
| Credential credential = new Credential(); |
| Validator validator = data.getValidator(WSConstants.SIGNATURE); |
| if (keyInfoElement == null) { |
| certs = getDefaultCerts(data.getSigVerCrypto()); |
| principal = certs[0].getSubjectX500Principal(); |
| } else { |
| int result = 0; |
| Node node = keyInfoElement.getFirstChild(); |
| Element child = null; |
| while (node != null) { |
| if (Node.ELEMENT_NODE == node.getNodeType()) { |
| result++; |
| child = (Element)node; |
| } |
| node = node.getNextSibling(); |
| } |
| if (result != 1) { |
| data.getBSPEnforcer().handleBSPRule(BSPRule.R5402); |
| } |
| |
| if (!(SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(child.getLocalName()) |
| && WSConstants.WSSE_NS.equals(child.getNamespaceURI()))) { |
| data.getBSPEnforcer().handleBSPRule(BSPRule.R5417); |
| |
| publicKey = X509Util.parseKeyValue(keyInfoElement, signatureFactory); |
| if (validator != null) { |
| credential.setPublicKey(publicKey); |
| principal = new PublicKeyPrincipalImpl(publicKey); |
| credential.setPrincipal(principal); |
| credential = validator.validate(credential, data); |
| } |
| } else { |
| STRParserParameters parameters = new STRParserParameters(); |
| parameters.setData(data); |
| parameters.setStrElement(child); |
| if (signatureMethod != null) { |
| parameters.setDerivationKeyLength(KeyUtils.getKeyLength(signatureMethod)); |
| } |
| |
| STRParser strParser = new SignatureSTRParser(); |
| STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters); |
| principal = parserResult.getPrincipal(); |
| certs = parserResult.getCertificates(); |
| publicKey = parserResult.getPublicKey(); |
| secretKey = parserResult.getSecretKey(); |
| referenceType = parserResult.getCertificatesReferenceType(); |
| |
| boolean trusted = parserResult.isTrustedCredential(); |
| if (trusted) { |
| LOG.debug("Direct Trust for SAML/BST credential"); |
| } |
| if (!trusted && (publicKey != null || certs != null) && validator != null) { |
| credential.setPublicKey(publicKey); |
| credential.setCertificates(certs); |
| credential.setPrincipal(principal); |
| credential = validator.validate(credential, data); |
| } |
| } |
| } |
| |
| // |
| // Check that we have a certificate, a public key or a secret key with which to |
| // perform signature verification |
| // |
| if ((certs == null || certs.length == 0 || certs[0] == null) |
| && secretKey == null |
| && publicKey == null) { |
| LOG.debug("No certificates or keys were found with which to validate the signature"); |
| throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK); |
| } |
| |
| // Check for compliance against the defined AlgorithmSuite |
| AlgorithmSuite algorithmSuite = data.getAlgorithmSuite(); |
| if (algorithmSuite != null) { |
| AlgorithmSuiteValidator algorithmSuiteValidator = new |
| AlgorithmSuiteValidator(algorithmSuite); |
| |
| if (principal instanceof WSDerivedKeyTokenPrincipal) { |
| algorithmSuiteValidator.checkDerivedKeyAlgorithm( |
| ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm() |
| ); |
| algorithmSuiteValidator.checkSignatureDerivedKeyLength( |
| ((WSDerivedKeyTokenPrincipal)principal).getLength() |
| ); |
| } else { |
| if (certs != null && certs.length > 0) { |
| algorithmSuiteValidator.checkAsymmetricKeyLength(certs); |
| } else if (publicKey != null) { |
| algorithmSuiteValidator.checkAsymmetricKeyLength(publicKey); |
| } else if (secretKey != null) { |
| algorithmSuiteValidator.checkSymmetricKeyLength(secretKey.length); |
| } |
| } |
| } |
| |
| XMLSignature xmlSignature = |
| verifyXMLSignature(elem, certs, publicKey, secretKey, signatureMethod, data, data.getWsDocInfo()); |
| byte[] signatureValue = xmlSignature.getSignatureValue().getValue(); |
| String c14nMethod = xmlSignature.getSignedInfo().getCanonicalizationMethod().getAlgorithm(); |
| |
| List<WSDataRef> dataRefs = |
| buildProtectedRefs( |
| elem.getOwnerDocument(), xmlSignature.getSignedInfo(), data, data.getWsDocInfo() |
| ); |
| if (dataRefs.isEmpty()) { |
| throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK); |
| } |
| |
| int actionPerformed = WSConstants.SIGN; |
| if (principal instanceof UsernameTokenPrincipal) { |
| actionPerformed = WSConstants.UT_SIGN; |
| } |
| |
| WSSecurityEngineResult result = new WSSecurityEngineResult( |
| actionPerformed, principal, |
| certs, dataRefs, signatureValue); |
| result.put(WSSecurityEngineResult.TAG_SIGNATURE_METHOD, signatureMethod); |
| result.put(WSSecurityEngineResult.TAG_CANONICALIZATION_METHOD, c14nMethod); |
| String tokenId = elem.getAttributeNS(null, "Id"); |
| if (!"".equals(tokenId)) { |
| result.put(WSSecurityEngineResult.TAG_ID, tokenId); |
| } |
| result.put(WSSecurityEngineResult.TAG_SECRET, secretKey); |
| result.put(WSSecurityEngineResult.TAG_PUBLIC_KEY, publicKey); |
| result.put(WSSecurityEngineResult.TAG_X509_REFERENCE_TYPE, referenceType); |
| result.put(WSSecurityEngineResult.TAG_TOKEN_ELEMENT, elem); |
| if (validator != null) { |
| result.put(WSSecurityEngineResult.TAG_VALIDATED_TOKEN, Boolean.TRUE); |
| if (credential != null) { |
| result.put(WSSecurityEngineResult.TAG_SUBJECT, credential.getSubject()); |
| } |
| } |
| data.getWsDocInfo().addResult(result); |
| data.getWsDocInfo().addTokenElement(elem); |
| return java.util.Collections.singletonList(result); |
| } |
| |
| /** |
| * Get the default certificates from the KeyStore |
| * @param crypto The Crypto object containing the default alias |
| * @return The default certificates |
| * @throws WSSecurityException |
| */ |
| private X509Certificate[] getDefaultCerts( |
| Crypto crypto |
| ) throws WSSecurityException { |
| if (crypto == null) { |
| throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noSigCryptoFile"); |
| } |
| if (crypto.getDefaultX509Identifier() != null) { |
| CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS); |
| cryptoType.setAlias(crypto.getDefaultX509Identifier()); |
| return crypto.getX509Certificates(cryptoType); |
| } else { |
| throw new WSSecurityException( |
| WSSecurityException.ErrorCode.INVALID_SECURITY, "unsupportedKeyInfo" |
| ); |
| } |
| } |
| |
| /** |
| * Verify the WS-Security signature. |
| * |
| * The functions at first checks if then <code>KeyInfo</code> that is |
| * contained in the signature contains standard X509 data. If yes then |
| * get the certificate data via the standard <code>KeyInfo</code> methods. |
| * |
| * Otherwise, if the <code>KeyInfo</code> info does not contain X509 data, check |
| * if we can find a <code>wsse:SecurityTokenReference</code> element. If yes, the next |
| * step is to check how to get the certificate. Two methods are currently supported |
| * here: |
| * <ul> |
| * <li> A URI reference to a binary security token contained in the <code>wsse:Security |
| * </code> header. If the dereferenced token is |
| * of the correct type the contained certificate is extracted. |
| * </li> |
| * <li> Issuer name an serial number of the certificate. In this case the method |
| * looks up the certificate in the keystore via the <code>crypto</code> parameter. |
| * </li> |
| * </ul> |
| * |
| * @param elem the XMLSignature DOM Element. |
| * @return the subject principal of the validated X509 certificate (the |
| * authenticated subject). The calling function may use this |
| * principal for further authentication or authorization. |
| * @throws WSSecurityException |
| */ |
| private XMLSignature verifyXMLSignature( |
| Element elem, |
| X509Certificate[] certs, |
| PublicKey publicKey, |
| byte[] secretKey, |
| String signatureMethod, |
| final RequestData data, |
| WSDocInfo wsDocInfo |
| ) throws WSSecurityException { |
| LOG.debug("Verify XML Signature"); |
| |
| // |
| // Perform the signature verification and build up a List of elements that the |
| // signature refers to |
| // |
| Key key = null; |
| if (certs != null && certs[0] != null) { |
| key = certs[0].getPublicKey(); |
| } else if (publicKey != null) { |
| key = publicKey; |
| } else { |
| key = KeyUtils.prepareSecretKey(signatureMethod, secretKey); |
| } |
| |
| if (data.isExpandXopInclude()) { |
| // Look for xop:Include Nodes and expand them |
| List<Element> includeElements = |
| XMLUtils.findElements(elem.getFirstChild(), "Include", WSConstants.XOP_NS); |
| WSSecurityUtil.inlineAttachments(includeElements, data.getAttachmentCallbackHandler(), true); |
| } |
| |
| XMLValidateContext context = new DOMValidateContext(key, elem); |
| context.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); |
| context.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE); |
| context.setProperty("org.jcp.xml.dsig.secureValidation", Boolean.TRUE); |
| context.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, wsDocInfo); |
| if (data.getSignatureProvider() != null) { |
| context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", data.getSignatureProvider()); |
| } |
| |
| context.setProperty(AttachmentContentSignatureTransform.ATTACHMENT_CALLBACKHANDLER, |
| data.getAttachmentCallbackHandler()); |
| |
| try { |
| XMLSignature xmlSignature = signatureFactory.unmarshalXMLSignature(context); |
| checkBSPCompliance(xmlSignature, data.getBSPEnforcer()); |
| |
| // Check for compliance against the defined AlgorithmSuite |
| AlgorithmSuite algorithmSuite = data.getAlgorithmSuite(); |
| if (algorithmSuite != null) { |
| AlgorithmSuiteValidator algorithmSuiteValidator = new |
| AlgorithmSuiteValidator(algorithmSuite); |
| algorithmSuiteValidator.checkSignatureAlgorithms(xmlSignature); |
| } |
| |
| // Test for replay attacks |
| testMessageReplay(elem, xmlSignature.getSignatureValue().getValue(), key, data, wsDocInfo); |
| |
| setElementsOnContext(xmlSignature, (DOMValidateContext)context, data, wsDocInfo); |
| |
| boolean signatureOk = xmlSignature.validate(context); |
| if (signatureOk) { |
| return xmlSignature; |
| } |
| // |
| // Log the exact signature error |
| // |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("XML Signature verification has failed"); |
| boolean signatureValidationCheck = |
| xmlSignature.getSignatureValue().validate(context); |
| LOG.debug("Signature Validation check: " + signatureValidationCheck); |
| java.util.Iterator<?> referenceIterator = |
| xmlSignature.getSignedInfo().getReferences().iterator(); |
| while (referenceIterator.hasNext()) { |
| Reference reference = (Reference)referenceIterator.next(); |
| boolean referenceValidationCheck = reference.validate(context); |
| String id = reference.getId(); |
| if (id == null) { |
| id = reference.getURI(); |
| } |
| LOG.debug("Reference " + id + " check: " + referenceValidationCheck); |
| } |
| } |
| } catch (WSSecurityException ex) { |
| throw ex; |
| } catch (Exception ex) { |
| throw new WSSecurityException( |
| WSSecurityException.ErrorCode.FAILED_CHECK, ex |
| ); |
| } |
| throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK); |
| } |
| |
| /** |
| * Retrieve the Reference elements and set them on the ValidateContext |
| * @param xmlSignature the XMLSignature object to get the references from |
| * @param context the ValidateContext |
| * @param wsDocInfo the WSDocInfo object where tokens are stored |
| * @param doc the owner document from which to find elements |
| * @throws WSSecurityException |
| */ |
| private void setElementsOnContext( |
| XMLSignature xmlSignature, |
| DOMValidateContext context, |
| RequestData data, |
| WSDocInfo wsDocInfo |
| ) throws WSSecurityException { |
| java.util.Iterator<?> referenceIterator = |
| xmlSignature.getSignedInfo().getReferences().iterator(); |
| CallbackLookup callbackLookup = wsDocInfo.getCallbackLookup(); |
| while (referenceIterator.hasNext()) { |
| Reference reference = (Reference)referenceIterator.next(); |
| String uri = reference.getURI(); |
| Element element = callbackLookup.getAndRegisterElement(uri, null, true, context); |
| if (element == null) { |
| wsDocInfo.setTokenOnContext(uri, context); |
| } else if ("BinarySecurityToken".equals(element.getLocalName()) |
| && WSConstants.WSSE_NS.equals(element.getNamespaceURI()) |
| && isXopInclude(element)) { |
| // We don't write out the xop:Include bytes into the BinarySecurityToken by default |
| // But if the BST is signed, then we have to, or else Signature validation fails... |
| handleXopInclude(element, wsDocInfo); |
| } else if (data.isExpandXopInclude() && element.getFirstChild() != null) { |
| // Look for xop:Include Nodes |
| List<Element> includeElements = |
| XMLUtils.findElements(element.getFirstChild(), "Include", WSConstants.XOP_NS); |
| for (Element includeElement : includeElements) { |
| String xopURI = includeElement.getAttributeNS(null, "href"); |
| if (xopURI != null) { |
| // Store the bytes in the attachment to calculate the signature |
| byte[] attachmentBytes = WSSecurityUtil.getBytesFromAttachment(xopURI, data); |
| String encodedBytes = org.apache.xml.security.utils.XMLUtils.encodeToString(attachmentBytes); |
| |
| Node newCipherValueChild = |
| includeElement.getOwnerDocument().createTextNode(encodedBytes); |
| includeElement.getParentNode().replaceChild(newCipherValueChild, includeElement); |
| } |
| } |
| } |
| } |
| } |
| |
| private boolean isXopInclude(Element element) { |
| Element elementChild = |
| XMLUtils.getDirectChildElement(element, "Include", WSConstants.XOP_NS); |
| if (elementChild != null && elementChild.hasAttributeNS(null, "href")) { |
| String xopUri = elementChild.getAttributeNS(null, "href"); |
| if (xopUri != null && xopUri.startsWith("cid:")) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void handleXopInclude(Element element, WSDocInfo wsDocInfo) { |
| Map<Integer, List<WSSecurityEngineResult>> actionResults = wsDocInfo.getActionResults(); |
| if (actionResults != null && actionResults.containsKey(WSConstants.BST)) { |
| for (WSSecurityEngineResult result : actionResults.get(WSConstants.BST)) { |
| Element token = (Element)result.get(WSSecurityEngineResult.TAG_TOKEN_ELEMENT); |
| if (element.equals(token)) { |
| BinarySecurity binarySecurity = |
| (BinarySecurity)result.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN); |
| binarySecurity.encodeRawToken(); |
| return; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Get the signature method algorithm URI from the associated signature element. |
| * @param signatureElement The signature element |
| * @return the signature method URI |
| */ |
| private static String getSignatureMethod( |
| Element signatureElement |
| ) { |
| Element signedInfoElement = |
| XMLUtils.getDirectChildElement( |
| signatureElement, |
| "SignedInfo", |
| WSConstants.SIG_NS |
| ); |
| if (signedInfoElement != null) { |
| Element signatureMethodElement = |
| XMLUtils.getDirectChildElement( |
| signedInfoElement, |
| "SignatureMethod", |
| WSConstants.SIG_NS |
| ); |
| if (signatureMethodElement != null) { |
| return signatureMethodElement.getAttributeNS(null, "Algorithm"); |
| } |
| } |
| return null; |
| } |
| |
| |
| /** |
| * This method digs into the Signature element to get the elements that |
| * this Signature covers. Build the QName of these Elements and return them |
| * to caller |
| * @param doc The owning document |
| * @param signedInfo The SignedInfo object |
| * @param requestData A RequestData instance |
| * @return A list of protected references |
| * @throws WSSecurityException |
| */ |
| private List<WSDataRef> buildProtectedRefs( |
| Document doc, |
| SignedInfo signedInfo, |
| RequestData requestData, |
| WSDocInfo wsDocInfo |
| ) throws WSSecurityException { |
| List<WSDataRef> protectedRefs = new ArrayList<>(signedInfo.getReferences().size()); |
| for (Object reference : signedInfo.getReferences()) { |
| Reference siRef = (Reference)reference; |
| String uri = siRef.getURI(); |
| |
| if (!"".equals(uri)) { |
| Element se = dereferenceSTR(doc, siRef, requestData, wsDocInfo); |
| // If an STR Transform is not used then just find the cached element |
| boolean attachment = false; |
| if (se == null) { |
| Data dereferencedData = siRef.getDereferencedData(); |
| if (dereferencedData instanceof NodeSetData) { |
| NodeSetData data = (NodeSetData)dereferencedData; |
| java.util.Iterator<?> iter = data.iterator(); |
| |
| while (iter.hasNext()) { |
| Node n = (Node)iter.next(); |
| if (n instanceof Element) { |
| se = (Element)n; |
| break; |
| } |
| } |
| } else if (dereferencedData instanceof OctetStreamData) { |
| se = doc.createElementNS("http://docs.oasis-open.org/wss/oasis-wss-SwAProfile-1.1", |
| "attachment"); |
| attachment = true; |
| } |
| } |
| if (se == null) { |
| throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK); |
| } |
| |
| WSDataRef ref = new WSDataRef(); |
| ref.setWsuId(uri); |
| ref.setProtectedElement(se); |
| ref.setAlgorithm(signedInfo.getSignatureMethod().getAlgorithm()); |
| ref.setDigestAlgorithm(siRef.getDigestMethod().getAlgorithm()); |
| ref.setDigestValue(siRef.getDigestValue()); |
| ref.setAttachment(attachment); |
| |
| // Set the Transform algorithms as well |
| @SuppressWarnings("unchecked") |
| List<Transform> transforms = (List<Transform>)siRef.getTransforms(); |
| List<String> transformAlgorithms = new ArrayList<>(transforms.size()); |
| for (Transform transform : transforms) { |
| transformAlgorithms.add(transform.getAlgorithm()); |
| } |
| ref.setTransformAlgorithms(transformAlgorithms); |
| |
| ref.setXpath(EncryptionUtils.getXPath(se)); |
| protectedRefs.add(ref); |
| } |
| } |
| return protectedRefs; |
| } |
| |
| /** |
| * Check to see if a SecurityTokenReference transform was used, if so then return the |
| * dereferenced element. |
| */ |
| private Element dereferenceSTR( |
| Document doc, |
| Reference siRef, |
| RequestData requestData, |
| WSDocInfo wsDocInfo |
| ) throws WSSecurityException { |
| |
| for (Object transformObject : siRef.getTransforms()) { |
| |
| Transform transform = (Transform)transformObject; |
| |
| if (STRTransform.TRANSFORM_URI.equals(transform.getAlgorithm())) { |
| NodeSetData data = (NodeSetData)siRef.getDereferencedData(); |
| if (data != null) { |
| java.util.Iterator<?> iter = data.iterator(); |
| |
| Node securityTokenReference = null; |
| while (iter.hasNext()) { |
| Node node = (Node)iter.next(); |
| if ("SecurityTokenReference".equals(node.getLocalName())) { |
| securityTokenReference = node; |
| break; |
| } |
| } |
| |
| if (securityTokenReference != null) { |
| SecurityTokenReference secTokenRef = |
| new SecurityTokenReference( |
| (Element)securityTokenReference, |
| requestData.getBSPEnforcer() |
| ); |
| Element se = STRTransformUtil.dereferenceSTR(doc, secTokenRef, wsDocInfo); |
| if (se != null) { |
| return se; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Test for a replayed message. The cache key is the Timestamp Created String, the signature |
| * value, and the encoded value of the signing key. |
| * @param signatureElement |
| * @param signatureValue |
| * @param key |
| * @param requestData |
| * @param wsDocInfo |
| * @throws WSSecurityException |
| */ |
| private void testMessageReplay( |
| Element signatureElement, |
| byte[] signatureValue, |
| Key key, |
| RequestData requestData, |
| WSDocInfo wsDocInfo |
| ) throws WSSecurityException { |
| ReplayCache replayCache = requestData.getTimestampReplayCache(); |
| if (replayCache == null) { |
| return; |
| } |
| |
| // Find the Timestamp |
| List<WSSecurityEngineResult> foundResults = wsDocInfo.getResultsByTag(WSConstants.TS); |
| Timestamp timeStamp = null; |
| if (foundResults.isEmpty()) { |
| // Search for a Timestamp below the Signature |
| Node sibling = signatureElement.getNextSibling(); |
| while (sibling != null) { |
| if (sibling instanceof Element |
| && WSConstants.TIMESTAMP_TOKEN_LN.equals(sibling.getLocalName()) |
| && WSConstants.WSU_NS.equals(sibling.getNamespaceURI())) { |
| timeStamp = new Timestamp((Element)sibling, requestData.getBSPEnforcer()); |
| break; |
| } |
| sibling = sibling.getNextSibling(); |
| } |
| } else { |
| timeStamp = (Timestamp)foundResults.get(0).get(WSSecurityEngineResult.TAG_TIMESTAMP); |
| } |
| if (timeStamp == null) { |
| return; |
| } |
| |
| // Test for replay attacks |
| String identifier = timeStamp.getCreatedString() + "" + Arrays.hashCode(signatureValue) |
| + "" + Arrays.hashCode(key.getEncoded()); |
| |
| if (replayCache.contains(identifier)) { |
| throw new WSSecurityException( |
| WSSecurityException.ErrorCode.INVALID_SECURITY, |
| "invalidTimestamp", |
| new Object[] {"A replay attack has been detected"}); |
| } |
| |
| // Store the Timestamp/SignatureValue/Key combination in the cache |
| if (timeStamp.getExpires() != null) { |
| replayCache.add(identifier, 1L + Duration.between(Instant.now(), timeStamp.getExpires()).getSeconds()); |
| } else { |
| replayCache.add(identifier); |
| } |
| |
| } |
| |
| /** |
| * Check BSP compliance (Note some other checks are done elsewhere in this class) |
| * @throws WSSecurityException |
| */ |
| private void checkBSPCompliance( |
| XMLSignature xmlSignature, |
| BSPEnforcer bspEnforcer |
| ) throws WSSecurityException { |
| // Check for Manifests |
| for (Object object : xmlSignature.getObjects()) { |
| if (object instanceof XMLObject) { |
| XMLObject xmlObject = (XMLObject)object; |
| for (Object xmlStructure : xmlObject.getContent()) { |
| if (xmlStructure instanceof Manifest) { |
| bspEnforcer.handleBSPRule(BSPRule.R5403); |
| } |
| } |
| } |
| } |
| |
| // Check the c14n algorithm |
| String c14nMethod = |
| xmlSignature.getSignedInfo().getCanonicalizationMethod().getAlgorithm(); |
| if (!WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(c14nMethod)) { |
| bspEnforcer.handleBSPRule(BSPRule.R5404); |
| } |
| |
| // Not allowed HMAC OutputLength |
| AlgorithmParameterSpec parameterSpec = |
| xmlSignature.getSignedInfo().getSignatureMethod().getParameterSpec(); |
| if (parameterSpec instanceof HMACParameterSpec) { |
| bspEnforcer.handleBSPRule(BSPRule.R5401); |
| } |
| |
| // Must have exclusive C14N without comments |
| parameterSpec = |
| xmlSignature.getSignedInfo().getCanonicalizationMethod().getParameterSpec(); |
| if (parameterSpec != null && !(parameterSpec instanceof ExcC14NParameterSpec)) { |
| bspEnforcer.handleBSPRule(BSPRule.R5404); |
| } |
| |
| // Check References |
| for (Object refObject : xmlSignature.getSignedInfo().getReferences()) { |
| Reference reference = (Reference)refObject; |
| if (reference.getTransforms().isEmpty()) { |
| bspEnforcer.handleBSPRule(BSPRule.R5416); |
| } |
| for (int i = 0; i < reference.getTransforms().size(); i++) { |
| Transform transform = (Transform)reference.getTransforms().get(i); |
| String algorithm = transform.getAlgorithm(); |
| if (!(WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm) |
| || STRTransform.TRANSFORM_URI.equals(algorithm) |
| || WSConstants.NS_XMLDSIG_FILTER2.equals(algorithm) |
| || WSConstants.NS_XMLDSIG_ENVELOPED_SIGNATURE.equals(algorithm) |
| || WSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm) |
| || WSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm))) { |
| bspEnforcer.handleBSPRule(BSPRule.R5423); |
| } |
| if (i == (reference.getTransforms().size() - 1) |
| && !(WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm) |
| || STRTransform.TRANSFORM_URI.equals(algorithm) |
| || WSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm) |
| || WSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm))) { |
| bspEnforcer.handleBSPRule(BSPRule.R5412); |
| } |
| |
| if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm)) { |
| parameterSpec = transform.getParameterSpec(); |
| if (parameterSpec != null && !(parameterSpec instanceof ExcC14NParameterSpec)) { |
| bspEnforcer.handleBSPRule(BSPRule.R5407); |
| } |
| } |
| } |
| } |
| } |
| |
| } |