Reference.java revision 11099:678faa7d1a6a
1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/**
6 * Licensed to the Apache Software Foundation (ASF) under one
7 * or more contributor license agreements. See the NOTICE file
8 * distributed with this work for additional information
9 * regarding copyright ownership. The ASF licenses this file
10 * to you under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance
12 * with the License. You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing,
17 * software distributed under the License is distributed on an
18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 * KIND, either express or implied. See the License for the
20 * specific language governing permissions and limitations
21 * under the License.
22 */
23package com.sun.org.apache.xml.internal.security.signature;
24
25import java.io.IOException;
26import java.io.OutputStream;
27import java.security.AccessController;
28import java.security.PrivilegedAction;
29import java.util.HashSet;
30import java.util.Iterator;
31import java.util.Set;
32
33import com.sun.org.apache.xml.internal.security.algorithms.MessageDigestAlgorithm;
34import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
35import com.sun.org.apache.xml.internal.security.c14n.InvalidCanonicalizerException;
36import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
37import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
38import com.sun.org.apache.xml.internal.security.signature.reference.ReferenceData;
39import com.sun.org.apache.xml.internal.security.signature.reference.ReferenceNodeSetData;
40import com.sun.org.apache.xml.internal.security.signature.reference.ReferenceOctetStreamData;
41import com.sun.org.apache.xml.internal.security.signature.reference.ReferenceSubTreeData;
42import com.sun.org.apache.xml.internal.security.transforms.InvalidTransformException;
43import com.sun.org.apache.xml.internal.security.transforms.Transform;
44import com.sun.org.apache.xml.internal.security.transforms.TransformationException;
45import com.sun.org.apache.xml.internal.security.transforms.Transforms;
46import com.sun.org.apache.xml.internal.security.transforms.params.InclusiveNamespaces;
47import com.sun.org.apache.xml.internal.security.utils.Base64;
48import com.sun.org.apache.xml.internal.security.utils.Constants;
49import com.sun.org.apache.xml.internal.security.utils.DigesterOutputStream;
50import com.sun.org.apache.xml.internal.security.utils.SignatureElementProxy;
51import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream;
52import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
53import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver;
54import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException;
55import org.w3c.dom.Attr;
56import org.w3c.dom.Document;
57import org.w3c.dom.Element;
58import org.w3c.dom.Node;
59import org.w3c.dom.Text;
60
61/**
62 * Handles <code>&lt;ds:Reference&gt;</code> elements.
63 *
64 * This includes:
65 *
66 * Constructs a <CODE>ds:Reference</CODE> from an {@link org.w3c.dom.Element}.
67 *
68 * <p>Create a new reference</p>
69 * <pre>
70 * Document doc;
71 * MessageDigestAlgorithm sha1 = MessageDigestAlgorithm.getInstance("http://#sha1");
72 * Reference ref = new Reference(new XMLSignatureInput(new FileInputStream("1.gif"),
73 *                               "http://localhost/1.gif",
74 *                               (Transforms) null, sha1);
75 * Element refElem = ref.toElement(doc);
76 * </pre>
77 *
78 * <p>Verify a reference</p>
79 * <pre>
80 * Element refElem = doc.getElement("Reference"); // PSEUDO
81 * Reference ref = new Reference(refElem);
82 * String url = ref.getURI();
83 * ref.setData(new XMLSignatureInput(new FileInputStream(url)));
84 * if (ref.verify()) {
85 *    System.out.println("verified");
86 * }
87 * </pre>
88 *
89 * <pre>
90 * &lt;element name="Reference" type="ds:ReferenceType"/&gt;
91 *  &lt;complexType name="ReferenceType"&gt;
92 *    &lt;sequence&gt;
93 *      &lt;element ref="ds:Transforms" minOccurs="0"/&gt;
94 *      &lt;element ref="ds:DigestMethod"/&gt;
95 *      &lt;element ref="ds:DigestValue"/&gt;
96 *    &lt;/sequence&gt;
97 *    &lt;attribute name="Id" type="ID" use="optional"/&gt;
98 *    &lt;attribute name="URI" type="anyURI" use="optional"/&gt;
99 *    &lt;attribute name="Type" type="anyURI" use="optional"/&gt;
100 *  &lt;/complexType&gt;
101 * </pre>
102 *
103 * @author Christian Geuer-Pollmann
104 * @see ObjectContainer
105 * @see Manifest
106 */
107public class Reference extends SignatureElementProxy {
108
109    /** Field OBJECT_URI */
110    public static final String OBJECT_URI = Constants.SignatureSpecNS + Constants._TAG_OBJECT;
111
112    /** Field MANIFEST_URI */
113    public static final String MANIFEST_URI = Constants.SignatureSpecNS + Constants._TAG_MANIFEST;
114
115    /**
116     * The maximum number of transforms per reference, if secure validation is enabled.
117     */
118    public static final int MAXIMUM_TRANSFORM_COUNT = 5;
119
120    private boolean secureValidation;
121
122    /**
123     * Look up useC14N11 system property. If true, an explicit C14N11 transform
124     * will be added if necessary when generating the signature. See section
125     * 3.1.1 of http://www.w3.org/2007/xmlsec/Drafts/xmldsig-core/ for more info.
126     */
127    private static boolean useC14N11 = (
128        AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
129            public Boolean run() {
130                return Boolean.valueOf(Boolean.getBoolean("com.sun.org.apache.xml.internal.security.useC14N11"));
131            }
132        })).booleanValue();
133
134    /** {@link org.apache.commons.logging} logging facility */
135    private static final java.util.logging.Logger log =
136        java.util.logging.Logger.getLogger(Reference.class.getName());
137
138    private Manifest manifest;
139    private XMLSignatureInput transformsOutput;
140
141    private Transforms transforms;
142
143    private Element digestMethodElem;
144
145    private Element digestValueElement;
146
147    private ReferenceData referenceData;
148
149    /**
150     * Constructor Reference
151     *
152     * @param doc the {@link Document} in which <code>XMLsignature</code> is placed
153     * @param baseURI the URI of the resource where the XML instance will be stored
154     * @param referenceURI URI indicate where is data which will digested
155     * @param manifest
156     * @param transforms {@link Transforms} applied to data
157     * @param messageDigestAlgorithm {@link MessageDigestAlgorithm Digest algorithm} which is
158     * applied to the data
159     * TODO should we throw XMLSignatureException if MessageDigestAlgoURI is wrong?
160     * @throws XMLSignatureException
161     */
162    protected Reference(
163        Document doc, String baseURI, String referenceURI, Manifest manifest,
164        Transforms transforms, String messageDigestAlgorithm
165    ) throws XMLSignatureException {
166        super(doc);
167
168        XMLUtils.addReturnToElement(this.constructionElement);
169
170        this.baseURI = baseURI;
171        this.manifest = manifest;
172
173        this.setURI(referenceURI);
174
175        // important: The ds:Reference must be added to the associated ds:Manifest
176        //            or ds:SignedInfo _before_ the this.resolverResult() is called.
177        // this.manifest.appendChild(this.constructionElement);
178        // this.manifest.appendChild(this.doc.createTextNode("\n"));
179
180        if (transforms != null) {
181            this.transforms=transforms;
182            this.constructionElement.appendChild(transforms.getElement());
183            XMLUtils.addReturnToElement(this.constructionElement);
184        }
185        MessageDigestAlgorithm mda =
186            MessageDigestAlgorithm.getInstance(this.doc, messageDigestAlgorithm);
187
188        digestMethodElem = mda.getElement();
189        this.constructionElement.appendChild(digestMethodElem);
190        XMLUtils.addReturnToElement(this.constructionElement);
191
192        digestValueElement =
193            XMLUtils.createElementInSignatureSpace(this.doc, Constants._TAG_DIGESTVALUE);
194
195        this.constructionElement.appendChild(digestValueElement);
196        XMLUtils.addReturnToElement(this.constructionElement);
197    }
198
199
200    /**
201     * Build a {@link Reference} from an {@link Element}
202     *
203     * @param element <code>Reference</code> element
204     * @param baseURI the URI of the resource where the XML instance was stored
205     * @param manifest is the {@link Manifest} of {@link SignedInfo} in which the Reference occurs.
206     * We need this because the Manifest has the individual {@link ResourceResolver}s which have
207     * been set by the user
208     * @throws XMLSecurityException
209     */
210    protected Reference(Element element, String baseURI, Manifest manifest) throws XMLSecurityException {
211        this(element, baseURI, manifest, false);
212    }
213
214    /**
215     * Build a {@link Reference} from an {@link Element}
216     *
217     * @param element <code>Reference</code> element
218     * @param baseURI the URI of the resource where the XML instance was stored
219     * @param manifest is the {@link Manifest} of {@link SignedInfo} in which the Reference occurs.
220     * @param secureValidation whether secure validation is enabled or not
221     * We need this because the Manifest has the individual {@link ResourceResolver}s which have
222     * been set by the user
223     * @throws XMLSecurityException
224     */
225    protected Reference(Element element, String baseURI, Manifest manifest, boolean secureValidation)
226        throws XMLSecurityException {
227        super(element, baseURI);
228        this.secureValidation = secureValidation;
229        this.baseURI = baseURI;
230        Element el = XMLUtils.getNextElement(element.getFirstChild());
231        if (Constants._TAG_TRANSFORMS.equals(el.getLocalName())
232            && Constants.SignatureSpecNS.equals(el.getNamespaceURI())) {
233            transforms = new Transforms(el, this.baseURI);
234            transforms.setSecureValidation(secureValidation);
235            if (secureValidation && transforms.getLength() > MAXIMUM_TRANSFORM_COUNT) {
236                Object exArgs[] = { transforms.getLength(), MAXIMUM_TRANSFORM_COUNT };
237
238                throw new XMLSecurityException("signature.tooManyTransforms", exArgs);
239            }
240            el = XMLUtils.getNextElement(el.getNextSibling());
241        }
242        digestMethodElem = el;
243        digestValueElement = XMLUtils.getNextElement(digestMethodElem.getNextSibling());
244        this.manifest = manifest;
245    }
246
247    /**
248     * Returns {@link MessageDigestAlgorithm}
249     *
250     *
251     * @return {@link MessageDigestAlgorithm}
252     *
253     * @throws XMLSignatureException
254     */
255    public MessageDigestAlgorithm getMessageDigestAlgorithm() throws XMLSignatureException {
256        if (digestMethodElem == null) {
257            return null;
258        }
259
260        String uri = digestMethodElem.getAttributeNS(null, Constants._ATT_ALGORITHM);
261
262        if (uri == null) {
263            return null;
264        }
265
266        if (secureValidation && MessageDigestAlgorithm.ALGO_ID_DIGEST_NOT_RECOMMENDED_MD5.equals(uri)) {
267            Object exArgs[] = { uri };
268
269            throw new XMLSignatureException("signature.signatureAlgorithm", exArgs);
270        }
271
272        return MessageDigestAlgorithm.getInstance(this.doc, uri);
273    }
274
275    /**
276     * Sets the <code>URI</code> of this <code>Reference</code> element
277     *
278     * @param uri the <code>URI</code> of this <code>Reference</code> element
279     */
280    public void setURI(String uri) {
281        if (uri != null) {
282            this.constructionElement.setAttributeNS(null, Constants._ATT_URI, uri);
283        }
284    }
285
286    /**
287     * Returns the <code>URI</code> of this <code>Reference</code> element
288     *
289     * @return URI the <code>URI</code> of this <code>Reference</code> element
290     */
291    public String getURI() {
292        return this.constructionElement.getAttributeNS(null, Constants._ATT_URI);
293    }
294
295    /**
296     * Sets the <code>Id</code> attribute of this <code>Reference</code> element
297     *
298     * @param id the <code>Id</code> attribute of this <code>Reference</code> element
299     */
300    public void setId(String id) {
301        if (id != null) {
302            this.constructionElement.setAttributeNS(null, Constants._ATT_ID, id);
303            this.constructionElement.setIdAttributeNS(null, Constants._ATT_ID, true);
304        }
305    }
306
307    /**
308     * Returns the <code>Id</code> attribute of this <code>Reference</code> element
309     *
310     * @return Id the <code>Id</code> attribute of this <code>Reference</code> element
311     */
312    public String getId() {
313        return this.constructionElement.getAttributeNS(null, Constants._ATT_ID);
314    }
315
316    /**
317     * Sets the <code>type</code> atttibute of the Reference indicate whether an
318     * <code>ds:Object</code>, <code>ds:SignatureProperty</code>, or <code>ds:Manifest</code>
319     * element.
320     *
321     * @param type the <code>type</code> attribute of the Reference
322     */
323    public void setType(String type) {
324        if (type != null) {
325            this.constructionElement.setAttributeNS(null, Constants._ATT_TYPE, type);
326        }
327    }
328
329    /**
330     * Return the <code>type</code> atttibute of the Reference indicate whether an
331     * <code>ds:Object</code>, <code>ds:SignatureProperty</code>, or <code>ds:Manifest</code>
332     * element
333     *
334     * @return the <code>type</code> attribute of the Reference
335     */
336    public String getType() {
337        return this.constructionElement.getAttributeNS(null, Constants._ATT_TYPE);
338    }
339
340    /**
341     * Method isReferenceToObject
342     *
343     * This returns true if the <CODE>Type</CODE> attribute of the
344     * <CODE>Reference</CODE> element points to a <CODE>#Object</CODE> element
345     *
346     * @return true if the Reference type indicates that this Reference points to an
347     * <code>Object</code>
348     */
349    public boolean typeIsReferenceToObject() {
350        if (Reference.OBJECT_URI.equals(this.getType())) {
351            return true;
352        }
353
354        return false;
355    }
356
357    /**
358     * Method isReferenceToManifest
359     *
360     * This returns true if the <CODE>Type</CODE> attribute of the
361     * <CODE>Reference</CODE> element points to a <CODE>#Manifest</CODE> element
362     *
363     * @return true if the Reference type indicates that this Reference points to a
364     * {@link Manifest}
365     */
366    public boolean typeIsReferenceToManifest() {
367        if (Reference.MANIFEST_URI.equals(this.getType())) {
368            return true;
369        }
370
371        return false;
372    }
373
374    /**
375     * Method setDigestValueElement
376     *
377     * @param digestValue
378     */
379    private void setDigestValueElement(byte[] digestValue) {
380        Node n = digestValueElement.getFirstChild();
381        while (n != null) {
382            digestValueElement.removeChild(n);
383            n = n.getNextSibling();
384        }
385
386        String base64codedValue = Base64.encode(digestValue);
387        Text t = this.doc.createTextNode(base64codedValue);
388
389        digestValueElement.appendChild(t);
390    }
391
392    /**
393     * Method generateDigestValue
394     *
395     * @throws ReferenceNotInitializedException
396     * @throws XMLSignatureException
397     */
398    public void generateDigestValue()
399        throws XMLSignatureException, ReferenceNotInitializedException {
400        this.setDigestValueElement(this.calculateDigest(false));
401    }
402
403    /**
404     * Returns the XMLSignatureInput which is created by de-referencing the URI attribute.
405     * @return the XMLSignatureInput of the source of this reference
406     * @throws ReferenceNotInitializedException If the resolver found any
407     * problem resolving the reference
408     */
409    public XMLSignatureInput getContentsBeforeTransformation()
410        throws ReferenceNotInitializedException {
411        try {
412            Attr uriAttr =
413                this.constructionElement.getAttributeNodeNS(null, Constants._ATT_URI);
414
415            ResourceResolver resolver =
416                ResourceResolver.getInstance(
417                    uriAttr, this.baseURI, this.manifest.getPerManifestResolvers(), secureValidation
418                );
419            resolver.addProperties(this.manifest.getResolverProperties());
420
421            return resolver.resolve(uriAttr, this.baseURI, secureValidation);
422        }  catch (ResourceResolverException ex) {
423            throw new ReferenceNotInitializedException("empty", ex);
424        }
425    }
426
427    private XMLSignatureInput getContentsAfterTransformation(
428        XMLSignatureInput input, OutputStream os
429    ) throws XMLSignatureException {
430        try {
431            Transforms transforms = this.getTransforms();
432            XMLSignatureInput output = null;
433
434            if (transforms != null) {
435                output = transforms.performTransforms(input, os);
436                this.transformsOutput = output;//new XMLSignatureInput(output.getBytes());
437
438                //this.transformsOutput.setSourceURI(output.getSourceURI());
439            } else {
440                output = input;
441            }
442
443            return output;
444        } catch (ResourceResolverException ex) {
445            throw new XMLSignatureException("empty", ex);
446        } catch (CanonicalizationException ex) {
447            throw new XMLSignatureException("empty", ex);
448        } catch (InvalidCanonicalizerException ex) {
449            throw new XMLSignatureException("empty", ex);
450        } catch (TransformationException ex) {
451            throw new XMLSignatureException("empty", ex);
452        } catch (XMLSecurityException ex) {
453            throw new XMLSignatureException("empty", ex);
454        }
455    }
456
457    /**
458     * Returns the XMLSignatureInput which is the result of the Transforms.
459     * @return a XMLSignatureInput with all transformations applied.
460     * @throws XMLSignatureException
461     */
462    public XMLSignatureInput getContentsAfterTransformation()
463        throws XMLSignatureException {
464        XMLSignatureInput input = this.getContentsBeforeTransformation();
465        cacheDereferencedElement(input);
466
467        return this.getContentsAfterTransformation(input, null);
468    }
469
470    /**
471     * This method returns the XMLSignatureInput which represents the node set before
472     * some kind of canonicalization is applied for the first time.
473     * @return Gets a the node doing everything till the first c14n is needed
474     *
475     * @throws XMLSignatureException
476     */
477    public XMLSignatureInput getNodesetBeforeFirstCanonicalization()
478        throws XMLSignatureException {
479        try {
480            XMLSignatureInput input = this.getContentsBeforeTransformation();
481            cacheDereferencedElement(input);
482            XMLSignatureInput output = input;
483            Transforms transforms = this.getTransforms();
484
485            if (transforms != null) {
486                doTransforms: for (int i = 0; i < transforms.getLength(); i++) {
487                    Transform t = transforms.item(i);
488                    String uri = t.getURI();
489
490                    if (uri.equals(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS)
491                        || uri.equals(Transforms.TRANSFORM_C14N_EXCL_WITH_COMMENTS)
492                        || uri.equals(Transforms.TRANSFORM_C14N_OMIT_COMMENTS)
493                        || uri.equals(Transforms.TRANSFORM_C14N_WITH_COMMENTS)) {
494                        break doTransforms;
495                    }
496
497                    output = t.performTransform(output, null);
498                }
499
500            output.setSourceURI(input.getSourceURI());
501            }
502            return output;
503        } catch (IOException ex) {
504            throw new XMLSignatureException("empty", ex);
505        } catch (ResourceResolverException ex) {
506            throw new XMLSignatureException("empty", ex);
507        } catch (CanonicalizationException ex) {
508            throw new XMLSignatureException("empty", ex);
509        } catch (InvalidCanonicalizerException ex) {
510            throw new XMLSignatureException("empty", ex);
511        } catch (TransformationException ex) {
512            throw new XMLSignatureException("empty", ex);
513        } catch (XMLSecurityException ex) {
514            throw new XMLSignatureException("empty", ex);
515        }
516    }
517
518    /**
519     * Method getHTMLRepresentation
520     * @return The HTML of the transformation
521     * @throws XMLSignatureException
522     */
523    public String getHTMLRepresentation() throws XMLSignatureException {
524        try {
525            XMLSignatureInput nodes = this.getNodesetBeforeFirstCanonicalization();
526
527            Transforms transforms = this.getTransforms();
528            Transform c14nTransform = null;
529
530            if (transforms != null) {
531                doTransforms: for (int i = 0; i < transforms.getLength(); i++) {
532                    Transform t = transforms.item(i);
533                    String uri = t.getURI();
534
535                    if (uri.equals(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS)
536                        || uri.equals(Transforms.TRANSFORM_C14N_EXCL_WITH_COMMENTS)) {
537                        c14nTransform = t;
538                        break doTransforms;
539                    }
540                }
541            }
542
543            Set<String> inclusiveNamespaces = new HashSet<String>();
544            if (c14nTransform != null
545                && (c14nTransform.length(
546                    InclusiveNamespaces.ExclusiveCanonicalizationNamespace,
547                    InclusiveNamespaces._TAG_EC_INCLUSIVENAMESPACES) == 1)) {
548
549                // there is one InclusiveNamespaces element
550                InclusiveNamespaces in =
551                    new InclusiveNamespaces(
552                        XMLUtils.selectNode(
553                            c14nTransform.getElement().getFirstChild(),
554                            InclusiveNamespaces.ExclusiveCanonicalizationNamespace,
555                            InclusiveNamespaces._TAG_EC_INCLUSIVENAMESPACES,
556                            0
557                        ), this.getBaseURI());
558
559                inclusiveNamespaces =
560                    InclusiveNamespaces.prefixStr2Set(in.getInclusiveNamespaces());
561            }
562
563            return nodes.getHTMLRepresentation(inclusiveNamespaces);
564        } catch (TransformationException ex) {
565            throw new XMLSignatureException("empty", ex);
566        } catch (InvalidTransformException ex) {
567            throw new XMLSignatureException("empty", ex);
568        } catch (XMLSecurityException ex) {
569            throw new XMLSignatureException("empty", ex);
570        }
571    }
572
573    /**
574     * This method only works after a call to verify.
575     * @return the transformed output(i.e. what is going to be digested).
576     */
577    public XMLSignatureInput getTransformsOutput() {
578        return this.transformsOutput;
579    }
580
581    /**
582     * Get the ReferenceData that corresponds to the cached representation of the dereferenced
583     * object before transformation.
584     */
585    public ReferenceData getReferenceData() {
586        return referenceData;
587    }
588
589    /**
590     * This method returns the {@link XMLSignatureInput} which is referenced by the
591     * <CODE>URI</CODE> Attribute.
592     * @param os where to write the transformation can be null.
593     * @return the element to digest
594     *
595     * @throws XMLSignatureException
596     * @see Manifest#verifyReferences()
597     */
598    protected XMLSignatureInput dereferenceURIandPerformTransforms(OutputStream os)
599        throws XMLSignatureException {
600        try {
601            XMLSignatureInput input = this.getContentsBeforeTransformation();
602            cacheDereferencedElement(input);
603
604            XMLSignatureInput output = this.getContentsAfterTransformation(input, os);
605            this.transformsOutput = output;
606            return output;
607        } catch (XMLSecurityException ex) {
608            throw new ReferenceNotInitializedException("empty", ex);
609        }
610    }
611
612    /**
613     * Store the dereferenced Element(s) so that it/they can be retrieved later.
614     */
615    private void cacheDereferencedElement(XMLSignatureInput input) {
616        if (input.isNodeSet()) {
617            try {
618                final Set<Node> s = input.getNodeSet();
619                referenceData = new ReferenceNodeSetData() {
620                    public Iterator<Node> iterator() {
621                        return new Iterator<Node>() {
622
623                            Iterator<Node> sIterator = s.iterator();
624
625                            public boolean hasNext() {
626                                return sIterator.hasNext();
627                            }
628
629                            public Node next() {
630                                return sIterator.next();
631                            }
632
633                            public void remove() {
634                                throw new UnsupportedOperationException();
635                            }
636                        };
637                    }
638                };
639            } catch (Exception e) {
640                // log a warning
641                log.log(java.util.logging.Level.WARNING, "cannot cache dereferenced data: " + e);
642            }
643        } else if (input.isElement()) {
644            referenceData = new ReferenceSubTreeData
645                (input.getSubNode(), input.isExcludeComments());
646        } else if (input.isOctetStream() || input.isByteArray()) {
647            try {
648                referenceData = new ReferenceOctetStreamData
649                    (input.getOctetStream(), input.getSourceURI(),
650                        input.getMIMEType());
651            } catch (IOException ioe) {
652                // log a warning
653                log.log(java.util.logging.Level.WARNING, "cannot cache dereferenced data: " + ioe);
654            }
655        }
656    }
657
658    /**
659     * Method getTransforms
660     *
661     * @return The transforms that applied this reference.
662     * @throws InvalidTransformException
663     * @throws TransformationException
664     * @throws XMLSecurityException
665     * @throws XMLSignatureException
666     */
667    public Transforms getTransforms()
668        throws XMLSignatureException, InvalidTransformException,
669        TransformationException, XMLSecurityException {
670        return transforms;
671    }
672
673    /**
674     * Method getReferencedBytes
675     *
676     * @return the bytes that will be used to generated digest.
677     * @throws ReferenceNotInitializedException
678     * @throws XMLSignatureException
679     */
680    public byte[] getReferencedBytes()
681        throws ReferenceNotInitializedException, XMLSignatureException {
682        try {
683            XMLSignatureInput output = this.dereferenceURIandPerformTransforms(null);
684            return output.getBytes();
685        } catch (IOException ex) {
686            throw new ReferenceNotInitializedException("empty", ex);
687        } catch (CanonicalizationException ex) {
688            throw new ReferenceNotInitializedException("empty", ex);
689        }
690    }
691
692
693    /**
694     * Method calculateDigest
695     *
696     * @param validating true if validating the reference
697     * @return reference Calculate the digest of this reference.
698     * @throws ReferenceNotInitializedException
699     * @throws XMLSignatureException
700     */
701    private byte[] calculateDigest(boolean validating)
702        throws ReferenceNotInitializedException, XMLSignatureException {
703        OutputStream os = null;
704        try {
705            MessageDigestAlgorithm mda = this.getMessageDigestAlgorithm();
706
707            mda.reset();
708            DigesterOutputStream diOs = new DigesterOutputStream(mda);
709            os = new UnsyncBufferedOutputStream(diOs);
710            XMLSignatureInput output = this.dereferenceURIandPerformTransforms(os);
711            // if signing and c14n11 property == true explicitly add
712            // C14N11 transform if needed
713            if (Reference.useC14N11 && !validating && !output.isOutputStreamSet()
714                && !output.isOctetStream()) {
715                if (transforms == null) {
716                    transforms = new Transforms(this.doc);
717                    transforms.setSecureValidation(secureValidation);
718                    this.constructionElement.insertBefore(transforms.getElement(), digestMethodElem);
719                }
720                transforms.addTransform(Transforms.TRANSFORM_C14N11_OMIT_COMMENTS);
721                output.updateOutputStream(os, true);
722            } else {
723                output.updateOutputStream(os);
724            }
725            os.flush();
726
727            if (output.getOctetStreamReal() != null) {
728                output.getOctetStreamReal().close();
729            }
730
731            //this.getReferencedBytes(diOs);
732            //mda.update(data);
733
734            return diOs.getDigestValue();
735        } catch (XMLSecurityException ex) {
736            throw new ReferenceNotInitializedException("empty", ex);
737        } catch (IOException ex) {
738            throw new ReferenceNotInitializedException("empty", ex);
739        } finally {
740            if (os != null) {
741                try {
742                    os.close();
743                } catch (IOException ex) {
744                    throw new ReferenceNotInitializedException("empty", ex);
745                }
746            }
747        }
748    }
749
750    /**
751     * Returns the digest value.
752     *
753     * @return the digest value.
754     * @throws Base64DecodingException if Reference contains no proper base64 encoded data.
755     * @throws XMLSecurityException if the Reference does not contain a DigestValue element
756     */
757    public byte[] getDigestValue() throws Base64DecodingException, XMLSecurityException {
758        if (digestValueElement == null) {
759            // The required element is not in the XML!
760            Object[] exArgs ={ Constants._TAG_DIGESTVALUE, Constants.SignatureSpecNS };
761            throw new XMLSecurityException(
762                "signature.Verification.NoSignatureElement", exArgs
763            );
764        }
765        return Base64.decode(digestValueElement);
766    }
767
768
769    /**
770     * Tests reference validation is success or false
771     *
772     * @return true if reference validation is success, otherwise false
773     * @throws ReferenceNotInitializedException
774     * @throws XMLSecurityException
775     */
776    public boolean verify()
777        throws ReferenceNotInitializedException, XMLSecurityException {
778        byte[] elemDig = this.getDigestValue();
779        byte[] calcDig = this.calculateDigest(true);
780        boolean equal = MessageDigestAlgorithm.isEqual(elemDig, calcDig);
781
782        if (!equal) {
783            log.log(java.util.logging.Level.WARNING, "Verification failed for URI \"" + this.getURI() + "\"");
784            log.log(java.util.logging.Level.WARNING, "Expected Digest: " + Base64.encode(elemDig));
785            log.log(java.util.logging.Level.WARNING, "Actual Digest: " + Base64.encode(calcDig));
786        } else {
787            if (log.isLoggable(java.util.logging.Level.FINE)) {
788                log.log(java.util.logging.Level.FINE, "Verification successful for URI \"" + this.getURI() + "\"");
789            }
790        }
791
792        return equal;
793    }
794
795    /**
796     * Method getBaseLocalName
797     * @inheritDoc
798     */
799    public String getBaseLocalName() {
800        return Constants._TAG_REFERENCE;
801    }
802}
803