Path: blob/master/src/java.base/share/classes/sun/security/util/DerInputStream.java
41159 views
/*1* Copyright (c) 1996, 2020, 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 java.io.InputStream;28import java.io.IOException;29import java.math.BigInteger;30import java.util.Arrays;31import java.util.Date;3233/**34* A DER input stream, used for parsing ASN.1 DER-encoded data such as35* that found in X.509 certificates. DER is a subset of BER/1, which has36* the advantage that it allows only a single encoding of primitive data.37* (High level data such as dates still support many encodings.) That is,38* it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER).39*40* <P>Note that, like BER/1, DER streams are streams of explicitly41* tagged data values. Accordingly, this programming interface does42* not expose any variant of the java.io.InputStream interface, since43* that kind of input stream holds untagged data values and using that44* I/O model could prevent correct parsing of the DER data.45*46* <P>At this time, this class supports only a subset of the types of DER47* data encodings which are defined. That subset is sufficient for parsing48* most X.509 certificates.49*50*51* @author David Brownell52* @author Amit Kapoor53* @author Hemma Prafullchandra54*/5556public class DerInputStream {5758// The static part59final byte[] data;60final int start; // inclusive61final int end; // exclusive62final boolean allowBER;6364// The moving part65int pos;66int mark;6768/**69* Constructs a DerInputStream by assigning all its fields.70*71* No checking on arguments since all callers are internal.72* {@code data} should never be null even if length is 0.73*/74public DerInputStream(byte[] data, int start, int length, boolean allowBER) {75this.data = data;76this.start = start;77this.end = start + length;78this.allowBER = allowBER;79this.pos = start;80this.mark = start;81}8283public DerInputStream(byte[] data) throws IOException {84this(data, 0, data.length, true);85}8687public DerInputStream(byte[] data, int offset, int len) throws IOException {88this(data, offset, len, true);89}9091/**92* Returns the remaining unread bytes, or, all bytes if none read yet.93*/94public byte[] toByteArray() {95return Arrays.copyOfRange(data, pos, end);96}9798/**99* Reads a DerValue from this stream. After the call, the data pointer100* is right after this DerValue so that the next call will read the101* next DerValue.102*103* @return the read DerValue.104* @throws IOException if a DerValue cannot be constructed starting from105* this position because of byte shortage or encoding error.106*/107public DerValue getDerValue() throws IOException {108DerValue result = new DerValue(109this.data, this.pos, this.end - this.pos, this.allowBER, true);110if (result.buffer != this.data) {111// Indefinite length observed. Unused bytes in data are appended112// to the end of return value by DerIndefLenConverter::convertBytes113// and stay inside result.buffer.114int unused = result.buffer.length - result.end;115this.pos = this.data.length - unused;116} else {117this.pos = result.end;118}119return result;120}121122// The following getXyz methods are mostly shorthands for getDerValue().getXyz().123124public int getInteger() throws IOException {125return getDerValue().getInteger();126}127128public BigInteger getBigInteger() throws IOException {129return getDerValue().getBigInteger();130}131132public BigInteger getPositiveBigInteger() throws IOException {133return getDerValue().getPositiveBigInteger();134}135136public int getEnumerated() throws IOException {137return getDerValue().getEnumerated();138}139140public byte[] getBitString() throws IOException {141return getDerValue().getBitString();142}143144public BitArray getUnalignedBitString() throws IOException {145return getDerValue().getUnalignedBitString();146}147148public byte[] getOctetString() throws IOException {149// Not identical to DerValue::getOctetString. This method150// does not accept constructed OCTET STRING.151DerValue v = getDerValue();152if (v.tag != DerValue.tag_OctetString) {153throw new IOException("DER input not an octet string");154}155return v.getOctetString();156}157158public void getNull() throws IOException {159getDerValue().getNull();160}161162public ObjectIdentifier getOID() throws IOException {163return getDerValue().getOID();164}165166public String getUTF8String() throws IOException {167return getDerValue().getUTF8String();168}169170public String getPrintableString() throws IOException {171return getDerValue().getPrintableString();172}173174public String getT61String() throws IOException {175return getDerValue().getT61String();176}177178public String getBMPString() throws IOException {179return getDerValue().getBMPString();180}181182public String getIA5String() throws IOException {183return getDerValue().getIA5String();184}185186public String getGeneralString() throws IOException {187return getDerValue().getGeneralString();188}189190public Date getUTCTime() throws IOException {191return getDerValue().getUTCTime();192}193194public Date getGeneralizedTime() throws IOException {195return getDerValue().getGeneralizedTime();196}197198// Read a series of DerValue objects which is the sub-elements199// of a SEQUENCE and SET.200201public DerValue[] getSequence(int startLen) throws IOException {202return getDerValue().subs(DerValue.tag_Sequence, startLen);203}204205public DerValue[] getSet(int startLen) throws IOException {206return getDerValue().subs(DerValue.tag_Set, startLen);207}208209public DerValue[] getSet(int startLen, boolean implicit) throws IOException {210if (implicit) {211return getDerValue().subs((byte) 0, startLen);212} else {213return getSet(startLen);214}215}216217public int peekByte() throws IOException {218if (pos == end) {219throw new IOException("At end");220}221return data[pos];222}223224/**225* Get a length from the input stream, allowing for at most 32 bits of226* encoding to be used. (Not the same as getting a tagged integer!)227*228* @return the length or -1 if indefinite length found.229* @exception IOException on parsing error or unsupported lengths.230*/231static int getLength(InputStream in) throws IOException {232int lenByte = in.read();233if (lenByte == -1) {234throw new IOException("Short read of DER length");235}236if (lenByte == 0x80) {237return -1;238}239240int value, tmp;241String mdName = "DerInputStream.getLength(): ";242tmp = lenByte;243if ((tmp & 0x080) == 0x00) { // short form, 1 byte datum244value = tmp;245} else { // long form246tmp &= 0x07f;247248// tmp > 4 indicates more than 4Gb of data.249if (tmp > 4) {250throw new IOException(mdName + "lengthTag=" + tmp + ", too big.");251}252253value = 0x0ff & in.read();254tmp--;255if (value == 0) {256// DER requires length value be encoded in minimum number of bytes257throw new IOException(mdName + "Redundant length bytes found");258}259while (tmp-- > 0) {260value <<= 8;261value += 0x0ff & in.read();262}263if (value < 0) {264throw new IOException(mdName + "Invalid length bytes");265} else if (value <= 127) {266throw new IOException(mdName + "Should use short form for length");267}268}269return value;270}271272/*273* Get a definite length from the input stream.274*275* @return the length276* @exception IOException on parsing error or if indefinite length found.277*/278static int getDefiniteLength(InputStream in) throws IOException {279int len = getLength(in);280if (len < 0) {281throw new IOException("Indefinite length encoding not supported");282}283return len;284}285286/**287* Mark the current position in the buffer, so that288* a later call to <code>reset</code> will return here.289* The {@code readAheadLimit} is useless here because290* all data is available and we can go to anywhere at will.291*/292public void mark(int readAheadLimit) { mark = pos; }293294/**295* Return to the position of the last <code>mark</code>296* call. A mark is implicitly set at the beginning of297* the stream when it is created.298*/299public void reset() { pos = mark; }300301/**302* Returns the number of bytes available for reading.303* This is most useful for testing whether the stream is304* empty.305*/306public int available() { return end - pos; }307}308309310