Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/util/DerValue.java
41159 views
1
/*
2
* Copyright (c) 1996, 2021, 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 sun.nio.cs.UTF_32BE;
29
import sun.util.calendar.CalendarDate;
30
import sun.util.calendar.CalendarSystem;
31
32
import java.io.*;
33
import java.math.BigInteger;
34
import java.nio.charset.Charset;
35
import java.util.*;
36
37
import static java.nio.charset.StandardCharsets.*;
38
39
/**
40
* Represents a single DER-encoded value. DER encoding rules are a subset
41
* of the "Basic" Encoding Rules (BER), but they only support a single way
42
* ("Definite" encoding) to encode any given value.
43
*
44
* <P>All DER-encoded data are triples <em>{type, length, data}</em>. This
45
* class represents such tagged values as they have been read (or constructed),
46
* and provides structured access to the encoded data.
47
*
48
* <P>At this time, this class supports only a subset of the types of DER
49
* data encodings which are defined. That subset is sufficient for parsing
50
* most X.509 certificates, and working with selected additional formats
51
* (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data).
52
*
53
* A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3
54
* and RFC 5280, section 8, we assume that this kind of string will contain
55
* ISO-8859-1 characters only.
56
*
57
*
58
* @author David Brownell
59
* @author Amit Kapoor
60
* @author Hemma Prafullchandra
61
*/
62
public class DerValue {
63
64
/** The tag class types */
65
public static final byte TAG_UNIVERSAL = (byte)0x000;
66
public static final byte TAG_APPLICATION = (byte)0x040;
67
public static final byte TAG_CONTEXT = (byte)0x080;
68
public static final byte TAG_PRIVATE = (byte)0x0c0;
69
70
/*
71
* The type starts at the first byte of the encoding, and
72
* is one of these tag_* values. That may be all the type
73
* data that is needed.
74
*/
75
76
/*
77
* These tags are the "universal" tags ... they mean the same
78
* in all contexts. (Mask with 0x1f -- five bits.)
79
*/
80
81
/** Tag value indicating an ASN.1 "BOOLEAN" value. */
82
public static final byte tag_Boolean = 0x01;
83
84
/** Tag value indicating an ASN.1 "INTEGER" value. */
85
public static final byte tag_Integer = 0x02;
86
87
/** Tag value indicating an ASN.1 "BIT STRING" value. */
88
public static final byte tag_BitString = 0x03;
89
90
/** Tag value indicating an ASN.1 "OCTET STRING" value. */
91
public static final byte tag_OctetString = 0x04;
92
93
/** Tag value indicating an ASN.1 "NULL" value. */
94
public static final byte tag_Null = 0x05;
95
96
/** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */
97
public static final byte tag_ObjectId = 0x06;
98
99
/** Tag value including an ASN.1 "ENUMERATED" value */
100
public static final byte tag_Enumerated = 0x0A;
101
102
/** Tag value indicating an ASN.1 "UTF8String" value. */
103
public static final byte tag_UTF8String = 0x0C;
104
105
/** Tag value including a "printable" string */
106
public static final byte tag_PrintableString = 0x13;
107
108
/** Tag value including a "teletype" string */
109
public static final byte tag_T61String = 0x14;
110
111
/** Tag value including an ASCII string */
112
public static final byte tag_IA5String = 0x16;
113
114
/** Tag value indicating an ASN.1 "UTCTime" value. */
115
public static final byte tag_UtcTime = 0x17;
116
117
/** Tag value indicating an ASN.1 "GeneralizedTime" value. */
118
public static final byte tag_GeneralizedTime = 0x18;
119
120
/** Tag value indicating an ASN.1 "GenerallString" value. */
121
public static final byte tag_GeneralString = 0x1B;
122
123
/** Tag value indicating an ASN.1 "UniversalString" value. */
124
public static final byte tag_UniversalString = 0x1C;
125
126
/** Tag value indicating an ASN.1 "BMPString" value. */
127
public static final byte tag_BMPString = 0x1E;
128
129
// CONSTRUCTED seq/set
130
131
/**
132
* Tag value indicating an ASN.1
133
* "SEQUENCE" (zero to N elements, order is significant).
134
*/
135
public static final byte tag_Sequence = 0x30;
136
137
/**
138
* Tag value indicating an ASN.1
139
* "SEQUENCE OF" (one to N elements, order is significant).
140
*/
141
public static final byte tag_SequenceOf = 0x30;
142
143
/**
144
* Tag value indicating an ASN.1
145
* "SET" (zero to N members, order does not matter).
146
*/
147
public static final byte tag_Set = 0x31;
148
149
/**
150
* Tag value indicating an ASN.1
151
* "SET OF" (one to N members, order does not matter).
152
*/
153
public static final byte tag_SetOf = 0x31;
154
155
// This class is mostly immutable except that:
156
//
157
// 1. resetTag() modifies the tag
158
// 2. the data field is mutable
159
//
160
// For compatibility, data, getData() and resetTag() are preserved.
161
// A modern caller should call withTag() or data() instead.
162
//
163
// Also, some constructors have not cloned buffer, so the data could
164
// be modified externally.
165
166
public /*final*/ byte tag;
167
final byte[] buffer;
168
private final int start;
169
final int end;
170
private final boolean allowBER;
171
172
// Unsafe. Legacy. Never null.
173
public final DerInputStream data;
174
175
/*
176
* These values are the high order bits for the other kinds of tags.
177
*/
178
179
/**
180
* Returns true if the tag class is UNIVERSAL.
181
*/
182
public boolean isUniversal() { return ((tag & 0x0c0) == 0x000); }
183
184
/**
185
* Returns true if the tag class is APPLICATION.
186
*/
187
public boolean isApplication() { return ((tag & 0x0c0) == 0x040); }
188
189
/**
190
* Returns true iff the CONTEXT SPECIFIC bit is set in the type tag.
191
* This is associated with the ASN.1 "DEFINED BY" syntax.
192
*/
193
public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); }
194
195
/**
196
* Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag.
197
*/
198
public boolean isContextSpecific(byte cntxtTag) {
199
if (!isContextSpecific()) {
200
return false;
201
}
202
return ((tag & 0x01f) == cntxtTag);
203
}
204
205
boolean isPrivate() { return ((tag & 0x0c0) == 0x0c0); }
206
207
/** Returns true iff the CONSTRUCTED bit is set in the type tag. */
208
public boolean isConstructed() { return ((tag & 0x020) == 0x020); }
209
210
/**
211
* Returns true iff the CONSTRUCTED TAG matches the passed tag.
212
*/
213
public boolean isConstructed(byte constructedTag) {
214
if (!isConstructed()) {
215
return false;
216
}
217
return ((tag & 0x01f) == constructedTag);
218
}
219
220
/**
221
* Creates a new DerValue by specifying all its fields.
222
*/
223
DerValue(byte tag, byte[] buffer, int start, int end, boolean allowBER) {
224
if ((tag & 0x1f) == 0x1f) {
225
throw new IllegalArgumentException("Tag number over 30 is not supported");
226
}
227
this.tag = tag;
228
this.buffer = buffer;
229
this.start = start;
230
this.end = end;
231
this.allowBER = allowBER;
232
this.data = data();
233
}
234
235
/**
236
* Creates a PrintableString or UTF8string DER value from a string.
237
*/
238
public DerValue(String value) {
239
this(isPrintableString(value) ? tag_PrintableString : tag_UTF8String,
240
value);
241
}
242
243
private static boolean isPrintableString(String value) {
244
for (int i = 0; i < value.length(); i++) {
245
if (!isPrintableStringChar(value.charAt(i))) {
246
return false;
247
}
248
}
249
return true;
250
}
251
252
/**
253
* Creates a string type DER value from a String object
254
* @param stringTag the tag for the DER value to create
255
* @param value the String object to use for the DER value
256
*/
257
public DerValue(byte stringTag, String value) {
258
this(stringTag, string2bytes(stringTag, value), false);
259
}
260
261
private static byte[] string2bytes(byte stringTag, String value) {
262
Charset charset = switch (stringTag) {
263
case tag_PrintableString, tag_IA5String, tag_GeneralString -> US_ASCII;
264
case tag_T61String -> ISO_8859_1;
265
case tag_BMPString -> UTF_16BE;
266
case tag_UTF8String -> UTF_8;
267
case tag_UniversalString -> Charset.forName("UTF_32BE");
268
default -> throw new IllegalArgumentException("Unsupported DER string type");
269
};
270
return value.getBytes(charset);
271
}
272
273
DerValue(byte tag, byte[] buffer, boolean allowBER) {
274
this(tag, buffer, 0, buffer.length, allowBER);
275
}
276
277
/**
278
* Creates a DerValue from a tag and some DER-encoded data.
279
*
280
* This is a public constructor.
281
*
282
* @param tag the DER type tag
283
* @param buffer the DER-encoded data
284
*/
285
public DerValue(byte tag, byte[] buffer) {
286
this(tag, buffer.clone(), true);
287
}
288
289
/**
290
* Wraps an DerOutputStream. All bytes currently written
291
* into the stream will become the content of the newly
292
* created DerValue.
293
*
294
* Attention: do not reset the DerOutputStream after this call.
295
* No array copying is made.
296
*
297
* @param tag the tag
298
* @param out the DerOutputStream
299
* @returns a new DerValue using out as its content
300
*/
301
public static DerValue wrap(byte tag, DerOutputStream out) {
302
return new DerValue(tag, out.buf(), 0, out.size(), false);
303
}
304
305
/**
306
* Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly
307
* one datum, including its tag and length.
308
*
309
* This is a public constructor.
310
*/
311
public DerValue(byte[] encoding) throws IOException {
312
this(encoding.clone(), 0, encoding.length, true, false);
313
}
314
315
/**
316
* Parse an ASN.1 encoded datum from a byte array.
317
*
318
* @param buf the byte array containing the DER-encoded datum
319
* @param offset where the encoded datum starts inside {@code buf}
320
* @param len length of bytes to parse inside {@code buf}
321
* @param allowBER whether BER is allowed
322
* @param allowMore whether extra bytes are allowed after the encoded datum.
323
* If true, {@code len} can be bigger than the length of
324
* the encoded datum.
325
*
326
* @throws IOException if it's an invalid encoding or there are extra bytes
327
* after the encoded datum and {@code allowMore} is false.
328
*/
329
DerValue(byte[] buf, int offset, int len, boolean allowBER, boolean allowMore)
330
throws IOException {
331
332
if (len < 2) {
333
throw new IOException("Too short");
334
}
335
int pos = offset;
336
tag = buf[pos++];
337
if ((tag & 0x1f) == 0x1f) {
338
throw new IOException("Tag number over 30 at " + offset + " is not supported");
339
}
340
int lenByte = buf[pos++];
341
342
int length;
343
if (lenByte == (byte) 0x80) { // indefinite length
344
if (!allowBER) {
345
throw new IOException("Indefinite length encoding " +
346
"not supported with DER");
347
}
348
if (!isConstructed()) {
349
throw new IOException("Indefinite length encoding " +
350
"not supported with non-constructed data");
351
}
352
353
// Reconstruct data source
354
buf = DerIndefLenConverter.convertStream(
355
new ByteArrayInputStream(buf, pos, len - (pos - offset)), tag);
356
offset = 0;
357
len = buf.length;
358
pos = 2;
359
360
if (tag != buf[0]) {
361
throw new IOException("Indefinite length encoding not supported");
362
}
363
lenByte = buf[1];
364
if (lenByte == (byte) 0x80) {
365
throw new IOException("Indefinite len conversion failed");
366
}
367
}
368
369
if ((lenByte & 0x080) == 0x00) { // short form, 1 byte datum
370
length = lenByte;
371
} else { // long form
372
lenByte &= 0x07f;
373
if (lenByte > 4) {
374
throw new IOException("Invalid lenByte");
375
}
376
if (len < 2 + lenByte) {
377
throw new IOException("Not enough length bytes");
378
}
379
length = 0x0ff & buf[pos++];
380
lenByte--;
381
if (length == 0 && !allowBER) {
382
// DER requires length value be encoded in minimum number of bytes
383
throw new IOException("Redundant length bytes found");
384
}
385
while (lenByte-- > 0) {
386
length <<= 8;
387
length += 0x0ff & buf[pos++];
388
}
389
if (length < 0) {
390
throw new IOException("Invalid length bytes");
391
} else if (length <= 127 && !allowBER) {
392
throw new IOException("Should use short form for length");
393
}
394
}
395
// pos is now at the beginning of the content
396
if (len - length < pos - offset) {
397
throw new EOFException("not enough content");
398
}
399
if (len - length > pos - offset && !allowMore) {
400
throw new IOException("extra data at the end");
401
}
402
this.buffer = buf;
403
this.start = pos;
404
this.end = pos + length;
405
this.allowBER = allowBER;
406
this.data = data();
407
}
408
409
// Get an ASN1/DER encoded datum from an input stream w/ additional
410
// arg to control whether DER checks are enforced.
411
DerValue(InputStream in, boolean allowBER) throws IOException {
412
this.tag = (byte)in.read();
413
if ((tag & 0x1f) == 0x1f) {
414
throw new IOException("Tag number over 30 is not supported");
415
}
416
int length = DerInputStream.getLength(in);
417
if (length == -1) { // indefinite length encoding found
418
if (!allowBER) {
419
throw new IOException("Indefinite length encoding " +
420
"not supported with DER");
421
}
422
if (!isConstructed()) {
423
throw new IOException("Indefinite length encoding " +
424
"not supported with non-constructed data");
425
}
426
this.buffer = DerIndefLenConverter.convertStream(in, tag);
427
ByteArrayInputStream bin = new ByteArrayInputStream(this.buffer);
428
if (tag != bin.read()) {
429
throw new IOException
430
("Indefinite length encoding not supported");
431
}
432
length = DerInputStream.getDefiniteLength(bin);
433
this.start = this.buffer.length - bin.available();
434
this.end = this.start + length;
435
// position of in is undetermined. Precisely, it might be n-bytes
436
// after DerValue, and these n bytes are at the end of this.buffer
437
// after this.end.
438
} else {
439
this.buffer = IOUtils.readExactlyNBytes(in, length);
440
this.start = 0;
441
this.end = length;
442
// position of in is right after the DerValue
443
}
444
this.allowBER = allowBER;
445
this.data = data();
446
}
447
448
/**
449
* Get an ASN1/DER encoded datum from an input stream. The
450
* stream may have additional data following the encoded datum.
451
* In case of indefinite length encoded datum, the input stream
452
* must hold only one datum, i.e. all bytes in the stream might
453
* be consumed. Otherwise, only one DerValue will be consumed.
454
*
455
* @param in the input stream holding a single DER datum,
456
* which may be followed by additional data
457
*/
458
public DerValue(InputStream in) throws IOException {
459
this(in, true);
460
}
461
462
/**
463
* Encode an ASN1/DER encoded datum onto a DER output stream.
464
*/
465
public void encode(DerOutputStream out) throws IOException {
466
out.write(tag);
467
out.putLength(end - start);
468
out.write(buffer, start, end - start);
469
data.pos = data.end; // Compatibility. Reach end.
470
}
471
472
/**
473
* Returns a new DerInputStream pointing at the start of this
474
* DerValue's content.
475
*
476
* @return the new DerInputStream value
477
*/
478
public final DerInputStream data() {
479
return new DerInputStream(buffer, start, end - start, allowBER);
480
}
481
482
/**
483
* Returns the data field inside this class directly.
484
*
485
* Both this method and the {@link #data} field should be avoided.
486
* Consider using {@link #data()} instead.
487
*/
488
public final DerInputStream getData() {
489
return data;
490
}
491
492
public final byte getTag() {
493
return tag;
494
}
495
496
/**
497
* Returns an ASN.1 BOOLEAN
498
*
499
* @return the boolean held in this DER value
500
*/
501
public boolean getBoolean() throws IOException {
502
if (tag != tag_Boolean) {
503
throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag);
504
}
505
if (end - start != 1) {
506
throw new IOException("DerValue.getBoolean, invalid length "
507
+ (end - start));
508
}
509
data.pos = data.end; // Compatibility. Reach end.
510
return buffer[start] != 0;
511
}
512
513
/**
514
* Returns an ASN.1 OBJECT IDENTIFIER.
515
*
516
* @return the OID held in this DER value
517
*/
518
public ObjectIdentifier getOID() throws IOException {
519
if (tag != tag_ObjectId) {
520
throw new IOException("DerValue.getOID, not an OID " + tag);
521
}
522
data.pos = data.end; // Compatibility. Reach end.
523
return new ObjectIdentifier(Arrays.copyOfRange(buffer, start, end));
524
}
525
526
/**
527
* Returns an ASN.1 OCTET STRING
528
*
529
* @return the octet string held in this DER value
530
*/
531
public byte[] getOctetString() throws IOException {
532
533
if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
534
throw new IOException(
535
"DerValue.getOctetString, not an Octet String: " + tag);
536
}
537
// Note: do not attempt to call buffer.read(bytes) at all. There's a
538
// known bug that it returns -1 instead of 0.
539
if (end - start == 0) {
540
return new byte[0];
541
}
542
543
data.pos = data.end; // Compatibility. Reach end.
544
if (!isConstructed()) {
545
return Arrays.copyOfRange(buffer, start, end);
546
} else {
547
ByteArrayOutputStream bout = new ByteArrayOutputStream();
548
DerInputStream dis = data();
549
while (dis.available() > 0) {
550
bout.write(dis.getDerValue().getOctetString());
551
}
552
return bout.toByteArray();
553
}
554
}
555
556
/**
557
* Returns an ASN.1 INTEGER value as an integer.
558
*
559
* @return the integer held in this DER value.
560
*/
561
public int getInteger() throws IOException {
562
return getIntegerInternal(tag_Integer);
563
}
564
565
private int getIntegerInternal(byte expectedTag) throws IOException {
566
BigInteger result = getBigIntegerInternal(expectedTag, false);
567
if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
568
throw new IOException("Integer below minimum valid value");
569
}
570
if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
571
throw new IOException("Integer exceeds maximum valid value");
572
}
573
return result.intValue();
574
}
575
576
/**
577
* Returns an ASN.1 INTEGER value as a BigInteger.
578
*
579
* @return the integer held in this DER value as a BigInteger.
580
*/
581
public BigInteger getBigInteger() throws IOException {
582
return getBigIntegerInternal(tag_Integer, false);
583
}
584
585
/**
586
* Returns an ASN.1 INTEGER value as a positive BigInteger.
587
* This is just to deal with implementations that incorrectly encode
588
* some values as negative.
589
*
590
* @return the integer held in this DER value as a BigInteger.
591
*/
592
public BigInteger getPositiveBigInteger() throws IOException {
593
return getBigIntegerInternal(tag_Integer, true);
594
}
595
596
/**
597
* Returns a BigInteger value
598
*
599
* @param makePositive whether to always return a positive value,
600
* irrespective of actual encoding
601
* @return the integer as a BigInteger.
602
*/
603
private BigInteger getBigIntegerInternal(byte expectedTag, boolean makePositive) throws IOException {
604
if (tag != expectedTag) {
605
throw new IOException("DerValue.getBigIntegerInternal, not expected " + tag);
606
}
607
if (end == start) {
608
throw new IOException("Invalid encoding: zero length Int value");
609
}
610
data.pos = data.end; // Compatibility. Reach end.
611
if (!allowBER && (end - start >= 2 && (buffer[start] == 0) && (buffer[start + 1] >= 0))) {
612
throw new IOException("Invalid encoding: redundant leading 0s");
613
}
614
return makePositive
615
? new BigInteger(1, buffer, start, end - start)
616
: new BigInteger(buffer, start, end - start);
617
}
618
619
/**
620
* Returns an ASN.1 ENUMERATED value.
621
*
622
* @return the integer held in this DER value.
623
*/
624
public int getEnumerated() throws IOException {
625
return getIntegerInternal(tag_Enumerated);
626
}
627
628
/**
629
* Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned.
630
*
631
* @return the bit string held in this value
632
*/
633
public byte[] getBitString() throws IOException {
634
return getBitString(false);
635
}
636
637
/**
638
* Returns an ASN.1 BIT STRING value that need not be byte-aligned.
639
*
640
* @return a BitArray representing the bit string held in this value
641
*/
642
public BitArray getUnalignedBitString() throws IOException {
643
return getUnalignedBitString(false);
644
}
645
646
/**
647
* Returns the name component as a Java string, regardless of its
648
* encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8).
649
*/
650
// TBD: Need encoder for UniversalString before it can be handled.
651
public String getAsString() throws IOException {
652
return switch (tag) {
653
case tag_UTF8String -> getUTF8String();
654
case tag_PrintableString -> getPrintableString();
655
case tag_T61String -> getT61String();
656
case tag_IA5String -> getIA5String();
657
case tag_UniversalString -> getUniversalString();
658
case tag_BMPString -> getBMPString();
659
case tag_GeneralString -> getGeneralString();
660
default -> null;
661
};
662
}
663
664
/**
665
* Returns an ASN.1 BIT STRING value, with the tag assumed implicit
666
* based on the parameter. The bit string must be byte-aligned.
667
*
668
* @param tagImplicit if true, the tag is assumed implicit.
669
* @return the bit string held in this value
670
*/
671
public byte[] getBitString(boolean tagImplicit) throws IOException {
672
if (!tagImplicit) {
673
if (tag != tag_BitString) {
674
throw new IOException("DerValue.getBitString, not a bit string "
675
+ tag);
676
}
677
}
678
if (end == start) {
679
throw new IOException("Invalid encoding: zero length bit string");
680
}
681
int numOfPadBits = buffer[start];
682
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
683
throw new IOException("Invalid number of padding bits");
684
}
685
// minus the first byte which indicates the number of padding bits
686
byte[] retval = Arrays.copyOfRange(buffer, start + 1, end);
687
if (numOfPadBits != 0) {
688
// get rid of the padding bits
689
retval[end - start - 2] &= (0xff << numOfPadBits);
690
}
691
data.pos = data.end; // Compatibility. Reach end.
692
return retval;
693
}
694
695
/**
696
* Returns an ASN.1 BIT STRING value, with the tag assumed implicit
697
* based on the parameter. The bit string need not be byte-aligned.
698
*
699
* @param tagImplicit if true, the tag is assumed implicit.
700
* @return the bit string held in this value
701
*/
702
public BitArray getUnalignedBitString(boolean tagImplicit)
703
throws IOException {
704
if (!tagImplicit) {
705
if (tag != tag_BitString) {
706
throw new IOException("DerValue.getBitString, not a bit string "
707
+ tag);
708
}
709
}
710
if (end == start) {
711
throw new IOException("Invalid encoding: zero length bit string");
712
}
713
data.pos = data.end; // Compatibility. Reach end.
714
int numOfPadBits = buffer[start];
715
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
716
throw new IOException("Invalid number of padding bits");
717
}
718
if (end == start + 1) {
719
return new BitArray(0);
720
} else {
721
return new BitArray(((end - start - 1) << 3) - numOfPadBits,
722
Arrays.copyOfRange(buffer, start + 1, end));
723
}
724
}
725
726
/**
727
* Helper routine to return all the bytes contained in the
728
* DerInputStream associated with this object.
729
*/
730
public byte[] getDataBytes() throws IOException {
731
data.pos = data.end; // Compatibility. Reach end.
732
return Arrays.copyOfRange(buffer, start, end);
733
}
734
735
private String readStringInternal(byte expectedTag, Charset cs) throws IOException {
736
if (tag != expectedTag) {
737
throw new IOException("Incorrect string type " + tag + " is not " + expectedTag);
738
}
739
data.pos = data.end; // Compatibility. Reach end.
740
return new String(buffer, start, end - start, cs);
741
}
742
743
/**
744
* Returns an ASN.1 STRING value
745
*
746
* @return the printable string held in this value
747
*/
748
public String getPrintableString() throws IOException {
749
return readStringInternal(tag_PrintableString, US_ASCII);
750
}
751
752
/**
753
* Returns an ASN.1 T61 (Teletype) STRING value
754
*
755
* @return the teletype string held in this value
756
*/
757
public String getT61String() throws IOException {
758
return readStringInternal(tag_T61String, ISO_8859_1);
759
}
760
761
/**
762
* Returns an ASN.1 IA5 (ASCII) STRING value
763
*
764
* @return the ASCII string held in this value
765
*/
766
public String getIA5String() throws IOException {
767
return readStringInternal(tag_IA5String, US_ASCII);
768
}
769
770
/**
771
* Returns the ASN.1 BMP (Unicode) STRING value as a Java string.
772
*
773
* @return a string corresponding to the encoded BMPString held in
774
* this value
775
*/
776
public String getBMPString() throws IOException {
777
// BMPString is the same as Unicode in big endian, unmarked format.
778
return readStringInternal(tag_BMPString, UTF_16BE);
779
}
780
781
/**
782
* Returns the ASN.1 UTF-8 STRING value as a Java String.
783
*
784
* @return a string corresponding to the encoded UTF8String held in
785
* this value
786
*/
787
public String getUTF8String() throws IOException {
788
return readStringInternal(tag_UTF8String, UTF_8);
789
}
790
791
/**
792
* Returns the ASN.1 GENERAL STRING value as a Java String.
793
*
794
* @return a string corresponding to the encoded GeneralString held in
795
* this value
796
*/
797
public String getGeneralString() throws IOException {
798
return readStringInternal(tag_GeneralString, US_ASCII);
799
}
800
801
/**
802
* Returns the ASN.1 UNIVERSAL (UTF-32) STRING value as a Java String.
803
*
804
* @return a string corresponding to the encoded UniversalString held in
805
* this value
806
*/
807
public String getUniversalString() throws IOException {
808
return readStringInternal(tag_UniversalString, new UTF_32BE());
809
}
810
811
/**
812
* Reads the ASN.1 NULL value
813
*/
814
public void getNull() throws IOException {
815
if (tag != tag_Null) {
816
throw new IOException("DerValue.getNull, not NULL: " + tag);
817
}
818
if (end != start) {
819
throw new IOException("NULL should contain no data");
820
}
821
}
822
823
/**
824
* Private helper routine to extract time from the der value.
825
* @param generalized true if Generalized Time is to be read, false
826
* if UTC Time is to be read.
827
*/
828
private Date getTimeInternal(boolean generalized) throws IOException {
829
830
/*
831
* UTC time encoded as ASCII chars:
832
* YYMMDDhhmmZ
833
* YYMMDDhhmmssZ
834
* YYMMDDhhmm+hhmm
835
* YYMMDDhhmm-hhmm
836
* YYMMDDhhmmss+hhmm
837
* YYMMDDhhmmss-hhmm
838
* UTC Time is broken in storing only two digits of year.
839
* If YY < 50, we assume 20YY;
840
* if YY >= 50, we assume 19YY, as per RFC 5280.
841
*
842
* Generalized time has a four-digit year and allows any
843
* precision specified in ISO 8601. However, for our purposes,
844
* we will only allow the same format as UTC time, except that
845
* fractional seconds (millisecond precision) are supported.
846
*/
847
848
int year, month, day, hour, minute, second, millis;
849
String type;
850
int pos = start;
851
int len = end - start;
852
853
if (generalized) {
854
type = "Generalized";
855
year = 1000 * toDigit(buffer[pos++], type);
856
year += 100 * toDigit(buffer[pos++], type);
857
year += 10 * toDigit(buffer[pos++], type);
858
year += toDigit(buffer[pos++], type);
859
len -= 2; // For the two extra YY
860
} else {
861
type = "UTC";
862
year = 10 * toDigit(buffer[pos++], type);
863
year += toDigit(buffer[pos++], type);
864
865
if (year < 50) { // origin 2000
866
year += 2000;
867
} else {
868
year += 1900; // origin 1900
869
}
870
}
871
872
month = 10 * toDigit(buffer[pos++], type);
873
month += toDigit(buffer[pos++], type);
874
875
day = 10 * toDigit(buffer[pos++], type);
876
day += toDigit(buffer[pos++], type);
877
878
hour = 10 * toDigit(buffer[pos++], type);
879
hour += toDigit(buffer[pos++], type);
880
881
minute = 10 * toDigit(buffer[pos++], type);
882
minute += toDigit(buffer[pos++], type);
883
884
len -= 10; // YYMMDDhhmm
885
886
/*
887
* We allow for non-encoded seconds, even though the
888
* IETF-PKIX specification says that the seconds should
889
* always be encoded even if it is zero.
890
*/
891
892
millis = 0;
893
if (len > 2) {
894
second = 10 * toDigit(buffer[pos++], type);
895
second += toDigit(buffer[pos++], type);
896
len -= 2;
897
// handle fractional seconds (if present)
898
if (generalized && (buffer[pos] == '.' || buffer[pos] == ',')) {
899
len --;
900
if (len == 0) {
901
throw new IOException("Parse " + type +
902
" time, empty fractional part");
903
}
904
pos++;
905
int precision = 0;
906
while (buffer[pos] != 'Z' &&
907
buffer[pos] != '+' &&
908
buffer[pos] != '-') {
909
// Validate all digits in the fractional part but
910
// store millisecond precision only
911
int thisDigit = toDigit(buffer[pos], type);
912
precision++;
913
len--;
914
if (len == 0) {
915
throw new IOException("Parse " + type +
916
" time, invalid fractional part");
917
}
918
pos++;
919
switch (precision) {
920
case 1 -> millis += 100 * thisDigit;
921
case 2 -> millis += 10 * thisDigit;
922
case 3 -> millis += thisDigit;
923
}
924
}
925
if (precision == 0) {
926
throw new IOException("Parse " + type +
927
" time, empty fractional part");
928
}
929
}
930
} else
931
second = 0;
932
933
if (month == 0 || day == 0
934
|| month > 12 || day > 31
935
|| hour >= 24 || minute >= 60 || second >= 60) {
936
throw new IOException("Parse " + type + " time, invalid format");
937
}
938
939
/*
940
* Generalized time can theoretically allow any precision,
941
* but we're not supporting that.
942
*/
943
CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
944
CalendarDate date = gcal.newCalendarDate(null); // no time zone
945
date.setDate(year, month, day);
946
date.setTimeOfDay(hour, minute, second, millis);
947
long time = gcal.getTime(date);
948
949
/*
950
* Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
951
*/
952
if (! (len == 1 || len == 5)) {
953
throw new IOException("Parse " + type + " time, invalid offset");
954
}
955
956
int hr, min;
957
958
switch (buffer[pos++]) {
959
case '+':
960
if (len != 5) {
961
throw new IOException("Parse " + type + " time, invalid offset");
962
}
963
hr = 10 * toDigit(buffer[pos++], type);
964
hr += toDigit(buffer[pos++], type);
965
min = 10 * toDigit(buffer[pos++], type);
966
min += toDigit(buffer[pos++], type);
967
968
if (hr >= 24 || min >= 60) {
969
throw new IOException("Parse " + type + " time, +hhmm");
970
}
971
972
time -= ((hr * 60) + min) * 60 * 1000;
973
break;
974
975
case '-':
976
if (len != 5) {
977
throw new IOException("Parse " + type + " time, invalid offset");
978
}
979
hr = 10 * toDigit(buffer[pos++], type);
980
hr += toDigit(buffer[pos++], type);
981
min = 10 * toDigit(buffer[pos++], type);
982
min += toDigit(buffer[pos++], type);
983
984
if (hr >= 24 || min >= 60) {
985
throw new IOException("Parse " + type + " time, -hhmm");
986
}
987
988
time += ((hr * 60) + min) * 60 * 1000;
989
break;
990
991
case 'Z':
992
if (len != 1) {
993
throw new IOException("Parse " + type + " time, invalid format");
994
}
995
break;
996
997
default:
998
throw new IOException("Parse " + type + " time, garbage offset");
999
}
1000
return new Date(time);
1001
}
1002
1003
/**
1004
* Converts byte (represented as a char) to int.
1005
* @throws IOException if integer is not a valid digit in the specified
1006
* radix (10)
1007
*/
1008
private static int toDigit(byte b, String type) throws IOException {
1009
if (b < '0' || b > '9') {
1010
throw new IOException("Parse " + type + " time, invalid format");
1011
}
1012
return b - '0';
1013
}
1014
1015
/**
1016
* Returns a Date if the DerValue is UtcTime.
1017
*
1018
* @return the Date held in this DER value
1019
*/
1020
public Date getUTCTime() throws IOException {
1021
if (tag != tag_UtcTime) {
1022
throw new IOException("DerValue.getUTCTime, not a UtcTime: " + tag);
1023
}
1024
if (end - start < 11 || end - start > 17)
1025
throw new IOException("DER UTC Time length error");
1026
1027
data.pos = data.end; // Compatibility. Reach end.
1028
return getTimeInternal(false);
1029
}
1030
1031
/**
1032
* Returns a Date if the DerValue is GeneralizedTime.
1033
*
1034
* @return the Date held in this DER value
1035
*/
1036
public Date getGeneralizedTime() throws IOException {
1037
if (tag != tag_GeneralizedTime) {
1038
throw new IOException(
1039
"DerValue.getGeneralizedTime, not a GeneralizedTime: " + tag);
1040
}
1041
if (end - start < 13)
1042
throw new IOException("DER Generalized Time length error");
1043
1044
data.pos = data.end; // Compatibility. Reach end.
1045
return getTimeInternal(true);
1046
}
1047
1048
/**
1049
* Bitwise equality comparison. DER encoded values have a single
1050
* encoding, so that bitwise equality of the encoded values is an
1051
* efficient way to establish equivalence of the unencoded values.
1052
*
1053
* @param o the object being compared with this one
1054
*/
1055
@Override
1056
public boolean equals(Object o) {
1057
if (this == o) {
1058
return true;
1059
}
1060
if (!(o instanceof DerValue)) {
1061
return false;
1062
}
1063
DerValue other = (DerValue) o;
1064
if (tag != other.tag) {
1065
return false;
1066
}
1067
if (buffer == other.buffer && start == other.start && end == other.end) {
1068
return true;
1069
}
1070
return Arrays.equals(buffer, start, end, other.buffer, other.start, other.end);
1071
}
1072
1073
/**
1074
* Returns a printable representation of the value.
1075
*
1076
* @return printable representation of the value
1077
*/
1078
@Override
1079
public String toString() {
1080
return String.format("DerValue(%02x, %s, %d, %d)",
1081
0xff & tag, buffer, start, end);
1082
}
1083
1084
/**
1085
* Returns a DER-encoded value, such that if it's passed to the
1086
* DerValue constructor, a value equivalent to "this" is returned.
1087
*
1088
* @return DER-encoded value, including tag and length.
1089
*/
1090
public byte[] toByteArray() throws IOException {
1091
data.pos = data.start; // Compatibility. At head.
1092
// Minimize content duplication by writing out tag and length only
1093
DerOutputStream out = new DerOutputStream();
1094
out.write(tag);
1095
out.putLength(end - start);
1096
int headLen = out.size();
1097
byte[] result = Arrays.copyOf(out.buf(), end - start + headLen);
1098
System.arraycopy(buffer, start, result, headLen, end - start);
1099
return result;
1100
}
1101
1102
/**
1103
* For "set" and "sequence" types, this function may be used
1104
* to return a DER stream of the members of the set or sequence.
1105
* This operation is not supported for primitive types such as
1106
* integers or bit strings.
1107
*/
1108
public DerInputStream toDerInputStream() throws IOException {
1109
if (tag == tag_Sequence || tag == tag_Set)
1110
return data;
1111
throw new IOException("toDerInputStream rejects tag type " + tag);
1112
}
1113
1114
/**
1115
* Get the length of the encoded value.
1116
*/
1117
public int length() {
1118
return end - start;
1119
}
1120
1121
/**
1122
* Determine if a character is one of the permissible characters for
1123
* PrintableString:
1124
* A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,
1125
* plus sign, comma, hyphen, period, slash, colon, equals sign,
1126
* and question mark.
1127
*
1128
* Characters that are *not* allowed in PrintableString include
1129
* exclamation point, quotation mark, number sign, dollar sign,
1130
* percent sign, ampersand, asterisk, semicolon, less than sign,
1131
* greater than sign, at sign, left and right square brackets,
1132
* backslash, circumflex (94), underscore, back quote (96),
1133
* left and right curly brackets, vertical line, tilde,
1134
* and the control codes (0-31 and 127).
1135
*
1136
* This list is based on X.680 (the ASN.1 spec).
1137
*/
1138
public static boolean isPrintableStringChar(char ch) {
1139
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1140
(ch >= '0' && ch <= '9')) {
1141
return true;
1142
} else {
1143
switch (ch) {
1144
case ' ': /* space */
1145
case '\'': /* apostrophe */
1146
case '(': /* left paren */
1147
case ')': /* right paren */
1148
case '+': /* plus */
1149
case ',': /* comma */
1150
case '-': /* hyphen */
1151
case '.': /* period */
1152
case '/': /* slash */
1153
case ':': /* colon */
1154
case '=': /* equals */
1155
case '?': /* question mark */
1156
return true;
1157
default:
1158
return false;
1159
}
1160
}
1161
}
1162
1163
/**
1164
* Create the tag of the attribute.
1165
*
1166
* @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,
1167
* APPLICATION or PRIVATE
1168
* @param form if true, the value is constructed, otherwise it
1169
* is primitive.
1170
* @param val the tag value
1171
*/
1172
public static byte createTag(byte tagClass, boolean form, byte val) {
1173
if (val < 0 || val > 30) {
1174
throw new IllegalArgumentException("Tag number over 30 is not supported");
1175
}
1176
byte tag = (byte)(tagClass | val);
1177
if (form) {
1178
tag |= (byte)0x20;
1179
}
1180
return (tag);
1181
}
1182
1183
/**
1184
* Set the tag of the attribute. Commonly used to reset the
1185
* tag value used for IMPLICIT encodings.
1186
*
1187
* This method should be avoided, consider using withTag() instead.
1188
*
1189
* @param tag the tag value
1190
*/
1191
public void resetTag(byte tag) {
1192
this.tag = tag;
1193
}
1194
1195
/**
1196
* Returns a new DerValue with a different tag. This method is used
1197
* to convert a DerValue decoded from an IMPLICIT encoding to its real
1198
* tag. The content is not checked against the tag in this method.
1199
*
1200
* @param newTag the new tag
1201
* @return a new DerValue
1202
*/
1203
public DerValue withTag(byte newTag) {
1204
return new DerValue(newTag, buffer, start, end, allowBER);
1205
}
1206
1207
/**
1208
* Returns a hashcode for this DerValue.
1209
*
1210
* @return a hashcode for this DerValue.
1211
*/
1212
@Override
1213
public int hashCode() {
1214
int result = tag;
1215
for (int i = start; i < end; i++) {
1216
result = 31 * result + buffer[i];
1217
}
1218
return result;
1219
}
1220
1221
/**
1222
* Reads the sub-values in a constructed DerValue.
1223
*
1224
* @param expectedTag the expected tag, or zero if we don't check.
1225
* This is useful when this DerValue is IMPLICIT.
1226
* @param startLen estimated number of sub-values
1227
* @return the sub-values in an array
1228
*/
1229
DerValue[] subs(byte expectedTag, int startLen) throws IOException {
1230
if (expectedTag != 0 && expectedTag != tag) {
1231
throw new IOException("Not the correct tag");
1232
}
1233
List<DerValue> result = new ArrayList<>(startLen);
1234
DerInputStream dis = data();
1235
while (dis.available() > 0) {
1236
result.add(dis.getDerValue());
1237
}
1238
return result.toArray(new DerValue[0]);
1239
}
1240
1241
public void clear() {
1242
Arrays.fill(buffer, start, end, (byte)0);
1243
}
1244
}
1245
1246