1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2005 The Apache Software Foundation.
   7  *
   8  *  Licensed under the Apache License, Version 2.0 (the "License");
   9  *  you may not use this file except in compliance with the License.
  10  *  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  *  Unless required by applicable law or agreed to in writing, software
  15  *  distributed under the License is distributed on an "AS IS" BASIS,
  16  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  *  See the License for the specific language governing permissions and
  18  *  limitations under the License.
  19  *
  20  */
  21 /*
  22  * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 /*
  25  * $Id: DOMKeyInfo.java,v 1.2 2008/07/24 15:20:32 mullan Exp $
  26  */
  27 package org.jcp.xml.dsig.internal.dom;
  28 
  29 import javax.xml.crypto.*;
  30 import javax.xml.crypto.dsig.*;
  31 import javax.xml.crypto.dsig.dom.DOMSignContext;
  32 import javax.xml.crypto.dsig.keyinfo.KeyInfo;
  33 import javax.xml.crypto.dom.*;
  34 
  35 import java.security.Provider;
  36 import java.util.*;
  37 import org.w3c.dom.Attr;
  38 import org.w3c.dom.Document;
  39 import org.w3c.dom.Element;
  40 import org.w3c.dom.Node;
  41 import org.w3c.dom.NodeList;
  42 
  43 /**
  44  * DOM-based implementation of KeyInfo.
  45  *
  46  * @author Sean Mullan
  47  */
  48 public final class DOMKeyInfo extends DOMStructure implements KeyInfo {
  49 
  50     private final String id;
  51     private final List keyInfoTypes;
  52 
  53     /**
  54      * Creates a <code>DOMKeyInfo</code>.
  55      *
  56      * @param content a list of one or more {@link XMLStructure}s representing
  57      *    key information types. The list is defensively copied to protect
  58      *    against subsequent modification.
  59      * @param id an ID attribute
  60      * @throws NullPointerException if <code>content</code> is <code>null</code>
  61      * @throws IllegalArgumentException if <code>content</code> is empty
  62      * @throws ClassCastException if <code>content</code> contains any entries
  63      *    that are not of type {@link XMLStructure}
  64      */
  65     public DOMKeyInfo(List content, String id) {
  66         if (content == null) {
  67             throw new NullPointerException("content cannot be null");
  68         }
  69         List typesCopy = new ArrayList(content);
  70         if (typesCopy.isEmpty()) {
  71             throw new IllegalArgumentException("content cannot be empty");
  72         }
  73         for (int i = 0, size = typesCopy.size(); i < size; i++) {
  74             if (!(typesCopy.get(i) instanceof XMLStructure)) {
  75                 throw new ClassCastException
  76                     ("content["+i+"] is not a valid KeyInfo type");
  77             }
  78         }
  79         this.keyInfoTypes = Collections.unmodifiableList(typesCopy);
  80         this.id = id;
  81     }
  82 
  83     /**
  84      * Creates a <code>DOMKeyInfo</code> from XML.
  85      *
  86      * @param kiElem KeyInfo element
  87      */
  88     public DOMKeyInfo(Element kiElem, XMLCryptoContext context,
  89         Provider provider) throws MarshalException {
  90         // get Id attribute, if specified
  91         Attr attr = kiElem.getAttributeNodeNS(null, "Id");
  92         if (attr != null) {
  93             id = attr.getValue();
  94             kiElem.setIdAttributeNode(attr, true);
  95         } else {
  96             id = null;
  97         }
  98 
  99         // get all children nodes
 100         NodeList nl = kiElem.getChildNodes();
 101         int length = nl.getLength();
 102         if (length < 1) {
 103             throw new MarshalException
 104                 ("KeyInfo must contain at least one type");
 105         }
 106         List content = new ArrayList(length);
 107         for (int i = 0; i < length; i++) {
 108             Node child = nl.item(i);
 109             // ignore all non-Element nodes
 110             if (child.getNodeType() != Node.ELEMENT_NODE) {
 111                 continue;
 112             }
 113             Element childElem = (Element) child;
 114             String localName = childElem.getLocalName();
 115             if (localName.equals("X509Data")) {
 116                 content.add(new DOMX509Data(childElem));
 117             } else if (localName.equals("KeyName")) {
 118                 content.add(new DOMKeyName(childElem));
 119             } else if (localName.equals("KeyValue")) {
 120                 content.add(new DOMKeyValue(childElem));
 121             } else if (localName.equals("RetrievalMethod")) {
 122                 content.add
 123                     (new DOMRetrievalMethod(childElem, context, provider));
 124             } else if (localName.equals("PGPData")) {
 125                 content.add(new DOMPGPData(childElem));
 126             } else { //may be MgmtData, SPKIData or element from other namespace
 127                 content.add(new javax.xml.crypto.dom.DOMStructure((childElem)));
 128             }
 129         }
 130         keyInfoTypes = Collections.unmodifiableList(content);
 131     }
 132 
 133     public String getId() {
 134         return id;
 135     }
 136 
 137     public List getContent() {
 138         return keyInfoTypes;
 139     }
 140 
 141     public void marshal(XMLStructure parent, XMLCryptoContext context)
 142         throws MarshalException {
 143         if (parent == null) {
 144             throw new NullPointerException("parent is null");
 145         }
 146 
 147         Node pNode = ((javax.xml.crypto.dom.DOMStructure) parent).getNode();
 148         String dsPrefix = DOMUtils.getSignaturePrefix(context);
 149         Element kiElem = DOMUtils.createElement
 150             (DOMUtils.getOwnerDocument(pNode), "KeyInfo",
 151              XMLSignature.XMLNS, dsPrefix);
 152         if (dsPrefix == null || dsPrefix.length() == 0) {
 153             kiElem.setAttributeNS
 154                 ("http://www.w3.org/2000/xmlns/", "xmlns", XMLSignature.XMLNS);
 155         } else {
 156             kiElem.setAttributeNS
 157                 ("http://www.w3.org/2000/xmlns/", "xmlns:" + dsPrefix,
 158                  XMLSignature.XMLNS);
 159         }
 160         marshal(pNode, kiElem, null, dsPrefix, (DOMCryptoContext) context);
 161     }
 162 
 163     public void marshal(Node parent, String dsPrefix,
 164         DOMCryptoContext context) throws MarshalException {
 165         marshal(parent, null, dsPrefix, context);
 166     }
 167 
 168     public void marshal(Node parent, Node nextSibling, String dsPrefix,
 169         DOMCryptoContext context) throws MarshalException {
 170         Document ownerDoc = DOMUtils.getOwnerDocument(parent);
 171 
 172         Element kiElem = DOMUtils.createElement
 173             (ownerDoc, "KeyInfo", XMLSignature.XMLNS, dsPrefix);
 174         marshal(parent, kiElem, nextSibling, dsPrefix, context);
 175     }
 176 
 177     private void marshal(Node parent, Element kiElem, Node nextSibling,
 178         String dsPrefix, DOMCryptoContext context) throws MarshalException {
 179         // create and append KeyInfoType elements
 180         for (int i = 0, size = keyInfoTypes.size(); i < size; i++) {
 181             XMLStructure kiType = (XMLStructure) keyInfoTypes.get(i);
 182             if (kiType instanceof DOMStructure) {
 183                 ((DOMStructure) kiType).marshal(kiElem, dsPrefix, context);
 184             } else {
 185                 DOMUtils.appendChild(kiElem,
 186                     ((javax.xml.crypto.dom.DOMStructure) kiType).getNode());
 187             }
 188         }
 189 
 190         // append id attribute
 191         DOMUtils.setAttributeID(kiElem, "Id", id);
 192 
 193         parent.insertBefore(kiElem, nextSibling);
 194     }
 195 
 196     public boolean equals(Object o) {
 197         if (this == o) {
 198             return true;
 199         }
 200 
 201         if (!(o instanceof KeyInfo)) {
 202             return false;
 203         }
 204         KeyInfo oki = (KeyInfo) o;
 205 
 206         boolean idsEqual = (id == null ? oki.getId() == null :
 207             id.equals(oki.getId()));
 208 
 209         return (keyInfoTypes.equals(oki.getContent()) && idsEqual);
 210     }
 211 }