You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by pl...@apache.org on 2016/05/11 05:43:46 UTC

directory-kerby git commit: DIRKRB-565 Implement Gss tokens defined in RFC 4121. Contributed by Wei.

Repository: directory-kerby
Updated Branches:
  refs/heads/gssapi 33acad0b4 -> e680dab2b


DIRKRB-565 Implement Gss tokens defined in RFC 4121. Contributed by Wei.


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

Branch: refs/heads/gssapi
Commit: e680dab2b3485cd6d7f3accb5c17fec279adf0ae
Parents: 33acad0
Author: plusplusjiajia <ji...@intel.com>
Authored: Wed May 11 13:48:55 2016 +0800
Committer: plusplusjiajia <ji...@intel.com>
Committed: Wed May 11 13:48:55 2016 +0800

----------------------------------------------------------------------
 .../kerberos/kerb/gssapi/krb5/KerbyContext.java |  10 +
 .../kerb/gssapi/krb5/KerbyGssEncryptor.java     | 138 +++++++++
 .../kerb/gssapi/krb5/KerbyGssTokenBase.java     |  59 ++++
 .../kerb/gssapi/krb5/KerbyGssTokenV2.java       | 282 +++++++++++++++++++
 .../kerberos/kerb/gssapi/krb5/KerbyUtil.java    |   1 -
 .../kerberos/kerb/gssapi/krb5/MicTokenV2.java   |  94 +++++++
 .../kerberos/kerb/gssapi/krb5/WrapTokenV2.java  | 153 ++++++++++
 7 files changed, 736 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyContext.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyContext.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyContext.java
index e017683..b450cc9 100644
--- a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyContext.java
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyContext.java
@@ -86,6 +86,8 @@ public class KerbyContext implements GSSContextSpi {
     private TicketFlags ticketFlags;
     private ApReq outApReq;
 
+    private KerbyGssEncryptor gssEncryptor;
+
     // Called on initiator's side.
     public KerbyContext(GSSCaller caller, KerbyNameElement peerName, KerbyCredElement myCred,
                         int lifeTime)
@@ -294,11 +296,13 @@ public class KerbyContext implements GSSContextSpi {
 
             ctxState = STATE_ESTABLISHING;
             if (!getMutualAuthState()) {
+                gssEncryptor = new KerbyGssEncryptor(getSessionKey());
                 ctxState = STATE_ESTABLISHED;
             }
 
         } else if (ctxState == STATE_ESTABLISHING) {
             verifyServerToken(is, mechTokenSize);
+            gssEncryptor = new KerbyGssEncryptor(getSessionKey());
             outApReq = null;
             ctxState = STATE_ESTABLISHED;
         }
@@ -389,6 +393,8 @@ public class KerbyContext implements GSSContextSpi {
                 ret = verifyClientToken(acceptCred, is, mechTokenSize);
             }
 
+            gssEncryptor = new KerbyGssEncryptor(getSessionKey());
+
             myCred = null;
             ctxState = STATE_ESTABLISHED;
         }
@@ -607,4 +613,8 @@ public class KerbyContext implements GSSContextSpi {
             return peerSequenceNumber++;
         }
     }
+
+    public KerbyGssEncryptor getGssEncryptor() {
+        return gssEncryptor;
+    }
 }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssEncryptor.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssEncryptor.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssEncryptor.java
new file mode 100644
index 0000000..d65346b
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssEncryptor.java
@@ -0,0 +1,138 @@
+/**
+ *  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.gssapi.krb5;
+
+
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.crypto.CheckSumHandler;
+import org.apache.kerby.kerberos.kerb.crypto.CheckSumTypeHandler;
+import org.apache.kerby.kerberos.kerb.crypto.EncTypeHandler;
+import org.apache.kerby.kerberos.kerb.crypto.EncryptionHandler;
+import org.apache.kerby.kerberos.kerb.type.base.CheckSumType;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
+import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
+import org.ietf.jgss.GSSException;
+
+/**
+ * This class implements encryption related function used in GSS tokens
+ */
+public class KerbyGssEncryptor {
+
+    private EncryptionKey encKey;
+    private boolean isV2 = false;
+
+    public KerbyGssEncryptor(EncryptionKey key) throws GSSException {
+        encKey = key;
+        EncryptionType keyType = key.getKeyType();
+        // TODO: add support for other algorithms
+        if (keyType == EncryptionType.AES128_CTS_HMAC_SHA1_96
+                || keyType == EncryptionType.AES256_CTS_HMAC_SHA1_96) {
+            isV2 = true;
+        } else {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Invalid encryption type: " + key.getKeyType().getDisplayName());
+        }
+    }
+
+    /**
+     * Return true if it is encryption type defined in RFC 4121
+     * @return
+     */
+    public boolean isV2() {
+        return isV2;
+    }
+
+    public byte[] encryptData(byte[] tokenHeader, byte[] data,
+                              int offset, int len, int keyUsage) throws GSSException {
+        byte[] ret;
+        byte[] toProcess = new byte[tokenHeader.length + len];
+        System.arraycopy(data, offset, toProcess, 0, len);
+        System.arraycopy(tokenHeader, 0, toProcess, len, tokenHeader.length);
+
+        ret = encryptData(toProcess, keyUsage);
+        return ret;
+    }
+
+    public byte[] encryptData(byte[] toProcess, int keyUsage) throws GSSException {
+        byte[] ret;
+        try {
+            EncTypeHandler encHandler = EncryptionHandler.getEncHandler(encKey.getKeyType());
+            ret = encHandler.encrypt(toProcess, encKey.getKeyData(), keyUsage);
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1, e.getMessage());
+        }
+        return ret;
+    }
+
+    public byte[] decryptData(byte[] dataEncrypted, int keyUsage) throws GSSException {
+        byte[] ret;
+        try {
+            EncTypeHandler encHandler = EncryptionHandler.getEncHandler(encKey.getKeyType());
+            ret = encHandler.decrypt(dataEncrypted, encKey.getKeyData(), keyUsage);
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1, e.getMessage());
+        }
+        return ret;
+    }
+
+    public byte[] calculateCheckSum(byte[] header, byte[] data, int offset, int len, int keyUsage)
+            throws GSSException {
+        int totalLen = len + (header == null ? 0 : header.length);
+        byte[] buffer = new byte[totalLen];
+        System.arraycopy(data, offset, buffer, 0, len);
+        if (header != null) {
+            System.arraycopy(header, 0, buffer, len, header.length);
+        }
+
+        try {
+            return getCheckSumHandler().checksumWithKey(buffer, encKey.getKeyData(), keyUsage);
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Exception in checksum calculation:" + encKey.getKeyType().getName());
+        }
+    }
+
+    private CheckSumTypeHandler getCheckSumHandler() throws GSSException {
+        CheckSumType checkSumType;
+        if (encKey.getKeyType() == EncryptionType.AES128_CTS_HMAC_SHA1_96) {
+            checkSumType = CheckSumType.HMAC_SHA1_96_AES128;
+        } else if (encKey.getKeyType() == EncryptionType.AES256_CTS_HMAC_SHA1_96) {
+            checkSumType = CheckSumType.HMAC_SHA1_96_AES256;
+        } else {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Unsupported checksum encryption type:" + encKey.getKeyType().getName());
+        }
+        try {
+            return CheckSumHandler.getCheckSumHandler(checkSumType);
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Unsupported checksum type:" + checkSumType.getName());
+        }
+    }
+
+    /**
+     * Get the size of the corresponding checksum algorithm
+     * @return
+     * @throws GSSException
+     */
+    public int getCheckSumSize() throws GSSException {
+        return getCheckSumHandler().cksumSize();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenBase.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenBase.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenBase.java
new file mode 100644
index 0000000..ae5122f
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenBase.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.gssapi.krb5;
+
+
+public abstract class KerbyGssTokenBase {
+    public static final int TOKEN_WRAP_V1 = 0x201;
+    public static final int TOKEN_MIC_V1 = 0x101;
+    public static final int TOKEN_WRAP_V2 = 0x504;
+    public static final int TOKEN_MIC_V2 = 0x404;
+
+    public void writeBigEndian(byte[] buf, int offset, int value) {
+        buf[offset] = (byte) (value >>> 24);
+        buf[offset + 1] = (byte) (value >>> 16);
+        buf[offset + 2] = (byte) (value >>> 8);
+        buf[offset + 3] = (byte) (value);
+    }
+
+    public int readBigEndian(byte[] buf, int offset) {
+        int value = 0;
+        value += (buf[offset] & 0xFF) << 24;
+        value += (buf[offset + 1] & 0xFF) << 16;
+        value += (buf[offset + 2] & 0xFF) << 8;
+        value += buf[offset + 3] & 0xFF;
+        return value;
+    }
+
+    /**
+     *
+     * @param buf
+     * @param offset
+     * @param len should not be larger than sizeof(int)
+     * @return
+     */
+    public int readBigEndian(byte[] buf, int offset, int len) {
+        int value = 0;
+        for (int i = 0; i < len; i++) {
+            value += (buf[offset + i] & 0xFF) << 8;
+        }
+        return value;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV2.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV2.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV2.java
new file mode 100644
index 0000000..f2d220a
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV2.java
@@ -0,0 +1,282 @@
+/**
+ *  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.gssapi.krb5;
+
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+
+/**
+ * This class implements the token formats defined in RFC 4121.
+ */
+abstract class KerbyGssTokenV2 extends KerbyGssTokenBase {
+    public static final int CONFOUNDER_SIZE = 16;
+    public static final int TOKEN_HEADER_SIZE = 16;
+    private static final int OFFSET_EC = 4;
+    private static final int OFFSET_RRC = 6;
+
+    // context states
+    private boolean isInitiator = true;
+    private boolean acceptorSubKey = false;
+    private boolean confState = true;
+    private int sequenceNumber;
+
+    // token data
+    protected int tokenType;
+    private byte[] header = new byte[TOKEN_HEADER_SIZE];
+    protected byte[] tokenData;
+
+    protected byte[] checkSum;
+    private int ec;
+    private int rrc;
+
+    static final int KG_USAGE_ACCEPTOR_SEAL = 22;
+    static final int KG_USAGE_ACCEPTOR_SIGN = 23;
+    static final int KG_USAGE_INITIATOR_SEAL = 24;
+    static final int KG_USAGE_INITIATOR_SIGN = 25;
+    private int keyUsage;
+
+    private static final int FLAG_SENT_BY_ACCEPTOR = 1;
+    private static final int FLAG_SEALED = 2;
+    private static final int FLAG_ACCEPTOR_SUBKEY = 4;
+
+    protected KerbyGssEncryptor encryptor;
+
+
+    // Create a new token
+    KerbyGssTokenV2(int tokenType, KerbyContext context) throws GSSException {
+        initialize(tokenType, context, false);
+    }
+
+    private void initialize(int tokenType, KerbyContext context, boolean reconstruct) throws GSSException {
+        this.tokenType = tokenType;
+        this.isInitiator = context.isInitiator();
+        this.acceptorSubKey = context.getKeyComesFrom() == KerbyContext.ACCEPTOR_SUBKEY;
+        this.confState = context.getConfState();
+
+        boolean usageFlag = reconstruct ? !this.isInitiator : this.isInitiator;
+        if (tokenType == TOKEN_WRAP_V2) {
+            keyUsage = usageFlag ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL;
+        } else if (tokenType == TOKEN_MIC_V2) {
+            keyUsage = usageFlag ? KG_USAGE_INITIATOR_SIGN : KG_USAGE_ACCEPTOR_SIGN;
+        }
+
+        encryptor = context.getGssEncryptor();
+
+        if (!reconstruct) {
+            this.sequenceNumber = context.incMySequenceNumber();
+        }
+    }
+
+    // Reconstruct token from bytes received
+    KerbyGssTokenV2(int tokenType, KerbyContext context,
+                         MessageProp prop, byte[] token, int offset, int len) throws GSSException {
+        this(tokenType, context, prop, new ByteArrayInputStream(token, offset, len));
+    }
+
+    // Reconstruct token from input stream
+    KerbyGssTokenV2(int tokenType, KerbyContext context,
+                         MessageProp prop, InputStream is) throws GSSException {
+        initialize(tokenType, context, true);
+
+        if (!confState) {
+            prop.setPrivacy(false);
+        }
+
+        reconstructTokenHeader(prop, is);
+
+        int minSize;
+        if (tokenType == TOKEN_WRAP_V2 && prop.getPrivacy()) {
+            minSize = CONFOUNDER_SIZE + TOKEN_HEADER_SIZE + encryptor.getCheckSumSize();
+        } else {
+            minSize = encryptor.getCheckSumSize();
+        }
+
+        try {
+            int tokenLen = is.available();
+
+            if (tokenType == TOKEN_MIC_V2) {
+                tokenLen = minSize;
+                tokenData = new byte[tokenLen];
+                is.read(tokenData);
+            } else {
+                if (tokenLen >= minSize) {
+                    tokenData = new byte[tokenLen];
+                    is.read(tokenData);
+                } else {
+                    throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token length");
+                }
+            }
+
+            if (tokenType == TOKEN_WRAP_V2) {
+                tokenData = rotate(tokenData);
+            }
+
+            if (tokenType == TOKEN_MIC_V2
+                    || tokenType == TOKEN_WRAP_V2 && !prop.getPrivacy()) {
+                int checksumLen = encryptor.getCheckSumSize();
+
+                if (tokenType != TOKEN_MIC_V2 && checksumLen != ec) {
+                    throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid EC");
+                }
+
+                checkSum = new byte[checksumLen];
+                System.arraycopy(tokenData, tokenLen - checksumLen, checkSum, 0, checksumLen);
+            }
+        } catch (IOException e) {
+            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token");
+        }
+    }
+
+    private byte[] rotate(byte[] data) {
+        int dataLen = data.length;
+        if (rrc % dataLen != 0) {
+            rrc = rrc % dataLen;
+            byte[] newBytes = new byte[dataLen];
+
+            System.arraycopy(data, rrc, newBytes, 0, dataLen - rrc);
+            System.arraycopy(data, 0, newBytes, dataLen - rrc, rrc);
+            data = newBytes;
+        }
+        return data;
+    }
+
+    public int getKeyUsage() {
+        return keyUsage;
+    }
+
+    public void generateCheckSum(MessageProp prop, byte[] data, int offset, int len) throws GSSException {
+        // generate token header
+        createTokenHeader(prop.getPrivacy());
+
+        if (tokenType == TOKEN_MIC_V2
+                || !prop.getPrivacy() && tokenType == TOKEN_WRAP_V2) {
+            checkSum = getCheckSum(data, offset, len);
+        }
+
+        if (!prop.getPrivacy() && tokenType == TOKEN_WRAP_V2) {
+            header[4] = (byte) (checkSum.length >>> 8);
+            header[5] = (byte) (checkSum.length & 0xFF);
+        }
+    }
+
+    public byte[] getCheckSum(byte[] data, int offset, int len) throws GSSException {
+        int confidentialFlag = header[2] & 2;
+        if (confidentialFlag == 0 && tokenType == TOKEN_WRAP_V2) {
+            header[4] = 0;
+            header[5] = 0;
+            header[6] = 0;
+            header[7] = 0;
+        }
+        return encryptor.calculateCheckSum(header, data, offset, len, keyUsage);
+    }
+
+    public boolean verifyCheckSum(byte[] data, int offset, int len) throws GSSException {
+        byte[] dataCheckSum = getCheckSum(data, offset, len);
+        return MessageDigest.isEqual(checkSum, dataCheckSum);
+    }
+
+    // Create a new header
+    private void createTokenHeader(boolean privacy) {
+        header[0] = (byte) (tokenType >>> 8);
+        header[1] = (byte) tokenType;
+
+        int flags = isInitiator ? 0 : FLAG_SENT_BY_ACCEPTOR;
+        flags |= privacy && tokenType != TOKEN_MIC_V2 ? FLAG_SEALED : 0;
+        flags |= acceptorSubKey ? FLAG_ACCEPTOR_SUBKEY : 0;
+
+        header[2] = (byte) (flags & 0xFF);
+        header[3] = (byte) 0xFF;
+
+        if (tokenType == TOKEN_WRAP_V2) {
+            header[4] = (byte) 0;
+            header[5] = (byte) 0;
+            header[6] = (byte) 0;
+            header[7] = (byte) 0;
+        } else if (tokenType == TOKEN_MIC_V2) {
+            header[4] = (byte) 0xFF;
+            header[5] = (byte) 0xFF;
+            header[6] = (byte) 0xFF;
+            header[7] = (byte) 0xFF;
+        }
+        writeBigEndian(header, 12, sequenceNumber);
+    }
+
+    // Reconstruct a token header
+    private void reconstructTokenHeader(MessageProp prop, InputStream is) throws GSSException {
+        try {
+            if (is.read(header, 0, header.length) != header.length) {
+                throw new GSSException(GSSException.FAILURE, -1, "Token header can not be read");
+            }
+            int tokenIDRecv = (((int) header[0]) << 8) + header[1];
+            if (tokenIDRecv != tokenType) {
+                throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
+                        "Token ID should be " + tokenType + " instead of " + tokenIDRecv);
+            }
+
+            int senderFlag = isInitiator ? FLAG_SENT_BY_ACCEPTOR : 0;
+            int senderFlagRecv = header[2] & FLAG_SENT_BY_ACCEPTOR;
+            if (senderFlagRecv != senderFlag) {
+                throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid acceptor flag");
+            }
+
+            int confFlagRecv = header[2] & FLAG_SEALED;
+            if (confFlagRecv == FLAG_SEALED && tokenType == TOKEN_WRAP_V2) {
+                prop.setPrivacy(true);
+            } else {
+                prop.setPrivacy(false);
+            }
+
+            if (tokenType == TOKEN_WRAP_V2) {
+                if (header[3] != (byte) 0xFF) {
+                    throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token filler");
+                }
+
+                ec = readBigEndian(header, OFFSET_EC, 2);
+                rrc = readBigEndian(header, OFFSET_RRC, 2);
+            } else if (tokenType == TOKEN_MIC_V2) {
+                for (int i = 3; i < 8; i++) {
+                    if ((header[i] & 0xFF) != 0xFF) {
+                        throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token filler");
+                    }
+                }
+            }
+
+            prop.setQOP(0);
+            sequenceNumber = readBigEndian(header, 0, 8);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Phrase token header failed");
+        }
+    }
+
+    public int encodeHeader(byte[] buf, int offset) {
+        System.arraycopy(header, 0, buf, offset, TOKEN_HEADER_SIZE);
+        return TOKEN_HEADER_SIZE;
+    }
+
+    public void encodeHeader(OutputStream os) throws IOException {
+        os.write(header);
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyUtil.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyUtil.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyUtil.java
index a5abb46..081788b 100644
--- a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyUtil.java
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyUtil.java
@@ -21,7 +21,6 @@ package org.apache.kerby.kerberos.kerb.gssapi.krb5;
 
 import org.apache.kerby.kerberos.kerb.KrbException;
 import org.apache.kerby.kerberos.kerb.client.KrbClientBase;
-import org.apache.kerby.kerberos.kerb.request.ApRequest;
 import org.apache.kerby.kerberos.kerb.type.KerberosTime;
 import org.apache.kerby.kerberos.kerb.type.ad.AuthorizationData;
 import org.apache.kerby.kerberos.kerb.type.ad.AuthorizationDataEntry;

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV2.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV2.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV2.java
new file mode 100644
index 0000000..7ba27ab
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV2.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.kerberos.kerb.gssapi.krb5;
+
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class MicTokenV2 extends KerbyGssTokenV2 {
+    private MessageProp prop;
+
+    // This is called to construct MicToken from user input
+    MicTokenV2(KerbyContext context,
+             byte[] inMsg,
+             int msgOffset,
+             int msgLength,
+             MessageProp messageProp) throws GSSException {
+        super(TOKEN_MIC_V2, context);
+
+        prop = messageProp;
+        if (prop == null) {
+            prop = new MessageProp(0, false);
+        }
+
+        generateCheckSum(prop, inMsg, msgOffset, msgLength);
+    }
+
+    // This is called to construct MicToken from MicToken bytes
+    MicTokenV2(KerbyContext context,
+             MessageProp messageProp,
+             byte[] inToken,
+             int tokenOffset,
+             int tokenLength) throws GSSException {
+        super(TOKEN_MIC_V2, context, messageProp, inToken, tokenOffset, tokenLength);
+        this.prop = messageProp;
+    }
+
+    public int getMic(byte[] outToken, int offset) {
+        encodeHeader(outToken, offset);
+        System.arraycopy(checkSum, 0, outToken, TOKEN_HEADER_SIZE + offset, checkSum.length);
+        return TOKEN_HEADER_SIZE + checkSum.length;
+    }
+
+    /**
+     * Get bytes for this Mic token
+     * @return
+     */
+    public byte[] getMic() {
+        byte[] ret = new byte[TOKEN_HEADER_SIZE + checkSum.length];
+        getMic(ret, 0);
+        return ret;
+    }
+
+    public void getMic(OutputStream os) throws GSSException {
+        try {
+            encodeHeader(os);
+            os.write(checkSum);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Output MicTokenV2 error:" + e.getMessage());
+        }
+    }
+
+    /**
+     * Calculate the checksum for inMsg and compare with it with this token, throw GssException if not equal
+     * @param inMsg
+     * @param msgOffset
+     * @param msgLen
+     * @throws GSSException
+     */
+    public void verify(byte[] inMsg, int msgOffset, int msgLen) throws GSSException {
+        if (!verifyCheckSum(inMsg, msgOffset, msgLen)) {
+            throw new GSSException(GSSException.BAD_MIC, -1, "Corrupt MIC token");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/e680dab2/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV2.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV2.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV2.java
new file mode 100644
index 0000000..3a128a9
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV2.java
@@ -0,0 +1,153 @@
+/**
+ *  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.gssapi.krb5;
+
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+
+public class WrapTokenV2 extends KerbyGssTokenV2 {
+    private MessageProp prop;
+
+    // Generate a token from user input data
+    WrapTokenV2(KerbyContext context,
+              byte[] data,
+              int dataOffset,
+              int dataLength,
+              MessageProp messageProp) throws GSSException {
+        super(TOKEN_WRAP_V2, context);
+
+        prop = messageProp;
+
+        if (prop.getQOP() != 0) {
+            prop.setQOP(0);
+        }
+
+        if (!context.getConfState()) {
+            prop.setPrivacy(false);
+        }
+
+        generateCheckSum(prop, data, dataOffset, dataLength);
+
+        if (prop.getPrivacy()) {
+            byte[] toProcess = new byte[dataLength + TOKEN_HEADER_SIZE];
+            System.arraycopy(data, dataOffset, toProcess, 0, dataLength);
+            encodeHeader(toProcess, dataLength);
+
+            tokenData = encryptor.encryptData(toProcess, getKeyUsage());
+        } else {
+            tokenData = data; // keep it for now
+        }
+    }
+
+    /**
+     * Get bytes of the token
+     * @return
+     */
+    public byte[] wrap() {
+        int dataSize = tokenData.length;
+        int ckSize = checkSum == null ? 0 : checkSum.length;
+        byte[] ret = new byte[TOKEN_HEADER_SIZE + dataSize + ckSize];
+        encodeHeader(ret, 0);
+        System.arraycopy(tokenData, 0, ret, TOKEN_HEADER_SIZE, dataSize);
+        if (ckSize > 0) {
+            System.arraycopy(checkSum, 0, ret, TOKEN_HEADER_SIZE + dataSize, ckSize);
+        }
+        return ret;
+    }
+
+    public void wrap(OutputStream os) throws GSSException {
+        try {
+            encodeHeader(os);
+            os.write(tokenData);
+            int ckSize = checkSum == null ? 0 : checkSum.length;
+            if (ckSize > 0) {
+                os.write(checkSum);
+            }
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Output token error:" + e.getMessage());
+        }
+    }
+
+    // Reconstruct a token from token bytes
+    public WrapTokenV2(KerbyContext context, MessageProp prop, byte[] token, int offset, int len) throws GSSException {
+        super(TOKEN_WRAP_V2, context, prop, token, offset, len);
+        this.prop = prop;
+    }
+
+    // Reconstruct a token from token bytes stream
+    public WrapTokenV2(KerbyContext context, MessageProp prop, InputStream is) throws GSSException {
+        super(TOKEN_WRAP_V2, context, prop, is);
+        this.prop = prop;
+    }
+
+    /**
+     * Get plain text data from token bytes
+     * @param outBuffer
+     * @param offset
+     * @return plain text contained in the wrap token
+     * @throws GSSException
+     */
+    public byte[] unwrap(byte[] outBuffer, int offset) throws GSSException {
+        int lenToCopy;
+        if (prop.getPrivacy()) {
+            byte[] plainText = encryptor.decryptData(tokenData, getKeyUsage());
+            lenToCopy = plainText.length - TOKEN_HEADER_SIZE;
+            if (outBuffer == null) {
+                outBuffer = new byte[lenToCopy];
+                offset = 0;
+            }
+            System.arraycopy(plainText, 0, outBuffer, offset, lenToCopy);
+        } else {
+            lenToCopy = tokenData.length - encryptor.getCheckSumSize();
+            if (outBuffer == null) {
+                outBuffer = new byte[lenToCopy];
+                offset = 0;
+            }
+            System.arraycopy(tokenData, 0, outBuffer, offset, lenToCopy);
+
+            if (!verifyCheckSum(outBuffer, offset, lenToCopy)) {
+                throw new GSSException(GSSException.BAD_MIC, -1, "Corrupt token checksum");
+            }
+        }
+        return outBuffer;
+    }
+
+    public byte[] unwrap() throws GSSException {
+        return unwrap(null, 0);
+    }
+
+    public void unwrap(OutputStream os) throws GSSException {
+        byte[] data = unwrap();
+        try {
+            os.write(data);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Output token error:" + e.getMessage());
+        }
+    }
+
+    static int getSizeLimit(int qop, boolean confReq, int maxTokSize) {
+        return maxTokSize; // TODO: to be implemented
+    }
+}