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: DOMKeyInfo.java 1333869 2012-05-04 10:42:44Z coheigea $
  28  */
  29 package org.jcp.xml.dsig.internal.dom;
  30 
  31 import javax.xml.crypto.*;
  32 import javax.xml.crypto.dsig.*;
  33 import javax.xml.crypto.dsig.keyinfo.KeyInfo;
  34 import javax.xml.crypto.dom.*;
  35 
  36 import java.security.Provider;
  37 import java.util.*;
  38 
  39 import org.w3c.dom.Attr;
  40 import org.w3c.dom.Document;
  41 import org.w3c.dom.Element;
  42 import org.w3c.dom.Node;
  43 import org.w3c.dom.NodeList;
  44 
  45 /**
  46  * DOM-based implementation of KeyInfo.
  47  *
  48  * @author Sean Mullan
  49  */
  50 public final class DOMKeyInfo extends DOMStructure implements KeyInfo {
  51 
  52     private final String id;
  53     private final List<XMLStructure> keyInfoTypes;
  54 
  55     /**
  56      * Creates a <code>DOMKeyInfo</code>.
  57      *
  58      * @param content a list of one or more {@link XMLStructure}s representing
  59      *    key information types. The list is defensively copied to protect
  60      *    against subsequent modification.
  61      * @param id an ID attribute
  62      * @throws NullPointerException if <code>content</code> is <code>null</code>
  63      * @throws IllegalArgumentException if <code>content</code> is empty
  64      * @throws ClassCastException if <code>content</code> contains any entries
  65      *    that are not of type {@link XMLStructure}
  66      */
  67     public DOMKeyInfo(List<? extends XMLStructure> content, String id) {
  68         if (content == null) {
  69             throw new NullPointerException("content cannot be null");
  70         }
  71         this.keyInfoTypes =
  72             Collections.unmodifiableList(new ArrayList<XMLStructure>(content));
  73         if (this.keyInfoTypes.isEmpty()) {
  74             throw new IllegalArgumentException("content cannot be empty");
  75         }
  76         for (int i = 0, size = this.keyInfoTypes.size(); i < size; i++) {
  77             if (!(this.keyInfoTypes.get(i) instanceof XMLStructure)) {
  78                 throw new ClassCastException
  79                     ("content["+i+"] is not a valid KeyInfo type");
  80             }
  81         }
  82         this.id = id;
  83     }
  84 
  85     /**
  86      * Creates a <code>DOMKeyInfo</code> from XML.
  87      *
  88      * @param kiElem KeyInfo element
  89      */
  90     public DOMKeyInfo(Element kiElem, XMLCryptoContext context,
  91                       Provider provider)
  92         throws MarshalException
  93     {
  94         // get Id attribute, if specified
  95         Attr attr = kiElem.getAttributeNodeNS(null, "Id");
  96         if (attr != null) {
  97             id = attr.getValue();
  98             kiElem.setIdAttributeNode(attr, true);
  99         } else {
 100             id = null;
 101         }
 102 
 103         // get all children nodes
 104         NodeList nl = kiElem.getChildNodes();
 105         int length = nl.getLength();
 106         if (length < 1) {
 107             throw new MarshalException
 108                 ("KeyInfo must contain at least one type");
 109         }
 110         List<XMLStructure> content = new ArrayList<XMLStructure>(length);
 111         for (int i = 0; i < length; i++) {
 112             Node child = nl.item(i);
 113             // ignore all non-Element nodes
 114             if (child.getNodeType() != Node.ELEMENT_NODE) {
 115                 continue;
 116             }
 117             Element childElem = (Element)child;
 118             String localName = childElem.getLocalName();
 119             if (localName.equals("X509Data")) {
 120                 content.add(new DOMX509Data(childElem));
 121             } else if (localName.equals("KeyName")) {
 122                 content.add(new DOMKeyName(childElem));
 123             } else if (localName.equals("KeyValue")) {
 124                 content.add(DOMKeyValue.unmarshal(childElem));
 125             } else if (localName.equals("RetrievalMethod")) {
 126                 content.add(new DOMRetrievalMethod(childElem,
 127                                                    context, provider));
 128             } else if (localName.equals("PGPData")) {
 129                 content.add(new DOMPGPData(childElem));
 130             } else { //may be MgmtData, SPKIData or element from other namespace
 131                 content.add(new javax.xml.crypto.dom.DOMStructure((childElem)));
 132             }
 133         }
 134         keyInfoTypes = Collections.unmodifiableList(content);
 135     }
 136 
 137     public String getId() {
 138         return id;
 139     }
 140 
 141     public List<XMLStructure> getContent() {
 142         return keyInfoTypes;
 143     }
 144 
 145     public void marshal(XMLStructure parent, XMLCryptoContext context)
 146         throws MarshalException
 147     {
 148         if (parent == null) {
 149             throw new NullPointerException("parent is null");
 150         }
 151         if (!(parent instanceof javax.xml.crypto.dom.DOMStructure)) {
 152             throw new ClassCastException("parent must be of type DOMStructure");
 153         }
 154 
 155         Node pNode = ((javax.xml.crypto.dom.DOMStructure)parent).getNode();
 156         String dsPrefix = DOMUtils.getSignaturePrefix(context);
 157         Element kiElem = DOMUtils.createElement
 158             (DOMUtils.getOwnerDocument(pNode), "KeyInfo",
 159              XMLSignature.XMLNS, dsPrefix);
 160         if (dsPrefix == null || dsPrefix.length() == 0) {
 161             kiElem.setAttributeNS("http://www.w3.org/2000/xmlns/",
 162                                   "xmlns", XMLSignature.XMLNS);
 163         } else {
 164             kiElem.setAttributeNS("http://www.w3.org/2000/xmlns/",
 165                                   "xmlns:" + dsPrefix, XMLSignature.XMLNS);
 166         }
 167         marshal(pNode, kiElem, null, dsPrefix, (DOMCryptoContext)context);
 168     }
 169 
 170     public void marshal(Node parent, String dsPrefix,
 171                         DOMCryptoContext context)
 172         throws MarshalException
 173     {
 174         marshal(parent, null, dsPrefix, context);
 175     }
 176 
 177     public void marshal(Node parent, Node nextSibling, String dsPrefix,
 178                         DOMCryptoContext context)
 179         throws MarshalException
 180     {
 181         Document ownerDoc = DOMUtils.getOwnerDocument(parent);
 182         Element kiElem = DOMUtils.createElement(ownerDoc, "KeyInfo",
 183                                                 XMLSignature.XMLNS, dsPrefix);
 184         marshal(parent, kiElem, nextSibling, dsPrefix, context);
 185     }
 186 
 187     private void marshal(Node parent, Element kiElem, Node nextSibling,
 188                          String dsPrefix, DOMCryptoContext context)
 189         throws MarshalException
 190     {
 191         // create and append KeyInfoType elements
 192         for (XMLStructure kiType : keyInfoTypes) {
 193             if (kiType instanceof DOMStructure) {
 194                 ((DOMStructure)kiType).marshal(kiElem, dsPrefix, context);
 195             } else {
 196                 DOMUtils.appendChild(kiElem,
 197                     ((javax.xml.crypto.dom.DOMStructure)kiType).getNode());
 198             }
 199         }
 200 
 201         // append id attribute
 202         DOMUtils.setAttributeID(kiElem, "Id", id);
 203 
 204         parent.insertBefore(kiElem, nextSibling);
 205     }
 206 
 207     @Override
 208     public boolean equals(Object o) {
 209         if (this == o) {
 210             return true;
 211         }
 212 
 213         if (!(o instanceof KeyInfo)) {
 214             return false;
 215         }
 216         KeyInfo oki = (KeyInfo)o;
 217 
 218         boolean idsEqual = (id == null ? oki.getId() == null
 219                                        : id.equals(oki.getId()));
 220 
 221         return (keyInfoTypes.equals(oki.getContent()) && idsEqual);
 222     }
 223 
 224     @Override
 225     public int hashCode() {
 226         int result = 17;
 227         if (id != null) {
 228             result = 31 * result + id.hashCode();
 229         }
 230         result = 31 * result + keyInfoTypes.hashCode();
 231 
 232         return result;
 233     }
 234 }