You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2015/12/09 03:13:23 UTC

[11/19] directory-kerby git commit: DIRKRB-490. Separate ASN1 parser

DIRKRB-490. Separate ASN1 parser


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/030234b3
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/030234b3
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/030234b3

Branch: refs/heads/pkinit-support
Commit: 030234b319a88fed8fa0f1b60d9bafa01e624935
Parents: f37896d
Author: Kai Zheng <ka...@intel.com>
Authored: Wed Dec 9 05:43:34 2015 +0800
Committer: Kai Zheng <ka...@intel.com>
Committed: Wed Dec 9 05:43:34 2015 +0800

----------------------------------------------------------------------
 .../main/java/org/apache/kerby/asn1/Asn1.java   |  59 +++----
 .../java/org/apache/kerby/asn1/Asn1Dumper.java  |  62 +++++--
 .../java/org/apache/kerby/asn1/Asn1Header.java  | 100 -----------
 .../org/apache/kerby/asn1/DecodingUtil.java     |  88 ++++++++++
 .../apache/kerby/asn1/parse/Asn1Container.java  |  81 +++++++++
 .../org/apache/kerby/asn1/parse/Asn1Header.java |  99 +++++++++++
 .../org/apache/kerby/asn1/parse/Asn1Item.java   |  39 +++++
 .../org/apache/kerby/asn1/parse/Asn1Parser.java |  88 ++++++++++
 .../kerby/asn1/parse/Asn1ParsingResult.java     | 100 +++++++++++
 .../org/apache/kerby/asn1/parse/Asn1Reader.java | 132 +++++++++++++++
 .../org/apache/kerby/asn1/type/Asn1Any.java     |  17 +-
 .../apache/kerby/asn1/type/Asn1BmpString.java   |   8 +-
 .../org/apache/kerby/asn1/type/Asn1Boolean.java |   8 +-
 .../org/apache/kerby/asn1/type/Asn1Choice.java  |  14 +-
 .../apache/kerby/asn1/type/Asn1Collection.java  |   2 +-
 .../kerby/asn1/type/Asn1CollectionOf.java       |  36 ++--
 .../kerby/asn1/type/Asn1CollectionType.java     |  36 ++--
 .../apache/kerby/asn1/type/Asn1Constructed.java |  40 +++--
 .../org/apache/kerby/asn1/type/Asn1Eoc.java     |   6 +-
 .../org/apache/kerby/asn1/type/Asn1Item.java    | 140 ----------------
 .../org/apache/kerby/asn1/type/Asn1Null.java    |   6 +-
 .../org/apache/kerby/asn1/type/Asn1Object.java  |  49 +++---
 .../apache/kerby/asn1/type/Asn1OctetString.java |   6 +-
 .../kerby/asn1/type/Asn1ParsingContainer.java   | 165 -------------------
 .../apache/kerby/asn1/type/Asn1ParsingItem.java |  49 ------
 .../kerby/asn1/type/Asn1ParsingResult.java      |  84 ----------
 .../org/apache/kerby/asn1/type/Asn1Simple.java  |   6 +-
 .../org/apache/kerby/asn1/type/Asn1Tagging.java |  13 +-
 .../kerby/asn1/type/Asn1TaggingCollection.java  |   6 +-
 .../org/apache/kerby/asn1/util/Asn1Reader.java  | 121 --------------
 .../org/apache/kerby/asn1/util/Asn1Reader1.java |  62 -------
 .../org/apache/kerby/asn1/util/Asn1Reader2.java |  65 --------
 .../asn1/TestAsn1ConstructedOctetString.java    |  35 ++++
 .../org/apache/kerby/asn1/TestAsn1Dump.java     |  30 ++--
 .../apache/kerby/asn1/TestPersonnelRecord.java  |   2 +-
 .../java/org/apache/kerby/asn1/TestUtil.java    |  47 ++++++
 .../test/resources/constructed-octet-string.txt |   1 +
 .../apache/kerby/kerberos/kerb/KrbCodec.java    |   8 +-
 .../org/apache/kerby/x509/type/GeneralName.java |  15 +-
 .../apache/kerby/cms/TestCompressedData.java    |   2 +-
 .../org/apache/kerby/cms/TestSignedData.java    |   2 +-
 41 files changed, 944 insertions(+), 985 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1.java
index 17279b8..8ca411d 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1.java
@@ -19,9 +19,9 @@
  */
 package org.apache.kerby.asn1;
 
-import org.apache.kerby.asn1.type.Asn1ParsingContainer;
+import org.apache.kerby.asn1.parse.Asn1Parser;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 import org.apache.kerby.asn1.type.Asn1Type;
-import org.apache.kerby.asn1.util.Asn1Reader1;
 import org.apache.kerby.asn1.util.HexUtil;
 
 import java.io.IOException;
@@ -45,17 +45,21 @@ public final class Asn1 {
         return value.encode();
     }
 
-    public static Asn1Header decodeHeader(ByteBuffer content) throws IOException {
-        Asn1Reader1 reader = new Asn1Reader1(content);
-        return reader.readHeader();
-    }
-
     public static Asn1Type decode(byte[] content) throws IOException {
         return decode(ByteBuffer.wrap(content));
     }
 
     public static Asn1Type decode(ByteBuffer content) throws IOException {
-        return Asn1ParsingContainer.decodeOne(content);
+        Asn1ParsingResult parsingResult = Asn1Parser.parse(content);
+        return DecodingUtil.decodeValue(parsingResult);
+    }
+
+    public static Asn1ParsingResult parse(byte[] content) throws IOException {
+        return parse(ByteBuffer.wrap(content));
+    }
+
+    public static Asn1ParsingResult parse(ByteBuffer content) throws IOException {
+        return Asn1Parser.parse(content);
     }
 
     public static void dump(Asn1Type value) {
@@ -72,36 +76,27 @@ public final class Asn1 {
         System.out.println(output);
     }
 
-    public static void dump(String hexStr) throws IOException {
-        System.out.println("Dumping data:");
-        System.out.println(hexStr);
-        Asn1Dumper dumper = new Asn1Dumper();
+    public static void dump(String hexStr,
+                            boolean useRawFormat) throws IOException {
         byte[] data = HexUtil.hex2bytes(hexStr);
-        dumper.dump(data);
-        String output = dumper.output();
-        System.out.println(output);
+        dump(data, useRawFormat);
     }
 
-    public static void dump(byte[] content) throws IOException {
-        String hexStr = HexUtil.bytesToHex(content);
-        System.out.println("Dumping data:");
-        int range = 100;
-        int pos = range;
-        while (pos < hexStr.length()) {
-            System.out.println(hexStr.substring(pos - range, pos));
-            pos = pos + range;
-        }
-        System.out.println(hexStr.substring(pos - range, hexStr.length()));
+    public static void dump(ByteBuffer content,
+                            boolean useRawFormat) throws IOException {
+        byte[] bytes = new byte[content.remaining()];
+        content.get(bytes);
+        dump(bytes, useRawFormat);
+    }
 
+    public static void dump(byte[] content,
+                            boolean useRawFormat) throws IOException {
+        String hexStr = HexUtil.bytesToHex(content);
         Asn1Dumper dumper = new Asn1Dumper();
-        dumper.dump(content);
+        System.out.println("Dumping data:");
+        dumper.dumpData(hexStr);
+        dumper.dump(content, useRawFormat);
         String output = dumper.output();
         System.out.println(output);
     }
-
-    public static void dump(ByteBuffer content) throws IOException {
-        byte[] bytes = new byte[content.remaining()];
-        content.get(bytes);
-        dump(bytes);
-    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dumper.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dumper.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dumper.java
index d8354b0..f06eb5c 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dumper.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Dumper.java
@@ -19,10 +19,13 @@
  */
 package org.apache.kerby.asn1;
 
+import org.apache.kerby.asn1.parse.Asn1Parser;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
+import org.apache.kerby.asn1.parse.Asn1Item;
 import org.apache.kerby.asn1.type.Asn1Any;
-import org.apache.kerby.asn1.type.Asn1ParsingItem;
 import org.apache.kerby.asn1.type.Asn1Simple;
 import org.apache.kerby.asn1.type.Asn1Type;
+import org.apache.kerby.asn1.util.HexUtil;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -47,17 +50,28 @@ public final class Asn1Dumper {
         return builder.toString();
     }
 
-    public void dump(byte[] content) throws IOException {
-        dump(ByteBuffer.wrap(content));
+    public void dumpWithHex(byte[] content,
+                                  boolean useRawFormat) throws IOException {
+        String hexStr = HexUtil.bytesToHex(content);
+        append("Dumping data:").newLine();
+        dumpData(hexStr);
+        dump(content, useRawFormat);
     }
 
-    public void dump(ByteBuffer content) throws IOException {
-        Asn1Type value = Asn1.decode(content);
-        if (value == null) {
-            return;
-        }
+    public void dump(byte[] content,
+                     boolean useRawFormat) throws IOException {
+        dump(ByteBuffer.wrap(content), useRawFormat);
+    }
 
-        dumpType(0, value);
+    public void dump(ByteBuffer content,
+                     boolean useRawFormat) throws IOException {
+        if (useRawFormat) {
+            Asn1ParsingResult parsingResult = Asn1Parser.parse(content);
+            dumpParseResult(0, parsingResult);
+        } else {
+            Asn1Type value = Asn1.decode(content);
+            dumpType(0, value);
+        }
     }
 
     public void dumpType(Asn1Type value) {
@@ -69,7 +83,7 @@ public final class Asn1Dumper {
             indent(indents).append("Null");
         } else if (value instanceof Asn1Simple) {
             indent(indents).append(value.toString());
-        }  else if (value instanceof Asn1ParsingItem) {
+        }  else if (value instanceof Asn1Item) {
             indent(indents).append(value.toString());
         } else if (value instanceof Asn1Dumpable) {
             Asn1Dumpable dumpable = (Asn1Dumpable) value;
@@ -83,6 +97,21 @@ public final class Asn1Dumper {
         return this;
     }
 
+    public Asn1Dumper dumpParseResult(int indents, Asn1ParsingResult value) {
+        if (value == null) {
+            indent(indents).append("Null");
+        } else if (value instanceof Asn1Item) {
+            indent(indents).append(value.toString());
+        } else if (value instanceof Asn1Dumpable) {
+            Asn1Dumpable dumpable = (Asn1Dumpable) value;
+            dumpable.dumpWith(this, indents);
+        } else {
+            indent(indents).append("<Unknown>");
+        }
+
+        return this;
+    }
+
     public Asn1Dumper indent(int numSpaces) {
         for (int i = 0; i < numSpaces; i++) {
             builder.append(' ');
@@ -125,4 +154,17 @@ public final class Asn1Dumper {
         builder.append("\n");
         return this;
     }
+
+    public Asn1Dumper dumpData(String hexData) {
+        int range = 100;
+        int pos = range;
+
+        while (pos < hexData.length()) {
+            System.out.println(hexData.substring(pos - range, pos));
+            pos = pos + range;
+        }
+        System.out.println(hexData.substring(pos - range, hexData.length()));
+
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Header.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Header.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Header.java
deleted file mode 100644
index 1284039..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/Asn1Header.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *  
- *    http://www.apache.org/licenses/LICENSE-2.0
- *  
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *  
- */
-package org.apache.kerby.asn1;
-
-import java.nio.ByteBuffer;
-
-public class Asn1Header {
-    private Tag tag;
-    private int length;
-    private int bodyStart;
-    private int bodyEnd;
-    private ByteBuffer buffer;
-
-    public Asn1Header(Tag tag, int length,
-                      int bodyStart, ByteBuffer buffer) {
-        this.tag = tag;
-        this.length = length;
-        this.bodyStart = bodyStart;
-        this.buffer = buffer;
-
-        this.bodyEnd = isDefinitiveLength() ? bodyStart + length : -1;
-    }
-
-    public Tag getTag() {
-        return tag;
-    }
-
-    public int getActualBodyLength() {
-        if (isDefinitiveLength()) {
-            return getLength();
-        } else if (getBodyEnd() != -1) {
-            return getBodyEnd() - getBodyStart();
-        }
-        return -1;
-    }
-
-    public int getLength() {
-        return length;
-    }
-
-    public int getBodyStart() {
-        return bodyStart;
-    }
-
-    public int getBodyEnd() {
-        return bodyEnd;
-    }
-
-    public void setBodyEnd(int bodyEnd) {
-        this.bodyEnd = bodyEnd;
-    }
-
-    public ByteBuffer getBuffer() {
-        return buffer;
-    }
-
-    public ByteBuffer getBodyBuffer() {
-        ByteBuffer result = buffer.duplicate();
-        result.position(bodyStart);
-
-        int end = getBodyEnd();
-        if (end >= bodyStart) {
-            result.limit(end);
-        }
-
-        return result;
-    }
-
-    public boolean isEOC() {
-        return length == 0 && tag.isEOC();
-    }
-
-    public boolean isDefinitiveLength() {
-        return length != -1;
-    }
-
-    public byte[] readBodyBytes() {
-        ByteBuffer bodyBuffer = getBodyBuffer();
-        byte[] result = new byte[bodyBuffer.remaining()];
-        bodyBuffer.get(result);
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/DecodingUtil.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/DecodingUtil.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/DecodingUtil.java
new file mode 100644
index 0000000..ef39540
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/DecodingUtil.java
@@ -0,0 +1,88 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1;
+
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
+import org.apache.kerby.asn1.type.Asn1Collection;
+import org.apache.kerby.asn1.type.Asn1Constructed;
+import org.apache.kerby.asn1.type.Asn1Object;
+import org.apache.kerby.asn1.type.Asn1Simple;
+import org.apache.kerby.asn1.type.Asn1Type;
+
+import java.io.IOException;
+
+public class DecodingUtil {
+
+    public static Asn1Type decodeValue(Asn1ParsingResult parsingResult) throws IOException {
+        if (Asn1Simple.isSimple(parsingResult.tag())) {
+            return DecodingUtil.decodeValueAsSimple(parsingResult);
+        } else if (Asn1Collection.isCollection(parsingResult.tag())) {
+            return DecodingUtil.decodeValueAsCollection(parsingResult);
+        } else if (!parsingResult.tag().isPrimitive()) {
+            Asn1Object tmpValue = new Asn1Constructed(parsingResult.tag());
+            tmpValue.decode(parsingResult);
+            return tmpValue;
+        } else {
+            throw new IOException("Unknow type of tag=" + parsingResult.tag());
+        }
+    }
+
+    public static Asn1Type decodeValueAsSimple(Asn1ParsingResult parsingResult) throws IOException {
+        Asn1Object value = (Asn1Object) Asn1Simple.createSimple(parsingResult.tagNo());
+        value.useDefinitiveLength(parsingResult.isDefinitiveLength());
+        decodeValueWith(parsingResult, value);
+        return value;
+    }
+
+    public static Asn1Type decodeValueAsCollection(Asn1ParsingResult parsingResult) throws IOException {
+        Asn1Collection value = Asn1Collection.createCollection(parsingResult.tag());
+        value.useDefinitiveLength(parsingResult.isDefinitiveLength());
+        value.setLazy(true);
+        decodeValueWith(parsingResult, value);
+        return value;
+    }
+
+    public static Asn1Type decodeValueAs(Asn1ParsingResult parsingResult,
+                                         Class<? extends Asn1Type> type) throws IOException {
+        Asn1Type value;
+        try {
+            value = type.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException("Invalid type: "
+                + type.getCanonicalName(), e);
+        }
+        decodeValueWith(parsingResult, value);
+        return value;
+    }
+
+    public static void decodeValueWith(Asn1ParsingResult parsingResult, Asn1Type value) throws IOException {
+        value.useDefinitiveLength(parsingResult.isDefinitiveLength());
+        ((Asn1Object) value).decode(parsingResult);
+    }
+
+    public static void decodeValueWith(Asn1ParsingResult parsingResult,
+                                       Asn1Type value, TaggingOption taggingOption) throws IOException {
+        if (!parsingResult.isTagSpecific()) {
+            throw new IllegalArgumentException(
+                "Attempting to decode non-tagged value using tagging way");
+        }
+        ((Asn1Object) value).taggedDecode(parsingResult, taggingOption);
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Container.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Container.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Container.java
new file mode 100644
index 0000000..1e796ec
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Container.java
@@ -0,0 +1,81 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+import org.apache.kerby.asn1.Asn1Dumpable;
+import org.apache.kerby.asn1.Asn1Dumper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * ASN1 constructed types, mainly structured ones, but also some primitive ones.
+ */
+public class Asn1Container
+    extends Asn1ParsingResult implements Asn1Dumpable {
+
+    private List<Asn1ParsingResult> children = new ArrayList<>();
+
+    public Asn1Container(Asn1Header header) {
+        super(header);
+    }
+
+    public Asn1Container(Asn1ParsingResult parsingResult) {
+        super(parsingResult.header);
+    }
+
+    public List<Asn1ParsingResult> getChildren() {
+        return children;
+    }
+
+    public void addItem(Asn1ParsingResult value) {
+        children.add(value);
+    }
+
+    public void clear() {
+        children.clear();
+    }
+
+    @Override
+    public void dumpWith(Asn1Dumper dumper, int indents) {
+        dumper.indent(indents).append(toString()).newLine();
+
+        for (Asn1ParsingResult aObj : children) {
+            dumper.dumpParseResult(indents + 4, aObj).newLine();
+        }
+    }
+
+    @Override
+    public String toString() {
+        String typeStr;
+        if (tag().isUniversal()) {
+            typeStr = tag().universalTag().toStr();
+        } else if (tag().isAppSpecific()) {
+            typeStr = "application " + tagNo();
+        } else {
+            typeStr = "[" + tagNo() + "]";
+        }
+        return typeStr + " ["
+            + "off=" + getOffset()
+            + ", len=" + getHeaderLength() + "+" + getBodyLength()
+            + (isDefinitiveLength() ? "" : "(undefined)")
+            + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Header.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Header.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Header.java
new file mode 100644
index 0000000..bf3177b
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Header.java
@@ -0,0 +1,99 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+import org.apache.kerby.asn1.Tag;
+
+import java.nio.ByteBuffer;
+
+public class Asn1Header {
+    private Tag tag;
+    private int length;
+    private int bodyStart;
+    private int bodyEnd;
+    private ByteBuffer buffer;
+
+    public Asn1Header(Tag tag, int length,
+                      int bodyStart, ByteBuffer buffer) {
+        this.tag = tag;
+        this.length = length;
+        this.bodyStart = bodyStart;
+        this.buffer = buffer;
+
+        this.bodyEnd = isDefinitiveLength() ? bodyStart + length : -1;
+    }
+
+    public Tag getTag() {
+        return tag;
+    }
+
+    public int getActualBodyLength() {
+        if (isDefinitiveLength()) {
+            return getLength();
+        } else if (getBodyEnd() != -1) {
+            return getBodyEnd() - getBodyStart();
+        }
+        return -1;
+    }
+
+    public int getLength() {
+        return length;
+    }
+
+    public int getBodyStart() {
+        return bodyStart;
+    }
+
+    public int getBodyEnd() {
+        return bodyEnd;
+    }
+
+    public void setBodyEnd(int bodyEnd) {
+        this.bodyEnd = bodyEnd;
+    }
+
+    public ByteBuffer getBuffer() {
+        return buffer;
+    }
+
+    public ByteBuffer getBodyBuffer() {
+        ByteBuffer result = buffer.duplicate();
+        result.position(bodyStart);
+
+        int end = getBodyEnd();
+        if (end >= bodyStart) {
+            result.limit(end);
+        }
+
+        return result;
+    }
+
+    public boolean isEOC() {
+        return length == 0 && tag.isEOC();
+    }
+
+    public boolean isDefinitiveLength() {
+        return length != -1;
+    }
+
+    public boolean checkBodyFinished(int pos) {
+        return getBodyEnd() != -1 && pos >= getBodyEnd();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Item.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Item.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Item.java
new file mode 100644
index 0000000..b1f0ef9
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Item.java
@@ -0,0 +1,39 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+public class Asn1Item extends Asn1ParsingResult {
+
+    public Asn1Item(Asn1Header header) {
+        super(header);
+    }
+
+    @Override
+    public String toString() {
+        String valueStr = "##undecoded##";
+        String typeStr = tag().isUniversal() ? tag().universalTag().toStr()
+            : tag().tagClass().name().toLowerCase();
+        return typeStr + " ["
+            + "off=" + getOffset()
+            + ", len=" + getHeaderLength() + "+" + getBodyLength()
+            + "] "
+            + valueStr;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Parser.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Parser.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Parser.java
new file mode 100644
index 0000000..9c79768
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Parser.java
@@ -0,0 +1,88 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+import org.apache.kerby.asn1.Tag;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * ASN1 parser.
+ */
+public class Asn1Parser {
+
+    public static void parse(Asn1Container container) throws IOException {
+        parse(container.header, container);
+    }
+
+    public static void parse(Asn1Header header,
+                      Asn1Container parent) throws IOException {
+        Asn1Reader reader = new Asn1Reader(header.getBuffer());
+        int pos = header.getBodyStart();
+        while (true) {
+            reader.setPosition(pos);
+            Asn1ParsingResult asn1Obj = parse(reader);
+            if (asn1Obj == null) {
+                break;
+            }
+
+            if (parent != null) {
+                parent.addItem(asn1Obj);
+            }
+
+            pos += asn1Obj.getAllLength();
+            if (asn1Obj.isEOC()) {
+                break;
+            }
+
+            if (header.checkBodyFinished(pos)) {
+                break;
+            }
+        }
+
+        header.setBodyEnd(pos);
+    }
+
+    public static Asn1ParsingResult parse(ByteBuffer content) throws IOException {
+        Asn1Reader reader = new Asn1Reader(content);
+        return parse(reader);
+    }
+
+    public static Asn1ParsingResult parse(Asn1Reader reader) throws IOException {
+        if (!reader.available()) {
+            return null;
+        }
+
+        Asn1Header header = reader.readHeader();
+        Tag tmpTag = header.getTag();
+        Asn1ParsingResult parsingResult;
+
+        if (tmpTag.isPrimitive()) {
+            parsingResult = new Asn1Item(header);
+        } else {
+            Asn1Container container = new Asn1Container(header);
+            parse(container);
+            parsingResult = container;
+        }
+
+        return parsingResult;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1ParsingResult.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1ParsingResult.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1ParsingResult.java
new file mode 100644
index 0000000..7be817f
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1ParsingResult.java
@@ -0,0 +1,100 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+import org.apache.kerby.asn1.Tag;
+import org.apache.kerby.asn1.util.Asn1Util;
+
+import java.nio.ByteBuffer;
+
+public abstract class Asn1ParsingResult {
+    protected Asn1Header header;
+
+    public Asn1ParsingResult(Asn1Header header) {
+        this.header = header;
+    }
+
+    public Tag tag() {
+        return header.getTag();
+    }
+
+    public int tagNo() {
+        return tag().tagNo();
+    }
+
+    public boolean isPrimitive() {
+        return tag().isPrimitive();
+    }
+
+    public boolean isUniversal() {
+        return tag().isUniversal();
+    }
+
+    public boolean isAppSpecific() {
+        return tag().isAppSpecific();
+    }
+
+    public boolean isContextSpecific() {
+        return tag().isContextSpecific();
+    }
+
+    public boolean isTagSpecific() {
+        return tag().isSpecific();
+    }
+
+    public boolean isEOC() {
+        return header.isEOC();
+    }
+
+    public Asn1Header getHeader() {
+        return header;
+    }
+
+    public boolean isDefinitiveLength() {
+        return header.isDefinitiveLength();
+    }
+
+    public int getAllLength() {
+        return getHeaderLength() + getBodyLength();
+    }
+
+    public int getHeaderLength() {
+        int bodyLen = getBodyLength();
+        int headerLen = Asn1Util.lengthOfTagLength(header.getTag().tagNo());
+        headerLen += (header.isDefinitiveLength()
+            ? Asn1Util.lengthOfBodyLength(bodyLen) : 1);
+        return headerLen;
+    }
+
+    public int getBodyLength() {
+        return header.getActualBodyLength();
+    }
+
+    protected int getOffset() {
+        return header.getBodyStart() - getHeaderLength();
+    }
+
+    public byte[] readBodyBytes() {
+        ByteBuffer bodyBuffer = header.getBodyBuffer();
+        byte[] result = new byte[bodyBuffer.remaining()];
+        bodyBuffer.get(result);
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Reader.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Reader.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Reader.java
new file mode 100644
index 0000000..555d7a8
--- /dev/null
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/parse/Asn1Reader.java
@@ -0,0 +1,132 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *  
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License. 
+ *  
+ */
+package org.apache.kerby.asn1.parse;
+
+import org.apache.kerby.asn1.Tag;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * ASN1 reader for positional reading.
+ */
+public final class Asn1Reader {
+    private ByteBuffer buffer;
+    private int position;
+
+    public ByteBuffer getBuffer() {
+        return buffer;
+    }
+
+    public Asn1Header readHeader() throws IOException {
+        Tag tag = readTag();
+        int valueLength = readLength();
+        int bodyStart = getPosition();
+        Asn1Header header = new Asn1Header(tag, valueLength,
+            bodyStart, getValueBuffer(valueLength));
+        return header;
+    }
+
+    public Asn1Reader(ByteBuffer buffer) {
+        this.buffer = buffer;
+        this.position = buffer.position();
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public void setPosition(int position) {
+        this.position = position;
+    }
+
+    public boolean available() {
+        return position < buffer.limit();
+    }
+
+    public ByteBuffer getValueBuffer(int valueLength) {
+        return buffer;
+    }
+
+    protected byte readByte() throws IOException {
+        return buffer.get(position++);
+    }
+
+    private Tag readTag() throws IOException {
+        int tagFlags = readTagFlags();
+        int tagNo = readTagNo(tagFlags);
+        return new Tag(tagFlags, tagNo);
+    }
+
+    private int readTagFlags() throws IOException {
+        int tagFlags = readByte() & 0xff;
+        return tagFlags;
+    }
+
+    private int readTagNo(int tagFlags) throws IOException {
+        int tagNo = tagFlags & 0x1f;
+
+        if (tagNo == 0x1f) {
+            tagNo = 0;
+
+            int b = readByte() & 0xff;
+            if ((b & 0x7f) == 0) {
+                throw new IOException("Invalid high tag number found");
+            }
+
+            while (b >= 0 && (b & 0x80) != 0) {
+                tagNo |= b & 0x7f;
+                tagNo <<= 7;
+                b = readByte();
+            }
+
+            tagNo |= b & 0x7f;
+        }
+
+        return tagNo;
+    }
+
+    private int readLength() throws IOException {
+        int result = readByte() & 0xff;
+        if (result == 0x80) {
+            return -1; // non-definitive length
+        }
+
+        if (result > 127) {
+            int length = result & 0x7f;
+            if (length > 4) {
+                throw new IOException("Bad length of more than 4 bytes: " + length);
+            }
+
+            result = 0;
+            int tmp;
+            for (int i = 0; i < length; i++) {
+                tmp = readByte() & 0xff;
+                result = (result << 8) + tmp;
+            }
+        }
+
+        if (result < 0) {
+            throw new IOException("Invalid length " + result);
+        }
+
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
index 7ad58f9..f63481f 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Any.java
@@ -20,8 +20,9 @@
 package org.apache.kerby.asn1.type;
 
 import org.apache.kerby.asn1.Asn1FieldInfo;
-import org.apache.kerby.asn1.Asn1Header;
+import org.apache.kerby.asn1.DecodingUtil;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -31,7 +32,7 @@ import java.nio.ByteBuffer;
  */
 public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
     private Asn1FieldInfo fieldInfo;
-    private Asn1Item field;
+    private Asn1ParsingResult field;
 
     public Asn1Any() {
         super(UniversalTag.ANY);
@@ -42,7 +43,7 @@ public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
         setValue(anyValue);
     }
 
-    public void setField(Asn1Item field) {
+    public void setField(Asn1ParsingResult field) {
         this.field = field;
     }
 
@@ -50,7 +51,7 @@ public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
         this.fieldInfo = fieldInfo;
     }
 
-    public Asn1Type getField() {
+    public Asn1ParsingResult getField() {
         return field;
     }
 
@@ -60,8 +61,8 @@ public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        ((Asn1Object) getValue()).decodeBody(header);
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        ((Asn1Object) getValue()).decodeBody(parsingResult);
     }
 
     @Override
@@ -84,10 +85,10 @@ public class Asn1Any extends AbstractAsn1Type<Asn1Type> {
 
         try {
             if (field.isContextSpecific()) {
-                field.decodeValueWith(result,
+                DecodingUtil.decodeValueWith(field, result,
                     fieldInfo.getTaggingOption());
             } else {
-                field.decodeValueWith(result);
+                DecodingUtil.decodeValueWith(field, result);
             }
         } catch (IOException e) {
             throw new RuntimeException("Fully decoding failed", e);

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
index 7c3d920..929b989 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1BmpString.java
@@ -19,8 +19,8 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 
@@ -61,10 +61,10 @@ public class Asn1BmpString extends Asn1Simple<String> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        if (header.getLength() % 2 != 0) {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        if (parsingResult.getBodyLength() % 2 != 0) {
             throw new IOException("Bad stream, BMP string expecting multiple of 2 bytes");
         }
-        super.decodeBody(header);
+        super.decodeBody(parsingResult);
     }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
index 61b8ef2..af2cc4e 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Boolean.java
@@ -19,8 +19,8 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 
@@ -55,11 +55,11 @@ public class Asn1Boolean extends Asn1Simple<Boolean> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        if (header.getLength() != 1) {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        if (parsingResult.getBodyLength() != 1) {
             throw new IOException("More than 1 byte found for Boolean");
         }
-        super.decodeBody(header);
+        super.decodeBody(parsingResult);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
index dd6d873..260c426 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Choice.java
@@ -19,12 +19,11 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1;
 import org.apache.kerby.asn1.Asn1FieldInfo;
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.EnumType;
 import org.apache.kerby.asn1.TaggingOption;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -75,6 +74,7 @@ public class Asn1Choice extends AbstractAsn1Type<Asn1Type> {
 
     @Override
     public void decode(ByteBuffer content) throws IOException {
+        /*
         int foundPos = -1;
         Asn1Item item = (Asn1Item) Asn1.decode(content);
         for (int i = 0; i < fieldInfos.length; ++i) {
@@ -107,24 +107,22 @@ public class Asn1Choice extends AbstractAsn1Type<Asn1Type> {
             }
         }
         fields[foundPos] = item.getValue();
+        */
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
 
     }
 
-    protected void decodeBody(ByteBuffer content) throws IOException {
-        // Not used
-    }
-
+    /*
     private void initField(int idx) {
         try {
             fields[idx] = fieldInfos[idx].getType().newInstance();
         } catch (Exception e) {
             throw new IllegalArgumentException("Bad field info specified at index of " + idx, e);
         }
-    }
+    }*/
 
     protected <T extends Asn1Type> T getFieldAs(EnumType index, Class<T> t) {
         Asn1Type value = fields[index.getValue()];

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
index 9cbf89a..982c29e 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Collection.java
@@ -23,7 +23,7 @@ import org.apache.kerby.asn1.Tag;
 import org.apache.kerby.asn1.UniversalTag;
 
 /**
- * ASN1 complex type, may be better named.
+ * ASN1 complex type of multiple ASN1 objects.
  */
 public class Asn1Collection extends Asn1Constructed {
 

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
index 5e4bd70..08eb317 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionOf.java
@@ -20,47 +20,36 @@
 package org.apache.kerby.asn1.type;
 
 import org.apache.kerby.asn1.Asn1Dumper;
-import org.apache.kerby.asn1.Asn1Header;
+import org.apache.kerby.asn1.DecodingUtil;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.lang.reflect.ParameterizedType;
-import java.util.ArrayList;
 import java.util.List;
 
 public abstract class Asn1CollectionOf<T extends Asn1Type>
     extends Asn1Collection {
 
-    private List<T> elements = new ArrayList<>();
-
     public Asn1CollectionOf(UniversalTag universalTag) {
         super(universalTag);
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        super.decodeBody(header);
-
-        decodeElements();
-    }
-
-    private void decodeElements() throws IOException {
-        List<Asn1Type> items = getValue();
-        for (Asn1Type itemObj : items) {
-            if (itemObj instanceof Asn1Item) {
-                Asn1Item item = (Asn1Item) itemObj;
-                if (!item.isFullyDecoded()) {
-                    Asn1Type tmpValue = createElement();
-                    item.decodeValueWith(tmpValue);
-                }
-                itemObj = item.getValue();
+    protected void decodeElements() throws IOException {
+        for (Asn1ParsingResult parsingItem : getContainer().getChildren()) {
+            if (parsingItem.isEOC()) {
+                continue;
             }
-            elements.add((T) itemObj);
+
+            Asn1Type tmpValue = createElement();
+            DecodingUtil.decodeValueWith(parsingItem, tmpValue);
+            addItem(tmpValue);
         }
     }
 
     public List<T> getElements() {
-        return elements;
+        return (List<T>) getValue();
     }
 
     public void setElements(List<T> elements) {
@@ -79,7 +68,6 @@ public abstract class Asn1CollectionOf<T extends Asn1Type>
 
     public void addElement(T element) {
         super.addItem(element);
-        this.elements.add(element);
     }
 
     private Class<T> getElementType() {
@@ -102,7 +90,7 @@ public abstract class Asn1CollectionOf<T extends Asn1Type>
     public void dumpWith(Asn1Dumper dumper, int indents) {
         dumper.dumpTypeInfo(indents, getClass());
 
-        for (Asn1Type aObj : elements) {
+        for (Asn1Type aObj : getValue()) {
             dumper.dumpType(indents + 4, aObj).newLine();
         }
     }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
index 81c00fc..2039b08 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1CollectionType.java
@@ -22,10 +22,12 @@ package org.apache.kerby.asn1.type;
 import org.apache.kerby.asn1.Asn1Dumpable;
 import org.apache.kerby.asn1.Asn1Dumper;
 import org.apache.kerby.asn1.Asn1FieldInfo;
-import org.apache.kerby.asn1.Asn1Header;
+import org.apache.kerby.asn1.DecodingUtil;
 import org.apache.kerby.asn1.EnumType;
 import org.apache.kerby.asn1.TaggingOption;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1Container;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -82,45 +84,47 @@ public abstract class Asn1CollectionType
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
         checkAndInitFields();
 
-        Asn1Collection coll = createCollection();
-        coll.setLazy(true);
-        coll.decodeBody(header);
+        Asn1Container container = (Asn1Container) parsingResult;
+        List<Asn1ParsingResult> parsingResults = container.getChildren();
 
         int lastPos = -1, foundPos = -1;
-        List<Asn1Type> decodedItems = coll.getValue();
-        for (Asn1Type itemObj : decodedItems) {
+
+        for (Asn1ParsingResult parsingItem : parsingResults) {
+            if (parsingItem.isEOC()) {
+                continue;
+            }
+
             foundPos = -1;
-            Asn1Item item = (Asn1Item) itemObj;
             for (int i = lastPos + 1; i < fieldInfos.length; ++i) {
-                if (item.isContextSpecific()) {
-                    if (fieldInfos[i].getTagNo() == item.tagNo()) {
+                if (parsingItem.isContextSpecific()) {
+                    if (fieldInfos[i].getTagNo() == parsingItem.tagNo()) {
                         foundPos = i;
                         break;
                     }
-                } else if (fields[i].tag().equals(item.tag())) {
+                } else if (fields[i].tag().equals(parsingItem.tag())) {
                     foundPos = i;
                     break;
                 }
             }
             if (foundPos == -1) {
-                throw new IOException("Unexpected item with tag: " + item.tag());
+                throw new IOException("Unexpected item with tag: " + parsingItem.tag());
             }
             lastPos = foundPos;
 
             AbstractAsn1Type<?> fieldValue = (AbstractAsn1Type<?>) fields[foundPos];
             if (fieldValue instanceof Asn1Any) {
                 Asn1Any any = (Asn1Any) fieldValue;
-                any.setField(item);
+                any.setField(parsingItem);
                 any.setFieldInfo(fieldInfos[foundPos]);
             } else {
-                if (item.isContextSpecific()) {
-                    item.decodeValueWith(fieldValue,
+                if (parsingItem.isContextSpecific()) {
+                    DecodingUtil.decodeValueWith(parsingItem, fieldValue,
                         fieldInfos[foundPos].getTaggingOption());
                 } else {
-                    item.decodeValueWith(fieldValue);
+                    DecodingUtil.decodeValueWith(parsingItem, fieldValue);
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Constructed.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Constructed.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Constructed.java
index dcb51fc..605b445 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Constructed.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Constructed.java
@@ -21,8 +21,10 @@ package org.apache.kerby.asn1.type;
 
 import org.apache.kerby.asn1.Asn1Dumpable;
 import org.apache.kerby.asn1.Asn1Dumper;
-import org.apache.kerby.asn1.Asn1Header;
+import org.apache.kerby.asn1.DecodingUtil;
 import org.apache.kerby.asn1.Tag;
+import org.apache.kerby.asn1.parse.Asn1Container;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -35,6 +37,8 @@ import java.util.List;
 public class Asn1Constructed
     extends AbstractAsn1Type<List<Asn1Type>> implements Asn1Dumpable {
 
+    protected Asn1Container container;
+
     private boolean lazy = false;
 
     public Asn1Constructed(Tag tag) {
@@ -43,6 +47,11 @@ public class Asn1Constructed
         usePrimitive(false);
     }
 
+
+    public Asn1Container getContainer() {
+        return container;
+    }
+
     public void setLazy(boolean lazy) {
         this.lazy = lazy;
     }
@@ -82,20 +91,23 @@ public class Asn1Constructed
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        Asn1ParsingContainer container = new Asn1ParsingContainer(tag());
-        container.decode(header);
-
-        for (Asn1ParsingResult result : container.getParsingResults()) {
-            if (!result.isEOC()) {
-                Asn1Item item = new Asn1Item(result);
-                if (item.isSimple() && !isLazy()) {
-                    item.decodeValueAsSimple();
-                    addItem(item.getValue());
-                } else {
-                    addItem(item);
-                }
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        Asn1Container container = (Asn1Container) parsingResult;
+        this.container = container;
+
+        if (!isLazy()) {
+            decodeElements();
+        }
+    }
+
+    protected void decodeElements() throws IOException {
+        for (Asn1ParsingResult parsingItem : getContainer().getChildren()) {
+            if (parsingItem.isEOC()) {
+                continue;
             }
+
+            Asn1Type tmpValue = DecodingUtil.decodeValue(parsingItem);
+            addItem(tmpValue);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Eoc.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Eoc.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Eoc.java
index f8610ea..ec8c280 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Eoc.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Eoc.java
@@ -19,8 +19,8 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 
@@ -46,8 +46,8 @@ public final class Asn1Eoc extends Asn1Simple<Object> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        if (header.getLength() != 0) {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        if (parsingResult.getBodyLength() != 0) {
             throw new IOException("Unexpected bytes found for EOC");
         }
     }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Item.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Item.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Item.java
deleted file mode 100644
index b3994ff..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Item.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *  
- *    http://www.apache.org/licenses/LICENSE-2.0
- *  
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *  
- */
-package org.apache.kerby.asn1.type;
-
-import org.apache.kerby.asn1.Asn1Factory;
-import org.apache.kerby.asn1.Asn1Header;
-import org.apache.kerby.asn1.Tag;
-import org.apache.kerby.asn1.TaggingOption;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Asn1Item serves two purposes:
- * 1. Wrapping an existing Asn1Type value for Asn1Collection;
- * 2. Wrapping a half decoded value whose body content is left to be decoded
- * later when appropriate.
- * Why not fully decoded at once? Lazy and decode on demand for collection, or
- * impossible due to lacking key parameters, like implicit encoded value for
- * tagged value.
- *
- * For not fully decoded value, you tell your case using isSimple/isCollection/
- * isSpecific/isContextSpecific etc., then call decodeValueAsSimple/
- * decodeValueAsCollection/decodeValueAsImplicitTagged/decodeValueAsExplicitTagged etc.
- * to decode it fully. Or if you have already derived the value holder or
- * the holder type, you can use decodeValueWith or decodeValueAs with your
- * holder or hodler type.
- */
-public class Asn1Item extends AbstractAsn1Type<Asn1Type> {
-    private final Asn1ParsingResult parsingResult;
-
-    public Asn1Item(Asn1ParsingResult parsingResult) {
-        super(parsingResult.tag());
-        this.parsingResult = parsingResult;
-    }
-
-    public Asn1Item(Tag tag, Asn1ParsingResult parsingResult) {
-        super(tag);
-        this.parsingResult = parsingResult;
-    }
-
-    public Asn1ParsingResult getParsingResult() {
-        return parsingResult;
-    }
-
-    @Override
-    protected int encodingBodyLength() {
-        if (getValue() != null) {
-            return ((AbstractAsn1Type<?>) getValue()).encodingBodyLength();
-        }
-        return parsingResult.encodingBodyLength();
-    }
-
-    @Override
-    protected void encodeBody(ByteBuffer buffer) {
-        if (getValue() != null) {
-            ((Asn1Object) getValue()).encodeBody(buffer);
-        }
-    }
-
-    @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        this.parsingResult.decodeBody(header);
-    }
-
-    public boolean isFullyDecoded() {
-        return getValue() != null;
-    }
-
-    public void decodeValueAsSimple() throws IOException {
-        if (getValue() != null) {
-            return;
-        }
-        if (!isSimple()) {
-            throw new IllegalArgumentException(
-                "Attempting to decode non-simple value as simple");
-        }
-
-        Asn1Object value = (Asn1Object) Asn1Factory.create(tagNo());
-        value.useDefinitiveLength(isDefinitiveLength());
-        decodeValueWith(value);
-    }
-
-    public void decodeValueAsCollection() throws IOException {
-        if (getValue() != null) {
-            return;
-        }
-        if (!isCollection()) {
-            throw new IllegalArgumentException(
-                "Attempting to decode non-collection value as collection");
-        }
-
-        Asn1Type value = Asn1Factory.create(tagNo());
-        value.useDefinitiveLength(isDefinitiveLength());
-        decodeValueWith(value);
-    }
-
-    public void decodeValueAs(Class<? extends Asn1Type> type) throws IOException {
-        Asn1Type value;
-        try {
-            value = type.newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException("Invalid type: "
-                + type.getCanonicalName(), e);
-        }
-        decodeValueWith(value);
-    }
-
-    public void decodeValueWith(Asn1Type value) throws IOException {
-        setValue(value);
-        value.useDefinitiveLength(isDefinitiveLength());
-        ((Asn1Object) value).decode(parsingResult.getHeader());
-    }
-
-    public void decodeValueWith(Asn1Type value, TaggingOption taggingOption) throws IOException {
-        if (!isTagSpecific()) {
-            throw new IllegalArgumentException(
-                "Attempting to decode non-tagged value using tagging way");
-        }
-        ((Asn1Object) value).taggedDecode(parsingResult.getHeader(), taggingOption);
-        setValue(value);
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Null.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Null.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Null.java
index 6affb95..87a80b8 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Null.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Null.java
@@ -19,8 +19,8 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 
@@ -46,8 +46,8 @@ public final class Asn1Null extends Asn1Simple<Object> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        if (header.getLength() != 0) {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        if (parsingResult.getHeader().getLength() != 0) {
             throw new IOException("Unexpected bytes found for NULL");
         }
     }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Object.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Object.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Object.java
index 6b4a20c..5bb9854 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Object.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Object.java
@@ -19,12 +19,12 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.Tag;
 import org.apache.kerby.asn1.TaggingOption;
 import org.apache.kerby.asn1.UniversalTag;
-import org.apache.kerby.asn1.util.Asn1Reader;
-import org.apache.kerby.asn1.util.Asn1Reader2;
+import org.apache.kerby.asn1.parse.Asn1Container;
+import org.apache.kerby.asn1.parse.Asn1Parser;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 import org.apache.kerby.asn1.util.Asn1Util;
 
 import java.io.IOException;
@@ -199,7 +199,7 @@ public abstract class Asn1Object implements Asn1Type {
         return tag.isSpecific();
     }
 
-    protected boolean isEOC() {
+    public boolean isEOC() {
         return tag().isEOC();
     }
 
@@ -215,23 +215,20 @@ public abstract class Asn1Object implements Asn1Type {
 
     @Override
     public void decode(ByteBuffer content) throws IOException {
-        Asn1Reader reader = new Asn1Reader2(content);
-        Asn1Header header = reader.readHeader();
-
-        useDefinitiveLength(header.isDefinitiveLength());
-        decode(header);
+        Asn1ParsingResult parsingResult = Asn1Parser.parse(content);
+        decode(parsingResult);
     }
 
-    public void decode(Asn1Header header) throws IOException {
-        if (!tag().equals(header.getTag())) {
-            throw new IOException("Unexpected tag " + header.getTag()
+    public void decode(Asn1ParsingResult parsingResult) throws IOException {
+        if (!tag().equals(parsingResult.tag())) {
+            throw new IOException("Unexpected tag " + parsingResult.tag()
                 + ", expecting " + tag());
         }
 
-        decodeBody(header);
+        decodeBody(parsingResult);
     }
 
-    protected abstract void decodeBody(Asn1Header header) throws IOException;
+    protected abstract void decodeBody(Asn1ParsingResult parsingResult) throws IOException;
 
     protected int taggedEncodingLength(TaggingOption taggingOption) {
         int taggingTagNo = taggingOption.getTagNo();
@@ -242,8 +239,10 @@ public abstract class Asn1Object implements Asn1Type {
         return taggingEncodingLen;
     }
 
+    @Override
     public byte[] taggedEncode(TaggingOption taggingOption) {
-        ByteBuffer byteBuffer = ByteBuffer.allocate(taggedEncodingLength(taggingOption));
+        int len = taggedEncodingLength(taggingOption);
+        ByteBuffer byteBuffer = ByteBuffer.allocate(len);
         taggedEncode(byteBuffer, taggingOption);
         byteBuffer.flip();
         return byteBuffer.array();
@@ -263,6 +262,7 @@ public abstract class Asn1Object implements Asn1Type {
         }
     }
 
+    @Override
     public void taggedDecode(byte[] content,
                              TaggingOption taggingOption) throws IOException {
         taggedDecode(ByteBuffer.wrap(content), taggingOption);
@@ -271,25 +271,24 @@ public abstract class Asn1Object implements Asn1Type {
     @Override
     public void taggedDecode(ByteBuffer content,
                              TaggingOption taggingOption) throws IOException {
-        Asn1Reader reader = new Asn1Reader2(content);
-        Asn1Header header = reader.readHeader();
-
-        useDefinitiveLength(header.isDefinitiveLength());
-        taggedDecode(header, taggingOption);
+        Asn1ParsingResult parsingResult = Asn1Parser.parse(content);
+        taggedDecode(parsingResult, taggingOption);
     }
 
-    protected void taggedDecode(Asn1Header header,
+    public void taggedDecode(Asn1ParsingResult parsingResult,
                                 TaggingOption taggingOption) throws IOException {
         Tag expectedTaggingTagFlags = taggingOption.getTag(!isPrimitive());
-        if (!expectedTaggingTagFlags.equals(header.getTag())) {
-            throw new IOException("Unexpected tag " + header.getTag()
+        if (!expectedTaggingTagFlags.equals(parsingResult.tag())) {
+            throw new IOException("Unexpected tag " + parsingResult.tag()
                     + ", expecting " + expectedTaggingTagFlags);
         }
 
         if (taggingOption.isImplicit()) {
-            decodeBody(header);
+            decodeBody(parsingResult);
         } else {
-            decode(header.getBodyBuffer());
+            Asn1Container container = (Asn1Container) parsingResult;
+            Asn1ParsingResult body = container.getChildren().get(0);
+            decode(body);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1OctetString.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1OctetString.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1OctetString.java
index 88c4f46..3b91459 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1OctetString.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1OctetString.java
@@ -19,8 +19,8 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 
@@ -44,8 +44,8 @@ public class Asn1OctetString extends Asn1Simple<byte[]> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        setValue(header.readBodyBytes());
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        setValue(parsingResult.readBodyBytes());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingContainer.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingContainer.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingContainer.java
deleted file mode 100644
index 3bd5a7f..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingContainer.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *  
- *    http://www.apache.org/licenses/LICENSE-2.0
- *  
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *  
- */
-package org.apache.kerby.asn1.type;
-
-import org.apache.kerby.asn1.Asn1Dumpable;
-import org.apache.kerby.asn1.Asn1Dumper;
-import org.apache.kerby.asn1.Asn1Header;
-import org.apache.kerby.asn1.Tag;
-import org.apache.kerby.asn1.util.Asn1Reader;
-import org.apache.kerby.asn1.util.Asn1Reader2;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * ASN1 constructed types, mainly structured ones, but also some primitive ones.
- */
-public class Asn1ParsingContainer
-    extends Asn1ParsingResult implements Asn1Dumpable {
-
-    private List<Asn1ParsingResult> parsingResults = new ArrayList<>();
-
-    public Asn1ParsingContainer(Tag tag) {
-        super(tag);
-    }
-
-    public Asn1ParsingContainer(Asn1Header header) {
-        super(header);
-        usePrimitive(false);
-    }
-
-    public List<Asn1ParsingResult> getParsingResults() {
-        return parsingResults;
-    }
-
-    public void addItem(Asn1ParsingResult value) {
-        parsingResults.add(value);
-    }
-
-    public void clear() {
-        parsingResults.clear();
-    }
-
-    @Override
-    public void decode(ByteBuffer content) throws IOException {
-        Asn1Reader2 reader = new Asn1Reader2(content);
-        Asn1Header header = reader.readHeader();
-        Tag tmpTag = header.getTag();
-        useDefinitiveLength(header.isDefinitiveLength());
-
-        if (!tag().equals(tmpTag)) {
-            throw new IOException("Unexpected tag " + tmpTag
-                + ", expecting " + tag());
-        }
-
-        decode(header);
-    }
-
-    @Override
-    public void decode(Asn1Header header) throws IOException {
-        this.header = header;
-
-        Asn1Reader reader = new Asn1Reader2(header.getBuffer());
-        int pos = header.getBodyStart();
-        while (true) {
-            reader.setPosition(pos);
-            Asn1ParsingResult asn1Obj = decodeOne(reader);
-            if (asn1Obj == null) {
-                break;
-            }
-
-            pos += asn1Obj.encodingLength();
-            if (asn1Obj.isEOC()) {
-                break;
-            }
-
-            if (header.getBodyEnd() != -1 && pos >= header.getBodyEnd()) {
-                break;
-            }
-        }
-
-        header.setBodyEnd(pos);
-    }
-
-    private Asn1ParsingResult decodeOne(Asn1Reader reader) throws IOException {
-        return decodeOne(reader, this);
-    }
-
-    public static Asn1ParsingResult decodeOne(ByteBuffer content) throws IOException {
-        Asn1Reader reader = new Asn1Reader2(content);
-        return Asn1ParsingContainer.decodeOne(reader, null);
-    }
-
-    public static Asn1ParsingResult decodeOne(Asn1Reader reader,
-                                     Asn1ParsingContainer parent) throws IOException {
-        if (!reader.available()) {
-            return null;
-        }
-
-        Asn1Header header = reader.readHeader();
-        Tag tmpTag = header.getTag();
-        Asn1ParsingResult parsingResult;
-
-        if (tmpTag.isPrimitive()) {
-            parsingResult = new Asn1ParsingItem(header);
-            parsingResult.useDefinitiveLength(header.isDefinitiveLength());
-        } else {
-            Asn1ParsingContainer container = new Asn1ParsingContainer(tmpTag);
-            container.useDefinitiveLength(header.isDefinitiveLength());
-            container.decode(header);
-            parsingResult = container;
-        }
-
-        if (parsingResult != null && parent != null) {
-            parent.addItem(parsingResult);
-        }
-
-        return parsingResult;
-    }
-
-    @Override
-    public void dumpWith(Asn1Dumper dumper, int indents) {
-        dumper.indent(indents).append(toString()).newLine();
-
-        for (Asn1ParsingResult aObj : parsingResults) {
-            dumper.dumpType(indents + 4, aObj).newLine();
-        }
-    }
-
-    @Override
-    public String toString() {
-        String typeStr;
-        if (tag().isUniversal()) {
-            typeStr = tag().universalTag().toStr();
-        } else if (tag().isAppSpecific()) {
-            typeStr = "application " + tagNo();
-        } else {
-            typeStr = "[" + tagNo() + "]";
-        }
-        return typeStr + " ["
-            + "off=" + getOffset()
-            + ", len=" + encodingHeaderLength() + "+" + encodingBodyLength()
-            + (isDefinitiveLength() ? "" : "(undefined)")
-            + "]";
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingItem.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingItem.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingItem.java
deleted file mode 100644
index e7cffa3..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingItem.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *  
- *    http://www.apache.org/licenses/LICENSE-2.0
- *  
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *  
- */
-package org.apache.kerby.asn1.type;
-
-import org.apache.kerby.asn1.Asn1Header;
-
-/**
- * Asn1Item serves two purposes:
- * 1. Wrapping an existing Asn1Type value for Asn1Collection;
- * 2. Wrapping a half decoded value whose body content is left to be decoded
- * later when appropriate.
- * Why not fully decoded at once? Lazy and decode on demand for collection, or
- * impossible due to lacking key parameters.
- */
-public class Asn1ParsingItem extends Asn1ParsingResult {
-
-    public Asn1ParsingItem(Asn1Header header) {
-        super(header);
-    }
-
-    @Override
-    public String toString() {
-        String valueStr = "##undecoded##";
-        String typeStr = tag().isUniversal() ? tag().universalTag().toStr()
-            : tag().tagClass().name().toLowerCase();
-        return typeStr + " ["
-            + "off=" + getOffset()
-            + ", len=" + encodingHeaderLength() + "+" + encodingBodyLength()
-            + "] "
-            + valueStr;
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingResult.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingResult.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingResult.java
deleted file mode 100644
index 004fd02..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1ParsingResult.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *  
- *    http://www.apache.org/licenses/LICENSE-2.0
- *  
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *  
- */
-package org.apache.kerby.asn1.type;
-
-import org.apache.kerby.asn1.Asn1Header;
-import org.apache.kerby.asn1.Tag;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-public abstract class Asn1ParsingResult extends AbstractAsn1Type<Asn1Type> {
-    protected Asn1Header header;
-
-    public Asn1ParsingResult(Tag tag) {
-        super(tag);
-        setValue(this);
-    }
-
-    public Asn1ParsingResult(Asn1Header header) {
-        super(header.getTag());
-        setValue(this);
-        this.header = header;
-    }
-
-    protected ByteBuffer getBodyBuffer() {
-        return header.getBodyBuffer();
-    }
-
-    protected Asn1Header getHeader() {
-        return header;
-    }
-
-    @Override
-    protected int encodingBodyLength() {
-        return header.getActualBodyLength();
-    }
-
-    @Override
-    protected void encodeBody(ByteBuffer buffer) {
-        buffer.put(header.getBodyBuffer());
-    }
-
-    protected int getOffset() {
-        return header.getBodyStart() - encodingHeaderLength();
-    }
-
-    @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        // NOT USED FOR NOW SINCE WE DON'T DECODE THE BODY.
-    }
-
-    @Override
-    public String toString() {
-        String valueStr = "undecoded";
-        if (getValue() != null) {
-            Asn1Type val = getValue();
-            valueStr = (val != null ? val.toString() : "null");
-        }
-        String typeStr = tag().isUniversal() ? tag().universalTag().toStr()
-            : tag().tagClass().name().toLowerCase();
-        return typeStr + " ["
-            + "off=" + getOffset()
-            + ", len=" + encodingHeaderLength() + "+" + encodingBodyLength()
-            + "] "
-            + valueStr;
-    }
-}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Simple.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Simple.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Simple.java
index 6d08f70..1472bc6 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Simple.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Simple.java
@@ -19,9 +19,9 @@
  */
 package org.apache.kerby.asn1.type;
 
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.Tag;
 import org.apache.kerby.asn1.UniversalTag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 import org.apache.kerby.asn1.util.Asn1Util;
 
 import java.io.IOException;
@@ -93,8 +93,8 @@ public abstract class Asn1Simple<T> extends AbstractAsn1Type<T> {
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        byte[] leftBytes = header.readBodyBytes();
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        byte[] leftBytes = parsingResult.readBodyBytes();
         if (leftBytes.length > 0) {
             setBytes(leftBytes);
             toValue();

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Tagging.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Tagging.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Tagging.java
index 8a8ee3b..4a5bddd 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Tagging.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1Tagging.java
@@ -21,8 +21,9 @@ package org.apache.kerby.asn1.type;
 
 import org.apache.kerby.asn1.Asn1Dumpable;
 import org.apache.kerby.asn1.Asn1Dumper;
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.Tag;
+import org.apache.kerby.asn1.parse.Asn1Container;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.lang.reflect.ParameterizedType;
@@ -81,19 +82,21 @@ public class Asn1Tagging<T extends Asn1Type>
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
         Asn1Object value = (Asn1Object) getValue();
         if (isImplicit()) {
-            value.decodeBody(header);
+            value.decodeBody(parsingResult);
         } else {
-            value.decode(header.getBodyBuffer());
+            Asn1Container container = (Asn1Container) parsingResult;
+            Asn1ParsingResult body = container.getChildren().get(0);
+            value.decode(body);
         }
     }
 
     private void initValue() {
         Class<? extends Asn1Type> valueType = (Class<T>) ((ParameterizedType)
                 getClass().getGenericSuperclass()).getActualTypeArguments()[0];
-        AbstractAsn1Type<?> value = null;
+        AbstractAsn1Type<?> value;
         try {
             value = (AbstractAsn1Type<?>) valueType.newInstance();
         } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1TaggingCollection.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1TaggingCollection.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1TaggingCollection.java
index 4d3cbe2..27e0898 100644
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1TaggingCollection.java
+++ b/kerby-asn1/src/main/java/org/apache/kerby/asn1/type/Asn1TaggingCollection.java
@@ -22,9 +22,9 @@ package org.apache.kerby.asn1.type;
 import org.apache.kerby.asn1.Asn1Dumpable;
 import org.apache.kerby.asn1.Asn1Dumper;
 import org.apache.kerby.asn1.Asn1FieldInfo;
-import org.apache.kerby.asn1.Asn1Header;
 import org.apache.kerby.asn1.EnumType;
 import org.apache.kerby.asn1.Tag;
+import org.apache.kerby.asn1.parse.Asn1ParsingResult;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -141,8 +141,8 @@ public abstract class Asn1TaggingCollection
     }
 
     @Override
-    protected void decodeBody(Asn1Header header) throws IOException {
-        tagging.decodeBody(header);
+    protected void decodeBody(Asn1ParsingResult parsingResult) throws IOException {
+        tagging.decodeBody(parsingResult);
     }
 
     protected <T extends Asn1Type> T getFieldAs(EnumType index, Class<T> t) {

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/030234b3/kerby-asn1/src/main/java/org/apache/kerby/asn1/util/Asn1Reader.java
----------------------------------------------------------------------
diff --git a/kerby-asn1/src/main/java/org/apache/kerby/asn1/util/Asn1Reader.java b/kerby-asn1/src/main/java/org/apache/kerby/asn1/util/Asn1Reader.java
deleted file mode 100644
index 2590a48..0000000
--- a/kerby-asn1/src/main/java/org/apache/kerby/asn1/util/Asn1Reader.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License. 
- *
- */
-package org.apache.kerby.asn1.util;
-
-import org.apache.kerby.asn1.Asn1Header;
-import org.apache.kerby.asn1.Tag;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * ASN1 reader from a byte buffer.
- */
-public abstract class Asn1Reader {
-    protected ByteBuffer buffer;
-
-    public Asn1Reader(ByteBuffer buffer) {
-        this.buffer = buffer;
-    }
-
-    public ByteBuffer getBuffer() {
-        return buffer;
-    }
-
-    public Asn1Header readHeader() throws IOException {
-        Tag tag = readTag();
-        int valueLength = readLength();
-        int bodyStart = getPosition();
-        Asn1Header header = new Asn1Header(tag, valueLength,
-            bodyStart, getValueBuffer(valueLength));
-        return header;
-    }
-
-    public abstract void setPosition(int position);
-
-    public abstract int getPosition();
-
-    public abstract boolean available();
-
-    public abstract ByteBuffer getValueBuffer(int valueLength);
-
-    protected abstract byte readByte() throws IOException;
-
-    private Tag readTag() throws IOException {
-        int tagFlags = readTagFlags();
-        int tagNo = readTagNo(tagFlags);
-        return new Tag(tagFlags, tagNo);
-    }
-
-    private int readTagFlags() throws IOException {
-        int tagFlags = readByte() & 0xff;
-        return tagFlags;
-    }
-
-    private int readTagNo(int tagFlags) throws IOException {
-        int tagNo = tagFlags & 0x1f;
-
-        if (tagNo == 0x1f) {
-            tagNo = 0;
-
-            int b = readByte() & 0xff;
-            if ((b & 0x7f) == 0) {
-                throw new IOException("Invalid high tag number found");
-            }
-
-            while (b >= 0 && (b & 0x80) != 0) {
-                tagNo |= b & 0x7f;
-                tagNo <<= 7;
-                b = readByte();
-            }
-
-            tagNo |= b & 0x7f;
-        }
-
-        return tagNo;
-    }
-
-    private int readLength() throws IOException {
-        int result = readByte() & 0xff;
-        if (result == 0x80) {
-            return -1; // non-definitive length
-        }
-
-        if (result > 127) {
-            int length = result & 0x7f;
-            if (length > 4) {
-                throw new IOException("Bad length of more than 4 bytes: " + length);
-            }
-
-            result = 0;
-            int tmp;
-            for (int i = 0; i < length; i++) {
-                tmp = readByte() & 0xff;
-                result = (result << 8) + tmp;
-            }
-        }
-
-        if (result < 0) {
-            throw new IOException("Invalid length " + result);
-        }
-
-        return result;
-    }
-}