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