Path: blob/master/src/java.base/share/classes/sun/security/util/DerValue.java
67771 views
/*1* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import sun.nio.cs.UTF_32BE;28import sun.util.calendar.CalendarDate;29import sun.util.calendar.CalendarSystem;3031import java.io.*;32import java.math.BigInteger;33import java.nio.charset.Charset;34import java.util.*;3536import static java.nio.charset.StandardCharsets.*;3738/**39* Represents a single DER-encoded value. DER encoding rules are a subset40* of the "Basic" Encoding Rules (BER), but they only support a single way41* ("Definite" encoding) to encode any given value.42*43* <P>All DER-encoded data are triples <em>{type, length, data}</em>. This44* class represents such tagged values as they have been read (or constructed),45* and provides structured access to the encoded data.46*47* <P>At this time, this class supports only a subset of the types of DER48* data encodings which are defined. That subset is sufficient for parsing49* most X.509 certificates, and working with selected additional formats50* (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data).51*52* A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.353* and RFC 5280, section 8, we assume that this kind of string will contain54* ISO-8859-1 characters only.55*56*57* @author David Brownell58* @author Amit Kapoor59* @author Hemma Prafullchandra60*/61public class DerValue {6263/** The tag class types */64public static final byte TAG_UNIVERSAL = (byte)0x000;65public static final byte TAG_APPLICATION = (byte)0x040;66public static final byte TAG_CONTEXT = (byte)0x080;67public static final byte TAG_PRIVATE = (byte)0x0c0;6869/*70* The type starts at the first byte of the encoding, and71* is one of these tag_* values. That may be all the type72* data that is needed.73*/7475/*76* These tags are the "universal" tags ... they mean the same77* in all contexts. (Mask with 0x1f -- five bits.)78*/7980/** Tag value indicating an ASN.1 "BOOLEAN" value. */81public static final byte tag_Boolean = 0x01;8283/** Tag value indicating an ASN.1 "INTEGER" value. */84public static final byte tag_Integer = 0x02;8586/** Tag value indicating an ASN.1 "BIT STRING" value. */87public static final byte tag_BitString = 0x03;8889/** Tag value indicating an ASN.1 "OCTET STRING" value. */90public static final byte tag_OctetString = 0x04;9192/** Tag value indicating an ASN.1 "NULL" value. */93public static final byte tag_Null = 0x05;9495/** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */96public static final byte tag_ObjectId = 0x06;9798/** Tag value including an ASN.1 "ENUMERATED" value */99public static final byte tag_Enumerated = 0x0A;100101/** Tag value indicating an ASN.1 "UTF8String" value. */102public static final byte tag_UTF8String = 0x0C;103104/** Tag value including a "printable" string */105public static final byte tag_PrintableString = 0x13;106107/** Tag value including a "teletype" string */108public static final byte tag_T61String = 0x14;109110/** Tag value including an ASCII string */111public static final byte tag_IA5String = 0x16;112113/** Tag value indicating an ASN.1 "UTCTime" value. */114public static final byte tag_UtcTime = 0x17;115116/** Tag value indicating an ASN.1 "GeneralizedTime" value. */117public static final byte tag_GeneralizedTime = 0x18;118119/** Tag value indicating an ASN.1 "GenerallString" value. */120public static final byte tag_GeneralString = 0x1B;121122/** Tag value indicating an ASN.1 "UniversalString" value. */123public static final byte tag_UniversalString = 0x1C;124125/** Tag value indicating an ASN.1 "BMPString" value. */126public static final byte tag_BMPString = 0x1E;127128// CONSTRUCTED seq/set129130/**131* Tag value indicating an ASN.1132* "SEQUENCE" (zero to N elements, order is significant).133*/134public static final byte tag_Sequence = 0x30;135136/**137* Tag value indicating an ASN.1138* "SEQUENCE OF" (one to N elements, order is significant).139*/140public static final byte tag_SequenceOf = 0x30;141142/**143* Tag value indicating an ASN.1144* "SET" (zero to N members, order does not matter).145*/146public static final byte tag_Set = 0x31;147148/**149* Tag value indicating an ASN.1150* "SET OF" (one to N members, order does not matter).151*/152public static final byte tag_SetOf = 0x31;153154// This class is mostly immutable except that:155//156// 1. resetTag() modifies the tag157// 2. the data field is mutable158//159// For compatibility, data, getData() and resetTag() are preserved.160// A modern caller should call withTag() or data() instead.161//162// Also, some constructors have not cloned buffer, so the data could163// be modified externally.164165public /*final*/ byte tag;166final byte[] buffer;167private final int start;168final int end;169private final boolean allowBER;170171// Unsafe. Legacy. Never null.172public final DerInputStream data;173174/*175* These values are the high order bits for the other kinds of tags.176*/177178/**179* Returns true if the tag class is UNIVERSAL.180*/181public boolean isUniversal() { return ((tag & 0x0c0) == 0x000); }182183/**184* Returns true if the tag class is APPLICATION.185*/186public boolean isApplication() { return ((tag & 0x0c0) == 0x040); }187188/**189* Returns true iff the CONTEXT SPECIFIC bit is set in the type tag.190* This is associated with the ASN.1 "DEFINED BY" syntax.191*/192public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); }193194/**195* Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag.196*/197public boolean isContextSpecific(byte cntxtTag) {198if (!isContextSpecific()) {199return false;200}201return ((tag & 0x01f) == cntxtTag);202}203204boolean isPrivate() { return ((tag & 0x0c0) == 0x0c0); }205206/** Returns true iff the CONSTRUCTED bit is set in the type tag. */207public boolean isConstructed() { return ((tag & 0x020) == 0x020); }208209/**210* Returns true iff the CONSTRUCTED TAG matches the passed tag.211*/212public boolean isConstructed(byte constructedTag) {213if (!isConstructed()) {214return false;215}216return ((tag & 0x01f) == constructedTag);217}218219/**220* Creates a new DerValue by specifying all its fields.221*/222DerValue(byte tag, byte[] buffer, int start, int end, boolean allowBER) {223if ((tag & 0x1f) == 0x1f) {224throw new IllegalArgumentException("Tag number over 30 is not supported");225}226this.tag = tag;227this.buffer = buffer;228this.start = start;229this.end = end;230this.allowBER = allowBER;231this.data = data();232}233234/**235* Creates a PrintableString or UTF8string DER value from a string.236*/237public DerValue(String value) {238this(isPrintableString(value) ? tag_PrintableString : tag_UTF8String,239value);240}241242private static boolean isPrintableString(String value) {243for (int i = 0; i < value.length(); i++) {244if (!isPrintableStringChar(value.charAt(i))) {245return false;246}247}248return true;249}250251/**252* Creates a string type DER value from a String object253* @param stringTag the tag for the DER value to create254* @param value the String object to use for the DER value255*/256public DerValue(byte stringTag, String value) {257this(stringTag, string2bytes(stringTag, value), false);258}259260private static byte[] string2bytes(byte stringTag, String value) {261Charset charset = switch (stringTag) {262case tag_PrintableString, tag_IA5String, tag_GeneralString -> US_ASCII;263case tag_T61String -> ISO_8859_1;264case tag_BMPString -> UTF_16BE;265case tag_UTF8String -> UTF_8;266case tag_UniversalString -> Charset.forName("UTF_32BE");267default -> throw new IllegalArgumentException("Unsupported DER string type");268};269return value.getBytes(charset);270}271272DerValue(byte tag, byte[] buffer, boolean allowBER) {273this(tag, buffer, 0, buffer.length, allowBER);274}275276/**277* Creates a DerValue from a tag and some DER-encoded data.278*279* This is a public constructor.280*281* @param tag the DER type tag282* @param buffer the DER-encoded data283*/284public DerValue(byte tag, byte[] buffer) {285this(tag, buffer.clone(), true);286}287288/**289* Wraps an DerOutputStream. All bytes currently written290* into the stream will become the content of the newly291* created DerValue.292*293* Attention: do not reset the DerOutputStream after this call.294* No array copying is made.295*296* @param tag the tag297* @param out the DerOutputStream298* @returns a new DerValue using out as its content299*/300public static DerValue wrap(byte tag, DerOutputStream out) {301return new DerValue(tag, out.buf(), 0, out.size(), false);302}303304/**305* Wraps a byte array as a single DerValue.306*307* Attention: no cloning is made.308*309* @param buf the byte array containing the DER-encoded datum310* @returns a new DerValue311*/312public static DerValue wrap(byte[] buf)313throws IOException {314return wrap(buf, 0, buf.length);315}316317/**318* Wraps a byte array as a single DerValue.319*320* Attention: no cloning is made.321*322* @param buf the byte array containing the DER-encoded datum323* @param offset where the encoded datum starts inside {@code buf}324* @param len length of bytes to parse inside {@code buf}325* @returns a new DerValue326*/327public static DerValue wrap(byte[] buf, int offset, int len)328throws IOException {329return new DerValue(buf, offset, len, true, false);330}331332/**333* Parse an ASN.1/BER encoded datum. The entire encoding must hold exactly334* one datum, including its tag and length.335*336* This is a public constructor.337*/338public DerValue(byte[] encoding) throws IOException {339this(encoding.clone(), 0, encoding.length, true, false);340}341342/**343* Parse an ASN.1 encoded datum from a byte array.344*345* @param buf the byte array containing the DER-encoded datum346* @param offset where the encoded datum starts inside {@code buf}347* @param len length of bytes to parse inside {@code buf}348* @param allowBER whether BER is allowed349* @param allowMore whether extra bytes are allowed after the encoded datum.350* If true, {@code len} can be bigger than the length of351* the encoded datum.352*353* @throws IOException if it's an invalid encoding or there are extra bytes354* after the encoded datum and {@code allowMore} is false.355*/356DerValue(byte[] buf, int offset, int len, boolean allowBER, boolean allowMore)357throws IOException {358359if (len < 2) {360throw new IOException("Too short");361}362int pos = offset;363tag = buf[pos++];364if ((tag & 0x1f) == 0x1f) {365throw new IOException("Tag number over 30 at " + offset + " is not supported");366}367int lenByte = buf[pos++];368369int length;370if (lenByte == (byte) 0x80) { // indefinite length371if (!allowBER) {372throw new IOException("Indefinite length encoding " +373"not supported with DER");374}375if (!isConstructed()) {376throw new IOException("Indefinite length encoding " +377"not supported with non-constructed data");378}379380// Reconstruct data source381buf = DerIndefLenConverter.convertStream(382new ByteArrayInputStream(buf, pos, len - (pos - offset)), tag);383offset = 0;384len = buf.length;385pos = 2;386387if (tag != buf[0]) {388throw new IOException("Indefinite length encoding not supported");389}390lenByte = buf[1];391if (lenByte == (byte) 0x80) {392throw new IOException("Indefinite len conversion failed");393}394}395396if ((lenByte & 0x080) == 0x00) { // short form, 1 byte datum397length = lenByte;398} else { // long form399lenByte &= 0x07f;400if (lenByte > 4) {401throw new IOException("Invalid lenByte");402}403if (len < 2 + lenByte) {404throw new IOException("Not enough length bytes");405}406length = 0x0ff & buf[pos++];407lenByte--;408if (length == 0 && !allowBER) {409// DER requires length value be encoded in minimum number of bytes410throw new IOException("Redundant length bytes found");411}412while (lenByte-- > 0) {413length <<= 8;414length += 0x0ff & buf[pos++];415}416if (length < 0) {417throw new IOException("Invalid length bytes");418} else if (length <= 127 && !allowBER) {419throw new IOException("Should use short form for length");420}421}422// pos is now at the beginning of the content423if (len - length < pos - offset) {424throw new EOFException("not enough content");425}426if (len - length > pos - offset && !allowMore) {427throw new IOException("extra data at the end");428}429this.buffer = buf;430this.start = pos;431this.end = pos + length;432this.allowBER = allowBER;433this.data = data();434}435436// Get an ASN1/DER encoded datum from an input stream w/ additional437// arg to control whether DER checks are enforced.438DerValue(InputStream in, boolean allowBER) throws IOException {439this.tag = (byte)in.read();440if ((tag & 0x1f) == 0x1f) {441throw new IOException("Tag number over 30 is not supported");442}443int length = DerInputStream.getLength(in);444if (length == -1) { // indefinite length encoding found445if (!allowBER) {446throw new IOException("Indefinite length encoding " +447"not supported with DER");448}449if (!isConstructed()) {450throw new IOException("Indefinite length encoding " +451"not supported with non-constructed data");452}453this.buffer = DerIndefLenConverter.convertStream(in, tag);454ByteArrayInputStream bin = new ByteArrayInputStream(this.buffer);455if (tag != bin.read()) {456throw new IOException457("Indefinite length encoding not supported");458}459length = DerInputStream.getDefiniteLength(bin);460this.start = this.buffer.length - bin.available();461this.end = this.start + length;462// position of in is undetermined. Precisely, it might be n-bytes463// after DerValue, and these n bytes are at the end of this.buffer464// after this.end.465} else {466this.buffer = IOUtils.readExactlyNBytes(in, length);467this.start = 0;468this.end = length;469// position of in is right after the DerValue470}471this.allowBER = allowBER;472this.data = data();473}474475/**476* Get an ASN1/DER encoded datum from an input stream. The477* stream may have additional data following the encoded datum.478* In case of indefinite length encoded datum, the input stream479* must hold only one datum, i.e. all bytes in the stream might480* be consumed. Otherwise, only one DerValue will be consumed.481*482* @param in the input stream holding a single DER datum,483* which may be followed by additional data484*/485public DerValue(InputStream in) throws IOException {486this(in, true);487}488489/**490* Encode an ASN1/DER encoded datum onto a DER output stream.491*/492public void encode(DerOutputStream out) throws IOException {493out.write(tag);494out.putLength(end - start);495out.write(buffer, start, end - start);496data.pos = data.end; // Compatibility. Reach end.497}498499/**500* Returns a new DerInputStream pointing at the start of this501* DerValue's content.502*503* @return the new DerInputStream value504*/505public final DerInputStream data() {506return new DerInputStream(buffer, start, end - start, allowBER);507}508509/**510* Returns the data field inside this class directly.511*512* Both this method and the {@link #data} field should be avoided.513* Consider using {@link #data()} instead.514*/515public final DerInputStream getData() {516return data;517}518519public final byte getTag() {520return tag;521}522523/**524* Returns an ASN.1 BOOLEAN525*526* @return the boolean held in this DER value527*/528public boolean getBoolean() throws IOException {529if (tag != tag_Boolean) {530throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag);531}532if (end - start != 1) {533throw new IOException("DerValue.getBoolean, invalid length "534+ (end - start));535}536data.pos = data.end; // Compatibility. Reach end.537return buffer[start] != 0;538}539540/**541* Returns an ASN.1 OBJECT IDENTIFIER.542*543* @return the OID held in this DER value544*/545public ObjectIdentifier getOID() throws IOException {546if (tag != tag_ObjectId) {547throw new IOException("DerValue.getOID, not an OID " + tag);548}549data.pos = data.end; // Compatibility. Reach end.550return new ObjectIdentifier(Arrays.copyOfRange(buffer, start, end));551}552553/**554* Returns an ASN.1 OCTET STRING555*556* @return the octet string held in this DER value557*/558public byte[] getOctetString() throws IOException {559560if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {561throw new IOException(562"DerValue.getOctetString, not an Octet String: " + tag);563}564// Note: do not attempt to call buffer.read(bytes) at all. There's a565// known bug that it returns -1 instead of 0.566if (end - start == 0) {567return new byte[0];568}569570data.pos = data.end; // Compatibility. Reach end.571if (!isConstructed()) {572return Arrays.copyOfRange(buffer, start, end);573} else {574ByteArrayOutputStream bout = new ByteArrayOutputStream();575DerInputStream dis = data();576while (dis.available() > 0) {577bout.write(dis.getDerValue().getOctetString());578}579return bout.toByteArray();580}581}582583/**584* Returns an ASN.1 INTEGER value as an integer.585*586* @return the integer held in this DER value.587*/588public int getInteger() throws IOException {589return getIntegerInternal(tag_Integer);590}591592private int getIntegerInternal(byte expectedTag) throws IOException {593BigInteger result = getBigIntegerInternal(expectedTag, false);594if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {595throw new IOException("Integer below minimum valid value");596}597if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {598throw new IOException("Integer exceeds maximum valid value");599}600return result.intValue();601}602603/**604* Returns an ASN.1 INTEGER value as a BigInteger.605*606* @return the integer held in this DER value as a BigInteger.607*/608public BigInteger getBigInteger() throws IOException {609return getBigIntegerInternal(tag_Integer, false);610}611612/**613* Returns an ASN.1 INTEGER value as a positive BigInteger.614* This is just to deal with implementations that incorrectly encode615* some values as negative.616*617* @return the integer held in this DER value as a BigInteger.618*/619public BigInteger getPositiveBigInteger() throws IOException {620return getBigIntegerInternal(tag_Integer, true);621}622623/**624* Returns a BigInteger value625*626* @param makePositive whether to always return a positive value,627* irrespective of actual encoding628* @return the integer as a BigInteger.629*/630private BigInteger getBigIntegerInternal(byte expectedTag, boolean makePositive) throws IOException {631if (tag != expectedTag) {632throw new IOException("DerValue.getBigIntegerInternal, not expected " + tag);633}634if (end == start) {635throw new IOException("Invalid encoding: zero length Int value");636}637data.pos = data.end; // Compatibility. Reach end.638if (!allowBER && (end - start >= 2 && (buffer[start] == 0) && (buffer[start + 1] >= 0))) {639throw new IOException("Invalid encoding: redundant leading 0s");640}641return makePositive642? new BigInteger(1, buffer, start, end - start)643: new BigInteger(buffer, start, end - start);644}645646/**647* Returns an ASN.1 ENUMERATED value.648*649* @return the integer held in this DER value.650*/651public int getEnumerated() throws IOException {652return getIntegerInternal(tag_Enumerated);653}654655/**656* Returns an ASN.1 BIT STRING value. The bit string must be byte-aligned.657*658* @return the bit string held in this value659*/660public byte[] getBitString() throws IOException {661return getBitString(false);662}663664/**665* Returns an ASN.1 BIT STRING value that need not be byte-aligned.666*667* @return a BitArray representing the bit string held in this value668*/669public BitArray getUnalignedBitString() throws IOException {670return getUnalignedBitString(false);671}672673/**674* Returns the name component as a Java string, regardless of its675* encoding restrictions (ASCII, T61, Printable, IA5, BMP, UTF8).676*/677// TBD: Need encoder for UniversalString before it can be handled.678public String getAsString() throws IOException {679return switch (tag) {680case tag_UTF8String -> getUTF8String();681case tag_PrintableString -> getPrintableString();682case tag_T61String -> getT61String();683case tag_IA5String -> getIA5String();684case tag_UniversalString -> getUniversalString();685case tag_BMPString -> getBMPString();686case tag_GeneralString -> getGeneralString();687default -> null;688};689}690691// check the number of pad bits, validate the pad bits in the bytes692// if enforcing DER (i.e. allowBER == false), and return the number of693// bits of the resulting BitString694private static int checkPaddedBits(int numOfPadBits, byte[] data,695int start, int end, boolean allowBER) throws IOException {696// number of pad bits should be from 0(min) to 7(max).697if ((numOfPadBits < 0) || (numOfPadBits > 7)) {698throw new IOException("Invalid number of padding bits");699}700int lenInBits = ((end - start) << 3) - numOfPadBits;701if (lenInBits < 0) {702throw new IOException("Not enough bytes in BitString");703}704705// padding bits should be all zeros for DER706if (!allowBER && numOfPadBits != 0 &&707(data[end - 1] & (0xff >>> (8 - numOfPadBits))) != 0) {708throw new IOException("Invalid value of padding bits");709}710return lenInBits;711}712713/**714* Returns an ASN.1 BIT STRING value, with the tag assumed implicit715* based on the parameter. The bit string must be byte-aligned.716*717* @param tagImplicit if true, the tag is assumed implicit.718* @return the bit string held in this value719*/720public byte[] getBitString(boolean tagImplicit) throws IOException {721if (!tagImplicit) {722if (tag != tag_BitString) {723throw new IOException("DerValue.getBitString, not a bit string "724+ tag);725}726}727if (end == start) {728throw new IOException("Invalid encoding: zero length bit string");729730}731data.pos = data.end; // Compatibility. Reach end.732733int numOfPadBits = buffer[start];734checkPaddedBits(numOfPadBits, buffer, start + 1, end, allowBER);735byte[] retval = Arrays.copyOfRange(buffer, start + 1, end);736if (allowBER && numOfPadBits != 0) {737// fix the potential non-zero padding bits738retval[retval.length - 1] &= (0xff << numOfPadBits);739}740return retval;741}742743/**744* Returns an ASN.1 BIT STRING value, with the tag assumed implicit745* based on the parameter. The bit string need not be byte-aligned.746*747* @param tagImplicit if true, the tag is assumed implicit.748* @return the bit string held in this value749*/750public BitArray getUnalignedBitString(boolean tagImplicit)751throws IOException {752if (!tagImplicit) {753if (tag != tag_BitString) {754throw new IOException("DerValue.getBitString, not a bit string "755+ tag);756}757}758if (end == start) {759throw new IOException("Invalid encoding: zero length bit string");760}761data.pos = data.end; // Compatibility. Reach end.762763int numOfPadBits = buffer[start];764int len = checkPaddedBits(numOfPadBits, buffer, start + 1, end,765allowBER);766return new BitArray(len, buffer, start + 1);767}768769/**770* Helper routine to return all the bytes contained in the771* DerInputStream associated with this object.772*/773public byte[] getDataBytes() throws IOException {774data.pos = data.end; // Compatibility. Reach end.775return Arrays.copyOfRange(buffer, start, end);776}777778private String readStringInternal(byte expectedTag, Charset cs) throws IOException {779if (tag != expectedTag) {780throw new IOException("Incorrect string type " + tag + " is not " + expectedTag);781}782data.pos = data.end; // Compatibility. Reach end.783return new String(buffer, start, end - start, cs);784}785786/**787* Returns an ASN.1 STRING value788*789* @return the printable string held in this value790*/791public String getPrintableString() throws IOException {792return readStringInternal(tag_PrintableString, US_ASCII);793}794795/**796* Returns an ASN.1 T61 (Teletype) STRING value797*798* @return the teletype string held in this value799*/800public String getT61String() throws IOException {801return readStringInternal(tag_T61String, ISO_8859_1);802}803804/**805* Returns an ASN.1 IA5 (ASCII) STRING value806*807* @return the ASCII string held in this value808*/809public String getIA5String() throws IOException {810return readStringInternal(tag_IA5String, US_ASCII);811}812813/**814* Returns the ASN.1 BMP (Unicode) STRING value as a Java string.815*816* @return a string corresponding to the encoded BMPString held in817* this value818*/819public String getBMPString() throws IOException {820// BMPString is the same as Unicode in big endian, unmarked format.821return readStringInternal(tag_BMPString, UTF_16BE);822}823824/**825* Returns the ASN.1 UTF-8 STRING value as a Java String.826*827* @return a string corresponding to the encoded UTF8String held in828* this value829*/830public String getUTF8String() throws IOException {831return readStringInternal(tag_UTF8String, UTF_8);832}833834/**835* Returns the ASN.1 GENERAL STRING value as a Java String.836*837* @return a string corresponding to the encoded GeneralString held in838* this value839*/840public String getGeneralString() throws IOException {841return readStringInternal(tag_GeneralString, US_ASCII);842}843844/**845* Returns the ASN.1 UNIVERSAL (UTF-32) STRING value as a Java String.846*847* @return a string corresponding to the encoded UniversalString held in848* this value849*/850public String getUniversalString() throws IOException {851return readStringInternal(tag_UniversalString, new UTF_32BE());852}853854/**855* Reads the ASN.1 NULL value856*/857public void getNull() throws IOException {858if (tag != tag_Null) {859throw new IOException("DerValue.getNull, not NULL: " + tag);860}861if (end != start) {862throw new IOException("NULL should contain no data");863}864}865866/**867* Private helper routine to extract time from the der value.868* @param generalized true if Generalized Time is to be read, false869* if UTC Time is to be read.870*/871private Date getTimeInternal(boolean generalized) throws IOException {872873/*874* UTC time encoded as ASCII chars:875* YYMMDDhhmmZ876* YYMMDDhhmmssZ877* YYMMDDhhmm+hhmm878* YYMMDDhhmm-hhmm879* YYMMDDhhmmss+hhmm880* YYMMDDhhmmss-hhmm881* UTC Time is broken in storing only two digits of year.882* If YY < 50, we assume 20YY;883* if YY >= 50, we assume 19YY, as per RFC 5280.884*885* Generalized time has a four-digit year and allows any886* precision specified in ISO 8601. However, for our purposes,887* we will only allow the same format as UTC time, except that888* fractional seconds (millisecond precision) are supported.889*/890891int year, month, day, hour, minute, second, millis;892String type;893int pos = start;894int len = end - start;895896if (generalized) {897type = "Generalized";898year = 1000 * toDigit(buffer[pos++], type);899year += 100 * toDigit(buffer[pos++], type);900year += 10 * toDigit(buffer[pos++], type);901year += toDigit(buffer[pos++], type);902len -= 2; // For the two extra YY903} else {904type = "UTC";905year = 10 * toDigit(buffer[pos++], type);906year += toDigit(buffer[pos++], type);907908if (year < 50) { // origin 2000909year += 2000;910} else {911year += 1900; // origin 1900912}913}914915month = 10 * toDigit(buffer[pos++], type);916month += toDigit(buffer[pos++], type);917918day = 10 * toDigit(buffer[pos++], type);919day += toDigit(buffer[pos++], type);920921hour = 10 * toDigit(buffer[pos++], type);922hour += toDigit(buffer[pos++], type);923924minute = 10 * toDigit(buffer[pos++], type);925minute += toDigit(buffer[pos++], type);926927len -= 10; // YYMMDDhhmm928929/*930* We allow for non-encoded seconds, even though the931* IETF-PKIX specification says that the seconds should932* always be encoded even if it is zero.933*/934935millis = 0;936if (len > 2) {937second = 10 * toDigit(buffer[pos++], type);938second += toDigit(buffer[pos++], type);939len -= 2;940// handle fractional seconds (if present)941if (generalized && (buffer[pos] == '.' || buffer[pos] == ',')) {942len --;943if (len == 0) {944throw new IOException("Parse " + type +945" time, empty fractional part");946}947pos++;948int precision = 0;949while (buffer[pos] != 'Z' &&950buffer[pos] != '+' &&951buffer[pos] != '-') {952// Validate all digits in the fractional part but953// store millisecond precision only954int thisDigit = toDigit(buffer[pos], type);955precision++;956len--;957if (len == 0) {958throw new IOException("Parse " + type +959" time, invalid fractional part");960}961pos++;962switch (precision) {963case 1 -> millis += 100 * thisDigit;964case 2 -> millis += 10 * thisDigit;965case 3 -> millis += thisDigit;966}967}968if (precision == 0) {969throw new IOException("Parse " + type +970" time, empty fractional part");971}972}973} else974second = 0;975976if (month == 0 || day == 0977|| month > 12 || day > 31978|| hour >= 24 || minute >= 60 || second >= 60) {979throw new IOException("Parse " + type + " time, invalid format");980}981982/*983* Generalized time can theoretically allow any precision,984* but we're not supporting that.985*/986CalendarSystem gcal = CalendarSystem.getGregorianCalendar();987CalendarDate date = gcal.newCalendarDate(null); // no time zone988date.setDate(year, month, day);989date.setTimeOfDay(hour, minute, second, millis);990long time = gcal.getTime(date);991992/*993* Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm994*/995if (! (len == 1 || len == 5)) {996throw new IOException("Parse " + type + " time, invalid offset");997}998999int hr, min;10001001switch (buffer[pos++]) {1002case '+':1003if (len != 5) {1004throw new IOException("Parse " + type + " time, invalid offset");1005}1006hr = 10 * toDigit(buffer[pos++], type);1007hr += toDigit(buffer[pos++], type);1008min = 10 * toDigit(buffer[pos++], type);1009min += toDigit(buffer[pos++], type);10101011if (hr >= 24 || min >= 60) {1012throw new IOException("Parse " + type + " time, +hhmm");1013}10141015time -= ((hr * 60) + min) * 60 * 1000;1016break;10171018case '-':1019if (len != 5) {1020throw new IOException("Parse " + type + " time, invalid offset");1021}1022hr = 10 * toDigit(buffer[pos++], type);1023hr += toDigit(buffer[pos++], type);1024min = 10 * toDigit(buffer[pos++], type);1025min += toDigit(buffer[pos++], type);10261027if (hr >= 24 || min >= 60) {1028throw new IOException("Parse " + type + " time, -hhmm");1029}10301031time += ((hr * 60) + min) * 60 * 1000;1032break;10331034case 'Z':1035if (len != 1) {1036throw new IOException("Parse " + type + " time, invalid format");1037}1038break;10391040default:1041throw new IOException("Parse " + type + " time, garbage offset");1042}1043return new Date(time);1044}10451046/**1047* Converts byte (represented as a char) to int.1048* @throws IOException if integer is not a valid digit in the specified1049* radix (10)1050*/1051private static int toDigit(byte b, String type) throws IOException {1052if (b < '0' || b > '9') {1053throw new IOException("Parse " + type + " time, invalid format");1054}1055return b - '0';1056}10571058/**1059* Returns a Date if the DerValue is UtcTime.1060*1061* @return the Date held in this DER value1062*/1063public Date getUTCTime() throws IOException {1064if (tag != tag_UtcTime) {1065throw new IOException("DerValue.getUTCTime, not a UtcTime: " + tag);1066}1067if (end - start < 11 || end - start > 17)1068throw new IOException("DER UTC Time length error");10691070data.pos = data.end; // Compatibility. Reach end.1071return getTimeInternal(false);1072}10731074/**1075* Returns a Date if the DerValue is GeneralizedTime.1076*1077* @return the Date held in this DER value1078*/1079public Date getGeneralizedTime() throws IOException {1080if (tag != tag_GeneralizedTime) {1081throw new IOException(1082"DerValue.getGeneralizedTime, not a GeneralizedTime: " + tag);1083}1084if (end - start < 13)1085throw new IOException("DER Generalized Time length error");10861087data.pos = data.end; // Compatibility. Reach end.1088return getTimeInternal(true);1089}10901091/**1092* Bitwise equality comparison. DER encoded values have a single1093* encoding, so that bitwise equality of the encoded values is an1094* efficient way to establish equivalence of the unencoded values.1095*1096* @param o the object being compared with this one1097*/1098@Override1099public boolean equals(Object o) {1100if (this == o) {1101return true;1102}1103if (!(o instanceof DerValue)) {1104return false;1105}1106DerValue other = (DerValue) o;1107if (tag != other.tag) {1108return false;1109}1110if (buffer == other.buffer && start == other.start && end == other.end) {1111return true;1112}1113return Arrays.equals(buffer, start, end, other.buffer, other.start, other.end);1114}11151116/**1117* Returns a printable representation of the value.1118*1119* @return printable representation of the value1120*/1121@Override1122public String toString() {1123return String.format("DerValue(%02x, %s, %d, %d)",11240xff & tag, buffer, start, end);1125}11261127/**1128* Returns a DER-encoded value, such that if it's passed to the1129* DerValue constructor, a value equivalent to "this" is returned.1130*1131* @return DER-encoded value, including tag and length.1132*/1133public byte[] toByteArray() throws IOException {1134data.pos = data.start; // Compatibility. At head.1135// Minimize content duplication by writing out tag and length only1136DerOutputStream out = new DerOutputStream();1137out.write(tag);1138out.putLength(end - start);1139int headLen = out.size();1140byte[] result = Arrays.copyOf(out.buf(), end - start + headLen);1141System.arraycopy(buffer, start, result, headLen, end - start);1142return result;1143}11441145/**1146* For "set" and "sequence" types, this function may be used1147* to return a DER stream of the members of the set or sequence.1148* This operation is not supported for primitive types such as1149* integers or bit strings.1150*/1151public DerInputStream toDerInputStream() throws IOException {1152if (tag == tag_Sequence || tag == tag_Set)1153return data;1154throw new IOException("toDerInputStream rejects tag type " + tag);1155}11561157/**1158* Get the length of the encoded value.1159*/1160public int length() {1161return end - start;1162}11631164/**1165* Determine if a character is one of the permissible characters for1166* PrintableString:1167* A-Z, a-z, 0-9, space, apostrophe (39), left and right parentheses,1168* plus sign, comma, hyphen, period, slash, colon, equals sign,1169* and question mark.1170*1171* Characters that are *not* allowed in PrintableString include1172* exclamation point, quotation mark, number sign, dollar sign,1173* percent sign, ampersand, asterisk, semicolon, less than sign,1174* greater than sign, at sign, left and right square brackets,1175* backslash, circumflex (94), underscore, back quote (96),1176* left and right curly brackets, vertical line, tilde,1177* and the control codes (0-31 and 127).1178*1179* This list is based on X.680 (the ASN.1 spec).1180*/1181public static boolean isPrintableStringChar(char ch) {1182if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||1183(ch >= '0' && ch <= '9')) {1184return true;1185} else {1186switch (ch) {1187case ' ': /* space */1188case '\'': /* apostrophe */1189case '(': /* left paren */1190case ')': /* right paren */1191case '+': /* plus */1192case ',': /* comma */1193case '-': /* hyphen */1194case '.': /* period */1195case '/': /* slash */1196case ':': /* colon */1197case '=': /* equals */1198case '?': /* question mark */1199return true;1200default:1201return false;1202}1203}1204}12051206/**1207* Create the tag of the attribute.1208*1209* @param tagClass the tag class type, one of UNIVERSAL, CONTEXT,1210* APPLICATION or PRIVATE1211* @param form if true, the value is constructed, otherwise it1212* is primitive.1213* @param val the tag value1214*/1215public static byte createTag(byte tagClass, boolean form, byte val) {1216if (val < 0 || val > 30) {1217throw new IllegalArgumentException("Tag number over 30 is not supported");1218}1219byte tag = (byte)(tagClass | val);1220if (form) {1221tag |= (byte)0x20;1222}1223return (tag);1224}12251226/**1227* Set the tag of the attribute. Commonly used to reset the1228* tag value used for IMPLICIT encodings.1229*1230* This method should be avoided, consider using withTag() instead.1231*1232* @param tag the tag value1233*/1234public void resetTag(byte tag) {1235this.tag = tag;1236}12371238/**1239* Returns a new DerValue with a different tag. This method is used1240* to convert a DerValue decoded from an IMPLICIT encoding to its real1241* tag. The content is not checked against the tag in this method.1242*1243* @param newTag the new tag1244* @return a new DerValue1245*/1246public DerValue withTag(byte newTag) {1247return new DerValue(newTag, buffer, start, end, allowBER);1248}12491250/**1251* Returns a hashcode for this DerValue.1252*1253* @return a hashcode for this DerValue.1254*/1255@Override1256public int hashCode() {1257int result = tag;1258for (int i = start; i < end; i++) {1259result = 31 * result + buffer[i];1260}1261return result;1262}12631264/**1265* Reads the sub-values in a constructed DerValue.1266*1267* @param expectedTag the expected tag, or zero if we don't check.1268* This is useful when this DerValue is IMPLICIT.1269* @param startLen estimated number of sub-values1270* @return the sub-values in an array1271*/1272DerValue[] subs(byte expectedTag, int startLen) throws IOException {1273if (expectedTag != 0 && expectedTag != tag) {1274throw new IOException("Not the correct tag");1275}1276List<DerValue> result = new ArrayList<>(startLen);1277DerInputStream dis = data();1278while (dis.available() > 0) {1279result.add(dis.getDerValue());1280}1281return result.toArray(new DerValue[0]);1282}12831284public void clear() {1285Arrays.fill(buffer, start, end, (byte)0);1286}1287}128812891290