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