Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/util/DerInputStream.java
38830 views
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
23
* questions.
24
*/
25
26
package sun.security.util;
27
28
import java.io.InputStream;
29
import java.io.IOException;
30
import java.io.EOFException;
31
import java.util.Date;
32
import java.util.Vector;
33
import java.math.BigInteger;
34
import java.io.DataInputStream;
35
36
/**
37
* A DER input stream, used for parsing ASN.1 DER-encoded data such as
38
* that found in X.509 certificates. DER is a subset of BER/1, which has
39
* the advantage that it allows only a single encoding of primitive data.
40
* (High level data such as dates still support many encodings.) That is,
41
* it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER).
42
*
43
* <P>Note that, like BER/1, DER streams are streams of explicitly
44
* tagged data values. Accordingly, this programming interface does
45
* not expose any variant of the java.io.InputStream interface, since
46
* that kind of input stream holds untagged data values and using that
47
* I/O model could prevent correct parsing of the DER data.
48
*
49
* <P>At this time, this class supports only a subset of the types of DER
50
* data encodings which are defined. That subset is sufficient for parsing
51
* most X.509 certificates.
52
*
53
*
54
* @author David Brownell
55
* @author Amit Kapoor
56
* @author Hemma Prafullchandra
57
*/
58
59
public class DerInputStream {
60
61
/*
62
* This version only supports fully buffered DER. This is easy to
63
* work with, though if large objects are manipulated DER becomes
64
* awkward to deal with. That's where BER is useful, since BER
65
* handles streaming data relatively well.
66
*/
67
DerInputBuffer buffer;
68
69
/** The DER tag of the value; one of the tag_ constants. */
70
public byte tag;
71
72
/**
73
* Create a DER input stream from a data buffer. The buffer is not
74
* copied, it is shared. Accordingly, the buffer should be treated
75
* as read-only.
76
*
77
* @param data the buffer from which to create the string (CONSUMED)
78
*/
79
public DerInputStream(byte[] data) throws IOException {
80
init(data, 0, data.length, true);
81
}
82
83
/**
84
* Create a DER input stream from part of a data buffer with
85
* additional arg to control whether DER checks are enforced.
86
* The buffer is not copied, it is shared. Accordingly, the
87
* buffer should be treated as read-only.
88
*
89
* @param data the buffer from which to create the string (CONSUMED)
90
* @param offset the first index of <em>data</em> which will
91
* be read as DER input in the new stream
92
* @param len how long a chunk of the buffer to use,
93
* starting at "offset"
94
* @param allowBER whether to allow constructed indefinite-length
95
* encoding as well as tolerate leading 0s
96
*/
97
public DerInputStream(byte[] data, int offset, int len,
98
boolean allowBER) throws IOException {
99
init(data, offset, len, allowBER);
100
}
101
102
/**
103
* Create a DER input stream from part of a data buffer.
104
* The buffer is not copied, it is shared. Accordingly, the
105
* buffer should be treated as read-only.
106
*
107
* @param data the buffer from which to create the string (CONSUMED)
108
* @param offset the first index of <em>data</em> which will
109
* be read as DER input in the new stream
110
* @param len how long a chunk of the buffer to use,
111
* starting at "offset"
112
*/
113
public DerInputStream(byte[] data, int offset, int len) throws IOException {
114
init(data, offset, len, true);
115
}
116
117
/*
118
* private helper routine
119
*/
120
private void init(byte[] data, int offset, int len, boolean allowBER) throws IOException {
121
if ((offset+2 > data.length) || (offset+len > data.length)) {
122
throw new IOException("Encoding bytes too short");
123
}
124
// check for indefinite length encoding
125
if (DerIndefLenConverter.isIndefinite(data[offset+1])) {
126
if (!allowBER) {
127
throw new IOException("Indefinite length BER encoding found");
128
} else {
129
byte[] inData = new byte[len];
130
System.arraycopy(data, offset, inData, 0, len);
131
132
DerIndefLenConverter derIn = new DerIndefLenConverter();
133
buffer = new DerInputBuffer(derIn.convert(inData), allowBER);
134
}
135
} else {
136
buffer = new DerInputBuffer(data, offset, len, allowBER);
137
}
138
buffer.mark(Integer.MAX_VALUE);
139
}
140
141
DerInputStream(DerInputBuffer buf) {
142
buffer = buf;
143
buffer.mark(Integer.MAX_VALUE);
144
}
145
146
/**
147
* Creates a new DER input stream from part of this input stream.
148
*
149
* @param len how long a chunk of the current input stream to use,
150
* starting at the current position.
151
* @param do_skip true if the existing data in the input stream should
152
* be skipped. If this value is false, the next data read
153
* on this stream and the newly created stream will be the
154
* same.
155
*/
156
public DerInputStream subStream(int len, boolean do_skip)
157
throws IOException {
158
DerInputBuffer newbuf = buffer.dup();
159
160
newbuf.truncate(len);
161
if (do_skip) {
162
buffer.skip(len);
163
}
164
return new DerInputStream(newbuf);
165
}
166
167
/**
168
* Return what has been written to this DerInputStream
169
* as a byte array. Useful for debugging.
170
*/
171
public byte[] toByteArray() {
172
return buffer.toByteArray();
173
}
174
175
/*
176
* PRIMITIVES -- these are "universal" ASN.1 simple types.
177
*
178
* INTEGER, ENUMERATED, BIT STRING, OCTET STRING, NULL
179
* OBJECT IDENTIFIER, SEQUENCE (OF), SET (OF)
180
* UTF8String, PrintableString, T61String, IA5String, UTCTime,
181
* GeneralizedTime, BMPString.
182
* Note: UniversalString not supported till encoder is available.
183
*/
184
185
/**
186
* Get an integer from the input stream as an integer.
187
*
188
* @return the integer held in this DER input stream.
189
*/
190
public int getInteger() throws IOException {
191
if (buffer.read() != DerValue.tag_Integer) {
192
throw new IOException("DER input, Integer tag error");
193
}
194
return buffer.getInteger(getDefiniteLength(buffer));
195
}
196
197
/**
198
* Get a integer from the input stream as a BigInteger object.
199
*
200
* @return the integer held in this DER input stream.
201
*/
202
public BigInteger getBigInteger() throws IOException {
203
if (buffer.read() != DerValue.tag_Integer) {
204
throw new IOException("DER input, Integer tag error");
205
}
206
return buffer.getBigInteger(getDefiniteLength(buffer), false);
207
}
208
209
/**
210
* Returns an ASN.1 INTEGER value as a positive BigInteger.
211
* This is just to deal with implementations that incorrectly encode
212
* some values as negative.
213
*
214
* @return the integer held in this DER value as a BigInteger.
215
*/
216
public BigInteger getPositiveBigInteger() throws IOException {
217
if (buffer.read() != DerValue.tag_Integer) {
218
throw new IOException("DER input, Integer tag error");
219
}
220
return buffer.getBigInteger(getDefiniteLength(buffer), true);
221
}
222
223
/**
224
* Get an enumerated from the input stream.
225
*
226
* @return the integer held in this DER input stream.
227
*/
228
public int getEnumerated() throws IOException {
229
if (buffer.read() != DerValue.tag_Enumerated) {
230
throw new IOException("DER input, Enumerated tag error");
231
}
232
return buffer.getInteger(getDefiniteLength(buffer));
233
}
234
235
/**
236
* Get a bit string from the input stream. Padded bits (if any)
237
* will be stripped off before the bit string is returned.
238
*/
239
public byte[] getBitString() throws IOException {
240
if (buffer.read() != DerValue.tag_BitString)
241
throw new IOException("DER input not an bit string");
242
243
return buffer.getBitString(getDefiniteLength(buffer));
244
}
245
246
/**
247
* Get a bit string from the input stream. The bit string need
248
* not be byte-aligned.
249
*/
250
public BitArray getUnalignedBitString() throws IOException {
251
if (buffer.read() != DerValue.tag_BitString) {
252
throw new IOException("DER input not a bit string");
253
}
254
255
int length = getDefiniteLength(buffer);
256
257
if (length == 0) {
258
return new BitArray(0);
259
}
260
261
/*
262
* First byte = number of excess bits in the last octet of the
263
* representation.
264
*/
265
length--;
266
int excessBits = buffer.read();
267
if (excessBits < 0) {
268
throw new IOException("Unused bits of bit string invalid");
269
}
270
int validBits = length*8 - excessBits;
271
if (validBits < 0) {
272
throw new IOException("Valid bits of bit string invalid");
273
}
274
275
byte[] repn = new byte[length];
276
277
if ((length != 0) && (buffer.read(repn) != length)) {
278
throw new IOException("Short read of DER bit string");
279
}
280
281
return new BitArray(validBits, repn);
282
}
283
284
/**
285
* Returns an ASN.1 OCTET STRING from the input stream.
286
*/
287
public byte[] getOctetString() throws IOException {
288
if (buffer.read() != DerValue.tag_OctetString)
289
throw new IOException("DER input not an octet string");
290
291
int length = getDefiniteLength(buffer);
292
byte[] retval = new byte[length];
293
if ((length != 0) && (buffer.read(retval) != length))
294
throw new IOException("Short read of DER octet string");
295
296
return retval;
297
}
298
299
/**
300
* Returns the asked number of bytes from the input stream.
301
*/
302
public void getBytes(byte[] val) throws IOException {
303
if ((val.length != 0) && (buffer.read(val) != val.length)) {
304
throw new IOException("Short read of DER octet string");
305
}
306
}
307
308
/**
309
* Reads an encoded null value from the input stream.
310
*/
311
public void getNull() throws IOException {
312
if (buffer.read() != DerValue.tag_Null || buffer.read() != 0)
313
throw new IOException("getNull, bad data");
314
}
315
316
/**
317
* Reads an X.200 style Object Identifier from the stream.
318
*/
319
public ObjectIdentifier getOID() throws IOException {
320
return new ObjectIdentifier(this);
321
}
322
323
/**
324
* Return a sequence of encoded entities. ASN.1 sequences are
325
* ordered, and they are often used, like a "struct" in C or C++,
326
* to group data values. They may have optional or context
327
* specific values.
328
*
329
* @param startLen guess about how long the sequence will be
330
* (used to initialize an auto-growing data structure)
331
* @return array of the values in the sequence
332
*/
333
public DerValue[] getSequence(int startLen) throws IOException {
334
tag = (byte)buffer.read();
335
if (tag != DerValue.tag_Sequence)
336
throw new IOException("Sequence tag error");
337
return readVector(startLen);
338
}
339
340
/**
341
* Return a set of encoded entities. ASN.1 sets are unordered,
342
* though DER may specify an order for some kinds of sets (such
343
* as the attributes in an X.500 relative distinguished name)
344
* to facilitate binary comparisons of encoded values.
345
*
346
* @param startLen guess about how large the set will be
347
* (used to initialize an auto-growing data structure)
348
* @return array of the values in the sequence
349
*/
350
public DerValue[] getSet(int startLen) throws IOException {
351
tag = (byte)buffer.read();
352
if (tag != DerValue.tag_Set)
353
throw new IOException("Set tag error");
354
return readVector(startLen);
355
}
356
357
/**
358
* Return a set of encoded entities. ASN.1 sets are unordered,
359
* though DER may specify an order for some kinds of sets (such
360
* as the attributes in an X.500 relative distinguished name)
361
* to facilitate binary comparisons of encoded values.
362
*
363
* @param startLen guess about how large the set will be
364
* (used to initialize an auto-growing data structure)
365
* @param implicit if true tag is assumed implicit.
366
* @return array of the values in the sequence
367
*/
368
public DerValue[] getSet(int startLen, boolean implicit)
369
throws IOException {
370
tag = (byte)buffer.read();
371
if (!implicit) {
372
if (tag != DerValue.tag_Set) {
373
throw new IOException("Set tag error");
374
}
375
}
376
return (readVector(startLen));
377
}
378
379
/*
380
* Read a "vector" of values ... set or sequence have the
381
* same encoding, except for the initial tag, so both use
382
* this same helper routine.
383
*/
384
protected DerValue[] readVector(int startLen) throws IOException {
385
DerInputStream newstr;
386
387
byte lenByte = (byte)buffer.read();
388
int len = getLength(lenByte, buffer);
389
390
if (len == -1) {
391
// indefinite length encoding found
392
int readLen = buffer.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(buffer);
398
dis.readFully(indefData, offset, readLen);
399
dis.close();
400
DerIndefLenConverter derIn = new DerIndefLenConverter();
401
buffer = new DerInputBuffer(derIn.convert(indefData), buffer.allowBER);
402
403
if (tag != buffer.read())
404
throw new IOException("Indefinite length encoding" +
405
" not supported");
406
len = DerInputStream.getDefiniteLength(buffer);
407
}
408
409
if (len == 0)
410
// return empty array instead of null, which should be
411
// used only for missing optionals
412
return new DerValue[0];
413
414
/*
415
* Create a temporary stream from which to read the data,
416
* unless it's not really needed.
417
*/
418
if (buffer.available() == len)
419
newstr = this;
420
else
421
newstr = subStream(len, true);
422
423
/*
424
* Pull values out of the stream.
425
*/
426
Vector<DerValue> vec = new Vector<DerValue>(startLen);
427
DerValue value;
428
429
do {
430
value = new DerValue(newstr.buffer, buffer.allowBER);
431
vec.addElement(value);
432
} while (newstr.available() > 0);
433
434
if (newstr.available() != 0)
435
throw new IOException("Extra data at end of vector");
436
437
/*
438
* Now stick them into the array we're returning.
439
*/
440
int i, max = vec.size();
441
DerValue[] retval = new DerValue[max];
442
443
for (i = 0; i < max; i++)
444
retval[i] = vec.elementAt(i);
445
446
return retval;
447
}
448
449
/**
450
* Get a single DER-encoded value from the input stream.
451
* It can often be useful to pull a value from the stream
452
* and defer parsing it. For example, you can pull a nested
453
* sequence out with one call, and only examine its elements
454
* later when you really need to.
455
*/
456
public DerValue getDerValue() throws IOException {
457
return new DerValue(buffer);
458
}
459
460
/**
461
* Read a string that was encoded as a UTF8String DER value.
462
*/
463
public String getUTF8String() throws IOException {
464
return readString(DerValue.tag_UTF8String, "UTF-8", "UTF8");
465
}
466
467
/**
468
* Read a string that was encoded as a PrintableString DER value.
469
*/
470
public String getPrintableString() throws IOException {
471
return readString(DerValue.tag_PrintableString, "Printable",
472
"ASCII");
473
}
474
475
/**
476
* Read a string that was encoded as a T61String DER value.
477
*/
478
public String getT61String() throws IOException {
479
/*
480
* Works for common characters between T61 and ASCII.
481
*/
482
return readString(DerValue.tag_T61String, "T61", "ISO-8859-1");
483
}
484
485
/**
486
* Read a string that was encoded as a IA5tring DER value.
487
*/
488
public String getIA5String() throws IOException {
489
return readString(DerValue.tag_IA5String, "IA5", "ASCII");
490
}
491
492
/**
493
* Read a string that was encoded as a BMPString DER value.
494
*/
495
public String getBMPString() throws IOException {
496
return readString(DerValue.tag_BMPString, "BMP",
497
"UnicodeBigUnmarked");
498
}
499
500
/**
501
* Read a string that was encoded as a GeneralString DER value.
502
*/
503
public String getGeneralString() throws IOException {
504
return readString(DerValue.tag_GeneralString, "General",
505
"ASCII");
506
}
507
508
/**
509
* Private helper routine to read an encoded string from the input
510
* stream.
511
* @param stringTag the tag for the type of string to read
512
* @param stringName a name to display in error messages
513
* @param enc the encoder to use to interpret the data. Should
514
* correspond to the stringTag above.
515
*/
516
private String readString(byte stringTag, String stringName,
517
String enc) throws IOException {
518
519
if (buffer.read() != stringTag)
520
throw new IOException("DER input not a " +
521
stringName + " string");
522
523
int length = getDefiniteLength(buffer);
524
byte[] retval = new byte[length];
525
if ((length != 0) && (buffer.read(retval) != length))
526
throw new IOException("Short read of DER " +
527
stringName + " string");
528
529
return new String(retval, enc);
530
}
531
532
/**
533
* Get a UTC encoded time value from the input stream.
534
*/
535
public Date getUTCTime() throws IOException {
536
if (buffer.read() != DerValue.tag_UtcTime)
537
throw new IOException("DER input, UTCtime tag invalid ");
538
return buffer.getUTCTime(getDefiniteLength(buffer));
539
}
540
541
/**
542
* Get a Generalized encoded time value from the input stream.
543
*/
544
public Date getGeneralizedTime() throws IOException {
545
if (buffer.read() != DerValue.tag_GeneralizedTime)
546
throw new IOException("DER input, GeneralizedTime tag invalid ");
547
return buffer.getGeneralizedTime(getDefiniteLength(buffer));
548
}
549
550
/*
551
* Get a byte from the input stream.
552
*/
553
// package private
554
int getByte() throws IOException {
555
return (0x00ff & buffer.read());
556
}
557
558
public int peekByte() throws IOException {
559
return buffer.peek();
560
}
561
562
// package private
563
int getLength() throws IOException {
564
return getLength(buffer);
565
}
566
567
/*
568
* Get a length from the input stream, allowing for at most 32 bits of
569
* encoding to be used. (Not the same as getting a tagged integer!)
570
*
571
* @return the length or -1 if indefinite length found.
572
* @exception IOException on parsing error or unsupported lengths.
573
*/
574
static int getLength(InputStream in) throws IOException {
575
return getLength(in.read(), in);
576
}
577
578
/*
579
* Get a length from the input stream, allowing for at most 32 bits of
580
* encoding to be used. (Not the same as getting a tagged integer!)
581
*
582
* @return the length or -1 if indefinite length found.
583
* @exception IOException on parsing error or unsupported lengths.
584
*/
585
static int getLength(int lenByte, InputStream in) throws IOException {
586
int value, tmp;
587
if (lenByte == -1) {
588
throw new IOException("Short read of DER length");
589
}
590
591
String mdName = "DerInputStream.getLength(): ";
592
tmp = lenByte;
593
if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum
594
value = tmp;
595
} else { // long form or indefinite
596
tmp &= 0x07f;
597
598
/*
599
* NOTE: tmp == 0 indicates indefinite length encoded data.
600
* tmp > 4 indicates more than 4Gb of data.
601
*/
602
if (tmp == 0)
603
return -1;
604
if (tmp < 0 || tmp > 4)
605
throw new IOException(mdName + "lengthTag=" + tmp + ", "
606
+ ((tmp < 0) ? "incorrect DER encoding." : "too big."));
607
608
value = 0x0ff & in.read();
609
tmp--;
610
if (value == 0) {
611
// DER requires length value be encoded in minimum number of bytes
612
throw new IOException(mdName + "Redundant length bytes found");
613
}
614
while (tmp-- > 0) {
615
value <<= 8;
616
value += 0x0ff & in.read();
617
}
618
if (value < 0) {
619
throw new IOException(mdName + "Invalid length bytes");
620
} else if (value <= 127) {
621
throw new IOException(mdName + "Should use short form for length");
622
}
623
}
624
return value;
625
}
626
627
int getDefiniteLength() throws IOException {
628
return getDefiniteLength(buffer);
629
}
630
631
/*
632
* Get a length from the input stream.
633
*
634
* @return the length
635
* @exception IOException on parsing error or if indefinite length found.
636
*/
637
static int getDefiniteLength(InputStream in) throws IOException {
638
int len = getLength(in);
639
if (len < 0) {
640
throw new IOException("Indefinite length encoding not supported");
641
}
642
return len;
643
}
644
645
/**
646
* Mark the current position in the buffer, so that
647
* a later call to <code>reset</code> will return here.
648
*/
649
public void mark(int value) { buffer.mark(value); }
650
651
652
/**
653
* Return to the position of the last <code>mark</code>
654
* call. A mark is implicitly set at the beginning of
655
* the stream when it is created.
656
*/
657
public void reset() { buffer.reset(); }
658
659
660
/**
661
* Returns the number of bytes available for reading.
662
* This is most useful for testing whether the stream is
663
* empty.
664
*/
665
public int available() { return buffer.available(); }
666
}
667
668