KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xml > security > signature > Reference


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999 The Apache Software Foundation. All rights
6  * reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Apache Software Foundation (http://www.apache.org/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "<WebSig>" and "Apache Software Foundation" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 2001, Institute for
53  * Data Communications Systems, <http://www.nue.et-inf.uni-siegen.de/>.
54  * The development of this software was partly funded by the European
55  * Commission in the <WebSig> project in the ISIS Programme.
56  * For more information on the Apache Software Foundation, please see
57  * <http://www.apache.org/>.
58  */

59 package org.apache.xml.security.signature;
60
61
62 import java.io.*;
63 import java.math.BigInteger JavaDoc;
64 import java.util.*;
65 import javax.xml.parsers.DocumentBuilder JavaDoc;
66 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
67 import javax.xml.parsers.ParserConfigurationException JavaDoc;
68 import javax.xml.transform.TransformerException JavaDoc;
69
70 import org.w3c.dom.*;
71 import org.w3c.dom.traversal.NodeIterator;
72 import org.xml.sax.InputSource JavaDoc;
73 import org.xml.sax.SAXException JavaDoc;
74
75 import org.apache.xml.serialize.OutputFormat;
76 import org.apache.xml.serialize.Serializer;
77 import org.apache.xml.serialize.SerializerFactory;
78 import org.apache.xml.serialize.XMLSerializer;
79 import org.apache.xml.utils.PrefixResolverDefault;
80 import org.apache.xpath.NodeSet;
81 import org.apache.xpath.objects.XObject;
82 import org.apache.xpath.XPath;
83 import org.apache.xpath.XPathAPI;
84 import org.apache.xpath.XPathContext;
85
86 import org.apache.xml.security.algorithms.MessageDigestAlgorithm;
87 import org.apache.xml.security.c14n.*;
88 import org.apache.xml.security.exceptions.*;
89 import org.apache.xml.security.signature.*;
90 import org.apache.xml.security.transforms.*;
91 import org.apache.xml.security.transforms.params.XPathContainer;
92 import org.apache.xml.security.utils.*;
93 import org.apache.xml.security.utils.resolver.*;
94
95
96
97 /**
98  * Handles <code>&lt;ds:Reference&gt;</code> elements.
99  *
100  * This includes:
101  *
102  * Constuct a <CODE>ds:Reference</CODE> from an {@link org.w3c.dom.Element}.
103  *
104  * <p>Create a new reference</p>
105  * <pre>
106  * Document _doc;
107  * MessageDigestAlgorithm sha1 = MessageDigestAlgorithm.getInstance("http://#sha1");
108  * Reference ref = new Reference(new XMLSignatureInput(new FileInputStream("1.gif"),
109  * "http://localhost/1.gif",
110  * (Transforms) null, sha1);
111  * Element refElem = ref.toElement(_doc);
112  * </pre>
113  *
114  * <p>Verify a reference</p>
115  * <pre>
116  * Element refElem = _doc.getElement("Reference"); // PSEUDO
117  * Reference ref = new Reference(refElem);
118  * String url = ref.getURI();
119  * ref.setData(new XMLSignatureInput(new FileInputStream(url)));
120  * if (ref.verify()) {
121  * System.out.println("verified");
122  * }
123  * </pre>
124  *
125  * <pre>
126  * &lt;element name="Reference" type="ds:ReferenceType"/&gt;
127  * &lt;complexType name="ReferenceType"&gt;
128  * &lt;sequence&gt;
129  * &lt;element ref="ds:Transforms" minOccurs="0"/&gt;
130  * &lt;element ref="ds:DigestMethod"/&gt;
131  * &lt;element ref="ds:DigestValue"/&gt;
132  * &lt;/sequence&gt;
133  * &lt;attribute name="Id" type="ID" use="optional"/&gt;
134  * &lt;attribute name="URI" type="anyURI" use="optional"/&gt;
135  * &lt;attribute name="Type" type="anyURI" use="optional"/&gt;
136  * &lt;/complexType&gt;
137  * </pre>
138  *
139  * @author Christian Geuer-Pollmann
140  * @see ObjectContainer
141  * @see Manifest
142  */

143 public class Reference extends SignatureElementProxy {
144
145    public static boolean CacheSignedNodes = false;
146
147    /** {@link org.apache.log4j} logging facility */
148    static org.apache.log4j.Category cat =
149       org.apache.log4j.Category.getInstance(Reference.class.getName());
150
151    /** Field OBJECT_URI */
152    public static final String JavaDoc OBJECT_URI = Constants.SignatureSpecNS
153                                            + Constants._TAG_OBJECT;
154
155    /** Field MANIFEST_URI */
156    public static final String JavaDoc MANIFEST_URI = Constants.SignatureSpecNS
157                                              + Constants._TAG_MANIFEST;
158    //J-
159
Manifest _manifest = null;
160    XMLSignatureInput _transformsInput;
161    XMLSignatureInput _transformsOutput;
162    //J+
163

164    /**
165     * Constructor Reference
166     *
167     * @param doc the {@link Document} in which <code>XMLsignature</code> is placed
168     * @param BaseURI the URI of the resource where the XML instance will be stored
169     * @param ReferenceURI URI indicate where is data which will digested
170     * @param manifest
171     * @param transforms {@link Transforms} applied to data
172     * @param messageDigestAlgorithm {@link MessageDigestAlgorithm Digest algorithm} which is applied to the data
173     * $todo$ should we throw XMLSignatureException if MessageDigestAlgoURI is wrong?
174     * @throws XMLSignatureException
175     */

176    protected Reference(
177            Document doc, String JavaDoc BaseURI, String JavaDoc ReferenceURI, Manifest manifest, Transforms transforms, String JavaDoc messageDigestAlgorithm)
178               throws XMLSignatureException {
179
180       super(doc);
181
182       XMLUtils.addReturnToElement(this._constructionElement);
183
184       this._baseURI = BaseURI;
185       this._manifest = manifest;
186
187       this.setURI(ReferenceURI);
188
189       // important: The ds:Reference must be added to the associated ds:Manifest
190
// or ds:SignedInfo _before_ the this.resolverResult() is called.
191
// this._manifest.appendChild(this._constructionElement);
192
// this._manifest.appendChild(this._doc.createTextNode("\n"));
193
Element nscontext = XMLUtils.createDSctx(this._doc, "ds");
194
195       if (transforms != null) {
196          this._constructionElement.appendChild(transforms.getElement());
197          XMLUtils.addReturnToElement(this._constructionElement);
198       }
199       {
200          MessageDigestAlgorithm mda =
201             MessageDigestAlgorithm.getInstance(this._doc,
202                                                messageDigestAlgorithm);
203
204          this._constructionElement.appendChild(mda.getElement());
205          XMLUtils.addReturnToElement(this._constructionElement);
206       }
207       {
208          Element digestValueElement =
209             XMLUtils.createElementInSignatureSpace(this._doc,
210                                                    Constants._TAG_DIGESTVALUE);
211
212          this._constructionElement.appendChild(digestValueElement);
213          XMLUtils.addReturnToElement(this._constructionElement);
214       }
215    }
216
217    /**
218     * Constructor Reference
219     *
220     * @param doc this {@link Document} in which <code>XMLsignature</code> is placed
221     * @param BaseURI the URI of the resource where the XML instance will be stored
222     * @param ReferenceURI This referenceURI indicate where the data will for signature validation
223     * @param manifest
224     * @param messageDigestAlgorithm {@link MessageDigestAlgorithm Digest algorithm} which is applied to the data
225     * @throws XMLSignatureException
226     */

227    protected Reference(
228            Document doc, String JavaDoc BaseURI, String JavaDoc ReferenceURI, Manifest manifest, String JavaDoc messageDigestAlgorithm)
229               throws XMLSignatureException {
230       this(doc, BaseURI, ReferenceURI, manifest, (Transforms) null,
231            messageDigestAlgorithm);
232    }
233
234    /**
235     * Constructor Reference
236     *
237     * @param doc this {@link Document} in which <code>XMLsignature</code> is placed
238     * @param BaseURI the URI of the resource where the XML instance will be stored
239     * @param ReferenceURI This referenceURI indicate where the data is for signature validation
240     * @param manifest
241     * @param transforms {@link Transforms} applied to data
242     * @throws XMLSignatureException
243     */

244    protected Reference(
245            Document doc, String JavaDoc BaseURI, String JavaDoc ReferenceURI, Manifest manifest, Transforms transforms)
246               throws XMLSignatureException {
247       this(doc, BaseURI, ReferenceURI, manifest, transforms,
248            Constants.ALGO_ID_DIGEST_SHA1);
249    }
250
251    /**
252     * Constructor Reference
253     *
254     * @param doc this {@link Document} in which <code>XMLsignature</code> is placed
255     * @param BaseURI the URI of the resource where the XML instance will be stored
256     * @param ReferenceURI This referenceURI indicate where the data is for signature validation
257     * @param manifest
258     * @throws XMLSignatureException
259     */

260    protected Reference(
261            Document doc, String JavaDoc BaseURI, String JavaDoc ReferenceURI, Manifest manifest)
262               throws XMLSignatureException {
263       this(doc, BaseURI, ReferenceURI, manifest, (Transforms) null,
264            Constants.ALGO_ID_DIGEST_SHA1);
265    }
266
267    /**
268     * Build a {@link Reference} from an {@link Element}
269     *
270     * @param element <code>Reference</code> element
271     * @param BaseURI the URI of the resource where the XML instance was stored
272     * @param manifest is the {@link Manifest} of {@link SignedInfo} in which the Reference occurs. We need this because the Manifest has the individual {@link ResourceResolver}s whcih have been set by the user
273     * @throws XMLSecurityException
274     */

275    protected Reference(Element element, String JavaDoc BaseURI, Manifest manifest)
276            throws XMLSecurityException {
277
278       super(element, BaseURI);
279
280       this._manifest = manifest;
281    }
282
283    /**
284     * Returns {@link MessageDigestAlgorithm}
285     *
286     *
287     * @return {@link MessageDigestAlgorithm}
288     *
289     * @throws XMLSignatureException
290     */

291    public MessageDigestAlgorithm getMessageDigestAlgorithm()
292            throws XMLSignatureException {
293
294       Element digestMethodElem = this.getChildElementLocalName(0,
295                                     Constants.SignatureSpecNS,
296                                     Constants._TAG_DIGESTMETHOD);
297
298       if (digestMethodElem == null) {
299          return null;
300       }
301
302       String JavaDoc uri = digestMethodElem.getAttributeNS(null, Constants._ATT_ALGORITHM);
303
304       return MessageDigestAlgorithm.getInstance(this._doc, uri);
305    }
306
307    /**
308     * Sets the <code>URI</code> of this <code>Reference</code> element
309     *
310     * @param URI the <code>URI</code> of this <code>Reference</code> element
311     */

312    public void setURI(String JavaDoc URI) {
313
314       if ((this._state == MODE_SIGN) && (URI != null)) {
315          this._constructionElement.setAttributeNS(null, Constants._ATT_URI, URI);
316       }
317    }
318
319    /**
320     * Returns the <code>URI</code> of this <code>Reference</code> element
321     *
322     * @return URI the <code>URI</code> of this <code>Reference</code> element
323     */

324    public String JavaDoc getURI() {
325       return this._constructionElement.getAttributeNS(null, Constants._ATT_URI);
326    }
327
328    /**
329     * Sets the <code>Id</code> attribute of this <code>Reference</code> element
330     *
331     * @param Id the <code>Id</code> attribute of this <code>Reference</code> element
332     */

333    public void setId(String JavaDoc Id) {
334
335       if ((this._state == MODE_SIGN) && (Id != null)) {
336          this._constructionElement.setAttributeNS(null, Constants._ATT_ID, Id);
337          IdResolver.registerElementById(this._constructionElement, Id);
338       }
339    }
340
341    /**
342     * Returns the <code>Id</code> attribute of this <code>Reference</code> element
343     *
344     * @return Id the <code>Id</code> attribute of this <code>Reference</code> element
345     */

346    public String JavaDoc getId() {
347       return this._constructionElement.getAttributeNS(null, Constants._ATT_ID);
348    }
349
350    /**
351     * Sets the <code>type</code> atttibute of the Reference indicate whether an <code>ds:Object</code>, <code>ds:SignatureProperty</code>, or <code>ds:Manifest</code> element
352     *
353     * @param Type the <code>type</code> attribute of the Reference
354     */

355    public void setType(String JavaDoc Type) {
356
357       if ((this._state == MODE_SIGN) && (Type != null)) {
358          this._constructionElement.setAttributeNS(null, Constants._ATT_TYPE, Type);
359       }
360    }
361
362    /**
363     * Return the <code>type</code> atttibute of the Reference indicate whether an <code>ds:Object</code>, <code>ds:SignatureProperty</code>, or <code>ds:Manifest</code> element
364     *
365     * @return the <code>type</code> attribute of the Reference
366     */

367    public String JavaDoc getType() {
368       return this._constructionElement.getAttributeNS(null, Constants._ATT_TYPE);
369    }
370
371    /**
372     * Method isReferenceToObject
373     *
374     * This returns true if the <CODE>Type</CODE> attribute of the
375     * <CODE>Refernce</CODE> element points to a <CODE>#Object</CODE> element
376     *
377     * @return true if the Reference type indicates that this Reference points to an <code>Object</code>
378     */

379    public boolean typeIsReferenceToObject() {
380
381       if ((this.getType() != null)
382               && this.getType().equals(Reference.OBJECT_URI)) {
383          return true;
384       }
385
386       return false;
387    }
388
389    /**
390     * Method isReferenceToManifest
391     *
392     * This returns true if the <CODE>Type</CODE> attribute of the
393     * <CODE>Refernce</CODE> element points to a <CODE>#Manifest</CODE> element
394     *
395     * @return true if the Reference type indicates that this Reference points to a {@link Manifest}
396     */

397    public boolean typeIsReferenceToManifest() {
398
399       if ((this.getType() != null)
400               && this.getType().equals(Reference.MANIFEST_URI)) {
401          return true;
402       }
403
404       return false;
405    }
406
407    /**
408     * Method setDigestValueElement
409     *
410     * @param digestValue
411     * @throws XMLSignatureException
412     */

413    private void setDigestValueElement(byte[] digestValue)
414            throws XMLSignatureException {
415
416       if (this._state == MODE_SIGN) {
417          Element digestValueElement = this.getChildElementLocalName(0,
418                                          Constants.SignatureSpecNS,
419                                          Constants._TAG_DIGESTVALUE);
420          NodeList children = digestValueElement.getChildNodes();
421
422          for (int i = 0; i < children.getLength(); i++) {
423             digestValueElement.removeChild(children.item(i));
424          }
425
426          String JavaDoc base64codedValue = Base64.encode(digestValue);
427          Text t = this._doc.createTextNode(base64codedValue);
428
429          digestValueElement.appendChild(t);
430       }
431    }
432
433    /**
434     * Method generateDigestValue
435     *
436     * @throws ReferenceNotInitializedException
437     * @throws XMLSignatureException
438     */

439    public void generateDigestValue()
440            throws XMLSignatureException, ReferenceNotInitializedException {
441
442       if (this._state == MODE_SIGN) {
443          byte calculatedBytes[] = this.calculateDigest();
444
445          this.setDigestValueElement(calculatedBytes);
446       }
447    }
448
449    /**
450     * This method returns the {@link XMLSignatureInput} which is referenced by the
451     * <CODE>URI</CODE> Attribute.
452     *
453     * @throws ReferenceNotInitializedException
454     * @throws XMLSignatureException
455     * @see Manifest#verifyReferences
456     */

457    protected void dereferenceURIandPerformTransforms()
458            throws ReferenceNotInitializedException, XMLSignatureException {
459
460       try {
461          Attr URIAttr =
462             this._constructionElement.getAttributeNodeNS(null, Constants._ATT_URI);
463          String JavaDoc URI;
464
465          if (URIAttr == null) {
466             URI = null;
467          } else {
468             URI = URIAttr.getNodeValue();
469          }
470
471          ResourceResolver resolver = ResourceResolver.getInstance(URIAttr,
472                                         this._baseURI,
473                                         this._manifest._perManifestResolvers);
474
475          if (resolver == null) {
476             Object JavaDoc exArgs[] = { URI };
477
478             throw new ReferenceNotInitializedException(
479                "signature.Verification.Reference.NoInput", exArgs);
480          }
481
482          resolver.addProperties(this._manifest._resolverProperties);
483
484          this._transformsInput = resolver.resolve(URIAttr, this._baseURI);
485
486          Transforms transforms = this.getTransforms();
487
488          if (transforms != null) {
489             this._transformsOutput =
490                transforms.performTransforms(this._transformsInput);
491          } else {
492             this._transformsOutput = this._transformsInput;
493          }
494
495          /* at this stage, this._transformsInput and this._transformsOutput
496           * contain a huge amount of nodes. When we do not cache these nodes
497           * but only preserve the octets, the memory footprint is dramatically
498           * reduced.
499           */

500          if (!Reference.CacheSignedNodes) {
501             this._transformsInput = new XMLSignatureInput(this._transformsInput.getBytes());
502             this._transformsOutput = new XMLSignatureInput(this._transformsOutput.getBytes());
503          }
504       } catch (IOException ex) {
505          throw new ReferenceNotInitializedException("empty", ex);
506       } catch (ResourceResolverException ex) {
507          throw new ReferenceNotInitializedException("empty", ex);
508       } catch (CanonicalizationException ex) {
509          throw new ReferenceNotInitializedException("empty", ex);
510       } catch (InvalidCanonicalizerException ex) {
511          throw new ReferenceNotInitializedException("empty", ex);
512       } catch (TransformationException ex) {
513          throw new ReferenceNotInitializedException("empty", ex);
514       } catch (XMLSecurityException ex) {
515          throw new ReferenceNotInitializedException("empty", ex);
516       }
517    }
518
519    /**
520     * Method getTransforms
521     *
522     *
523     * @throws InvalidTransformException
524     * @throws TransformationException
525     * @throws XMLSecurityException
526     * @throws XMLSignatureException
527     */

528    public Transforms getTransforms()
529            throws XMLSignatureException, InvalidTransformException,
530                   TransformationException, XMLSecurityException {
531
532       Element transformsElement = this.getChildElementLocalName(0,
533                                      Constants.SignatureSpecNS,
534                                      Constants._TAG_TRANSFORMS);
535
536       if (transformsElement != null) {
537          Transforms transforms = new Transforms(transformsElement,
538                                                 this._baseURI);
539
540          return transforms;
541       } else {
542          return null;
543       }
544    }
545
546    /**
547     * Method getReferencedBytes
548     *
549     *
550     * @throws ReferenceNotInitializedException
551     * @throws XMLSignatureException
552     */

553    public byte[] getReferencedBytes()
554            throws ReferenceNotInitializedException, XMLSignatureException {
555
556       try {
557          this.dereferenceURIandPerformTransforms();
558
559          byte[] signedBytes = this.getTransformsOutput().getBytes();
560
561          return signedBytes;
562       } catch (IOException ex) {
563          throw new ReferenceNotInitializedException("empty", ex);
564       } catch (CanonicalizationException ex) {
565          throw new ReferenceNotInitializedException("empty", ex);
566       } catch (InvalidCanonicalizerException ex) {
567          throw new ReferenceNotInitializedException("empty", ex);
568       }
569    }
570
571    /**
572     * Method resolverResult
573     *
574     *
575     * @throws ReferenceNotInitializedException
576     * @throws XMLSignatureException
577     */

578    private byte[] calculateDigest()
579            throws ReferenceNotInitializedException, XMLSignatureException {
580
581       try {
582          byte[] data = this.getReferencedBytes();
583          MessageDigestAlgorithm mda = this.getMessageDigestAlgorithm();
584
585          mda.reset();
586          mda.update(data);
587
588          byte calculatedDigestValue[] = mda.digest();
589
590          //J-
591
if (data.length < 20) {
592             cat.debug(new String JavaDoc(data));
593          } else {
594             cat.debug(new String JavaDoc(data).substring(0, 20) + " ...");
595          }
596          //J+
597
return calculatedDigestValue;
598       } catch (XMLSecurityException ex) {
599          throw new ReferenceNotInitializedException("empty", ex);
600       }
601    }
602
603    /**
604     * Tests reference valdiation is success or false
605     *
606     * @return true if reference valdiation is success, otherwise false
607     * @throws ReferenceNotInitializedException
608     * @throws XMLSecurityException
609     */

610    public boolean verify()
611            throws ReferenceNotInitializedException, XMLSecurityException {
612
613       Element digestValueElem = this.getChildElementLocalName(0,
614                                    Constants.SignatureSpecNS,
615                                    Constants._TAG_DIGESTVALUE);
616       byte[] elemDig = Base64.decode(digestValueElem);
617       byte[] calcDig = this.calculateDigest();
618       boolean equal = MessageDigestAlgorithm.isEqual(elemDig, calcDig);
619
620       if (!equal) {
621          cat.warn("Verification failed for URI \"" + this.getURI() + "\"");
622
623          if (cat.isDebugEnabled()) {
624             cat.debug("unverifiedDigestValue= " + Base64.encode(elemDig));
625             cat.debug("calculatedDigestValue= " + Base64.encode(calcDig));
626
627             /*
628             try {
629                String tmp = new Long(System.currentTimeMillis()).toString()
630                             + ".txt";
631
632                cat.warn("Wrote \"" + this.getURI() + "\" to file " + tmp);
633                JavaUtils.writeBytesToFilename(tmp, this.getReferencedBytes());
634             } catch (Exception ex) {}
635             */

636          }
637       } else {
638          cat.info("Verification successful for URI \"" + this.getURI() + "\"");
639       }
640
641       return equal;
642    }
643
644    /**
645     * Method getTransformsInput
646     *
647     *
648     */

649    public XMLSignatureInput getTransformsInput() {
650       return this._transformsInput;
651    }
652
653    /**
654     * Method getTransformsOutput
655     *
656     *
657     */

658    public XMLSignatureInput getTransformsOutput() {
659       return this._transformsOutput;
660    }
661
662    /**
663     * Method getBaseLocalName
664     *
665     *
666     */

667    public String JavaDoc getBaseLocalName() {
668       return Constants._TAG_REFERENCE;
669    }
670 }
671
Popular Tags