|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.util; |
|
|
|
import java.io.InputStream; |
|
import java.io.IOException; |
|
import java.math.BigInteger; |
|
import java.util.Arrays; |
|
import java.util.Date; |
|
|
|
/** |
|
* A DER input stream, used for parsing ASN.1 DER-encoded data such as |
|
* that found in X.509 certificates. DER is a subset of BER/1, which has |
|
* the advantage that it allows only a single encoding of primitive data. |
|
* (High level data such as dates still support many encodings.) That is, |
|
* it uses the "Definite" Encoding Rules (DER) not the "Basic" ones (BER). |
|
* |
|
* <P>Note that, like BER/1, DER streams are streams of explicitly |
|
* tagged data values. Accordingly, this programming interface does |
|
* not expose any variant of the java.io.InputStream interface, since |
|
* that kind of input stream holds untagged data values and using that |
|
* I/O model could prevent correct parsing of the DER data. |
|
* |
|
* <P>At this time, this class supports only a subset of the types of DER |
|
* data encodings which are defined. That subset is sufficient for parsing |
|
* most X.509 certificates. |
|
* |
|
* |
|
* @author David Brownell |
|
* @author Amit Kapoor |
|
* @author Hemma Prafullchandra |
|
*/ |
|
|
|
public class DerInputStream { |
|
|
|
|
|
final byte[] data; |
|
final int start; |
|
final int end; |
|
final boolean allowBER; |
|
|
|
|
|
int pos; |
|
int mark; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DerInputStream(byte[] data, int start, int length, boolean allowBER) { |
|
this.data = data; |
|
this.start = start; |
|
this.end = start + length; |
|
this.allowBER = allowBER; |
|
this.pos = start; |
|
this.mark = start; |
|
} |
|
|
|
public DerInputStream(byte[] data) throws IOException { |
|
this(data, 0, data.length, true); |
|
} |
|
|
|
public DerInputStream(byte[] data, int offset, int len) throws IOException { |
|
this(data, offset, len, true); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public byte[] toByteArray() { |
|
return Arrays.copyOfRange(data, pos, end); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public DerValue getDerValue() throws IOException { |
|
DerValue result = new DerValue( |
|
this.data, this.pos, this.end - this.pos, this.allowBER, true); |
|
if (result.buffer != this.data) { |
|
// Indefinite length observed. Unused bytes in data are appended |
|
// to the end of return value by DerIndefLenConverter::convertBytes |
|
|
|
int unused = result.buffer.length - result.end; |
|
this.pos = this.data.length - unused; |
|
} else { |
|
this.pos = result.end; |
|
} |
|
return result; |
|
} |
|
|
|
// The following getXyz methods are mostly shorthands for getDerValue().getXyz(). |
|
|
|
public int getInteger() throws IOException { |
|
return getDerValue().getInteger(); |
|
} |
|
|
|
public BigInteger getBigInteger() throws IOException { |
|
return getDerValue().getBigInteger(); |
|
} |
|
|
|
public BigInteger getPositiveBigInteger() throws IOException { |
|
return getDerValue().getPositiveBigInteger(); |
|
} |
|
|
|
public int getEnumerated() throws IOException { |
|
return getDerValue().getEnumerated(); |
|
} |
|
|
|
public byte[] getBitString() throws IOException { |
|
return getDerValue().getBitString(); |
|
} |
|
|
|
public BitArray getUnalignedBitString() throws IOException { |
|
return getDerValue().getUnalignedBitString(); |
|
} |
|
|
|
public byte[] getOctetString() throws IOException { |
|
// Not identical to DerValue::getOctetString. This method |
|
|
|
DerValue v = getDerValue(); |
|
if (v.tag != DerValue.tag_OctetString) { |
|
throw new IOException("DER input not an octet string"); |
|
} |
|
return v.getOctetString(); |
|
} |
|
|
|
public void getNull() throws IOException { |
|
getDerValue().getNull(); |
|
} |
|
|
|
public ObjectIdentifier getOID() throws IOException { |
|
return getDerValue().getOID(); |
|
} |
|
|
|
public String getUTF8String() throws IOException { |
|
return getDerValue().getUTF8String(); |
|
} |
|
|
|
public String getPrintableString() throws IOException { |
|
return getDerValue().getPrintableString(); |
|
} |
|
|
|
public String getT61String() throws IOException { |
|
return getDerValue().getT61String(); |
|
} |
|
|
|
public String getBMPString() throws IOException { |
|
return getDerValue().getBMPString(); |
|
} |
|
|
|
public String getIA5String() throws IOException { |
|
return getDerValue().getIA5String(); |
|
} |
|
|
|
public String getGeneralString() throws IOException { |
|
return getDerValue().getGeneralString(); |
|
} |
|
|
|
public Date getUTCTime() throws IOException { |
|
return getDerValue().getUTCTime(); |
|
} |
|
|
|
public Date getGeneralizedTime() throws IOException { |
|
return getDerValue().getGeneralizedTime(); |
|
} |
|
|
|
// Read a series of DerValue objects which is the sub-elements |
|
// of a SEQUENCE and SET. |
|
|
|
public DerValue[] getSequence(int startLen) throws IOException { |
|
return getDerValue().subs(DerValue.tag_Sequence, startLen); |
|
} |
|
|
|
public DerValue[] getSet(int startLen) throws IOException { |
|
return getDerValue().subs(DerValue.tag_Set, startLen); |
|
} |
|
|
|
public DerValue[] getSet(int startLen, boolean implicit) throws IOException { |
|
if (implicit) { |
|
return getDerValue().subs((byte) 0, startLen); |
|
} else { |
|
return getSet(startLen); |
|
} |
|
} |
|
|
|
public int peekByte() throws IOException { |
|
if (pos == end) { |
|
throw new IOException("At end"); |
|
} |
|
return data[pos]; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static int getLength(InputStream in) throws IOException { |
|
int lenByte = in.read(); |
|
if (lenByte == -1) { |
|
throw new IOException("Short read of DER length"); |
|
} |
|
if (lenByte == 0x80) { |
|
return -1; |
|
} |
|
|
|
int value, tmp; |
|
String mdName = "DerInputStream.getLength(): "; |
|
tmp = lenByte; |
|
if ((tmp & 0x080) == 0x00) { |
|
value = tmp; |
|
} else { |
|
tmp &= 0x07f; |
|
|
|
|
|
if (tmp > 4) { |
|
throw new IOException(mdName + "lengthTag=" + tmp + ", too big."); |
|
} |
|
|
|
value = 0x0ff & in.read(); |
|
tmp--; |
|
if (value == 0) { |
|
|
|
throw new IOException(mdName + "Redundant length bytes found"); |
|
} |
|
while (tmp-- > 0) { |
|
value <<= 8; |
|
value += 0x0ff & in.read(); |
|
} |
|
if (value < 0) { |
|
throw new IOException(mdName + "Invalid length bytes"); |
|
} else if (value <= 127) { |
|
throw new IOException(mdName + "Should use short form for length"); |
|
} |
|
} |
|
return value; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static int getDefiniteLength(InputStream in) throws IOException { |
|
int len = getLength(in); |
|
if (len < 0) { |
|
throw new IOException("Indefinite length encoding not supported"); |
|
} |
|
return len; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void mark(int readAheadLimit) { mark = pos; } |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void reset() { pos = mark; } |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int available() { return end - pos; } |
|
} |