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.message;
21  
22  import java.time.Instant;
23  import java.time.ZoneOffset;
24  import java.time.ZonedDateTime;
25  import java.util.ArrayList;
26  import java.util.Collections;
27  import java.util.List;
28  import java.util.Locale;
29  
30  import org.apache.wss4j.common.bsp.BSPEnforcer;
31  import org.apache.wss4j.common.bsp.BSPRule;
32  import org.apache.wss4j.common.ext.WSSecurityException;
33  import org.apache.wss4j.common.util.DateUtil;
34  import org.apache.wss4j.common.util.SOAPUtil;
35  import org.apache.wss4j.common.util.WSTimeSource;
36  import org.apache.wss4j.common.util.XMLUtils;
37  import org.apache.wss4j.dom.WSConstants;
38  
39  import org.apache.wss4j.dom.engine.WSSConfig;
40  import org.apache.wss4j.dom.engine.WSSecurityEngine;
41  import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
42  import org.apache.wss4j.dom.handler.RequestData;
43  import org.apache.wss4j.dom.handler.WSHandlerResult;
44  import org.apache.wss4j.dom.message.token.Timestamp;
45  import org.apache.wss4j.dom.validate.NoOpValidator;
46  
47  import org.junit.jupiter.api.Test;
48  import org.w3c.dom.Document;
49  import org.w3c.dom.Element;
50  
51  import static org.junit.jupiter.api.Assertions.assertNotNull;
52  import static org.junit.jupiter.api.Assertions.assertTrue;
53  import static org.junit.jupiter.api.Assertions.fail;
54  
55  /**
56   * WS-Security Test Case for Timestamps.
57   */
58  public class TimestampTest {
59      private static final org.slf4j.Logger LOG =
60          org.slf4j.LoggerFactory.getLogger(TimestampTest.class);
61  
62      /**
63       * This is a test for processing a valid Timestamp.
64       */
65      @Test
66      public void testValidTimestamp() throws Exception {
67  
68          Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
69          WSSecHeader secHeader = new WSSecHeader(doc);
70          secHeader.insertSecurityHeader();
71  
72          WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
73          timestamp.setTimeToLive(300);
74          Document createdDoc = timestamp.build();
75  
76          if (LOG.isDebugEnabled()) {
77              String outputString =
78                  XMLUtils.prettyDocumentToString(createdDoc);
79              LOG.debug(outputString);
80          }
81  
82          //
83          // Do some processing
84          //
85          WSHandlerResult wsResult = verify(createdDoc);
86          WSSecurityEngineResult actionResult =
87              wsResult.getActionResults().get(WSConstants.TS).get(0);
88          assertNotNull(actionResult);
89  
90          Timestamp receivedTimestamp =
91              (Timestamp)actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP);
92          assertNotNull(receivedTimestamp);
93  
94          Timestamp clone = new Timestamp(receivedTimestamp.getElement(), new BSPEnforcer(true));
95          assertTrue(clone.equals(receivedTimestamp));
96          assertTrue(clone.hashCode() == receivedTimestamp.hashCode());
97      }
98  
99  
100     /**
101      * This is a test for processing a valid Timestamp with no expires element
102      */
103     @Test
104     public void testValidTimestampNoExpires() throws Exception {
105 
106         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
107         WSSecHeader secHeader = new WSSecHeader(doc);
108         secHeader.insertSecurityHeader();
109 
110         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
111         timestamp.setTimeToLive(0);
112         Document createdDoc = timestamp.build();
113 
114         if (LOG.isDebugEnabled()) {
115             String outputString =
116                 XMLUtils.prettyDocumentToString(createdDoc);
117             LOG.debug(outputString);
118         }
119 
120         //
121         // Do some processing
122         //
123         WSHandlerResult wsResult = verify(createdDoc);
124         WSSecurityEngineResult actionResult =
125             wsResult.getActionResults().get(WSConstants.TS).get(0);
126         assertNotNull(actionResult);
127 
128         Timestamp receivedTimestamp =
129             (Timestamp)actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP);
130         assertNotNull(receivedTimestamp);
131     }
132 
133     @Test
134     public void testInvalidTimestampNoExpires() throws Exception {
135 
136         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
137         WSSecHeader secHeader = new WSSecHeader(doc);
138         secHeader.insertSecurityHeader();
139 
140         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
141         timestamp.setTimeToLive(0);
142         Document createdDoc = timestamp.build();
143 
144         if (LOG.isDebugEnabled()) {
145             String outputString =
146                 XMLUtils.prettyDocumentToString(createdDoc);
147             LOG.debug(outputString);
148         }
149 
150         //
151         // Do some processing
152         //
153         WSSecurityEngine secEngine = new WSSecurityEngine();
154         RequestData requestData = new RequestData();
155         requestData.setWssConfig(WSSConfig.getNewInstance());
156         requestData.setRequireTimestampExpires(true);
157         try {
158             secEngine.processSecurityHeader(doc, requestData);
159             fail("Failure expected on no Expires Element");
160         } catch (WSSecurityException ex) {
161             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.SECURITY_ERROR);
162         }
163 
164         requestData.setWssConfig(WSSConfig.getNewInstance());
165         requestData.setRequireTimestampExpires(false);
166         WSHandlerResult wsResult = secEngine.processSecurityHeader(doc, requestData);
167         WSSecurityEngineResult actionResult =
168             wsResult.getActionResults().get(WSConstants.TS).get(0);
169         assertNotNull(actionResult);
170 
171         Timestamp receivedTimestamp =
172             (Timestamp)actionResult.get(WSSecurityEngineResult.TAG_TIMESTAMP);
173         assertNotNull(receivedTimestamp);
174     }
175 
176 
177     /**
178      * This is a test for processing an expired Timestamp.
179      */
180     @Test
181     public void testExpiredTimestamp() throws Exception {
182 
183         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
184         WSSecHeader secHeader = new WSSecHeader(doc);
185         secHeader.insertSecurityHeader();
186 
187         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
188         timestamp.setTimeToLive(-1);
189         Document createdDoc = timestamp.build();
190 
191         if (LOG.isDebugEnabled()) {
192             String outputString =
193                 XMLUtils.prettyDocumentToString(createdDoc);
194             LOG.debug(outputString);
195         }
196 
197         try {
198             verify(createdDoc);
199             fail("Expected failure on an expired timestamp");
200         } catch (WSSecurityException ex) {
201             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
202         }
203     }
204 
205 
206     /**
207      * This is a test for processing an "old" Timestamp, i.e. one with a "Created" element that is
208      * out of date
209      */
210     @Test
211     public void testOldTimestamp() throws Exception {
212 
213         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
214         WSSecHeader secHeader = new WSSecHeader(doc);
215         secHeader.insertSecurityHeader();
216 
217         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
218         Document createdDoc = timestamp.build();
219 
220         if (LOG.isDebugEnabled()) {
221             String outputString =
222                 XMLUtils.prettyDocumentToString(createdDoc);
223             LOG.debug(outputString);
224         }
225 
226         //
227         // Do some processing
228         //
229         RequestData requestData = new RequestData();
230         requestData.setWssConfig(WSSConfig.getNewInstance());
231         requestData.setTimeStampTTL(-1);
232         try {
233             verify(createdDoc, requestData);
234             fail("The timestamp validation should have failed");
235         } catch (WSSecurityException ex) {
236             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
237         }
238     }
239 
240 
241     /**
242      * This is a test for processing an Timestamp where the "Created" element is in the (near)
243      * future. It should be accepted by default when it is created 30 seconds in the future,
244      * and then rejected once we configure "0 seconds" for future-time-to-live.
245      */
246     @Test
247     public void testNearFutureCreated() throws Exception {
248 
249         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
250         WSSecHeader secHeader = new WSSecHeader(doc);
251         secHeader.insertSecurityHeader();
252 
253         Element timestampElement =
254             doc.createElementNS(
255                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
256             );
257 
258         Element elementCreated =
259             doc.createElementNS(
260                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
261             );
262         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(30L);
263         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
264         timestampElement.appendChild(elementCreated);
265 
266         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
267 
268         if (LOG.isDebugEnabled()) {
269             String outputString =
270                 XMLUtils.prettyDocumentToString(doc);
271             LOG.debug(outputString);
272         }
273         //
274         // Do some processing
275         //
276         RequestData requestData = new RequestData();
277         requestData.setWssConfig(WSSConfig.getNewInstance());
278         requestData.setTimeStampFutureTTL(0);
279         try {
280             verify(doc, requestData);
281             fail("The timestamp validation should have failed");
282         } catch (WSSecurityException ex) {
283             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
284         }
285     }
286 
287     /**
288      * This is a test for processing an Timestamp where the "Created" element is in the future.
289      * A Timestamp that is 120 seconds in the future should be rejected by default.
290      */
291     @Test
292     public void testFutureCreated() throws Exception {
293 
294         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
295         WSSecHeader secHeader = new WSSecHeader(doc);
296         secHeader.insertSecurityHeader();
297 
298         Element timestampElement =
299             doc.createElementNS(
300                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
301             );
302 
303         Element elementCreated =
304             doc.createElementNS(
305                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
306             );
307         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(120L);
308         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
309         timestampElement.appendChild(elementCreated);
310 
311         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
312 
313         if (LOG.isDebugEnabled()) {
314             String outputString =
315                 XMLUtils.prettyDocumentToString(doc);
316             LOG.debug(outputString);
317         }
318         //
319         // Do some processing
320         //
321         try {
322             verify(doc);
323             fail("The timestamp validation should have failed");
324         } catch (WSSecurityException ex) {
325             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
326         }
327     }
328 
329 
330     /**
331      * This is a test for processing an Timestamp where the "Created" element is greater than
332      * the expiration time.
333      */
334     @Test
335     public void testExpiresBeforeCreated() throws Exception {
336 
337         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
338         WSSecHeader secHeader = new WSSecHeader(doc);
339         secHeader.insertSecurityHeader();
340 
341         Element timestampElement =
342             doc.createElementNS(
343                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
344             );
345 
346         Element elementCreated =
347             doc.createElementNS(
348                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
349             );
350         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
351         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
352         timestampElement.appendChild(elementCreated);
353 
354         Element elementExpires =
355             doc.createElementNS(
356                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.EXPIRES_LN
357             );
358         now = now.minusSeconds(300L);
359         elementExpires.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
360         timestampElement.appendChild(elementExpires);
361 
362         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
363 
364         if (LOG.isDebugEnabled()) {
365             String outputString =
366                 XMLUtils.prettyDocumentToString(doc);
367             LOG.debug(outputString);
368         }
369         //
370         // Do some processing
371         //
372         try {
373             verify(doc);
374             fail("The timestamp validation should have failed");
375         } catch (WSSecurityException ex) {
376             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
377         }
378     }
379 
380     /**
381      * This is a test for processing multiple Timestamps in the security header
382      */
383     @Test
384     public void testMultipleTimestamps() throws Exception {
385 
386         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
387         WSSecHeader secHeader = new WSSecHeader(doc);
388         secHeader.insertSecurityHeader();
389 
390         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
391         timestamp.setTimeToLive(300);
392         Document createdDoc = timestamp.build();
393 
394         timestamp = new WSSecTimestamp(secHeader);
395         timestamp.setTimeToLive(60);
396         createdDoc = timestamp.build();
397 
398         if (LOG.isDebugEnabled()) {
399             String outputString =
400                 XMLUtils.prettyDocumentToString(createdDoc);
401             LOG.debug(outputString);
402         }
403 
404         //
405         // Do some processing
406         //
407         try {
408             verify(createdDoc);
409             fail("Expected failure on multiple timestamps");
410         } catch (WSSecurityException ex) {
411             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
412         }
413 
414         verify(createdDoc, Collections.singletonList(BSPRule.R3227));
415     }
416 
417     /**
418      * This is a test for processing an Timestamp where it contains multiple "Created" elements.
419      * This Timestamp should be rejected.
420      */
421     @Test
422     public void testMultipleCreated() throws Exception {
423 
424         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
425         WSSecHeader secHeader = new WSSecHeader(doc);
426         secHeader.insertSecurityHeader();
427 
428         Element timestampElement =
429             doc.createElementNS(
430                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
431             );
432 
433         Element elementCreated =
434             doc.createElementNS(
435                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
436             );
437         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
438         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
439         timestampElement.appendChild(elementCreated);
440         timestampElement.appendChild(elementCreated.cloneNode(true));
441 
442         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
443 
444         if (LOG.isDebugEnabled()) {
445             String outputString =
446                 XMLUtils.prettyDocumentToString(doc);
447             LOG.debug(outputString);
448         }
449         //
450         // Do some processing
451         //
452         try {
453             verify(doc);
454             fail("The timestamp validation should have failed on multiple Created elements");
455         } catch (WSSecurityException ex) {
456             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
457         }
458 
459         verify(doc, Collections.singletonList(BSPRule.R3203));
460     }
461 
462     /**
463      * A "Created" element with no text content of its own must be rejected as an invalid
464      * Timestamp, not read as though its first child were a Text node.
465      */
466     @Test
467     public void testEmptyCreated() throws Exception {
468         Document doc = createTimestampWithCreatedContent(null);
469 
470         try {
471             verify(doc);
472             fail("The timestamp validation should have failed on an empty Created element");
473         } catch (WSSecurityException ex) {
474             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
475         }
476     }
477 
478     /**
479      * The first child of a "Created" element need not be a Text node - here it is a comment,
480      * which c14n excludes from the signed bytes in any case.
481      */
482     @Test
483     public void testCreatedWithLeadingComment() throws Exception {
484         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
485         Document doc =
486             createTimestampWithCreatedContent(DateUtil.getDateTimeFormatter(true).format(now), true);
487 
488         verify(doc);
489     }
490 
491     private Document createTimestampWithCreatedContent(String createdText) throws Exception {
492         return createTimestampWithCreatedContent(createdText, false);
493     }
494 
495     private Document createTimestampWithCreatedContent(
496         String createdText, boolean leadingComment
497     ) throws Exception {
498         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
499         WSSecHeader secHeader = new WSSecHeader(doc);
500         secHeader.insertSecurityHeader();
501 
502         Element timestampElement =
503             doc.createElementNS(
504                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
505             );
506 
507         Element elementCreated =
508             doc.createElementNS(
509                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
510             );
511         if (leadingComment) {
512             elementCreated.appendChild(doc.createComment("a comment"));
513         }
514         if (createdText != null) {
515             elementCreated.appendChild(doc.createTextNode(createdText));
516         }
517         timestampElement.appendChild(elementCreated);
518 
519         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
520 
521         if (LOG.isDebugEnabled()) {
522             LOG.debug(XMLUtils.prettyDocumentToString(doc));
523         }
524         return doc;
525     }
526 
527     /**
528      * This is a test for processing an Timestamp where it contains no "Created" element.
529      * This Timestamp should be rejected.
530      */
531     @Test
532     public void testNoCreated() throws Exception {
533 
534         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
535         WSSecHeader secHeader = new WSSecHeader(doc);
536         secHeader.insertSecurityHeader();
537 
538         Element timestampElement =
539             doc.createElementNS(
540                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
541             );
542 
543         Element elementExpires =
544             doc.createElementNS(
545                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.EXPIRES_LN
546             );
547         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(300L);
548         elementExpires.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
549         timestampElement.appendChild(elementExpires);
550 
551         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
552 
553         if (LOG.isDebugEnabled()) {
554             String outputString =
555                 XMLUtils.prettyDocumentToString(doc);
556             LOG.debug(outputString);
557         }
558         //
559         // Do some processing
560         //
561         try {
562             verify(doc);
563             fail("The timestamp validation should have failed on no Created element");
564         } catch (WSSecurityException ex) {
565             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
566         }
567 
568         List<BSPRule> rules = new ArrayList<>();
569         rules.add(BSPRule.R3203);
570         rules.add(BSPRule.R3221);
571         verify(doc, rules);
572     }
573 
574     /**
575      * This is a test for processing an Timestamp where it contains multiple "Expires" elements.
576      * This Timestamp should be rejected.
577      */
578     @Test
579     public void testMultipleExpires() throws Exception {
580 
581         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
582         WSSecHeader secHeader = new WSSecHeader(doc);
583         secHeader.insertSecurityHeader();
584 
585         Element timestampElement =
586             doc.createElementNS(
587                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
588             );
589 
590         Element elementCreated =
591             doc.createElementNS(
592                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
593             );
594         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
595         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
596         timestampElement.appendChild(elementCreated);
597 
598         Element elementExpires =
599             doc.createElementNS(
600                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.EXPIRES_LN
601             );
602         now = now.plusSeconds(300L);
603         elementExpires.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
604         timestampElement.appendChild(elementExpires);
605         timestampElement.appendChild(elementExpires.cloneNode(true));
606 
607         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
608 
609         if (LOG.isDebugEnabled()) {
610             String outputString =
611                 XMLUtils.prettyDocumentToString(doc);
612             LOG.debug(outputString);
613         }
614         //
615         // Do some processing
616         //
617         try {
618             verify(doc);
619             fail("The timestamp validation should have failed on multiple Expires elements");
620         } catch (WSSecurityException ex) {
621             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
622         }
623 
624         verify(doc, Collections.singletonList(BSPRule.R3224));
625     }
626 
627     /**
628      * This is a test for processing an Timestamp where it contains an "Expires" element before
629      * the Created element. This Timestamp should be rejected as per the BSP spec.
630      */
631     @Test
632     public void testExpiresInFrontOfCreated() throws Exception {
633 
634         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
635         WSSecHeader secHeader = new WSSecHeader(doc);
636         secHeader.insertSecurityHeader();
637 
638         Element timestampElement =
639             doc.createElementNS(
640                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
641             );
642 
643         Element elementCreated =
644             doc.createElementNS(
645                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.EXPIRES_LN
646             );
647         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(300L);
648         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
649         timestampElement.appendChild(elementCreated);
650 
651         Element elementExpires =
652             doc.createElementNS(
653                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
654             );
655         now = ZonedDateTime.now(ZoneOffset.UTC);
656         elementExpires.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
657         timestampElement.appendChild(elementExpires);
658 
659         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
660 
661         if (LOG.isDebugEnabled()) {
662             String outputString =
663                 XMLUtils.prettyDocumentToString(doc);
664             LOG.debug(outputString);
665         }
666         //
667         // Do some processing
668         //
669         try {
670             verify(doc);
671             fail("The timestamp validation should have failed");
672         } catch (WSSecurityException ex) {
673             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
674         }
675 
676         verify(doc, Collections.singletonList(BSPRule.R3221));
677     }
678 
679 
680     /**
681      * This is a test for processing an Timestamp where it contains a Created element with
682      * seconds > 60. This should be rejected as per the BSP spec.
683      */
684     @Test
685     public void testCreatedSeconds() throws Exception {
686 
687         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
688         WSSecHeader secHeader = new WSSecHeader(doc);
689         secHeader.insertSecurityHeader();
690 
691         Element timestampElement =
692             doc.createElementNS(
693                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
694             );
695 
696         Element elementCreated =
697             doc.createElementNS(
698                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
699             );
700         elementCreated.appendChild(doc.createTextNode("2011-02-08T13:13:84.535Z"));
701         timestampElement.appendChild(elementCreated);
702 
703         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
704 
705         if (LOG.isDebugEnabled()) {
706             String outputString =
707                 XMLUtils.prettyDocumentToString(doc);
708             LOG.debug(outputString);
709         }
710         //
711         // Do some processing - disable the validator to make sure that the Timestamp processor
712         // is rejecting the Timestamp
713         //
714         WSSConfig wssConfig = WSSConfig.getNewInstance();
715         wssConfig.setValidator(WSConstants.TIMESTAMP, new NoOpValidator());
716         try {
717             verify(doc, wssConfig, new ArrayList<>());
718             fail("The timestamp validation should have failed");
719         } catch (WSSecurityException ex) {
720             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
721         }
722     }
723 
724 
725     /**
726      * This is a test for processing an Timestamp where it contains a Created element with
727      * a ValueType. This should be rejected as per the BSP spec.
728      */
729     @Test
730     public void testCreatedValueType() throws Exception {
731 
732         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
733         WSSecHeader secHeader = new WSSecHeader(doc);
734         secHeader.insertSecurityHeader();
735 
736         Element timestampElement =
737             doc.createElementNS(
738                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
739             );
740 
741         Element elementCreated =
742             doc.createElementNS(
743                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
744             );
745         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
746         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
747         elementCreated.setAttributeNS(null, "ValueType", WSConstants.WSS_SAML_KI_VALUE_TYPE);
748         timestampElement.appendChild(elementCreated);
749 
750         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
751 
752         if (LOG.isDebugEnabled()) {
753             String outputString =
754                 XMLUtils.prettyDocumentToString(doc);
755             LOG.debug(outputString);
756         }
757         //
758         // Do some processing
759         //
760         try {
761             verify(doc);
762             fail("The timestamp validation should have failed");
763         } catch (WSSecurityException ex) {
764             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
765         }
766 
767         // Now it should pass...
768         WSSConfig wssConfig = WSSConfig.getNewInstance();
769         wssConfig.setValidator(WSConstants.TIMESTAMP, new NoOpValidator());
770         verify(doc, wssConfig, Collections.singletonList(BSPRule.R3225));
771     }
772 
773 
774 
775     /**
776      * This is a test for processing an Timestamp where it contains a CustomElement. This should
777      * be rejected as per the BSP spec.
778      */
779     @Test
780     public void testCustomElement() throws Exception {
781 
782         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
783         WSSecHeader secHeader = new WSSecHeader(doc);
784         secHeader.insertSecurityHeader();
785 
786         Element timestampElement =
787             doc.createElementNS(
788                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN
789             );
790 
791         Element elementCreated =
792             doc.createElementNS(
793                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
794             );
795         ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
796         elementCreated.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
797         timestampElement.appendChild(elementCreated);
798 
799         Element elementExpires =
800             doc.createElementNS(
801                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.EXPIRES_LN
802             );
803         now = now.plusSeconds(300L);
804         elementExpires.appendChild(doc.createTextNode(DateUtil.getDateTimeFormatter(true).format(now)));
805         timestampElement.appendChild(elementExpires);
806 
807         Element elementCustom =
808             doc.createElementNS(
809                 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + "Custom"
810             );
811         timestampElement.appendChild(elementCustom);
812 
813         secHeader.getSecurityHeaderElement().appendChild(timestampElement);
814 
815         if (LOG.isDebugEnabled()) {
816             String outputString =
817                 XMLUtils.prettyDocumentToString(doc);
818             LOG.debug(outputString);
819         }
820         //
821         // Do some processing
822         //
823         try {
824             verify(doc);
825             fail("The timestamp validation should have failed");
826         } catch (WSSecurityException ex) {
827             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY);
828         }
829 
830         // Now it should pass...
831         verify(doc, Collections.singletonList(BSPRule.R3222));
832     }
833 
834     /**
835      * This is a test to create a "Spoofed" Timestamp (see WSS-441)
836      */
837     @Test
838     public void testSpoofedTimestamp() throws Exception {
839 
840         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
841         WSSecHeader secHeader = new WSSecHeader(doc);
842         secHeader.insertSecurityHeader();
843 
844         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
845         timestamp.setTimeToLive(300);
846 
847         WSTimeSource spoofedTimeSource = new WSTimeSource() {
848 
849             public Instant now() {
850                 return Instant.now().minusSeconds(500L);
851             }
852 
853         };
854         timestamp.setWsTimeSource(spoofedTimeSource);
855 
856         Document createdDoc = timestamp.build();
857 
858         if (LOG.isDebugEnabled()) {
859             String outputString =
860                 XMLUtils.prettyDocumentToString(createdDoc);
861             LOG.debug(outputString);
862         }
863 
864         //
865         // Do some processing
866         //
867         try {
868             verify(createdDoc);
869             fail("Expected failure on an expired timestamp");
870         } catch (WSSecurityException ex) {
871             assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.MESSAGE_EXPIRED);
872         }
873     }
874 
875     @Test
876     public void testTimestampNoMilliseconds() throws Exception {
877 
878         Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
879         WSSecHeader secHeader = new WSSecHeader(doc);
880         secHeader.insertSecurityHeader();
881 
882         WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
883         timestamp.setPrecisionInMilliSeconds(false);
884         timestamp.setTimeToLive(300);
885         Document createdDoc = timestamp.build();
886 
887         if (LOG.isDebugEnabled()) {
888             String outputString =
889                 XMLUtils.prettyDocumentToString(createdDoc);
890             LOG.debug(outputString);
891         }
892 
893         //
894         // Do some processing
895         //
896         WSHandlerResult wsResult = verify(createdDoc);
897         WSSecurityEngineResult actionResult =
898             wsResult.getActionResults().get(WSConstants.TS).get(0);
899         assertNotNull(actionResult);
900     }
901 
902     @Test
903     public void testThaiLocaleVerification() throws Exception {
904 
905         Locale defaultLocale = Locale.getDefault();
906         try {
907             Locale.setDefault(new Locale("th", "TH"));
908 
909             Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG);
910             WSSecHeader secHeader = new WSSecHeader(doc);
911             secHeader.insertSecurityHeader();
912 
913             WSSecTimestamp timestamp = new WSSecTimestamp(secHeader);
914             timestamp.setTimeToLive(300);
915             Document createdDoc = timestamp.build();
916 
917             //
918             // Do some processing
919             //
920             WSHandlerResult wsResult = verify(createdDoc);
921             WSSecurityEngineResult actionResult =
922                 wsResult.getActionResults().get(WSConstants.TS).get(0);
923             assertNotNull(actionResult);
924         } finally {
925             Locale.setDefault(defaultLocale);
926         }
927     }
928 
929     /**
930      * Verifies the soap envelope
931      */
932     private WSHandlerResult verify(
933         Document doc
934     ) throws Exception {
935         WSSecurityEngine secEngine = new WSSecurityEngine();
936         RequestData requestData = new RequestData();
937         requestData.setWssConfig(WSSConfig.getNewInstance());
938         return secEngine.processSecurityHeader(doc, requestData);
939     }
940 
941     private WSHandlerResult verify(
942         Document doc, RequestData requestData
943     ) throws Exception {
944         WSSecurityEngine secEngine = new WSSecurityEngine();
945         return secEngine.processSecurityHeader(doc, requestData);
946     }
947 
948     /**
949      * Verifies the soap envelope
950      */
951     private WSHandlerResult verify(
952         Document doc, List<BSPRule> ignoredRules
953     ) throws Exception {
954         WSSecurityEngine secEngine = new WSSecurityEngine();
955         RequestData requestData = new RequestData();
956         requestData.setIgnoredBSPRules(ignoredRules);
957         return secEngine.processSecurityHeader(doc, requestData);
958     }
959 
960     /**
961      * Verifies the soap envelope
962      */
963     private WSHandlerResult verify(
964         Document doc, WSSConfig wssConfig, List<BSPRule> ignoredRules
965     ) throws Exception {
966         WSSecurityEngine secEngine = new WSSecurityEngine();
967         RequestData requestData = new RequestData();
968         requestData.setWssConfig(wssConfig);
969         requestData.setIgnoredBSPRules(ignoredRules);
970         return secEngine.processSecurityHeader(doc, requestData);
971     }
972 
973 
974 }