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, 2008, Oracle and/or its affiliates. All rights reserved. 25 */ 26 /* 27 * $Id: DOMPGPData.java 1203846 2011-11-18 21:18:17Z mullan $ 28 */ 29 package org.jcp.xml.dsig.internal.dom; 30 31 import java.util.*; 32 import javax.xml.crypto.*; 33 import javax.xml.crypto.dom.DOMCryptoContext; 34 import javax.xml.crypto.dsig.*; 35 import javax.xml.crypto.dsig.keyinfo.PGPData; 36 import org.w3c.dom.Document; 37 import org.w3c.dom.Element; 38 import org.w3c.dom.Node; 39 import org.w3c.dom.NodeList; 40 41 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; 42 import com.sun.org.apache.xml.internal.security.utils.Base64; 43 44 /** 45 * DOM-based implementation of PGPData. 46 * 47 * @author Sean Mullan 48 */ 49 public final class DOMPGPData extends DOMStructure implements PGPData { 50 51 private final byte[] keyId; 52 private final byte[] keyPacket; 53 private final List<XMLStructure> externalElements; 54 55 /** 56 * Creates a <code>DOMPGPData</code> containing the specified key packet. 57 * and optional list of external elements. 58 * 59 * @param keyPacket a PGP Key Material Packet as defined in section 5.5 of 60 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The 61 * array is cloned to prevent subsequent modification. 62 * @param other a list of {@link XMLStructure}s representing elements from 63 * an external namespace. The list is defensively copied to prevent 64 * subsequent modification. May be <code>null</code> or empty. 65 * @throws NullPointerException if <code>keyPacket</code> is 66 * <code>null</code> 67 * @throws IllegalArgumentException if the key packet is not in the 68 * correct format 69 * @throws ClassCastException if <code>other</code> contains any 70 * entries that are not of type {@link XMLStructure} 71 */ 72 public DOMPGPData(byte[] keyPacket, List<? extends XMLStructure> other) { 73 if (keyPacket == null) { 74 throw new NullPointerException("keyPacket cannot be null"); 75 } 76 if (other == null || other.isEmpty()) { 77 this.externalElements = Collections.emptyList(); 78 } else { 79 this.externalElements = 80 Collections.unmodifiableList(new ArrayList<XMLStructure>(other)); 81 for (int i = 0, size = this.externalElements.size(); i < size; i++) { 82 if (!(this.externalElements.get(i) instanceof XMLStructure)) { 83 throw new ClassCastException 84 ("other["+i+"] is not a valid PGPData type"); 85 } 86 } 87 } 88 this.keyPacket = keyPacket.clone(); 89 checkKeyPacket(keyPacket); 90 this.keyId = null; 91 } 92 93 /** 94 * Creates a <code>DOMPGPData</code> containing the specified key id and 95 * optional key packet and list of external elements. 96 * 97 * @param keyId a PGP public key id as defined in section 11.2 of 98 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The 99 * array is cloned to prevent subsequent modification. 100 * @param keyPacket a PGP Key Material Packet as defined in section 5.5 of 101 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a> (may 102 * be <code>null</code>). The array is cloned to prevent subsequent 103 * modification. 104 * @param other a list of {@link XMLStructure}s representing elements from 105 * an external namespace. The list is defensively copied to prevent 106 * subsequent modification. May be <code>null</code> or empty. 107 * @throws NullPointerException if <code>keyId</code> is <code>null</code> 108 * @throws IllegalArgumentException if the key id or packet is not in the 109 * correct format 110 * @throws ClassCastException if <code>other</code> contains any 111 * entries that are not of type {@link XMLStructure} 112 */ 113 public DOMPGPData(byte[] keyId, byte[] keyPacket, 114 List<? extends XMLStructure> other) 115 { 116 if (keyId == null) { 117 throw new NullPointerException("keyId cannot be null"); 118 } 119 // key ids must be 8 bytes 120 if (keyId.length != 8) { 121 throw new IllegalArgumentException("keyId must be 8 bytes long"); 122 } 123 if (other == null || other.isEmpty()) { 124 this.externalElements = Collections.emptyList(); 125 } else { 126 this.externalElements = 127 Collections.unmodifiableList(new ArrayList<XMLStructure>(other)); 128 for (int i = 0, size = this.externalElements.size(); i < size; i++) { 129 if (!(this.externalElements.get(i) instanceof XMLStructure)) { 130 throw new ClassCastException 131 ("other["+i+"] is not a valid PGPData type"); 132 } 133 } 134 } 135 this.keyId = keyId.clone(); 136 this.keyPacket = keyPacket == null ? null 137 : keyPacket.clone(); 138 if (keyPacket != null) { 139 checkKeyPacket(keyPacket); 140 } 141 } 142 143 /** 144 * Creates a <code>DOMPGPData</code> from an element. 145 * 146 * @param pdElem a PGPData element 147 */ 148 public DOMPGPData(Element pdElem) throws MarshalException { 149 // get all children nodes 150 byte[] keyId = null; 151 byte[] keyPacket = null; 152 NodeList nl = pdElem.getChildNodes(); 153 int length = nl.getLength(); 154 List<XMLStructure> other = new ArrayList<XMLStructure>(length); 155 for (int x = 0; x < length; x++) { 156 Node n = nl.item(x); 157 if (n.getNodeType() == Node.ELEMENT_NODE) { 158 Element childElem = (Element)n; 159 String localName = childElem.getLocalName(); 160 try { 161 if (localName.equals("PGPKeyID")) { 162 keyId = Base64.decode(childElem); 163 } else if (localName.equals("PGPKeyPacket")){ 164 keyPacket = Base64.decode(childElem); 165 } else { 166 other.add 167 (new javax.xml.crypto.dom.DOMStructure(childElem)); 168 } 169 } catch (Base64DecodingException bde) { 170 throw new MarshalException(bde); 171 } 172 } 173 } 174 this.keyId = keyId; 175 this.keyPacket = keyPacket; 176 this.externalElements = Collections.unmodifiableList(other); 177 } 178 179 public byte[] getKeyId() { 180 return (keyId == null ? null : keyId.clone()); 181 } 182 183 public byte[] getKeyPacket() { 184 return (keyPacket == null ? null : keyPacket.clone()); 185 } 186 187 public List getExternalElements() { 188 return externalElements; 189 } 190 191 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) 192 throws MarshalException 193 { 194 Document ownerDoc = DOMUtils.getOwnerDocument(parent); 195 Element pdElem = DOMUtils.createElement(ownerDoc, "PGPData", 196 XMLSignature.XMLNS, dsPrefix); 197 198 // create and append PGPKeyID element 199 if (keyId != null) { 200 Element keyIdElem = DOMUtils.createElement(ownerDoc, "PGPKeyID", 201 XMLSignature.XMLNS, 202 dsPrefix); 203 keyIdElem.appendChild 204 (ownerDoc.createTextNode(Base64.encode(keyId))); 205 pdElem.appendChild(keyIdElem); 206 } 207 208 // create and append PGPKeyPacket element 209 if (keyPacket != null) { 210 Element keyPktElem = DOMUtils.createElement(ownerDoc, 211 "PGPKeyPacket", 212 XMLSignature.XMLNS, 213 dsPrefix); 214 keyPktElem.appendChild 215 (ownerDoc.createTextNode(Base64.encode(keyPacket))); 216 pdElem.appendChild(keyPktElem); 217 } 218 219 // create and append any elements 220 for (XMLStructure extElem : externalElements) { 221 DOMUtils.appendChild(pdElem, ((javax.xml.crypto.dom.DOMStructure) 222 extElem).getNode()); 223 } 224 225 parent.appendChild(pdElem); 226 } 227 228 /** 229 * We assume packets use the new format packet syntax, as specified in 230 * section 4 of RFC 2440. 231 * 232 * This method only checks if the packet contains a valid tag. The 233 * contents of the packet should be checked by the application. 234 */ 235 private void checkKeyPacket(byte[] keyPacket) { 236 // length must be at least 3 (one byte for tag, one byte for length, 237 // and minimally one byte of content 238 if (keyPacket.length < 3) { 239 throw new IllegalArgumentException("keypacket must be at least " + 240 "3 bytes long"); 241 } 242 243 int tag = keyPacket[0]; 244 // first bit must be set 245 if ((tag & 128) != 128) { 246 throw new IllegalArgumentException("keypacket tag is invalid: " + 247 "bit 7 is not set"); 248 } 249 // make sure using new format 250 if ((tag & 64) != 64) { 251 throw new IllegalArgumentException("old keypacket tag format is " + 252 "unsupported"); 253 } 254 255 // tag value must be 6, 14, 5 or 7 256 if (((tag & 6) != 6) && ((tag & 14) != 14) && 257 ((tag & 5) != 5) && ((tag & 7) != 7)) { 258 throw new IllegalArgumentException("keypacket tag is invalid: " + 259 "must be 6, 14, 5, or 7"); 260 } 261 } 262 }