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