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 dk.sosi.seal.transform.internal;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.ws.security.WSConstants;
25  import org.apache.ws.security.WSDocInfo;
26  import org.apache.ws.security.WSPasswordCallback;
27  import org.apache.ws.security.WSSecurityException;
28  import org.apache.ws.security.components.crypto.Crypto;
29  import org.apache.ws.security.message.token.BinarySecurity;
30  import org.apache.ws.security.message.token.Reference;
31  import org.apache.ws.security.message.token.X509Security;
32  import org.apache.ws.security.util.Base64;
33  import org.apache.ws.security.util.DOM2Writer;
34  import org.apache.ws.security.util.WSSecurityUtil;
35  import org.apache.xml.security.exceptions.XMLSecurityException;
36  import org.apache.xml.security.keys.content.X509Data;
37  import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
38  import org.apache.xml.security.utils.Constants;
39  import org.w3c.dom.*;
40  
41  import javax.security.auth.callback.Callback;
42  import javax.security.auth.callback.CallbackHandler;
43  import java.security.MessageDigest;
44  import java.security.NoSuchAlgorithmException;
45  import java.security.cert.CertificateEncodingException;
46  import java.security.cert.X509Certificate;
47  
48  /**
49   * Security Token Reference.
50   *
51   * @author Davanum Srinivas (dims@yahoo.com).
52   */
53  public class SecurityTokenReference {
54      private static Log log =
55              LogFactory.getLog(SecurityTokenReference.class.getName());
56      public static final String SECURITY_TOKEN_REFERENCE = "SecurityTokenReference";
57      public static final String KEY_NAME = "KeyName";
58      public static final String SKI_URI = 
59          WSConstants.X509TOKEN_NS + "#X509SubjectKeyIdentifier";
60      public static final String THUMB_URI = 
61          WSConstants.SOAPMESSAGE_NS11 + "#" + WSConstants.THUMBPRINT;
62      public static final String SAML_ID_URI = 
63          WSConstants.SAMLTOKEN_NS + "#" + WSConstants.SAML_ASSERTION_ID;
64      public static final String ENC_KEY_SHA1_URI = 
65          WSConstants.SOAPMESSAGE_NS11 + "#" + WSConstants.ENC_KEY_SHA1_URI;
66      protected Element element = null;
67      private XMLX509IssuerSerial issuerSerial = null;
68      private byte[] skiBytes = null;
69      private static boolean doDebug = false;
70  
71      /**
72       * Constructor.
73       *
74       * @param elem TODO
75       * @throws WSSecurityException
76       */
77      public SecurityTokenReference(Element elem) throws WSSecurityException {
78          doDebug = log.isDebugEnabled();
79          this.element = elem;
80          boolean goodElement = false;
81          if (SECURITY_TOKEN_REFERENCE.equals(element.getLocalName())) {
82              goodElement = WSConstants.WSSE_NS.equals(element.getNamespaceURI());
83  //        } else if (KEY_NAME.equals(element.getLocalName())) {
84  //            goodElement = WSConstants.SIG_NS.equals(element.getNamespaceURI());
85          }
86          if (!goodElement) {
87              throw new WSSecurityException(WSSecurityException.FAILURE, "badElement", null);
88          }
89      }
90  
91      /**
92       * Constructor.
93       *
94       * @param doc TODO
95       */
96      public SecurityTokenReference(Document doc) {
97          doDebug = log.isDebugEnabled();
98          this.element =
99                  doc.createElementNS(WSConstants.WSSE_NS, "wsse:SecurityTokenReference");
100         WSSecurityUtil.setNamespace(this.element, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);        
101     }
102 
103     /**
104      * set the reference.
105      *
106      * @param ref
107      */
108     public void setReference(Reference ref) {
109         Element elem = getFirstElement();
110         if (elem != null) {
111             this.element.replaceChild(ref.getElement(), elem);
112         } else {
113             this.element.appendChild(ref.getElement());
114         }
115     }
116 
117     /**
118      * Gets the Reference.
119      *
120      * @return the <code>Reference</code> element contained in this
121      *         SecurityTokenReference
122      * @throws WSSecurityException
123      */
124     public Reference getReference() throws WSSecurityException {
125         Element elem = getFirstElement();
126         return new Reference(elem);
127     }
128 
129     /**
130      * Gets the signing token element, which maybe a <code>BinarySecurityToken
131      * </code> or a SAML token.
132      * 
133      * The method gets the URI attribute of the {@link Reference} contained in
134      * the {@link SecurityTokenReference} and tries to find the referenced
135      * Element in the document.
136      *
137      * @param doc the document that contains the binary security token
138      *            element. This could be different from the document
139      *            that contains the SecurityTokenReference (STR). See
140      *            STRTransform.derefenceBST() method
141      * @return Element containing the signing token, must be a BinarySecurityToken
142      * @throws WSSecurityException When either no <code>Reference</code> element, or the found
143      *                   reference contains no URI, or the referenced signing not found.
144      */
145     public Element getTokenElement(Document doc, WSDocInfo docInfo, CallbackHandler cb)
146         throws WSSecurityException {
147         Reference ref = getReference();
148         String uri = ref.getURI();
149         if (doDebug) {
150             log.debug("Token reference uri: " + uri);
151         }
152         if (uri == null) {
153             throw new WSSecurityException(
154                 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
155             );
156         }
157         
158         Element tokElement = findTokenElement(doc, docInfo, cb, uri, ref.getValueType());
159         
160         if (tokElement == null) {
161             throw new WSSecurityException(
162                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
163                 "noToken",
164                 new Object[]{uri}
165             );
166         }
167         return tokElement;
168     }
169     
170     
171     /**
172      * Gets the signing token element, which may be a <code>BinarySecurityToken
173      * </code> or a SAML token.
174      * 
175      * The method gets the value of the KeyIdentifier contained in
176      * the {@link SecurityTokenReference} and tries to find the referenced
177      * Element in the document.
178      *
179      * @param doc the document that contains the binary security token
180      *            element. This could be different from the document
181      *            that contains the SecurityTokenReference (STR). See
182      *            STRTransform.derefenceBST() method
183      * @return Element containing the signing token
184      */
185     public Element getKeyIdentifierTokenElement(
186         Document doc, WSDocInfo docInfo, CallbackHandler cb
187     ) throws WSSecurityException {
188         String value = getKeyIdentifierValue();
189         String type = getKeyIdentifierValueType();
190         if (doDebug) {
191             log.debug("Token reference uri: " + value);
192         }
193         if (value == null) {
194             throw new WSSecurityException(
195                 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
196             );
197         }
198         
199         Element tokElement = findTokenElement(doc, docInfo, cb, value, type);
200         
201         if (tokElement == null) {
202             throw new WSSecurityException(
203                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
204                 "noToken",
205                 new Object[]{value}
206             );
207         }
208         return tokElement;
209     }
210     
211     
212     private Element findTokenElement(
213         Document doc,
214         WSDocInfo docInfo,
215         CallbackHandler cb,
216         String uri,
217         String type
218     ) {
219         Element tokElement = null;
220         String id = uri;
221         if (id.charAt(0) == '#') {
222             id = id.substring(1);
223         }
224         //
225         // If the type is a SAMLAssertionID then find the SAML assertion - first check
226         // if it has been previously processed, else search the header for it
227         //
228         String assertionStr = WSConstants.WSS_SAML_NS + WSConstants.ASSERTION_LN;
229         if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type)
230             // Start added by Seal
231             || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(type)
232             // End added by Seal
233             || assertionStr.equals(type)) {
234             Element sa = docInfo.getAssertion();
235             if (sa != null) {
236                 String saID = sa.getAttribute("AssertionID");
237                 if (doDebug) {
238                     log.debug("SAML token ID: " + saID);
239                 }
240                 if (saID.equals(id)) {
241                     tokElement = sa;
242                 }
243             }
244             if (tokElement == null) {
245                 Node assertion = 
246                     WSSecurityUtil.findSAMLAssertionElementById(
247                         doc.getDocumentElement(),
248                         id
249                     );
250                 if (assertion != null) {
251                     tokElement = (Element)assertion;
252                 }
253             }
254         }
255         
256         // 
257         // Try to find a custom token
258         //
259         if (tokElement == null && cb != null && (WSConstants.WSC_SCT.equals(type) ||
260             WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type) || assertionStr.equals(type))) {
261             //try to find a custom token
262             WSPasswordCallback pwcb = 
263                 new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
264             try {
265                 cb.handle(new Callback[]{pwcb});
266                 Element assertionElem = pwcb.getCustomToken();
267                 if (assertionElem != null) {
268                     tokElement = (Element)doc.importNode(assertionElem, true);
269                 }
270             } catch (Exception e) {
271                 log.debug(e.getMessage(), e);
272                 // Consume this failure
273             }
274         }
275         
276         //
277         // Finally try to find the element by its Id
278         //
279         if (tokElement == null) {
280             tokElement = WSSecurityUtil.getElementByWsuId(doc, uri);
281             
282             // In some scenarios id is used rather than wsu:Id
283             if (tokElement == null) {
284                 tokElement = WSSecurityUtil.getElementByGenId(doc, uri);
285             }
286         }
287         
288         return tokElement;
289     }
290 
291 
292     /**
293      * Sets the KeyIdentifier Element as a X509 certificate.
294      * Takes a X509 certificate, converts its data into base 64 and inserts
295      * it into a <code>wsse:KeyIdentifier</code> element, which is placed
296      * in the <code>wsse:SecurityTokenReference</code> element.
297      *
298      * @param cert is the X509 certificate to be inserted as key identifier
299      */
300     public void setKeyIdentifier(X509Certificate cert)
301         throws WSSecurityException {
302         Document doc = this.element.getOwnerDocument();
303         byte data[] = null;
304         try {
305             data = cert.getEncoded();
306         } catch (CertificateEncodingException e) {
307             throw new WSSecurityException(
308                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError", null, e
309             );
310         }
311         Text text = doc.createTextNode(Base64.encode(data));
312         
313         createKeyIdentifier(doc, X509Security.X509_V3_TYPE, text, true);
314     }
315 
316     /**
317      * Sets the KeyIdentifier Element as a X509 Subject-Key-Identifier (SKI).
318      * Takes a X509 certificate, gets it SKI data, converts into base 64 and
319      * inserts it into a <code>wsse:KeyIdentifier</code> element, which is placed
320      * in the <code>wsse:SecurityTokenReference</code> element.
321      *
322      * @param cert   is the X509 certificate to get the SKI
323      * @param crypto is the Crypto implementation. Used to read SKI info bytes from certificate
324      */
325     public void setKeyIdentifierSKI(X509Certificate cert, Crypto crypto)
326         throws WSSecurityException {
327         //
328         // As per the 1.1 specification, SKI can only be used for a V3 certificate
329         //
330         if (cert.getVersion() != 3) {
331             throw new WSSecurityException(
332                 WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
333                 "invalidCertForSKI",
334                 new Object[]{new Integer(cert.getVersion())}
335             );
336         }
337         
338         Document doc = this.element.getOwnerDocument();
339         byte data[] = crypto.getSKIBytesFromCert(cert);
340         
341         org.w3c.dom.Text text = doc.createTextNode(Base64.encode(data));
342         createKeyIdentifier(doc, SKI_URI, text, true);        
343     }
344 
345     /**
346      * Sets the KeyIdentifier Element as a Thumbprint.
347      * 
348      * Takes a X509 certificate, computes its thumbprint using SHA-1, converts
349      * into base 64 and inserts it into a <code>wsse:KeyIdentifier</code>
350      * element, which is placed in the <code>wsse:SecurityTokenReference</code>
351      * element.
352      * 
353      * @param cert is the X509 certificate to get the thumbprint
354      */
355     public void setKeyIdentifierThumb(X509Certificate cert) throws WSSecurityException {
356         Document doc = this.element.getOwnerDocument();
357         MessageDigest sha = null;
358         try {
359             sha = MessageDigest.getInstance("SHA-1");
360         } catch (NoSuchAlgorithmException e1) {
361             throw new WSSecurityException(
362                 WSSecurityException.FAILURE, "noSHA1availabe", null, e1
363             );
364         }
365         sha.reset();
366         try {
367             sha.update(cert.getEncoded());
368         } catch (CertificateEncodingException e1) {
369             throw new WSSecurityException(
370                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError", null, e1
371             );
372         }
373         byte[] data = sha.digest();
374 
375         org.w3c.dom.Text text = doc.createTextNode(Base64.encode(data));
376         createKeyIdentifier(doc, THUMB_URI, text, true);
377     }
378     
379 
380     public void setKeyIdentifierEncKeySHA1(String value) throws WSSecurityException {
381         Document doc = this.element.getOwnerDocument();
382         org.w3c.dom.Text text = doc.createTextNode(value);
383         createKeyIdentifier(doc, ENC_KEY_SHA1_URI, text, true);
384     }
385     
386     public void setSAMLKeyIdentifier(String keyIdVal) throws WSSecurityException {
387         Document doc = this.element.getOwnerDocument();
388         createKeyIdentifier(doc, SAML_ID_URI, doc.createTextNode(keyIdVal), false);
389     }
390     public void setKeyIdentifier(String valueType, String keyIdVal) throws WSSecurityException {
391         Document doc = this.element.getOwnerDocument();
392         createKeyIdentifier(doc, valueType, doc.createTextNode(keyIdVal), false);
393     }
394 
395     private void createKeyIdentifier(Document doc, String uri, Node node, boolean base64) {
396         Element keyId = doc.createElementNS(WSConstants.WSSE_NS, "wsse:KeyIdentifier");
397         keyId.setAttributeNS(null, "ValueType", uri);
398         if (base64) {
399             keyId.setAttributeNS(null, "EncodingType", BinarySecurity.BASE64_ENCODING);
400         }
401 
402         keyId.appendChild(node);
403         Element elem = getFirstElement();
404         if (elem != null) {
405             this.element.replaceChild(keyId, elem);
406         } else {
407             this.element.appendChild(keyId);
408         }
409     }
410 
411     
412     /**
413      * get the first child element.
414      *
415      * @return the first <code>Element</code> child node
416      */
417     public Element getFirstElement() {
418         for (Node currentChild = this.element.getFirstChild();
419              currentChild != null;
420              currentChild = currentChild.getNextSibling()
421         ) {
422             if (currentChild instanceof Element) {
423                 return (Element) currentChild;
424             }
425         }
426         return null;
427     }
428 
429     /**
430      * Gets the KeyIdentifier.
431      *
432      * @return the the X509 certificate or zero if a unknown key identifier
433      *         type was detected.
434      */
435     public X509Certificate[] getKeyIdentifier(Crypto crypto) throws WSSecurityException {
436         Element elem = getFirstElement();
437         String value = elem.getAttribute("ValueType");
438         String alias = null;
439 
440         if (X509Security.X509_V3_TYPE.equals(value)) {
441             X509Security token = new X509Security(elem);
442             if (token != null) {
443                 X509Certificate cert = token.getX509Certificate(crypto);
444                 X509Certificate[] certs = new X509Certificate[1];
445                 certs[0] = cert;
446                 return certs;
447             }
448         } else if (SKI_URI.equals(value)) {
449             alias = getX509SKIAlias(crypto);
450         } else if (THUMB_URI.equals(value)) {
451             Node node = getFirstElement().getFirstChild();
452             if (node == null) {
453                 return null;
454             }
455             if (node.getNodeType() == Node.TEXT_NODE) {
456                 byte[] thumb = Base64.decode(((Text) node).getData());
457                 alias = crypto.getAliasForX509CertThumb(thumb);
458             }
459         }
460         
461         if (alias != null) {
462             return crypto.getCertificates(alias);
463         }
464         return null;
465     }
466     
467     public String getKeyIdentifierValue() {
468         if (containsKeyIdentifier()) {
469             Node node = getFirstElement().getFirstChild();
470             if (node == null) {
471                 return null;
472             }
473             if (node.getNodeType() == Node.TEXT_NODE) {
474                 return ((Text) node).getData();
475             }
476         } 
477         return null;
478     }
479     
480     public String getKeyIdentifierValueType() {
481         if (containsKeyIdentifier()) {
482             Element elem = getFirstElement();
483             return elem.getAttribute("ValueType");
484         } 
485         return null;
486     }
487     
488 
489     public String getX509SKIAlias(Crypto crypto) throws WSSecurityException {
490         if (skiBytes == null) {
491             skiBytes = getSKIBytes();
492             if (skiBytes == null) {
493                 return null;
494             }
495         }
496         String alias = crypto.getAliasForX509Cert(skiBytes);
497         if (doDebug) {
498             log.info("X509 SKI alias: " + alias);
499         }
500         return alias;
501     }
502 
503     public byte[] getSKIBytes() {
504         if (skiBytes != null) {
505             return skiBytes;
506         }
507         Node node = getFirstElement().getFirstChild();
508         if (node == null) {
509             return null;
510         }
511         if (node.getNodeType() == Node.TEXT_NODE) {
512             try {
513                 skiBytes = Base64.decode(((Text) node).getData());
514             } catch (WSSecurityException e) {
515                 return null;
516             }
517         }
518         return skiBytes;
519     }
520 
521 
522     /**
523      * Sets the X509 IssuerSerial data.
524      *
525      * @param ref the {@link XMLX509IssuerSerial} to put into this
526      *            SecurityTokenReference
527      */
528     public void setX509IssuerSerial(X509Data ref) {
529         Element elem = getFirstElement();
530         if (elem != null) {
531             this.element.replaceChild(ref.getElement(), elem);
532         } else {
533             this.element.appendChild(ref.getElement());
534         }
535     }
536 
537     /**
538      * Gets the certificate identified with X509 issuerSerial data.
539      * This method first tries to get the embedded certificate.
540      * If this fails it checks if the certificate is  in the
541      * keystore.
542      *
543      * @return a certificate array or null if nothing found
544      */
545     public X509Certificate[] getX509IssuerSerial(Crypto crypto) throws WSSecurityException {
546         String alias = getX509IssuerSerialAlias(crypto);
547         if (alias != null) {
548             return crypto.getCertificates(alias);
549         }
550         return null;
551     }
552 
553     /**
554      * Gets the alias name of the certificate identified with X509 issuerSerial data.
555      * The keystore identifies the certificate and the key with this alias name.
556      *
557      * @return the alias name for the certificate or null if nothing found
558      */
559     public String getX509IssuerSerialAlias(Crypto crypto) throws WSSecurityException {
560         if (issuerSerial == null) {
561             issuerSerial = getIssuerSerial();
562             if (issuerSerial == null) {
563                 return null;
564             }
565         }
566 
567         String alias = 
568             crypto.getAliasForX509Cert(issuerSerial.getIssuerName(), issuerSerial.getSerialNumber());
569         if (doDebug) {
570             log.info("X509IssuerSerial alias: " + alias);
571         }
572         return alias;
573     }
574 
575     private XMLX509IssuerSerial getIssuerSerial() throws WSSecurityException {
576         if (issuerSerial != null) {
577             return issuerSerial;
578         }
579         Element elem = getFirstElement();
580         if (elem == null) {
581             return null;
582         }
583         try {
584             if (Constants._TAG_X509DATA.equals(elem.getLocalName())) {
585                 elem = 
586                     (Element)WSSecurityUtil.findElement(
587                         elem, Constants._TAG_X509ISSUERSERIAL, Constants.SignatureSpecNS
588                     );
589             }
590             issuerSerial = new XMLX509IssuerSerial(elem, "");
591         } catch (XMLSecurityException e) {
592             throw new WSSecurityException(
593                 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
594                 "noToken",
595                 new Object[]{"Issuer/Serial data element missing"},
596                 e
597             );
598         }
599         return issuerSerial;
600     }
601 
602     /**
603      * Method containsReference
604      *
605      * @return true if the <code>SecurtityTokenReference</code> contains
606      *         a <code>wsse:Reference</code> element
607      */
608     public boolean containsReference() {
609         return this.lengthReference() > 0;
610     }
611 
612     /**
613      * Method lengthReference.
614      *
615      * @return number of <code>wsse:Reference</code> elements in
616      *         the <code>SecurtityTokenReference</code>
617      */
618     public int lengthReference() {
619         return this.length(WSConstants.WSSE_NS, "Reference");
620     }
621 
622     /**
623      * Method containsX509IssuerSerial
624      *
625      * @return true if the <code>SecurtityTokenReference</code> contains
626      *         a <code>ds:IssuerSerial</code> element
627      */
628     public boolean containsX509IssuerSerial() {
629         return this.lengthX509IssuerSerial() > 0;
630     }
631 
632     /**
633      * Method containsX509Data
634      *
635      * @return true if the <code>SecurtityTokenReference</code> contains
636      *         a <code>ds:X509Data</code> element
637      */
638     public boolean containsX509Data() {
639         return this.lengthX509Data() > 0;
640     }
641     
642     /**
643      * Method lengthX509IssuerSerial.
644      *
645      * @return number of <code>ds:IssuerSerial</code> elements in
646      *         the <code>SecurtityTokenReference</code>
647      */
648     public int lengthX509IssuerSerial() {
649         return this.length(WSConstants.SIG_NS, Constants._TAG_X509ISSUERSERIAL);
650     }
651 
652     /**
653      * Method lengthX509Data.
654      *
655      * @return number of <code>ds:IssuerSerial</code> elements in
656      *         the <code>SecurtityTokenReference</code>
657      */
658     public int lengthX509Data() {
659         return this.length(WSConstants.SIG_NS, Constants._TAG_X509DATA);
660     }
661     
662     /**
663      * Method containsKeyIdentifier.
664      *
665      * @return true if the <code>SecurtityTokenReference</code> contains
666      *         a <code>wsse:KeyIdentifier</code> element
667      */
668     public boolean containsKeyIdentifier() {
669         return this.lengthKeyIdentifier() > 0;
670     }
671 
672     /**
673      * Method lengthKeyIdentifier.
674      *
675      * @return number of <code>wsse:KeyIdentifier</code> elements in
676      *         the <code>SecurtityTokenReference</code>
677      */
678     public int lengthKeyIdentifier() {
679         return this.length(WSConstants.WSSE_NS, "KeyIdentifier");
680     }
681 
682     /**
683      * Method length.
684      *
685      * @param namespace
686      * @param localname
687      * @return number of elements with matching localname and namespace
688      */
689     public int length(String namespace, String localname) {
690         NodeList childNodes = this.element.getChildNodes();
691         int result = 0;
692         for (int i = 0; i < childNodes.getLength(); i++) {
693             Node n = childNodes.item(i);
694             if (n.getNodeType() == Node.ELEMENT_NODE) {
695                 String ns = n.getNamespaceURI();
696                 String name = n.getLocalName();
697                 if ((((namespace != null) && namespace.equals(ns))
698                     || ((namespace == null) && (ns == null)))
699                     && (localname.equals(name))
700                 ) {
701                     result++;
702                 }
703             }
704         }
705         return result;
706     }
707 
708     /**
709      * get the dom element.
710      *
711      * @return TODO
712      */
713     public Element getElement() {
714         return this.element;
715     }
716 
717     /**
718      * set the id.
719      *
720      * @param id
721      */
722     public void setID(String id) {
723         String prefix =
724                 WSSecurityUtil.setNamespace(
725                     this.element, WSConstants.WSU_NS, WSConstants.WSU_PREFIX
726                 );
727         this.element.setAttributeNS(WSConstants.WSU_NS, prefix + ":Id", id);
728     }
729 
730     /**
731      * return the string representation.
732      *
733      * @return TODO
734      */
735     public String toString() {
736         return DOM2Writer.nodeToString(this.element);
737     }
738 }