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