001: /*
002: * Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025: /*
026: * $Id: DOMPGPData.java,v 1.18 2005/05/12 19:28:31 mullan Exp $
027: */
028: package org.jcp.xml.dsig.internal.dom;
029:
030: import java.util.*;
031: import javax.xml.crypto.*;
032: import javax.xml.crypto.dom.DOMCryptoContext;
033: import javax.xml.crypto.dsig.*;
034: import javax.xml.crypto.dsig.keyinfo.PGPData;
035: import org.w3c.dom.Document;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.Node;
038: import org.w3c.dom.NodeList;
039:
040: import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
041: import com.sun.org.apache.xml.internal.security.utils.Base64;
042:
043: /**
044: * DOM-based implementation of PGPData.
045: *
046: * @author Sean Mullan
047: */
048: public final class DOMPGPData extends DOMStructure implements PGPData {
049:
050: private final byte[] keyId;
051: private final byte[] keyPacket;
052: private final List externalElements;
053:
054: /**
055: * Creates a <code>DOMPGPData</code> containing the specified key packet.
056: * and optional list of external elements.
057: *
058: * @param keyPacket a PGP Key Material Packet as defined in section 5.5 of
059: * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The
060: * array is cloned to prevent subsequent modification.
061: * @param other a list of {@link XMLStructure}s representing elements from
062: * an external namespace. The list is defensively copied to prevent
063: * subsequent modification. May be <code>null</code> or empty.
064: * @throws NullPointerException if <code>keyPacket</code> is
065: * <code>null</code>
066: * @throws IllegalArgumentException if the key packet is not in the
067: * correct format
068: * @throws ClassCastException if <code>other</code> contains any
069: * entries that are not of type {@link XMLStructure}
070: */
071: public DOMPGPData(byte[] keyPacket, List other) {
072: if (keyPacket == null) {
073: throw new NullPointerException("keyPacket cannot be null");
074: }
075: if (other == null || other.isEmpty()) {
076: this .externalElements = Collections.EMPTY_LIST;
077: } else {
078: List otherCopy = new ArrayList(other);
079: for (int i = 0, size = otherCopy.size(); i < size; i++) {
080: if (!(otherCopy.get(i) instanceof XMLStructure)) {
081: throw new ClassCastException("other[" + i
082: + "] is not a valid PGPData type");
083: }
084: }
085: this .externalElements = Collections
086: .unmodifiableList(otherCopy);
087: }
088: this .keyPacket = (byte[]) keyPacket.clone();
089: checkKeyPacket(keyPacket);
090: this .keyId = null;
091: }
092:
093: /**
094: * Creates a <code>DOMPGPData</code> containing the specified key id and
095: * optional key packet and list of external elements.
096: *
097: * @param keyId a PGP public key id as defined in section 11.2 of
098: * <a href="http://www.ietf.org/rfc/rfc2440.txt"/>RFC 2440</a>. The
099: * 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, List other) {
114: if (keyId == null) {
115: throw new NullPointerException("keyId cannot be null");
116: }
117: // key ids must be 8 bytes
118: if (keyId.length != 8) {
119: throw new IllegalArgumentException(
120: "keyId must be 8 bytes long");
121: }
122: if (other == null || other.isEmpty()) {
123: this .externalElements = Collections.EMPTY_LIST;
124: } else {
125: List otherCopy = new ArrayList(other);
126: for (int i = 0, size = otherCopy.size(); i < size; i++) {
127: if (!(otherCopy.get(i) instanceof XMLStructure)) {
128: throw new ClassCastException("other[" + i
129: + "] is not a valid PGPData type");
130: }
131: }
132: this .externalElements = Collections
133: .unmodifiableList(otherCopy);
134: }
135: this .keyId = (byte[]) keyId.clone();
136: this .keyPacket = keyPacket == null ? null : (byte[]) keyPacket
137: .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 other = new ArrayList(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
167: .add(new javax.xml.crypto.dom.DOMStructure(
168: childElem));
169: }
170: } catch (Base64DecodingException bde) {
171: throw new MarshalException(bde);
172: }
173: }
174: }
175: this .keyId = keyId;
176: this .keyPacket = keyPacket;
177: this .externalElements = Collections.unmodifiableList(other);
178: }
179:
180: public byte[] getKeyId() {
181: return (keyId == null ? null : (byte[]) keyId.clone());
182: }
183:
184: public byte[] getKeyPacket() {
185: return (keyPacket == null ? null : (byte[]) keyPacket.clone());
186: }
187:
188: public List getExternalElements() {
189: return externalElements;
190: }
191:
192: public void marshal(Node parent, String dsPrefix,
193: DOMCryptoContext context) throws MarshalException {
194: Document ownerDoc = DOMUtils.getOwnerDocument(parent);
195:
196: Element pdElem = DOMUtils.createElement(ownerDoc, "PGPData",
197: XMLSignature.XMLNS, dsPrefix);
198:
199: // create and append PGPKeyID element
200: if (keyId != null) {
201: Element keyIdElem = DOMUtils.createElement(ownerDoc,
202: "PGPKeyID", XMLSignature.XMLNS, dsPrefix);
203: keyIdElem.appendChild(ownerDoc.createTextNode(Base64
204: .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", XMLSignature.XMLNS, dsPrefix);
212: keyPktElem.appendChild(ownerDoc.createTextNode(Base64
213: .encode(keyPacket)));
214: pdElem.appendChild(keyPktElem);
215: }
216:
217: // create and append any elements
218: for (int i = 0, size = externalElements.size(); i < size; i++) {
219: DOMUtils
220: .appendChild(
221: pdElem,
222: ((javax.xml.crypto.dom.DOMStructure) externalElements
223: .get(i)).getNode());
224: }
225:
226: parent.appendChild(pdElem);
227: }
228:
229: /**
230: * We assume packets use the new format packet syntax, as specified in
231: * section 4 of RFC 2440.
232: *
233: * This method only checks if the packet contains a valid tag. The
234: * contents of the packet should be checked by the application.
235: */
236: private void checkKeyPacket(byte[] keyPacket) {
237: // length must be at least 3 (one byte for tag, one byte for length,
238: // and minimally one byte of content
239: if (keyPacket.length < 3) {
240: throw new IllegalArgumentException(
241: "keypacket must be at least " + "3 bytes long");
242: }
243:
244: int tag = keyPacket[0];
245: // first bit must be set
246: if ((tag & 128) != 128) {
247: throw new IllegalArgumentException(
248: "keypacket tag is invalid: " + "bit 7 is not set");
249: }
250: // make sure using new format
251: if ((tag & 64) != 64) {
252: throw new IllegalArgumentException(
253: "old keypacket tag format is " + "unsupported");
254: }
255:
256: // tag value must be 6, 14, 5 or 7
257: if (((tag & 6) != 6) && ((tag & 14) != 14) && ((tag & 5) != 5)
258: && ((tag & 7) != 7)) {
259: throw new IllegalArgumentException(
260: "keypacket tag is invalid: "
261: + "must be 6, 14, 5, or 7");
262: }
263: }
264: }
|