Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.base/share/classes/sun/security/util/DerValue.java
67771 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
* Wraps a byte array as a single DerValue.
307
*
308
* Attention: no cloning is made.
309
*
310
* @param buf the byte array containing the DER-encoded datum
311
* @returns a new DerValue
312
*/
313
public static DerValue wrap(byte[] buf)
314
throws IOException {
315
return wrap(buf, 0, buf.length);
316
}
317
318
/**
319
* Wraps a byte array as a single DerValue.
320
*
321
* Attention: no cloning is made.
322
*
323
* @param buf the byte array containing the DER-encoded datum
324
* @param offset where the encoded datum starts inside {@code buf}
325
* @param len length of bytes to parse inside {@code buf}
326
* @returns a new DerValue
327
*/
328
public static DerValue wrap(byte[] buf, int offset, int len)
329
throws IOException {
330
return new DerValue(buf, offset, len, true, false);
331
}
332
333
/**
334
* Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly
335
* one datum, including its tag and length.
336
*
337
* This is a public constructor.
338
*/
339
public DerValue(byte[] encoding) throws IOException {
340
this(encoding.clone(), 0, encoding.length, true, false);
341
}
342
343
/**
344
* Parse an ASN.1 encoded datum from a byte array.
345
*
346
* @param buf the byte array containing the DER-encoded datum
347
* @param offset where the encoded datum starts inside {@code buf}
348
* @param len length of bytes to parse inside {@code buf}
349
* @param allowBER whether BER is allowed
350
* @param allowMore whether extra bytes are allowed after the encoded datum.
351
* If true, {@code len} can be bigger than the length of
352
* the encoded datum.
353
*
354
* @throws IOException if it's an invalid encoding or there are extra bytes
355
* after the encoded datum and {@code allowMore} is false.
356
*/
357
DerValue(byte[] buf, int offset, int len, boolean allowBER, boolean allowMore)
358
throws IOException {
359
360
if (len < 2) {
361
throw new IOException("Too short");
362
}
363
int pos = offset;
364
tag = buf[pos++];
365
if ((tag & 0x1f) == 0x1f) {
366
throw new IOException("Tag number over 30 at " + offset + " is not supported");
367
}
368
int lenByte = buf[pos++];
369
370
int length;
371
if (lenByte == (byte) 0x80) { // indefinite length
372
if (!allowBER) {
373
throw new IOException("Indefinite length encoding " +
374
"not supported with DER");
375
}
376
if (!isConstructed()) {
377
throw new IOException("Indefinite length encoding " +
378
"not supported with non-constructed data");
379
}
380
381
// Reconstruct data source
382
buf = DerIndefLenConverter.convertStream(
383
new ByteArrayInputStream(buf, pos, len - (pos - offset)), tag);
384
offset = 0;
385
len = buf.length;
386
pos = 2;
387
388
if (tag != buf[0]) {
389
throw new IOException("Indefinite length encoding not supported");
390
}
391
lenByte = buf[1];
392
if (lenByte == (byte) 0x80) {
393
throw new IOException("Indefinite len conversion failed");
394
}
395
}
396
397
if ((lenByte & 0x080) == 0x00) { // short form, 1 byte datum
398
length = lenByte;
399
} else { // long form
400
lenByte &= 0x07f;
401
if (lenByte > 4) {
402
throw new IOException("Invalid lenByte");
403
}
404
if (len < 2 + lenByte) {
405
throw new IOException("Not enough length bytes");
406
}
407
length = 0x0ff & buf[pos++];
408
lenByte--;
409
if (length == 0 && !allowBER) {
410
// DER requires length value be encoded in minimum number of bytes
411
throw new IOException("Redundant length bytes found");
412
}
413
while (lenByte-- > 0) {
414
length <<= 8;
415
length += 0x0ff & buf[pos++];
416
}
417
if (length < 0) {
418
throw new IOException("Invalid length bytes");
419
} else if (length <= 127 && !allowBER) {
420
throw new IOException("Should use short form for length");
421
}
422
}
423
// pos is now at the beginning of the content
424
if (len - length < pos - offset) {
425
throw new EOFException("not enough content");
426
}
427
if (len - length > pos - offset && !allowMore) {
428
throw new IOException("extra data at the end");
429
}
430
this.buffer = buf;
431
this.start = pos;
432
this.end = pos + length;
433
this.allowBER = allowBER;
434
this.data = data();
435
}
436
437
// Get an ASN1/DER encoded datum from an input stream w/ additional
438
// arg to control whether DER checks are enforced.
439
DerValue(InputStream in, boolean allowBER) throws IOException {
440
this.tag = (byte)in.read();
441
if ((tag & 0x1f) == 0x1f) {
442
throw new IOException("Tag number over 30 is not supported");
443
}
444
int length = DerInputStream.getLength(in);
445
if (length == -1) { // indefinite length encoding found
446
if (!allowBER) {
447
throw new IOException("Indefinite length encoding " +
448
"not supported with DER");
449
}
450
if (!isConstructed()) {
451
throw new IOException("Indefinite length encoding " +
452
"not supported with non-constructed data");
453
}
454
this.buffer = DerIndefLenConverter.convertStream(in, tag);
455
ByteArrayInputStream bin = new ByteArrayInputStream(this.buffer);
456
if (tag != bin.read()) {
457
throw new IOException
458
("Indefinite length encoding not supported");
459
}
460
length = DerInputStream.getDefiniteLength(bin);
461
this.start = this.buffer.length - bin.available();
462
this.end = this.start + length;
463
// position of in is undetermined. Precisely, it might be n-bytes
464
// after DerValue, and these n bytes are at the end of this.buffer
465
// after this.end.
466
} else {
467
this.buffer = IOUtils.readExactlyNBytes(in, length);
468
this.start = 0;
469
this.end = length;
470
// position of in is right after the DerValue
471
}
472
this.allowBER = allowBER;
473
this.data = data();
474
}
475
476
/**
477
* Get an ASN1/DER encoded datum from an input stream. The
478
* stream may have additional data following the encoded datum.
479
* In case of indefinite length encoded datum, the input stream
480
* must hold only one datum, i.e. all bytes in the stream might
481
* be consumed. Otherwise, only one DerValue will be consumed.
482
*
483
* @param in the input stream holding a single DER datum,
484
* which may be followed by additional data
485
*/
486
public DerValue(InputStream in) throws IOException {
487
this(in, true);
488
}
489
490
/**
491
* Encode an ASN1/DER encoded datum onto a DER output stream.
492
*/
493
public void encode(DerOutputStream out) throws IOException {
494
out.write(tag);
495
out.putLength(end - start);
496
out.write(buffer, start, end - start);
497
data.pos = data.end; // Compatibility. Reach end.
498
}
499
500
/**
501
* Returns a new DerInputStream pointing at the start of this
502
* DerValue's content.
503
*
504
* @return the new DerInputStream value
505
*/
506
public final DerInputStream data() {
507
return new DerInputStream(buffer, start, end - start, allowBER);
508
}
509
510
/**
511
* Returns the data field inside this class directly.
512
*
513
* Both this method and the {@link #data} field should be avoided.
514
* Consider using {@link #data()} instead.
515
*/
516
public final DerInputStream getData() {
517
return data;
518
}
519
520
public final byte getTag() {
521
return tag;
522
}
523
524
/**
525
* Returns an ASN.1 BOOLEAN
526
*
527
* @return the boolean held in this DER value
528
*/
529
public boolean getBoolean() throws IOException {
530
if (tag != tag_Boolean) {
531
throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag);
532
}
533
if (end - start != 1) {
534
throw new IOException("DerValue.getBoolean, invalid length "
535
+ (end - start));
536
}
537
data.pos = data.end; // Compatibility. Reach end.
538
return buffer[start] != 0;
539
}
540
541
/**
542
* Returns an ASN.1 OBJECT IDENTIFIER.
543
*
544
* @return the OID held in this DER value
545
*/
546
public ObjectIdentifier getOID() throws IOException {
547
if (tag != tag_ObjectId) {
548
throw new IOException("DerValue.getOID, not an OID " + tag);
549
}
550
data.pos = data.end; // Compatibility. Reach end.
551
return new ObjectIdentifier(Arrays.copyOfRange(buffer, start, end));
552
}
553
554
/**
555
* Returns an ASN.1 OCTET STRING
556
*
557
* @return the octet string held in this DER value
558
*/
559
public byte[] getOctetString() throws IOException {
560
561
if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {
562
throw new IOException(
563
"DerValue.getOctetString, not an Octet String: " + tag);
564
}
565
// Note: do not attempt to call buffer.read(bytes) at all. There's a
566
// known bug that it returns -1 instead of 0.
567
if (end - start == 0) {
568
return new byte[0];
569
}
570
571
data.pos = data.end; // Compatibility. Reach end.
572
if (!isConstructed()) {
573
return Arrays.copyOfRange(buffer, start, end);
574
} else {
575
ByteArrayOutputStream bout = new ByteArrayOutputStream();
576
DerInputStream dis = data();
577
while (dis.available() > 0) {
578
bout.write(dis.getDerValue().getOctetString());
579
}
580
return bout.toByteArray();
581
}
582
}
583
584
/**
585
* Returns an ASN.1 INTEGER value as an integer.
586
*
587
* @return the integer held in this DER value.
588
*/
589
public int getInteger() throws IOException {
590
return getIntegerInternal(tag_Integer);
591
}
592
593
private int getIntegerInternal(byte expectedTag) throws IOException {
594
BigInteger result = getBigIntegerInternal(expectedTag, false);
595
if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
596
throw new IOException("Integer below minimum valid value");
597
}
598
if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
599
throw new IOException("Integer exceeds maximum valid value");
600
}
601
return result.intValue();
602
}
603
604
/**
605
* Returns an ASN.1 INTEGER value as a BigInteger.
606
*
607
* @return the integer held in this DER value as a BigInteger.
608
*/
609
public BigInteger getBigInteger() throws IOException {
610
return getBigIntegerInternal(tag_Integer, false);
611
}
612
613
/**
614
* Returns an ASN.1 INTEGER value as a positive BigInteger.
615
* This is just to deal with implementations that incorrectly encode
616
* some values as negative.
617
*
618
* @return the integer held in this DER value as a BigInteger.
619
*/
620
public BigInteger getPositiveBigInteger() throws IOException {
621
return getBigIntegerInternal(tag_Integer, true);
622
}
623
624
/**
625
* Returns a BigInteger value
626
*
627
* @param makePositive whether to always return a positive value,
628
* irrespective of actual encoding
629
* @return the integer as a BigInteger.
630
*/
631
private BigInteger getBigIntegerInternal(byte expectedTag, boolean makePositive) throws IOException {
632
if (tag != expectedTag) {
633
throw new IOException("DerValue.getBigIntegerInternal, not expected " + tag);
634
}
635
if (end == start) {
636
throw new IOException("Invalid encoding: zero length Int value");
637
}
638
data.pos = data.end; // Compatibility. Reach end.
639
if (!allowBER && (end - start >= 2 && (buffer[start] == 0) && (buffer[start + 1] >= 0))) {
640
throw new IOException("Invalid encoding: redundant leading 0s");
641
}
642
return makePositive
643
? new BigInteger(1, buffer, start, end - start)
644
: new BigInteger(buffer, start, end - start);
645
}
646
647
/**
648
* Returns an ASN.1 ENUMERATED value.
649
*
650
* @return the integer held in this DER value.
651
*/
652
public int getEnumerated() throws IOException {
653
return getIntegerInternal(tag_Enumerated);
654
}
655
656
/**
657
* Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned.
658
*
659
* @return the bit string held in this value
660
*/
661
public byte[] getBitString() throws IOException {
662
return getBitString(false);
663
}
664
665
/**
666
* Returns an ASN.1 BIT STRING value that need not be byte-aligned.
667
*
668
* @return a BitArray representing the bit string held in this value
669
*/
670
public BitArray getUnalignedBitString() throws IOException {
671
return getUnalignedBitString(false);
672
}
673
674
/**
675
* Returns the name component as a Java string, regardless of its
676
* encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8).
677
*/
678
// TBD: Need encoder for UniversalString before it can be handled.
679
public String getAsString() throws IOException {
680
return switch (tag) {
681
case tag_UTF8String -> getUTF8String();
682
case tag_PrintableString -> getPrintableString();
683
case tag_T61String -> getT61String();
684
case tag_IA5String -> getIA5String();
685
case tag_UniversalString -> getUniversalString();
686
case tag_BMPString -> getBMPString();
687
case tag_GeneralString -> getGeneralString();
688
default -> null;
689
};
690
}
691
692
// check the number of pad bits, validate the pad bits in the bytes
693
// if enforcing DER (i.e. allowBER == false), and return the number of
694
// bits of the resulting BitString
695
private static int checkPaddedBits(int numOfPadBits, byte[] data,
696
int start, int end, boolean allowBER) throws IOException {
697
// number of pad bits should be from 0(min) to 7(max).
698
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
699
throw new IOException("Invalid number of padding bits");
700
}
701
int lenInBits = ((end - start) << 3) - numOfPadBits;
702
if (lenInBits < 0) {
703
throw new IOException("Not enough bytes in BitString");
704
}
705
706
// padding bits should be all zeros for DER
707
if (!allowBER && numOfPadBits != 0 &&
708
(data[end - 1] & (0xff >>> (8 - numOfPadBits))) != 0) {
709
throw new IOException("Invalid value of padding bits");
710
}
711
return lenInBits;
712
}
713
714
/**
715
* Returns an ASN.1 BIT STRING value, with the tag assumed implicit
716
* based on the parameter. The bit string must be byte-aligned.
717
*
718
* @param tagImplicit if true, the tag is assumed implicit.
719
* @return the bit string held in this value
720
*/
721
public byte[] getBitString(boolean tagImplicit) throws IOException {
722
if (!tagImplicit) {
723
if (tag != tag_BitString) {
724
throw new IOException("DerValue.getBitString, not a bit string "
725
+ tag);
726
}
727
}
728
if (end == start) {
729
throw new IOException("Invalid encoding: zero length bit string");
730
731
}
732
data.pos = data.end; // Compatibility. Reach end.
733
734
int numOfPadBits = buffer[start];
735
checkPaddedBits(numOfPadBits, buffer, start + 1, end, allowBER);
736
byte[] retval = Arrays.copyOfRange(buffer, start + 1, end);
737
if (allowBER && numOfPadBits != 0) {
738
// fix the potential non-zero padding bits
739
retval[retval.length - 1] &= (0xff << numOfPadBits);
740
}
741
return retval;
742
}
743
744
/**
745
* Returns an ASN.1 BIT STRING value, with the tag assumed implicit
746
* based on the parameter. The bit string need not be byte-aligned.
747
*
748
* @param tagImplicit if true, the tag is assumed implicit.
749
* @return the bit string held in this value
750
*/
751
public BitArray getUnalignedBitString(boolean tagImplicit)
752
throws IOException {
753
if (!tagImplicit) {
754
if (tag != tag_BitString) {
755
throw new IOException("DerValue.getBitString, not a bit string "
756
+ tag);
757
}
758
}
759
if (end == start) {
760
throw new IOException("Invalid encoding: zero length bit string");
761
}
762
data.pos = data.end; // Compatibility. Reach end.
763
764
int numOfPadBits = buffer[start];
765
int len = checkPaddedBits(numOfPadBits, buffer, start + 1, end,
766
allowBER);
767
return new BitArray(len, buffer, start + 1);
768
}
769
770
/**
771
* Helper routine to return all the bytes contained in the
772
* DerInputStream associated with this object.
773
*/
774
public byte[] getDataBytes() throws IOException {
775
data.pos = data.end; // Compatibility. Reach end.
776
return Arrays.copyOfRange(buffer, start, end);
777
}
778
779
private String readStringInternal(byte expectedTag, Charset cs) throws IOException {
780
if (tag != expectedTag) {
781
throw new IOException("Incorrect string type " + tag + " is not " + expectedTag);
782
}
783
data.pos = data.end; // Compatibility. Reach end.
784
return new String(buffer, start, end - start, cs);
785
}
786
787
/**
788
* Returns an ASN.1 STRING value
789
*
790
* @return the printable string held in this value
791
*/
792
public String getPrintableString() throws IOException {
793
return readStringInternal(tag_PrintableString, US_ASCII);
794
}
795
796
/**
797
* Returns an ASN.1 T61 (Teletype) STRING value
798
*
799
* @return the teletype string held in this value
800
*/
801
public String getT61String() throws IOException {
802
return readStringInternal(tag_T61String, ISO_8859_1);
803
}
804
805
/**
806
* Returns an ASN.1 IA5 (ASCII) STRING value
807
*
808
* @return the ASCII string held in this value
809
*/
810
public String getIA5String() throws IOException {
811
return readStringInternal(tag_IA5String, US_ASCII);
812
}
813
814
/**
815
* Returns the ASN.1 BMP (Unicode) STRING value as a Java string.
816
*
817
* @return a string corresponding to the encoded BMPString held in
818
* this value
819
*/
820
public String getBMPString() throws IOException {
821
// BMPString is the same as Unicode in big endian, unmarked format.
822
return readStringInternal(tag_BMPString, UTF_16BE);
823
}
824
825
/**
826
* Returns the ASN.1 UTF-8 STRING value as a Java String.
827
*
828
* @return a string corresponding to the encoded UTF8String held in
829
* this value
830
*/
831
public String getUTF8String() throws IOException {
832
return readStringInternal(tag_UTF8String, UTF_8);
833
}
834
835
/**
836
* Returns the ASN.1 GENERAL STRING value as a Java String.
837
*
838
* @return a string corresponding to the encoded GeneralString held in
839
* this value
840
*/
841
public String getGeneralString() throws IOException {
842
return readStringInternal(tag_GeneralString, US_ASCII);
843
}
844
845
/**
846
* Returns the ASN.1 UNIVERSAL (UTF-32) STRING value as a Java String.
847
*
848
* @return a string corresponding to the encoded UniversalString held in
849
* this value
850
*/
851
public String getUniversalString() throws IOException {
852
return readStringInternal(tag_UniversalString, new UTF_32BE());
853
}
854
855
/**
856
* Reads the ASN.1 NULL value
857
*/
858
public void getNull() throws IOException {
859
if (tag != tag_Null) {
860
throw new IOException("DerValue.getNull, not NULL: " + tag);
861
}
862
if (end != start) {
863
throw new IOException("NULL should contain no data");
864
}
865
}
866
867
/**
868
* Private helper routine to extract time from the der value.
869
* @param generalized true if Generalized Time is to be read, false
870
* if UTC Time is to be read.
871
*/
872
private Date getTimeInternal(boolean generalized) throws IOException {
873
874
/*
875
* UTC time encoded as ASCII chars:
876
* YYMMDDhhmmZ
877
* YYMMDDhhmmssZ
878
* YYMMDDhhmm+hhmm
879
* YYMMDDhhmm-hhmm
880
* YYMMDDhhmmss+hhmm
881
* YYMMDDhhmmss-hhmm
882
* UTC Time is broken in storing only two digits of year.
883
* If YY < 50, we assume 20YY;
884
* if YY >= 50, we assume 19YY, as per RFC 5280.
885
*
886
* Generalized time has a four-digit year and allows any
887
* precision specified in ISO 8601. However, for our purposes,
888
* we will only allow the same format as UTC time, except that
889
* fractional seconds (millisecond precision) are supported.
890
*/
891
892
int year, month, day, hour, minute, second, millis;
893
String type;
894
int pos = start;
895
int len = end - start;
896
897
if (generalized) {
898
type = "Generalized";
899
year = 1000 * toDigit(buffer[pos++], type);
900
year += 100 * toDigit(buffer[pos++], type);
901
year += 10 * toDigit(buffer[pos++], type);
902
year += toDigit(buffer[pos++], type);
903
len -= 2; // For the two extra YY
904
} else {
905
type = "UTC";
906
year = 10 * toDigit(buffer[pos++], type);
907
year += toDigit(buffer[pos++], type);
908
909
if (year < 50) { // origin 2000
910
year += 2000;
911
} else {
912
year += 1900; // origin 1900
913
}
914
}
915
916
month = 10 * toDigit(buffer[pos++], type);
917
month += toDigit(buffer[pos++], type);
918
919
day = 10 * toDigit(buffer[pos++], type);
920
day += toDigit(buffer[pos++], type);
921
922
hour = 10 * toDigit(buffer[pos++], type);
923
hour += toDigit(buffer[pos++], type);
924
925
minute = 10 * toDigit(buffer[pos++], type);
926
minute += toDigit(buffer[pos++], type);
927
928
len -= 10; // YYMMDDhhmm
929
930
/*
931
* We allow for non-encoded seconds, even though the
932
* IETF-PKIX specification says that the seconds should
933
* always be encoded even if it is zero.
934
*/
935
936
millis = 0;
937
if (len > 2) {
938
second = 10 * toDigit(buffer[pos++], type);
939
second += toDigit(buffer[pos++], type);
940
len -= 2;
941
// handle fractional seconds (if present)
942
if (generalized && (buffer[pos] == '.' || buffer[pos] == ',')) {
943
len --;
944
if (len == 0) {
945
throw new IOException("Parse " + type +
946
" time, empty fractional part");
947
}
948
pos++;
949
int precision = 0;
950
while (buffer[pos] != 'Z' &&
951
buffer[pos] != '+' &&
952
buffer[pos] != '-') {
953
// Validate all digits in the fractional part but
954
// store millisecond precision only
955
int thisDigit = toDigit(buffer[pos], type);
956
precision++;
957
len--;
958
if (len == 0) {
959
throw new IOException("Parse " + type +
960
" time, invalid fractional part");
961
}
962
pos++;
963
switch (precision) {
964
case 1 -> millis += 100 * thisDigit;
965
case 2 -> millis += 10 * thisDigit;
966
case 3 -> millis += thisDigit;
967
}
968
}
969
if (precision == 0) {
970
throw new IOException("Parse " + type +
971
" time, empty fractional part");
972
}
973
}
974
} else
975
second = 0;
976
977
if (month == 0 || day == 0
978
|| month > 12 || day > 31
979
|| hour >= 24 || minute >= 60 || second >= 60) {
980
throw new IOException("Parse " + type + " time, invalid format");
981
}
982
983
/*
984
* Generalized time can theoretically allow any precision,
985
* but we're not supporting that.
986
*/
987
CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
988
CalendarDate date = gcal.newCalendarDate(null); // no time zone
989
date.setDate(year, month, day);
990
date.setTimeOfDay(hour, minute, second, millis);
991
long time = gcal.getTime(date);
992
993
/*
994
* Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
995
*/
996
if (! (len == 1 || len == 5)) {
997
throw new IOException("Parse " + type + " time, invalid offset");
998
}
999
1000
int hr, min;
1001
1002
switch (buffer[pos++]) {
1003
case '+':
1004
if (len != 5) {
1005
throw new IOException("Parse " + type + " time, invalid offset");
1006
}
1007
hr = 10 * toDigit(buffer[pos++], type);
1008
hr += toDigit(buffer[pos++], type);
1009
min = 10 * toDigit(buffer[pos++], type);
1010
min += toDigit(buffer[pos++], type);
1011
1012
if (hr >= 24 || min >= 60) {
1013
throw new IOException("Parse " + type + " time, +hhmm");
1014
}
1015
1016
time -= ((hr * 60) + min) * 60 * 1000;
1017
break;
1018
1019
case '-':
1020
if (len != 5) {
1021
throw new IOException("Parse " + type + " time, invalid offset");
1022
}
1023
hr = 10 * toDigit(buffer[pos++], type);
1024
hr += toDigit(buffer[pos++], type);
1025
min = 10 * toDigit(buffer[pos++], type);
1026
min += toDigit(buffer[pos++], type);
1027
1028
if (hr >= 24 || min >= 60) {
1029
throw new IOException("Parse " + type + " time, -hhmm");
1030
}
1031
1032
time += ((hr * 60) + min) * 60 * 1000;
1033
break;
1034
1035
case 'Z':
1036
if (len != 1) {
1037
throw new IOException("Parse " + type + " time, invalid format");
1038
}
1039
break;
1040
1041
default:
1042
throw new IOException("Parse " + type + " time, garbage offset");
1043
}
1044
return new Date(time);
1045
}
1046
1047
/**
1048
* Converts byte (represented as a char) to int.
1049
* @throws IOException if integer is not a valid digit in the specified
1050
* radix (10)
1051
*/
1052
private static int toDigit(byte b, String type) throws IOException {
1053
if (b < '0' || b > '9') {
1054
throw new IOException("Parse " + type + " time, invalid format");
1055
}
1056
return b - '0';
1057
}
1058
1059
/**
1060
* Returns a Date if the DerValue is UtcTime.
1061
*
1062
* @return the Date held in this DER value
1063
*/
1064
public Date getUTCTime() throws IOException {
1065
if (tag != tag_UtcTime) {
1066
throw new IOException("DerValue.getUTCTime, not a UtcTime: " + tag);
1067
}
1068
if (end - start < 11 || end - start > 17)
1069
throw new IOException("DER UTC Time length error");
1070
1071
data.pos = data.end; // Compatibility. Reach end.
1072
return getTimeInternal(false);
1073
}
1074
1075
/**
1076
* Returns a Date if the DerValue is GeneralizedTime.
1077
*
1078
* @return the Date held in this DER value
1079
*/
1080
public Date getGeneralizedTime() throws IOException {
1081
if (tag != tag_GeneralizedTime) {
1082
throw new IOException(
1083
"DerValue.getGeneralizedTime, not a GeneralizedTime: " + tag);
1084
}
1085
if (end - start < 13)
1086
throw new IOException("DER Generalized Time length error");
1087
1088
data.pos = data.end; // Compatibility. Reach end.
1089
return getTimeInternal(true);
1090
}
1091
1092
/**
1093
* Bitwise equality comparison. DER encoded values have a single
1094
* encoding, so that bitwise equality of the encoded values is an
1095
* efficient way to establish equivalence of the unencoded values.
1096
*
1097
* @param o the object being compared with this one
1098
*/
1099
@Override
1100
public boolean equals(Object o) {
1101
if (this == o) {
1102
return true;
1103
}
1104
if (!(o instanceof DerValue)) {
1105
return false;
1106
}
1107
DerValue other = (DerValue) o;
1108
if (tag != other.tag) {
1109
return false;
1110
}
1111
if (buffer == other.buffer && start == other.start && end == other.end) {
1112
return true;
1113
}
1114
return Arrays.equals(buffer, start, end, other.buffer, other.start, other.end);
1115
}
1116
1117
/**
1118
* Returns a printable representation of the value.
1119
*
1120
* @return printable representation of the value
1121
*/
1122
@Override
1123
public String toString() {
1124
return String.format("DerValue(%02x, %s, %d, %d)",
1125
0xff & tag, buffer, start, end);
1126
}
1127
1128
/**
1129
* Returns a DER-encoded value, such that if it's passed to the
1130
* DerValue constructor, a value equivalent to "this" is returned.
1131
*
1132
* @return DER-encoded value, including tag and length.
1133
*/
1134
public byte[] toByteArray() throws IOException {
1135
data.pos = data.start; // Compatibility. At head.
1136
// Minimize content duplication by writing out tag and length only
1137
DerOutputStream out = new DerOutputStream();
1138
out.write(tag);
1139
out.putLength(end - start);
1140
int headLen = out.size();
1141
byte[] result = Arrays.copyOf(out.buf(), end - start + headLen);
1142
System.arraycopy(buffer, start, result, headLen, end - start);
1143
return result;
1144
}
1145
1146
/**
1147
* For "set" and "sequence" types, this function may be used
1148
* to return a DER stream of the members of the set or sequence.
1149
* This operation is not supported for primitive types such as
1150
* integers or bit strings.
1151
*/
1152
public DerInputStream toDerInputStream() throws IOException {
1153
if (tag == tag_Sequence || tag == tag_Set)
1154
return data;
1155
throw new IOException("toDerInputStream rejects tag type " + tag);
1156
}
1157
1158
/**
1159
* Get the length of the encoded value.
1160
*/
1161
public int length() {
1162
return end - start;
1163
}
1164
1165
/**
1166
* Determine if a character is one of the permissible characters for
1167
* PrintableString:
1168
* A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,
1169
* plus sign, comma, hyphen, period, slash, colon, equals sign,
1170
* and question mark.
1171
*
1172
* Characters that are *not* allowed in PrintableString include
1173
* exclamation point, quotation mark, number sign, dollar sign,
1174
* percent sign, ampersand, asterisk, semicolon, less than sign,
1175
* greater than sign, at sign, left and right square brackets,
1176
* backslash, circumflex (94), underscore, back quote (96),
1177
* left and right curly brackets, vertical line, tilde,
1178
* and the control codes (0-31 and 127).
1179
*
1180
* This list is based on X.680 (the ASN.1 spec).
1181
*/
1182
public static boolean isPrintableStringChar(char ch) {
1183
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
1184
(ch >= '0' && ch <= '9')) {
1185
return true;
1186
} else {
1187
switch (ch) {
1188
case ' ': /* space */
1189
case '\'': /* apostrophe */
1190
case '(': /* left paren */
1191
case ')': /* right paren */
1192
case '+': /* plus */
1193
case ',': /* comma */
1194
case '-': /* hyphen */
1195
case '.': /* period */
1196
case '/': /* slash */
1197
case ':': /* colon */
1198
case '=': /* equals */
1199
case '?': /* question mark */
1200
return true;
1201
default:
1202
return false;
1203
}
1204
}
1205
}
1206
1207
/**
1208
* Create the tag of the attribute.
1209
*
1210
* @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,
1211
* APPLICATION or PRIVATE
1212
* @param form if true, the value is constructed, otherwise it
1213
* is primitive.
1214
* @param val the tag value
1215
*/
1216
public static byte createTag(byte tagClass, boolean form, byte val) {
1217
if (val < 0 || val > 30) {
1218
throw new IllegalArgumentException("Tag number over 30 is not supported");
1219
}
1220
byte tag = (byte)(tagClass | val);
1221
if (form) {
1222
tag |= (byte)0x20;
1223
}
1224
return (tag);
1225
}
1226
1227
/**
1228
* Set the tag of the attribute. Commonly used to reset the
1229
* tag value used for IMPLICIT encodings.
1230
*
1231
* This method should be avoided, consider using withTag() instead.
1232
*
1233
* @param tag the tag value
1234
*/
1235
public void resetTag(byte tag) {
1236
this.tag = tag;
1237
}
1238
1239
/**
1240
* Returns a new DerValue with a different tag. This method is used
1241
* to convert a DerValue decoded from an IMPLICIT encoding to its real
1242
* tag. The content is not checked against the tag in this method.
1243
*
1244
* @param newTag the new tag
1245
* @return a new DerValue
1246
*/
1247
public DerValue withTag(byte newTag) {
1248
return new DerValue(newTag, buffer, start, end, allowBER);
1249
}
1250
1251
/**
1252
* Returns a hashcode for this DerValue.
1253
*
1254
* @return a hashcode for this DerValue.
1255
*/
1256
@Override
1257
public int hashCode() {
1258
int result = tag;
1259
for (int i = start; i < end; i++) {
1260
result = 31 * result + buffer[i];
1261
}
1262
return result;
1263
}
1264
1265
/**
1266
* Reads the sub-values in a constructed DerValue.
1267
*
1268
* @param expectedTag the expected tag, or zero if we don't check.
1269
* This is useful when this DerValue is IMPLICIT.
1270
* @param startLen estimated number of sub-values
1271
* @return the sub-values in an array
1272
*/
1273
DerValue[] subs(byte expectedTag, int startLen) throws IOException {
1274
if (expectedTag != 0 && expectedTag != tag) {
1275
throw new IOException("Not the correct tag");
1276
}
1277
List<DerValue> result = new ArrayList<>(startLen);
1278
DerInputStream dis = data();
1279
while (dis.available() > 0) {
1280
result.add(dis.getDerValue());
1281
}
1282
return result.toArray(new DerValue[0]);
1283
}
1284
1285
public void clear() {
1286
Arrays.fill(buffer, start, end, (byte)0);
1287
}
1288
}
1289
1290