View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.dom.processor;
21  
22  import java.security.Key;
23  import java.security.NoSuchProviderException;
24  import java.security.Principal;
25  import java.security.Provider;
26  import java.security.PublicKey;
27  import java.security.cert.X509Certificate;
28  import java.security.spec.AlgorithmParameterSpec;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.List;
32  import java.util.Map;
33  
34  import javax.xml.crypto.Data;
35  import javax.xml.crypto.NodeSetData;
36  import javax.xml.crypto.OctetStreamData;
37  import javax.xml.crypto.dsig.Manifest;
38  import javax.xml.crypto.dsig.Reference;
39  import javax.xml.crypto.dsig.SignedInfo;
40  import javax.xml.crypto.dsig.Transform;
41  import javax.xml.crypto.dsig.XMLObject;
42  import javax.xml.crypto.dsig.XMLSignature;
43  import javax.xml.crypto.dsig.XMLSignatureFactory;
44  import javax.xml.crypto.dsig.XMLValidateContext;
45  import javax.xml.crypto.dsig.dom.DOMValidateContext;
46  import javax.xml.crypto.dsig.spec.ExcC14NParameterSpec;
47  import javax.xml.crypto.dsig.spec.HMACParameterSpec;
48  
49  import org.apache.wss4j.common.bsp.BSPEnforcer;
50  import org.apache.wss4j.common.bsp.BSPRule;
51  import org.apache.wss4j.common.cache.ReplayCache;
52  import org.apache.wss4j.common.crypto.AlgorithmSuite;
53  import org.apache.wss4j.common.crypto.AlgorithmSuiteValidator;
54  import org.apache.wss4j.common.crypto.Crypto;
55  import org.apache.wss4j.common.crypto.CryptoType;
56  import org.apache.wss4j.common.ext.WSSecurityException;
57  import org.apache.wss4j.common.principal.PublicKeyPrincipalImpl;
58  import org.apache.wss4j.common.principal.UsernameTokenPrincipal;
59  import org.apache.wss4j.common.principal.WSDerivedKeyTokenPrincipal;
60  import org.apache.wss4j.common.token.BinarySecurity;
61  import org.apache.wss4j.common.token.SecurityTokenReference;
62  import org.apache.wss4j.common.util.KeyUtils;
63  import org.apache.wss4j.common.util.XMLUtils;
64  import org.apache.wss4j.dom.WSConstants;
65  import org.apache.wss4j.dom.WSDataRef;
66  import org.apache.wss4j.dom.WSDocInfo;
67  import org.apache.wss4j.dom.callback.CallbackLookup;
68  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
69  import org.apache.wss4j.dom.handler.RequestData;
70  import org.apache.wss4j.dom.message.token.Timestamp;
71  import org.apache.wss4j.dom.str.STRParser;
72  import org.apache.wss4j.dom.str.STRParser.REFERENCE_TYPE;
73  import org.apache.wss4j.dom.str.STRParserParameters;
74  import org.apache.wss4j.dom.str.STRParserResult;
75  import org.apache.wss4j.dom.str.SignatureSTRParser;
76  import org.apache.wss4j.dom.transform.AttachmentContentSignatureTransform;
77  import org.apache.wss4j.dom.transform.STRTransform;
78  import org.apache.wss4j.dom.transform.STRTransformUtil;
79  import org.apache.wss4j.dom.util.EncryptionUtils;
80  import org.apache.wss4j.dom.util.WSSecurityUtil;
81  import org.apache.wss4j.dom.util.X509Util;
82  import org.apache.wss4j.dom.validate.Credential;
83  import org.apache.wss4j.dom.validate.Validator;
84  import org.w3c.dom.Document;
85  import org.w3c.dom.Element;
86  import org.w3c.dom.Node;
87  
88  public class SignatureProcessor implements Processor {
89      private static final org.slf4j.Logger LOG =
90          org.slf4j.LoggerFactory.getLogger(SignatureProcessor.class);
91  
92      private XMLSignatureFactory signatureFactory;
93  
94      public SignatureProcessor() {
95          init(null);
96      }
97  
98      public SignatureProcessor(Provider provider) {
99          init(provider);
100     }
101 
102     private void init(Provider provider) {
103         if (provider == null) {
104             // Try to install the Santuario Provider - fall back to the JDK provider if this does
105             // not work
106             try {
107                 signatureFactory = XMLSignatureFactory.getInstance("DOM", "ApacheXMLDSig");
108             } catch (NoSuchProviderException ex) {
109                 signatureFactory = XMLSignatureFactory.getInstance("DOM");
110             }
111         } else {
112             signatureFactory = XMLSignatureFactory.getInstance("DOM", provider);
113         }
114     }
115 
116     public List<WSSecurityEngineResult> handleToken(
117         Element elem,
118         RequestData data
119     ) throws WSSecurityException {
120         LOG.debug("Found signature element");
121         Element keyInfoElement =
122             XMLUtils.getDirectChildElement(
123                 elem,
124                 "KeyInfo",
125                 WSConstants.SIG_NS
126             );
127         X509Certificate[] certs = null;
128         Principal principal = null;
129         PublicKey publicKey = null;
130         byte[] secretKey = null;
131         String signatureMethod = getSignatureMethod(elem);
132         REFERENCE_TYPE referenceType = null;
133 
134         Credential credential = new Credential();
135         Validator validator = data.getValidator(WSConstants.SIGNATURE);
136         if (keyInfoElement == null) {
137             certs = getDefaultCerts(data.getSigVerCrypto());
138             principal = certs[0].getSubjectX500Principal();
139         } else {
140             int result = 0;
141             Node node = keyInfoElement.getFirstChild();
142             Element child = null;
143             while (node != null) {
144                 if (Node.ELEMENT_NODE == node.getNodeType()) {
145                     result++;
146                     child = (Element)node;
147                 }
148                 node = node.getNextSibling();
149             }
150             if (result != 1) {
151                 data.getBSPEnforcer().handleBSPRule(BSPRule.R5402);
152             }
153 
154             if (!(SecurityTokenReference.SECURITY_TOKEN_REFERENCE.equals(child.getLocalName())
155                 && WSConstants.WSSE_NS.equals(child.getNamespaceURI()))) {
156                 data.getBSPEnforcer().handleBSPRule(BSPRule.R5417);
157 
158                 publicKey = X509Util.parseKeyValue(keyInfoElement, signatureFactory);
159                 if (validator != null) {
160                     credential.setPublicKey(publicKey);
161                     principal = new PublicKeyPrincipalImpl(publicKey);
162                     credential.setPrincipal(principal);
163                     credential = validator.validate(credential, data);
164                 }
165             } else {
166                 STRParserParameters parameters = new STRParserParameters();
167                 parameters.setData(data);
168                 parameters.setStrElement(child);
169                 if (signatureMethod != null) {
170                     parameters.setDerivationKeyLength(KeyUtils.getKeyLength(signatureMethod));
171                 }
172 
173                 STRParser strParser = new SignatureSTRParser();
174                 STRParserResult parserResult = strParser.parseSecurityTokenReference(parameters);
175                 principal = parserResult.getPrincipal();
176                 certs = parserResult.getCertificates();
177                 publicKey = parserResult.getPublicKey();
178                 secretKey = parserResult.getSecretKey();
179                 referenceType = parserResult.getCertificatesReferenceType();
180 
181                 boolean trusted = parserResult.isTrustedCredential();
182                 if (trusted) {
183                     LOG.debug("Direct Trust for SAML/BST credential");
184                 }
185                 if (!trusted && (publicKey != null || (certs != null && certs.length > 0)) && validator != null) {
186                     credential.setPublicKey(publicKey);
187                     credential.setCertificates(certs);
188                     credential.setPrincipal(principal);
189                     credential = validator.validate(credential, data);
190                 }
191             }
192         }
193 
194         //
195         // Check that we have a certificate, a public key or a secret key with which to
196         // perform signature verification
197         //
198         if ((certs == null || certs.length == 0 || certs[0] == null)
199             && secretKey == null
200             && publicKey == null) {
201             LOG.debug("No certificates or keys were found with which to validate the signature");
202             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
203         }
204 
205         // Check for compliance against the defined AlgorithmSuite
206         AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
207         if (algorithmSuite != null) {
208             AlgorithmSuiteValidator algorithmSuiteValidator = new
209                 AlgorithmSuiteValidator(algorithmSuite);
210 
211             if (principal instanceof WSDerivedKeyTokenPrincipal) {
212                 algorithmSuiteValidator.checkDerivedKeyAlgorithm(
213                     ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm()
214                 );
215                 algorithmSuiteValidator.checkSignatureDerivedKeyLength(
216                     ((WSDerivedKeyTokenPrincipal)principal).getLength()
217                 );
218             } else {
219                 if (certs != null && certs.length > 0) {
220                     algorithmSuiteValidator.checkAsymmetricKeyLength(certs);
221                 } else if (publicKey != null) {
222                     algorithmSuiteValidator.checkAsymmetricKeyLength(publicKey);
223                 } else if (secretKey != null) {
224                     algorithmSuiteValidator.checkSymmetricKeyLength(secretKey.length);
225                 }
226             }
227         }
228 
229         XMLSignature xmlSignature =
230             verifyXMLSignature(elem, certs, publicKey, secretKey, signatureMethod, data, data.getWsDocInfo());
231         byte[] signatureValue = xmlSignature.getSignatureValue().getValue();
232         String c14nMethod = xmlSignature.getSignedInfo().getCanonicalizationMethod().getAlgorithm();
233 
234         List<WSDataRef> dataRefs =
235             buildProtectedRefs(
236                 elem.getOwnerDocument(), xmlSignature.getSignedInfo(), data, data.getWsDocInfo()
237             );
238         if (dataRefs.isEmpty()) {
239             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
240         }
241 
242         int actionPerformed = WSConstants.SIGN;
243         if (principal instanceof UsernameTokenPrincipal) {
244             actionPerformed = WSConstants.UT_SIGN;
245         }
246 
247         WSSecurityEngineResult result = new WSSecurityEngineResult(
248                 actionPerformed, principal,
249                 certs, dataRefs, signatureValue);
250         result.put(WSSecurityEngineResult.TAG_SIGNATURE_METHOD, signatureMethod);
251         result.put(WSSecurityEngineResult.TAG_CANONICALIZATION_METHOD, c14nMethod);
252         String tokenId = elem.getAttributeNS(null, "Id");
253         if (tokenId.length() != 0) {
254             result.put(WSSecurityEngineResult.TAG_ID, tokenId);
255         }
256         result.put(WSSecurityEngineResult.TAG_SECRET, secretKey);
257         result.put(WSSecurityEngineResult.TAG_PUBLIC_KEY, publicKey);
258         result.put(WSSecurityEngineResult.TAG_X509_REFERENCE_TYPE, referenceType);
259         result.put(WSSecurityEngineResult.TAG_TOKEN_ELEMENT, elem);
260         if (validator != null) {
261             result.put(WSSecurityEngineResult.TAG_VALIDATED_TOKEN, Boolean.TRUE);
262             if (credential != null) {
263                 result.put(WSSecurityEngineResult.TAG_SUBJECT, credential.getSubject());
264             }
265         }
266         data.getWsDocInfo().addResult(result);
267         data.getWsDocInfo().addTokenElement(elem);
268         return java.util.Collections.singletonList(result);
269     }
270 
271     /**
272      * Get the default certificates from the KeyStore
273      * @param crypto The Crypto object containing the default alias
274      * @return The default certificates
275      * @throws WSSecurityException
276      */
277     private X509Certificate[] getDefaultCerts(
278         Crypto crypto
279     ) throws WSSecurityException {
280         if (crypto == null) {
281             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "noSigCryptoFile");
282         }
283         if (crypto.getDefaultX509Identifier() != null) {
284             CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
285             cryptoType.setAlias(crypto.getDefaultX509Identifier());
286             return crypto.getX509Certificates(cryptoType);
287         } else {
288             throw new WSSecurityException(
289                 WSSecurityException.ErrorCode.INVALID_SECURITY, "unsupportedKeyInfo"
290             );
291         }
292     }
293 
294     /**
295      * Verify the WS-Security signature.
296      *
297      * The functions at first checks if then <code>KeyInfo</code> that is
298      * contained in the signature contains standard X509 data. If yes then
299      * get the certificate data via the standard <code>KeyInfo</code> methods.
300      *
301      * Otherwise, if the <code>KeyInfo</code> info does not contain X509 data, check
302      * if we can find a <code>wsse:SecurityTokenReference</code> element. If yes, the next
303      * step is to check how to get the certificate. Two methods are currently supported
304      * here:
305      * <ul>
306      * <li> A URI reference to a binary security token contained in the <code>wsse:Security
307      * </code> header.  If the dereferenced token is
308      * of the correct type the contained certificate is extracted.
309      * </li>
310      * <li> Issuer name an serial number of the certificate. In this case the method
311      * looks up the certificate in the keystore via the <code>crypto</code> parameter.
312      * </li>
313      * </ul>
314      *
315      * @param elem        the XMLSignature DOM Element.
316      * @return the subject principal of the validated X509 certificate (the
317      *         authenticated subject). The calling function may use this
318      *         principal for further authentication or authorization.
319      * @throws WSSecurityException
320      */
321     private XMLSignature verifyXMLSignature(
322         Element elem,
323         X509Certificate[] certs,
324         PublicKey publicKey,
325         byte[] secretKey,
326         String signatureMethod,
327         final RequestData data,
328         WSDocInfo wsDocInfo
329     ) throws WSSecurityException {
330         LOG.debug("Verify XML Signature");
331 
332         //
333         // Perform the signature verification and build up a List of elements that the
334         // signature refers to
335         //
336         Key key = null;
337         if (certs != null && certs.length > 0 && certs[0] != null) {
338             key = certs[0].getPublicKey();
339         } else if (publicKey != null) {
340             key = publicKey;
341         } else {
342             key = KeyUtils.prepareSecretKey(signatureMethod, secretKey);
343         }
344 
345         if (data.isExpandXopInclude()) {
346             // Look for xop:Include Nodes and expand them
347             List<Element> includeElements =
348                 XMLUtils.findElements(elem.getFirstChild(), "Include", WSConstants.XOP_NS);
349             WSSecurityUtil.inlineAttachments(includeElements, data.getAttachmentCallbackHandler(), true);
350         }
351 
352         XMLValidateContext context = new DOMValidateContext(key, elem);
353         context.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
354         context.setProperty("org.apache.jcp.xml.dsig.secureValidation", Boolean.TRUE);
355         context.setProperty("org.jcp.xml.dsig.secureValidation", Boolean.TRUE);
356         context.setProperty(STRTransform.TRANSFORM_WS_DOC_INFO, wsDocInfo);
357         if (data.getSignatureProvider() != null) {
358             context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", data.getSignatureProvider());
359         }
360 
361         context.setProperty(AttachmentContentSignatureTransform.ATTACHMENT_CALLBACKHANDLER,
362                             data.getAttachmentCallbackHandler());
363 
364         try {
365             XMLSignature xmlSignature = signatureFactory.unmarshalXMLSignature(context);
366             checkBSPCompliance(xmlSignature, data.getBSPEnforcer());
367 
368             // Check for compliance against the defined AlgorithmSuite
369             AlgorithmSuite algorithmSuite = data.getAlgorithmSuite();
370             if (algorithmSuite != null) {
371                 AlgorithmSuiteValidator algorithmSuiteValidator = new
372                     AlgorithmSuiteValidator(algorithmSuite);
373                 algorithmSuiteValidator.checkSignatureAlgorithms(xmlSignature);
374             }
375 
376             // Test for replay attacks
377             testMessageReplay(elem, xmlSignature.getSignatureValue().getValue(), key, data, wsDocInfo);
378 
379             setElementsOnContext(xmlSignature, (DOMValidateContext)context, data, wsDocInfo);
380 
381             boolean signatureOk = xmlSignature.validate(context);
382             if (signatureOk) {
383                 return xmlSignature;
384             }
385             //
386             // Log the exact signature error
387             //
388             if (LOG.isDebugEnabled()) {
389                 LOG.warn("XML Signature verification has failed");
390                 boolean signatureValidationCheck =
391                     xmlSignature.getSignatureValue().validate(context);
392                 LOG.debug("Signature Validation check: " + signatureValidationCheck);
393                 java.util.Iterator<?> referenceIterator =
394                     xmlSignature.getSignedInfo().getReferences().iterator();
395                 while (referenceIterator.hasNext()) {
396                     Reference reference = (Reference)referenceIterator.next();
397                     boolean referenceValidationCheck = reference.validate(context);
398                     String id = reference.getId();
399                     if (id == null) {
400                         id = reference.getURI();
401                     }
402                     LOG.debug("Reference " + id + " check: " + referenceValidationCheck);
403                 }
404             }
405         } catch (WSSecurityException ex) {
406             throw ex;
407         } catch (Exception ex) {
408             throw new WSSecurityException(
409                 WSSecurityException.ErrorCode.FAILED_CHECK, ex
410             );
411         }
412         throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
413     }
414 
415     /**
416      * Retrieve the Reference elements and set them on the ValidateContext
417      * @param xmlSignature the XMLSignature object to get the references from
418      * @param context the ValidateContext
419      * @param data The RequestData object
420      * @param wsDocInfo the WSDocInfo object where tokens are stored
421      * @throws WSSecurityException
422      */
423     private void setElementsOnContext(
424         XMLSignature xmlSignature,
425         DOMValidateContext context,
426         RequestData data,
427         WSDocInfo wsDocInfo
428     ) throws WSSecurityException {
429         java.util.Iterator<?> referenceIterator =
430             xmlSignature.getSignedInfo().getReferences().iterator();
431         CallbackLookup callbackLookup = wsDocInfo.getCallbackLookup();
432         while (referenceIterator.hasNext()) {
433             Reference reference = (Reference)referenceIterator.next();
434             String uri = reference.getURI();
435             Element element = callbackLookup.getAndRegisterElement(uri, null, true, context);
436             if (element == null) {
437                 wsDocInfo.setTokenOnContext(uri, context);
438             } else if ("BinarySecurityToken".equals(element.getLocalName())
439                 && WSConstants.WSSE_NS.equals(element.getNamespaceURI())
440                 && isXopInclude(element)) {
441                 // We don't write out the xop:Include bytes into the BinarySecurityToken by default
442                 // But if the BST is signed, then we have to, or else Signature validation fails...
443                 handleXopInclude(element, wsDocInfo);
444             } else if (data.isExpandXopInclude() && element.getFirstChild() != null) {
445                 // Look for xop:Include Nodes
446                 List<Element> includeElements =
447                     XMLUtils.findElements(element.getFirstChild(), "Include", WSConstants.XOP_NS);
448                 WSSecurityUtil.inlineAttachments(includeElements, data.getAttachmentCallbackHandler(), true);
449             }
450         }
451     }
452 
453     private boolean isXopInclude(Element element) {
454         Element elementChild =
455             XMLUtils.getDirectChildElement(element, "Include", WSConstants.XOP_NS);
456         if (elementChild != null && elementChild.hasAttributeNS(null, "href")) {
457             String xopUri = elementChild.getAttributeNS(null, "href");
458             if (xopUri != null && xopUri.startsWith("cid:")) {
459                 return true;
460             }
461         }
462         return false;
463     }
464 
465     private void handleXopInclude(Element element, WSDocInfo wsDocInfo) {
466         Map<Integer, List<WSSecurityEngineResult>> actionResults = wsDocInfo.getActionResults();
467         if (actionResults != null && actionResults.containsKey(WSConstants.BST)) {
468             for (WSSecurityEngineResult result : actionResults.get(WSConstants.BST)) {
469                 Element token = (Element)result.get(WSSecurityEngineResult.TAG_TOKEN_ELEMENT);
470                 if (element.equals(token)) {
471                     BinarySecurity binarySecurity =
472                         (BinarySecurity)result.get(WSSecurityEngineResult.TAG_BINARY_SECURITY_TOKEN);
473                     binarySecurity.encodeRawToken();
474                     return;
475                 }
476             }
477         }
478     }
479 
480     /**
481      * Get the signature method algorithm URI from the associated signature element.
482      * @param signatureElement The signature element
483      * @return the signature method URI
484      */
485     private static String getSignatureMethod(
486         Element signatureElement
487     ) {
488         Element signedInfoElement =
489             XMLUtils.getDirectChildElement(
490                 signatureElement,
491                 "SignedInfo",
492                 WSConstants.SIG_NS
493             );
494         if (signedInfoElement != null) {
495             Element signatureMethodElement =
496                 XMLUtils.getDirectChildElement(
497                     signedInfoElement,
498                     "SignatureMethod",
499                     WSConstants.SIG_NS
500                 );
501             if (signatureMethodElement != null) {
502                 return signatureMethodElement.getAttributeNS(null, "Algorithm");
503             }
504         }
505         return null;
506     }
507 
508 
509     /**
510      * This method digs into the Signature element to get the elements that
511      * this Signature covers. Build the QName of these Elements and return them
512      * to caller
513      * @param doc The owning document
514      * @param signedInfo The SignedInfo object
515      * @param requestData A RequestData instance
516      * @return A list of protected references
517      * @throws WSSecurityException
518      */
519     private List<WSDataRef> buildProtectedRefs(
520         Document doc,
521         SignedInfo signedInfo,
522         RequestData requestData,
523         WSDocInfo wsDocInfo
524     ) throws WSSecurityException {
525         List<WSDataRef> protectedRefs = new ArrayList<>(signedInfo.getReferences().size());
526         for (Object reference : signedInfo.getReferences()) {
527             Reference siRef = (Reference)reference;
528             String uri = siRef.getURI();
529 
530             if (uri.length() != 0) {
531                 Element se = dereferenceSTR(doc, siRef, requestData, wsDocInfo);
532                 // If an STR Transform is not used then just find the cached element
533                 boolean attachment = false;
534                 if (se == null) {
535                     Data dereferencedData = siRef.getDereferencedData();
536                     if (dereferencedData instanceof NodeSetData) {
537                         NodeSetData data = (NodeSetData)dereferencedData;
538                         java.util.Iterator<?> iter = data.iterator();
539 
540                         while (iter.hasNext()) {
541                             Node n = (Node)iter.next();
542                             if (n instanceof Element) {
543                                 se = (Element)n;
544                                 break;
545                             }
546                         }
547                     } else if (dereferencedData instanceof OctetStreamData) {
548                         se = doc.createElementNS("http://docs.oasis-open.org/wss/oasis-wss-SwAProfile-1.1",
549                                                  "attachment");
550                         attachment = true;
551                     }
552                 }
553                 if (se == null) {
554                     throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_CHECK);
555                 }
556 
557                 WSDataRef ref = new WSDataRef();
558                 ref.setWsuId(uri);
559                 ref.setProtectedElement(se);
560                 ref.setAlgorithm(signedInfo.getSignatureMethod().getAlgorithm());
561                 ref.setDigestAlgorithm(siRef.getDigestMethod().getAlgorithm());
562                 ref.setDigestValue(siRef.getDigestValue());
563                 ref.setAttachment(attachment);
564 
565                 // Set the Transform algorithms as well
566                 @SuppressWarnings("unchecked")
567                 List<Transform> transforms = siRef.getTransforms();
568                 List<String> transformAlgorithms = new ArrayList<>(transforms.size());
569                 for (Transform transform : transforms) {
570                     transformAlgorithms.add(transform.getAlgorithm());
571                 }
572                 ref.setTransformAlgorithms(transformAlgorithms);
573 
574                 ref.setXpath(EncryptionUtils.getXPath(se));
575                 protectedRefs.add(ref);
576             }
577         }
578         return protectedRefs;
579     }
580 
581     /**
582      * Check to see if a SecurityTokenReference transform was used, if so then return the
583      * dereferenced element.
584      */
585     private Element dereferenceSTR(
586         Document doc,
587         Reference siRef,
588         RequestData requestData,
589         WSDocInfo wsDocInfo
590     ) throws WSSecurityException {
591 
592         for (Object transformObject : siRef.getTransforms()) {
593 
594             Transform transform = (Transform)transformObject;
595 
596             if (STRTransform.TRANSFORM_URI.equals(transform.getAlgorithm())) {
597                 NodeSetData data = (NodeSetData)siRef.getDereferencedData();
598                 if (data != null) {
599                     java.util.Iterator<?> iter = data.iterator();
600 
601                     Node securityTokenReference = null;
602                     while (iter.hasNext()) {
603                         Node node = (Node)iter.next();
604                         if ("SecurityTokenReference".equals(node.getLocalName())) {
605                             securityTokenReference = node;
606                             break;
607                         }
608                     }
609 
610                     if (securityTokenReference != null) {
611                         SecurityTokenReference secTokenRef =
612                             new SecurityTokenReference(
613                                 (Element)securityTokenReference,
614                                 requestData.getBSPEnforcer()
615                             );
616                         Element se = STRTransformUtil.dereferenceSTR(doc, secTokenRef, wsDocInfo);
617                         if (se != null) {
618                             return se;
619                         }
620                     }
621                 }
622             }
623         }
624         return null;
625     }
626 
627     /**
628      * Test for a replayed message. The cache key is the Timestamp Created String, the signature
629      * value, and the encoded value of the signing key.
630      * @param signatureElement
631      * @param signatureValue
632      * @param key
633      * @param requestData
634      * @param wsDocInfo
635      * @throws WSSecurityException
636      */
637     private void testMessageReplay(
638         Element signatureElement,
639         byte[] signatureValue,
640         Key key,
641         RequestData requestData,
642         WSDocInfo wsDocInfo
643     ) throws WSSecurityException {
644         ReplayCache replayCache = requestData.getTimestampReplayCache();    //NOPMD
645         if (replayCache == null) {
646             return;
647         }
648 
649         // Find the Timestamp
650         List<WSSecurityEngineResult> foundResults = wsDocInfo.getResultsByTag(WSConstants.TS);
651         Timestamp timeStamp = null;
652         if (foundResults.isEmpty()) {
653             // Search for a Timestamp below the Signature
654             Node sibling = signatureElement.getNextSibling();
655             while (sibling != null) {
656                 if (sibling instanceof Element
657                     && WSConstants.TIMESTAMP_TOKEN_LN.equals(sibling.getLocalName())
658                     && WSConstants.WSU_NS.equals(sibling.getNamespaceURI())) {
659                     timeStamp = new Timestamp((Element)sibling, requestData.getBSPEnforcer());
660                     break;
661                 }
662                 sibling = sibling.getNextSibling();
663             }
664         } else {
665             timeStamp = (Timestamp)foundResults.get(0).get(WSSecurityEngineResult.TAG_TIMESTAMP);
666         }
667         if (timeStamp == null) {
668             return;
669         }
670 
671         // Test for replay attacks
672         String identifier = timeStamp.getCreatedString() + "" + Arrays.hashCode(signatureValue)
673             + "" + Arrays.hashCode(key.getEncoded());
674 
675         if (replayCache.contains(identifier)) {
676             throw new WSSecurityException(
677                 WSSecurityException.ErrorCode.INVALID_SECURITY,
678                 "invalidTimestamp",
679                 new Object[] {"A replay attack has been detected"});
680         }
681 
682         // Store the Timestamp/SignatureValue/Key combination in the cache
683         if (timeStamp.getExpires() != null) {
684             replayCache.add(identifier, timeStamp.getExpires());
685         } else {
686             replayCache.add(identifier);
687         }
688     }
689 
690     /**
691      * Check BSP compliance (Note some other checks are done elsewhere in this class)
692      * @throws WSSecurityException
693      */
694     private void checkBSPCompliance(
695         XMLSignature xmlSignature,
696         BSPEnforcer bspEnforcer
697     ) throws WSSecurityException {
698         // Check for Manifests
699         for (Object object : xmlSignature.getObjects()) {
700             if (object instanceof XMLObject) {
701                 XMLObject xmlObject = (XMLObject)object;
702                 for (Object xmlStructure : xmlObject.getContent()) {
703                     if (xmlStructure instanceof Manifest) {
704                         bspEnforcer.handleBSPRule(BSPRule.R5403);
705                     }
706                 }
707             }
708         }
709 
710         // Check the c14n algorithm
711         String c14nMethod =
712             xmlSignature.getSignedInfo().getCanonicalizationMethod().getAlgorithm();
713         if (!WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(c14nMethod)) {
714             bspEnforcer.handleBSPRule(BSPRule.R5404);
715         }
716 
717         // Not allowed HMAC OutputLength
718         AlgorithmParameterSpec parameterSpec =
719             xmlSignature.getSignedInfo().getSignatureMethod().getParameterSpec();
720         if (parameterSpec instanceof HMACParameterSpec) {
721             bspEnforcer.handleBSPRule(BSPRule.R5401);
722         }
723 
724         // Must have exclusive C14N without comments
725         parameterSpec =
726             xmlSignature.getSignedInfo().getCanonicalizationMethod().getParameterSpec();
727         if (parameterSpec != null && !(parameterSpec instanceof ExcC14NParameterSpec)) {
728             bspEnforcer.handleBSPRule(BSPRule.R5404);
729         }
730 
731         // Check References
732         for (Object refObject : xmlSignature.getSignedInfo().getReferences()) {
733             Reference reference = (Reference)refObject;
734             if (reference.getTransforms().isEmpty()) {
735                 bspEnforcer.handleBSPRule(BSPRule.R5416);
736             }
737             for (int i = 0; i < reference.getTransforms().size(); i++) {
738                 Transform transform = (Transform)reference.getTransforms().get(i);
739                 String algorithm = transform.getAlgorithm();
740                 if (!(WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm)
741                     || STRTransform.TRANSFORM_URI.equals(algorithm)
742                     || WSConstants.NS_XMLDSIG_FILTER2.equals(algorithm)
743                     || WSConstants.NS_XMLDSIG_ENVELOPED_SIGNATURE.equals(algorithm)
744                     || WSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm)
745                     || WSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm))) {
746                     bspEnforcer.handleBSPRule(BSPRule.R5423);
747                 }
748                 if (i == (reference.getTransforms().size() - 1)
749                     && !(WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm)
750                         || STRTransform.TRANSFORM_URI.equals(algorithm)
751                         || WSConstants.SWA_ATTACHMENT_COMPLETE_SIG_TRANS.equals(algorithm)
752                         || WSConstants.SWA_ATTACHMENT_CONTENT_SIG_TRANS.equals(algorithm))) {
753                     bspEnforcer.handleBSPRule(BSPRule.R5412);
754                 }
755 
756                 if (WSConstants.C14N_EXCL_OMIT_COMMENTS.equals(algorithm)) {
757                     parameterSpec = transform.getParameterSpec();
758                     if (parameterSpec != null && !(parameterSpec instanceof ExcC14NParameterSpec)) {
759                         bspEnforcer.handleBSPRule(BSPRule.R5407);
760                     }
761                 }
762             }
763         }
764     }
765 
766 }