blob: e2dc505ffd1216f550cbce0dcd4f3b0c2726fc5a [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25/*
26 * $Id: DOMPGPData.java,v 1.18 2005/05/12 19:28:31 mullan Exp $
27 */
28package org.jcp.xml.dsig.internal.dom;
29
30import java.util.*;
31import javax.xml.crypto.*;
32import javax.xml.crypto.dom.DOMCryptoContext;
33import javax.xml.crypto.dsig.*;
34import javax.xml.crypto.dsig.keyinfo.PGPData;
35import org.w3c.dom.Document;
36import org.w3c.dom.Element;
37import org.w3c.dom.Node;
38import org.w3c.dom.NodeList;
39
40import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
41import com.sun.org.apache.xml.internal.security.utils.Base64;
42
43/**
44 * DOM-based implementation of PGPData.
45 *
46 * @author Sean Mullan
47 */
48public final class DOMPGPData extends DOMStructure implements PGPData {
49
50 private final byte[] keyId;
51 private final byte[] keyPacket;
52 private final List externalElements;
53
54 /**
55 * Creates a <code>DOMPGPData</code> containing the specified key packet.
56 * and optional list of external elements.
57 *
58 * @param keyPacket a PGP Key Material Packet as defined in section 5.5 of
59 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The
60 * array is cloned to prevent subsequent modification.
61 * @param other a list of {@link XMLStructure}s representing elements from
62 * an external namespace. The list is defensively copied to prevent
63 * subsequent modification. May be <code>null</code> or empty.
64 * @throws NullPointerException if <code>keyPacket</code> is
65 * <code>null</code>
66 * @throws IllegalArgumentException if the key packet is not in the
67 * correct format
68 * @throws ClassCastException if <code>other</code> contains any
69 * entries that are not of type {@link XMLStructure}
70 */
71 public DOMPGPData(byte[] keyPacket, List other) {
72 if (keyPacket == null) {
73 throw new NullPointerException("keyPacket cannot be null");
74 }
75 if (other == null || other.isEmpty()) {
76 this.externalElements = Collections.EMPTY_LIST;
77 } else {
78 List otherCopy = new ArrayList(other);
79 for (int i = 0, size = otherCopy.size(); i < size; i++) {
80 if (!(otherCopy.get(i) instanceof XMLStructure)) {
81 throw new ClassCastException
82 ("other["+i+"] is not a valid PGPData type");
83 }
84 }
85 this.externalElements = Collections.unmodifiableList(otherCopy);
86 }
87 this.keyPacket = (byte []) keyPacket.clone();
88 checkKeyPacket(keyPacket);
89 this.keyId = null;
90 }
91
92 /**
93 * Creates a <code>DOMPGPData</code> containing the specified key id and
94 * optional key packet and list of external elements.
95 *
96 * @param keyId a PGP public key id as defined in section 11.2 of
97 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The
98 * array is cloned to prevent subsequent modification.
99 * @param keyPacket a PGP Key Material Packet as defined in section 5.5 of
100 * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a> (may
101 * be <code>null</code>). The array is cloned to prevent subsequent
102 * modification.
103 * @param other a list of {@link XMLStructure}s representing elements from
104 * an external namespace. The list is defensively copied to prevent
105 * subsequent modification. May be <code>null</code> or empty.
106 * @throws NullPointerException if <code>keyId</code> is <code>null</code>
107 * @throws IllegalArgumentException if the key id or packet is not in the
108 * correct format
109 * @throws ClassCastException if <code>other</code> contains any
110 * entries that are not of type {@link XMLStructure}
111 */
112 public DOMPGPData(byte[] keyId, byte[] keyPacket, List other) {
113 if (keyId == null) {
114 throw new NullPointerException("keyId cannot be null");
115 }
116 // key ids must be 8 bytes
117 if (keyId.length != 8) {
118 throw new IllegalArgumentException("keyId must be 8 bytes long");
119 }
120 if (other == null || other.isEmpty()) {
121 this.externalElements = Collections.EMPTY_LIST;
122 } else {
123 List otherCopy = new ArrayList(other);
124 for (int i = 0, size = otherCopy.size(); i < size; i++) {
125 if (!(otherCopy.get(i) instanceof XMLStructure)) {
126 throw new ClassCastException
127 ("other["+i+"] is not a valid PGPData type");
128 }
129 }
130 this.externalElements = Collections.unmodifiableList(otherCopy);
131 }
132 this.keyId = (byte []) keyId.clone();
133 this.keyPacket = keyPacket == null ? null : (byte []) keyPacket.clone();
134 if (keyPacket != null) {
135 checkKeyPacket(keyPacket);
136 }
137 }
138
139 /**
140 * Creates a <code>DOMPGPData</code> from an element.
141 *
142 * @param pdElem a PGPData element
143 */
144 public DOMPGPData(Element pdElem) throws MarshalException {
145 // get all children nodes
146 byte[] keyId = null;
147 byte[] keyPacket = null;
148 NodeList nl = pdElem.getChildNodes();
149 int length = nl.getLength();
150 List other = new ArrayList(length);
151 for (int x = 0; x < length; x++) {
152 Node n = nl.item(x);
153 if (n.getNodeType() == Node.ELEMENT_NODE) {
154 Element childElem = (Element) n;
155 String localName = childElem.getLocalName();
156 try {
157 if (localName.equals("PGPKeyID")) {
158 keyId = Base64.decode(childElem);
159 } else if (localName.equals("PGPKeyPacket")){
160 keyPacket = Base64.decode(childElem);
161 } else {
162 other.add
163 (new javax.xml.crypto.dom.DOMStructure(childElem));
164 }
165 } catch (Base64DecodingException bde) {
166 throw new MarshalException(bde);
167 }
168 }
169 }
170 this.keyId = keyId;
171 this.keyPacket = keyPacket;
172 this.externalElements = Collections.unmodifiableList(other);
173 }
174
175 public byte[] getKeyId() {
176 return (keyId == null ? null : (byte []) keyId.clone());
177 }
178
179 public byte[] getKeyPacket() {
180 return (keyPacket == null ? null : (byte []) keyPacket.clone());
181 }
182
183 public List getExternalElements() {
184 return externalElements;
185 }
186
187 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context)
188 throws MarshalException {
189 Document ownerDoc = DOMUtils.getOwnerDocument(parent);
190
191 Element pdElem = DOMUtils.createElement
192 (ownerDoc, "PGPData", XMLSignature.XMLNS, dsPrefix);
193
194 // create and append PGPKeyID element
195 if (keyId != null) {
196 Element keyIdElem = DOMUtils.createElement
197 (ownerDoc, "PGPKeyID", XMLSignature.XMLNS, dsPrefix);
198 keyIdElem.appendChild
199 (ownerDoc.createTextNode(Base64.encode(keyId)));
200 pdElem.appendChild(keyIdElem);
201 }
202
203 // create and append PGPKeyPacket element
204 if (keyPacket != null) {
205 Element keyPktElem = DOMUtils.createElement
206 (ownerDoc, "PGPKeyPacket", XMLSignature.XMLNS, dsPrefix);
207 keyPktElem.appendChild
208 (ownerDoc.createTextNode(Base64.encode(keyPacket)));
209 pdElem.appendChild(keyPktElem);
210 }
211
212 // create and append any elements
213 for (int i = 0, size = externalElements.size(); i < size; i++) {
214 DOMUtils.appendChild(pdElem, ((javax.xml.crypto.dom.DOMStructure)
215 externalElements.get(i)).getNode());
216 }
217
218 parent.appendChild(pdElem);
219 }
220
221 /**
222 * We assume packets use the new format packet syntax, as specified in
223 * section 4 of RFC 2440.
224 *
225 * This method only checks if the packet contains a valid tag. The
226 * contents of the packet should be checked by the application.
227 */
228 private void checkKeyPacket(byte[] keyPacket) {
229 // length must be at least 3 (one byte for tag, one byte for length,
230 // and minimally one byte of content
231 if (keyPacket.length < 3) {
232 throw new IllegalArgumentException("keypacket must be at least " +
233 "3 bytes long");
234 }
235
236 int tag = keyPacket[0];
237 // first bit must be set
238 if ((tag & 128) != 128) {
239 throw new IllegalArgumentException("keypacket tag is invalid: " +
240 "bit 7 is not set");
241 }
242 // make sure using new format
243 if ((tag & 64) != 64) {
244 throw new IllegalArgumentException("old keypacket tag format is " +
245 "unsupported");
246 }
247
248 // tag value must be 6, 14, 5 or 7
249 if (((tag & 6) != 6) && ((tag & 14) != 14) &&
250 ((tag & 5) != 5) && ((tag & 7) != 7)) {
251 throw new IllegalArgumentException("keypacket tag is invalid: " +
252 "must be 6, 14, 5, or 7");
253 }
254 }
255}