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  */
23 /*
24  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
25  */
26 package org.jcp.xml.dsig.internal.dom;
27 
28 import javax.xml.crypto.*;
29 import javax.xml.crypto.dom.DOMCryptoContext;
30 import javax.xml.crypto.dsig.*;
31 
32 import java.io.ByteArrayInputStream;
33 import java.io.ByteArrayOutputStream;
34 import java.io.InputStream;
35 import java.io.OutputStream;
36 import java.io.IOException;
37 import java.security.Provider;
38 import java.util.*;
39 
40 import org.w3c.dom.Document;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.Node;
43 import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream;
44 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
45 
46 /**
47  * DOM-based implementation of SignedInfo.
48  *
49  */
50 public final class DOMSignedInfo extends DOMStructure implements SignedInfo {
51 
52     private static final com.sun.org.slf4j.internal.Logger LOG =
53         com.sun.org.slf4j.internal.LoggerFactory.getLogger(DOMSignedInfo.class);
54 
55     private List<Reference> references;
56     private CanonicalizationMethod canonicalizationMethod;
57     private SignatureMethod signatureMethod;
58     private String id;
59     private Document ownerDoc;
60     private Element localSiElem;
61     private InputStream canonData;
62 
63     /**
64      * Creates a {@code DOMSignedInfo} from the specified parameters. Use
65      * this constructor when the {@code Id} is not specified.
66      *
67      * @param cm the canonicalization method
68      * @param sm the signature method
69      * @param references the list of references. The list is copied.
70      * @throws NullPointerException if
71      *    {@code cm}, {@code sm}, or {@code references} is
72      *    {@code null}
73      * @throws IllegalArgumentException if {@code references} is empty
74      * @throws ClassCastException if any of the references are not of
75      *    type {@code Reference}
76      */
DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm, List<? extends Reference> references)77     public DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm,
78                          List<? extends Reference> references) {
79         if (cm == null || sm == null || references == null) {
80             throw new NullPointerException();
81         }
82         this.canonicalizationMethod = cm;
83         this.signatureMethod = sm;
84         this.references = Collections.unmodifiableList(
85             new ArrayList<>(references));
86         if (this.references.isEmpty()) {
87             throw new IllegalArgumentException("list of references must " +
88                 "contain at least one entry");
89         }
90         for (int i = 0, size = this.references.size(); i < size; i++) {
91             Object obj = this.references.get(i);
92             if (!(obj instanceof Reference)) {
93                 throw new ClassCastException("list of references contains " +
94                     "an illegal type");
95             }
96         }
97     }
98 
99     /**
100      * Creates a {@code DOMSignedInfo} from the specified parameters.
101      *
102      * @param cm the canonicalization method
103      * @param sm the signature method
104      * @param references the list of references. The list is copied.
105      * @param id an optional identifer that will allow this
106      *    {@code SignedInfo} to be referenced by other signatures and
107      *    objects
108      * @throws NullPointerException if {@code cm}, {@code sm},
109      *    or {@code references} is {@code null}
110      * @throws IllegalArgumentException if {@code references} is empty
111      * @throws ClassCastException if any of the references are not of
112      *    type {@code Reference}
113      */
DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm, List<? extends Reference> references, String id)114     public DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm,
115                          List<? extends Reference> references, String id) {
116         this(cm, sm, references);
117         this.id = id;
118     }
119 
120     /**
121      * Creates a {@code DOMSignedInfo} from an element.
122      *
123      * @param siElem a SignedInfo element
124      */
DOMSignedInfo(Element siElem, XMLCryptoContext context, Provider provider)125     public DOMSignedInfo(Element siElem, XMLCryptoContext context, Provider provider)
126         throws MarshalException {
127         localSiElem = siElem;
128         ownerDoc = siElem.getOwnerDocument();
129 
130         // get Id attribute, if specified
131         id = DOMUtils.getAttributeValue(siElem, "Id");
132 
133         // unmarshal CanonicalizationMethod
134         Element cmElem = DOMUtils.getFirstChildElement(siElem,
135                                                        "CanonicalizationMethod",
136                                                        XMLSignature.XMLNS);
137         canonicalizationMethod = new DOMCanonicalizationMethod(cmElem, context,
138                                                                provider);
139 
140         // unmarshal SignatureMethod
141         Element smElem = DOMUtils.getNextSiblingElement(cmElem,
142                                                         "SignatureMethod",
143                                                         XMLSignature.XMLNS);
144         signatureMethod = DOMSignatureMethod.unmarshal(smElem);
145 
146         boolean secVal = Utils.secureValidation(context);
147 
148         String signatureMethodAlgorithm = signatureMethod.getAlgorithm();
149         if (secVal && Policy.restrictAlg(signatureMethodAlgorithm)) {
150             throw new MarshalException(
151                 "It is forbidden to use algorithm " + signatureMethodAlgorithm +
152                 " when secure validation is enabled"
153             );
154         }
155 
156         // unmarshal References
157         ArrayList<Reference> refList = new ArrayList<>(5);
158         Element refElem = DOMUtils.getNextSiblingElement(smElem, "Reference", XMLSignature.XMLNS);
159         refList.add(new DOMReference(refElem, context, provider));
160 
161         refElem = DOMUtils.getNextSiblingElement(refElem);
162         while (refElem != null) {
163             String name = refElem.getLocalName();
164             String namespace = refElem.getNamespaceURI();
165             if (!"Reference".equals(name) || !XMLSignature.XMLNS.equals(namespace)) {
166                 throw new MarshalException("Invalid element name: " +
167                                            namespace + ":" + name + ", expected Reference");
168             }
169             refList.add(new DOMReference(refElem, context, provider));
170             if (secVal && Policy.restrictNumReferences(refList.size())) {
171                 String error = "A maxiumum of " + Policy.maxReferences()
172                     + " references per Manifest are allowed when"
173                     + " secure validation is enabled";
174                 throw new MarshalException(error);
175             }
176             refElem = DOMUtils.getNextSiblingElement(refElem);
177         }
178         references = Collections.unmodifiableList(refList);
179     }
180 
getCanonicalizationMethod()181     public CanonicalizationMethod getCanonicalizationMethod() {
182         return canonicalizationMethod;
183     }
184 
getSignatureMethod()185     public SignatureMethod getSignatureMethod() {
186         return signatureMethod;
187     }
188 
getId()189     public String getId() {
190         return id;
191     }
192 
getReferences()193     public List<Reference> getReferences() {
194         return references;
195     }
196 
getCanonicalizedData()197     public InputStream getCanonicalizedData() {
198         return canonData;
199     }
200 
canonicalize(XMLCryptoContext context, ByteArrayOutputStream bos)201     public void canonicalize(XMLCryptoContext context, ByteArrayOutputStream bos)
202         throws XMLSignatureException {
203         if (context == null) {
204             throw new NullPointerException("context cannot be null");
205         }
206 
207         DOMSubTreeData subTree = new DOMSubTreeData(localSiElem, true);
208         try (OutputStream os = new UnsyncBufferedOutputStream(bos)) {
209             ((DOMCanonicalizationMethod)
210                 canonicalizationMethod).canonicalize(subTree, context, os);
211 
212             os.flush();
213 
214             byte[] signedInfoBytes = bos.toByteArray();
215 
216             // this whole block should only be done if LOGging is enabled
217             if (LOG.isDebugEnabled()) {
218                 LOG.debug("Canonicalized SignedInfo:");
219                 StringBuilder sb = new StringBuilder(signedInfoBytes.length);
220                 for (int i = 0; i < signedInfoBytes.length; i++) {
221                     sb.append((char)signedInfoBytes[i]);
222                 }
223                 LOG.debug(sb.toString());
224                 LOG.debug("Data to be signed/verified:" + XMLUtils.encodeToString(signedInfoBytes));
225             }
226 
227             this.canonData = new ByteArrayInputStream(signedInfoBytes);
228         } catch (TransformException te) {
229             throw new XMLSignatureException(te);
230         } catch (IOException e) {
231             LOG.debug(e.getMessage(), e);
232             // Impossible
233         }
234     }
235 
236     @Override
marshal(Node parent, String dsPrefix, DOMCryptoContext context)237     public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
238         throws MarshalException
239     {
240         ownerDoc = DOMUtils.getOwnerDocument(parent);
241         Element siElem = DOMUtils.createElement(ownerDoc, "SignedInfo",
242                                                 XMLSignature.XMLNS, dsPrefix);
243 
244         // create and append CanonicalizationMethod element
245         DOMCanonicalizationMethod dcm =
246             (DOMCanonicalizationMethod)canonicalizationMethod;
247         dcm.marshal(siElem, dsPrefix, context);
248 
249         // create and append SignatureMethod element
250         ((DOMStructure)signatureMethod).marshal(siElem, dsPrefix, context);
251 
252         // create and append Reference elements
253         for (Reference reference : references) {
254             ((DOMReference)reference).marshal(siElem, dsPrefix, context);
255         }
256 
257         // append Id attribute
258         DOMUtils.setAttributeID(siElem, "Id", id);
259 
260         parent.appendChild(siElem);
261         localSiElem = siElem;
262     }
263 
264     @Override
equals(Object o)265     public boolean equals(Object o) {
266         if (this == o) {
267             return true;
268         }
269 
270         if (!(o instanceof SignedInfo)) {
271             return false;
272         }
273         SignedInfo osi = (SignedInfo)o;
274 
275         boolean idEqual = id == null ? osi.getId() == null
276                                       : id.equals(osi.getId());
277 
278         return canonicalizationMethod.equals(osi.getCanonicalizationMethod())
279                 && signatureMethod.equals(osi.getSignatureMethod()) &&
280                 references.equals(osi.getReferences()) && idEqual;
281     }
282 
283     @SuppressWarnings("unchecked")
getSignedInfoReferences(SignedInfo si)284     public static List<Reference> getSignedInfoReferences(SignedInfo si) {
285         return si.getReferences();
286     }
287 
288     @Override
hashCode()289     public int hashCode() {
290         int result = 17;
291         if (id != null) {
292             result = 31 * result + id.hashCode();
293         }
294         result = 31 * result + canonicalizationMethod.hashCode();
295         result = 31 * result + signatureMethod.hashCode();
296         result = 31 * result + references.hashCode();
297 
298         return result;
299     }
300 }
301