/* * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * $Id: DOMSignedInfo.java,v 1.30 2005/09/23 20:14:07 mullan Exp $ */ package org.jcp.xml.dsig.internal.dom; import javax.xml.crypto.*; import javax.xml.crypto.dom.DOMCryptoContext; import javax.xml.crypto.dsig.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import com.sun.org.apache.xml.internal.security.utils.Base64; import com.sun.org.apache.xml.internal.security.utils.UnsyncBufferedOutputStream; import com.sun.org.apache.xml.internal.security.utils.XMLUtils; /** * DOM-based implementation of SignedInfo. * * @author Sean Mullan */ public final class DOMSignedInfo extends DOMStructure implements SignedInfo { private static Logger log = Logger.getLogger("org.jcp.xml.dsig.internal.dom"); private List references; private CanonicalizationMethod canonicalizationMethod; private SignatureMethod signatureMethod; private String id; private Document ownerDoc; private Element localSiElem; private InputStream canonData; /** * Creates a <code>DOMSignedInfo</code> from the specified parameters. Use * this constructor when the <code>Id</code> is not specified. * * @param cm the canonicalization method * @param sm the signature method * @param references the list of references. The list is copied. * @throws NullPointerException if * <code>cm</code>, <code>sm</code>, or <code>references</code> is * <code>null</code> * @throws IllegalArgumentException if <code>references</code> is empty * @throws ClassCastException if any of the references are not of * type <code>Reference</code> */ public DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm, List references) { if (cm == null || sm == null || references == null) { throw new NullPointerException(); } this.canonicalizationMethod = cm; this.signatureMethod = sm; this.references = Collections.unmodifiableList (new ArrayList(references)); if (this.references.isEmpty()) { throw new IllegalArgumentException("list of references must " + "contain at least one entry"); } for (int i = 0, size = this.references.size(); i < size; i++) { Object obj = this.references.get(i); if (!(obj instanceof Reference)) { throw new ClassCastException("list of references contains " + "an illegal type"); } } } /** * Creates a <code>DOMSignedInfo</code> from the specified parameters. * * @param cm the canonicalization method * @param sm the signature method * @param references the list of references. The list is copied. * @param id an optional identifer that will allow this * <code>SignedInfo</code> to be referenced by other signatures and * objects * @throws NullPointerException if <code>cm</code>, <code>sm</code>, * or <code>references</code> is <code>null</code> * @throws IllegalArgumentException if <code>references</code> is empty * @throws ClassCastException if any of the references are not of * type <code>Reference</code> */ public DOMSignedInfo(CanonicalizationMethod cm, SignatureMethod sm, List references, String id) { this(cm, sm, references); this.id = id; } /** * Creates a <code>DOMSignedInfo</code> from an element. * * @param siElem a SignedInfo element */ public DOMSignedInfo(Element siElem, XMLCryptoContext context) throws MarshalException { localSiElem = siElem; ownerDoc = siElem.getOwnerDocument(); // get Id attribute, if specified id = DOMUtils.getAttributeValue(siElem, "Id"); // unmarshal CanonicalizationMethod Element cmElem = DOMUtils.getFirstChildElement(siElem); canonicalizationMethod = new DOMCanonicalizationMethod(cmElem, context); // unmarshal SignatureMethod Element smElem = DOMUtils.getNextSiblingElement(cmElem); signatureMethod = DOMSignatureMethod.unmarshal(smElem); // unmarshal References ArrayList refList = new ArrayList(5); Element refElem = DOMUtils.getNextSiblingElement(smElem); while (refElem != null) { refList.add(new DOMReference(refElem, context)); refElem = DOMUtils.getNextSiblingElement(refElem); } references = Collections.unmodifiableList(refList); } public CanonicalizationMethod getCanonicalizationMethod() { return canonicalizationMethod; } public SignatureMethod getSignatureMethod() { return signatureMethod; } public String getId() { return id; } public List getReferences() { return references; } public InputStream getCanonicalizedData() { return canonData; } public void canonicalize(XMLCryptoContext context,ByteArrayOutputStream bos) throws XMLSignatureException { if (context == null) { throw new NullPointerException("context cannot be null"); } OutputStream os = new UnsyncBufferedOutputStream(bos); try { os.close(); } catch (IOException e) { // Impossible } DOMSubTreeData subTree = new DOMSubTreeData(localSiElem, true); OctetStreamData data = null; try { data = (OctetStreamData) ((DOMCanonicalizationMethod) canonicalizationMethod).canonicalize(subTree, context, os); } catch (TransformException te) { throw new XMLSignatureException(te); } byte[] signedInfoBytes = bos.toByteArray(); // this whole block should only be done if logging is enabled if (log.isLoggable(Level.FINE)) { InputStreamReader isr = new InputStreamReader (new ByteArrayInputStream(signedInfoBytes)); char[] siBytes = new char[signedInfoBytes.length]; try { isr.read(siBytes); } catch (IOException ioex) {} //ignore since this is logging code log.log(Level.FINE, "Canonicalized SignedInfo:\n" + new String(siBytes)); log.log(Level.FINE, "Data to be signed/verified:" + Base64.encode(signedInfoBytes)); } this.canonData = new ByteArrayInputStream(signedInfoBytes); } public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) throws MarshalException { ownerDoc = DOMUtils.getOwnerDocument(parent); Element siElem = DOMUtils.createElement (ownerDoc, "SignedInfo", XMLSignature.XMLNS, dsPrefix); // create and append CanonicalizationMethod element DOMCanonicalizationMethod dcm = (DOMCanonicalizationMethod) canonicalizationMethod; dcm.marshal(siElem, dsPrefix, context); // create and append SignatureMethod element ((DOMSignatureMethod) signatureMethod).marshal (siElem, dsPrefix, context); // create and append Reference elements for (int i = 0, size = references.size(); i < size; i++) { DOMReference reference = (DOMReference) references.get(i); reference.marshal(siElem, dsPrefix, context); } // append Id attribute DOMUtils.setAttributeID(siElem, "Id", id); parent.appendChild(siElem); localSiElem = siElem; } public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof SignedInfo)) { return false; } SignedInfo osi = (SignedInfo) o; boolean idEqual = (id == null ? osi.getId() == null : id.equals(osi.getId())); return (canonicalizationMethod.equals(osi.getCanonicalizationMethod()) && signatureMethod.equals(osi.getSignatureMethod()) && references.equals(osi.getReferences()) && idEqual); } }