1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.xml.security.signature; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.IOException; 23 import java.io.OutputStream; 24 import javax.crypto.SecretKey; 25 import javax.crypto.spec.SecretKeySpec; 26 import javax.xml.XMLConstants; 27 import javax.xml.parsers.ParserConfigurationException; 28 29 import org.apache.xml.security.algorithms.SignatureAlgorithm; 30 import org.apache.xml.security.c14n.CanonicalizationException; 31 import org.apache.xml.security.c14n.Canonicalizer; 32 import org.apache.xml.security.c14n.InvalidCanonicalizerException; 33 import org.apache.xml.security.exceptions.XMLSecurityException; 34 import org.apache.xml.security.utils.Constants; 35 import org.apache.xml.security.utils.XMLUtils; 36 import org.apache.xml.security.transforms.params.InclusiveNamespaces; 37 import org.w3c.dom.Document; 38 import org.w3c.dom.Element; 39 import org.w3c.dom.Node; 40 import org.xml.sax.SAXException; 41 42 /** 43 * Handles <code><ds:SignedInfo></code> elements 44 * This <code>SignedInfo<code> element includes the canonicalization algorithm, 45 * a signature algorithm, and one or more references. 46 * 47 * @author Christian Geuer-Pollmann 48 */ 49 public class SignedInfo extends Manifest { 50 51 /** Field signatureAlgorithm */ 52 private SignatureAlgorithm signatureAlgorithm = null; 53 54 /** Field c14nizedBytes */ 55 private byte[] c14nizedBytes = null; 56 57 private Element c14nMethod; 58 private Element signatureMethod; 59 60 /** 61 * Overwrites {@link Manifest#addDocument} because it creates another 62 * Element. 63 * 64 * @param doc the {@link Document} in which <code>XMLsignature</code> will 65 * be placed 66 * @throws XMLSecurityException 67 */ SignedInfo(Document doc)68 public SignedInfo(Document doc) throws XMLSecurityException { 69 this(doc, XMLSignature.ALGO_ID_SIGNATURE_DSA, 70 Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS); 71 } 72 73 /** 74 * Constructs {@link SignedInfo} using given Canonicalization algorithm and 75 * Signature algorithm. 76 * 77 * @param doc <code>SignedInfo</code> is placed in this document 78 * @param signatureMethodURI URI representation of the Digest and 79 * Signature algorithm 80 * @param canonicalizationMethodURI URI representation of the 81 * Canonicalization method 82 * @throws XMLSecurityException 83 */ SignedInfo( Document doc, String signatureMethodURI, String canonicalizationMethodURI )84 public SignedInfo( 85 Document doc, String signatureMethodURI, String canonicalizationMethodURI 86 ) throws XMLSecurityException { 87 this(doc, signatureMethodURI, 0, canonicalizationMethodURI); 88 } 89 90 /** 91 * Constructor SignedInfo 92 * 93 * @param doc <code>SignedInfo</code> is placed in this document 94 * @param signatureMethodURI URI representation of the Digest and 95 * Signature algorithm 96 * @param hMACOutputLength 97 * @param canonicalizationMethodURI URI representation of the 98 * Canonicalization method 99 * @throws XMLSecurityException 100 */ SignedInfo( Document doc, String signatureMethodURI, int hMACOutputLength, String canonicalizationMethodURI )101 public SignedInfo( 102 Document doc, String signatureMethodURI, 103 int hMACOutputLength, String canonicalizationMethodURI 104 ) throws XMLSecurityException { 105 super(doc); 106 107 c14nMethod = 108 XMLUtils.createElementInSignatureSpace(this.doc, Constants._TAG_CANONICALIZATIONMETHOD); 109 110 c14nMethod.setAttributeNS(null, Constants._ATT_ALGORITHM, canonicalizationMethodURI); 111 this.constructionElement.appendChild(c14nMethod); 112 XMLUtils.addReturnToElement(this.constructionElement); 113 114 if (hMACOutputLength > 0) { 115 this.signatureAlgorithm = 116 new SignatureAlgorithm(this.doc, signatureMethodURI, hMACOutputLength); 117 } else { 118 this.signatureAlgorithm = new SignatureAlgorithm(this.doc, signatureMethodURI); 119 } 120 121 signatureMethod = this.signatureAlgorithm.getElement(); 122 this.constructionElement.appendChild(signatureMethod); 123 XMLUtils.addReturnToElement(this.constructionElement); 124 } 125 126 /** 127 * @param doc 128 * @param signatureMethodElem 129 * @param canonicalizationMethodElem 130 * @throws XMLSecurityException 131 */ SignedInfo( Document doc, Element signatureMethodElem, Element canonicalizationMethodElem )132 public SignedInfo( 133 Document doc, Element signatureMethodElem, Element canonicalizationMethodElem 134 ) throws XMLSecurityException { 135 super(doc); 136 // Check this? 137 this.c14nMethod = canonicalizationMethodElem; 138 this.constructionElement.appendChild(c14nMethod); 139 XMLUtils.addReturnToElement(this.constructionElement); 140 141 this.signatureAlgorithm = 142 new SignatureAlgorithm(signatureMethodElem, null); 143 144 signatureMethod = this.signatureAlgorithm.getElement(); 145 this.constructionElement.appendChild(signatureMethod); 146 147 XMLUtils.addReturnToElement(this.constructionElement); 148 } 149 150 /** 151 * Build a {@link SignedInfo} from an {@link Element} 152 * 153 * @param element <code>SignedInfo</code> 154 * @param baseURI the URI of the resource where the XML instance was stored 155 * @throws XMLSecurityException 156 * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0033.html"> 157 * Question</A> 158 * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0054.html"> 159 * Answer</A> 160 */ SignedInfo(Element element, String baseURI)161 public SignedInfo(Element element, String baseURI) throws XMLSecurityException { 162 this(element, baseURI, false); 163 } 164 165 /** 166 * Build a {@link SignedInfo} from an {@link Element} 167 * 168 * @param element <code>SignedInfo</code> 169 * @param baseURI the URI of the resource where the XML instance was stored 170 * @param secureValidation whether secure validation is enabled or not 171 * @throws XMLSecurityException 172 * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0033.html"> 173 * Question</A> 174 * @see <A HREF="http://lists.w3.org/Archives/Public/w3c-ietf-xmldsig/2001OctDec/0054.html"> 175 * Answer</A> 176 */ SignedInfo( Element element, String baseURI, boolean secureValidation )177 public SignedInfo( 178 Element element, String baseURI, boolean secureValidation 179 ) throws XMLSecurityException { 180 // Parse the Reference children and Id attribute in the Manifest 181 super(reparseSignedInfoElem(element), baseURI, secureValidation); 182 183 c14nMethod = XMLUtils.getNextElement(element.getFirstChild()); 184 signatureMethod = XMLUtils.getNextElement(c14nMethod.getNextSibling()); 185 this.signatureAlgorithm = 186 new SignatureAlgorithm(signatureMethod, this.getBaseURI(), secureValidation); 187 } 188 reparseSignedInfoElem(Element element)189 private static Element reparseSignedInfoElem(Element element) 190 throws XMLSecurityException { 191 /* 192 * If a custom canonicalizationMethod is used, canonicalize 193 * ds:SignedInfo, reparse it into a new document 194 * and replace the original not-canonicalized ds:SignedInfo by 195 * the re-parsed canonicalized one. 196 */ 197 Element c14nMethod = XMLUtils.getNextElement(element.getFirstChild()); 198 String c14nMethodURI = 199 c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM); 200 if (!(c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS) || 201 c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS) || 202 c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS) || 203 c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N_EXCL_WITH_COMMENTS) || 204 c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS) || 205 c14nMethodURI.equals(Canonicalizer.ALGO_ID_C14N11_WITH_COMMENTS))) { 206 // the c14n is not a secure one and can rewrite the URIs or like 207 // so reparse the SignedInfo to be sure 208 try { 209 Canonicalizer c14nizer = 210 Canonicalizer.getInstance(c14nMethodURI); 211 212 byte[] c14nizedBytes = c14nizer.canonicalizeSubtree(element); 213 javax.xml.parsers.DocumentBuilderFactory dbf = 214 javax.xml.parsers.DocumentBuilderFactory.newInstance(); 215 dbf.setNamespaceAware(true); 216 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); 217 javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(); 218 Document newdoc = 219 db.parse(new ByteArrayInputStream(c14nizedBytes)); 220 Node imported = 221 element.getOwnerDocument().importNode(newdoc.getDocumentElement(), true); 222 223 element.getParentNode().replaceChild(imported, element); 224 225 return (Element) imported; 226 } catch (ParserConfigurationException ex) { 227 throw new XMLSecurityException("empty", ex); 228 } catch (IOException ex) { 229 throw new XMLSecurityException("empty", ex); 230 } catch (SAXException ex) { 231 throw new XMLSecurityException("empty", ex); 232 } 233 } 234 return element; 235 } 236 237 /** 238 * Tests core validation process 239 * 240 * @return true if verification was successful 241 * @throws MissingResourceFailureException 242 * @throws XMLSecurityException 243 */ verify()244 public boolean verify() 245 throws MissingResourceFailureException, XMLSecurityException { 246 return super.verifyReferences(false); 247 } 248 249 /** 250 * Tests core validation process 251 * 252 * @param followManifests defines whether the verification process has to verify referenced <CODE>ds:Manifest</CODE>s, too 253 * @return true if verification was successful 254 * @throws MissingResourceFailureException 255 * @throws XMLSecurityException 256 */ verify(boolean followManifests)257 public boolean verify(boolean followManifests) 258 throws MissingResourceFailureException, XMLSecurityException { 259 return super.verifyReferences(followManifests); 260 } 261 262 /** 263 * Returns getCanonicalizedOctetStream 264 * 265 * @return the canonicalization result octet stream of <code>SignedInfo</code> element 266 * @throws CanonicalizationException 267 * @throws InvalidCanonicalizerException 268 * @throws XMLSecurityException 269 */ getCanonicalizedOctetStream()270 public byte[] getCanonicalizedOctetStream() 271 throws CanonicalizationException, InvalidCanonicalizerException, XMLSecurityException { 272 if (this.c14nizedBytes == null) { 273 Canonicalizer c14nizer = 274 Canonicalizer.getInstance(this.getCanonicalizationMethodURI()); 275 276 this.c14nizedBytes = 277 c14nizer.canonicalizeSubtree(this.constructionElement); 278 } 279 280 // make defensive copy 281 return (byte[]) this.c14nizedBytes.clone(); 282 } 283 284 /** 285 * Output the C14n stream to the given OutputStream. 286 * @param os 287 * @throws CanonicalizationException 288 * @throws InvalidCanonicalizerException 289 * @throws XMLSecurityException 290 */ signInOctetStream(OutputStream os)291 public void signInOctetStream(OutputStream os) 292 throws CanonicalizationException, InvalidCanonicalizerException, XMLSecurityException { 293 if (this.c14nizedBytes == null) { 294 Canonicalizer c14nizer = 295 Canonicalizer.getInstance(this.getCanonicalizationMethodURI()); 296 c14nizer.setWriter(os); 297 String inclusiveNamespaces = this.getInclusiveNamespaces(); 298 299 if (inclusiveNamespaces == null) { 300 c14nizer.canonicalizeSubtree(this.constructionElement); 301 } else { 302 c14nizer.canonicalizeSubtree(this.constructionElement, inclusiveNamespaces); 303 } 304 } else { 305 try { 306 os.write(this.c14nizedBytes); 307 } catch (IOException e) { 308 throw new RuntimeException(e); 309 } 310 } 311 } 312 313 /** 314 * Returns the Canonicalization method URI 315 * 316 * @return the Canonicalization method URI 317 */ getCanonicalizationMethodURI()318 public String getCanonicalizationMethodURI() { 319 return c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM); 320 } 321 322 /** 323 * Returns the Signature method URI 324 * 325 * @return the Signature method URI 326 */ getSignatureMethodURI()327 public String getSignatureMethodURI() { 328 Element signatureElement = this.getSignatureMethodElement(); 329 330 if (signatureElement != null) { 331 return signatureElement.getAttributeNS(null, Constants._ATT_ALGORITHM); 332 } 333 334 return null; 335 } 336 337 /** 338 * Method getSignatureMethodElement 339 * @return returns the SignatureMethod Element 340 * 341 */ getSignatureMethodElement()342 public Element getSignatureMethodElement() { 343 return signatureMethod; 344 } 345 346 /** 347 * Creates a SecretKey for the appropriate Mac algorithm based on a 348 * byte[] array password. 349 * 350 * @param secretKeyBytes 351 * @return the secret key for the SignedInfo element. 352 */ createSecretKey(byte[] secretKeyBytes)353 public SecretKey createSecretKey(byte[] secretKeyBytes) { 354 return new SecretKeySpec(secretKeyBytes, this.signatureAlgorithm.getJCEAlgorithmString()); 355 } 356 getSignatureAlgorithm()357 protected SignatureAlgorithm getSignatureAlgorithm() { 358 return signatureAlgorithm; 359 } 360 361 /** 362 * Method getBaseLocalName 363 * @inheritDoc 364 * 365 */ getBaseLocalName()366 public String getBaseLocalName() { 367 return Constants._TAG_SIGNEDINFO; 368 } 369 getInclusiveNamespaces()370 public String getInclusiveNamespaces() { 371 String c14nMethodURI = c14nMethod.getAttributeNS(null, Constants._ATT_ALGORITHM); 372 if (!(c14nMethodURI.equals("http://www.w3.org/2001/10/xml-exc-c14n#") || 373 c14nMethodURI.equals("http://www.w3.org/2001/10/xml-exc-c14n#WithComments"))) { 374 return null; 375 } 376 377 Element inclusiveElement = XMLUtils.getNextElement(c14nMethod.getFirstChild()); 378 379 if (inclusiveElement != null) { 380 try { 381 String inclusiveNamespaces = 382 new InclusiveNamespaces( 383 inclusiveElement, 384 InclusiveNamespaces.ExclusiveCanonicalizationNamespace 385 ).getInclusiveNamespaces(); 386 return inclusiveNamespaces; 387 } catch (XMLSecurityException e) { 388 return null; 389 } 390 } 391 return null; 392 } 393 } 394