1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.wss4j.dom.message.token;
21
22 import java.security.Principal;
23 import java.time.Instant;
24 import java.time.ZoneOffset;
25 import java.time.ZonedDateTime;
26 import java.time.format.DateTimeFormatter;
27 import java.time.format.DateTimeParseException;
28 import java.util.Arrays;
29 import java.util.List;
30
31 import javax.xml.namespace.QName;
32
33 import org.apache.wss4j.common.bsp.BSPEnforcer;
34 import org.apache.wss4j.common.bsp.BSPRule;
35 import org.apache.wss4j.common.ext.WSSecurityException;
36 import org.apache.wss4j.common.principal.WSUsernameTokenPrincipalImpl;
37 import org.apache.wss4j.common.util.DOM2Writer;
38 import org.apache.wss4j.common.util.DateUtil;
39 import org.apache.wss4j.common.util.UsernameTokenUtil;
40 import org.apache.wss4j.common.util.WSCurrentTimeSource;
41 import org.apache.wss4j.common.util.WSTimeSource;
42 import org.apache.wss4j.common.util.XMLUtils;
43 import org.apache.wss4j.dom.WSConstants;
44 import org.apache.wss4j.dom.util.WSSecurityUtil;
45 import org.w3c.dom.Document;
46 import org.w3c.dom.Element;
47 import org.w3c.dom.Node;
48 import org.w3c.dom.Text;
49
50
51
52
53
54
55
56 public class UsernameToken {
57 public static final String BASE64_ENCODING = WSConstants.SOAPMESSAGE_NS + "#Base64Binary";
58 public static final String PASSWORD_TYPE = "passwordType";
59 public static final int DEFAULT_ITERATION = 1000;
60 public static final QName TOKEN =
61 new QName(WSConstants.WSSE_NS, WSConstants.USERNAME_TOKEN_LN);
62
63 private static final org.slf4j.Logger LOG =
64 org.slf4j.LoggerFactory.getLogger(UsernameToken.class);
65
66 private Element element;
67 private Element elementUsername;
68 private Element elementPassword;
69 private Element elementNonce;
70 private Element elementCreated;
71 private Element elementSalt;
72 private Element elementIteration;
73 private int iteration = DEFAULT_ITERATION;
74 private String passwordType;
75 private boolean hashed = true;
76 private boolean passwordsAreEncoded;
77 private Instant created;
78
79
80
81
82
83
84
85
86
87
88
89
90 public UsernameToken(
91 Element elem,
92 boolean allowNamespaceQualifiedPasswordTypes,
93 BSPEnforcer bspEnforcer
94 ) throws WSSecurityException {
95 element = elem;
96 QName el = new QName(element.getNamespaceURI(), element.getLocalName());
97 if (!el.equals(TOKEN)) {
98 throw new WSSecurityException(
99 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
100 "badElement",
101 new Object[] {TOKEN, el}
102 );
103 }
104 elementUsername =
105 XMLUtils.getDirectChildElement(
106 element, WSConstants.USERNAME_LN, WSConstants.WSSE_NS
107 );
108 elementPassword =
109 XMLUtils.getDirectChildElement(
110 element, WSConstants.PASSWORD_LN, WSConstants.WSSE_NS
111 );
112 elementNonce =
113 XMLUtils.getDirectChildElement(
114 element, WSConstants.NONCE_LN, WSConstants.WSSE_NS
115 );
116 elementCreated =
117 XMLUtils.getDirectChildElement(
118 element, WSConstants.CREATED_LN, WSConstants.WSU_NS
119 );
120 elementSalt =
121 XMLUtils.getDirectChildElement(
122 element, WSConstants.SALT_LN, WSConstants.WSSE11_NS
123 );
124 elementIteration =
125 XMLUtils.getDirectChildElement(
126 element, WSConstants.ITERATION_LN, WSConstants.WSSE11_NS
127 );
128 if (elementUsername == null) {
129 throw new WSSecurityException(
130 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
131 "badUsernameToken",
132 new Object[] {"Username is missing"}
133 );
134 }
135 checkBSPCompliance(bspEnforcer);
136 hashed = false;
137 if (elementSalt != null && (elementPassword != null || elementIteration == null)) {
138
139
140
141
142
143 throw new WSSecurityException(
144 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
145 "badUsernameToken",
146 new Object[] {"Password is missing"}
147 );
148 }
149
150
151 if (elementIteration != null) {
152 String iter = XMLUtils.getElementText(elementIteration);
153 if (iter != null) {
154 try {
155 iteration = Integer.parseInt(iter);
156 if (iteration < 0 || iteration > 10000) {
157 throw new WSSecurityException(
158 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
159 "badUsernameToken",
160 new Object[] {"Iteration is missing"}
161 );
162 }
163 } catch (NumberFormatException ex) {
164 throw new WSSecurityException(
165 WSSecurityException.ErrorCode.FAILURE, ex, "decoding.general"
166 );
167 }
168 }
169 }
170
171 if (elementPassword != null) {
172 if (elementPassword.hasAttributeNS(null, WSConstants.PASSWORD_TYPE_ATTR)) {
173 passwordType = elementPassword.getAttributeNS(null, WSConstants.PASSWORD_TYPE_ATTR);
174 } else if (elementPassword.hasAttributeNS(
175 WSConstants.WSSE_NS, WSConstants.PASSWORD_TYPE_ATTR)
176 ) {
177 if (allowNamespaceQualifiedPasswordTypes) {
178 passwordType =
179 elementPassword.getAttributeNS(
180 WSConstants.WSSE_NS, WSConstants.PASSWORD_TYPE_ATTR
181 );
182 } else {
183 throw new WSSecurityException(
184 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
185 "badUsernameToken",
186 new Object[] {"The Password Type is not allowed to be namespace qualified"}
187 );
188 }
189 }
190
191 }
192 if (WSConstants.PASSWORD_DIGEST.equals(passwordType)) {
193 hashed = true;
194 if (elementNonce == null || elementCreated == null) {
195 throw new WSSecurityException(
196 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
197 "badUsernameToken",
198 new Object[] {"Nonce or Created is missing"}
199 );
200 }
201 }
202
203 if (elementCreated != null) {
204 String createdString = getCreated();
205 if (createdString != null && createdString.length() != 0) {
206 try {
207 created = ZonedDateTime.parse(createdString).toInstant();
208 } catch (DateTimeParseException e) {
209 throw new WSSecurityException(WSSecurityException.ErrorCode.INVALID_SECURITY, e);
210 }
211 }
212 }
213 }
214
215
216
217
218
219
220
221
222 public UsernameToken(boolean milliseconds, Document doc) {
223 this(milliseconds, doc, WSConstants.PASSWORD_DIGEST);
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237 public UsernameToken(boolean milliseconds, Document doc, String pwType) {
238 this(milliseconds, doc, new WSCurrentTimeSource(), pwType);
239 }
240
241 public UsernameToken(boolean milliseconds, Document doc, WSTimeSource timeSource, String pwType) {
242 element =
243 doc.createElementNS(WSConstants.WSSE_NS, "wsse:" + WSConstants.USERNAME_TOKEN_LN);
244
245 elementUsername =
246 doc.createElementNS(WSConstants.WSSE_NS, "wsse:" + WSConstants.USERNAME_LN);
247 elementUsername.appendChild(doc.createTextNode(""));
248 element.appendChild(elementUsername);
249
250 if (pwType != null) {
251 elementPassword =
252 doc.createElementNS(WSConstants.WSSE_NS, "wsse:" + WSConstants.PASSWORD_LN);
253 elementPassword.appendChild(doc.createTextNode(""));
254 element.appendChild(elementPassword);
255
256 passwordType = pwType;
257 if (passwordType.equals(WSConstants.PASSWORD_DIGEST)) {
258 addNonce(doc);
259 addCreated(milliseconds, timeSource, doc);
260 } else {
261 hashed = false;
262 }
263 }
264 }
265
266
267
268
269
270 public void addWSSENamespace() {
271 XMLUtils.setNamespace(element, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);
272 }
273
274
275
276
277
278 public void addWSUNamespace() {
279 element.setAttributeNS(XMLUtils.XMLNS_NS, "xmlns:" + WSConstants.WSU_PREFIX, WSConstants.WSU_NS);
280 }
281
282
283
284
285 public void addNonce(Document doc) {
286 if (elementNonce != null) {
287 return;
288 }
289 byte[] nonceValue = null;
290 try {
291 nonceValue = UsernameTokenUtil.generateNonce(16);
292 } catch (WSSecurityException ex) {
293 LOG.debug(ex.getMessage(), ex);
294 return;
295 }
296 elementNonce = doc.createElementNS(WSConstants.WSSE_NS, "wsse:" + WSConstants.NONCE_LN);
297 elementNonce.appendChild(doc.createTextNode(org.apache.xml.security.utils.XMLUtils.encodeToString(nonceValue)));
298 elementNonce.setAttributeNS(null, "EncodingType", BASE64_ENCODING);
299 element.appendChild(elementNonce);
300 }
301
302
303
304
305 public void addCreated(boolean milliseconds, Document doc) {
306 addCreated(milliseconds, new WSCurrentTimeSource(), doc);
307 }
308
309
310
311
312 public void addCreated(boolean milliseconds, WSTimeSource timeSource, Document doc) {
313 if (elementCreated != null) {
314 return;
315 }
316 elementCreated =
317 doc.createElementNS(
318 WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN
319 );
320 Instant currentTime = timeSource.now();
321
322 DateTimeFormatter formatter = DateUtil.getDateTimeFormatter(milliseconds);
323 elementCreated.appendChild(doc.createTextNode(currentTime.atZone(ZoneOffset.UTC).format(formatter)));
324 element.appendChild(elementCreated);
325 }
326
327
328
329
330
331
332
333 public void addSalt(Document doc, byte[] saltValue) {
334 elementSalt =
335 doc.createElementNS(
336 WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX + ":" + WSConstants.SALT_LN
337 );
338 XMLUtils.setNamespace(element, WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX);
339 elementSalt.appendChild(doc.createTextNode(org.apache.xml.security.utils.XMLUtils.encodeToString(saltValue)));
340 element.appendChild(elementSalt);
341 }
342
343
344
345
346 public void addIteration(Document doc, int iteration) {
347 String text = "" + iteration;
348 elementIteration =
349 doc.createElementNS(
350 WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX + ":" + WSConstants.ITERATION_LN
351 );
352 XMLUtils.setNamespace(element, WSConstants.WSSE11_NS, WSConstants.WSSE11_PREFIX);
353 elementIteration.appendChild(doc.createTextNode(text));
354 element.appendChild(elementIteration);
355 this.iteration = iteration;
356 }
357
358
359
360
361
362
363 public String getName() {
364 return XMLUtils.getElementText(elementUsername);
365 }
366
367
368
369
370
371
372
373 public void setName(String name) {
374 Text node = getFirstNode(elementUsername);
375 node.setData(name);
376 }
377
378
379
380
381
382
383 public String getNonce() {
384 return XMLUtils.getElementText(elementNonce);
385 }
386
387
388
389
390
391
392 public String getCreated() {
393 return XMLUtils.getElementText(elementCreated);
394 }
395
396
397
398
399
400 public Instant getCreatedDate() {
401 return created;
402 }
403
404
405
406
407
408
409
410
411 public String getPassword() {
412 String password = XMLUtils.getElementText(elementPassword);
413
414 if (password == null && elementPassword != null) {
415 return "";
416 }
417 return password;
418 }
419
420
421
422
423 public boolean containsPasswordElement() {
424 return elementPassword != null;
425 }
426
427
428
429
430
431
432
433
434 public byte[] getSalt() throws WSSecurityException {
435 String salt = XMLUtils.getElementText(elementSalt);
436 if (salt != null) {
437 return org.apache.xml.security.utils.XMLUtils.decode(salt);
438 }
439 return new byte[0];
440 }
441
442
443
444
445
446
447
448
449 public int getIteration() {
450 return iteration;
451 }
452
453
454
455
456
457
458
459 public boolean isHashed() {
460 return hashed;
461 }
462
463
464
465
466 public String getPasswordType() {
467 return passwordType;
468 }
469
470
471
472
473
474
475
476
477
478 public void setPassword(String pwd) {
479 if (pwd == null) {
480 if (passwordType != null) {
481 throw new IllegalArgumentException("pwd == null but a password is needed");
482 } else {
483
484 return;
485 }
486 }
487
488 Text node = getFirstNode(elementPassword);
489 try {
490 if (hashed) {
491 byte[] decodedNonce = org.apache.xml.security.utils.XMLUtils.decode(getNonce());
492 if (passwordsAreEncoded) {
493 node.setData(UsernameTokenUtil.doPasswordDigest(decodedNonce, getCreated(),
494 org.apache.xml.security.utils.XMLUtils.decode(pwd)));
495 } else {
496 node.setData(UsernameTokenUtil.doPasswordDigest(decodedNonce, getCreated(), pwd));
497 }
498 } else {
499 node.setData(pwd);
500 }
501 if (passwordType != null) {
502 elementPassword.setAttributeNS(null, "Type", passwordType);
503 }
504 } catch (Exception e) {
505 LOG.debug(e.getMessage(), e);
506 }
507 }
508
509
510
511
512 public void setPasswordsAreEncoded(boolean passwordsAreEncoded) {
513 this.passwordsAreEncoded = passwordsAreEncoded;
514 }
515
516
517
518
519 public boolean getPasswordsAreEncoded() {
520 return passwordsAreEncoded;
521 }
522
523
524
525
526
527
528
529
530 private Text getFirstNode(Element e) {
531 Node node = e.getFirstChild();
532 return node != null && Node.TEXT_NODE == node.getNodeType() ? (Text) node : null;
533 }
534
535
536
537
538
539
540 public Element getElement() {
541 return element;
542 }
543
544
545
546
547
548
549 public String toString() {
550 return DOM2Writer.nodeToString(element);
551 }
552
553
554
555
556
557
558
559 public String getID() {
560 return element.getAttributeNS(WSConstants.WSU_NS, "Id");
561 }
562
563
564
565
566
567
568
569
570 public void setID(String id) {
571 element.setAttributeNS(WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":Id", id);
572 }
573
574
575
576
577
578
579
580
581 public byte[] getDerivedKey(BSPEnforcer bspEnforcer, String rawPassword) throws WSSecurityException {
582 if (rawPassword == null) {
583 LOG.warn("The raw password was null");
584 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);
585 }
586
587 if (elementSalt == null) {
588
589 bspEnforcer.handleBSPRule(BSPRule.R4217);
590 }
591 if (elementIteration == null) {
592
593 bspEnforcer.handleBSPRule(BSPRule.R4218);
594 } else {
595 String iter = XMLUtils.getElementText(elementIteration);
596 try {
597 if (iter == null || Integer.parseInt(iter) < 1000) {
598 bspEnforcer.handleBSPRule(BSPRule.R4218);
599 }
600 } catch (NumberFormatException ex) {
601 throw new WSSecurityException(
602 WSSecurityException.ErrorCode.FAILURE, ex, "decoding.general"
603 );
604 }
605 }
606
607 int iteration = getIteration();
608 byte[] salt = getSalt();
609 if (passwordsAreEncoded) {
610 return UsernameTokenUtil.generateDerivedKey(org.apache.xml.security.utils.XMLUtils.decode(rawPassword),
611 salt, iteration);
612 } else {
613 return UsernameTokenUtil.generateDerivedKey(rawPassword, salt, iteration);
614 }
615 }
616
617
618
619
620
621
622
623
624 public boolean isDerivedKey() throws WSSecurityException {
625 return elementSalt != null && elementIteration != null;
626 }
627
628
629
630
631 public Principal createPrincipal() throws WSSecurityException {
632 WSUsernameTokenPrincipalImpl principal =
633 new WSUsernameTokenPrincipalImpl(getName(), isHashed());
634 String nonce = getNonce();
635 if (nonce != null) {
636 principal.setNonce(org.apache.xml.security.utils.XMLUtils.decode(nonce));
637 }
638 principal.setPassword(getPassword());
639 principal.setCreatedTime(getCreated());
640 return principal;
641 }
642
643
644
645
646
647
648
649
650
651 public boolean verifyCreated(
652 int timeToLive,
653 int futureTimeToLive
654 ) {
655 return DateUtil.verifyCreated(created, timeToLive, futureTimeToLive);
656 }
657
658 @Override
659 public int hashCode() {
660 int result = 17;
661 String username = getName();
662 if (username != null) {
663 result = 31 * result + username.hashCode();
664 }
665 String password = getPassword();
666 if (password != null) {
667 result = 31 * result + password.hashCode();
668 }
669 String passwordType = getPasswordType();
670 if (passwordType != null) {
671 result = 31 * result + passwordType.hashCode();
672 }
673 String nonce = getNonce();
674 if (nonce != null) {
675 result = 31 * result + nonce.hashCode();
676 }
677 String created = getCreated();
678 if (created != null) {
679 result = 31 * result + created.hashCode();
680 }
681 try {
682 byte[] salt = getSalt();
683 if (salt != null && salt.length > 0) {
684 result = 31 * result + Arrays.hashCode(salt);
685 }
686 } catch (WSSecurityException ex) {
687 LOG.debug(ex.getMessage(), ex);
688 }
689 result = 31 * result + Integer.valueOf(getIteration()).hashCode();
690
691 return result;
692 }
693
694 @Override
695 public boolean equals(Object object) {
696 if (!(object instanceof UsernameToken)) {
697 return false;
698 }
699 UsernameToken usernameToken = (UsernameToken)object;
700 if (!compare(usernameToken.getName(), getName())) {
701 return false;
702 }
703 if (!compare(usernameToken.getPassword(), getPassword())) {
704 return false;
705 }
706 if (!compare(usernameToken.getPasswordType(), getPasswordType())) {
707 return false;
708 }
709 if (!compare(usernameToken.getNonce(), getNonce())) {
710 return false;
711 }
712 if (!compare(usernameToken.getCreated(), getCreated())) {
713 return false;
714 }
715 try {
716 byte[] salt = usernameToken.getSalt();
717 if (!Arrays.equals(salt, getSalt())) {
718 return false;
719 }
720 } catch (WSSecurityException ex) {
721 LOG.debug(ex.getMessage(), ex);
722 }
723 int iteration = usernameToken.getIteration();
724 return iteration == getIteration();
725 }
726
727 private boolean compare(String item1, String item2) {
728 if (item1 == null && item2 != null) {
729 return false;
730 } else if (item1 != null && !item1.equals(item2)) {
731 return false;
732 }
733 return true;
734 }
735
736
737
738
739
740 private void checkBSPCompliance(BSPEnforcer bspEnforcer) throws WSSecurityException {
741 List<Element> passwordElements =
742 WSSecurityUtil.getDirectChildElements(
743 element, WSConstants.PASSWORD_LN, WSConstants.WSSE_NS
744 );
745
746 if (passwordElements.size() > 1) {
747 LOG.debug("The Username Token had more than one password element");
748 bspEnforcer.handleBSPRule(BSPRule.R4222);
749 }
750
751
752 if (passwordElements.size() == 1) {
753 Element passwordChild = passwordElements.get(0);
754 String type = passwordChild.getAttributeNS(null, WSConstants.PASSWORD_TYPE_ATTR);
755 if (type == null || type.length() == 0) {
756 LOG.debug("The Username Token password does not have a Type attribute");
757 bspEnforcer.handleBSPRule(BSPRule.R4201);
758 }
759 }
760
761 List<Element> createdElements =
762 WSSecurityUtil.getDirectChildElements(
763 element, WSConstants.CREATED_LN, WSConstants.WSU_NS
764 );
765
766 if (createdElements.size() > 1) {
767 LOG.debug("The Username Token has more than one created element");
768 bspEnforcer.handleBSPRule(BSPRule.R4223);
769 }
770
771 List<Element> nonceElements =
772 WSSecurityUtil.getDirectChildElements(
773 element, WSConstants.NONCE_LN, WSConstants.WSSE_NS
774 );
775
776 if (nonceElements.size() > 1) {
777 LOG.debug("The Username Token has more than one nonce element");
778 bspEnforcer.handleBSPRule(BSPRule.R4225);
779 }
780
781 if (nonceElements.size() == 1) {
782 Element nonce = nonceElements.get(0);
783 String encodingType = nonce.getAttributeNS(null, "EncodingType");
784
785 if (encodingType == null || encodingType.length() == 0) {
786 bspEnforcer.handleBSPRule(BSPRule.R4220);
787 } else if (!WSConstants.BASE64_ENCODING.equals(encodingType)) {
788 LOG.debug("The Username Token's nonce element has a bad encoding type");
789 bspEnforcer.handleBSPRule(BSPRule.R4221);
790 }
791 }
792 }
793 }