Path: blob/master/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java
41159 views
/*1* Copyright (c) 1998, 2019, 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.IOException;28import java.io.InputStream;29import java.util.ArrayList;30import java.util.Arrays;3132/**33* A package private utility class to convert indefinite length DER34* encoded byte arrays to definite length DER encoded byte arrays.35*36* This assumes that the basic data structure is "tag, length, value"37* triplet. In the case where the length is "indefinite", terminating38* end-of-contents bytes are expected.39*40* @author Hemma Prafullchandra41*/42class DerIndefLenConverter {4344private static final int TAG_MASK = 0x1f; // bits 5-145private static final int FORM_MASK = 0x20; // bits 646private static final int CLASS_MASK = 0xC0; // bits 8 and 74748private static final int LEN_LONG = 0x80; // bit 8 set49private static final int LEN_MASK = 0x7f; // bits 7 - 150private static final int SKIP_EOC_BYTES = 2;5152private byte[] data, newData;53private int newDataPos, dataPos, dataSize, index;54private int unresolved = 0;5556private ArrayList<Object> ndefsList = new ArrayList<Object>();5758private int numOfTotalLenBytes = 0;5960private boolean isEOC(int tag) {61return (((tag & TAG_MASK) == 0x00) && // EOC62((tag & FORM_MASK) == 0x00) && // primitive63((tag & CLASS_MASK) == 0x00)); // universal64}6566// if bit 8 is set then it implies either indefinite length or long form67static boolean isLongForm(int lengthByte) {68return ((lengthByte & LEN_LONG) == LEN_LONG);69}7071/*72* Default package private constructor73*/74DerIndefLenConverter() { }7576/**77* Checks whether the given length byte is of the form78* <em>Indefinite</em>.79*80* @param lengthByte the length byte from a DER encoded81* object.82* @return true if the byte is of Indefinite form otherwise83* returns false.84*/85static boolean isIndefinite(int lengthByte) {86return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));87}8889/**90* Parse the tag and if it is an end-of-contents tag then91* add the current position to the <code>eocList</code> vector.92*/93private void parseTag() throws IOException {94if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) {95int numOfEncapsulatedLenBytes = 0;96Object elem = null;97int index;98for (index = ndefsList.size()-1; index >= 0; index--) {99// Determine the first element in the vector that does not100// have a matching EOC101elem = ndefsList.get(index);102if (elem instanceof Integer) {103break;104} else {105numOfEncapsulatedLenBytes += ((byte[])elem).length - 3;106}107}108if (index < 0) {109throw new IOException("EOC does not have matching " +110"indefinite-length tag");111}112int sectionLen = dataPos - ((Integer)elem).intValue() +113numOfEncapsulatedLenBytes;114byte[] sectionLenBytes = getLengthBytes(sectionLen);115ndefsList.set(index, sectionLenBytes);116unresolved--;117118// Add the number of bytes required to represent this section119// to the total number of length bytes,120// and subtract the indefinite-length tag (1 byte) and121// EOC bytes (2 bytes) for this section122numOfTotalLenBytes += (sectionLenBytes.length - 3);123}124dataPos++;125}126127/**128* Write the tag and if it is an end-of-contents tag129* then skip the tag and its 1 byte length of zero.130*/131private void writeTag() {132if (dataPos == dataSize)133return;134int tag = data[dataPos++];135if (isEOC(tag) && (data[dataPos] == 0)) {136dataPos++; // skip length137writeTag();138} else139newData[newDataPos++] = (byte)tag;140}141142/**143* Parse the length and if it is an indefinite length then add144* the current position to the <code>ndefsList</code> vector.145*146* @return the length of definite length data next, or -1 if there is147* not enough bytes to determine it148* @throws IOException if invalid data is read149*/150private int parseLength() throws IOException {151int curLen = 0;152if (dataPos == dataSize)153return curLen;154int lenByte = data[dataPos++] & 0xff;155if (isIndefinite(lenByte)) {156ndefsList.add(dataPos);157unresolved++;158return curLen;159}160if (isLongForm(lenByte)) {161lenByte &= LEN_MASK;162if (lenByte > 4) {163throw new IOException("Too much data");164}165if ((dataSize - dataPos) < (lenByte + 1)) {166return -1;167}168for (int i = 0; i < lenByte; i++) {169curLen = (curLen << 8) + (data[dataPos++] & 0xff);170}171if (curLen < 0) {172throw new IOException("Invalid length bytes");173}174} else {175curLen = (lenByte & LEN_MASK);176}177return curLen;178}179180/**181* Write the length and if it is an indefinite length182* then calculate the definite length from the positions183* of the indefinite length and its matching EOC terminator.184* Then, write the value.185*/186private void writeLengthAndValue() throws IOException {187if (dataPos == dataSize)188return;189int curLen = 0;190int lenByte = data[dataPos++] & 0xff;191if (isIndefinite(lenByte)) {192byte[] lenBytes = (byte[])ndefsList.get(index++);193System.arraycopy(lenBytes, 0, newData, newDataPos,194lenBytes.length);195newDataPos += lenBytes.length;196return;197}198if (isLongForm(lenByte)) {199lenByte &= LEN_MASK;200for (int i = 0; i < lenByte; i++) {201curLen = (curLen << 8) + (data[dataPos++] & 0xff);202}203if (curLen < 0) {204throw new IOException("Invalid length bytes");205}206} else {207curLen = (lenByte & LEN_MASK);208}209writeLength(curLen);210writeValue(curLen);211}212213private void writeLength(int curLen) {214if (curLen < 128) {215newData[newDataPos++] = (byte)curLen;216217} else if (curLen < (1 << 8)) {218newData[newDataPos++] = (byte)0x81;219newData[newDataPos++] = (byte)curLen;220221} else if (curLen < (1 << 16)) {222newData[newDataPos++] = (byte)0x82;223newData[newDataPos++] = (byte)(curLen >> 8);224newData[newDataPos++] = (byte)curLen;225226} else if (curLen < (1 << 24)) {227newData[newDataPos++] = (byte)0x83;228newData[newDataPos++] = (byte)(curLen >> 16);229newData[newDataPos++] = (byte)(curLen >> 8);230newData[newDataPos++] = (byte)curLen;231232} else {233newData[newDataPos++] = (byte)0x84;234newData[newDataPos++] = (byte)(curLen >> 24);235newData[newDataPos++] = (byte)(curLen >> 16);236newData[newDataPos++] = (byte)(curLen >> 8);237newData[newDataPos++] = (byte)curLen;238}239}240241private byte[] getLengthBytes(int curLen) {242byte[] lenBytes;243int index = 0;244245if (curLen < 128) {246lenBytes = new byte[1];247lenBytes[index++] = (byte)curLen;248249} else if (curLen < (1 << 8)) {250lenBytes = new byte[2];251lenBytes[index++] = (byte)0x81;252lenBytes[index++] = (byte)curLen;253254} else if (curLen < (1 << 16)) {255lenBytes = new byte[3];256lenBytes[index++] = (byte)0x82;257lenBytes[index++] = (byte)(curLen >> 8);258lenBytes[index++] = (byte)curLen;259260} else if (curLen < (1 << 24)) {261lenBytes = new byte[4];262lenBytes[index++] = (byte)0x83;263lenBytes[index++] = (byte)(curLen >> 16);264lenBytes[index++] = (byte)(curLen >> 8);265lenBytes[index++] = (byte)curLen;266267} else {268lenBytes = new byte[5];269lenBytes[index++] = (byte)0x84;270lenBytes[index++] = (byte)(curLen >> 24);271lenBytes[index++] = (byte)(curLen >> 16);272lenBytes[index++] = (byte)(curLen >> 8);273lenBytes[index++] = (byte)curLen;274}275276return lenBytes;277}278279// Returns the number of bytes needed to represent the given length280// in ASN.1 notation281private int getNumOfLenBytes(int len) {282int numOfLenBytes = 0;283284if (len < 128) {285numOfLenBytes = 1;286} else if (len < (1 << 8)) {287numOfLenBytes = 2;288} else if (len < (1 << 16)) {289numOfLenBytes = 3;290} else if (len < (1 << 24)) {291numOfLenBytes = 4;292} else {293numOfLenBytes = 5;294}295return numOfLenBytes;296}297298/**299* Parse the value;300*/301private void parseValue(int curLen) {302dataPos += curLen;303}304305/**306* Write the value;307*/308private void writeValue(int curLen) {309for (int i=0; i < curLen; i++)310newData[newDataPos++] = data[dataPos++];311}312313/**314* Converts a indefinite length DER encoded byte array to315* a definte length DER encoding.316*317* @param indefData the byte array holding the indefinite318* length encoding.319* @return the byte array containing the definite length320* DER encoding, or null if there is not enough data.321* @exception IOException on parsing or re-writing errors.322*/323byte[] convertBytes(byte[] indefData) throws IOException {324data = indefData;325dataPos=0; index=0;326dataSize = data.length;327int len=0;328int unused = 0;329330// parse and set up the vectors of all the indefinite-lengths331while (dataPos < dataSize) {332if (dataPos + 2 > dataSize) {333// There should be at least one tag and one length334return null;335}336parseTag();337len = parseLength();338if (len < 0) {339return null;340}341parseValue(len);342if (unresolved == 0) {343unused = dataSize - dataPos;344dataSize = dataPos;345break;346}347}348349if (unresolved != 0) {350return null;351}352353newData = new byte[dataSize + numOfTotalLenBytes + unused];354dataPos=0; newDataPos=0; index=0;355356// write out the new byte array replacing all the indefinite-lengths357// and EOCs358while (dataPos < dataSize) {359writeTag();360writeLengthAndValue();361}362System.arraycopy(indefData, dataSize,363newData, dataSize + numOfTotalLenBytes, unused);364365return newData;366}367368/**369* Read the input stream into a DER byte array. If an indef len BER is370* not resolved this method will try to read more data until EOF is reached.371* This may block.372*373* @param in the input stream with tag and lenByte already read374* @param tag the tag to remember375* @return a DER byte array376* @throws IOException if not all indef len BER377* can be resolved or another I/O error happens378*/379public static byte[] convertStream(InputStream in, byte tag)380throws IOException {381int offset = 2; // for tag and length bytes382int readLen = in.available();383byte[] indefData = new byte[readLen + offset];384indefData[0] = tag;385indefData[1] = (byte)0x80;386while (true) {387int bytesRead = in.readNBytes(indefData, offset, readLen);388if (bytesRead != readLen) {389readLen = bytesRead;390indefData = Arrays.copyOf(indefData, offset + bytesRead);391}392DerIndefLenConverter derIn = new DerIndefLenConverter();393byte[] result = derIn.convertBytes(indefData);394if (result == null) {395int next = in.read(); // This could block, but we need more396if (next == -1) {397throw new IOException("not all indef len BER resolved");398}399int more = in.available();400// expand array to include next and more401indefData = Arrays.copyOf(indefData, offset + readLen + 1 + more);402indefData[offset + readLen] = (byte)next;403offset = offset + readLen + 1;404readLen = more;405} else {406return result;407}408}409}410}411412413