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