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.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   * UsernameToken according to WS Security specifications, UsernameToken profile.
52   *
53   * Enhanced to support digest password type for username token signature
54   * Enhanced to support passwordless usernametokens as allowed by spec.
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       * Constructs a <code>UsernameToken</code> object and parses the
81       * <code>wsse:UsernameToken</code> element to initialize it.
82       *
83       * @param elem the <code>wsse:UsernameToken</code> element that contains
84       *             the UsernameToken data
85       * @param allowNamespaceQualifiedPasswordTypes whether to allow (wsse)
86       *        namespace qualified password types or not (for interop with WCF)
87       * @param bspEnforcer a BSPEnforcer instance to enforce BSP rules
88       * @throws WSSecurityException
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             // If the UsernameToken is to be used for key derivation, the (1.1)
140             // spec says that it cannot contain a password, and it must contain
141             // an Iteration element
142             //
143             throw new WSSecurityException(
144                 WSSecurityException.ErrorCode.INVALID_SECURITY_TOKEN,
145                 "badUsernameToken",
146                 new Object[] {"Password is missing"}
147             );
148         }
149 
150         // Guard against a malicious user sending a bogus iteration value
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      * Constructs a <code>UsernameToken</code> object according to the defined
217      * parameters. <p/> This constructs set the password encoding to
218      * {@link WSConstants#PASSWORD_DIGEST}
219      *
220      * @param doc the SOAP envelope as <code>Document</code>
221      */
222     public UsernameToken(boolean milliseconds, Document doc) {
223         this(milliseconds, doc, WSConstants.PASSWORD_DIGEST);
224     }
225 
226     /**
227      * Constructs a <code>UsernameToken</code> object according to the defined
228      * parameters.
229      *
230      * @param doc the SOAP envelope as <code>Document</code>
231      * @param pwType the required password encoding, either
232      *               {@link WSConstants#PASSWORD_DIGEST} or
233      *               {@link WSConstants#PASSWORD_TEXT} or
234      *               {@link WSConstants#PW_NONE} <code>null</code> if no
235      *               password required
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      * Add the WSSE Namespace to this UT. The namespace is not added by default for
268      * efficiency purposes.
269      */
270     public void addWSSENamespace() {
271         XMLUtils.setNamespace(element, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);
272     }
273 
274     /**
275      * Add the WSU Namespace to this UT. The namespace is not added by default for
276      * efficiency purposes.
277      */
278     public void addWSUNamespace() {
279         element.setAttributeNS(XMLUtils.XMLNS_NS, "xmlns:" + WSConstants.WSU_PREFIX, WSConstants.WSU_NS);
280     }
281 
282     /**
283      * Creates and adds a Nonce element to this UsernameToken
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      * Creates and adds a Created element to this UsernameToken
304      */
305     public void addCreated(boolean milliseconds, Document doc) {
306         addCreated(milliseconds, new WSCurrentTimeSource(), doc);
307     }
308 
309     /**
310      * Creates and adds a Created element to this UsernameToken
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      * Adds a Salt element to this UsernameToken.
329      *
330      * @param doc The Document for the UsernameToken
331      * @param saltValue The salt to add.
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      * Creates and adds a Iteration element to this UsernameToken
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      * Get the user name.
360      *
361      * @return the data from the user name element.
362      */
363     public String getName() {
364         return XMLUtils.getElementText(elementUsername);
365     }
366 
367     /**
368      * Set the user name.
369      *
370      * @param name sets a text node containing the use name into the user name
371      *             element.
372      */
373     public void setName(String name) {
374         Text node = getFirstNode(elementUsername);
375         node.setData(name);
376     }
377 
378     /**
379      * Get the nonce.
380      *
381      * @return the data from the nonce element.
382      */
383     public String getNonce() {
384         return XMLUtils.getElementText(elementNonce);
385     }
386 
387     /**
388      * Get the created timestamp.
389      *
390      * @return the data from the created time element.
391      */
392     public String getCreated() {
393         return XMLUtils.getElementText(elementCreated);
394     }
395 
396     /**
397      * Return the Created Element as a Date object
398      * @return the Created Date
399      */
400     public Instant getCreatedDate() {
401         return created;
402     }
403 
404     /**
405      * Gets the password string. This is the password as it is in the password
406      * element of a username token. Thus it can be either plain text or the
407      * password digest value.
408      *
409      * @return the password string or <code>null</code> if no such node exists.
410      */
411     public String getPassword() {
412         String password = XMLUtils.getElementText(elementPassword);
413         // See WSS-219
414         if (password == null && elementPassword != null) {
415             return "";
416         }
417         return password;
418     }
419 
420     /**
421      * Return true if this UsernameToken contains a Password element
422      */
423     public boolean containsPasswordElement() {
424         return elementPassword != null;
425     }
426 
427     /**
428      * Get the Salt value of this UsernameToken.
429      *
430      * @return Returns the binary Salt value or <code>null</code> if no Salt
431      *         value is available in the username token.
432      * @throws WSSecurityException
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      * Get the Iteration value of this UsernameToken.
444      *
445      * @return Returns the Iteration value. If no Iteration was specified in the
446      *         username token the default value according to the specification
447      *         is returned.
448      */
449     public int getIteration() {
450         return iteration;
451     }
452 
453     /**
454      * Get the hashed indicator. If the indicator is <code>true> the password of the
455      * <code>UsernameToken</code> was encoded using {@link WSConstants#PASSWORD_DIGEST}
456      *
457      * @return the hashed indicator.
458      */
459     public boolean isHashed() {
460         return hashed;
461     }
462 
463     /**
464      * @return Returns the passwordType.
465      */
466     public String getPasswordType() {
467         return passwordType;
468     }
469 
470     /**
471      * Sets the password string. This function sets the password in the
472      * <code>UsernameToken</code> either as plain text or encodes the password
473      * according to the WS Security specifications, UsernameToken profile, into
474      * a password digest.
475      *
476      * @param pwd the password to use
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                 // Ignore setting the password.
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      * @param passwordsAreEncoded whether passwords are encoded
511      */
512     public void setPasswordsAreEncoded(boolean passwordsAreEncoded) {
513         this.passwordsAreEncoded = passwordsAreEncoded;
514     }
515 
516     /**
517      * @return whether passwords are encoded
518      */
519     public boolean getPasswordsAreEncoded() {
520         return passwordsAreEncoded;
521     }
522 
523     /**
524      * Returns the first text node of an element.
525      *
526      * @param e the element to get the node from
527      * @return the first text node or <code>null</code> if node is null or is
528      *         not a text node
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      * Returns the dom element of this <code>UsernameToken</code> object.
537      *
538      * @return the <code>wsse:UsernameToken</code> element
539      */
540     public Element getElement() {
541         return element;
542     }
543 
544     /**
545      * Returns the string representation of the token.
546      *
547      * @return a XML string representation
548      */
549     public String toString() {
550         return DOM2Writer.nodeToString(element);
551     }
552 
553     /**
554      * Gets the id.
555      *
556      * @return the value of the <code>wsu:Id</code> attribute of this username
557      *         token
558      */
559     public String getID() {
560         return element.getAttributeNS(WSConstants.WSU_NS, "Id");
561     }
562 
563     /**
564      * Set the id of this username token.
565      *
566      * @param id
567      *            the value for the <code>wsu:Id</code> attribute of this
568      *            username token
569      */
570     public void setID(String id) {
571         element.setAttributeNS(WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":Id", id);
572     }
573 
574     /**
575      * This method gets a derived key as defined in WSS Username Token Profile.
576      *
577      * @param rawPassword The raw password to use to derive the key
578      * @return Returns the derived key as a byte array
579      * @throws WSSecurityException
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             // We must have a salt element to use this token for a derived key
589             bspEnforcer.handleBSPRule(BSPRule.R4217);
590         }
591         if (elementIteration == null) {
592             // we must have an iteration element to use this token for a derived key
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      * Return whether the UsernameToken represented by this class is to be used
619      * for key derivation as per the UsernameToken Profile 1.1. It does this by
620      * checking that the username token has salt and iteration values.
621      *
622      * @throws WSSecurityException
623      */
624     public boolean isDerivedKey() throws WSSecurityException {
625         return elementSalt != null && elementIteration != null;
626     }
627 
628     /**
629      * Create a WSUsernameTokenPrincipal from this UsernameToken object
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      * Return true if the "Created" value is before the current time minus the timeToLive
645      * argument, and if the Created value is not "in the future".
646      *
647      * @param timeToLive the value in seconds for the validity of the Created time
648      * @param futureTimeToLive the value in seconds for the future validity of the Created time
649      * @return true if the UsernameToken is before (now-timeToLive), false otherwise
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      * A method to check that the UsernameToken is compliant with the BSP spec.
738      * @throws WSSecurityException
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         // We can only have one password element
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         // We must have a password type
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         // We can only have one created element
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         // We can only have one nonce element
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             // Encoding Type must be equal to Base64Binary
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 }