1 /*
2 * Copyright (c) 1996, 2016, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
208 boolean isPrintableString = true;
209 for (int i = 0; i < value.length(); i++) {
210 if (!isPrintableStringChar(value.charAt(i))) {
211 isPrintableString = false;
212 break;
213 }
214 }
215
216 data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value);
217 }
218
219 /**
220 * Creates a string type DER value from a String object
221 * @param stringTag the tag for the DER value to create
222 * @param value the String object to use for the DER value
223 */
224 public DerValue(byte stringTag, String value) throws IOException {
225 data = init(stringTag, value);
226 }
227
228 /**
229 * Creates a DerValue from a tag and some DER-encoded data.
230 *
231 * @param tag the DER type tag
232 * @param data the DER-encoded data
233 */
234 public DerValue(byte tag, byte[] data) {
235 this.tag = tag;
236 buffer = new DerInputBuffer(data.clone());
237 length = data.length;
238 this.data = new DerInputStream(buffer);
239 this.data.mark(Integer.MAX_VALUE);
240 }
241
242 /*
243 * package private
244 */
245 DerValue(DerInputBuffer in) throws IOException {
246 // XXX must also parse BER-encoded constructed
247 // values such as sequences, sets...
248
249 tag = (byte)in.read();
250 byte lenByte = (byte)in.read();
251 length = DerInputStream.getLength(lenByte, in);
252 if (length == -1) { // indefinite length encoding found
253 DerInputBuffer inbuf = in.dup();
254 int readLen = inbuf.available();
255 int offset = 2; // for tag and length bytes
256 byte[] indefData = new byte[readLen + offset];
257 indefData[0] = tag;
258 indefData[1] = lenByte;
259 DataInputStream dis = new DataInputStream(inbuf);
260 dis.readFully(indefData, offset, readLen);
261 dis.close();
262 DerIndefLenConverter derIn = new DerIndefLenConverter();
263 inbuf = new DerInputBuffer(derIn.convert(indefData));
264 if (tag != inbuf.read())
265 throw new IOException
266 ("Indefinite length encoding not supported");
267 length = DerInputStream.getDefiniteLength(inbuf);
268 buffer = inbuf.dup();
269 buffer.truncate(length);
270 data = new DerInputStream(buffer);
271 // indefinite form is encoded by sending a length field with a
272 // length of 0. - i.e. [1000|0000].
273 // the object is ended by sending two zero bytes.
274 in.skip(length + offset);
275 } else {
276
277 buffer = in.dup();
278 buffer.truncate(length);
279 data = new DerInputStream(buffer);
280
281 in.skip(length);
282 }
283 }
284
285 /**
286 * Get an ASN.1/DER encoded datum from a buffer. The
287 * entire buffer must hold exactly one datum, including
288 * its tag and length.
289 *
290 * @param buf buffer holding a single DER-encoded datum.
291 */
292 public DerValue(byte[] buf) throws IOException {
293 data = init(true, new ByteArrayInputStream(buf));
294 }
295
296 /**
297 * Get an ASN.1/DER encoded datum from part of a buffer.
298 * That part of the buffer must hold exactly one datum, including
299 * its tag and length.
300 *
301 * @param buf the buffer
302 * @param offset start point of the single DER-encoded dataum
303 * @param len how many bytes are in the encoded datum
304 */
305 public DerValue(byte[] buf, int offset, int len) throws IOException {
306 data = init(true, new ByteArrayInputStream(buf, offset, len));
307 }
308
309 /**
310 * Get an ASN1/DER encoded datum from an input stream. The
311 * stream may have additional data following the encoded datum.
312 * In case of indefinite length encoded datum, the input stream
313 * must hold only one datum.
314 *
315 * @param in the input stream holding a single DER datum,
316 * which may be followed by additional data
317 */
318 public DerValue(InputStream in) throws IOException {
319 data = init(false, in);
320 }
321
322 private DerInputStream init(byte stringTag, String value) throws IOException {
323 String enc = null;
324
325 tag = stringTag;
326
327 switch (stringTag) {
328 case tag_PrintableString:
329 case tag_IA5String:
330 case tag_GeneralString:
331 enc = "ASCII";
332 break;
333 case tag_T61String:
334 enc = "ISO-8859-1";
335 break;
336 case tag_BMPString:
337 enc = "UnicodeBigUnmarked";
338 break;
339 case tag_UTF8String:
340 enc = "UTF8";
341 break;
342 // TBD: Need encoder for UniversalString before it can
343 // be handled.
344 default:
345 throw new IllegalArgumentException("Unsupported DER string type");
346 }
347
348 byte[] buf = value.getBytes(enc);
349 length = buf.length;
350 buffer = new DerInputBuffer(buf);
351 DerInputStream result = new DerInputStream(buffer);
352 result.mark(Integer.MAX_VALUE);
353 return result;
354 }
355
356 /*
357 * helper routine
358 */
359 private DerInputStream init(boolean fullyBuffered, InputStream in)
360 throws IOException {
361
362 tag = (byte)in.read();
363 byte lenByte = (byte)in.read();
364 length = DerInputStream.getLength(lenByte, in);
365 if (length == -1) { // indefinite length encoding found
366 int readLen = in.available();
367 int offset = 2; // for tag and length bytes
368 byte[] indefData = new byte[readLen + offset];
369 indefData[0] = tag;
370 indefData[1] = lenByte;
371 DataInputStream dis = new DataInputStream(in);
372 dis.readFully(indefData, offset, readLen);
373 dis.close();
374 DerIndefLenConverter derIn = new DerIndefLenConverter();
375 in = new ByteArrayInputStream(derIn.convert(indefData));
376 if (tag != in.read())
377 throw new IOException
378 ("Indefinite length encoding not supported");
379 length = DerInputStream.getDefiniteLength(in);
380 }
381
382 if (fullyBuffered && in.available() != length)
383 throw new IOException("extra data given to DerValue constructor");
384
385 byte[] bytes = IOUtils.readFully(in, length, true);
386
387 buffer = new DerInputBuffer(bytes);
388 return new DerInputStream(buffer);
389 }
390
391 /**
392 * Encode an ASN1/DER encoded datum onto a DER output stream.
393 */
394 public void encode(DerOutputStream out)
395 throws IOException {
396 out.write(tag);
397 out.putLength(length);
398 // XXX yeech, excess copies ... DerInputBuffer.write(OutStream)
399 if (length > 0) {
400 byte[] value = new byte[length];
401 // always synchronized on data
402 synchronized (data) {
403 buffer.reset();
404 if (buffer.read(value) != length) {
405 throw new IOException("short DER value read (encode)");
406 }
407 out.write(value);
462 * Returns an ASN.1 OCTET STRING
463 *
464 * @return the octet string held in this DER value
465 */
466 public byte[] getOctetString() throws IOException {
467 byte[] bytes;
468
469 if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
470 throw new IOException(
471 "DerValue.getOctetString, not an Octet String: " + tag);
472 }
473 bytes = new byte[length];
474 // Note: do not tempt to call buffer.read(bytes) at all. There's a
475 // known bug that it returns -1 instead of 0.
476 if (length == 0) {
477 return bytes;
478 }
479 if (buffer.read(bytes) != length)
480 throw new IOException("short read on DerValue buffer");
481 if (isConstructed()) {
482 DerInputStream in = new DerInputStream(bytes);
483 bytes = null;
484 while (in.available() != 0) {
485 bytes = append(bytes, in.getOctetString());
486 }
487 }
488 return bytes;
489 }
490
491 /**
492 * Returns an ASN.1 INTEGER value as an integer.
493 *
494 * @return the integer held in this DER value.
495 */
496 public int getInteger() throws IOException {
497 if (tag != tag_Integer) {
498 throw new IOException("DerValue.getInteger, not an int " + tag);
499 }
500 return buffer.getInteger(data.available());
501 }
502
|
1 /**
2 * Copyright (c) 1996, 2017, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
208 boolean isPrintableString = true;
209 for (int i = 0; i < value.length(); i++) {
210 if (!isPrintableStringChar(value.charAt(i))) {
211 isPrintableString = false;
212 break;
213 }
214 }
215
216 data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value);
217 }
218
219 /**
220 * Creates a string type DER value from a String object
221 * @param stringTag the tag for the DER value to create
222 * @param value the String object to use for the DER value
223 */
224 public DerValue(byte stringTag, String value) throws IOException {
225 data = init(stringTag, value);
226 }
227
228 // Creates a DerValue from a tag and some DER-encoded data w/ additional
229 // arg to control whether DER checks are enforced.
230 DerValue(byte tag, byte[] data, boolean allowBER) {
231 this.tag = tag;
232 buffer = new DerInputBuffer(data.clone(), allowBER);
233 length = data.length;
234 this.data = new DerInputStream(buffer);
235 this.data.mark(Integer.MAX_VALUE);
236 }
237
238 /**
239 * Creates a DerValue from a tag and some DER-encoded data.
240 *
241 * @param tag the DER type tag
242 * @param data the DER-encoded data
243 */
244 public DerValue(byte tag, byte[] data) {
245 this(tag, data, true);
246 }
247
248 /*
249 * package private
250 */
251 DerValue(DerInputBuffer in) throws IOException {
252
253 // XXX must also parse BER-encoded constructed
254 // values such as sequences, sets...
255 tag = (byte)in.read();
256 byte lenByte = (byte)in.read();
257 length = DerInputStream.getLength(lenByte, in);
258 if (length == -1) { // indefinite length encoding found
259 DerInputBuffer inbuf = in.dup();
260 int readLen = inbuf.available();
261 int offset = 2; // for tag and length bytes
262 byte[] indefData = new byte[readLen + offset];
263 indefData[0] = tag;
264 indefData[1] = lenByte;
265 DataInputStream dis = new DataInputStream(inbuf);
266 dis.readFully(indefData, offset, readLen);
267 dis.close();
268 DerIndefLenConverter derIn = new DerIndefLenConverter();
269 inbuf = new DerInputBuffer(derIn.convert(indefData), in.allowBER);
270 if (tag != inbuf.read())
271 throw new IOException
272 ("Indefinite length encoding not supported");
273 length = DerInputStream.getDefiniteLength(inbuf);
274 buffer = inbuf.dup();
275 buffer.truncate(length);
276 data = new DerInputStream(buffer);
277 // indefinite form is encoded by sending a length field with a
278 // length of 0. - i.e. [1000|0000].
279 // the object is ended by sending two zero bytes.
280 in.skip(length + offset);
281 } else {
282
283 buffer = in.dup();
284 buffer.truncate(length);
285 data = new DerInputStream(buffer);
286
287 in.skip(length);
288 }
289 }
290
291 // Get an ASN.1/DER encoded datum from a buffer w/ additional
292 // arg to control whether DER checks are enforced.
293 DerValue(byte[] buf, boolean allowBER) throws IOException {
294 data = init(true, new ByteArrayInputStream(buf), allowBER);
295 }
296
297 /**
298 * Get an ASN.1/DER encoded datum from a buffer. The
299 * entire buffer must hold exactly one datum, including
300 * its tag and length.
301 *
302 * @param buf buffer holding a single DER-encoded datum.
303 */
304 public DerValue(byte[] buf) throws IOException {
305 this(buf, true);
306 }
307
308 // Get an ASN.1/DER encoded datum from part of a buffer w/ additional
309 // arg to control whether DER checks are enforced.
310 DerValue(byte[] buf, int offset, int len, boolean allowBER)
311 throws IOException {
312 data = init(true, new ByteArrayInputStream(buf, offset, len), allowBER);
313 }
314
315 /**
316 * Get an ASN.1/DER encoded datum from part of a buffer.
317 * That part of the buffer must hold exactly one datum, including
318 * its tag and length.
319 *
320 * @param buf the buffer
321 * @param offset start point of the single DER-encoded dataum
322 * @param len how many bytes are in the encoded datum
323 */
324 public DerValue(byte[] buf, int offset, int len) throws IOException {
325 this(buf, offset, len, true);
326 }
327
328 // Get an ASN1/DER encoded datum from an input stream w/ additional
329 // arg to control whether DER checks are enforced.
330 DerValue(InputStream in, boolean allowBER) throws IOException {
331 data = init(false, in, allowBER);
332 }
333
334 /**
335 * Get an ASN1/DER encoded datum from an input stream. The
336 * stream may have additional data following the encoded datum.
337 * In case of indefinite length encoded datum, the input stream
338 * must hold only one datum.
339 *
340 * @param in the input stream holding a single DER datum,
341 * which may be followed by additional data
342 */
343 public DerValue(InputStream in) throws IOException {
344 this(in, true);
345 }
346
347 private DerInputStream init(byte stringTag, String value)
348 throws IOException {
349 String enc = null;
350
351 tag = stringTag;
352
353 switch (stringTag) {
354 case tag_PrintableString:
355 case tag_IA5String:
356 case tag_GeneralString:
357 enc = "ASCII";
358 break;
359 case tag_T61String:
360 enc = "ISO-8859-1";
361 break;
362 case tag_BMPString:
363 enc = "UnicodeBigUnmarked";
364 break;
365 case tag_UTF8String:
366 enc = "UTF8";
367 break;
368 // TBD: Need encoder for UniversalString before it can
369 // be handled.
370 default:
371 throw new IllegalArgumentException("Unsupported DER string type");
372 }
373
374 byte[] buf = value.getBytes(enc);
375 length = buf.length;
376 buffer = new DerInputBuffer(buf, true);
377 DerInputStream result = new DerInputStream(buffer);
378 result.mark(Integer.MAX_VALUE);
379 return result;
380 }
381
382 /*
383 * helper routine
384 */
385 private DerInputStream init(boolean fullyBuffered, InputStream in,
386 boolean allowBER) throws IOException {
387
388 tag = (byte)in.read();
389 byte lenByte = (byte)in.read();
390 length = DerInputStream.getLength(lenByte, in);
391 if (length == -1) { // indefinite length encoding found
392 int readLen = in.available();
393 int offset = 2; // for tag and length bytes
394 byte[] indefData = new byte[readLen + offset];
395 indefData[0] = tag;
396 indefData[1] = lenByte;
397 DataInputStream dis = new DataInputStream(in);
398 dis.readFully(indefData, offset, readLen);
399 dis.close();
400 DerIndefLenConverter derIn = new DerIndefLenConverter();
401 in = new ByteArrayInputStream(derIn.convert(indefData));
402 if (tag != in.read())
403 throw new IOException
404 ("Indefinite length encoding not supported");
405 length = DerInputStream.getDefiniteLength(in);
406 }
407
408 if (fullyBuffered && in.available() != length)
409 throw new IOException("extra data given to DerValue constructor");
410
411 byte[] bytes = IOUtils.readFully(in, length, true);
412
413 buffer = new DerInputBuffer(bytes, allowBER);
414 return new DerInputStream(buffer);
415 }
416
417 /**
418 * Encode an ASN1/DER encoded datum onto a DER output stream.
419 */
420 public void encode(DerOutputStream out)
421 throws IOException {
422 out.write(tag);
423 out.putLength(length);
424 // XXX yeech, excess copies ... DerInputBuffer.write(OutStream)
425 if (length > 0) {
426 byte[] value = new byte[length];
427 // always synchronized on data
428 synchronized (data) {
429 buffer.reset();
430 if (buffer.read(value) != length) {
431 throw new IOException("short DER value read (encode)");
432 }
433 out.write(value);
488 * Returns an ASN.1 OCTET STRING
489 *
490 * @return the octet string held in this DER value
491 */
492 public byte[] getOctetString() throws IOException {
493 byte[] bytes;
494
495 if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
496 throw new IOException(
497 "DerValue.getOctetString, not an Octet String: " + tag);
498 }
499 bytes = new byte[length];
500 // Note: do not tempt to call buffer.read(bytes) at all. There's a
501 // known bug that it returns -1 instead of 0.
502 if (length == 0) {
503 return bytes;
504 }
505 if (buffer.read(bytes) != length)
506 throw new IOException("short read on DerValue buffer");
507 if (isConstructed()) {
508 DerInputStream in = new DerInputStream(bytes, 0, bytes.length,
509 buffer.allowBER);
510 bytes = null;
511 while (in.available() != 0) {
512 bytes = append(bytes, in.getOctetString());
513 }
514 }
515 return bytes;
516 }
517
518 /**
519 * Returns an ASN.1 INTEGER value as an integer.
520 *
521 * @return the integer held in this DER value.
522 */
523 public int getInteger() throws IOException {
524 if (tag != tag_Integer) {
525 throw new IOException("DerValue.getInteger, not an int " + tag);
526 }
527 return buffer.getInteger(data.available());
528 }
529
|