Source code: gnu/javax/crypto/key/IncomingMessage.java
1 /* IncomingMessage.java --
2 Copyright (C) 2003, 2006 Free Software Foundation, Inc.
3
4 This file is a part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package gnu.javax.crypto.key;
40
41 import gnu.java.security.Registry;
42 import gnu.java.security.key.IKeyPairCodec;
43 import gnu.java.security.key.dss.DSSKeyPairPKCS8Codec;
44 import gnu.java.security.key.dss.DSSKeyPairRawCodec;
45 import gnu.java.security.key.dss.DSSKeyPairX509Codec;
46 import gnu.java.security.key.rsa.RSAKeyPairPKCS8Codec;
47 import gnu.java.security.key.rsa.RSAKeyPairRawCodec;
48 import gnu.java.security.key.rsa.RSAKeyPairX509Codec;
49 import gnu.javax.crypto.key.dh.DHKeyPairPKCS8Codec;
50 import gnu.javax.crypto.key.dh.DHKeyPairRawCodec;
51 import gnu.javax.crypto.key.dh.DHKeyPairX509Codec;
52 import gnu.javax.crypto.key.srp6.SRPKeyPairRawCodec;
53
54 import java.io.ByteArrayInputStream;
55 import java.io.UnsupportedEncodingException;
56 import java.math.BigInteger;
57 import java.security.PrivateKey;
58 import java.security.PublicKey;
59
60 /**
61 * <p>An implementation of an incoming message for use with key agreement
62 * protocols.</p>
63 */
64 public class IncomingMessage
65 {
66
67 // Constants and variables
68 // -------------------------------------------------------------------------
69
70 /** The internal buffer stream containing the message's contents. */
71 protected ByteArrayInputStream in;
72
73 /** The length of the message contents, according to its 4-byte header. */
74 protected int length;
75
76 // Constructor(s)
77 // -------------------------------------------------------------------------
78
79 /**
80 * <p>Constructs an incoming message given the message's encoded form,
81 * including its header bytes.</p>
82 *
83 * @param b the encoded form, including the header bytes, of an incoming
84 * message.
85 * @throws KeyAgreementException if the buffer is malformed.
86 */
87 public IncomingMessage(byte[] b) throws KeyAgreementException
88 {
89 this();
90
91 if (b.length < 4)
92 {
93 throw new KeyAgreementException("message header too short");
94 }
95 length = b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8
96 | (b[3] & 0xFF);
97 if (length > Registry.SASL_BUFFER_MAX_LIMIT || length < 0)
98 {
99 throw new KeyAgreementException("message size limit exceeded");
100 }
101 in = new ByteArrayInputStream(b, 4, length);
102 }
103
104 /** Trivial private constructor for use by the class method. */
105 private IncomingMessage()
106 {
107 super();
108 }
109
110 // Class methods
111 // -------------------------------------------------------------------------
112
113 /**
114 * <p>Returns an instance of a message given its encoded contents, excluding
115 * the message's header bytes.</p>
116 *
117 * <p>Calls the method with the same name and three arguments as:
118 * <code>getInstance(raw, 0, raw.length)</code>.
119 *
120 * @param raw the encoded form, excluding the header bytes.
121 * @return a new instance of <code>IncomingMessage</code>.
122 */
123 public static IncomingMessage getInstance(byte[] raw)
124 {
125 return getInstance(raw, 0, raw.length);
126 }
127
128 /**
129 * <p>Returns an instance of a message given its encoded contents, excluding
130 * the message's header bytes.</p>
131 *
132 * @param raw the encoded form, excluding the header bytes.
133 * @param offset offset where to start using raw bytes from.
134 * @param len number of bytes to use.
135 * @return a new instance of <code>IncomingMessage</code>.
136 */
137 public static IncomingMessage getInstance(byte[] raw, int offset, int len)
138 {
139 IncomingMessage result = new IncomingMessage();
140 result.in = new ByteArrayInputStream(raw, offset, len);
141 return result;
142 }
143
144 /**
145 * <p>Converts two octets into the number that they represent.</p>
146 *
147 * @param b the two octets.
148 * @return the length.
149 */
150 public static int twoBytesToLength(byte[] b) throws KeyAgreementException
151 {
152 int result = (b[0] & 0xFF) << 8 | (b[1] & 0xFF);
153 if (result > Registry.SASL_TWO_BYTE_MAX_LIMIT)
154 {
155 throw new KeyAgreementException("encoded MPI size limit exceeded");
156 }
157 return result;
158 }
159
160 /**
161 * <p>Converts four octets into the number that they represent.</p>
162 *
163 * @param b the four octets.
164 * @return the length.
165 */
166 public static int fourBytesToLength(byte[] b) throws KeyAgreementException
167 {
168 int result = b[0] << 24 | (b[1] & 0xFF) << 16 | (b[2] & 0xFF) << 8
169 | (b[3] & 0xFF);
170 if (result > Registry.SASL_FOUR_BYTE_MAX_LIMIT || result < 0)
171 {
172 throw new KeyAgreementException("encoded entity size limit exceeded");
173 }
174 return result;
175 }
176
177 // Instance methods
178 // -------------------------------------------------------------------------
179
180 public boolean hasMoreElements()
181 {
182 return (in.available() > 0);
183 }
184
185 /**
186 * Decodes a public key from the message.
187 * <p>
188 * See {@link OutgoingMessage#writePublicKey(java.security.PublicKey)} for
189 * more details on the internal format.
190 *
191 * @throws KeyAgreementException if an encoding size constraint is violated or
192 * a mismatch was detected in the encoding.
193 */
194 public PublicKey readPublicKey() throws KeyAgreementException
195 {
196 if (in.available() < 5)
197 throw new KeyAgreementException("not enough bytes for a public key in message");
198
199 byte[] elementLengthBytes = new byte[4];
200 in.read(elementLengthBytes, 0, 4);
201 int elementLength = fourBytesToLength(elementLengthBytes);
202 if (in.available() < elementLength)
203 throw new KeyAgreementException("illegal public key encoding");
204
205 int keyTypeAndFormatID = in.read() & 0xFF;
206 elementLength--;
207 byte[] kb = new byte[elementLength];
208 in.read(kb, 0, elementLength);
209
210 // instantiate the right codec and decode
211 IKeyPairCodec kpc = getKeyPairCodec(keyTypeAndFormatID);
212 return kpc.decodePublicKey(kb);
213 }
214
215 /**
216 * Decodes a private key from the message.
217 * <p>
218 * See {@link OutgoingMessage#writePrivateKey(java.security.PrivateKey)} for
219 * more details.
220 *
221 * @throws KeyAgreementException if an encoding size constraint is violated or
222 * a mismatch was detected in the encoding.
223 */
224 public PrivateKey readPrivateKey() throws KeyAgreementException
225 {
226 if (in.available() < 5)
227 throw new KeyAgreementException("not enough bytes for a private key in message");
228
229 byte[] elementLengthBytes = new byte[4];
230 in.read(elementLengthBytes, 0, 4);
231 int elementLength = fourBytesToLength(elementLengthBytes);
232 if (in.available() < elementLength)
233 throw new KeyAgreementException("illegal private key encoding");
234
235 int keyTypeAndFormatID = in.read() & 0xFF;
236 elementLength--;
237 byte[] kb = new byte[elementLength];
238 in.read(kb, 0, elementLength);
239
240 // instantiate the right codec and decode
241 IKeyPairCodec kpc = getKeyPairCodec(keyTypeAndFormatID);
242 return kpc.decodePrivateKey(kb);
243 }
244
245 /**
246 * <p>Decodes an MPI from the current message's contents.</p>
247 *
248 * @return a native representation of an MPI.
249 * @throws KeyAgreementException if an encoding exception occurs during the
250 * operation.
251 */
252 public BigInteger readMPI() throws KeyAgreementException
253 {
254 if (in.available() < 2)
255 {
256 throw new KeyAgreementException(
257 "not enough bytes for an MPI in message");
258 }
259 byte[] elementLengthBytes = new byte[2];
260 in.read(elementLengthBytes, 0, 2);
261 int elementLength = twoBytesToLength(elementLengthBytes);
262 if (in.available() < elementLength)
263 {
264 throw new KeyAgreementException("illegal MPI encoding");
265 }
266
267 byte[] element = new byte[elementLength];
268 in.read(element, 0, element.length);
269
270 return new BigInteger(1, element);
271 }
272
273 public String readString() throws KeyAgreementException
274 {
275 if (in.available() < 2)
276 {
277 throw new KeyAgreementException(
278 "not enough bytes for a text in message");
279 }
280 byte[] elementLengthBytes = new byte[2];
281 in.read(elementLengthBytes, 0, 2);
282 int elementLength = twoBytesToLength(elementLengthBytes);
283 if (in.available() < elementLength)
284 {
285 throw new KeyAgreementException("illegal text encoding");
286 }
287
288 byte[] element = new byte[elementLength];
289 in.read(element, 0, element.length);
290 String result = null;
291 try
292 {
293 result = new String(element, "UTF8");
294 }
295 catch (UnsupportedEncodingException x)
296 {
297 throw new KeyAgreementException("unxupported UTF8 encoding", x);
298 }
299
300 return result;
301 }
302
303 private IKeyPairCodec getKeyPairCodec(int keyTypeAndFormatID)
304 throws KeyAgreementException
305 {
306 int keyType = (keyTypeAndFormatID >>> 4) & 0x0F;
307 int formatID = keyTypeAndFormatID & 0x0F;
308 switch (formatID)
309 {
310 case Registry.RAW_ENCODING_ID:
311 switch (keyType)
312 {
313 case 0:
314 return new DSSKeyPairRawCodec();
315 case 1:
316 return new RSAKeyPairRawCodec();
317 case 2:
318 return new DHKeyPairRawCodec();
319 case 3:
320 return new SRPKeyPairRawCodec();
321 default:
322 throw new KeyAgreementException("Unknown key-type for Raw format: "
323 + keyType);
324 }
325 case Registry.X509_ENCODING_ID:
326 switch (keyType)
327 {
328 case 0:
329 return new DSSKeyPairX509Codec();
330 case 1:
331 return new RSAKeyPairX509Codec();
332 case 2:
333 return new DHKeyPairX509Codec();
334 default:
335 throw new KeyAgreementException("Unknown key-type for X.509 format: "
336 + keyType);
337 }
338 case Registry.PKCS8_ENCODING_ID:
339 switch (keyType)
340 {
341 case 0:
342 return new DSSKeyPairPKCS8Codec();
343 case 1:
344 return new RSAKeyPairPKCS8Codec();
345 case 2:
346 return new DHKeyPairPKCS8Codec();
347 default:
348 throw new KeyAgreementException("Unknown key-type for PKCS#8 format: "
349 + keyType);
350 }
351 default:
352 throw new KeyAgreementException("Unknown format identifier: "
353 + formatID);
354 }
355 }
356 }