Merge remote-tracking branch 'asf/trunk' into kadmin-remote
diff --git a/kerby-common/kerby-asn1/pom.xml b/kerby-common/kerby-asn1/pom.xml
index 8b1e23c..26dbef8 100644
--- a/kerby-common/kerby-asn1/pom.xml
+++ b/kerby-common/kerby-asn1/pom.xml
@@ -26,7 +26,4 @@
   <name>Kerby ASN1 Project</name>
   <description>Kerby ASN1 Project</description>
 
-  <dependencies>
-  </dependencies>
-
 </project>
diff --git a/kerby-common/kerby-asn1/src/main/java/org/apache/kerby/asn1/EnumType.java b/kerby-common/kerby-asn1/src/main/java/org/apache/kerby/asn1/EnumType.java
index e0166ec..5b9a65f 100644
--- a/kerby-common/kerby-asn1/src/main/java/org/apache/kerby/asn1/EnumType.java
+++ b/kerby-common/kerby-asn1/src/main/java/org/apache/kerby/asn1/EnumType.java
@@ -20,7 +20,7 @@
 package org.apache.kerby.asn1;
 
 /**
- * A helper interface used by Asn1Enumerated.
+ * A helper interface for enum types.
  * 
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
diff --git a/kerby-common/kerby-xdr/pom.xml b/kerby-common/kerby-xdr/pom.xml
new file mode 100644
index 0000000..d6ad54c
--- /dev/null
+++ b/kerby-common/kerby-xdr/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed 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. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <parent>
+    <groupId>org.apache.kerby</groupId>
+    <artifactId>kerby-common</artifactId>
+    <version>1.0.0-RC3-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>kerby-xdr</artifactId>
+  <name>Kerby XDR Project</name>
+  <description>Kerby XDR Project</description>
+
+</project>
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/EnumType.java
similarity index 67%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/EnumType.java
index 325f1db..0936863 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/EnumType.java
@@ -6,19 +6,32 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.xdr;
 
-public class KadminTest {
-
+/**
+ * A helper interface for enum types.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public interface EnumType {
+    /**
+     * @return the Enum element value
+     */
+    int getValue();
+    
+    /**
+     * @return The enum element name
+     */
+    String getName();
 }
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrDataType.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrDataType.java
new file mode 100644
index 0000000..34bc014
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrDataType.java
@@ -0,0 +1,55 @@
+/**
+ *  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.xdr;
+
+/**
+ * An enumeration for every XDR type.
+ * 
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+public enum XdrDataType {
+    UNKNOWN             (-1),
+    BOOLEAN             (0x01),
+    INTEGER             (0x02),
+    BYTES               (0x03),
+    STRING              (0X04),
+    ENUM                (0x05),
+    OPAQUE              (0x06),
+    UNSIGNED_INTEGER   (0x07),
+    STRUCT               (0x08),
+    UNION                (0x09);
+
+    /** The dataType value */
+    private int value;
+
+    /**
+     * Create an instance of this class
+     */
+    XdrDataType(int value) {
+        this.value = value;
+    }
+
+    /**
+     * @return The associated dataType value
+     */
+    public int getValue() {
+        return value;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrFieldInfo.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrFieldInfo.java
new file mode 100644
index 0000000..2ab727c
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/XdrFieldInfo.java
@@ -0,0 +1,53 @@
+/**
+ *  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.xdr;
+
+/**
+ * Representing a field in a XDR struct.
+ */
+public class XdrFieldInfo {
+    private int index;
+    private XdrDataType dataType;
+    private Object value;
+
+    /**
+     * Constructor.
+     * @param index
+     * @param dataType
+     *
+     */
+    public XdrFieldInfo(int index, XdrDataType dataType, Object value) {
+        this.index = index;
+        this.dataType = dataType;
+        this.value = value;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public XdrDataType getDataType() {
+        return dataType;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/AbstractXdrType.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/AbstractXdrType.java
new file mode 100644
index 0000000..68facec
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/AbstractXdrType.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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * The abstract XDR type for all the XDR types. It provides basic
+ * encoding and decoding utilities.
+ *
+ * @param <T> the type of the value encoded/decoded or wrapped by this
+ */
+public abstract class AbstractXdrType<T> implements XdrType {
+    private XdrDataType dataType;
+
+    // The wrapped real value.
+    private T value;
+
+    /**
+     * Default constructor.
+     * @param dataType the dataType
+     * @param value the value
+     */
+    public AbstractXdrType(XdrDataType dataType, T value) {
+        this(dataType);
+        this.value = value;
+    }
+
+    /**
+     * Default constructor.
+     * @param dataType the dataType
+     */
+    public AbstractXdrType(XdrDataType dataType) {
+        this.dataType = dataType;
+    }
+
+    @Override
+    public byte[] encode() throws IOException {
+        int len = encodingLength();
+        ByteBuffer byteBuffer = ByteBuffer.allocate(len);
+        encode(byteBuffer);
+        byteBuffer.flip();
+        return byteBuffer.array();
+    }
+
+    @Override
+    public void encode(ByteBuffer buffer) throws IOException {
+        encodeBody(buffer);
+    }
+
+    protected abstract void encodeBody(ByteBuffer buffer) throws IOException;
+
+    @Override
+    public void decode(byte[] content) throws IOException {
+        decode(ByteBuffer.wrap(content));
+    }
+
+    @Override
+    public int encodingLength() throws IOException {
+        return encodingBodyLength();
+    }
+
+    protected abstract int encodingBodyLength() throws IOException;
+
+    @Override
+    public void decode(ByteBuffer content) throws IOException {
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public void setValue(T value) {
+        this.value = value;
+    }
+
+    public XdrDataType getDataType() {
+        return dataType;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBoolean.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBoolean.java
new file mode 100644
index 0000000..e8e092f
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBoolean.java
@@ -0,0 +1,94 @@
+/**
+ *  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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+/**
+ * Xdr Boolean type from RFC 4506
+ * Boolean type has the same representation as signed integers.
+ */
+public class XdrBoolean extends XdrSimple<Boolean> {
+    private static final byte[] TRUE_BYTE = new byte[]
+        {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01};
+    private static final byte[] FALSE_BYTE = new byte[]
+        {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
+
+    public static final XdrBoolean TRUE = new XdrBoolean(true);
+    public static final XdrBoolean FALSE = new XdrBoolean(false);
+
+    /**
+     * Default constructor, generally for decoding as a container
+     */
+    public XdrBoolean() {
+        this(null);
+    }
+
+    /**
+     * Constructor with a value, generally for encoding of the value
+     * @param value The boolean value
+     */
+    public XdrBoolean(Boolean value) {
+        super(XdrDataType.BOOLEAN, value);
+    }
+
+    /**
+     * The length of a signed integer is 4.
+     * @return Length of a boolean type.
+     */
+    @Override
+    protected int encodingBodyLength() {
+        return 4;
+    }
+
+    /**
+     * Encode boolean type to bytes.
+     */
+    @Override
+    protected void toBytes() {
+        setBytes(getValue() ? TRUE_BYTE : FALSE_BYTE);
+    }
+
+    /**
+     * Decode bytes to boolean value.
+     * @throws IOException Wrong bytes for boolean.
+     */
+    @Override
+    protected void toValue() throws IOException {
+        if (getBytes().length != 4) {
+            byte[] boolBytes = ByteBuffer.allocate(4).put(getBytes(), 0, 4).array();
+            /**reset bytes in case the enum type is in a struct or union*/
+            setBytes(boolBytes);
+        }
+
+        byte[] bytes = getBytes();
+        if (Arrays.equals(bytes, TRUE_BYTE)) {
+            setValue(true);
+        } else if (Arrays.equals(bytes, FALSE_BYTE)) {
+            setValue(false);
+        } else {
+            throw new IOException("Fail to decode boolean type: " + bytes.toString());
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBytes.java
similarity index 66%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBytes.java
index 325f1db..105ff74 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrBytes.java
@@ -17,8 +17,29 @@
  *  under the License. 
  *  
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.xdr.type;
 
-public class KadminTest {
+import org.apache.kerby.xdr.XdrDataType;
 
+import java.io.IOException;
+
+public class XdrBytes extends XdrSimple<byte[]> {
+
+    public XdrBytes() {
+        this(null);
+    }
+
+    public XdrBytes(byte[] value) {
+        super(XdrDataType.BYTES, value);
+    }
+
+    @Override
+    protected void toValue() throws IOException {
+
+    }
+
+    @Override
+    protected void toBytes() {
+
+    }
 }
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrEnumerated.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrEnumerated.java
new file mode 100644
index 0000000..e04b484
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrEnumerated.java
@@ -0,0 +1,66 @@
+/**
+ *  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.xdr.type;
+
+import org.apache.kerby.xdr.EnumType;
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+
+public abstract class XdrEnumerated<T extends EnumType> extends XdrSimple<T> {
+    /**
+     * Default constructor, generally for decoding as a container
+     */
+    public XdrEnumerated() {
+        this(null);
+    }
+
+    /**
+     * Constructor with a value, generally for encoding of the value
+     * @param value The Enum value
+     */
+    public XdrEnumerated(T value) {
+        super(XdrDataType.ENUM, value);
+    }
+
+    protected void toBytes() {
+        byte[] bytes = ByteBuffer.allocate(4).putInt(getValue().getValue()).array();
+        setBytes(bytes);
+    }
+
+    protected void toValue() {
+        if (getBytes().length != 4) {
+            byte[] intBytes = ByteBuffer.allocate(4).put(getBytes(), 0, 4).array();
+            /**reset bytes in case the enum type is in a struct or union*/
+            setBytes(intBytes);
+        }
+        BigInteger biVal = new BigInteger(getBytes());
+        int iVal = biVal.intValue();
+        EnumType[] allValues = getAllEnumValues();
+        for (EnumType val : allValues) {
+            if (val.getValue() == iVal) {
+                setValue((T) val);
+            }
+        }
+    }
+
+    protected abstract EnumType[] getAllEnumValues();
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrInteger.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrInteger.java
new file mode 100644
index 0000000..478cf80
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrInteger.java
@@ -0,0 +1,85 @@
+/**
+ *  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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+import java.nio.ByteBuffer;
+
+/**
+ * Xdr Integer type from RFC 4506
+ * An XDR signed integer is a 32-bit datum
+ * that encodes an integer in the range [-2147483648,2147483647].
+ * The integer is represented in two's complement notation.
+ * The most and least significant bytes are0 and 3, respectively.
+ * Integers are declared as follows:
+ * int identifier;
+ *
+ *      (MSB)                   (LSB)
+ *      +-------+-------+-------+-------+
+ *      |byte 0 |byte 1 |byte 2 |byte 3 |
+ *      +-------+-------+-------+-------+
+ *      <------------32 bits------------>
+ */
+public class XdrInteger extends XdrSimple<Integer> {
+    public XdrInteger() {
+        this((Integer) null);
+    }
+
+    public XdrInteger(Integer value) {
+        super(XdrDataType.INTEGER, value);
+    }
+
+    /**
+     * The length of a signed integer is 4.
+     * @return Length of a signed integer type.
+     */
+    @Override
+    protected int encodingBodyLength() {
+        return 4; /**Length of XdrInteger is fixed as 4 bytes*/
+    }
+
+    /**
+     * Encode Integer type to bytes.
+     * Cannot only use toByteArray() because of fixed 4 bytes length.
+     */
+    @Override
+    protected void toBytes() {
+        int value = getValue().intValue();
+        ByteBuffer buffer = ByteBuffer.allocate(4);
+        buffer.putInt(value);
+        buffer.flip();
+        setBytes(buffer.array());
+    }
+
+    /**
+     * Decode bytes to Integer value.
+     */
+    @Override
+    protected void toValue() {
+        if (getBytes().length != 4) {
+            byte[] intBytes = ByteBuffer.allocate(4).put(getBytes(), 0, 4).array();
+            /**reset bytes in case the enum type is in a struct or union*/
+            setBytes(intBytes);
+        }
+        ByteBuffer buffer = ByteBuffer.wrap(getBytes());
+        setValue(buffer.getInt());
+    }
+
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrSimple.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrSimple.java
new file mode 100644
index 0000000..a3e13b1
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrSimple.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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Xdr simple type, of single value other than complex type of multiple values.
+ * Including: Bytes, Integer, Boolean, String.
+ * Use toBytes() for encoding, toValue() for decoding.
+ */
+public abstract class XdrSimple<T> extends AbstractXdrType<T> {
+    private byte[] bytes;
+
+    /**
+     * Default constructor, generally for decoding as a value container
+     * @param dataTypeNo The dataType number
+     */
+    public XdrSimple(XdrDataType dataTypeNo) {
+        this(dataTypeNo, null);
+    }
+
+    /**
+     * Constructor with a value, generally for encoding of the value
+     * @param xdrDataType The dataType number
+     * @param value The value
+     */
+    public XdrSimple(XdrDataType xdrDataType, T value) {
+        super(xdrDataType, value);
+    }
+
+    protected byte[] getBytes() {
+        return bytes;
+    }
+
+    protected void setBytes(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    protected byte[] encodeBody() throws IOException {
+        if (bytes == null) {
+            /**Terminal step for encoding all the simple type to bytes.*/
+            toBytes();
+        }
+        return bytes;
+    }
+
+    /**
+     * Put encoded bytes into buffer.
+     * @param buffer ByteBuffer to hold encoded bytes.
+     */
+    @Override
+    protected void encodeBody(ByteBuffer buffer) throws IOException {
+        byte[] body = encodeBody();
+        if (body != null) {
+            buffer.put(body);
+        }
+    }
+
+    /**
+     * Length including null bytes to maintain an multiple of 4.
+     * @return
+     */
+    @Override
+    protected int encodingBodyLength() throws IOException {
+        if (getValue() == null) {
+            return 0;
+        }
+        if (bytes == null) {
+            /**Terminal step for decoding all the simple type to bytes.*/
+            toBytes();
+        }
+        return bytes.length;
+    }
+
+    @Override
+    public void decode(ByteBuffer content) throws IOException {
+        decodeBody(content);
+    }
+
+    protected void decodeBody(ByteBuffer body) throws IOException {
+        byte[] result = body.array();
+        if (result.length > 0) {
+            setBytes(result);
+            /**Terminal step for decoding all the bytes into simple types.*/
+            toValue();
+        }
+    }
+
+    /**
+     * Decode bytes to simple value.
+     */
+    protected abstract void toValue() throws IOException;
+
+    /**
+     * Encode simple type to bytes.
+     */
+    protected abstract void toBytes() throws IOException;
+
+    public static boolean isSimple(XdrDataType dataType) {
+        switch (dataType) {
+            case BOOLEAN:
+            case INTEGER:
+            case UNSIGNED_INTEGER:
+            case ENUM:
+            case STRING:
+                return true;
+            default:
+                return false;
+        }
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrString.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrString.java
new file mode 100644
index 0000000..32b2302
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrString.java
@@ -0,0 +1,346 @@
+/**
+ *  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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ *  From RFC 4506 :
+ *
+ *  0     1     2     3     4     5   ...
+ *        +-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
+ *        |        length n       |byte0|byte1|...| n-1 |  0  |...|  0  |
+ *        +-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
+ *        |<-------4 bytes------->|<------n bytes------>|<---r bytes--->|
+ *                                |<----n+r (where (n+r) mod 4 = 0)---->|
+ *                                                                 STRING
+ */
+public class XdrString extends XdrSimple<String> {
+    private int padding;
+
+    public XdrString() {
+        this((String) null);
+    }
+
+    public XdrString(String value) {
+        super(XdrDataType.STRING, value);
+    }
+
+    @Override
+    protected void toBytes() {
+        if (getValue() != null) {
+            /**Default value of byte is 0. So we don't have to initialize it with 0*/
+            byte[] bytes = new byte[encodingBodyLength()];
+            int length = bytes.length - padding - 4;
+            bytes[0] = (byte) (length >> 24);
+            bytes[1] = (byte) (length >> 16);
+            bytes[2] = (byte) (length >> 8);
+            bytes[3] = (byte) (length);
+            System.arraycopy(getValue().getBytes(), 0, bytes, 4, length);
+            setBytes(bytes);
+        }
+    }
+
+    @Override
+    protected int encodingBodyLength() {
+        if (getValue() != null) {
+            padding = (4 - getValue().length() % 4) % 4;
+            return getValue().length() + padding + 4;
+        }
+        return 0;
+    }
+
+    protected void toValue() throws IOException {
+        byte[] bytes = getBytes();
+        byte[] header = new byte[4];
+        System.arraycopy(bytes, 0, header, 0, 4);
+        int stringLen  = ByteBuffer.wrap(header).getInt();
+        int paddingBytes = (4 - (stringLen % 4)) % 4;
+        validatePaddingBytes(paddingBytes);
+        setPadding(paddingBytes);
+
+        if (bytes.length != stringLen + 4 + paddingBytes) {
+            int totalLength = stringLen + paddingBytes + 4;
+            byte[] stringBytes = ByteBuffer.allocate(totalLength).put(getBytes(),
+                0, totalLength).array();
+            /**reset bytes in case the enum type is in a struct or union*/
+            setBytes(stringBytes);
+        }
+
+        byte[] content = new byte[stringLen];
+        if (bytes.length > 1) {
+            System.arraycopy(bytes, 4, content, 0, stringLen);
+        }
+        setValue(new String(content, StandardCharsets.US_ASCII));
+    }
+
+    public void setPadding(int padding) {
+        this.padding = padding;
+    }
+
+    public int getPadding() {
+        return padding;
+    }
+
+    public static String fromUTF8ByteArray(byte[] bytes) {
+        int i = 0;
+        int length = 0;
+
+        while (i < bytes.length) {
+            length++;
+            if ((bytes[i] & 0xf0) == 0xf0) {
+                // surrogate pair
+                length++;
+                i += 4;
+            } else if ((bytes[i] & 0xe0) == 0xe0) {
+                i += 3;
+            } else if ((bytes[i] & 0xc0) == 0xc0) {
+                i += 2;
+            } else {
+                i += 1;
+            }
+        }
+
+        char[] cs = new char[length];
+        i = 0;
+        length = 0;
+
+        while (i < bytes.length) {
+            char ch;
+
+            if ((bytes[i] & 0xf0) == 0xf0) {
+                int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i + 1] & 0x3F) << 12)
+                        | ((bytes[i + 2] & 0x3F) << 6) | (bytes[i + 3] & 0x3F);
+                int u = codePoint - 0x10000;
+                char w1 = (char) (0xD800 | (u >> 10));
+                char w2 = (char) (0xDC00 | (u & 0x3FF));
+                cs[length++] = w1;
+                ch = w2;
+                i += 4;
+            } else if ((bytes[i] & 0xe0) == 0xe0) {
+                ch = (char) (((bytes[i] & 0x0f) << 12)
+                        | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f));
+                i += 3;
+            } else if ((bytes[i] & 0xd0) == 0xd0) {
+                ch = (char) (((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
+                i += 2;
+            } else if ((bytes[i] & 0xc0) == 0xc0) {
+                ch = (char) (((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f));
+                i += 2;
+            } else {
+                ch = (char) (bytes[i] & 0xff);
+                i += 1;
+            }
+
+            cs[length++] = ch;
+        }
+
+        return new String(cs);
+    }
+
+    public static byte[] toUTF8ByteArray(String string) {
+        return toUTF8ByteArray(string.toCharArray());
+    }
+
+    public static byte[] toUTF8ByteArray(char[] string) {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        try {
+            toUTF8ByteArray(string, bOut);
+        } catch (IOException e) {
+            throw new IllegalStateException("cannot encode string to byte array!");
+        }
+
+        return bOut.toByteArray();
+    }
+
+    public static void toUTF8ByteArray(char[] string, OutputStream sOut) throws IOException {
+        char[] c = string;
+        int i = 0;
+
+        while (i < c.length) {
+            char ch = c[i];
+
+            if (ch < 0x0080) {
+                sOut.write(ch);
+            } else if (ch < 0x0800) {
+                sOut.write(0xc0 | (ch >> 6));
+                sOut.write(0x80 | (ch & 0x3f));
+            } else if (ch >= 0xD800 && ch <= 0xDFFF) {
+                // in error - can only happen, if the Java String class has a
+                // bug.
+                if (i + 1 >= c.length) {
+                    throw new IllegalStateException("invalid UTF-16 codepoint");
+                }
+                char w1 = ch;
+                ch = c[++i];
+                char w2 = ch;
+                // in error - can only happen, if the Java String class has a
+                // bug.
+                if (w1 > 0xDBFF) {
+                    throw new IllegalStateException("invalid UTF-16 codepoint");
+                }
+                int codePoint = ((w1 & 0x03FF) << 10) | (w2 & 0x03FF) + 0x10000;
+                sOut.write(0xf0 | (codePoint >> 18));
+                sOut.write(0x80 | ((codePoint >> 12) & 0x3F));
+                sOut.write(0x80 | ((codePoint >> 6) & 0x3F));
+                sOut.write(0x80 | (codePoint & 0x3F));
+            } else {
+                sOut.write(0xe0 | (ch >> 12));
+                sOut.write(0x80 | ((ch >> 6) & 0x3F));
+                sOut.write(0x80 | (ch & 0x3F));
+            }
+
+            i++;
+        }
+    }
+
+    /**
+     * A locale independent version of toUpperCase.
+     *
+     * @param string input to be converted
+     * @return a US Ascii uppercase version
+     */
+    public static String toUpperCase(String string) {
+        boolean changed = false;
+        char[] chars = string.toCharArray();
+
+        for (int i = 0; i != chars.length; i++) {
+            char ch = chars[i];
+            if ('a' <= ch && 'z' >= ch) {
+                changed = true;
+                chars[i] = (char) (ch - 'a' + 'A');
+            }
+        }
+
+        if (changed) {
+            return new String(chars);
+        }
+
+        return string;
+    }
+
+    /**
+     * A locale independent version of toLowerCase.
+     *
+     * @param string input to be converted
+     * @return a US ASCII lowercase version
+     */
+    public static String toLowerCase(String string) {
+        boolean changed = false;
+        char[] chars = string.toCharArray();
+
+        for (int i = 0; i != chars.length; i++) {
+            char ch = chars[i];
+            if ('A' <= ch && 'Z' >= ch) {
+                changed = true;
+                chars[i] = (char) (ch - 'A' + 'a');
+            }
+        }
+
+        if (changed) {
+            return new String(chars);
+        }
+
+        return string;
+    }
+
+    public static byte[] toByteArray(char[] chars) {
+        byte[] bytes = new byte[chars.length];
+
+        for (int i = 0; i != bytes.length; i++) {
+            bytes[i] = (byte) chars[i];
+        }
+
+        return bytes;
+    }
+
+    public static byte[] toByteArray(String string) {
+        byte[] bytes = new byte[string.length()];
+
+        for (int i = 0; i != bytes.length; i++) {
+            char ch = string.charAt(i);
+
+            bytes[i] = (byte) ch;
+        }
+
+        return bytes;
+    }
+
+    /**
+     * Convert an array of 8 bit characters into a string.
+     *
+     * @param bytes 8 bit characters.
+     * @return resulting String.
+     */
+    public static String fromByteArray(byte[] bytes) {
+        return new String(asCharArray(bytes));
+    }
+
+    /**
+     * Do a simple conversion of an array of 8 bit characters into a string.
+     *
+     * @param bytes 8 bit characters.
+     * @return resulting String.
+     */
+    public static char[] asCharArray(byte[] bytes) {
+        char[] chars = new char[bytes.length];
+
+        for (int i = 0; i != chars.length; i++) {
+            chars[i] = (char) (bytes[i] & 0xff);
+        }
+
+        return chars;
+    }
+
+    public static String[] split(String input, char delimiter) {
+        List<String> v = new ArrayList<String>();
+        boolean moreTokens = true;
+        String subString;
+
+        while (moreTokens) {
+            int tokenLocation = input.indexOf(delimiter);
+            if (tokenLocation > 0) {
+                subString = input.substring(0, tokenLocation);
+                v.add(subString);
+                input = input.substring(tokenLocation + 1);
+            } else {
+                moreTokens = false;
+                v.add(input);
+            }
+        }
+
+        return v.toArray(new String[v.size()]);
+    }
+
+    private void validatePaddingBytes(int paddingBytes) throws IOException {
+        if (paddingBytes < 0 || paddingBytes > 3) {
+            throw new IOException("Bad padding number: " + paddingBytes + ", should be in [0, 3]");
+        }
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrStructType.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrStructType.java
new file mode 100644
index 0000000..6bb74a5
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrStructType.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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * For collection type that may consist of dataTypeged fields
+ */
+public abstract class XdrStructType extends AbstractXdrType<XdrStructType> {
+    private XdrFieldInfo[] fieldInfos;
+    private XdrType[] fields;
+
+    public XdrStructType(XdrDataType xdrDataType) {
+        super(xdrDataType);
+        this.fieldInfos = null;
+        this.fields = null;
+    }
+
+    public XdrStructType(XdrDataType xdrDataType,
+                         final XdrFieldInfo[] fieldInfos) {
+        super(xdrDataType);
+        this.fieldInfos = fieldInfos;
+        this.fields = new XdrType[fieldInfos.length];
+
+        getStructTypeInstance(this.fields, fieldInfos);
+    }
+
+    protected abstract void getStructTypeInstance(final XdrType[] fields, final XdrFieldInfo[] fieldInfos);
+
+    public XdrFieldInfo[] getXdrFieldInfos() {
+        return fieldInfos;
+    }
+
+    @Override
+    protected int encodingBodyLength() throws IOException {
+        int allLen = 0;
+        for (int i = 0; i < fields.length; ++i) {
+            AbstractXdrType field = (AbstractXdrType) fields[i];
+            if (field != null) {
+                allLen += field.encodingLength();
+            }
+        }
+        return allLen;
+    }
+
+    @Override
+    protected void encodeBody(ByteBuffer buffer) throws IOException {
+        for (int i = 0; i < fields.length; ++i) {
+            XdrType field = fields[i];
+            if (field != null) {
+                field.encode(buffer);
+            }
+        }
+    }
+
+    @Override
+    public void decode(ByteBuffer content) throws IOException {
+        AbstractXdrType[] fields = getAllFields();
+        Object[] value;
+        for (int i = 0; i < fields.length; i++) {
+            if (fields[i] != null) {
+                fields[i].decode(content);
+                int length = fields[i].encodingLength();
+                byte[] array = content.array();
+                byte[] newArray = new byte[array.length - length];
+                System.arraycopy(array, length, newArray, 0, array.length - length);
+                content = ByteBuffer.wrap(newArray);
+            }
+        }
+        this.fields = fields;
+        setValue(fieldsToValues(fields));
+    }
+
+    protected abstract XdrStructType fieldsToValues(AbstractXdrType[] fields);
+
+    protected abstract AbstractXdrType[] getAllFields();
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrType.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrType.java
new file mode 100644
index 0000000..6840e59
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrType.java
@@ -0,0 +1,62 @@
+/**
+ *  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.xdr.type;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * The ASN1 type interface for all ASN1 types.
+ */
+public interface XdrType {
+
+    /**
+     * Get length of encoding bytes by just calculating without real encoding.
+     * Generally it's called to prepare for the encoding buffer.
+     * @return length of encoding bytes
+     */
+    int encodingLength() throws IOException;
+
+    /**
+     * Encode the type, by recursively.
+     * @return encoded bytes
+     */
+    byte[] encode() throws IOException;
+
+    /**
+     * Encode the type, by recursively, using the provided buffer.
+     * @param buffer The byte buffer
+     */
+    void encode(ByteBuffer buffer) throws IOException;
+
+    /**
+     * Decode the content bytes into this type.
+     * @param content The content bytes
+     * @throws IOException e
+     */
+    void decode(byte[] content) throws IOException;
+
+    /**
+     * Decode the content bytes into this type.
+     * @param content The content bytes
+     * @throws IOException e
+     */
+    void decode(ByteBuffer content) throws IOException;
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnion.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnion.java
new file mode 100644
index 0000000..b7dc59c
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnion.java
@@ -0,0 +1,131 @@
+/**
+ *  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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * A discriminated union is a type composed of a discriminant followed
+ * by a type selected from a set of prearranged types according to the
+ * value of the discriminant.  The type of discriminant is either "int",
+ * "unsigned int", or an enumerated type, such as "bool".  The component
+ * types are called "arms" of the union and are preceded by the value of
+ * the discriminant that implies their encoding.  Discriminated unions
+ * are declared as follows:
+ *
+ *  union switch (discriminant-declaration) {
+ *      case discriminant-value-A:
+ *          arm-declaration-A;
+ *      case discriminant-value-B:
+ *          arm-declaration-B;
+ *      ...
+ *      default: default-declaration;
+ *  } identifier;
+ * Each "case" keyword is followed by a legal value of the discriminant.
+ * The default arm is optional.  If it is not specified, then a valid
+ * encoding of the union cannot take on unspecified discriminant values.
+ * The size of the implied arm is always a multiple of four bytes.
+ *
+ * The discriminated union is encoded as its discriminant followed by
+ * the encoding of the implied arm.
+ *                  0   1   2   3
+ *                  +---+---+---+---+---+---+---+---+
+ *                  |  discriminant |  implied arm  |
+ *                  +---+---+---+---+---+---+---+---+
+ *                  |<---4 bytes--->|
+ */
+public abstract class XdrUnion extends AbstractXdrType<XdrUnion> {
+    /**
+     * [0] is the discriminant
+     *      index, XdrDataType, value;
+     * [1] is the implied arm
+     */
+    private XdrFieldInfo[] fieldInfos;
+    private XdrType[] fields;
+
+    public XdrUnion(XdrDataType xdrDataType) {
+        super(xdrDataType);
+        this.fieldInfos = null;
+        this.fields = null;
+    }
+
+    public XdrUnion(XdrDataType xdrDataType,
+                         final XdrFieldInfo[] fieldInfos) {
+        super(xdrDataType);
+        this.fieldInfos = fieldInfos;
+        this.fields = new XdrType[fieldInfos.length];
+
+        getUnionInstance(this.fields, fieldInfos);
+    }
+
+    protected abstract void getUnionInstance(final XdrType[] fields, final XdrFieldInfo[] fieldInfos);
+
+    public XdrFieldInfo[] getXdrFieldInfos() {
+        return fieldInfos;
+    }
+
+    @Override
+    protected int encodingBodyLength() throws IOException {
+        int allLen = 0;
+        for (int i = 0; i < fields.length; i++) {
+            AbstractXdrType field = (AbstractXdrType) fields[i];
+            if (field != null) {
+                allLen += field.encodingLength();
+            }
+        }
+        return allLen;
+    }
+
+    @Override
+    protected void encodeBody(ByteBuffer buffer) throws IOException {
+        for (int i = 0; i < fields.length; ++i) {
+            XdrType field = fields[i];
+            if (field != null) {
+                field.encode(buffer);
+            }
+        }
+    }
+
+    @Override
+    public void decode(ByteBuffer content) throws IOException {
+        AbstractXdrType[] fields = getAllFields();
+        Object[] value;
+        for (int i = 0; i < fields.length; i++) {
+            if (fields[i] != null) {
+                fields[i].decode(content);
+                int length = fields[i].encodingLength();
+                byte[] array = content.array();
+                byte[] newArray = new byte[array.length - length];
+                System.arraycopy(array, length, newArray, 0, array.length - length);
+                content = ByteBuffer.wrap(newArray);
+            }
+        }
+        this.fields = fields;
+        setValue(fieldsToValues(fields));
+    }
+
+    protected abstract XdrUnion fieldsToValues(AbstractXdrType[] fields);
+
+    protected abstract AbstractXdrType[] getAllFields();
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnsignedInteger.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnsignedInteger.java
new file mode 100644
index 0000000..ad1df69
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/type/XdrUnsignedInteger.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.xdr.type;
+
+import org.apache.kerby.xdr.XdrDataType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Xdr Unsigned Integer type from RFC 4506
+ * An XDR unsigned integer is a 32-bit datum that encodes
+ * a non-negative integer in the range [0,4294967295].
+ * It is represented by an unsigned binary number whose most
+ * and least significant bytes are 0 and 3, respectively.
+ * An unsigned integer is declared as follows:
+ * unsigned int identifier;
+ *
+ *      (MSB)                   (LSB)
+ *      +-------+-------+-------+-------+
+ *      |byte 0 |byte 1 |byte 2 |byte 3 |
+ *      +-------+-------+-------+-------+
+ *      <------------32 bits------------>
+ */
+public class XdrUnsignedInteger extends XdrSimple<Long> {
+    public XdrUnsignedInteger() {
+        this((Long) null);
+    }
+
+    public XdrUnsignedInteger(String value) {
+        this(Long.valueOf(value));
+    }
+
+    public XdrUnsignedInteger(Long value) {
+        super(XdrDataType.UNSIGNED_INTEGER, value);
+    }
+
+    /**
+     * The length of an unsigned integer is 4.
+     * @return Length of a unsigned integer type.
+     */
+    @Override
+    protected int encodingBodyLength() {
+        return 4; /**Length of XdrInteger is fixed as 4 bytes*/
+    }
+
+    /**
+     * Encode Unsigned Integer type to bytes.
+     */
+    @Override
+    protected void toBytes() throws IOException {
+        Long value = getValue();
+        validateUnsignedInteger(value); /**Check whether the long value is valid unsigned int*/
+        ByteBuffer buffer = ByteBuffer.allocate(8);
+        buffer.putLong(value);
+        byte[] bytes = new byte[4]; /**The encoding length is 4*/
+        System.arraycopy(buffer.array(), 4, bytes, 0, 4);
+        setBytes(bytes);
+    }
+
+    private void validateUnsignedInteger(Long value) throws IOException {
+        if (value < 0 || value > 4294967295L) {
+            throw new IOException("Invalid unsigned integer: " + value);
+        }
+    }
+
+    /**
+     * Decode bytes to Unsigned Integer value.
+     */
+    @Override
+    protected void toValue() {
+        if (getBytes().length != 4) {
+            byte[] bytes = ByteBuffer.allocate(4).put(getBytes(), 0, 4).array();
+            setBytes(bytes); /**reset bytes in case the enum type is in a struct or union*/
+        }
+
+        byte[] longBytes = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00};
+        System.arraycopy(getBytes(), 0, longBytes, 4, 4);
+        ByteBuffer buffer = ByteBuffer.wrap(longBytes);
+        setValue(buffer.getLong());
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/HexUtil.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/HexUtil.java
new file mode 100644
index 0000000..70a2b1c
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/HexUtil.java
@@ -0,0 +1,113 @@
+/**
+ *  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.xdr.util;
+
+/**
+ * This is only for test, be careful when use in production codes.
+ */
+public class HexUtil {
+
+    static final String HEX_CHARS_STR = "0123456789ABCDEF";
+    static final char[] HEX_CHARS = HEX_CHARS_STR.toCharArray();
+
+    /**
+     * Convert bytes into friendly format as:
+     * 0x02 02 00 80
+     */
+    public static String bytesToHexFriendly(byte[] bytes) {
+        int len = bytes.length * 2;
+        len += bytes.length; // for ' ' appended for each char
+        len += 2; // for '0x' prefix
+        char[] hexChars = new char[len];
+        hexChars[0] = '0';
+        hexChars[1] = 'x';
+        for (int j = 0; j < bytes.length; j++) {
+            int v = bytes[j] & 0xFF;
+            hexChars[j * 3 + 2] = HEX_CHARS[v >>> 4];
+            hexChars[j * 3 + 3] = HEX_CHARS[v & 0x0F];
+            hexChars[j * 3 + 4] = ' ';
+        }
+
+        return new String(hexChars);
+    }
+
+    /**
+     * Convert friendly hex string like follows into byte array
+     * 0x02 02 00 80
+     */
+    public static byte[] hex2bytesFriendly(String hexString) {
+        hexString = hexString.toUpperCase();
+        String hexStr = hexString;
+        if (hexString.startsWith("0X")) {
+            hexStr = hexString.substring(2);
+        }
+        String[] hexParts = hexStr.split(" ");
+
+        byte[] bytes = new byte[hexParts.length];
+        char[] hexPart;
+        for (int i = 0; i < hexParts.length; ++i) {
+            hexPart = hexParts[i].toCharArray();
+            if (hexPart.length != 2) {
+                throw new IllegalArgumentException("Invalid hex string to convert");
+            }
+            bytes[i] = (byte) ((HEX_CHARS_STR.indexOf(hexPart[0]) << 4)
+                + HEX_CHARS_STR.indexOf(hexPart[1]));
+        }
+
+        return bytes;
+    }
+
+    /**
+     * Convert bytes into format as:
+     * 02020080
+     * @param bytes The bytes
+     * @return The hex string
+     */
+    public static String bytesToHex(byte[] bytes) {
+        int len = bytes.length * 2;
+        char[] hexChars = new char[len];
+        for (int j = 0; j < bytes.length; j++) {
+            int v = bytes[j] & 0xFF;
+            hexChars[j * 2] = HEX_CHARS[v >>> 4];
+            hexChars[j * 2 + 1] = HEX_CHARS[v & 0x0F];
+        }
+
+        return new String(hexChars);
+    }
+
+    /**
+     * Convert hex string like follows into byte array
+     * 02020080
+     * @param hexString The hex string
+     * @return The bytes
+     */
+    public static byte[] hex2bytes(String hexString) {
+        hexString = hexString.toUpperCase();
+        int len = hexString.length() / 2;
+        byte[] bytes = new byte[len];
+        char[] hexChars = hexString.toCharArray();
+        for (int i = 0, j = 0; i < len; ++i) {
+            bytes[i] = (byte) ((HEX_CHARS_STR.indexOf(hexChars[j++]) << 4)
+                + HEX_CHARS_STR.indexOf(hexChars[j++]));
+        }
+
+        return bytes;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/IOUtil.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/IOUtil.java
new file mode 100644
index 0000000..2136511
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/IOUtil.java
@@ -0,0 +1,109 @@
+/**
+ *  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.xdr.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Some IO and file related utilities.
+ */
+public final class IOUtil {
+    private IOUtil() { }
+
+    public static byte[] readInputStream(InputStream in) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int length = 0;
+        while ((length = in.read(buffer)) != -1) {
+            baos.write(buffer, 0, length);
+        }
+        in.close();
+        return baos.toByteArray();
+    }
+
+    public static void readInputStream(InputStream in,
+                                       byte[] buf) throws IOException {
+        int toRead = buf.length;
+        int off = 0;
+        while (toRead > 0) {
+            int ret = in.read(buf, off, toRead);
+            if (ret < 0) {
+                throw new IOException("Bad inputStream, premature EOF");
+            }
+            toRead -= ret;
+            off += ret;
+        }
+        in.close();
+    }
+
+    /**
+     * Read an input stream and return the content as string assuming UTF8.
+     * @param in The input stream
+     * @return The content
+     * @throws IOException e
+     */
+    public static String readInput(InputStream in) throws IOException {
+        byte[] content = readInputStream(in);
+        return Utf8.toString(content);
+    }
+
+    /**
+     * Read a file and return the content as string assuming UTF8.
+     * @param file The file to read
+     * @return The content
+     * @throws IOException e
+     */
+    public static String readFile(File file) throws IOException {
+        long len = 0;
+        if (file.length() >= Integer.MAX_VALUE) {
+            throw new IOException("Too large file, unexpected!");
+        } else {
+            len = file.length();
+        }
+        byte[] buf = new byte[(int) len];
+
+        InputStream is = new FileInputStream(file);
+        readInputStream(is, buf);
+
+        return Utf8.toString(buf);
+    }
+
+    /**
+     * Write a file with the content assuming UTF8.
+     * @param content The content
+     * @param file The file to write
+     * @throws IOException e
+     */
+    public static void writeFile(String content, File file) throws IOException {
+        FileOutputStream outputStream = new FileOutputStream(file);
+        FileChannel fc = outputStream.getChannel();
+
+        ByteBuffer buffer = ByteBuffer.wrap(Utf8.toBytes(content));
+        fc.write(buffer);
+        outputStream.close();
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/Utf8.java
similarity index 67%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/Utf8.java
index 325f1db..374c16f 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/Utf8.java
@@ -6,19 +6,29 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.xdr.util;
 
-public class KadminTest {
+import java.nio.charset.StandardCharsets;
 
+public final class Utf8 {
+    private Utf8() { }
+
+    public static String toString(byte[] bytes) {
+        return new String(bytes, StandardCharsets.UTF_8);
+    }
+
+    public static byte[] toBytes(String s) {
+        return s.getBytes(StandardCharsets.UTF_8);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/XdrUtil.java
similarity index 88%
rename from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
rename to kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/XdrUtil.java
index 325f1db..880bbb0 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-common/kerby-xdr/src/main/java/org/apache/kerby/xdr/util/XdrUtil.java
@@ -17,8 +17,10 @@
  *  under the License. 
  *  
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.xdr.util;
 
-public class KadminTest {
-
+public final class XdrUtil {
+    private XdrUtil() {
+        
+    }
 }
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/TestUtil.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/TestUtil.java
new file mode 100644
index 0000000..6a3d9e8
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/TestUtil.java
@@ -0,0 +1,47 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.util.HexUtil;
+import org.apache.kerby.xdr.util.IOUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public final class TestUtil {
+    private TestUtil() {
+
+    }
+
+    static byte[] readBytesFromTxtFile(String resource) throws IOException {
+        String hexStr = readStringFromTxtFile(resource);
+        return HexUtil.hex2bytes(hexStr);
+    }
+
+    static String readStringFromTxtFile(String resource) throws IOException {
+        InputStream is = TestUtil.class.getResourceAsStream(resource);
+        return IOUtil.readInput(is);
+    }
+
+    static byte[] readBytesFromBinFile(String resource) throws IOException {
+        InputStream is = TestUtil.class.getResourceAsStream(resource);
+        return IOUtil.readInputStream(is);
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrBooleanTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrBooleanTest.java
new file mode 100644
index 0000000..0d7b0f2
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrBooleanTest.java
@@ -0,0 +1,60 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrBoolean;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrBooleanTest {
+    @Test
+    public void testEncoding() throws IOException {
+        testEncodingWith(true, "0x00 00 00 01");
+        testEncodingWith(false, "0x00 00 00 00");
+        //what about undefined codeBytes?
+    }
+
+    private void testEncodingWith(Boolean value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        XdrBoolean aValue = new XdrBoolean(value);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+    @Test
+    public void testDecoding() throws IOException {
+        testDecodingWith(true, "0x00 00 00 01");
+        testDecodingWith(false, "0x00 00 00 00");
+        //what about undefined codeBytes?
+    }
+
+    private void testDecodingWith(Boolean expectedValue, String content) throws IOException {
+        XdrBoolean decoded = new XdrBoolean();
+
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+        assertThat(decoded.getValue()).isEqualTo(expectedValue);
+    }
+
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedInstance.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedInstance.java
new file mode 100644
index 0000000..e35d03c
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedInstance.java
@@ -0,0 +1,56 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrEnumerated;
+
+enum Color implements EnumType {
+    RED(2),
+    YELLOW(3),
+    BLUE(5);
+    int value;
+    Color(int value) {
+        this.value = value;
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public String getName() {
+        return name();
+    }
+}
+
+public class XdrEnumeratedInstance extends XdrEnumerated<Color> {
+
+    public XdrEnumeratedInstance() {
+        super(null);
+    }
+
+    public XdrEnumeratedInstance(Color value) {
+        super(value);
+    }
+    @Override
+    protected EnumType[] getAllEnumValues() {
+        return Color.values();
+    }
+
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedTest.java
new file mode 100644
index 0000000..2a3096a
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrEnumeratedTest.java
@@ -0,0 +1,58 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrEnumerated;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrEnumeratedTest {
+    @Test
+    public void testEncoding() throws IOException {
+        testEncodingWith(Color.RED, "0x00 00 00 02");
+        testEncodingWith(Color.YELLOW, "0x00 00 00 03");
+        testEncodingWith(Color.BLUE, "0x00 00 00 05");
+    }
+
+    private void testEncodingWith(Color value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        XdrEnumerated aValue = new XdrEnumeratedInstance(value);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+
+    @Test
+    public void testDecoding() throws IOException {
+        testDecodingWith(Color.RED, "0x00 00 00 02");
+        testDecodingWith(Color.YELLOW, "0x00 00 00 03");
+        testDecodingWith(Color.BLUE, "0x00 00 00 05");
+    }
+
+    private void testDecodingWith(Color expectedValue, String content) throws IOException {
+        XdrEnumerated decoded = new XdrEnumeratedInstance();
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+        assertThat(decoded.getValue()).isEqualTo(expectedValue);
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrIntegerTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrIntegerTest.java
new file mode 100644
index 0000000..9246f12
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrIntegerTest.java
@@ -0,0 +1,80 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrInteger;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrIntegerTest {
+    @Test
+    public void testEncoding() throws IOException {
+        testEncodingWith(0, "0x00 00 00 00");
+        testEncodingWith(1, "0x00 00 00 01");
+        testEncodingWith(2, "0x00 00 00 02");
+        testEncodingWith(127, "0x00 00 00 7F");
+        testEncodingWith(128, "0x00 00 00 80");
+        testEncodingWith(-1, "0xFF FF FF FF");
+        testEncodingWith(-127, "0xFF FF FF 81");
+        testEncodingWith(-255, "0xFF FF FF 01");
+        testEncodingWith(-32768, "0xFF FF 80 00");
+        testEncodingWith(1234567890, "0x49 96 02 D2");
+        testEncodingWith(2147483647, "0x7F FF FF FF");
+        testEncodingWith(-2147483647, "0x80 00 00 01");
+        testEncodingWith(-2147483648, "0x80 00 00 00");
+    }
+
+    private void testEncodingWith(int value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        XdrInteger aValue = new XdrInteger(value);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+
+    @Test
+    public void testDecoding() throws IOException {
+        testDecodingWith(0, "0x00 00 00 00");
+        testDecodingWith(1, "0x00 00 00 01");
+        testDecodingWith(2, "0x00 00 00 02");
+        testDecodingWith(127, "0x00 00 00 7F");
+        testDecodingWith(128, "0x00 00 00 80");
+        testDecodingWith(-1, "0xFF FF FF FF");
+        testDecodingWith(-127, "0xFF FF FF 81");
+        testDecodingWith(-255, "0xFF FF FF 01");
+        testDecodingWith(-32768, "0xFF FF 80 00");
+        testDecodingWith(1234567890, "0x49 96 02 D2");
+        testDecodingWith(2147483647, "0x7F FF FF FF");
+        testDecodingWith(-2147483647, "0x80 00 00 01");
+        testDecodingWith(-2147483648, "0x80 00 00 00");
+    }
+
+    private void testDecodingWith(int expectedValue, String content) throws IOException {
+        XdrInteger decoded = new XdrInteger();
+
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+        assertThat(decoded.getValue().intValue()).isEqualTo(expectedValue);
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStringTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStringTest.java
new file mode 100644
index 0000000..8bb30a4
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStringTest.java
@@ -0,0 +1,59 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrString;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrStringTest {
+        @Test
+        public void testEncoding() throws IOException {
+            testEncodingWith("Hello, Kerby!", "0X00 00 00 0D 48 65 6C 6C 6F 2C 20 4B 65 72 62 79 21 00 00 00");
+            testEncodingWith("sillyprog", "0X00 00 00 09 73 69 6C 6C 79 70 72 6F 67 00 00 00");
+            testEncodingWith("(quit)", "0X00 00 00 06 28 71 75 69 74 29 00 00");
+        }
+
+        private void testEncodingWith(String value, String expectedEncoding) throws IOException {
+            byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+            XdrString aValue = new XdrString(value);
+
+            byte[] encodingBytes = aValue.encode();
+            assertThat(encodingBytes).isEqualTo(expected);
+        }
+
+
+        @Test
+        public void testDecoding() throws IOException {
+            testDecodingWith("Hello, Kerby!", "0X00 00 00 0D 48 65 6C 6C 6F 2C 20 4B 65 72 62 79 21 00 00 00");
+            testDecodingWith("sillyprog", "0X00 00 00 09 73 69 6c 6c 79 70 72 6f 67 00 00 00");
+            testDecodingWith("(quit)", "0X00 00 00 06 28 71 75 69 74 29 00 00");
+        }
+
+        private void testDecodingWith(String expectedValue, String content) throws IOException {
+            XdrString decoded = new XdrString();
+            decoded.decode(HexUtil.hex2bytesFriendly(content));
+            assertThat(decoded.getValue()).isEqualTo(expectedValue);
+        }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeInstance.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeInstance.java
new file mode 100644
index 0000000..37c5196
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeInstance.java
@@ -0,0 +1,111 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.AbstractXdrType;
+import org.apache.kerby.xdr.type.XdrBoolean;
+import org.apache.kerby.xdr.type.XdrInteger;
+import org.apache.kerby.xdr.type.XdrString;
+import org.apache.kerby.xdr.type.XdrStructType;
+import org.apache.kerby.xdr.type.XdrType;
+import org.apache.kerby.xdr.type.XdrUnion;
+import org.apache.kerby.xdr.type.XdrUnsignedInteger;
+
+class MyFile {
+    String fileName;
+    UnionFileTypeSwitch fileType;
+    String owner;
+
+    MyFile(String name, UnionFileTypeSwitch fileType, String owner) {
+        this.fileName = name;
+        this.fileType = fileType;
+        this.owner = owner;
+    }
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public UnionFileTypeSwitch getFileType() {
+        return fileType;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+}
+
+public class XdrStructTypeInstance extends XdrStructType {
+    public XdrStructTypeInstance() {
+        super(XdrDataType.STRUCT);
+    }
+
+    public XdrStructTypeInstance(XdrFieldInfo[] fieldInfos) {
+        super(XdrDataType.STRUCT, fieldInfos);
+    }
+
+    protected  void getStructTypeInstance(final XdrType[] fields, final XdrFieldInfo[] fieldInfos) {
+        for (int i = 0; i < fieldInfos.length; i++) {
+            switch (fieldInfos[i].getDataType()) {
+                case INTEGER:
+                    fields[i] = new XdrInteger((Integer) fieldInfos[i].getValue());
+                    break;
+                case UNSIGNED_INTEGER:
+                    fields[i] = new XdrUnsignedInteger((Long) fieldInfos[i].getValue());
+                    break;
+                case BOOLEAN:
+                    fields[i] = new XdrBoolean((Boolean) fieldInfos[i].getValue());
+                    break;
+                case ENUM:
+                    fields[i] = new FileKindEnumeratedInstance((FileKind) fieldInfos[i].getValue());
+                    break;
+                case UNION:
+                    fields[i] = (XdrUnion) fieldInfos[i].getValue();
+                    break;
+                case STRING:
+                    fields[i] = new XdrString((String) fieldInfos[i].getValue());
+                    break;
+                case STRUCT:
+                    fields[i] = new XdrStructTypeInstance((XdrFieldInfo[]) fieldInfos[i].getValue());
+                default:
+                    fields[i] = null;
+            }
+        }
+
+    }
+
+    @Override
+    protected XdrStructType fieldsToValues(AbstractXdrType[] fields) {
+        XdrFieldInfo[] fieldInfos = {new XdrFieldInfo(0, XdrDataType.STRING, fields[0].getValue()),
+                new XdrFieldInfo(1, XdrDataType.UNION, fields[1].getValue()),
+                new XdrFieldInfo(2, XdrDataType.STRING, fields[2].getValue())};
+        return new XdrStructTypeInstance(fieldInfos);
+    }
+
+    @Override
+    protected AbstractXdrType[] getAllFields() {
+        AbstractXdrType[] fields = new AbstractXdrType[3];
+        fields[0] = new XdrString();
+        fields[1] = new XdrUnionInstance();
+        fields[2] = new XdrString();
+        return fields;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeTest.java
new file mode 100644
index 0000000..c41a20b
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrStructTypeTest.java
@@ -0,0 +1,83 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrStructType;
+import org.apache.kerby.xdr.type.XdrUnion;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrStructTypeTest {
+    @Test
+    public void testEncoding() throws IOException {
+        MyFile file = new MyFile("sillyprog", new UnionFileTypeSwitch(FileKind.EXEC), "john");
+        testEncodingWith(file, "0x00 00 00 09 73 69 6c 6c 79 70 72 6f 67 00 00 00 00 00 00 02 "
+                + "00 00 00 04 6c 69 73 70 00 00 00 04 6a 6f 68 6e");
+    }
+
+    private void testEncodingWith(MyFile value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        UnionFileTypeSwitch fileType = value.getFileType();
+        XdrFieldInfo[] unionFieldInfo = {new XdrFieldInfo(0, fileType.getFileKind(), fileType.getFileValue()),
+                new XdrFieldInfo(1, fileType.getArmKind(), fileType.getArmValue())};
+        XdrFieldInfo[] fieldInfos = {new XdrFieldInfo(0, XdrDataType.STRING, value.getFileName()),
+                new XdrFieldInfo(1, XdrDataType.UNION, new XdrUnionInstance(unionFieldInfo)),
+                new XdrFieldInfo(2, XdrDataType.STRING, value.getOwner())};
+
+        XdrStructType aValue = new XdrStructTypeInstance(fieldInfos);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+
+    @Test
+    public void testDecoding() throws IOException {
+        MyFile file = new MyFile("sillyprog", new UnionFileTypeSwitch(FileKind.EXEC), "john");
+        testDecodingWith(file, "0x00 00 00 09 73 69 6c 6c 79 70 72 6f 67 00 00 00 00 00 00 02 "
+                + "00 00 00 04 6c 69 73 70 00 00 00 04 6a 6f 68 6e");
+    }
+
+    private void testDecodingWith(MyFile expectedValue, String content) throws IOException {
+        XdrStructType decoded = new XdrStructTypeInstance();
+
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+
+        XdrFieldInfo[] fieldInfos = decoded.getValue().getXdrFieldInfos();
+        assertThat(fieldInfos.length).isEqualTo(3);
+        assertThat(fieldInfos[0].getDataType()).isEqualTo(XdrDataType.STRING);
+        assertThat((String) fieldInfos[0].getValue()).isEqualTo(expectedValue.getFileName());
+
+        assertThat(fieldInfos[1].getDataType()).isEqualTo(XdrDataType.UNION);
+        XdrFieldInfo[] unionFieldInfo = ((XdrUnion) fieldInfos[1].getValue()).getXdrFieldInfos();
+        assertThat(unionFieldInfo[0].getDataType()).isEqualTo(expectedValue.getFileType().getFileKind());
+        assertThat(unionFieldInfo[0].getValue()).isEqualTo(expectedValue.getFileType().getFileValue());
+        assertThat(unionFieldInfo[1].getDataType()).isEqualTo(expectedValue.getFileType().getArmKind());
+        assertThat((String) unionFieldInfo[1].getValue()).isEqualTo(expectedValue.getFileType().getArmValue());
+
+        assertThat(fieldInfos[2].getDataType()).isEqualTo(XdrDataType.STRING);
+        assertThat((String) fieldInfos[2].getValue()).isEqualTo(expectedValue.getOwner());
+    }
+
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionInstance.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionInstance.java
new file mode 100644
index 0000000..fbc13b7
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionInstance.java
@@ -0,0 +1,170 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.AbstractXdrType;
+import org.apache.kerby.xdr.type.XdrBoolean;
+import org.apache.kerby.xdr.type.XdrEnumerated;
+import org.apache.kerby.xdr.type.XdrInteger;
+import org.apache.kerby.xdr.type.XdrString;
+import org.apache.kerby.xdr.type.XdrType;
+import org.apache.kerby.xdr.type.XdrUnion;
+import org.apache.kerby.xdr.type.XdrUnsignedInteger;
+
+enum FileKind implements EnumType {
+    TEXT,
+    DATA,
+    EXEC;
+
+    public int getValue() {
+        return ordinal();
+    }
+
+    public String getName() {
+        return name();
+    }
+}
+
+class FileKindEnumeratedInstance extends XdrEnumerated<FileKind> {
+
+    FileKindEnumeratedInstance() {
+        super(null);
+    }
+
+    FileKindEnumeratedInstance(FileKind value) {
+        super(value);
+    }
+    @Override
+    protected EnumType[] getAllEnumValues() {
+        return FileKind.values();
+    }
+
+}
+
+class UnionFileTypeSwitch {
+    FileKind fileKind;
+    Object arm;
+    UnionFileTypeSwitch(FileKind fileKind) {
+        this.fileKind = fileKind;
+        switch (fileKind) {
+            case TEXT:
+                arm = null;
+                break;
+            case DATA:
+                arm = "creator";
+                break;
+            case EXEC:
+                arm = "lisp";
+                break;
+        }
+    }
+
+    XdrDataType getFileKind() {
+        return XdrDataType.ENUM;
+    }
+
+    FileKind getFileValue() {
+        return fileKind;
+    }
+
+    XdrDataType getArmKind() {
+        XdrDataType xdrDataType = XdrDataType.UNKNOWN;
+        switch (fileKind) {
+            case TEXT:
+                xdrDataType = XdrDataType.UNKNOWN;
+                break;
+            case DATA:
+                xdrDataType = XdrDataType.STRING;
+                break;
+            case EXEC:
+                xdrDataType = XdrDataType.STRING;
+                break;
+        }
+        return xdrDataType;
+    }
+
+    Object getArmValue() {
+        return arm;
+    }
+}
+
+public class XdrUnionInstance extends XdrUnion {
+
+    public XdrUnionInstance() {
+        super(XdrDataType.UNION);
+    }
+
+    public XdrUnionInstance(XdrFieldInfo[] fieldInfos) {
+        super(XdrDataType.UNION, fieldInfos);
+    }
+
+
+    @Override
+    protected void getUnionInstance(XdrType[] fields, XdrFieldInfo[] fieldInfos) {
+        switch (fieldInfos[0].getDataType()) {
+            case INTEGER:
+                fields[0] = new XdrInteger((Integer) fieldInfos[0].getValue());
+                break;
+            case UNSIGNED_INTEGER:
+                fields[0] = new XdrUnsignedInteger((Long) fieldInfos[0].getValue());
+                break;
+            case BOOLEAN:
+                fields[0] = new XdrBoolean((Boolean) fieldInfos[0].getValue());
+                break;
+            case ENUM:
+                fields[0] = new FileKindEnumeratedInstance((FileKind) fieldInfos[0].getValue());
+                break;
+            default:
+                throw new RuntimeException("Wrong discriminant type for union: " + fieldInfos[0].getDataType());
+        }
+
+        switch (fieldInfos[1].getDataType()) {
+            case INTEGER:
+                fields[1] = new XdrInteger((Integer) fieldInfos[1].getValue());
+                break;
+            case UNSIGNED_INTEGER:
+                fields[1] = new XdrUnsignedInteger((Long) fieldInfos[1].getValue());
+                break;
+            case BOOLEAN:
+                fields[1] = new XdrBoolean((Boolean) fieldInfos[1].getValue());
+                break;
+            case STRING:
+                fields[1] = new XdrString((String) fieldInfos[1].getValue());
+                break;
+            default:
+                fields[1] = null;
+        }
+    }
+
+    @Override
+    protected XdrUnion fieldsToValues(AbstractXdrType[] fields) {
+        XdrFieldInfo[] fieldInfos = {new XdrFieldInfo(0, XdrDataType.ENUM, fields[0].getValue()),
+                new XdrFieldInfo(1, XdrDataType.STRING, fields[1].getValue())};
+        return new XdrUnionInstance(fieldInfos);
+    }
+
+    @Override
+    protected AbstractXdrType[] getAllFields() {
+        AbstractXdrType[] fields = new AbstractXdrType[2];
+        fields[0] = new FileKindEnumeratedInstance();
+        fields[1] = new XdrString();
+        return fields;
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionTest.java
new file mode 100644
index 0000000..e9b9275
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnionTest.java
@@ -0,0 +1,67 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrUnion;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrUnionTest {
+    @Test
+    public void testEncoding() throws IOException {
+        UnionFileTypeSwitch fileType = new UnionFileTypeSwitch(FileKind.EXEC);
+        testEncodingWith(fileType, "0x00 00 00 02 00 00 00 04 6c 69 73 70");
+    }
+
+    private void testEncodingWith(UnionFileTypeSwitch value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        XdrFieldInfo[] fieldInfos = {new XdrFieldInfo(0, value.getFileKind(), value.getFileValue()),
+                new XdrFieldInfo(1, value.getArmKind(), value.getArmValue())};
+
+        XdrUnion aValue = new XdrUnionInstance(fieldInfos);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+
+    @Test
+    public void testDecoding() throws IOException {
+        UnionFileTypeSwitch fileType = new UnionFileTypeSwitch(FileKind.EXEC);
+        testDecodingWith(fileType, "0x00 00 00 02 00 00 00 04 6c 69 73 70");
+    }
+
+    private void testDecodingWith(UnionFileTypeSwitch expectedValue, String content) throws IOException {
+        XdrUnion decoded = new XdrUnionInstance();
+
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+
+        XdrFieldInfo[] fieldInfos = decoded.getValue().getXdrFieldInfos();
+        assertThat(fieldInfos.length).isEqualTo(2);
+        assertThat(fieldInfos[0].getDataType()).isEqualTo(expectedValue.getFileKind());
+        assertThat((FileKind) fieldInfos[0].getValue()).isEqualTo(expectedValue.getFileValue());
+        assertThat(fieldInfos[1].getDataType()).isEqualTo(expectedValue.getArmKind());
+        assertThat((String) fieldInfos[1].getValue()).isEqualTo(expectedValue.getArmValue());
+    }
+}
diff --git a/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnsignedIntegerTest.java b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnsignedIntegerTest.java
new file mode 100644
index 0000000..118f094
--- /dev/null
+++ b/kerby-common/kerby-xdr/src/test/java/org/apache/kerby/xdr/XdrUnsignedIntegerTest.java
@@ -0,0 +1,69 @@
+/**
+ *  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.xdr;
+
+import org.apache.kerby.xdr.type.XdrUnsignedInteger;
+import org.apache.kerby.xdr.util.HexUtil;
+import org.junit.Test;
+
+import java.io.IOException;
+
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class XdrUnsignedIntegerTest {
+    @Test
+    public void testEncoding() throws IOException {
+        testEncodingWith("0", "0x00 00 00 00");
+        testEncodingWith("1", "0x00 00 00 01");
+        testEncodingWith("2", "0x00 00 00 02");
+        testEncodingWith("1234567890", "0x49 96 02 D2");
+        testEncodingWith("2147483647", "0x7F FF FF FF");
+        testEncodingWith("2147483648", "0x80 00 00 00");
+        testEncodingWith("4294967295", "0xFF FF FF FF");
+    }
+
+    private void testEncodingWith(String value, String expectedEncoding) throws IOException {
+        byte[] expected = HexUtil.hex2bytesFriendly(expectedEncoding);
+        XdrUnsignedInteger aValue = new XdrUnsignedInteger(value);
+
+        byte[] encodingBytes = aValue.encode();
+        assertThat(encodingBytes).isEqualTo(expected);
+    }
+
+
+    @Test
+    public void testDecoding() throws IOException {
+        testDecodingWith("0", "0x00 00 00 00");
+        testDecodingWith("1", "0x00 00 00 01");
+        testDecodingWith("2", "0x00 00 00 02");
+        testDecodingWith("1234567890", "0x49 96 02 D2");
+        testDecodingWith("2147483647", "0x7F FF FF FF");
+        testDecodingWith("2147483648", "0x80 00 00 00");
+        testDecodingWith("4294967295", "0xFF FF FF FF");
+    }
+
+    private void testDecodingWith(String expectedValue, String content) throws IOException {
+        XdrUnsignedInteger decoded = new XdrUnsignedInteger();
+
+        decoded.decode(HexUtil.hex2bytesFriendly(content));
+        assertThat(decoded.getValue().toString()).isEqualTo(expectedValue);
+    }
+}
diff --git a/kerby-common/pom.xml b/kerby-common/pom.xml
index 309fcd7..e2e679b 100644
--- a/kerby-common/pom.xml
+++ b/kerby-common/pom.xml
@@ -29,5 +29,6 @@
     <module>kerby-asn1</module>
     <module>kerby-config</module>
     <module>kerby-util</module>
+    <module>kerby-xdr</module>
   </modules>
 </project>
diff --git a/kerby-dist/kdc-dist/bin/admin-server.cmd b/kerby-dist/kdc-dist/bin/admin-server.cmd
new file mode 100644
index 0000000..091773f
--- /dev/null
+++ b/kerby-dist/kdc-dist/bin/admin-server.cmd
@@ -0,0 +1,32 @@
+@echo off
+@rem  Licensed to the Apache Software Foundation (ASF) under one
+@rem  or more contributor license agreements.  See the NOTICE file
+@rem  distributed with this work for additional information
+@rem  regarding copyright ownership.  The ASF licenses this file
+@rem  to you under the Apache License, Version 2.0 (the
+@rem  "License"); you may not use this file except in compliance
+@rem  with the License.  You may obtain a copy of the License at
+@rem
+@rem    http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem  Unless required by applicable law or agreed to in writing,
+@rem  software distributed under the License is distributed on an
+@rem  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@rem  KIND, either express or implied.  See the License for the
+@rem  specific language governing permissions and limitations
+@rem  under the License.
+@rem
+
+set DEBUG=
+set args=%*
+for %%a in (%*) do (
+  if -D == %%a (
+    set DEBUG=-Xdebug -Xrunjdwp:transport=dt_socket,address=8008,server=y,suspend=n
+    set args=%args:-D=%
+  )
+)
+
+java %DEBUG% ^
+-classpath target\lib\* ^
+-DKERBY_LOGFILE=admin-server ^
+org.apache.kerby.kerberos.kerb.admin.server.KerbyAdminServer %args%
diff --git a/kerby-dist/kdc-dist/bin/admin-server.sh b/kerby-dist/kdc-dist/bin/admin-server.sh
new file mode 100644
index 0000000..fb455f2
--- /dev/null
+++ b/kerby-dist/kdc-dist/bin/admin-server.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# 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.
+
+DEBUG=
+args=
+for var in $*; do
+  if [ X"$var" = X"-D" ]; then
+    DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=8008,server=y,suspend=n"
+  else
+    args="$args $var"
+  fi
+done
+
+java $DEBUG \
+-classpath target/lib/*:. \
+-DKERBY_LOGFILE=admin-server \
+org.apache.kerby.kerberos.kerb.admin.server.KerbyAdminServer $args
\ No newline at end of file
diff --git a/kerby-dist/kdc-dist/bin/remote-admin-client.cmd b/kerby-dist/kdc-dist/bin/remote-admin-client.cmd
new file mode 100644
index 0000000..d8fc483
--- /dev/null
+++ b/kerby-dist/kdc-dist/bin/remote-admin-client.cmd
@@ -0,0 +1,32 @@
+@echo off
+@rem  Licensed to the Apache Software Foundation (ASF) under one
+@rem  or more contributor license agreements.  See the NOTICE file
+@rem  distributed with this work for additional information
+@rem  regarding copyright ownership.  The ASF licenses this file
+@rem  to you under the Apache License, Version 2.0 (the
+@rem  "License"); you may not use this file except in compliance
+@rem  with the License.  You may obtain a copy of the License at
+@rem
+@rem    http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem  Unless required by applicable law or agreed to in writing,
+@rem  software distributed under the License is distributed on an
+@rem  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@rem  KIND, either express or implied.  See the License for the
+@rem  specific language governing permissions and limitations
+@rem  under the License.
+@rem
+
+set DEBUG=
+set args=%*
+for %%a in (%*) do (
+  if -D == %%a (
+    set DEBUG=-Xdebug -Xrunjdwp:transport=dt_socket,address=8009,server=y,suspend=n
+    set args=%args:-D=%
+  )
+)
+
+java %DEBUG% ^
+-classpath target\lib\* ^
+-DKERBY_LOGFILE=remote-admin-client ^
+org.apache.kerby.kerberos.kerb.admin.RemoteAdminClientTool %args%
\ No newline at end of file
diff --git a/kerby-dist/kdc-dist/bin/remote-admin-client.sh b/kerby-dist/kdc-dist/bin/remote-admin-client.sh
new file mode 100644
index 0000000..21b7848
--- /dev/null
+++ b/kerby-dist/kdc-dist/bin/remote-admin-client.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# 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.
+
+DEBUG=
+args=
+for var in $*; do
+  if [ X"$var" = X"-D" ]; then
+    DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=8009,server=y,suspend=n"
+  else
+    args="$args $var"
+  fi
+done
+
+java $DEBUG \
+-classpath target/lib/*:. \
+-DKERBY_LOGFILE=remote-admin-client \
+org.apache.kerby.kerberos.kerb.admin.RemoteAdminClientTool $args
\ No newline at end of file
diff --git a/kerby-dist/kdc-dist/conf/adminClient.conf b/kerby-dist/kdc-dist/conf/adminClient.conf
new file mode 100644
index 0000000..7c6909b
--- /dev/null
+++ b/kerby-dist/kdc-dist/conf/adminClient.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+[libdefaults]
+default_realm = EXAMPLE.COM
+admin_port = 65417
+keytab_file = admin.keytab
+protocol = adminprotocol
+server_name = localhost
diff --git a/kerby-dist/kdc-dist/conf/adminServer.conf b/kerby-dist/kdc-dist/conf/adminServer.conf
new file mode 100644
index 0000000..ecb5bca
--- /dev/null
+++ b/kerby-dist/kdc-dist/conf/adminServer.conf
@@ -0,0 +1,23 @@
+#
+# 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.
+#
+[libdefaults]
+default_realm = EXAMPLE.COM
+admin_port = 65417
+keytab_file = protocol.keytab
+protocol = adminprotocol
+server_name = localhost
diff --git a/kerby-dist/kdc-dist/pom.xml b/kerby-dist/kdc-dist/pom.xml
index 71c5f25..da3a2e1 100644
--- a/kerby-dist/kdc-dist/pom.xml
+++ b/kerby-dist/kdc-dist/pom.xml
@@ -35,6 +35,16 @@
     </dependency>
     <dependency>
       <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-admin-server</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-admin</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
       <artifactId>kerby-asn1</artifactId>
       <version>${project.version}</version>
     </dependency>
diff --git a/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java b/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
index 0bbe7b7..79cc46a 100644
--- a/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
+++ b/kerby-kdc/src/main/java/org/apache/kerby/kerberos/kdc/KerbyKdcServer.java
@@ -21,8 +21,8 @@
 
 import org.apache.kerby.kerberos.kdc.impl.NettyKdcServerImpl;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.server.KdcServer;
 import org.apache.kerby.util.OSUtil;
 
diff --git a/kerby-kerb/kerb-admin-server/pom.xml b/kerby-kerb/kerb-admin-server/pom.xml
new file mode 100644
index 0000000..546f6da
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed 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. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.kerby</groupId>
+    <artifactId>kerby-kerb</artifactId>
+    <version>1.0.0-RC3-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>kerb-admin-server</artifactId>
+
+  <name>Kerby-kerb Admin Server</name>
+  <description>Kerby-kerb Admin Server</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerby-config</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-identity</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-util</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-admin</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/KerbyAdminServer.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/KerbyAdminServer.java
new file mode 100644
index 0000000..50e59e3
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/KerbyAdminServer.java
@@ -0,0 +1,80 @@
+/**
+ *  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.kerberos.kerb.admin.server;
+
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.Krb5Conf;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServer;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+import org.apache.kerby.util.OSUtil;
+
+import java.io.File;
+import java.io.IOException;
+
+public class KerbyAdminServer {
+    private static final String USAGE = (OSUtil.isWindows()
+        ? "Usage: bin\\admin-server.cmd" : "Usage: sh bin/admin-server.sh")
+        + " <conf-file>\n"
+        + "\tExample:\n"
+        + "\t\t"
+        + (OSUtil.isWindows()
+        ? "bin\\admin-server.cmd" : "sh bin/admin-server.sh")
+        + " conf\n";
+
+    public static void main(String[] args) throws Exception {
+
+        if (args.length != 1) {
+            System.err.println(USAGE);
+            System.exit(1);
+        }
+
+        String confDirPath = args[0];
+        AdminServer adminServer = new AdminServer(new File(confDirPath));
+        AdminServerConfig adminServerConfig = adminServer.getAdminServerConfig();
+
+        adminServer.setAdminHost(adminServerConfig.getAdminHost());
+        adminServer.setAllowTcp(true);
+        adminServer.setAllowUdp(false);
+        adminServer.setAdminServerPort(adminServerConfig.getAdminPort());
+
+        KdcConfig kdcConfig = KdcUtil.getKdcConfig(new File(confDirPath));
+        if (kdcConfig == null) {
+            kdcConfig = new KdcConfig();
+        }
+        try {
+            Krb5Conf krb5Conf = new Krb5Conf(new File(confDirPath), kdcConfig);
+            krb5Conf.initKrb5conf();
+        } catch (IOException e) {
+            throw new KrbException("Failed to make krb5.conf", e);
+        }
+
+        try {
+            adminServer.init();
+        } catch (KrbException e) {
+            System.err.println("Errors occurred when start admin server:  " + e.getMessage());
+            System.exit(2);
+        }
+        adminServer.start();
+        System.out.println("Admin server started!");
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServer.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServer.java
new file mode 100644
index 0000000..659750a
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServer.java
@@ -0,0 +1,266 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.impl.DefaultInternalAdminServerImpl;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.impl.InternalAdminServer;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+
+import java.io.File;
+
+/**
+ * The implemented Kerberos remote admin server API.
+ * We add the KdcConfig as a member variable to AdminServer,
+ * In order to make it easy to use LocalKadminImpl.
+ * The Kdc Config of corresponding KDC can be read from ConfDir.
+ */
+public class AdminServer {
+    private final AdminServerConfig adminServerConfig;
+    private final BackendConfig backendConfig;
+    private final KdcConfig kdcConfig;
+    private final AdminServerSetting adminServerSetting;
+    private final KOptions startupOptions;
+
+    private InternalAdminServer innerAdminServer;
+
+    /**
+     * Constructor passing adminServerConfig, kdcConfig and backendConfig.
+     * @param adminServerConfig The admin server config
+     * @param backendConfig The backend config
+     * @param kdcConfig The kdc config
+     * @throws KrbException e
+     */
+    public AdminServer(AdminServerConfig adminServerConfig,
+                     BackendConfig backendConfig, KdcConfig kdcConfig) throws KrbException {
+        this.adminServerConfig = adminServerConfig;
+        this.kdcConfig = kdcConfig;
+        this.backendConfig = backendConfig;
+        startupOptions = new KOptions();
+        adminServerSetting = new AdminServerSetting(startupOptions,
+            adminServerConfig, kdcConfig, backendConfig);
+    }
+
+    /**
+     * Constructor given confDir where 'adminServer.conf', 'kdc.conf' and
+     * 'backend.conf' should be available.
+     * adminServer.conf that contains adminServer related items.
+     * kdc.conf, that contains kdc related items.
+     * backend.conf, that contains identity backend related items.
+     *
+     * @param confDir The conf dir
+     * @throws KrbException e
+     */
+    public AdminServer(File confDir) throws KrbException {
+        AdminServerConfig tmpAdminServerConfig =
+            AdminServerUtil.getAdminServerConfig(confDir);
+        if (tmpAdminServerConfig == null) {
+            tmpAdminServerConfig = new AdminServerConfig();
+        }
+        this.adminServerConfig = tmpAdminServerConfig;
+
+        KdcConfig tmpKdcConfig = AdminServerUtil.getKdcConfig(confDir);
+        if (tmpKdcConfig == null) {
+            tmpKdcConfig = new KdcConfig();
+        }
+        this.kdcConfig = tmpKdcConfig;
+
+        BackendConfig tmpBackendConfig = AdminServerUtil.getBackendConfig(confDir);
+        if (tmpBackendConfig == null) {
+            tmpBackendConfig = new BackendConfig();
+        }
+        tmpBackendConfig.setConfDir(confDir);
+        this.backendConfig = tmpBackendConfig;
+
+        startupOptions = new KOptions();
+        adminServerSetting = new AdminServerSetting(startupOptions,
+            adminServerConfig, kdcConfig, backendConfig);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public AdminServer() {
+        adminServerConfig = new AdminServerConfig();
+        backendConfig = new BackendConfig();
+        kdcConfig = new KdcConfig();
+        startupOptions = new KOptions();
+        adminServerSetting = new AdminServerSetting(startupOptions,
+            adminServerConfig, kdcConfig, backendConfig);
+    }
+
+    /**
+     * Set Admin realm for ticket request
+     * @param realm The kdc realm
+     */
+    public void setAdminServerRealm(String realm) {
+        startupOptions.add(AdminServerOption.ADMIN_REALM, realm);
+    }
+
+    /**
+     * Set Admin host.
+     * @param adminHost The kdc host
+     */
+    public void setAdminHost(String adminHost) {
+        startupOptions.add(AdminServerOption.ADMIN_HOST, adminHost);
+    }
+
+    /**
+     * Set Admin port.
+     * @param adminPort The admin port
+     */
+    public void setAdminServerPort(int adminPort) {
+        startupOptions.add(AdminServerOption.ADMIN_PORT, adminPort);
+    }
+
+    /**
+     * Set Admin tcp port.
+     * @param adminTcpPort The admin tcp port
+     */
+    public void setAdminTcpPort(int adminTcpPort) {
+        startupOptions.add(AdminServerOption.ADMIN_TCP_PORT, adminTcpPort);
+    }
+
+    /**
+     * Set to allow UDP or not.
+     * @param allowUdp true if allow udp
+     */
+    public void setAllowUdp(boolean allowUdp) {
+        startupOptions.add(AdminServerOption.ALLOW_UDP, allowUdp);
+    }
+
+    /**
+     * Set to allow TCP or not.
+     * @param allowTcp true if allow tcp
+     */
+    public void setAllowTcp(boolean allowTcp) {
+        startupOptions.add(AdminServerOption.ALLOW_TCP, allowTcp);
+    }
+    /**
+     * Set Admin udp port. Only makes sense when allowUdp is set.
+     * @param adminUdpPort The admin udp port
+     */
+    public void setAdminUdpPort(int adminUdpPort) {
+        startupOptions.add(AdminServerOption.ADMIN_UDP_PORT, adminUdpPort);
+    }
+
+    /**
+     * Set runtime folder.
+     * @param workDir The work dir
+     */
+    public void setWorkDir(File workDir) {
+        startupOptions.add(AdminServerOption.WORK_DIR, workDir);
+    }
+
+    /**
+     * Allow to debug so have more logs.
+     */
+    public void enableDebug() {
+        startupOptions.add(AdminServerOption.ENABLE_DEBUG);
+    }
+
+    /**
+     * Allow to hook customized admin implementation.
+     *
+     * @param innerAdminServerImpl The inner admin implementation
+     */
+    public void setInnerAdminServerImpl(InternalAdminServer innerAdminServerImpl) {
+        startupOptions.add(AdminServerOption.INNER_ADMIN_IMPL, innerAdminServerImpl);
+    }
+
+    /**
+     * Get Admin setting from startup options and configs.
+     * @return setting
+     */
+    public AdminServerSetting getAdminServerSetting() {
+        return adminServerSetting;
+    }
+
+    /**
+     * Get the Admin config.
+     * @return AdminServerConfig
+     */
+    public AdminServerConfig getAdminServerConfig() {
+        return adminServerConfig;
+    }
+
+    /**
+     * Get backend config.
+     *
+     * @return backend configuration
+     */
+    public BackendConfig getBackendConfig() {
+        return backendConfig;
+    }
+
+    /**
+     * Get identity service.
+     * @return IdentityService
+     */
+    public IdentityBackend getIdentityService() {
+        if (innerAdminServer == null) {
+            throw new RuntimeException("Not init yet");
+        }
+        return innerAdminServer.getIdentityBackend();
+    }
+
+    /**
+     * Initialize.
+     *
+     * @throws KrbException e.
+     */
+    public void init() throws KrbException {
+        if (startupOptions.contains(AdminServerOption.INNER_ADMIN_IMPL)) {
+            innerAdminServer = (InternalAdminServer) startupOptions.getOptionValue(
+                AdminServerOption.INNER_ADMIN_IMPL);
+        } else {
+            innerAdminServer =
+                new DefaultInternalAdminServerImpl(adminServerSetting);
+        }
+
+        innerAdminServer.init();
+    }
+
+    /**
+     * Start the Admin admin.
+     *
+     * @throws KrbException e.
+     */
+    public void start() throws KrbException {
+        if (innerAdminServer == null) {
+            throw new RuntimeException("Not init yet");
+        }
+        innerAdminServer.start();
+    }
+
+    /**
+     * Stop the Admin admin.
+     *
+     * @throws KrbException e.
+     */
+    public void stop() throws KrbException {
+        if (innerAdminServer != null) {
+            innerAdminServer.stop();
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfig.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfig.java
new file mode 100644
index 0000000..97e0904
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfig.java
@@ -0,0 +1,105 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.kerberos.kerb.common.Krb5Conf;
+
+/**
+ * Kerb admin server side configuration API.
+ */
+public class AdminServerConfig extends Krb5Conf {
+    private static final String KDCDEFAULT = "kdcdefaults";
+
+    public boolean enableDebug() {
+        return getBoolean(AdminServerConfigKey.KRB_DEBUG, true, KDCDEFAULT);
+    }
+
+    public String getAdminServiceName() {
+        return getString(AdminServerConfigKey.ADMIN_SERVICE_NAME, true, KDCDEFAULT);
+    }
+
+    public String getAdminHost() {
+        return getString(AdminServerConfigKey.ADMIN_HOST, true, KDCDEFAULT);
+    }
+
+    public int getAdminPort() {
+        Integer kdcPort = getInt(AdminServerConfigKey.ADMIN_PORT, true, KDCDEFAULT);
+        if (kdcPort != null && kdcPort > 0) {
+            return kdcPort.intValue();
+        }
+        return -1;
+    }
+
+    public int getAdminTcpPort() {
+        Integer kdcTcpPort = getInt(AdminServerConfigKey.ADMIN_TCP_PORT, true, KDCDEFAULT);
+        if (kdcTcpPort != null && kdcTcpPort > 0) {
+            return kdcTcpPort.intValue();
+        }
+        return getAdminPort();
+    }
+
+    /**
+     * Is to allow TCP for KDC
+     * @return true to allow TCP, false otherwise
+     */
+    public Boolean allowTcp() {
+        return getBoolean(AdminServerConfigKey.ADMIN_ALLOW_TCP, true, KDCDEFAULT)
+                || getInt(AdminServerConfigKey.ADMIN_TCP_PORT, true, KDCDEFAULT) != null
+            || getInt(AdminServerConfigKey.ADMIN_PORT, false, KDCDEFAULT) != null;
+    }
+
+    /**
+     * Is to allow UDP for KDC
+     * @return true to allow UDP, false otherwise
+     */
+    public Boolean allowUdp() {
+        return getBoolean(AdminServerConfigKey.ADMIN_ALLOW_UDP, true, KDCDEFAULT)
+                || getInt(AdminServerConfigKey.ADMIN_UDP_PORT, true, KDCDEFAULT) != null
+            || getInt(AdminServerConfigKey.ADMIN_PORT, false, KDCDEFAULT) != null;
+    }
+
+    public int getAdminUdpPort() {
+        Integer kdcUdpPort = getInt(AdminServerConfigKey.ADMIN_UDP_PORT, true, KDCDEFAULT);
+        if (kdcUdpPort != null && kdcUdpPort > 0) {
+            return kdcUdpPort.intValue();
+        }
+        return getAdminPort();
+    }
+
+    public String getAdminRealm() {
+        return getString(AdminServerConfigKey.ADMIN_REALM, true, KDCDEFAULT);
+    }
+
+    public String getAdminDomain() {
+        return getString(AdminServerConfigKey.ADMIN_DOMAIN, true, KDCDEFAULT);
+    }
+
+    public String getKeyTabFile() {
+        return getString(AdminServerConfigKey.KEYTAB_FILE, true, KDCDEFAULT);
+    }
+
+    public String getProtocol() {
+        return getString(AdminServerConfigKey.PROTOCOL, true, KDCDEFAULT);
+    }
+
+    public String getServerName() {
+        return getString(AdminServerConfigKey.SERVER_NAME, true, KDCDEFAULT);
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfigKey.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfigKey.java
new file mode 100644
index 0000000..6b71042
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerConfigKey.java
@@ -0,0 +1,59 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.config.ConfigKey;
+
+public enum AdminServerConfigKey implements ConfigKey {
+    KRB_DEBUG(true),
+    ADMIN_SERVICE_NAME("Kadmin-Server"),
+    KDC_IDENTITY_BACKEND,
+    ADMIN_HOST("localhost"),
+    ADMIN_PORT,
+    ADMIN_ALLOW_TCP(true),
+    ADMIN_ALLOW_UDP(true),
+    ADMIN_UDP_PORT,
+    ADMIN_TCP_PORT,
+    ADMIN_DOMAIN("example.com"),
+    ADMIN_REALM("EXAMPLE.COM"),
+    KEYTAB_FILE,
+    PROTOCOL,
+    SERVER_NAME("localhost");
+
+    private Object defaultValue;
+
+    AdminServerConfigKey() {
+        this.defaultValue = null;
+    }
+
+    AdminServerConfigKey(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    @Override
+    public String getPropertyKey() {
+        return name().toLowerCase();
+    }
+
+    @Override
+    public Object getDefaultValue() {
+        return this.defaultValue;
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerContext.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerContext.java
new file mode 100644
index 0000000..e057872
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerContext.java
@@ -0,0 +1,52 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.kerberos.kerb.identity.IdentityService;
+
+public class AdminServerContext {
+    private final AdminServerSetting adminServerSetting;
+
+    private IdentityService identityService;
+
+    public AdminServerContext(AdminServerSetting adminServerSetting) {
+        this.adminServerSetting = adminServerSetting;
+    }
+
+    public AdminServerSetting getAdminServerSetting() {
+        return adminServerSetting;
+    }
+
+    public AdminServerConfig getConfig() {
+        return adminServerSetting.getAdminServerConfig();
+    }
+
+    public void setIdentityService(IdentityService identityService) {
+        this.identityService = identityService;
+    }
+
+    public IdentityService getIdentityService() {
+        return identityService;
+    }
+
+    public String getAdminRealm() {
+        return adminServerSetting.getAdminRealm();
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java
new file mode 100644
index 0000000..5b85d96
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerHandler.java
@@ -0,0 +1,238 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
+import org.apache.kerby.kerberos.kerb.admin.message.AddPrincipalRep;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessage;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.kerberos.kerb.admin.message.DeletePrincipalRep;
+import org.apache.kerby.kerberos.kerb.admin.message.GetprincsRep;
+import org.apache.kerby.kerberos.kerb.admin.message.KadminCode;
+import org.apache.kerby.kerberos.kerb.admin.message.RenamePrincipalRep;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+import org.apache.kerby.xdr.type.XdrStructType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * admin server handler to process client acmin requests.
+ */
+public class AdminServerHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(AdminServerHandler.class);
+    private final AdminServerContext adminServerContext;
+
+    /**
+     * Constructor with admin server context.
+     *
+     * @param adminServerContext admin admin server context
+     */
+    public AdminServerHandler(AdminServerContext adminServerContext) {
+        this.adminServerContext = adminServerContext;
+        LOG.info("Admin realm: " + this.adminServerContext.getAdminRealm());
+    }
+
+    /**
+     * Process the client request message.
+     *
+     * @throws KrbException e
+     * @param receivedMessage The client request message
+     * @param remoteAddress Address from remote side
+     * @return The response message
+     */
+    public ByteBuffer handleMessage(ByteBuffer receivedMessage,
+                                    InetAddress remoteAddress) throws KrbException, IOException {
+        XdrStructType decoded = new AdminMessageCode();
+        decoded.decode(receivedMessage);
+        XdrFieldInfo[] fieldInfos = decoded.getValue().getXdrFieldInfos();
+        AdminMessageType type = (AdminMessageType) fieldInfos[0].getValue();
+
+        /**Create LocalKadmin here*/
+        LocalKadmin localKadmin = new LocalKadminImpl(adminServerContext.getAdminServerSetting());
+        ByteBuffer responseMessage = null;
+
+        switch (type) {
+            case ADD_PRINCIPAL_REQ:
+                System.out.println("message type: add principal req");
+                responseMessage = handleAddPrincipalReq(localKadmin, fieldInfos);
+                break;
+            case DELETE_PRINCIPAL_REQ:
+                System.out.println("message type: delete principal req");
+                responseMessage = handleDeletePrincipalReq(localKadmin, fieldInfos);
+                break;
+            case RENAME_PRINCIPAL_REQ:
+                System.out.println("message type: rename principal req");
+                responseMessage = handleRenamePrincipalReq(localKadmin, fieldInfos);
+                break;
+            case GET_PRINCS_REQ:
+                System.out.println("message type getPrincs req");
+                responseMessage = handleGetprincsReq(localKadmin, fieldInfos);
+                break;
+            default:
+                throw new KrbException("AdminMessageType error, can not handle it.");
+        }
+        return responseMessage;
+
+    }
+
+    private ByteBuffer handleAddPrincipalReq(LocalKadmin localKadmin, XdrFieldInfo[] fieldInfos) throws IOException {
+        String principal = (String) fieldInfos[2].getValue();
+        int paramNum = (int) fieldInfos[1].getValue();
+
+        if (paramNum == 1) {
+            /** Add principal with only principal name*/
+            LOG.info("handle nokey principal " + principal);
+            String[] temp = principal.split("@");
+            try {
+                localKadmin.addPrincipal(temp[0]);
+            } catch (KrbException e) {
+                String error = "principal already exist!";
+                LOG.error(error);
+                System.err.println(error);
+                ByteBuffer response = infoPackageTool(error, "addPrincipal");
+                return response;
+            }
+        } else if (paramNum == 2 && fieldInfos[3].getDataType() == XdrDataType.STRING) {
+            /** Add principal with password*/
+            LOG.info("handle principal with password " + principal);
+            String[] temp = principal.split("@");
+            String password = (String) fieldInfos[3].getValue();
+            try {
+                localKadmin.addPrincipal(temp[0], password);
+            } catch (KrbException e) {
+                String error = "principal already exist.\n"
+                        + "Choose update password instead of add principal";
+                LOG.error(error);
+                ByteBuffer response = infoPackageTool(error, "addPrincipal");
+                return response;
+            }
+        }
+        String message = "add principal of " + principal;
+        LOG.info(message);
+        ByteBuffer responseMessage = infoPackageTool(message, "addPrincipal");
+        return responseMessage;
+    }
+
+    private ByteBuffer handleDeletePrincipalReq(LocalKadmin localKadmin, XdrFieldInfo[] fieldInfos) throws IOException {
+        /** message structure: msg_type, para_num(always equals 1), principal_name*/
+        String principal = (String) fieldInfos[2].getValue();
+        String[] temp = principal.split("@");
+        try {
+            localKadmin.deletePrincipal(temp[0]);
+        } catch (KrbException e) {
+            String error = "no such principal exist!";
+            LOG.error(error);
+            ByteBuffer response = infoPackageTool(error, "deletePrincipal");
+            return response;
+        }
+        String message = "delete principal of " + principal;
+        LOG.info(message);
+        ByteBuffer responseMessage = infoPackageTool(message, "deletePrincipal");
+        return responseMessage;
+    }
+
+    private ByteBuffer handleRenamePrincipalReq(LocalKadmin localKadmin, XdrFieldInfo[] fieldInfos) throws IOException {
+        /** message structure: msg_type, para_num(always equals 2), old name, new name*/
+
+        String[] oldPrincipalName = ((String) fieldInfos[2].getValue()).split("@");
+        String[] newPrincipalName = ((String) fieldInfos[3].getValue()).split("@");
+        try {
+            localKadmin.renamePrincipal(oldPrincipalName[0], newPrincipalName[0]);
+        } catch (KrbException e) {
+            String error = "the old principal name does not exist, or the new principal name"
+                    + " already exists, rename failed.";
+            System.err.println(error);
+            ByteBuffer response = infoPackageTool(error, "renamePrincipal");
+            return response;
+        }
+
+        String message = "rename " + oldPrincipalName[0] + " to " + newPrincipalName[0];
+        ByteBuffer responseMessage = infoPackageTool(message, "renamePrincipal");
+        return responseMessage;
+    }
+
+    private ByteBuffer handleGetprincsReq(LocalKadmin localKadmin, XdrFieldInfo[] fieldInfos) throws IOException {
+        String globString = ((String) fieldInfos[2].getValue());
+        List<String> princsList = null;
+
+        try {
+            if (globString == null || globString.isEmpty()) {
+                princsList = localKadmin.getPrincipals();
+            } else {
+                princsList = localKadmin.getPrincipals(globString);
+            }
+            ByteBuffer responseMessage = infoPackageTool(listToString(princsList), "getPrincs");
+            return responseMessage;
+        } catch (KrbException e) {
+            String error = "principal do not exist.";
+            ByteBuffer responseError = infoPackageTool(error, "getPrincs");
+            return responseError;
+        }
+    }
+
+    private ByteBuffer infoPackageTool(String message, String dealType) throws IOException {
+        AdminMessage adminMessage = null;
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[3];
+
+        if ("getPrincs".equals(dealType)) {
+            adminMessage = new GetprincsRep();
+            xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.GET_PRINCS_REP);
+        } else if ("renamePrincipal".equals(dealType)) {
+            adminMessage = new RenamePrincipalRep();
+            xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.RENAME_PRINCIPAL_REP);
+        } else if ("deletePrincipal".equals(dealType)) {
+            adminMessage = new DeletePrincipalRep();
+            xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.DELETE_PRINCIPAL_REP);
+        } else if ("addPrincipal".equals(dealType)) {
+            adminMessage = new AddPrincipalRep();
+            xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.ADD_PRINCIPAL_REP);
+        }
+
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, 1);
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, message);
+
+        AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
+        adminMessage.setMessageBuffer(ByteBuffer.wrap(value.encode()));
+
+        ByteBuffer responseMessage = KadminCode.encodeMessage(adminMessage);
+        return responseMessage;
+    }
+
+    private String listToString(List<String> list) {
+        if (list.size() <= 0) {
+            return null;
+        }
+        //Both speed and safety,so use StringBuffer
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < list.size(); i++) {
+            result.append(list.get(i)).append(" ");
+        }
+        return result.toString();
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerOption.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerOption.java
new file mode 100644
index 0000000..aa87a7c
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerOption.java
@@ -0,0 +1,52 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.KOption;
+import org.apache.kerby.KOptionInfo;
+import org.apache.kerby.KOptionType;
+
+/**
+ * KDC admin startup options
+ */
+public enum AdminServerOption implements KOption {
+    NONE(null),
+    INNER_ADMIN_IMPL(new KOptionInfo("inner KDC impl", "inner KDC impl", KOptionType.OBJ)),
+    ADMIN_REALM(new KOptionInfo("kdc realm", "kdc realm", KOptionType.STR)),
+    ADMIN_HOST(new KOptionInfo("kdc host", "kdc host", KOptionType.STR)),
+    ADMIN_PORT(new KOptionInfo("kdc port", "kdc port", KOptionType.INT)),
+    ALLOW_TCP(new KOptionInfo("allow tcp", "allow tcp", KOptionType.BOOL)),
+    ADMIN_TCP_PORT(new KOptionInfo("kdc tcp port", "kdc tcp port", KOptionType.INT)),
+    ALLOW_UDP(new KOptionInfo("allow udp", "allow udp", KOptionType.BOOL)),
+    ADMIN_UDP_PORT(new KOptionInfo("kdc udp port", "kdc udp port", KOptionType.INT)),
+    WORK_DIR(new KOptionInfo("work dir", "work dir", KOptionType.DIR)),
+    ENABLE_DEBUG(new KOptionInfo("enable debug", "enable debug", KOptionType.BOOL));
+
+    private final KOptionInfo optionInfo;
+
+    AdminServerOption(KOptionInfo optionInfo) {
+        this.optionInfo = optionInfo;
+    }
+
+    @Override
+    public KOptionInfo getOptionInfo() {
+        return optionInfo;
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerSetting.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerSetting.java
new file mode 100644
index 0000000..a8dba48
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerSetting.java
@@ -0,0 +1,212 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.kerberos.kerb.server.ServerSetting;
+
+/**
+ * Admin Server setting that combines startup options and admin config.
+ */
+public class AdminServerSetting implements ServerSetting {
+    private final KOptions startupOptions;
+    private final AdminServerConfig adminServerConfig;
+    private final KdcConfig kdcConfig;
+    private final BackendConfig backendConfig;
+
+    /**
+     * AdminServerSetting constructor
+     * @param startupOptions startup options
+     * @param adminServerConfig admin configuration
+     * @param kdcConfig kdc configuration
+     * @param backendConfig backend configuration
+     */
+    public AdminServerSetting(KOptions startupOptions,
+                              AdminServerConfig adminServerConfig,
+                              KdcConfig kdcConfig,
+                              BackendConfig backendConfig) {
+        this.startupOptions = startupOptions;
+        this.adminServerConfig = adminServerConfig;
+        this.kdcConfig = kdcConfig;
+        this.backendConfig = backendConfig;
+    }
+
+    public AdminServerSetting(AdminServerConfig adminServerConfig, 
+                              BackendConfig backendConfig, KdcConfig kdcConfig) {
+        this(new KOptions(), adminServerConfig, kdcConfig, backendConfig);
+    }
+
+    /**
+     * Get the Admin Server config.
+     * @return admin configuration
+     */
+    public AdminServerConfig getAdminServerConfig() {
+        return adminServerConfig;
+    }
+
+    /**
+     * Get the realm of KDC of Admin Server.
+     * @return the realm of KDC
+     */
+    @Override
+    public String getKdcRealm() {
+         return kdcConfig.getKdcRealm();
+    }
+
+    /**
+     * Get the KDC config of Admin server.
+     * @return the KDC configuration
+     */
+    @Override
+    public KdcConfig getKdcConfig() {
+        return kdcConfig;
+    }
+
+    /**
+     * Get the backend config.
+     * @return backend configuration
+     */
+    public BackendConfig getBackendConfig() {
+        return backendConfig;
+    }
+
+    public String getAdminHost() {
+        String adminHost = startupOptions.getStringOption(
+                AdminServerOption.ADMIN_HOST);
+        if (adminHost == null) {
+            adminHost = adminServerConfig.getAdminHost();
+        }
+        return adminHost;
+    }
+
+    /**
+     * Check admin tcp setting and see if any bad.
+     * @return valid tcp port or -1 if not allowTcp
+     * @throws KrbException e
+     */
+    public int checkGetAdminTcpPort() throws KrbException {
+        if (allowTcp()) {
+            int adminPort = getAdminTcpPort();
+            if (adminPort < 1) {
+                throw new KrbException("Admin Server tcp port isn't set or configured");
+            }
+            return adminPort;
+        }
+        return -1;
+    }
+
+    /**
+     * Check admin udp setting and see if any bad.
+     * @return valid udp port or -1 if not allowUdp
+     * @throws KrbException e
+     */
+    public int checkGetAdminUdpPort() throws KrbException {
+        if (allowUdp()) {
+            int adminPort = getAdminUdpPort();
+            if (adminPort < 1) {
+                throw new KrbException("Admin Server udp port isn't set or configured");
+            }
+            return adminPort;
+        }
+        return -1;
+    }
+
+    /**
+     * Get admin tcp port
+     *
+     * @return admin tcp port
+     */
+    public int getAdminTcpPort() {
+        int tcpPort = startupOptions.getIntegerOption(AdminServerOption.ADMIN_TCP_PORT);
+        if (tcpPort < 1) {
+            tcpPort = adminServerConfig.getAdminTcpPort();
+        }
+        if (tcpPort < 1) {
+            tcpPort = getAdminPort();
+        }
+
+        return tcpPort;
+    }
+
+    /**
+     * Get admin port
+     *
+     * @return admin port
+     */
+    public int getAdminPort() {
+        int adminPort = startupOptions.getIntegerOption(AdminServerOption.ADMIN_PORT);
+        if (adminPort < 1) {
+            adminPort = adminServerConfig.getAdminPort();
+        }
+        return adminPort;
+    }
+
+    /**
+     * Get whether tcp protocol is allowed
+     * @return tcp protocol is allowed or not
+     */
+    public boolean allowTcp() {
+        Boolean allowTcp = startupOptions.getBooleanOption(
+                AdminServerOption.ALLOW_TCP, adminServerConfig.allowTcp());
+        return allowTcp;
+    }
+
+    /**
+     * Get whether udp protocol is allowed
+     * @return udp protocol is allowed or not
+     */
+    public boolean allowUdp() {
+        Boolean allowUdp = startupOptions.getBooleanOption(
+                AdminServerOption.ALLOW_UDP, adminServerConfig.allowUdp());
+        return allowUdp;
+    }
+
+    /**
+     * Get admin udp port
+     *
+     * @return udp port
+     */
+    public int getAdminUdpPort() {
+        int udpPort = startupOptions.getIntegerOption(AdminServerOption.ADMIN_UDP_PORT);
+        if (udpPort < 1) {
+            udpPort = adminServerConfig.getAdminUdpPort();
+        }
+        if (udpPort < 1) {
+            udpPort = getAdminPort();
+        }
+
+        return udpPort;
+    }
+
+    /**
+     * Get Admin Server realm.
+     * @return Admin Server realm
+     */
+    public String getAdminRealm() {
+        String adminRealm = startupOptions.getStringOption(AdminServerOption.ADMIN_REALM);
+        if (adminRealm == null || adminRealm.isEmpty()) {
+            adminRealm = adminServerConfig.getAdminRealm();
+        }
+        return adminRealm;
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java
new file mode 100644
index 0000000..f48bf5b
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/AdminServerUtil.java
@@ -0,0 +1,165 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+import org.apache.kerby.kerberos.kerb.identity.backend.MemoryIdentityBackend;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+/**
+ * Admin Server utilities.
+ */
+public final class AdminServerUtil {
+
+    private AdminServerUtil() { }
+
+    /**
+     * Get adminServer configuration
+     * @param confDir configuration directory
+     * @return adminServer configuration
+     * @throws KrbException e.
+     */
+    public static AdminServerConfig getAdminServerConfig(File confDir) throws KrbException {
+        File adminServerConfFile = new File(confDir, "adminServer.conf");
+        if (adminServerConfFile.exists()) {
+            AdminServerConfig adminServerConfig = new AdminServerConfig();
+            try {
+                adminServerConfig.addKrb5Config(adminServerConfFile);
+            } catch (IOException e) {
+                throw new KrbException("Can not load the adminServer configuration file "
+                        + adminServerConfFile.getAbsolutePath());
+            }
+            return adminServerConfig;
+        }
+
+        return null;
+    }
+
+    /**
+     * Get kdc configuration
+     * @param confDir configuration directory
+     * @return kdc configuration
+     * @throws KrbException e.
+     */
+    public static KdcConfig getKdcConfig(File confDir) throws KrbException {
+        File kdcConfFile = new File(confDir, "kdc.conf");
+        if (kdcConfFile.exists()) {
+            KdcConfig kdcConfig = new KdcConfig();
+            try {
+                kdcConfig.addKrb5Config(kdcConfFile);
+            } catch (IOException e) {
+                throw new KrbException("Can not load the kdc configuration file "
+                    + kdcConfFile.getAbsolutePath());
+            }
+            return kdcConfig;
+        }
+
+        return null;
+    }
+
+    /**
+     * Get backend configuration
+     * @param confDir configuration directory
+     * @return backend configuration
+     * @throws KrbException e.
+     */
+    public static BackendConfig getBackendConfig(File confDir) throws KrbException {
+        File backendConfigFile = new File(confDir, "backend.conf");
+        if (backendConfigFile.exists()) {
+            BackendConfig backendConfig = new BackendConfig();
+            try {
+                backendConfig.addIniConfig(backendConfigFile);
+            } catch (IOException e) {
+                throw new KrbException("Can not load the backend configuration file "
+                        + backendConfigFile.getAbsolutePath());
+            }
+            return backendConfig;
+        }
+
+        return null;
+    }
+
+    /**
+     * Init the identity backend from backend configuration.
+     *
+     * @throws KrbException e.
+     * @param backendConfig backend configuration information
+     * @return backend
+     */
+    public static IdentityBackend getBackend(
+            BackendConfig backendConfig) throws KrbException {
+        String backendClassName = backendConfig.getString(
+                AdminServerConfigKey.KDC_IDENTITY_BACKEND, true);
+        if (backendClassName == null) {
+            backendClassName = MemoryIdentityBackend.class.getCanonicalName();
+        }
+
+        Class<?> backendClass;
+        try {
+            backendClass = Class.forName(backendClassName);
+        } catch (ClassNotFoundException e) {
+            throw new KrbException("Failed to load backend class: "
+                    + backendClassName);
+        }
+
+        IdentityBackend backend;
+        try {
+            backend = (IdentityBackend) backendClass.newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            throw new KrbException("Failed to create backend: "
+                    + backendClassName);
+        }
+
+        backend.setConfig(backendConfig);
+        backend.initialize();
+        return backend;
+    }
+
+    /**
+     * Get KDC network transport addresses according to KDC setting.
+     * @param setting kdc setting
+     * @return UDP and TCP addresses pair
+     * @throws KrbException e
+     */
+    public static TransportPair getTransportPair(
+            AdminServerSetting setting) throws KrbException {
+        TransportPair result = new TransportPair();
+
+        int tcpPort = setting.checkGetAdminTcpPort();
+        if (tcpPort > 0) {
+            result.tcpAddress = new InetSocketAddress(
+                    setting.getAdminHost(), tcpPort);
+        }
+        int udpPort = setting.checkGetAdminUdpPort();
+        if (udpPort > 0) {
+            result.udpAddress = new InetSocketAddress(
+                    setting.getAdminHost(), udpPort);
+        }
+
+        return result;
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/AbstractInternalAdminServer.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/AbstractInternalAdminServer.java
new file mode 100644
index 0000000..ac71386
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/AbstractInternalAdminServer.java
@@ -0,0 +1,116 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin.impl;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerConfig;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerSetting;
+import org.apache.kerby.kerberos.kerb.identity.CacheableIdentityService;
+import org.apache.kerby.kerberos.kerb.identity.IdentityService;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+import org.apache.kerby.kerberos.kerb.identity.backend.MemoryIdentityBackend;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+
+/**
+ * Abstract Kadmin admin implementation.
+ */
+public class AbstractInternalAdminServer implements InternalAdminServer {
+    private boolean started;
+    private final AdminServerConfig adminServerConfig;
+    private final BackendConfig backendConfig;
+    private final AdminServerSetting adminServerSetting;
+    private IdentityBackend backend;
+    private IdentityService identityService;
+
+    public AbstractInternalAdminServer(AdminServerSetting adminServerSetting) {
+        this.adminServerSetting = adminServerSetting;
+        this.adminServerConfig = adminServerSetting.getAdminServerConfig();
+        this.backendConfig = adminServerSetting.getBackendConfig();
+    }
+
+    @Override
+    public AdminServerSetting getSetting() {
+        return adminServerSetting;
+    }
+
+    public boolean isStarted() {
+        return started;
+    }
+
+    protected String getServiceName() {
+        return adminServerConfig.getAdminServiceName();
+    }
+
+    protected IdentityService getIdentityService() {
+        if (identityService == null) {
+            if (backend instanceof MemoryIdentityBackend) { // Already in memory
+                identityService = backend;
+            } else {
+                identityService = new CacheableIdentityService(
+                        backendConfig, backend);
+            }
+        }
+        return identityService;
+    }
+
+    @Override
+    public void init() throws KrbException {
+        backend = KdcUtil.getBackend(backendConfig);
+    }
+
+    @Override
+    public void start() throws KrbException {
+        try {
+            doStart();
+        } catch (Exception e) {
+            throw new KrbException("Failed to start " + getServiceName(), e);
+        }
+
+        started = true;
+    }
+
+    public boolean enableDebug() {
+        return adminServerConfig.enableDebug();
+    }
+
+    @Override
+    public IdentityBackend getIdentityBackend() {
+        return backend;
+    }
+
+    protected void doStart() throws Exception {
+        backend.start();
+    }
+
+    public void stop() throws KrbException {
+        try {
+            doStop();
+        } catch (Exception e) {
+            throw new KrbException("Failed to stop " + getServiceName(), e);
+        }
+
+        started = false;
+    }
+
+    protected void doStop() throws Exception {
+        backend.stop();
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java
new file mode 100644
index 0000000..1dbb017
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultAdminServerHandler.java
@@ -0,0 +1,199 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin.impl;
+
+import org.apache.kerby.kerberos.kerb.admin.AuthUtil;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerContext;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerHandler;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslServer;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.SocketTimeoutException;
+import java.nio.ByteBuffer;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultAdminServerHandler extends AdminServerHandler implements Runnable {
+    private static Logger logger = LoggerFactory.getLogger(DefaultAdminServerHandler.class);
+    private final KrbTransport transport;
+    private static boolean sasl = false;
+    private AdminServerContext adminServerContext;
+
+    public DefaultAdminServerHandler(AdminServerContext adminServerContext, KrbTransport transport) {
+        super(adminServerContext);
+        this.transport  = transport;
+        this.adminServerContext = adminServerContext;
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            try {
+                if (!sasl) {
+                    logger.info("Doing the sasl negotiation !!!");
+                    try {
+                        saslNegotiation();
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                } else {
+                    ByteBuffer message = transport.receiveMessage();
+                    if (message == null) {
+                        logger.debug("No valid request recved. Disconnect actively");
+                        transport.release();
+                        break;
+                    }
+                    handleMessage(message);
+                }
+            } catch (IOException e) {
+                transport.release();
+                logger.debug("Transport or decoding error occurred, "
+                        + "disconnecting abnormally", e);
+                break;
+            }
+        }
+    }
+
+    protected void handleMessage(ByteBuffer message) {
+        InetAddress clientAddress = transport.getRemoteAddress();
+
+        try {
+            ByteBuffer adminResponse = handleMessage(message, clientAddress);
+            transport.sendMessage(adminResponse);
+        } catch (Exception e) {
+            transport.release();
+            logger.error("Error occured while processing request:", e);
+        }
+    }
+
+    private void saslNegotiation() throws Exception {
+
+        File keytabFile = new File(adminServerContext.getConfig().getKeyTabFile());
+        String principal = adminServerContext.getConfig().getProtocol() + "/"
+            + adminServerContext.getConfig().getAdminHost();
+
+        Subject subject = AuthUtil.loginUsingKeytab(principal, keytabFile);
+        Subject.doAs(subject, new PrivilegedAction<Object>() {
+            @Override
+            public Object run() {
+                try {
+                    ByteBuffer message = null;
+                    try {
+                        message = transport.receiveMessage();
+                    } catch (SocketTimeoutException e) {
+                        // ignore time out
+                        return null;
+                    }
+
+                    Map<String, Object> props = new HashMap<String, Object>();
+                    props.put(Sasl.QOP, "auth-conf");
+                    props.put(Sasl.SERVER_AUTH, "true");
+
+                    String protocol = adminServerContext.getConfig().getProtocol();
+                    String serverName = adminServerContext.getConfig().getServerName();
+                    CallbackHandler callbackHandler = new SaslGssCallbackHandler();
+                    SaslServer ss = Sasl.createSaslServer("GSSAPI",
+                        protocol, serverName, props, callbackHandler);
+
+                    if (ss == null) {
+                        throw new Exception("Unable to find server implementation for: GSSAPI");
+                    }
+
+                    while (!ss.isComplete()) {
+                        int scComplete = message.getInt();
+                        if (scComplete == 0) {
+                            System.out.println("success!!!");
+                            sasl = true;
+                            break;
+                        }
+                        sendMessage(message, ss);
+                        if (!ss.isComplete()) {
+                            logger.info("Waiting receive message");
+                            message = transport.receiveMessage();
+                        }
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return null;
+            }
+        });
+
+    }
+
+    private void sendMessage(ByteBuffer message, SaslServer ss) throws IOException {
+
+        byte[] arr = new byte[message.remaining()];
+        message.get(arr);
+        byte[] challenge = ss.evaluateResponse(arr);
+
+        // 4 is the head to go through network
+        ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
+        buffer.putInt(challenge.length + 4);
+        int ssComplete = ss.isComplete() ? 0 : 1;
+        buffer.putInt(ssComplete);
+        buffer.put(challenge);
+        buffer.flip();
+        transport.sendMessage(buffer);
+    }
+
+    private static class SaslGssCallbackHandler implements CallbackHandler {
+
+        @Override
+        public void handle(Callback[] callbacks) throws
+            UnsupportedCallbackException {
+            AuthorizeCallback ac = null;
+            for (Callback callback : callbacks) {
+                if (callback instanceof AuthorizeCallback) {
+                    ac = (AuthorizeCallback) callback;
+                } else {
+                    throw new UnsupportedCallbackException(callback,
+                        "Unrecognized SASL GSSAPI Callback");
+                }
+            }
+            if (ac != null) {
+                String authid = ac.getAuthenticationID();
+                String authzid = ac.getAuthorizationID();
+                if (authid.equals(authzid)) {
+                    ac.setAuthorized(true);
+                } else {
+                    ac.setAuthorized(false);
+                }
+                if (ac.isAuthorized()) {
+                    // System.out.println("SASL server GSSAPI callback: setting "
+                    //+ "canonicalized client ID: " + authzid);
+                    ac.setAuthorizedID(authzid);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultInternalAdminServerImpl.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultInternalAdminServerImpl.java
new file mode 100644
index 0000000..4234481
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/DefaultInternalAdminServerImpl.java
@@ -0,0 +1,80 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin.impl;
+
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerContext;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerSetting;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerUtil;
+import org.apache.kerby.kerberos.kerb.transport.KdcNetwork;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * A default admin admin implementation.
+ */
+public class DefaultInternalAdminServerImpl extends AbstractInternalAdminServer {
+    private ExecutorService executor;
+    private AdminServerContext adminContext;
+    private KdcNetwork network;
+
+    public DefaultInternalAdminServerImpl(AdminServerSetting adminSetting) {
+        super(adminSetting);
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        super.doStart();
+
+        prepareHandler();
+
+        executor = Executors.newCachedThreadPool();
+
+        network = new KdcNetwork() {
+            @Override
+            protected void onNewTransport(KrbTransport transport) {
+                DefaultAdminServerHandler kdcHandler = 
+                    new DefaultAdminServerHandler(adminContext, transport);
+                executor.execute(kdcHandler);
+            }
+        };
+
+        network.init();
+        TransportPair tpair = AdminServerUtil.getTransportPair(getSetting());
+        network.listen(tpair);
+        network.start();
+    }
+
+    private void prepareHandler() {
+        adminContext = new AdminServerContext(getSetting());
+        adminContext.setIdentityService(getIdentityService());
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        super.doStop();
+
+        network.stop();
+
+        executor.shutdownNow();
+    }
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/InternalAdminServer.java b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/InternalAdminServer.java
new file mode 100644
index 0000000..c0cde44
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/java/org/apache/kerby/kerberos/kerb/admin/server/kadmin/impl/InternalAdminServer.java
@@ -0,0 +1,60 @@
+/**
+ *  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.kerberos.kerb.admin.server.kadmin.impl;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerSetting;
+import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
+
+/**
+ * An internal KDC admin interface.
+ */
+public interface InternalAdminServer {
+
+    /**
+     * Initialize.
+     * @throws KrbException e
+     */
+    void init() throws KrbException;
+
+    /**
+     * Start the KDC admin.
+     * @throws KrbException e
+     */
+    void start() throws KrbException;
+
+    /**
+     * Stop the KDC admin.
+     * @throws KrbException e
+     */
+    void stop() throws KrbException;
+
+    /**
+     * Get admin admin setting.
+     * @return setting
+     */
+    AdminServerSetting getSetting();
+
+    /**
+     * Get identity backend.
+     * @return IdentityBackend
+     */
+    IdentityBackend getIdentityBackend();
+}
diff --git a/kerby-kerb/kerb-admin-server/src/main/resources/adminServer.conf b/kerby-kerb/kerb-admin-server/src/main/resources/adminServer.conf
new file mode 100644
index 0000000..8c7a11e
--- /dev/null
+++ b/kerby-kerb/kerb-admin-server/src/main/resources/adminServer.conf
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+[libdefaults]
+default_realm = TEST.COM
+admin_port = 65417
\ No newline at end of file
diff --git a/kerby-kerb/kerb-admin/pom.xml b/kerby-kerb/kerb-admin/pom.xml
index 2a50bce..e4d52de 100644
--- a/kerby-kerb/kerb-admin/pom.xml
+++ b/kerby-kerb/kerb-admin/pom.xml
@@ -37,5 +37,10 @@
       <artifactId>kerb-util</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerby-xdr</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java
new file mode 100644
index 0000000..68d03e7
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AuthUtil.java
@@ -0,0 +1,141 @@
+/**
+ *  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.kerberos.kerb.admin;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class AuthUtil {
+
+    public static final boolean ENABLE_DEBUG = true;
+
+    private static String getKrb5LoginModuleName() {
+        return System.getProperty("java.vendor").contains("IBM")
+            ? "com.ibm.security.auth.module.Krb5LoginModule"
+            : "com.sun.security.auth.module.Krb5LoginModule";
+    }
+
+    public static Subject loginUsingTicketCache(
+        String principal, File cacheFile) throws LoginException {
+        Set<Principal> principals = new HashSet<Principal>();
+        principals.add(new KerberosPrincipal(principal));
+
+        Subject subject = new Subject(false, principals,
+            new HashSet<Object>(), new HashSet<Object>());
+
+        Configuration conf = useTicketCache(principal, cacheFile);
+        String confName = "TicketCacheConf";
+        LoginContext loginContext = new LoginContext(confName, subject, null, conf);
+        loginContext.login();
+        return loginContext.getSubject();
+    }
+
+    public static Subject loginUsingKeytab(
+        String principal, File keytabFile) throws LoginException {
+        Set<Principal> principals = new HashSet<Principal>();
+        principals.add(new KerberosPrincipal(principal));
+
+        Subject subject = new Subject(false, principals,
+            new HashSet<Object>(), new HashSet<Object>());
+
+        Configuration conf = useKeytab(principal, keytabFile);
+        String confName = "KeytabConf";
+        LoginContext loginContext = new LoginContext(confName, subject, null, conf);
+        loginContext.login();
+        return loginContext.getSubject();
+    }
+
+    public static Configuration useTicketCache(String principal,
+                                               File credentialFile) {
+        return new TicketCacheJaasConf(principal, credentialFile);
+    }
+
+    public static Configuration useKeytab(String principal, File keytabFile) {
+        return new KeytabJaasConf(principal, keytabFile);
+    }
+
+    static class TicketCacheJaasConf extends Configuration {
+        private String principal;
+        private File clientCredentialFile;
+
+        TicketCacheJaasConf(String principal, File clientCredentialFile) {
+            this.principal = principal;
+            this.clientCredentialFile = clientCredentialFile;
+        }
+
+        @Override
+        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+            Map<String, String> options = new HashMap<String, String>();
+            options.put("principal", principal);
+            options.put("storeKey", "false");
+            options.put("doNotPrompt", "false");
+            options.put("useTicketCache", "true");
+            options.put("renewTGT", "true");
+            options.put("refreshKrb5Config", "true");
+            options.put("isInitiator", "true");
+            options.put("ticketCache", clientCredentialFile.getAbsolutePath());
+            options.put("debug", String.valueOf(ENABLE_DEBUG));
+
+            return new AppConfigurationEntry[]{
+                new AppConfigurationEntry(getKrb5LoginModuleName(),
+                    AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                    options)};
+        }
+    }
+
+    static class KeytabJaasConf extends Configuration {
+        private String principal;
+        private File keytabFile;
+
+        KeytabJaasConf(String principal, File keytab) {
+            this.principal = principal;
+            this.keytabFile = keytab;
+        }
+
+        @Override
+        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+            Map<String, String> options = new HashMap<String, String>();
+            options.put("keyTab", keytabFile.getAbsolutePath());
+            options.put("principal", principal);
+            options.put("useKeyTab", "true");
+            options.put("storeKey", "true");
+            options.put("doNotPrompt", "true");
+            options.put("renewTGT", "false");
+            options.put("refreshKrb5Config", "true");
+            options.put("isInitiator", "true");
+            options.put("debug", String.valueOf(ENABLE_DEBUG));
+
+            return new AppConfigurationEntry[]{
+                new AppConfigurationEntry(getKrb5LoginModuleName(),
+                    AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                    options)};
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java
deleted file mode 100644
index 933accf..0000000
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminServer.java
+++ /dev/null
@@ -1,144 +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.kerberos.kerb.admin;
-
-import org.apache.kerby.KOptions;
-import org.apache.kerby.kerberos.kerb.KrbException;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Server side admin facilities for remote, similar to MIT kadmind service.
- * It uses GSSAPI and XDR to communicate with remote client/kadmin to receive
- * and perform the requested operations. In this server side, it simply leverages
- * LocalKadmin to perform the real work.
- *
- * TO BE IMPLEMENTED.
- */
-public class KadminServer implements Kadmin {
-    //private LocalKadmin localKadmin;
-
-    @Override
-    public String getKadminPrincipal() {
-        return null;
-    }
-
-    @Override
-    public void addPrincipal(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal,
-                             KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal,
-                             String password) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal, String password,
-                             KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile,
-                             String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile,
-                             List<String> principals) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile) throws KrbException {
-
-    }
-
-    @Override
-    public void removeKeytabEntriesOf(File keytabFile,
-                                      String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void removeKeytabEntriesOf(File keytabFile, String principal,
-                                      int kvno) throws KrbException {
-
-    }
-
-    @Override
-    public void removeOldKeytabEntriesOf(File keytabFile,
-                                         String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void deletePrincipal(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void modifyPrincipal(String principal,
-                                KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void renamePrincipal(String oldPrincipalName,
-                                String newPrincipalName) throws KrbException {
-
-    }
-
-    @Override
-    public List<String> getPrincipals() throws KrbException {
-        return null;
-    }
-
-    @Override
-    public List<String> getPrincipals(String globString) throws KrbException {
-        return null;
-    }
-
-    @Override
-    public void changePassword(String principal,
-                               String newPassword) throws KrbException {
-
-    }
-
-    @Override
-    public void updateKeys(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void release() throws KrbException {
-
-    }
-}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Krb5Conf.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Krb5Conf.java
new file mode 100644
index 0000000..9e3b3cf
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Krb5Conf.java
@@ -0,0 +1,86 @@
+/**
+ *  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.kerberos.kerb.admin;
+
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.util.IOUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Generate krb5 file using given kdc server settings.
+ */
+public class Krb5Conf {
+    public static final String KRB5_CONF = "java.security.krb5.conf";
+    private static final String KRB5_CONF_FILE = "krb5.conf";
+    private File confDir;
+    private KdcConfig kdcConfig;
+
+    public Krb5Conf(File confDir, KdcConfig kdcConfig) {
+        this.confDir = confDir;
+        this.kdcConfig = kdcConfig;
+    }
+
+    public void initKrb5conf() throws IOException {
+        File confFile = generateConfFile();
+        System.setProperty(KRB5_CONF, confFile.getAbsolutePath());
+    }
+
+    // Read in krb5.conf and substitute in the correct port
+    private File generateConfFile() throws IOException {
+
+        String resourcePath = kdcConfig.allowUdp() ? "/krb5_udp.conf" : "/krb5.conf";
+        InputStream templateResource = getClass().getResourceAsStream(resourcePath);
+
+        String templateContent = IOUtil.readInput(templateResource);
+
+        String content = templateContent;
+
+        content = content.replaceAll("_REALM_", "" + kdcConfig.getKdcRealm());
+
+        int kdcPort = kdcConfig.allowUdp() ? kdcConfig.getKdcUdpPort()
+                : kdcConfig.getKdcTcpPort();
+        content = content.replaceAll("_KDC_PORT_",
+                String.valueOf(kdcPort));
+
+        if (kdcConfig.allowTcp()) {
+            content = content.replaceAll("#_KDC_TCP_PORT_", "kdc_tcp_port = " + kdcConfig.getKdcTcpPort());
+        }
+        if (kdcConfig.allowUdp()) {
+            content = content.replaceAll("#_KDC_UDP_PORT_", "kdc_udp_port = " + kdcConfig.getKdcUdpPort());
+        }
+
+        int udpLimit = kdcConfig.allowUdp() ? 4096 : 1;
+        content = content.replaceAll("_UDP_LIMIT_", String.valueOf(udpLimit));
+
+        File confFile = new File(confDir, KRB5_CONF_FILE);
+        if (confFile.exists()) {
+            boolean delete = confFile.delete();
+            if (!delete) {
+                throw new RuntimeException("File delete error!");
+            }
+        }
+        IOUtil.writeFile(content, confFile);
+
+        return confFile;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java
new file mode 100644
index 0000000..27a8da2
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteAdminClientTool.java
@@ -0,0 +1,263 @@
+/**
+ *  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.kerberos.kerb.admin;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminConfig;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminUtil;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteAddPrincipalCommand;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteCommand;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteDeletePrincipalCommand;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteGetprincsCommand;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemotePrintUsageCommand;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.command.RemoteRenamePrincipalCommand;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+import org.apache.kerby.kerberos.kerb.server.KdcConfig;
+import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+import org.apache.kerby.util.OSUtil;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslClient;
+import javax.security.sasl.SaslException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * Command use of remote admin
+ */
+public class RemoteAdminClientTool {
+    private static final byte[] EMPTY = new byte[0];
+    private static KrbTransport transport;
+    private static final String PROMPT = RemoteAdminClientTool.class.getSimpleName() + ".local:";
+    private static final String USAGE = (OSUtil.isWindows()
+        ? "Usage: bin\\remote-admin-client.cmd" : "Usage: sh bin/remote-admin-client.sh")
+        + " <conf-file>\n"
+        + "\tExample:\n"
+        + "\t\t"
+        + (OSUtil.isWindows()
+        ? "bin\\remote-admin-client.cmd" : "sh bin/remote-admin-client.sh")
+        + " conf\n";
+
+    private static final String LEGAL_COMMANDS = "Available commands are: "
+        + "\n"
+        + "add_principal, addprinc\n"
+        + "                         Add principal\n"
+        + "delete_principal, delprinc\n"
+        + "                         Delete principal\n"
+        + "rename_principal, renprinc\n"
+        + "                         Rename principal\n"
+        + "listprincs\n"
+        + "          List principals\n";
+
+    public static void main(String[] args) throws Exception {
+        AdminClient adminClient;
+
+        if (args.length < 1) {
+            System.err.println(USAGE);
+            System.exit(1);
+        }
+
+        String confDirPath = args[0];
+
+        File confFile = new File(confDirPath, "adminClient.conf");
+
+        final AdminConfig adminConfig = new AdminConfig();
+        adminConfig.addKrb5Config(confFile);
+
+        KdcConfig tmpKdcConfig = KdcUtil.getKdcConfig(new File(confDirPath));
+        if (tmpKdcConfig == null) {
+            tmpKdcConfig = new KdcConfig();
+        }
+
+        try {
+            Krb5Conf krb5Conf = new Krb5Conf(new File(confDirPath), tmpKdcConfig);
+            krb5Conf.initKrb5conf();
+        } catch (IOException e) {
+            throw new KrbException("Failed to make krb5.conf", e);
+        }
+
+        adminClient = new AdminClient(adminConfig);
+
+        File keytabFile = new File(adminConfig.getKeyTabFile());
+        if (keytabFile == null || !keytabFile.exists()) {
+            System.err.println("Need the valid keytab file value in conf file.");
+            return;
+        }
+
+        String adminRealm = adminConfig.getAdminRealm();
+
+        adminClient.setAdminRealm(adminRealm);
+        adminClient.setAllowTcp(true);
+        adminClient.setAllowUdp(false);
+        adminClient.setAdminTcpPort(adminConfig.getAdminPort());
+
+        adminClient.init();
+        System.out.println("admin init successful");
+
+        TransportPair tpair = null;
+        try {
+            tpair = AdminUtil.getTransportPair(adminClient.getSetting());
+        } catch (KrbException e) {
+            e.printStackTrace();
+        }
+        KrbNetwork network = new KrbNetwork();
+        network.setSocketTimeout(adminClient.getSetting().getTimeout());
+
+        try {
+            transport = network.connect(tpair);
+        } catch (IOException e) {
+            throw new KrbException("Failed to create transport", e);
+        }
+
+        String adminPrincipal = KrbUtil.makeKadminPrincipal(
+            adminClient.getSetting().getKdcRealm()).getName();
+        Subject subject = null;
+        try {
+            subject = AuthUtil.loginUsingKeytab(adminPrincipal,
+                new File(adminConfig.getKeyTabFile()));
+        } catch (LoginException e) {
+            e.printStackTrace();
+        }
+        Subject.doAs(subject, new PrivilegedAction<Object>() {
+            @Override
+            public Object run() {
+                try {
+
+                    Map<String, String> props = new HashMap<String, String>();
+                    props.put(Sasl.QOP, "auth-conf");
+                    props.put(Sasl.SERVER_AUTH, "true");
+                    SaslClient saslClient = null;
+                    try {
+                        String protocol = adminConfig.getProtocol();
+                        String serverName = adminConfig.getServerName();
+                        saslClient = Sasl.createSaslClient(new String[]{"GSSAPI"}, null,
+                            protocol, serverName, props, null);
+                    } catch (SaslException e) {
+                        e.printStackTrace();
+                    }
+                    if (saslClient == null) {
+                        throw new KrbException("Unable to find client implementation for: GSSAPI");
+                    }
+                    byte[] response = new byte[0];
+                    try {
+                        response = saslClient.hasInitialResponse()
+                            ? saslClient.evaluateChallenge(EMPTY) : EMPTY;
+                    } catch (SaslException e) {
+                        e.printStackTrace();
+                    }
+
+                    sendMessage(response, saslClient);
+
+                    ByteBuffer message = transport.receiveMessage();
+
+                    while (!saslClient.isComplete()) {
+                        int ssComplete = message.getInt();
+                        if (ssComplete == 0) {
+                            System.out.println("Sasl Server completed");
+                        }
+                        byte[] arr = new byte[message.remaining()];
+                        message.get(arr);
+                        byte[] challenge = saslClient.evaluateChallenge(arr);
+
+                        sendMessage(challenge, saslClient);
+
+                        if (!saslClient.isComplete()) {
+                            message = transport.receiveMessage();
+                        }
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                return null;
+            }
+        });
+
+        System.out.println("enter \"command\" to see legal commands.");
+
+        try (Scanner scanner = new Scanner(System.in, "UTF-8")) {
+            String input = scanner.nextLine();
+
+            while (!(input.equals("quit") || input.equals("exit") || input.equals("q"))) {
+                excute(adminClient, input);
+                System.out.print(PROMPT);
+                input = scanner.nextLine();
+            }
+        }
+    }
+
+    private static void sendMessage(byte[] challenge, SaslClient saslClient)
+        throws SaslException {
+
+        // 4 is the head to go through network
+        ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
+        buffer.putInt(challenge.length + 4);
+        int scComplete = saslClient.isComplete() ? 0 : 1;
+
+        buffer.putInt(scComplete);
+        buffer.put(challenge);
+        buffer.flip();
+
+        try {
+            transport.sendMessage(buffer);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static void excute(AdminClient adminClient, String input) throws KrbException {
+        input = input.trim();
+        if (input.startsWith("command")) {
+            System.out.println(LEGAL_COMMANDS);
+            return;
+        }
+
+        RemoteCommand executor = null;
+
+        if (input.startsWith("add_principal")
+            || input.startsWith("addprinc")) {
+            executor = new RemoteAddPrincipalCommand(adminClient);
+        } else if (input.startsWith("delete_principal")
+            || input.startsWith("delprinc")) {
+            executor = new RemoteDeletePrincipalCommand(adminClient);
+        } else if (input.startsWith("rename_principal")
+            || input.startsWith("renprinc")) {
+            executor = new RemoteRenamePrincipalCommand(adminClient);
+        } else if (input.startsWith("list_principals")) {
+            executor = new RemoteGetprincsCommand(adminClient);
+        } else if (input.startsWith("listprincs")) {
+            executor = new RemotePrintUsageCommand();
+        } else {
+            System.out.println(LEGAL_COMMANDS);
+            return;
+        }
+        executor.execute(input);
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java
deleted file mode 100644
index 16115d8..0000000
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/RemoteKadminImpl.java
+++ /dev/null
@@ -1,144 +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.kerberos.kerb.admin;
-
-import org.apache.kerby.KOptions;
-import org.apache.kerby.kerberos.kerb.KrbException;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Server side admin facilities from remote, similar to MIT Kadmin remote mode.
- * It uses GSSAPI and XDR to communicate with remote KDC/kadmind to do the
- * requested operations. In the client side, it simply wraps and sends the
- * request info to the server kadmind side, and then unwraps the response for
- * the operation result.
- *
- * TO BE IMPLEMENTED.
- */
-public class RemoteKadminImpl implements Kadmin {
-
-    @Override
-    public String getKadminPrincipal() {
-        return null;
-    }
-
-    @Override
-    public void addPrincipal(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal,
-                             KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal,
-                             String password) throws KrbException {
-
-    }
-
-    @Override
-    public void addPrincipal(String principal, String password,
-                             KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile,
-                             String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile,
-                             List<String> principals) throws KrbException {
-
-    }
-
-    @Override
-    public void exportKeytab(File keytabFile) throws KrbException {
-
-    }
-
-    @Override
-    public void removeKeytabEntriesOf(File keytabFile,
-                                      String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void removeKeytabEntriesOf(File keytabFile, String principal,
-                                      int kvno) throws KrbException {
-
-    }
-
-    @Override
-    public void removeOldKeytabEntriesOf(File keytabFile,
-                                         String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void deletePrincipal(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void modifyPrincipal(String principal,
-                                KOptions kOptions) throws KrbException {
-
-    }
-
-    @Override
-    public void renamePrincipal(String oldPrincipalName,
-                                String newPrincipalName) throws KrbException {
-
-    }
-
-    @Override
-    public List<String> getPrincipals() throws KrbException {
-        return null;
-    }
-
-    @Override
-    public List<String> getPrincipals(String globString) throws KrbException {
-        return null;
-    }
-
-    @Override
-    public void changePassword(String principal,
-                               String newPassword) throws KrbException {
-
-    }
-
-    @Override
-    public void updateKeys(String principal) throws KrbException {
-
-    }
-
-    @Override
-    public void release() throws KrbException {
-
-    }
-}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/Kadmin.java
similarity index 98%
rename from kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java
rename to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/Kadmin.java
index 594ff6b..8f95b37 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/Kadmin.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/Kadmin.java
@@ -17,7 +17,7 @@
  *  under the License. 
  *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin;
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminOption.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/KadminOption.java
similarity index 97%
rename from kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminOption.java
rename to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/KadminOption.java
index 0c11fe7..f6caa87 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/KadminOption.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/KadminOption.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin;
 
 import org.apache.kerby.KOption;
 import org.apache.kerby.KOptionInfo;
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/AdminHelper.java
similarity index 98%
rename from kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java
rename to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/AdminHelper.java
index 62c38b6..f78ec45 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/AdminHelper.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/AdminHelper.java
@@ -17,16 +17,17 @@
  *  under the License. 
  *  
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin.local;
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
 import org.apache.kerby.kerberos.kerb.keytab.Keytab;
 import org.apache.kerby.kerberos.kerb.keytab.KeytabEntry;
-import org.apache.kerby.kerberos.kerb.type.KerberosTime;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
+import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
+import org.apache.kerby.kerberos.kerb.type.KerberosTime;
 import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
 
 import java.io.File;
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadmin.java
similarity index 90%
rename from kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java
rename to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadmin.java
index d8d38f1..5fd2d0d 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadmin.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadmin.java
@@ -17,9 +17,10 @@
  *  under the License. 
  *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin.local;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.Kadmin;
 import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
 import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
 import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
@@ -29,9 +30,9 @@
  * Server side admin facilities for local, similar to MIT kadmin local mode. It
  * may be not accurate regarding 'local' because, if the identity backend itself
  * is supported to be accessed from remote, it won't have to be remote; but if
- * not, then it must be local to the KDC server bounded with the local backend.
+ * not, then it must be local to the KDC admin bounded with the local backend.
  *
- * Note, suitable with Kerby KdcServer based KDCs like Kerby KDC.
+ * Note, suitable with Kerby AdminServerImpl based KDCs like Kerby KDC.
  */
 public interface LocalKadmin extends Kadmin {
 
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadminImpl.java
similarity index 92%
rename from kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java
rename to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadminImpl.java
index 9f0f89e..80fc10b 100644
--- a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/LocalKadminImpl.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/local/LocalKadminImpl.java
@@ -17,20 +17,21 @@
  *  under the License. 
  *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin.local;
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
-import org.apache.kerby.kerberos.kerb.common.KrbUtil;
-import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
-import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
 import org.apache.kerby.kerberos.kerb.identity.backend.IdentityBackend;
 import org.apache.kerby.kerberos.kerb.keytab.Keytab;
 import org.apache.kerby.kerberos.kerb.server.KdcConfig;
 import org.apache.kerby.kerberos.kerb.server.KdcSetting;
 import org.apache.kerby.kerberos.kerb.server.KdcUtil;
+import org.apache.kerby.kerberos.kerb.server.ServerSetting;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
+import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
 import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,16 +45,16 @@
 import java.util.regex.Pattern;
 
 /**
- * The implementation of server side admin facilities for local mode.
+ * The implementation of admin side admin facilities for local mode.
  */
 public class LocalKadminImpl implements LocalKadmin {
     private static final Logger LOG = LoggerFactory.getLogger(LocalKadminImpl.class);
 
-    private final KdcSetting kdcSetting;
+    private final ServerSetting serverSetting;
     private final IdentityBackend backend;
 
     /**
-     * Construct with prepared KdcConfig and BackendConfig.
+     * Construct with prepared AdminServerConfig and BackendConfig.
      *
      * @param kdcConfig     The kdc config
      * @param backendConfig The backend config
@@ -62,7 +63,13 @@
     public LocalKadminImpl(KdcConfig kdcConfig,
                            BackendConfig backendConfig) throws KrbException {
         this.backend = KdcUtil.getBackend(backendConfig);
-        this.kdcSetting = new KdcSetting(kdcConfig, backendConfig);
+        this.serverSetting = new KdcSetting(kdcConfig, backendConfig);
+    }
+
+    //
+    public LocalKadminImpl(ServerSetting serverSetting) throws KrbException {
+        this.backend = KdcUtil.getBackend(serverSetting.getBackendConfig());
+        this.serverSetting = serverSetting;
     }
 
     /**
@@ -82,19 +89,19 @@
             tmpBackendConfig = new BackendConfig();
         }
 
-        this.kdcSetting = new KdcSetting(tmpKdcConfig, tmpBackendConfig);
+        this.serverSetting = new KdcSetting(tmpKdcConfig, tmpBackendConfig);
 
         backend = KdcUtil.getBackend(tmpBackendConfig);
     }
 
     /**
-     * Construct with prepared KdcSetting and Backend.
+     * Construct with prepared AdminServerSetting and Backend.
      *
      * @param kdcSetting The kdc setting
      * @param backend    The identity backend
      */
     public LocalKadminImpl(KdcSetting kdcSetting, IdentityBackend backend) {
-        this.kdcSetting = kdcSetting;
+        this.serverSetting = kdcSetting;
         this.backend = backend;
     }
 
@@ -102,12 +109,12 @@
      * Get the tgs principal name.
      */
     private String getTgsPrincipal() {
-        return KrbUtil.makeTgsPrincipal(kdcSetting.getKdcRealm()).getName();
+        return KrbUtil.makeTgsPrincipal(serverSetting.getKdcRealm()).getName();
     }
 
     @Override
     public String getKadminPrincipal() {
-        return KrbUtil.makeKadminPrincipal(kdcSetting.getKdcRealm()).getName();
+        return KrbUtil.makeKadminPrincipal(serverSetting.getKdcRealm()).getName();
     }
 
     @Override
@@ -152,12 +159,12 @@
 
     @Override
     public KdcConfig getKdcConfig() {
-        return kdcSetting.getKdcConfig();
+        return serverSetting.getKdcConfig();
     }
 
     @Override
     public BackendConfig getBackendConfig() {
-        return kdcSetting.getBackendConfig();
+        return serverSetting.getBackendConfig();
     }
 
     @Override
@@ -393,7 +400,7 @@
      */
     private String fixPrincipal(String principal) {
         if (!principal.contains("@")) {
-            principal += "@" + kdcSetting.getKdcRealm();
+            principal += "@" + serverSetting.getKdcRealm();
         }
         return principal;
     }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java
new file mode 100644
index 0000000..01c336d
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminClient.java
@@ -0,0 +1,204 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl.DefaultInternalAdminClient;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl.InternalAdminClient;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A Admin client API for applications to interact with Admin Server
+ */
+public class AdminClient {
+
+    private final AdminConfig adminConfig;
+    private final KOptions commonOptions;
+    private final AdminSetting adminSetting;
+
+    private InternalAdminClient innerClient;
+
+    /**
+     * Default constructor.
+     * @throws KrbException e
+     */
+    public AdminClient() throws KrbException {
+        this.adminConfig = AdminUtil.getDefaultConfig();
+        this.commonOptions = new KOptions();
+        this.adminSetting = new AdminSetting(commonOptions, adminConfig);
+    }
+
+    /**
+     * Construct with prepared AdminConfig.
+     * @param adminConfig The krb config
+     */
+    public AdminClient(AdminConfig adminConfig) {
+        this.adminConfig = adminConfig;
+        this.commonOptions = new KOptions();
+        this.adminSetting = new AdminSetting(commonOptions, adminConfig);
+    }
+
+    /**
+     * Constructor with conf dir
+     * @param confDir The conf dir
+     * @throws KrbException e
+     */
+    public AdminClient(File confDir) throws KrbException {
+        this.commonOptions = new KOptions();
+        this.adminConfig = AdminUtil.getConfig(confDir);
+        this.adminSetting = new AdminSetting(commonOptions, adminConfig);
+    }
+
+    /**
+     * Constructor with prepared AdminClient.
+     * @param krbClient The krb client
+     */
+    public AdminClient(AdminClient krbClient) {
+        this.commonOptions = krbClient.commonOptions;
+        this.adminConfig = krbClient.adminConfig;
+        this.adminSetting = krbClient.adminSetting;
+        this.innerClient = krbClient.innerClient;
+    }
+
+    /**
+     * Set KDC realm for ticket request
+     * @param realm The realm
+     */
+    public void setAdminRealm(String realm) {
+        commonOptions.add(AdminOption.ADMIN_REALM, realm);
+    }
+
+    public void setKeyTabFile(File file) {
+        commonOptions.add(AdminOption.KEYTAB_FILE, file);
+    }
+
+    /**
+     * Set Admin Server host.
+     * @param kdcHost The kdc host
+     */
+    public void setKdcHost(String kdcHost) {
+        commonOptions.add(AdminOption.ADMIN_HOST, kdcHost);
+    }
+
+    /**
+     * Set Admin Server tcp port.
+     * @param kdcTcpPort The kdc tcp port
+     */
+    public void setAdminTcpPort(int kdcTcpPort) {
+        if (kdcTcpPort < 1) {
+            throw new IllegalArgumentException("Invalid port");
+        }
+        commonOptions.add(AdminOption.ADMIN_TCP_PORT, kdcTcpPort);
+        setAllowTcp(true);
+    }
+
+    /**
+     * Set to allow UDP or not.
+     * @param allowUdp true if allow udp
+     */
+    public void setAllowUdp(boolean allowUdp) {
+        commonOptions.add(AdminOption.ALLOW_UDP, allowUdp);
+    }
+
+    /**
+     * Set to allow TCP or not.
+     * @param allowTcp true if allow tcp
+     */
+    public void setAllowTcp(boolean allowTcp) {
+        commonOptions.add(AdminOption.ALLOW_TCP, allowTcp);
+    }
+
+    /**
+     * Set Admin Server udp port. Only makes sense when allowUdp is set.
+     * @param adminUdpPort The kdc udp port
+     */
+    public void setAdminUdpPort(int adminUdpPort) {
+        if (adminUdpPort < 1) {
+            throw new IllegalArgumentException("Invalid port");
+        }
+        commonOptions.add(AdminOption.ADMIN_UDP_PORT, adminUdpPort);
+        setAllowUdp(true);
+    }
+
+    /**
+     * Set time out for connection
+     * @param timeout in seconds
+     */
+    public void setTimeout(int timeout) {
+        commonOptions.add(AdminOption.CONN_TIMEOUT, timeout);
+    }
+
+    /**
+     * Init the client.
+     * @throws KrbException e
+     */
+    public void init() throws KrbException {
+        innerClient = new DefaultInternalAdminClient(adminSetting);
+        innerClient.init();
+    }
+
+    /**
+     * Get krb client settings from options and configs.
+     * @return setting
+     */
+    public AdminSetting getSetting() {
+        return adminSetting;
+    }
+
+    public AdminConfig getAdminConfig() {
+        return adminConfig;
+    }
+
+    public void requestAddPrincipal(String principal) throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        remote.addPrincipal(principal);
+    }
+
+    public void requestAddPrincipal(String principal, String password) throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        remote.addPrincipal(principal, password);
+    }
+
+    public void requestDeletePrincipal(String principal) throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        remote.deletePrincipal(principal);
+    }
+
+    public void requestRenamePrincipal(String oldPrincipal, String newPrincipal) throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        remote.renamePrincipal(oldPrincipal, newPrincipal);
+    }
+
+    public List<String> requestGetprincs() throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        List<String> principalLists = remote.getPrincipals();
+        return principalLists;
+    }
+
+    public List<String> requestGetprincsWithExp(String exp) throws KrbException {
+        Kadmin remote = new RemoteKadminImpl(innerClient);
+        List<String> principalLists = remote.getPrincipals(exp);
+        return principalLists;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfig.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfig.java
new file mode 100644
index 0000000..e2e6443
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfig.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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.kerberos.kerb.common.Krb5Conf;
+
+/**
+ * Kerb client side configuration API.
+ */
+public class AdminConfig extends Krb5Conf {
+    private static final String LIBDEFAULT = "libdefaults";
+
+    public boolean enableDebug() {
+        return getBoolean(AdminConfigKey.KRB_DEBUG, true, LIBDEFAULT);
+    }
+
+    /**
+     * Get KDC host name
+     *
+     * @return The kdc host
+     */
+    public String getAdminHost() {
+        return getString(
+            AdminConfigKey.ADMIN_HOST, true, LIBDEFAULT);
+    }
+
+    /**
+     * Get KDC port, as both TCP and UDP ports
+     *
+     * @return The kdc host
+     */
+    public int getAdminPort() {
+        Integer kdcPort = getInt(AdminConfigKey.ADMIN_PORT, true, LIBDEFAULT);
+        if (kdcPort != null) {
+            return kdcPort.intValue();
+        }
+        return -1;
+    }
+
+    /**
+     * Get KDC TCP port
+     *
+     * @return The kdc tcp port
+     */
+    public int getAdminTcpPort() {
+        Integer kdcPort = getInt(AdminConfigKey.ADMIN_TCP_PORT, true, LIBDEFAULT);
+        if (kdcPort != null && kdcPort > 0) {
+            return kdcPort.intValue();
+        }
+        return getAdminPort();
+    }
+
+    /**
+     * Is to allow UDP for KDC
+     *
+     * @return true to allow UDP, false otherwise
+     */
+    public boolean allowUdp() {
+        return getBoolean(AdminConfigKey.ADMIN_ALLOW_UDP, true, LIBDEFAULT)
+                || getInt(AdminConfigKey.ADMIN_UDP_PORT, true, LIBDEFAULT) != null
+            || getInt(AdminConfigKey.ADMIN_PORT, false, LIBDEFAULT) != null;
+    }
+
+    /**
+     * Is to allow TCP for KDC
+     *
+     * @return true to allow TCP, false otherwise
+     */
+    public boolean allowTcp() {
+        return getBoolean(AdminConfigKey.ADMIN_ALLOW_TCP, true, LIBDEFAULT)
+                || getInt(AdminConfigKey.ADMIN_TCP_PORT, true, LIBDEFAULT) != null
+            || getInt(AdminConfigKey.ADMIN_PORT, false, LIBDEFAULT) != null;
+    }
+
+    /**
+     * Get KDC UDP port
+     *
+     * @return The kdc udp port
+     */
+    public int getAdminUdpPort() {
+        Integer kdcPort = getInt(AdminConfigKey.ADMIN_UDP_PORT, true, LIBDEFAULT);
+        if (kdcPort != null && kdcPort > 0) {
+            return kdcPort.intValue();
+        }
+        return getAdminPort();
+    }
+
+    /**
+     * Get KDC realm.
+     * @return The kdc realm
+     */
+    public String getAdminRealm() {
+        String realm = getString(AdminConfigKey.ADMIN_REALM, false, LIBDEFAULT);
+        if (realm == null) {
+            realm = getString(AdminConfigKey.DEFAULT_REALM, false, LIBDEFAULT);
+            if (realm == null) {
+                realm = (String) AdminConfigKey.ADMIN_REALM.getDefaultValue();
+            }
+        }
+
+        return realm;
+    }
+
+    public String getKeyTabFile() {
+        return getString(AdminConfigKey.KEYTAB_FILE, true, LIBDEFAULT);
+    }
+
+    public String getProtocol() {
+        return getString(AdminConfigKey.PROTOCOL, true, LIBDEFAULT);
+    }
+
+    public String getServerName() {
+        return getString(AdminConfigKey.SERVER_NAME, true, LIBDEFAULT);
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfigKey.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfigKey.java
new file mode 100644
index 0000000..4227930
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminConfigKey.java
@@ -0,0 +1,58 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.config.ConfigKey;
+
+public enum AdminConfigKey implements ConfigKey {
+    KRB_DEBUG(true),
+    ADMIN_HOST("localhost"),
+    ADMIN_PORT(null),
+    ADMIN_ALLOW_UDP(false),
+    ADMIN_ALLOW_TCP(false),
+    ADMIN_UDP_PORT(null),
+    ADMIN_TCP_PORT(null),
+    ADMIN_DOMAIN("example.com"),
+    DEFAULT_REALM(null),
+    ADMIN_REALM("EXAMPLE.COM"),
+    KEYTAB_FILE,
+    PROTOCOL,
+    SERVER_NAME("localhost");
+
+    private Object defaultValue;
+
+    AdminConfigKey() {
+        this.defaultValue = null;
+    }
+
+    AdminConfigKey(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    @Override
+    public String getPropertyKey() {
+        return name().toLowerCase();
+    }
+
+    @Override
+    public Object getDefaultValue() {
+        return this.defaultValue;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminContext.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminContext.java
new file mode 100644
index 0000000..67219a6
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminContext.java
@@ -0,0 +1,49 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+public class AdminContext {
+
+    private AdminSetting adminSetting;
+
+    /**
+     * Init with krbsetting.
+     * @param adminSetting The krb setting
+     */
+    public void init(AdminSetting adminSetting) {
+        this.adminSetting = adminSetting;
+    }
+
+    /**
+     * Get krbsetting.
+     * @return The krb setting
+     */
+    public AdminSetting getAdminSetting() {
+        return adminSetting;
+    }
+
+    /**
+     * Get krbconfig.
+     * @return The krb config
+     */
+    public AdminConfig getConfig() {
+        return adminSetting.getAdminConfig();
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java
new file mode 100644
index 0000000..9debfdd
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminHandler.java
@@ -0,0 +1,162 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.AdminRequest;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminReq;
+import org.apache.kerby.kerberos.kerb.admin.message.KadminCode;
+import org.apache.kerby.xdr.XdrFieldInfo;
+import org.apache.kerby.xdr.type.XdrStructType;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.List;
+
+public abstract class AdminHandler {
+
+    /**
+     * Init with krbcontext.
+     *
+     * @param context The krbcontext
+     */
+    public void init(AdminContext context) {
+
+    }
+
+    /**
+     * Handle the kdc request.
+     *
+     * @param adminRequest The admin request
+     * @throws KrbException e
+     */
+    public void handleRequest(AdminRequest adminRequest) throws KrbException {
+        adminRequest.process();
+        AdminReq adminReq = adminRequest.getAdminReq();
+        ByteBuffer requestMessage = KadminCode.encodeMessage(adminReq);
+        requestMessage.flip();
+
+        try {
+            sendMessage(adminRequest, requestMessage);
+        } catch (IOException e) {
+            throw new KrbException("Admin sends request message failed", e);
+        }
+
+    }
+
+    /**
+     * Process the response message from kdc.
+     *
+     * @param adminRequest The admin request
+     * @param responseMessage The message from kdc
+     * @throws KrbException e
+     */
+    public void onResponseMessage(AdminRequest adminRequest,
+                                  ByteBuffer responseMessage) throws KrbException {
+
+
+        XdrStructType decoded = new AdminMessageCode();
+        try {
+            decoded.decode(responseMessage);
+        } catch (IOException e) {
+            throw new KrbException("On response message failed.", e);
+        }
+        XdrFieldInfo[] fieldInfos = decoded.getValue().getXdrFieldInfos();
+        AdminMessageType type = (AdminMessageType) fieldInfos[0].getValue();
+
+        switch (type) {
+            case ADD_PRINCIPAL_REP:
+                if (adminRequest.getAdminReq().getAdminMessageType()
+                    == AdminMessageType.ADD_PRINCIPAL_REQ) {
+                    System.out.println((String) fieldInfos[2].getValue());
+                } else {
+                    throw new KrbException("Response message type error: need "
+                    + AdminMessageType.ADD_PRINCIPAL_REP);
+                }
+                break;
+            case DELETE_PRINCIPAL_REP:
+                if (adminRequest.getAdminReq().getAdminMessageType()
+                    == AdminMessageType.DELETE_PRINCIPAL_REQ) {
+                    System.out.println((String) fieldInfos[2].getValue());
+                } else {
+                    throw new KrbException("Response message type error: need "
+                    + AdminMessageType.DELETE_PRINCIPAL_REP);
+                }
+                break;
+            case RENAME_PRINCIPAL_REP:
+                if (adminRequest.getAdminReq().getAdminMessageType()
+                    == AdminMessageType.RENAME_PRINCIPAL_REQ) {
+                    System.out.println((String) fieldInfos[2].getValue());
+                } else {
+                    throw new KrbException("Response message type error: need "
+                    + AdminMessageType.RENAME_PRINCIPAL_REP);
+                }
+                break;
+            default:
+                throw new KrbException("Response message type error: " + type);
+        }
+    }
+
+    public List<String> onResponseMessageForList(AdminRequest adminRequest,
+                                  ByteBuffer responseMessage) throws KrbException {
+        List<String> princalsList = null;
+
+        XdrStructType decoded = new AdminMessageCode();
+        try {
+            decoded.decode(responseMessage);
+        } catch (IOException e) {
+            throw new KrbException("On response message failed.", e);
+        }
+        XdrFieldInfo[] fieldInfos = decoded.getValue().getXdrFieldInfos();
+        AdminMessageType type = (AdminMessageType) fieldInfos[0].getValue();
+
+        switch (type) {
+            case GET_PRINCS_REP:
+                if (adminRequest.getAdminReq().getAdminMessageType()
+                        == AdminMessageType.GET_PRINCS_REQ) {
+                    String[] temp = ((String) fieldInfos[2].getValue()).trim().split(" ");
+                    princalsList = Arrays.asList(temp);
+                } else {
+                    throw new KrbException("Response message type error: need "
+                            + AdminMessageType.GET_PRINCS_REP);
+                }
+                break;
+            default:
+                throw new KrbException("Response message type error: " + type);
+        }
+
+        return princalsList;
+    }
+
+    /**
+     * Send message to kdc.
+     *
+     * @param adminRequest The admin request
+     * @param requestMessage The request message to kdc
+     * @throws IOException e
+     */
+    protected abstract void sendMessage(AdminRequest adminRequest,
+                                        ByteBuffer requestMessage) throws IOException;
+
+    protected abstract List<String> handleRequestForList(AdminRequest adminRequest) throws KrbException;
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminOption.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminOption.java
new file mode 100644
index 0000000..fc2d45b
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminOption.java
@@ -0,0 +1,102 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.KOption;
+import org.apache.kerby.KOptionInfo;
+import org.apache.kerby.KOptionType;
+
+/**
+ * This defines all the options that come across the client side.
+ */
+public enum AdminOption implements KOption {
+    NONE(null),
+
+    ADMIN_REALM(new KOptionInfo("admin-realm", "kdc realm",
+        KOptionType.STR)),
+    ADMIN_HOST(new KOptionInfo("admin-host", "kdc host",
+        KOptionType.STR)),
+    ADMIN_TCP_PORT(new KOptionInfo("admin-tcp-port", "kdc tcp port",
+        KOptionType.INT)),
+    ALLOW_UDP(new KOptionInfo("allow-udp", "allow udp",
+        KOptionType.BOOL)),
+    ALLOW_TCP(new KOptionInfo("allow-tcp", "allow tcp",
+        KOptionType.BOOL)),
+    ADMIN_UDP_PORT(new KOptionInfo("admin-udp-port", "kdc udp port",
+        KOptionType.INT)),
+    CONN_TIMEOUT(new KOptionInfo("conn-timeout", "connection timeout",
+        KOptionType.INT)),
+
+    LIFE_TIME(new KOptionInfo("life-time", "life time",
+        KOptionType.INT)),
+    START_TIME(new KOptionInfo("start-time", "start time",
+        KOptionType.INT)),
+    RENEWABLE_TIME(new KOptionInfo("renewable_lifetime", "renewable lifetime",
+        KOptionType.INT)),
+    INCLUDE_ADDRESSES(new KOptionInfo("include_addresses",
+        "include addresses")),
+    AS_ENTERPRISE_PN(new KOptionInfo("as-enterprise-pn",
+        "client is enterprise principal name")),
+    CLIENT_PRINCIPAL(new KOptionInfo("client-principal", "Client principal",
+        KOptionType.STR)),
+
+    USE_PASSWD(new KOptionInfo("using-password", "using password")),
+    USER_PASSWD(new KOptionInfo("user-passwd", "User plain password")),
+
+    USE_KEYTAB(new KOptionInfo("use-keytab", "use keytab")),
+    USE_DFT_KEYTAB(new KOptionInfo("use-dft-keytab", "use default client keytab (with -k)")),
+    KEYTAB_FILE(new KOptionInfo("keytab-file", "filename of keytab to use",
+        KOptionType.FILE)),
+
+    KRB5_CACHE(new KOptionInfo("krb5-cache", "K5 cache name",
+        KOptionType.FILE)),
+    SERVICE_PRINCIPAL(new KOptionInfo("service-principal", "service principal",
+        KOptionType.STR)),
+    SERVER_PRINCIPAL(new KOptionInfo("admin-principal", "admin principal",
+        KOptionType.STR)),
+    ARMOR_CACHE(new KOptionInfo("armor-cache", "armor credential cache",
+        KOptionType.STR)),
+    USE_TGT(new KOptionInfo("use-tgt", "use tgt to get service ticket",
+        KOptionType.OBJ)),
+    CONF_DIR(new KOptionInfo("-conf", "conf dir", KOptionType.DIR));
+
+    private final KOptionInfo optionInfo;
+
+    AdminOption(KOptionInfo optionInfo) {
+        this.optionInfo = optionInfo;
+    }
+
+    @Override
+    public KOptionInfo getOptionInfo() {
+        return optionInfo;
+    }
+
+    public static AdminOption fromOptionName(String optionName) {
+        if (optionName != null) {
+            for (AdminOption ko : values()) {
+                if (ko.optionInfo != null
+                    && ko.optionInfo.getName().equals(optionName)) {
+                    return ko;
+                }
+            }
+        }
+        return NONE;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminSetting.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminSetting.java
new file mode 100644
index 0000000..1e519ea
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminSetting.java
@@ -0,0 +1,129 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+
+/**
+ * Admin client setting that combines common options and client config.
+ */
+public class AdminSetting {
+    private final KOptions commonOptions;
+    private final AdminConfig adminConfig;
+
+    public AdminSetting(KOptions commonOptions, AdminConfig config) {
+        this.commonOptions = commonOptions;
+        this.adminConfig = config;
+    }
+
+    public AdminSetting(AdminConfig config) {
+        this.commonOptions = new KOptions();
+        this.adminConfig = config;
+    }
+
+    public AdminConfig getAdminConfig() {
+        return adminConfig;
+    }
+
+    public String getKdcRealm() {
+        String kdcRealm = commonOptions.getStringOption(AdminOption.ADMIN_REALM);
+        if (kdcRealm == null || kdcRealm.isEmpty()) {
+            kdcRealm = adminConfig.getAdminRealm();
+        }
+        return kdcRealm;
+    }
+
+    public String getKdcHost() {
+        String kdcHost = commonOptions.getStringOption(AdminOption.ADMIN_HOST);
+        if (kdcHost == null) {
+            return adminConfig.getAdminHost();
+        }
+        return kdcHost;
+    }
+
+    /**
+     * Check kdc tcp setting and see if any bad.
+     * @return valid tcp port or -1 if not allowTcp
+     * @throws KrbException e
+     */
+    public int checkGetKdcTcpPort() throws KrbException {
+        if (allowTcp()) {
+            int kdcPort = getKdcTcpPort();
+            if (kdcPort < 1) {
+                throw new KrbException("KDC tcp port isn't set or configured");
+            }
+            return kdcPort;
+        }
+        return -1;
+    }
+
+    /**
+     * Check kdc udp setting and see if any bad.
+     * @return valid udp port or -1 if not allowUdp
+     * @throws KrbException e
+     */
+    public int checkGetKdcUdpPort() throws KrbException {
+        if (allowUdp()) {
+            int kdcPort = getKdcUdpPort();
+            if (kdcPort < 1) {
+                throw new KrbException("KDC udp port isn't set or configured");
+            }
+            return kdcPort;
+        }
+        return -1;
+    }
+
+    public int getKdcTcpPort() {
+        int tcpPort = commonOptions.getIntegerOption(AdminOption.ADMIN_TCP_PORT);
+        if (tcpPort > 0) {
+            return tcpPort;
+        }
+        return adminConfig.getAdminTcpPort();
+    }
+
+    public boolean allowUdp() {
+        Boolean allowUdp = commonOptions.getBooleanOption(
+                AdminOption.ALLOW_UDP, adminConfig.allowUdp());
+        return allowUdp;
+    }
+
+    public boolean allowTcp() {
+        Boolean allowTcp = commonOptions.getBooleanOption(
+                AdminOption.ALLOW_TCP, adminConfig.allowTcp());
+        return allowTcp;
+    }
+
+    public int getKdcUdpPort() {
+        int udpPort = commonOptions.getIntegerOption(AdminOption.ADMIN_UDP_PORT);
+        if (udpPort > 0) {
+            return udpPort;
+        }
+        return adminConfig.getAdminUdpPort();
+    }
+
+    public int getTimeout() {
+        int timeout = commonOptions.getIntegerOption(AdminOption.CONN_TIMEOUT);
+        if (timeout > 0) {
+            return timeout;
+        }
+        return 1000; // by default
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminUtil.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminUtil.java
new file mode 100644
index 0000000..00cc409
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/AdminUtil.java
@@ -0,0 +1,127 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Map;
+
+public final class AdminUtil {
+    private AdminUtil() { }
+
+    private static final String KRB5_FILE_NAME = "krb5.conf";
+    private static final String KRB5_ENV_NAME = "KRB5_CONFIG";
+
+    /**
+     * Load krb5.conf from specified conf dir.
+     * @param confDir The conf dir
+     * @return AdminConfig
+     * @throws KrbException e
+     */
+    public static AdminConfig getConfig(File confDir) throws KrbException {
+        File confFile = new File(confDir, KRB5_FILE_NAME);
+        if (!confFile.exists()) {
+            throw new KrbException(KRB5_FILE_NAME + " not found");
+        }
+
+        if (confFile != null && confFile.exists()) {
+            AdminConfig adminConfig = new AdminConfig();
+            try {
+                adminConfig.addKrb5Config(confFile);
+                return adminConfig;
+            } catch (IOException e) {
+                throw new KrbException("Failed to load krb config "
+                        + confFile.getAbsolutePath());
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Load default krb5.conf
+     * @return The AdminConfig
+     * @throws KrbException e
+     */
+    public static AdminConfig getDefaultConfig() throws KrbException {
+        File confFile = null;
+        File confDir;
+        String tmpEnv;
+
+        try {
+            Map<String, String> mapEnv = System.getenv();
+            tmpEnv = mapEnv.get(KRB5_ENV_NAME);
+        } catch (SecurityException e) {
+            tmpEnv = null;
+        }
+        if (tmpEnv != null) {
+            confFile = new File(tmpEnv);
+            if (!confFile.exists()) {
+                throw new KrbException("krb5 conf not found. Invalid env "
+                        + KRB5_ENV_NAME);
+            }
+        } else {
+            confDir = new File("/etc/"); // for Linux. TODO: fix for Win etc.
+            if (confDir.exists()) {
+                confFile = new File(confDir, "krb5.conf");
+            }
+        }
+
+        AdminConfig adminConfig = new AdminConfig();
+        if (confFile != null && confFile.exists()) {
+            try {
+                adminConfig.addKrb5Config(confFile);
+            } catch (IOException e) {
+                throw new KrbException("Failed to load krb config "
+                        + confFile.getAbsolutePath());
+            }
+        }
+
+        return adminConfig;
+    }
+
+    /**
+     * Get KDC network transport addresses according to krb client setting.
+     * @param setting The krb setting
+     * @return UDP and TCP addresses pair
+     * @throws KrbException e
+     */
+    public static TransportPair getTransportPair(
+            AdminSetting setting) throws KrbException {
+        TransportPair result = new TransportPair();
+
+        int tcpPort = setting.checkGetKdcTcpPort();
+        if (tcpPort > 0) {
+            result.tcpAddress = new InetSocketAddress(
+                    setting.getKdcHost(), tcpPort);
+        }
+        int udpPort = setting.checkGetKdcUdpPort();
+        if (udpPort > 0) {
+            result.udpAddress = new InetSocketAddress(
+                    setting.getKdcHost(), udpPort);
+        }
+
+        return result;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java
new file mode 100644
index 0000000..c4ac154
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/RemoteKadminImpl.java
@@ -0,0 +1,207 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl.DefaultAdminHandler;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl.InternalAdminClient;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.AddPrincipalRequest;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.AdminRequest;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.DeletePrincipalRequest;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.GetprincsRequest;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.RenamePrincipalRequest;
+import org.apache.kerby.kerberos.kerb.common.KrbUtil;
+import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Server side admin facilities from remote, similar to MIT Kadmin remote mode.
+ * It uses GSSAPI and XDR to communicate with remote KDC/kadmind to do the
+ * requested operations. In the client side, it simply wraps and sends the
+ * request info to the admin kadmind side, and then unwraps the response for
+ * the operation result.
+ *
+ * TO BE IMPLEMENTED.
+ */
+public class RemoteKadminImpl implements Kadmin {
+
+    private InternalAdminClient innerClient;
+    private KrbTransport transport;
+
+    public RemoteKadminImpl(InternalAdminClient innerClient) throws KrbException {
+        this.innerClient = innerClient;
+        TransportPair tpair = null;
+        try {
+            tpair = AdminUtil.getTransportPair(innerClient.getSetting());
+        } catch (KrbException e) {
+            e.printStackTrace();
+        }
+        KrbNetwork network = new KrbNetwork();
+        network.setSocketTimeout(innerClient.getSetting().getTimeout());
+        try {
+            transport = network.connect(tpair);
+        } catch (IOException e) {
+            throw new KrbException("Failed to create transport", e);
+        }
+    }
+
+    public InternalAdminClient getInnerClient() {
+        return innerClient;
+    }
+
+
+    @Override
+    public String getKadminPrincipal() {
+        return KrbUtil.makeKadminPrincipal(innerClient.getSetting().getKdcRealm()).getName();
+    }
+
+    @Override
+    public void addPrincipal(String principal) throws KrbException {
+        //generate an admin request
+        AdminRequest adRequest = new AddPrincipalRequest(principal);
+        adRequest.setTransport(transport);
+        //handle it
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        adminHandler.handleRequest(adRequest);
+
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             KOptions kOptions) throws KrbException {
+        AdminRequest adRequest = new AddPrincipalRequest(principal, kOptions);
+        //wrap buffer problem
+        adRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        adminHandler.handleRequest(adRequest);
+    }
+
+    @Override
+    public void addPrincipal(String principal,
+                             String password) throws KrbException {
+        AdminRequest addPrincipalRequest = new AddPrincipalRequest(principal, password);
+        addPrincipalRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        adminHandler.handleRequest(addPrincipalRequest);
+    }
+
+    @Override
+    public void addPrincipal(String principal, String password,
+                             KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile,
+                             List<String> principals) throws KrbException {
+
+    }
+
+    @Override
+    public void exportKeytab(File keytabFile) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile,
+                                      String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void removeKeytabEntriesOf(File keytabFile, String principal,
+                                      int kvno) throws KrbException {
+
+    }
+
+    @Override
+    public void removeOldKeytabEntriesOf(File keytabFile,
+                                         String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void deletePrincipal(String principal) throws KrbException {
+        AdminRequest deletePrincipalRequest = new DeletePrincipalRequest(principal);
+        deletePrincipalRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        adminHandler.handleRequest(deletePrincipalRequest);
+    }
+
+    @Override
+    public void modifyPrincipal(String principal,
+                                KOptions kOptions) throws KrbException {
+
+    }
+
+    @Override
+    public void renamePrincipal(String oldPrincipalName,
+                                String newPrincipalName) throws KrbException {
+        AdminRequest renamePrincipalRequest =  new RenamePrincipalRequest(oldPrincipalName, newPrincipalName);
+        renamePrincipalRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        adminHandler.handleRequest(renamePrincipalRequest);
+    }
+
+    @Override
+    public List<String> getPrincipals() throws KrbException {
+        AdminRequest grtPrincsRequest = new GetprincsRequest();
+        grtPrincsRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        return adminHandler.handleRequestForList(grtPrincsRequest);
+    }
+
+    @Override
+    public List<String> getPrincipals(String globString) throws KrbException {
+        AdminRequest grtPrincsRequest = new GetprincsRequest(globString);
+        grtPrincsRequest.setTransport(transport);
+        AdminHandler adminHandler = new DefaultAdminHandler();
+        return adminHandler.handleRequestForList(grtPrincsRequest);
+    }
+
+    @Override
+    public void changePassword(String principal,
+                               String newPassword) throws KrbException {
+
+    }
+
+    @Override
+    public void updateKeys(String principal) throws KrbException {
+
+    }
+
+    @Override
+    public void release() throws KrbException {
+
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteAddPrincipalCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteAddPrincipalCommand.java
new file mode 100644
index 0000000..d3d27a5
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteAddPrincipalCommand.java
@@ -0,0 +1,65 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+
+/**
+ * Remote add principal command
+ */
+public class RemoteAddPrincipalCommand extends RemoteCommand {
+
+    public static final String USAGE = "Usage: add_principal [options] <principal-name>\n"
+        + "\toptions are:\n"
+        + "\t\t[-randkey|-nokey]\n"
+        + "\t\t[-pw password]"
+        + "\tExample:\n"
+        + "\t\tadd_principal -pw mypassword alice\n";
+
+    public RemoteAddPrincipalCommand(AdminClient adminClient) {
+        super(adminClient);
+    }
+
+    @Override
+    public void execute(String input) throws KrbException {
+        String[] items = input.split("\\s+");
+
+        if (items.length < 2) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        String adminRealm = adminClient.getAdminConfig().getAdminRealm();
+        String clientPrincipal = items[items.length - 1] + "@" + adminRealm;
+
+        if (!items[1].startsWith("-")) {
+            adminClient.requestAddPrincipal(clientPrincipal);
+        } else if (items[1].startsWith("-nokey")) {
+            adminClient.requestAddPrincipal(clientPrincipal);
+        } else if (items[1].startsWith("-pw")) {
+            String password = items[2];
+            adminClient.requestAddPrincipal(clientPrincipal, password);
+        } else {
+            System.err.println("add_principal command format error.");
+            System.err.println(USAGE);
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteCommand.java
new file mode 100644
index 0000000..d5ffe0f
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteCommand.java
@@ -0,0 +1,41 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+
+/**
+ * Abstract class of all remote kadmin commands
+ */
+public abstract class RemoteCommand {
+
+    AdminClient adminClient;
+
+    public RemoteCommand(AdminClient adminClient) {
+        this.adminClient = adminClient;
+    }
+
+    /**
+     * Execute the remote kadmin command
+     * @param input String includes commands
+     */
+    public abstract void execute(String input) throws KrbException;
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteDeletePrincipalCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteDeletePrincipalCommand.java
new file mode 100644
index 0000000..e6368bd
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteDeletePrincipalCommand.java
@@ -0,0 +1,83 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+
+import java.io.Console;
+import java.util.Scanner;
+
+/**
+ * Remote delete principal command
+ */
+public class RemoteDeletePrincipalCommand extends RemoteCommand {
+
+    public static final String USAGE = "Usage: delete_principal <principal-name>\n"
+        + "\tExample:\n"
+        + "\t\tdelete_principal alice\n";
+
+    public RemoteDeletePrincipalCommand(AdminClient adminClient) {
+        super(adminClient);
+    }
+
+    @Override
+    public void execute(String input) throws KrbException {
+        String[] items = input.split("\\s+");
+        if (items.length < 2) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        String principal = items[items.length - 1] + "@"
+            + adminClient.getAdminConfig().getAdminRealm();
+        String reply;
+        Console console = System.console();
+        String prompt = "Are you sure to delete the principal? (yes/no, YES/NO, y/n, Y/N) ";
+        if (console == null) {
+            System.out.println("Couldn't get Console instance, "
+                + "maybe you're running this from within an IDE. "
+                + "Use scanner to read password.");
+            Scanner scanner = new Scanner(System.in, "UTF-8");
+            reply = getReply(scanner, prompt);
+        } else {
+            reply = getReply(console, prompt);
+        }
+        if (reply.equals("yes") || reply.equals("YES") || reply.equals("y") || reply.equals("Y")) {
+            adminClient.requestDeletePrincipal(principal);
+        } else if (reply.equals("no") || reply.equals("NO") || reply.equals("n") || reply.equals("N")) {
+            System.out.println("Principal \"" + principal + "\"  not deleted.");
+        } else {
+            System.err.println("Unknown request, fail to delete the principal.");
+            System.err.println(USAGE);
+        }
+    }
+
+    private String getReply(Scanner scanner, String prompt) {
+        System.out.println(prompt);
+        return scanner.nextLine().trim();
+    }
+
+    private String getReply(Console console, String prompt) {
+        console.printf(prompt);
+        String line = console.readLine();
+        return line;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteGetprincsCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteGetprincsCommand.java
new file mode 100644
index 0000000..2e15281
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteGetprincsCommand.java
@@ -0,0 +1,65 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+import java.util.List;
+
+public class RemoteGetprincsCommand extends RemoteCommand {
+    private static final String USAGE = "Usage: list_principals [expression]\n"
+            + "\t'expression' is a shell-style glob expression that can contain the wild-card characters ?, *, and []."
+            + "\tExample:\n"
+            + "\t\tlist_principals [expression]\n";
+
+    public RemoteGetprincsCommand(AdminClient adminClient) {
+        super(adminClient);
+    }
+
+    @Override
+    public void execute(String input) throws KrbException {
+        String[] items = input.split("\\s+");
+        //String param = items[0];
+        if (items.length > 2) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        List<String> principalLists = null;
+
+        if (items.length == 1) {
+            principalLists = adminClient.requestGetprincs();
+        } else {
+            //have expression
+            String exp = items[1];
+            principalLists = adminClient.requestGetprincsWithExp(exp);
+        }
+
+        if (principalLists.size() == 0 || principalLists.size() == 1 && principalLists.get(0).isEmpty()) {
+            return;
+        } else {
+            System.out.println("Principals are listed:");
+            for (int i = 0; i < principalLists.size(); i++) {
+                System.out.println(principalLists.get(i));
+            }
+        }
+    }
+
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemotePrintUsageCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemotePrintUsageCommand.java
new file mode 100644
index 0000000..a27e252
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemotePrintUsageCommand.java
@@ -0,0 +1,42 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+
+public class RemotePrintUsageCommand extends RemoteCommand {
+
+    private static final String LISTPRINCSUSAGE = "Usage: list_principals [expression]\n"
+            + "\t'expression' is a shell-style glob expression that can contain "
+            + "the wild-card characters ?, *, and [].\n"
+            + "\tExample:\n"
+            + "\t\tlist_principals [expression]\n";
+
+    public RemotePrintUsageCommand() {
+        super(null);
+    }
+
+    @Override
+    public void execute(String input) throws KrbException {
+        if (input.startsWith("listprincs")) {
+            System.out.println(LISTPRINCSUSAGE);
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteRenamePrincipalCommand.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteRenamePrincipalCommand.java
new file mode 100644
index 0000000..fd0cd61
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/command/RemoteRenamePrincipalCommand.java
@@ -0,0 +1,85 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.command;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
+
+import java.io.Console;
+import java.util.Scanner;
+
+/**
+ * Remote rename principal command
+ */
+public class RemoteRenamePrincipalCommand extends RemoteCommand {
+    public static final String USAGE = "Usage: rename_principal <old_principal_name>"
+        + " <new_principal_name>\n"
+        + "\tExample:\n"
+        + "\t\trename_principal alice bob\n";
+
+    public RemoteRenamePrincipalCommand(AdminClient adminClient) {
+        super(adminClient);
+    }
+
+    @Override
+    public void execute(String input) throws KrbException {
+        String[] items = input.split("\\s+");
+        if (items.length < 3) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        String adminRealm = adminClient.getAdminConfig().getAdminRealm();
+        String oldPrincipalName = items[items.length - 2] + "@" + adminRealm;
+        String newPrincipalName = items[items.length - 1] + "@" + adminRealm;
+
+        String reply;
+        Console console = System.console();
+        String prompt = "Are you sure to rename the principal? (yes/no, YES/NO, y/n, Y/N) ";
+        if (console == null) {
+            System.out.println("Couldn't get Console instance, "
+                + "maybe you're running this from within an IDE. "
+                + "Use scanner to read password.");
+            Scanner scanner = new Scanner(System.in, "UTF-8");
+            reply = getReply(scanner, prompt);
+        } else {
+            reply = getReply(console, prompt);
+        }
+        if (reply.equals("yes") || reply.equals("YES") || reply.equals("y") || reply.equals("Y")) {
+            adminClient.requestRenamePrincipal(oldPrincipalName, newPrincipalName);
+        } else if (reply.equals("no") || reply.equals("NO") || reply.equals("n") || reply.equals("N")) {
+            System.out.println("Principal \"" + oldPrincipalName + "\"  not renamed.");
+        } else {
+            System.err.println("Unknown request, fail to rename the principal.");
+            System.err.println(USAGE);
+        }
+    }
+
+    private String getReply(Scanner scanner, String prompt) {
+        System.out.println(prompt);
+        return scanner.nextLine().trim();
+    }
+
+    private String getReply(Console console, String prompt) {
+        console.printf(prompt);
+        String line = console.readLine();
+        return line;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/AbstractInternalAdminClient.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/AbstractInternalAdminClient.java
new file mode 100644
index 0000000..1f1635f
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/AbstractInternalAdminClient.java
@@ -0,0 +1,71 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.impl;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminContext;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminSetting;
+
+/**
+ * A krb client API for applications to interact with KDC
+ */
+public abstract class AbstractInternalAdminClient
+                                    implements InternalAdminClient {
+    private AdminContext context;
+    private final AdminSetting krbSetting;
+
+    public AbstractInternalAdminClient(AdminSetting krbSetting) {
+        this.krbSetting = krbSetting;
+    }
+
+    protected AdminContext getContext() {
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AdminSetting getSetting() {
+        return krbSetting;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void init() throws KrbException {
+        context = new AdminContext();
+        context.init(krbSetting);
+    }
+
+    /**
+     * Fix principal name.
+     *
+     * @param principal The principal name
+     * @return The fixed principal
+     */
+    protected String fixPrincipal(String principal) {
+        if (!principal.contains("@")) {
+            principal += "@" + krbSetting.getKdcRealm();
+        }
+        return principal;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java
new file mode 100644
index 0000000..3d05b50
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultAdminHandler.java
@@ -0,0 +1,79 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.impl;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminHandler;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.request.AdminRequest;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class DefaultAdminHandler extends AdminHandler {
+
+    /**
+     * Use super.handleRequest to send message
+     * and use this to receive message.
+     */
+    @Override
+    public void handleRequest(AdminRequest adminRequest) throws KrbException {
+        /**super is used to send message*/
+        super.handleRequest(adminRequest);
+
+        KrbTransport transport = adminRequest.getTransport();
+        ByteBuffer receiveMessage = null;
+        try {
+            receiveMessage = transport.receiveMessage();
+        } catch (IOException e) {
+            throw new KrbException("Admin receives response message failed", e);
+        }
+        super.onResponseMessage(adminRequest, receiveMessage);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void sendMessage(AdminRequest adminRequest,
+                               ByteBuffer requestMessage) throws IOException {
+        KrbTransport transport = adminRequest.getTransport();
+        transport.sendMessage(requestMessage);
+    }
+
+    @Override
+    public List<String> handleRequestForList(AdminRequest adminRequest) throws KrbException {
+        /**send message*/
+        super.handleRequest(adminRequest);
+
+        KrbTransport transport = adminRequest.getTransport();
+        ByteBuffer receiveMessage = null;
+        List<String> prinicalList = null;
+        try {
+            receiveMessage = transport.receiveMessage();
+            prinicalList = super.onResponseMessageForList(adminRequest, receiveMessage);
+        } catch (IOException e) {
+            throw new KrbException("Admin receives response message failed", e);
+        }
+
+        return prinicalList;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultInternalAdminClient.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultInternalAdminClient.java
new file mode 100644
index 0000000..2d40b6f
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/DefaultInternalAdminClient.java
@@ -0,0 +1,71 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.impl;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminHandler;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminSetting;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminUtil;
+import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.apache.kerby.kerberos.kerb.transport.TransportPair;
+
+import java.io.IOException;
+
+/**
+ * A default krb client implementation.
+ */
+public class DefaultInternalAdminClient extends AbstractInternalAdminClient {
+
+    private DefaultAdminHandler adminHandler;
+    private KrbTransport transport;
+
+    public DefaultInternalAdminClient(AdminSetting krbSetting) {
+        super(krbSetting);
+    }
+
+    public AdminHandler getAdminHanlder() {
+        return adminHandler;
+    }
+
+    public KrbTransport getTransport() {
+        return transport;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void init() throws KrbException {
+        super.init();
+
+        this.adminHandler = new DefaultAdminHandler();
+        adminHandler.init(getContext());
+
+        TransportPair tpair = AdminUtil.getTransportPair(getSetting());
+        KrbNetwork network = new KrbNetwork();
+        network.setSocketTimeout(getSetting().getTimeout());
+        try {
+            transport = network.connect(tpair);
+        } catch (IOException e) {
+            throw new KrbException("Failed to create transport", e);
+        }
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/InternalAdminClient.java
similarity index 61%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/InternalAdminClient.java
index 325f1db..47bfd3d 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/impl/InternalAdminClient.java
@@ -17,8 +17,25 @@
  *  under the License. 
  *  
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.kadmin.remote.impl;
 
-public class KadminTest {
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminSetting;
 
+/**
+ * An internal krb client interface.
+ */
+public interface InternalAdminClient {
+
+    /**
+     * Init with all the necessary options.
+     * @throws KrbException e
+     */
+    void init() throws KrbException;
+
+    /**
+     * Get krb client settings.
+     * @return setting
+     */
+    AdminSetting getSetting();
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AddPrincipalRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AddPrincipalRequest.java
new file mode 100644
index 0000000..96622bc
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AddPrincipalRequest.java
@@ -0,0 +1,114 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.request;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.message.AddPrincipalReq;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * AddPrincipal request
+ */
+public class AddPrincipalRequest extends AdminRequest {
+
+    private KOptions kOptions;
+    private String password;
+
+    public AddPrincipalRequest(String principal) {
+        super(principal);
+    }
+
+    public AddPrincipalRequest(String principal, KOptions kOptions) {
+        super(principal);
+        this.kOptions = kOptions;
+    }
+
+    public AddPrincipalRequest(String principal, String password) {
+        super(principal);
+        this.password = password;
+    }
+
+    public AddPrincipalRequest(String princial, KOptions kOptions, String password) {
+        super(princial);
+        this.kOptions = kOptions;
+        this.password = password;
+    }
+
+
+    @Override
+    public void process() throws KrbException {
+        super.process();
+        /**replace this with encode in handler*/
+        AddPrincipalReq addPrincipalReq = new AddPrincipalReq();
+        /** encode admin message:
+         *  encode type
+         *  encode paranum
+         *  encode principal name
+         *  (encode koptions)
+         *  (encode passsword)
+         */
+        int paramNum = getParamNum();
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[paramNum + 2];
+        xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.ADD_PRINCIPAL_REQ);
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, paramNum);
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, getPrincipal());
+        if (paramNum == 2 && kOptions != null) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRUCT, kOptions); /////koption
+        } else if (paramNum == 2 && password != null) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRING, password);
+        } else if (paramNum == 3) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRUCT, kOptions); ////koption
+            xdrFieldInfos[4] = new XdrFieldInfo(4, XdrDataType.STRING, password);
+        }
+        AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
+        byte[] encodeBytes;
+        try {
+            encodeBytes = value.encode();
+        } catch (IOException e) {
+            throw new KrbException("Xdr encode error when generate add principal request.", e);
+        }
+        ByteBuffer messageBuffer = ByteBuffer.wrap(encodeBytes);
+        addPrincipalReq.setMessageBuffer(messageBuffer);
+
+        setAdminReq(addPrincipalReq);
+    }
+
+    public int getParamNum() {
+        int paramNum = 0;
+        if (getPrincipal() == null) {
+            throw new RuntimeException("Principal name missing.");
+        }
+        if (kOptions == null && password == null) {
+            paramNum = 1;
+        } else if (kOptions == null || password == null) {
+            paramNum = 2;
+        } else {
+            paramNum = 3;
+        }
+        return paramNum;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AdminRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AdminRequest.java
new file mode 100644
index 0000000..7c8d152
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/AdminRequest.java
@@ -0,0 +1,63 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.request;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminReq;
+
+public class AdminRequest {
+    private String principal;
+    private KrbTransport transport;
+    private AdminReq adminReq;
+
+    public AdminRequest(String principal) {
+        this.principal = principal;
+    }
+
+    public String getPrincipal() {
+        return principal;
+    }
+
+    public void setPrincipal(String principal) {
+        this.principal = principal;
+    }
+
+    public void setAdminReq(AdminReq adminReq) {
+        this.adminReq = adminReq;
+    }
+
+    public AdminReq getAdminReq() {
+        return adminReq;
+    }
+
+    public void process() throws KrbException {
+        //encoding and set adminReq
+    }
+
+
+    public void setTransport(KrbTransport transport) {
+        this.transport = transport;
+    }
+
+    public KrbTransport getTransport() {
+        return transport;
+    }
+}
\ No newline at end of file
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/DeletePrincipalRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/DeletePrincipalRequest.java
new file mode 100644
index 0000000..929f324
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/DeletePrincipalRequest.java
@@ -0,0 +1,70 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.request;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.kerberos.kerb.admin.message.DeletePrincipalReq;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * DeletePrincipal request
+ */
+public class DeletePrincipalRequest extends AdminRequest {
+    /** Admin delete principal do not need password or koptions. */
+
+    public DeletePrincipalRequest(String principal) {
+        super(principal);
+    }
+
+    @Override
+    public void process() throws KrbException {
+        super.process();
+        DeletePrincipalReq deletePrincipalReq = new DeletePrincipalReq();
+        /** encode admin message:
+         *  encode type
+         *  encode paranum
+         *  encode principal name
+         *  (encode koptions)
+         *  (encode passsword)
+         */
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[3];
+        xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.DELETE_PRINCIPAL_REQ);
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, 1);
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, getPrincipal());
+
+        AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
+        byte[] encodeBytes;
+        try {
+            encodeBytes = value.encode();
+        } catch (IOException e) {
+            throw new KrbException("Xdr encode error when generate delete principal request.", e);
+        }
+        ByteBuffer messageBuffer = ByteBuffer.wrap(encodeBytes);
+        deletePrincipalReq.setMessageBuffer(messageBuffer);
+
+        setAdminReq(deletePrincipalReq);
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java
new file mode 100644
index 0000000..2794010
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/GetprincsRequest.java
@@ -0,0 +1,70 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.request;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.kerberos.kerb.admin.message.GetprincsReq;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * GetprincsRequest request.
+ */
+public class GetprincsRequest extends AdminRequest {
+    private String globString = null;
+
+    public GetprincsRequest() {
+        super(null);
+    }
+
+    public GetprincsRequest(String globString) {
+        super(null);
+        this.globString = globString;
+    }
+
+    @Override
+    public void process() throws KrbException {
+        //encoding and set adminReq
+
+        GetprincsReq getprincsReq = new GetprincsReq();
+
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[3];
+        xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.GET_PRINCS_REQ);
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, 2);
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, globString);
+
+        AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
+        byte[] encodeBytes;
+        try {
+            encodeBytes = value.encode();
+        } catch (IOException e) {
+            throw new KrbException("Xdr encode error when generate get principals request.", e);
+        }
+        ByteBuffer messageBuffer = ByteBuffer.wrap(encodeBytes);
+        getprincsReq.setMessageBuffer(messageBuffer);
+
+        setAdminReq(getprincsReq);
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/RenamePrincipalRequest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/RenamePrincipalRequest.java
new file mode 100644
index 0000000..a7cfcc2
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/kadmin/remote/request/RenamePrincipalRequest.java
@@ -0,0 +1,74 @@
+/**
+ *  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.kerberos.kerb.admin.kadmin.remote.request;
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageCode;
+import org.apache.kerby.kerberos.kerb.admin.message.AdminMessageType;
+import org.apache.kerby.kerberos.kerb.admin.message.RenamePrincipalReq;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * RenamePrincipal request.
+ */
+public class RenamePrincipalRequest extends AdminRequest {
+    String newPrincipalName;
+
+    public RenamePrincipalRequest(String oldPrincipalName, String newPrincipalName) {
+        super(oldPrincipalName);
+        this.newPrincipalName = newPrincipalName;
+    }
+
+    @Override
+    public void process() throws KrbException {
+        super.process();
+
+        RenamePrincipalReq renamePrincipalReq = new RenamePrincipalReq();
+
+        /** encode admin message:
+         *  encode type
+         *  encode paranum
+         *  encode old principal name
+         *  encode new principal name
+         */
+        int paramNum = 2;
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[paramNum + 2];
+        xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, AdminMessageType.RENAME_PRINCIPAL_REQ);
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, paramNum);
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, getPrincipal());
+        xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRING, newPrincipalName);
+
+        AdminMessageCode value = new AdminMessageCode(xdrFieldInfos);
+        byte[] encodeBytes;
+        try {
+            encodeBytes = value.encode();
+        } catch (IOException e) {
+            throw new KrbException("Xdr encode error when generate rename principal request.", e);
+        }
+        ByteBuffer messageBuffer = ByteBuffer.wrap(encodeBytes);
+        renamePrincipalReq.setMessageBuffer(messageBuffer);
+
+        setAdminReq(renamePrincipalReq);
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalRep.java
similarity index 73%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalRep.java
index 325f1db..3c52ab0 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalRep.java
@@ -6,19 +6,25 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
 
+/**
+ * Add principal reply, to general admin message
+ */
+public class AddPrincipalRep extends AdminRep {
+    public AddPrincipalRep() {
+        super(AdminMessageType.ADD_PRINCIPAL_REP);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalReq.java
similarity index 73%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalReq.java
index 325f1db..0450a0e 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AddPrincipalReq.java
@@ -6,19 +6,25 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
 
+/**
+ * Add principal request, to general admin message
+ */
+public class AddPrincipalReq extends AdminReq {
+    public AddPrincipalReq() {
+      super(AdminMessageType.ADD_PRINCIPAL_REQ);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessage.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessage.java
new file mode 100644
index 0000000..ec21f91
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessage.java
@@ -0,0 +1,56 @@
+/**
+ *  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.kerberos.kerb.admin.message;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Deal with messages sent and received between Kadmin and Kadmin Server.
+ *       (MSB)                   (LSB)
+ *      +-------+-------+-------+-------+
+ *      |msg_type |para_num |prin_name |...(koptions, password) |
+ *      +-------+-------+-------+-------+
+ */
+public class AdminMessage {
+    private AdminMessageType adminMessageType;
+    private ByteBuffer messageBuffer;
+
+    public AdminMessage(AdminMessageType adminMessageType) {
+        this.adminMessageType = adminMessageType;
+    }
+
+    public AdminMessageType getAdminMessageType() {
+        return adminMessageType;
+    }
+
+    public void setMessageBuffer(ByteBuffer messageBuffer) {
+        this.messageBuffer = messageBuffer;
+    }
+
+    public ByteBuffer getMessageBuffer() {
+        return messageBuffer;
+    }
+
+    public int encodingLength() {
+        return messageBuffer.limit(); // no + 4 is the length of whole message
+    }
+
+
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageCode.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageCode.java
new file mode 100644
index 0000000..016d577
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageCode.java
@@ -0,0 +1,90 @@
+/**
+ *  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.kerberos.kerb.admin.message;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.xdr.XdrDataType;
+import org.apache.kerby.xdr.XdrFieldInfo;
+import org.apache.kerby.xdr.type.AbstractXdrType;
+import org.apache.kerby.xdr.type.XdrInteger;
+import org.apache.kerby.xdr.type.XdrString;
+import org.apache.kerby.xdr.type.XdrStructType;
+import org.apache.kerby.xdr.type.XdrType;
+
+/**
+ * An extend XdrStructType to encode and decode AdminMessage.
+ */
+public class AdminMessageCode extends XdrStructType {
+    public AdminMessageCode() {
+        super(XdrDataType.STRUCT);
+    }
+
+    public AdminMessageCode(XdrFieldInfo[] fieldInfos) {
+        super(XdrDataType.STRUCT, fieldInfos);
+    }
+
+    protected  void getStructTypeInstance(final XdrType[] fields, final XdrFieldInfo[] fieldInfos) {
+        for (int i = 0; i < fieldInfos.length; i++) {
+            switch (fieldInfos[i].getDataType()) {
+                case INTEGER:
+                    fields[i] = new XdrInteger((Integer) fieldInfos[i].getValue());
+                    break;
+                case ENUM:
+                    fields[i] = new AdminMessageEnum((AdminMessageType) fieldInfos[i].getValue());
+                    break;
+                case STRING:
+                    fields[i] = new XdrString((String) fieldInfos[i].getValue());
+                    break;
+                default:
+                    fields[i] = null;
+            }
+
+        }
+    }
+
+    @Override
+    protected XdrStructType fieldsToValues(AbstractXdrType[] fields) {
+        int paramNum = (int) fields[1].getValue();
+        XdrFieldInfo[] xdrFieldInfos = new XdrFieldInfo[paramNum + 2];
+        xdrFieldInfos[0] = new XdrFieldInfo(0, XdrDataType.ENUM, fields[0].getValue());
+        xdrFieldInfos[1] = new XdrFieldInfo(1, XdrDataType.INTEGER, fields[1].getValue());
+        xdrFieldInfos[2] = new XdrFieldInfo(2, XdrDataType.STRING, fields[2].getValue());
+        if (paramNum == 2 && fields[3].getValue() instanceof KOptions) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRUCT, fields[3].getValue()); /////koption
+        } else if (paramNum == 2 && fields[3].getValue() instanceof String) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRING, fields[3].getValue());
+        } else if (paramNum == 3) {
+            xdrFieldInfos[3] = new XdrFieldInfo(3, XdrDataType.STRUCT, fields[3].getValue()); ////koption
+            xdrFieldInfos[4] = new XdrFieldInfo(4, XdrDataType.STRING, fields[4].getValue());
+        }
+        return new AdminMessageCode(xdrFieldInfos);
+    }
+
+    @Override
+    protected AbstractXdrType[] getAllFields() {
+        AbstractXdrType[] fields = new AbstractXdrType[5];
+        fields[0] = new AdminMessageEnum();
+        fields[1] = new XdrInteger();
+        fields[2] = new XdrString();
+        fields[3] = new XdrString(); //suppose it is string
+        fields[4] = null; // kOptions is not supported.
+        return fields;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageEnum.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageEnum.java
new file mode 100644
index 0000000..2ea60b8
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageEnum.java
@@ -0,0 +1,41 @@
+/**
+ *  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.kerberos.kerb.admin.message;
+
+import org.apache.kerby.xdr.EnumType;
+import org.apache.kerby.xdr.type.XdrEnumerated;
+
+/**
+ * An extend XdrEnumerate to encode and decode AdminMessageType.
+ */
+public class AdminMessageEnum extends XdrEnumerated<AdminMessageType> {
+    public AdminMessageEnum() {
+        super(null);
+    }
+
+    public AdminMessageEnum(AdminMessageType value) {
+        super(value);
+    }
+    @Override
+    protected EnumType[] getAllEnumValues() {
+        return AdminMessageType.values();
+    }
+
+}
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageType.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageType.java
new file mode 100644
index 0000000..f44187e
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminMessageType.java
@@ -0,0 +1,73 @@
+/**
+ *  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.kerberos.kerb.admin.message;
+
+import org.apache.kerby.xdr.EnumType;
+
+/**
+ * Type of Admin Message:
+ * NONE(-1)
+ * ADD_PRINCIPAL_REQ(0) add principal request
+ * ADD_PRINCIPAL_REP(1) add principal reply
+ * DELETE_PRINCIPAL_REQ(2),
+ * DELETE_PRINCIPAL_REP(3);
+ * RENAME_PRINCIPAL_REQ(4),
+ * RENAME_PRINCIPAL_REP(5);
+ *
+ */
+
+public enum AdminMessageType implements EnumType {
+    NONE(-1),
+    ADD_PRINCIPAL_REQ(0),
+    ADD_PRINCIPAL_REP(1),
+    DELETE_PRINCIPAL_REQ(2),
+    DELETE_PRINCIPAL_REP(3),
+    RENAME_PRINCIPAL_REQ(4),
+    RENAME_PRINCIPAL_REP(5),
+    GET_PRINCS_REQ(6),
+    GET_PRINCS_REP(7);
+
+    private int value;
+
+    AdminMessageType(int value) {
+        this.value = value;
+    }
+
+    @Override
+    public int getValue() {
+        return value;
+    }
+
+    @Override
+    public String getName() {
+        return name();
+    }
+
+    public static AdminMessageType findType(int value) {
+        if (value >= 0) {
+            for (EnumType e : values()) {
+                if (e.getValue() == value) {
+                    return (AdminMessageType) e;
+                }
+            }
+        }
+        return NONE;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminRep.java
similarity index 69%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminRep.java
index 325f1db..a26cd6b 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminRep.java
@@ -6,19 +6,28 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
 
+/**
+ * Use to construct Admin message.
+ * Probably two kinds of reply.
+ * add principal -- AdRep
+ * change password? -- chRep
+ */
+public class AdminRep extends AdminMessage {
+    public AdminRep(AdminMessageType messageType) {
+      super(messageType);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminReq.java
similarity index 69%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminReq.java
index 325f1db..c69218f 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/AdminReq.java
@@ -6,19 +6,29 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
+
+/**
+ * Use to construct Admin message.
+ * Probably two kinds of request.
+ * add principal -- AdReq
+ * change password? -- chReq
+ */
+public class AdminReq extends AdminMessage {
+  public AdminReq(AdminMessageType messageType) {
+    super(messageType);
+  }
 
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalRep.java
similarity index 72%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalRep.java
index 325f1db..35b1f6e 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalRep.java
@@ -6,19 +6,25 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
 
+/**
+ * Delete principal reply, to general admin message
+ */
+public class DeletePrincipalRep extends AdminRep {
+    public DeletePrincipalRep() {
+        super(AdminMessageType.DELETE_PRINCIPAL_REP);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalReq.java
similarity index 72%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalReq.java
index 325f1db..ad3b320 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/DeletePrincipalReq.java
@@ -6,19 +6,25 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
 
+/**
+ * Delete principal request, to general admin message
+ */
+public class DeletePrincipalReq extends AdminReq {
+    public DeletePrincipalReq() {
+        super(AdminMessageType.DELETE_PRINCIPAL_REQ);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsRep.java
similarity index 78%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsRep.java
index 325f1db..2a6364a 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsRep.java
@@ -6,19 +6,21 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
-
+public class GetprincsRep extends AdminRep {
+    public GetprincsRep() {
+        super(AdminMessageType.GET_PRINCS_REP);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsReq.java
similarity index 78%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsReq.java
index 325f1db..75e819b 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/GetprincsReq.java
@@ -6,19 +6,21 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
-
+public class GetprincsReq extends AdminReq {
+    public GetprincsReq() {
+        super(AdminMessageType.GET_PRINCS_REQ);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java
new file mode 100644
index 0000000..c5d6359
--- /dev/null
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/KadminCode.java
@@ -0,0 +1,63 @@
+/**
+ *  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.kerberos.kerb.admin.message;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Used to decode messages between admin and admin server.
+ */
+public class KadminCode {
+    public static ByteBuffer encodeMessage(AdminMessage adminMessage) {
+        int length = adminMessage.encodingLength();
+        // 4 is the head to go through network
+        ByteBuffer buffer = ByteBuffer.allocate(length + 4);
+        buffer.putInt(length); // head in network
+        //buffer.putInt(adminMessage.getAdminMessageType().getValue());
+        // type has been encoded in the admin message
+        buffer.put(adminMessage.getMessageBuffer());
+        buffer.flip();
+        return buffer;
+    }
+
+    public static AdminMessage decodeMessage(ByteBuffer buffer) throws IOException {
+        //go through network, the total length has been removed.
+        int type = buffer.getInt();
+        System.out.println("type: " + type);
+        AdminMessageType adminMessageType = AdminMessageType.findType(type);
+        AdminMessage adminMessage = null;
+        byte[] bytes = new byte[buffer.remaining()];
+        buffer.get(bytes);
+        if (adminMessageType == AdminMessageType.ADD_PRINCIPAL_REQ) {
+            adminMessage = new AddPrincipalReq();
+            System.out.println("check if decoding right: "
+                + new String(ByteBuffer.wrap(bytes).array()));
+        } else if (adminMessageType == AdminMessageType.ADD_PRINCIPAL_REP) {
+            adminMessage = new AddPrincipalRep();
+            System.out.println("check if decoding right2: "
+                + new String(ByteBuffer.wrap(bytes).array()));
+        } else {
+            throw new IOException("Unknown Admin Message Type: " + type);
+        }
+
+        return adminMessage;
+    }
+}
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalRep.java
similarity index 72%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalRep.java
index 325f1db..5406190 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalRep.java
@@ -6,19 +6,24 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
-
+/**
+ * Rename principal reply, to general admin message
+ */
+public class RenamePrincipalRep extends AdminRep {
+    public RenamePrincipalRep() {
+        super(AdminMessageType.RENAME_PRINCIPAL_REP);
+    }
 }
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalReq.java
similarity index 72%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalReq.java
index 325f1db..4bc8c1b 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-admin/src/main/java/org/apache/kerby/kerberos/kerb/admin/message/RenamePrincipalReq.java
@@ -6,19 +6,24 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.admin.message;
 
-public class KadminTest {
-
+/**
+ * Rename principal request, to general admin message
+ */
+public class RenamePrincipalReq extends AdminReq {
+    public RenamePrincipalReq() {
+        super(AdminMessageType.RENAME_PRINCIPAL_REQ);
+    }
 }
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
index 5323225..135eb6e 100644
--- a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/transport/KdcNetwork.java
@@ -124,7 +124,6 @@
         }
     }
 
-
     private void checkUdpMessage() throws IOException {
         InetSocketAddress fromAddress = (InetSocketAddress) udpServer.receive(recvBuffer);
         if (fromAddress != null) {
diff --git a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcSetting.java b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcSetting.java
index 85f4da7..c53d5d6 100644
--- a/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcSetting.java
+++ b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/KdcSetting.java
@@ -26,7 +26,7 @@
 /**
  * KDC setting that combines startup options and kdc config.
  */
-public class KdcSetting {
+public class KdcSetting implements ServerSetting {
     private final KOptions startupOptions;
     private final KdcConfig kdcConfig;
     private final BackendConfig backendConfig;
diff --git a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/ServerSetting.java
similarity index 62%
copy from kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
copy to kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/ServerSetting.java
index 325f1db..7044693 100644
--- a/kerby-kerb/kerb-admin/src/test/java/org/apache/kerby/kerberos/kerb/admin/KadminTest.java
+++ b/kerby-kerb/kerb-server/src/main/java/org/apache/kerby/kerberos/kerb/server/ServerSetting.java
@@ -6,19 +6,30 @@
  *  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. 
- *  
+ *  under the License.
+ *
  */
-package org.apache.kerby.kerberos.kerb.admin;
+package org.apache.kerby.kerberos.kerb.server;
 
-public class KadminTest {
+import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
 
+/**
+ * Super clsss of KdcSetting and AdminServer Setting.
+ * This class is used to solve the problem of member variable in
+ * LocalKadminImpl (KdcSetting or AdminServerSetting).
+ */
+public interface ServerSetting {
+    String getKdcRealm();
+
+    KdcConfig getKdcConfig();
+
+    BackendConfig getBackendConfig();
 }
diff --git a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
index c342d8b..4de8e7f 100644
--- a/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
+++ b/kerby-kerb/kerb-simplekdc/src/main/java/org/apache/kerby/kerberos/kerb/server/SimpleKdcServer.java
@@ -20,8 +20,8 @@
 package org.apache.kerby.kerberos.kerb.server;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
 import org.apache.kerby.kerberos.kerb.client.Krb5Conf;
 import org.apache.kerby.kerberos.kerb.client.KrbClient;
 import org.apache.kerby.kerberos.kerb.client.KrbConfig;
diff --git a/kerby-kerb/pom.xml b/kerby-kerb/pom.xml
index 4b2537b..d9879d9 100644
--- a/kerby-kerb/pom.xml
+++ b/kerby-kerb/pom.xml
@@ -37,6 +37,7 @@
     <module>kerb-kdc-test</module>
     <module>integration-test</module>
     <module>kerb-admin</module>
+    <module>kerb-admin-server</module>
     <module>kerb-simplekdc</module>
     <module>kerb-client-api-all</module>
     <module>kerb-server-api-all</module>
diff --git a/kerby-tool/kdc-tool/pom.xml b/kerby-tool/kdc-tool/pom.xml
index 64edba2..dd4d62c 100644
--- a/kerby-tool/kdc-tool/pom.xml
+++ b/kerby-tool/kdc-tool/pom.xml
@@ -51,6 +51,12 @@
         <artifactId>kerb-admin</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.kerby</groupId>
+        <artifactId>kerb-admin-server</artifactId>
+        <version>${project.version}</version>
+      </dependency>
+
     </dependencies>
 
 </project>
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
index 1c97204..add63a4 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/KadminTool.java
@@ -21,9 +21,9 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
 import org.apache.kerby.kerberos.tool.kadmin.command.AddPrincipalCommand;
 import org.apache.kerby.kerberos.tool.kadmin.command.ChangePasswordCommand;
 import org.apache.kerby.kerberos.tool.kadmin.command.DeletePrincipalCommand;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/ToolUtil.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/ToolUtil.java
index e2f33ff..9c64351 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/ToolUtil.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/ToolUtil.java
@@ -21,7 +21,7 @@
 
 import org.apache.kerby.KOptionType;
 import org.apache.kerby.KOptions;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
 
 import java.util.Scanner;
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
index e2374bd..c9b36be 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalCommand.java
@@ -21,8 +21,8 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
 import java.io.Console;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalsCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalsCommand.java
index 32fe808..b1843e5 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalsCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/AddPrincipalsCommand.java
@@ -21,8 +21,8 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
 public class AddPrincipalsCommand extends KadminCommand {
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
index f3d2f45..b4bc4a0 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ChangePasswordCommand.java
@@ -21,8 +21,8 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
 import java.io.Console;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
index 8322b7b..0a2e146 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/DeletePrincipalCommand.java
@@ -20,8 +20,8 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 
 import java.io.Console;
 import java.util.Scanner;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
index 6c4501f..bc8024a 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/GetPrincipalCommand.java
@@ -20,7 +20,7 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.kerb.identity.KrbIdentity;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
 import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
index 53890e2..46f1087 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KadminCommand.java
@@ -19,7 +19,7 @@
  */
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 
 public abstract class KadminCommand {
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
index 65802f4..d96d5a0 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabAddCommand.java
@@ -20,7 +20,7 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 
 import java.io.File;
 import java.util.List;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
index d1d9df4..82ab676 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/KeytabRemoveCommand.java
@@ -21,8 +21,8 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
 import java.io.File;
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
index 71d909f..d236c65 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ListPrincipalCommand.java
@@ -20,7 +20,7 @@
 package org.apache.kerby.kerberos.tool.kadmin.command;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 
 import java.util.List;
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
index 4d0d16b..f3fe0fc 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/ModifyPrincipalCommand.java
@@ -22,8 +22,8 @@
 import org.apache.kerby.KOptionType;
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
 public class ModifyPrincipalCommand extends KadminCommand {
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
index 80d6785..ca31199 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kadmin/command/RenamePrincipalCommand.java
@@ -21,12 +21,11 @@
 
 import org.apache.kerby.KOptions;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.Kadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.Kadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
 import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
 
-
 public class RenamePrincipalCommand extends KadminCommand {
     private static final String USAGE = "Usage: rename_principal [-force] old_principal new_principal\n";
 
diff --git a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
index 4cf4de8..faf1cb2 100644
--- a/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
+++ b/kerby-tool/kdc-tool/src/main/java/org/apache/kerby/kerberos/tool/kdcinit/KdcInitTool.java
@@ -20,8 +20,10 @@
 package org.apache.kerby.kerberos.tool.kdcinit;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadmin;
-import org.apache.kerby.kerberos.kerb.admin.LocalKadminImpl;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServer;
+import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerConfig;
 import org.apache.kerby.util.OSUtil;
 
 import java.io.File;
@@ -53,6 +55,19 @@
                     + " has been exported to the specified file "
                     + keytabFile.getAbsolutePath() + ", please safely keep it, "
                     + "in order to use kadmin tool later");
+
+            // Export protocol keytab file for remote admin tool
+            AdminServer adminServer = new AdminServer(confDir);
+            AdminServerConfig adminServerConfig = adminServer.getAdminServerConfig();
+            String principal = adminServerConfig.getProtocol() + "/"
+                + adminServerConfig.getAdminHost() + "@" + adminServerConfig.getAdminRealm();
+            kadmin.addPrincipal(principal);
+            File protocolFile = new File("protocol.keytab");
+            kadmin.exportKeytab(protocolFile, principal);
+            System.out.println("The keytab for protocol principal "
+                    + " has been exported to the specified file "
+                    + protocolFile.getAbsolutePath() + ", please safely keep it, "
+                    + "in order to use remote kadmin tool later");
         } finally {
             kadmin.release();
         }