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/16 07:27:38 UTC

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

Repository: directory-kerby
Updated Branches:
  refs/heads/gssapi 6e34327e2 -> aa0098253


DIRKRB-566 Implement Gss tokens defined in RFC 1964. 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/aa009825
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/aa009825
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/aa009825

Branch: refs/heads/gssapi
Commit: aa0098253b9ee527a077d94272b7be3ae57f780b
Parents: 6e34327
Author: plusplusjiajia <ji...@intel.com>
Authored: Mon May 16 15:32:51 2016 +0800
Committer: plusplusjiajia <ji...@intel.com>
Committed: Mon May 16 15:32:51 2016 +0800

----------------------------------------------------------------------
 .../kerberos/kerb/gssapi/krb5/CredUtils.java    |   8 +-
 .../kerberos/kerb/gssapi/krb5/KerbyContext.java |  33 +-
 .../kerb/gssapi/krb5/KerbyGssEncryptor.java     | 300 +++++++++++++++--
 .../kerb/gssapi/krb5/KerbyGssTokenV1.java       | 319 +++++++++++++++++++
 .../kerberos/kerb/gssapi/krb5/MicTokenV1.java   |  92 ++++++
 .../kerberos/kerb/gssapi/krb5/WrapTokenV1.java  | 196 ++++++++++++
 .../kerberos/kerb/gssapi/krb5/WrapTokenV2.java  |   9 +-
 7 files changed, 921 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/CredUtils.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/CredUtils.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/CredUtils.java
index 6d066db..f7ddc31 100644
--- a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/CredUtils.java
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/CredUtils.java
@@ -4,10 +4,7 @@ import org.ietf.jgss.GSSException;
 import sun.security.jgss.GSSCaller;
 
 import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.kerberos.KerberosTicket;
-import javax.security.auth.kerberos.KeyTab;
-import javax.security.auth.kerberos.ServicePermission;
+import javax.security.auth.kerberos.*;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
@@ -55,7 +52,8 @@ public class CredUtils {
     public static KeyTab getKeyTabFromContext(KerberosPrincipal principal) throws GSSException {
         Set<KeyTab> tabs = getContextCredentials(KeyTab.class);
         for (KeyTab tab : tabs) {
-            if (tab.getPrincipal().equals(principal)) {
+            KerberosKey[] keys = tab.getKeys(principal);
+            if (keys != null && keys.length > 0) {
                 return tab;
             }
         }

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/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 1496cac..0bdd360 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
@@ -469,7 +469,11 @@ public class KerbyContext implements GSSContextSpi {
 
     public int getWrapSizeLimit(int qop, boolean confReq, int maxTokSize)
             throws GSSException {
-        return 65536;   // TODO: to be implemented
+        if (gssEncryptor.isV2()) {
+            return WrapTokenV2.getMsgSizeLimit(qop, confReq, maxTokSize, gssEncryptor);
+        } else {
+            return WrapTokenV1.getMsgSizeLimit(qop, confReq, maxTokSize, gssEncryptor);
+        }
     }
 
     public void wrap(InputStream is, OutputStream os, MessageProp msgProp)
@@ -490,6 +494,9 @@ public class KerbyContext implements GSSContextSpi {
         if (gssEncryptor.isV2()) {
             WrapTokenV2 token = new WrapTokenV2(this, inBuf, 0, len, msgProp);
             token.wrap(os);
+        } else {
+            WrapTokenV1 token = new WrapTokenV1(this, inBuf, 0, len, msgProp);
+            token.wrap(os);
         }
     }
 
@@ -498,10 +505,13 @@ public class KerbyContext implements GSSContextSpi {
         if (ctxState != STATE_ESTABLISHED) {
             throw new GSSException(GSSException.NO_CONTEXT, -1, "Context invalid for wrap");
         }
-        byte[] ret = null;
+        byte[] ret;
         if (gssEncryptor.isV2()) {
             WrapTokenV2 token = new WrapTokenV2(this, inBuf, offset, len, msgProp);
             ret = token.wrap();
+        } else {
+            WrapTokenV1 token = new WrapTokenV1(this, inBuf, offset, len, msgProp);
+            ret = token.wrap();
         }
         return ret;
     }
@@ -515,6 +525,9 @@ public class KerbyContext implements GSSContextSpi {
         if (gssEncryptor.isV2()) {
             WrapTokenV2 token = new WrapTokenV2(this, msgProp, is);
             token.unwrap(os);
+        } else {
+            WrapTokenV1 token = new WrapTokenV1(this, msgProp, is);
+            token.unwrap(os);
         }
     }
 
@@ -524,10 +537,13 @@ public class KerbyContext implements GSSContextSpi {
             throw new GSSException(GSSException.NO_CONTEXT, -1, "Context invalid for unwrap");
         }
 
-        byte[] ret = null;
+        byte[] ret;
         if (gssEncryptor.isV2()) {
             WrapTokenV2 token = new WrapTokenV2(this, msgProp, inBuf, offset, len);
             ret = token.unwrap();
+        } else {
+            WrapTokenV1 token = new WrapTokenV1(this, msgProp, inBuf, offset, len);
+            ret = token.unwrap();
         }
         return ret;
     }
@@ -545,6 +561,9 @@ public class KerbyContext implements GSSContextSpi {
             if (gssEncryptor.isV2()) {
                 MicTokenV2 token = new MicTokenV2(this, inMsg, 0, len, msgProp);
                 token.getMic(os);
+            } else {
+                MicTokenV1 token = new MicTokenV1(this, inMsg, 0, len, msgProp);
+                token.getMic(os);
             }
         } catch (IOException e) {
             throw new GSSException(GSSException.FAILURE, -1, "Error when get user data in getMIC:" + e.getMessage());
@@ -557,10 +576,13 @@ public class KerbyContext implements GSSContextSpi {
             throw new GSSException(GSSException.NO_CONTEXT, -1, "Context invalid for getMIC");
         }
 
-        byte[] ret = null;
+        byte[] ret;
         if (gssEncryptor.isV2()) {
             MicTokenV2 token = new MicTokenV2(this, inMsg, offset, len, msgProp);
             ret = token.getMic();
+        } else {
+            MicTokenV1 token = new MicTokenV1(this, inMsg, offset, len, msgProp);
+            ret = token.getMic();
         }
         return ret;
     }
@@ -594,6 +616,9 @@ public class KerbyContext implements GSSContextSpi {
         if (gssEncryptor.isV2()) {
             MicTokenV2 token = new MicTokenV2(this, msgProp, inTok, tokOffset, tokLen);
             token.verify(inMsg, msgOffset, msgLen);
+        } else {
+            MicTokenV1 token = new MicTokenV1(this, msgProp, inTok, tokOffset, tokLen);
+            token.verify(inMsg, msgOffset, msgLen);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/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
index d65346b..9aff63e 100644
--- 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
@@ -25,29 +25,66 @@ 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.crypto.cksum.provider.Md5Provider;
+import org.apache.kerby.kerberos.kerb.crypto.enc.provider.DesProvider;
+import org.apache.kerby.kerberos.kerb.crypto.enc.provider.Rc4Provider;
 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;
 
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
 /**
  * This class implements encryption related function used in GSS tokens
  */
 public class KerbyGssEncryptor {
 
-    private EncryptionKey encKey;
+    private final EncryptionKey encKey;
+    private final EncryptionType encKeyType; // The following two variables used for convenience
+    private final byte[] encKeyBytes;
+
+    private CheckSumType checkSumTypeDef;
+    private int checkSumSize;
+
     private boolean isV2 = false;
+    private int sgnAlg = 0xFFFF;
+    private int sealAlg = 0xFFFF;
+    private boolean isArcFourHmac = false;
+
+    private static final byte[] IV_ZEROR_8B = new byte[8];
 
     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) {
+        encKeyBytes = encKey.getKeyData();
+        encKeyType = key.getKeyType();
+
+        if (encKeyType == EncryptionType.AES128_CTS_HMAC_SHA1_96) {
+            checkSumSize = 12;
+            checkSumTypeDef = CheckSumType.HMAC_SHA1_96_AES128;
             isV2 = true;
+        } else if (encKeyType == EncryptionType.AES256_CTS_HMAC_SHA1_96) {
+            checkSumSize = 12;
+            checkSumTypeDef = CheckSumType.HMAC_SHA1_96_AES256;
+            isV2 = true;
+        } else if (encKeyType == EncryptionType.DES_CBC_CRC || encKeyType == EncryptionType.DES_CBC_MD5) {
+            sgnAlg = KerbyGssTokenV1.SGN_ALG_DES_MAC_MD5;
+            sealAlg = KerbyGssTokenV1.SEAL_ALG_DES;
+            checkSumSize = 8;
+        } else if (encKeyType == EncryptionType.DES3_CBC_SHA1) {
+            sgnAlg = KerbyGssTokenV1.SGN_ALG_HMAC_SHA1_DES3_KD;
+            sealAlg = KerbyGssTokenV1.SEAL_ALG_DES3_KD;
+            checkSumSize = 20;
+        } else if (encKeyType == EncryptionType.ARCFOUR_HMAC) {
+            sgnAlg = KerbyGssTokenV1.SGN_ALG_RC4_HMAC;
+            sealAlg = KerbyGssTokenV1.SEAL_ALG_RC4_HMAC;
+            checkSumSize = 16;
+            isArcFourHmac = true;
         } else {
             throw new GSSException(GSSException.FAILURE, -1,
-                    "Invalid encryption type: " + key.getKeyType().getDisplayName());
+                    "Invalid encryption type: " + encKeyType.getDisplayName());
         }
     }
 
@@ -59,6 +96,18 @@ public class KerbyGssEncryptor {
         return isV2;
     }
 
+    public int getSgnAlg() {
+        return sgnAlg;
+    }
+
+    public int getSealAlg() {
+        return sealAlg;
+    }
+
+    public boolean isArcFourHmac() {
+        return isArcFourHmac;
+    }
+
     public byte[] encryptData(byte[] tokenHeader, byte[] data,
                               int offset, int len, int keyUsage) throws GSSException {
         byte[] ret;
@@ -102,37 +151,238 @@ public class KerbyGssEncryptor {
         }
 
         try {
-            return getCheckSumHandler().checksumWithKey(buffer, encKey.getKeyData(), keyUsage);
+            return CheckSumHandler.getCheckSumHandler(checkSumTypeDef)
+                    .checksumWithKey(buffer, encKey.getKeyData(), keyUsage);
         } catch (KrbException e) {
             throw new GSSException(GSSException.FAILURE, -1,
-                    "Exception in checksum calculation:" + encKey.getKeyType().getName());
+                    "Exception in checksum calculation:" + e.getMessage());
+        }
+    }
+
+    /**
+     * Get the size of the corresponding checksum algorithm
+     * @return
+     * @throws GSSException
+     */
+    public int getCheckSumSize() throws GSSException {
+        return checkSumSize;
+    }
+
+
+    private void addPadding(int paddingLen, byte[] outBuf, int offset) {
+        for (int i = 0; i < paddingLen; i++) {
+            outBuf[offset + i] = (byte) paddingLen;
+        }
+    }
+
+    private byte[] getFirstBytes(byte[] src, int len) {
+        if (len < src.length) {
+            byte[] ret = new byte[len];
+            System.arraycopy(src, 0, ret, 0, len);
+            return ret;
         }
+        return src;
     }
 
-    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;
+    private byte[] getKeyBytesWithLength(int len) {
+        return getFirstBytes(encKeyBytes, len);
+    }
+
+    public byte[] calculateCheckSum(byte[] confounder, byte[] header,
+                                    byte[] data, int offset, int len, int paddingLen, boolean isMic)
+            throws GSSException {
+        byte[] ret;
+        int keyUsage = KerbyGssTokenV1.KG_USAGE_SIGN;
+        CheckSumTypeHandler handler;
+
+        int keySize;
+        byte[] key;
+        byte[] toProc;
+        int toOffset;
+        int toLen = (confounder == null ? 0 : confounder.length)
+                + (header == null ? 0 : header.length) + len + paddingLen;
+        if (toLen == len) {
+            toProc = data;
+            toOffset = offset;
         } else {
+            toOffset = 0;
+            int idx = 0;
+            toProc = new byte[toLen];
+
+            if (header != null) {
+                System.arraycopy(header, 0, toProc, idx, header.length);
+                idx += header.length;
+            }
+
+            if (confounder != null) {
+                System.arraycopy(confounder, 0, toProc, idx, confounder.length);
+                idx += confounder.length;
+            }
+
+            System.arraycopy(data, offset, toProc, idx, len);
+            addPadding(paddingLen, toProc, len + idx);
+        }
+
+        CheckSumType chksumType;
+        try {
+            switch (sgnAlg) {
+                case KerbyGssTokenV1.SGN_ALG_DES_MAC_MD5:
+                    Md5Provider md5Provider = new Md5Provider();
+                    md5Provider.hash(toProc);
+                    toProc = md5Provider.output();
+
+                case KerbyGssTokenV1.SGN_ALG_DES_MAC:
+                    DesProvider desProvider = new DesProvider();
+                    return desProvider.cbcMac(encKeyBytes, IV_ZEROR_8B, toProc);
+
+                case KerbyGssTokenV1.SGN_ALG_HMAC_SHA1_DES3_KD:
+                    chksumType = CheckSumType.HMAC_SHA1_DES3_KD;
+                    break;
+                case KerbyGssTokenV1.SGN_ALG_RC4_HMAC:
+                    chksumType = CheckSumType.MD5_HMAC_ARCFOUR;
+                    if (isMic) {
+                        keyUsage = KerbyGssTokenV1.KG_USAGE_MS_SIGN;
+                    }
+                    break;
+                case KerbyGssTokenV1.SGN_ALG_MD25:
+                    throw new GSSException(GSSException.FAILURE, -1, "CheckSum not implemented for SGN_ALG_MD25");
+                default:
+                    throw new GSSException(GSSException.FAILURE, -1, "CheckSum not implemented for sgnAlg=" + sgnAlg);
+            }
+            handler = CheckSumHandler.getCheckSumHandler(chksumType);
+            keySize = handler.keySize();
+            key = getKeyBytesWithLength(keySize);
+            ret = handler.checksumWithKey(toProc, toOffset, toLen, key, keyUsage);
+        } catch (KrbException e) {
             throw new GSSException(GSSException.FAILURE, -1,
-                    "Unsupported checksum encryption type:" + encKey.getKeyType().getName());
+                    "Exception in checksum calculation sgnAlg = " + sgnAlg + " : " + e.getMessage());
         }
+        return ret;
+    }
+
+    public byte[] encryptSequenceNumber(byte[] seqBytes, byte[] ivSrc, boolean encrypt)
+            throws GSSException {
+        EncTypeHandler handler;
         try {
-            return CheckSumHandler.getCheckSumHandler(checkSumType);
+            switch (sgnAlg) {
+                case KerbyGssTokenV1.SGN_ALG_DES_MAC_MD5:
+                case KerbyGssTokenV1.SGN_ALG_DES_MAC:
+                    DesProvider desProvider = new DesProvider();
+                    byte[] data = seqBytes.clone();
+                    if (encrypt) {
+                        desProvider.encrypt(encKeyBytes, ivSrc, data);
+                    } else {
+                        desProvider.decrypt(encKeyBytes, ivSrc, data);
+                    }
+                    return data;
+                case KerbyGssTokenV1.SGN_ALG_HMAC_SHA1_DES3_KD:
+                    handler = EncryptionHandler.getEncHandler(EncryptionType.DES3_CBC_SHA1_KD);
+                    break;
+                case KerbyGssTokenV1.SGN_ALG_RC4_HMAC:
+                    return encryptArcFourHmac(seqBytes, getKeyBytesWithLength(16), getFirstBytes(ivSrc, 8), encrypt);
+                case KerbyGssTokenV1.SGN_ALG_MD25:
+                    throw new GSSException(GSSException.FAILURE, -1, "EncSeq not implemented for SGN_ALG_MD25");
+                default:
+                    throw new GSSException(GSSException.FAILURE, -1, "EncSeq not implemented for sgnAlg=" + sgnAlg);
+            }
+            int keySize = handler.keySize();
+            byte[] key = getKeyBytesWithLength(keySize);
+            int ivLen = handler.encProvider().blockSize();
+            byte[] iv = getFirstBytes(ivSrc, ivLen);
+            if (encrypt) {
+                return handler.encryptRaw(seqBytes, key, iv, KerbyGssTokenV1.KG_USAGE_SEQ);
+            } else {
+                return handler.decryptRaw(seqBytes, key, iv, KerbyGssTokenV1.KG_USAGE_SEQ);
+            }
         } catch (KrbException e) {
             throw new GSSException(GSSException.FAILURE, -1,
-                    "Unsupported checksum type:" + checkSumType.getName());
+                    "Exception in encrypt seq number sgnAlg = " + sgnAlg + " : " + e.getMessage());
         }
     }
 
-    /**
-     * Get the size of the corresponding checksum algorithm
-     * @return
-     * @throws GSSException
-     */
-    public int getCheckSumSize() throws GSSException {
-        return getCheckSumHandler().cksumSize();
+    private byte[] getHmacMd5(byte[] key, byte[] salt) throws GSSException {
+        try {
+            SecretKey secretKey = new SecretKeySpec(key, "HmacMD5");
+            Mac mac = Mac.getInstance("HmacMD5");
+            mac.init(secretKey);
+            return mac.doFinal(salt);
+        } catch (Exception e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Get HmacMD5 failed: " + e.getMessage());
+        }
+    }
+
+    private byte[] encryptArcFourHmac(byte[] data, byte[] key, byte[] iv, boolean encrypt)
+            throws GSSException {
+        byte[] sk1 = getHmacMd5(key, new byte[4]);
+        byte[] sk2 = getHmacMd5(sk1, iv);
+        Rc4Provider provider = new Rc4Provider();
+        try {
+            byte[] ret = data.clone();
+            if (encrypt) {
+                provider.encrypt(sk2, ret);
+            } else {
+                provider.decrypt(sk2, ret);
+            }
+            return ret;
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "En/Decrypt sequence failed for ArcFourHmac: " + e.getMessage());
+        }
+    }
+
+    private byte[] encryptDataArcFourHmac(byte[] data, byte[] key, byte[] seqNum, boolean encrypt) throws GSSException {
+        byte[] dataKey = new byte[key.length];
+        for (int i = 0; i <= 15; i++) {
+            dataKey[i] = (byte) (key[i] ^ 0xF0);
+        }
+        return encryptArcFourHmac(data, dataKey, seqNum, encrypt);
+    }
+
+    public byte[] encryptTokenV1(byte[] confounder, byte[] data, int offset, int len,
+                            int paddingLen, byte[] seqNumber, boolean encrypt) throws GSSException {
+        byte[] toProc;
+        if (encrypt) {
+            int toLen = (confounder == null ? 0 : confounder.length) + len + paddingLen;
+            int index = 0;
+            toProc = new byte[toLen];
+            if (confounder != null) {
+                System.arraycopy(confounder, 0, toProc, 0, confounder.length);
+                index += confounder.length;
+            }
+            System.arraycopy(data, offset, toProc, index, len);
+            addPadding(paddingLen, toProc, index + len);
+        } else {
+            toProc = data;
+            if (data.length != len) {
+                toProc = new byte[len];
+                System.arraycopy(data, offset, toProc, 0, len);
+            }
+        }
+        EncTypeHandler handler;
+        try {
+            switch (sealAlg) {
+                case KerbyGssTokenV1.SEAL_ALG_DES:
+                    handler = EncryptionHandler.getEncHandler(EncryptionType.DES_CBC_MD5);
+                    break;
+                case KerbyGssTokenV1.SEAL_ALG_DES3_KD:
+                    handler = EncryptionHandler.getEncHandler(EncryptionType.DES3_CBC_SHA1_KD);
+                    break;
+                case KerbyGssTokenV1.SEAL_ALG_RC4_HMAC:
+                    return encryptDataArcFourHmac(toProc, getKeyBytesWithLength(16), seqNumber, encrypt);
+                default:
+                    throw new GSSException(GSSException.FAILURE, -1, "Unknown encryption type sealAlg = " + sealAlg);
+            }
+
+            int keySize = handler.keySize();
+            byte[] key = getKeyBytesWithLength(keySize);
+            if (encrypt) {
+                return handler.encryptRaw(toProc, key, KerbyGssTokenV1.KG_USAGE_SEAL);
+            } else {
+                return handler.decryptRaw(toProc, key, KerbyGssTokenV1.KG_USAGE_SEAL);
+            }
+        } catch (KrbException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Exception in encrypt data sealAlg = " + sealAlg + " : " + e.getMessage());
+        }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV1.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV1.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV1.java
new file mode 100644
index 0000000..6b1a2c7
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/KerbyGssTokenV1.java
@@ -0,0 +1,319 @@
+/**
+ *  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 sun.security.jgss.GSSHeader;
+import sun.security.util.ObjectIdentifier;
+
+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 1964 and its updates
+ *
+ * The GSS Wrap token has the following format:
+ *
+ * Byte no          Name           Description
+ * 0..1           TOK_ID          0201
+ *
+ * 2..3           SGN_ALG         Checksum algorithm indicator.
+ *                                00 00 DES MAC MD5
+ *                                01 00 MD2.5
+ *                                02 00 DES MAC
+ *                                04 00 HMAC SHA1 DES3-KD
+ *                                11 00 RC4-HMAC used by Microsoft Windows, RFC 4757
+ * 4..5           SEAL_ALG        ff ff none
+ *                                00 00 DES
+ *                                02 00 DES3-KD
+ *                                10 00 RC4-HMAC
+ * 6..7           Filler          FF FF
+ * 8..15          SND_SEQ         Encrypted sequence number field.
+ * 16..23         SNG_CKSUM       Checksum of plaintext padded data,
+ *                                calculated according to algorithm
+ *                                specified in SGN_ALG field.
+ * 24..           Data            Encrypted or plaintext padded data
+ *
+ *
+ *
+ * Use of the GSS MIC token has the following format:
+
+ * Byte no          Name           Description
+ * 0..1           TOK_ID          0101
+ * 2..3           SGN_ALG         Integrity algorithm indicator.
+ * 4..7           Filler          Contains ff ff ff ff
+ * 8..15          SND_SEQ         Sequence number field.
+ * 16..23         SGN_CKSUM       Checksum of "to-be-signed data",
+ *                                calculated according to algorithm
+ *                                specified in SGN_ALG field.
+ *
+ */
+abstract class KerbyGssTokenV1 extends KerbyGssTokenBase {
+    // SGN ALG
+    public static final int SGN_ALG_DES_MAC_MD5 = 0;
+    public static final int SGN_ALG_MD25 = 0x0100;
+    public static final int SGN_ALG_DES_MAC = 0x0200;
+    public static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400;
+    public static final int SGN_ALG_RC4_HMAC = 0x1100;
+
+    // SEAL ALG
+    public static final int SEAL_ALG_NONE = 0xFFFF;
+    public static final int SEAL_ALG_DES = 0x0;  // "DES/CBC/NoPadding"
+    public static final int SEAL_ALG_DES3_KD = 0x0200;
+    public static final int SEAL_ALG_RC4_HMAC = 0x1000;
+
+    public static final int KG_USAGE_SEAL = 22;
+    public static final int KG_USAGE_SIGN = 23;
+    public static final int KG_USAGE_SEQ = 24;
+    public static final int KG_USAGE_MS_SIGN = 15;
+
+    private boolean isInitiator;
+    private boolean confState;
+    private int sequenceNumber;
+
+    protected KerbyGssEncryptor encryptor;
+
+    private GSSHeader gssHeader;
+
+    public static final int TOKEN_HEADER_COMM_SIZE = 8;
+    public static final int TOKEN_HEADER_SEQ_SIZE = 8;
+
+    // Token commHeader data
+    private int tokenType;
+    private byte[] commHeader = new byte[TOKEN_HEADER_COMM_SIZE];
+    private int sgnAlg;
+    private int sealAlg;
+
+    private byte[] plainSequenceBytes;
+    private byte[] encryptedSequenceNumber = new byte[TOKEN_HEADER_SEQ_SIZE];
+    private byte[] checkSum;
+    private int checkSumSize;
+
+    protected int reconHeaderLen; // only used for certain reason
+
+    public static ObjectIdentifier objId;
+
+    static {
+        try {
+            objId = new ObjectIdentifier("1.2.840.113554.1.2.2");
+        } catch (IOException ioe) { // NOPMD
+        }
+    }
+
+    protected int getTokenHeaderSize() {
+        return TOKEN_HEADER_COMM_SIZE + TOKEN_HEADER_SEQ_SIZE + checkSumSize;
+    }
+
+    protected byte[] getPlainSequenceBytes() {
+        byte[] ret = new byte[4];
+        ret[0] = plainSequenceBytes[0];
+        ret[1] = plainSequenceBytes[1];
+        ret[2] = plainSequenceBytes[2];
+        ret[3] = plainSequenceBytes[3];
+        return ret;
+    }
+
+    // Generate a new token
+    KerbyGssTokenV1(int tokenType, KerbyContext context) throws GSSException {
+        initialize(tokenType, context, false);
+        createTokenHeader();
+    }
+
+    // Reconstruct a token
+    KerbyGssTokenV1(int tokenType, KerbyContext context, MessageProp prop,
+                    byte[] token, int offset, int size) throws GSSException {
+        int proxLen = size > 64 ? 64 : size;
+        InputStream is = new ByteArrayInputStream(token, offset, proxLen);
+        reconstructInitializaion(tokenType, context, prop, is);
+        reconHeaderLen = gssHeader.getLength() + getTokenHeaderSize();
+    }
+
+    // Reconstruct a token
+    KerbyGssTokenV1(int tokenType, KerbyContext context, MessageProp prop, InputStream is) throws GSSException {
+        reconstructInitializaion(tokenType, context, prop, is);
+    }
+
+    private void reconstructInitializaion(int tokenType, KerbyContext context, MessageProp prop, InputStream is)
+            throws GSSException {
+        initialize(tokenType, context, true);
+        if (!confState) {
+            prop.setPrivacy(false);
+        }
+
+        try {
+            gssHeader = new GSSHeader(is);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token:" + e.getMessage());
+        }
+
+        if (!gssHeader.getOid().equals((Object) objId)) {
+            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token OID");
+        }
+
+        reconstructTokenHeader(is, prop);
+    }
+
+    private void initialize(int tokenType,
+                            KerbyContext context,
+                            boolean reconstruct) throws GSSException {
+        this.tokenType = tokenType;
+        this.isInitiator = context.isInitiator();
+        this.confState = context.getConfState();
+        this.encryptor = context.getGssEncryptor();
+        this.checkSumSize = encryptor.getCheckSumSize();
+        if (!reconstruct) {
+            this.sequenceNumber = context.incMySequenceNumber();
+        } else {
+            checkSum = new byte[checkSumSize];
+        }
+    }
+
+    protected void calcPrivacyInfo(MessageProp prop, byte[] confounder, byte[] data,
+                                   int dataOffset, int dataLength, int paddingLen) throws GSSException {
+        prop.setQOP(0);
+        if (!confState) {
+            prop.setPrivacy(false);
+        }
+
+        checkSum = calcCheckSum(confounder, commHeader, data, dataOffset, dataLength, paddingLen);
+        encryptSequenceNumber();
+    }
+
+    protected void verifyToken(byte[] confounder, byte[] data, int dataOffset, int dataLength, int paddingLen)
+            throws GSSException {
+        byte[] sum = calcCheckSum(confounder, commHeader, data, dataOffset, dataLength, paddingLen);
+        if (!MessageDigest.isEqual(checkSum, sum)) {
+            throw new GSSException(GSSException.BAD_MIC, -1,
+                    "Corrupt token checksum for " + (tokenType == TOKEN_MIC_V1 ? "Mic" : "Wrap") + "TokenV1");
+        }
+    }
+
+    private byte[] calcCheckSum(byte[] confounder, byte[] header, byte[] data,
+                                int dataOffset, int dataLength, int paddingLen) throws GSSException {
+        return encryptor.calculateCheckSum(confounder, header, data, dataOffset, dataLength, paddingLen,
+                tokenType == TOKEN_MIC_V1);
+    }
+
+    private void encryptSequenceNumber() throws GSSException {
+        plainSequenceBytes = new byte[8];
+        if (encryptor.isArcFourHmac()) {
+            writeBigEndian(plainSequenceBytes, 0, sequenceNumber);
+        } else {
+            plainSequenceBytes[0] = (byte) sequenceNumber;
+            plainSequenceBytes[1] = (byte) (sequenceNumber >>> 8);
+            plainSequenceBytes[2] = (byte) (sequenceNumber >>> 16);
+            plainSequenceBytes[3] = (byte) (sequenceNumber >>> 24);
+        }
+
+        // Hex 0 - sender is the context initiator, Hex FF - sender is the context acceptor
+        if (!isInitiator) {
+            plainSequenceBytes[4] = (byte) 0xFF;
+            plainSequenceBytes[5] = (byte) 0xFF;
+            plainSequenceBytes[6] = (byte) 0xFF;
+            plainSequenceBytes[7] = (byte) 0xFF;
+        }
+
+        encryptedSequenceNumber = encryptor.encryptSequenceNumber(plainSequenceBytes, checkSum, true);
+    }
+
+    public void encodeHeader(OutputStream os) throws GSSException, IOException {
+        // | GSSHeader | TokenHeader |
+        GSSHeader gssHeader = new GSSHeader(objId, getTokenSizeWithoutGssHeader());
+        gssHeader.encode(os);
+        os.write(commHeader);
+        os.write(encryptedSequenceNumber);
+        os.write(checkSum);
+    }
+
+    private void createTokenHeader() {
+        commHeader[0] = (byte) (tokenType >>> 8);
+        commHeader[1] = (byte) tokenType;
+
+        sgnAlg = encryptor.getSgnAlg();
+        commHeader[2] = (byte) (sgnAlg >>> 8);
+        commHeader[3] = (byte) sgnAlg;
+
+        if (tokenType == TOKEN_WRAP_V1) {
+            sealAlg = encryptor.getSealAlg();
+            commHeader[4] = (byte) (sealAlg >>> 8);
+            commHeader[5] = (byte) sealAlg;
+        } else {
+            commHeader[4] = (byte) 0xFF;
+            commHeader[5] = (byte) 0xFF;
+        }
+
+        commHeader[6] = (byte) 0xFF;
+        commHeader[7] = (byte) 0xFF;
+    }
+
+    // Re-construct token commHeader
+    private void reconstructTokenHeader(InputStream is, MessageProp prop) throws GSSException {
+        try {
+            if (is.read(commHeader) != commHeader.length
+                    || is.read(encryptedSequenceNumber) != encryptedSequenceNumber.length
+                    || is.read(checkSum) != checkSum.length) {
+                throw new GSSException(GSSException.FAILURE, -1,
+                        "Insufficient in reconstruct token header");
+            }
+            initTokenHeader(commHeader, prop);
+
+            plainSequenceBytes = encryptor.encryptSequenceNumber(encryptedSequenceNumber, checkSum, false);
+            byte dirc = isInitiator ? (byte) 0xFF : 0;
+            // Hex 0 - sender is the context initiator, Hex FF - sender is the context acceptor
+            if (!(plainSequenceBytes[4] == dirc && plainSequenceBytes[5] == dirc
+                    && plainSequenceBytes[6] == dirc && plainSequenceBytes[7] == dirc)) {
+                throw new GSSException(GSSException.BAD_MIC, -1,
+                        "Corrupt token sequence for " + (tokenType == TOKEN_MIC_V1 ? "Mic" : "Wrap") + "TokenV1");
+            }
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Error in reconstruct token header:" + e.getMessage());
+        }
+    }
+
+    private void initTokenHeader(byte[] tokenBytes, MessageProp prop) throws GSSException {
+        int tokenIDRecv = (((int) tokenBytes[0]) << 8) + tokenBytes[1];
+        if (tokenType != tokenIDRecv) {
+            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
+                    "Token ID should be " + tokenType + " instead of " + tokenIDRecv);
+        }
+
+        sgnAlg = (((int) tokenBytes[2]) << 8) + tokenBytes[3];
+        sealAlg = (((int) tokenBytes[4]) << 8) + tokenBytes[5];
+
+        if (tokenBytes[6] != (byte) 0xFF || tokenBytes[7] != (byte) 0xFF) {
+            throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, "Invalid token head filler");
+        }
+
+        prop.setQOP(0);
+        prop.setPrivacy(sealAlg != SEAL_ALG_NONE);
+    }
+
+    protected GSSHeader getGssHeader() {
+        return gssHeader;
+    }
+
+    abstract int getTokenSizeWithoutGssHeader();
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV1.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV1.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV1.java
new file mode 100644
index 0000000..6a76e4c
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/MicTokenV1.java
@@ -0,0 +1,92 @@
+/**
+ *  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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class MicTokenV1 extends KerbyGssTokenV1 {
+    public MicTokenV1(KerbyContext context,
+                       byte[] inMsg,
+                       int msgOffset,
+                       int msgLength,
+                       MessageProp messageProp) throws GSSException {
+        super(TOKEN_MIC_V1, context);
+        calcPrivacyInfo(messageProp, null, inMsg, msgOffset, msgLength, 0);
+    }
+
+    // This is called to construct MicToken from MicToken bytes
+    MicTokenV1(KerbyContext context,
+               MessageProp messageProp,
+               byte[] inToken,
+               int tokenOffset,
+               int tokenLength) throws GSSException {
+        super(TOKEN_MIC_V1, context, messageProp, inToken, tokenOffset, tokenLength);
+    }
+
+    public int getMic(byte[] outToken, int offset) throws GSSException, IOException {
+        byte[] data = getMic();
+        System.arraycopy(data, 0, outToken, offset, data.length);
+        return data.length;
+    }
+
+    /**
+     * Get bytes for this Mic token
+     * @return
+     */
+    public byte[] getMic() throws GSSException {
+        ByteArrayOutputStream os = new ByteArrayOutputStream(64);
+        getMic(os);
+        return os.toByteArray();
+    }
+
+    public void getMic(OutputStream os) throws GSSException {
+        try {
+            encodeHeader(os);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Error in output MicTokenV1 bytes:" + e.getMessage());
+        }
+    }
+
+    public void verify(InputStream is) throws GSSException {
+        byte[] data;
+        try {
+            data = new byte[is.available()];
+            is.read(data);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Read plain data for MicTokenV1 error:" + e.getMessage());
+        }
+        verify(data, 0, data.length);
+    }
+
+    public void verify(byte[] data, int offset, int len) throws GSSException {
+        verifyToken(null, data, offset, len, 0);
+    }
+
+    protected int getTokenSizeWithoutGssHeader() {
+        return getTokenHeaderSize();
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV1.java
----------------------------------------------------------------------
diff --git a/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV1.java b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV1.java
new file mode 100644
index 0000000..8ecdae4
--- /dev/null
+++ b/kerby-kerb/kerb-gssapi/src/main/java/org/apache/kerby/kerberos/kerb/gssapi/krb5/WrapTokenV1.java
@@ -0,0 +1,196 @@
+/**
+ *  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.crypto.util.Random;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+import sun.security.jgss.GSSHeader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class WrapTokenV1 extends KerbyGssTokenV1 {
+    public static final int CONFOUNDER_SIZE = 8;
+
+    private boolean privacy;
+
+    private byte[] inData;
+    private int inOffset;
+    private int inLen;
+
+    private int paddingLen;
+    private byte[] confounder;
+    private int tokenBodyLen;
+
+    private byte[] bodyData;
+    private int bodyOffset;
+    private int bodyLen;
+
+    // for reconstruct
+    private int rawDataLength;
+    private byte[] rawData;
+    private int rawDataOffset;
+
+
+    // Generate wrap token according user data
+    public WrapTokenV1(KerbyContext context,
+                       byte[] inMsg,
+                       int msgOffset,
+                       int msgLength,
+                       MessageProp prop) throws GSSException {
+        super(TOKEN_WRAP_V1, context);
+
+        paddingLen = getPaddingLength(msgLength);
+        confounder = Random.makeBytes(CONFOUNDER_SIZE);
+        tokenBodyLen = CONFOUNDER_SIZE + msgLength + paddingLen;
+
+        calcPrivacyInfo(prop, confounder, inMsg, msgOffset, msgLength, paddingLen);
+
+        if (!context.getConfState()) {
+            prop.setPrivacy(false);
+        }
+        privacy = prop.getPrivacy();
+        inData = inMsg;
+        inOffset = msgOffset;
+        inLen = msgLength;
+    }
+
+    // Reconstruct a token from token bytes
+    public WrapTokenV1(KerbyContext context, MessageProp prop,
+                       byte[] token, int offset, int len) throws GSSException {
+        super(TOKEN_WRAP_V1, context, prop, token, offset, len);
+        // adjust the offset to the beginning of the body
+        bodyData = token;
+        bodyOffset = offset + reconHeaderLen;
+        bodyLen = len - reconHeaderLen;
+        getRawData(prop);
+    }
+
+    // Reconstruct a token from token bytes stream
+    public WrapTokenV1(KerbyContext context, MessageProp prop, InputStream is) throws GSSException {
+        super(TOKEN_WRAP_V1, context, prop, is);
+        byte[] token;
+        int len;
+        try {
+            len = is.available();
+            token = new byte[len];
+            is.read(token);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Read wrap token V1 error:" + e.getMessage());
+        }
+        bodyData = token;
+        bodyOffset = 0;
+        bodyLen = len;
+        getRawData(prop);
+    }
+
+    private void getRawData(MessageProp prop) throws GSSException {
+        privacy = prop.getPrivacy();
+        tokenBodyLen = getGssHeader().getMechTokenLength() - getTokenHeaderSize();
+
+        if (bodyLen < tokenBodyLen) {
+            throw new GSSException(GSSException.FAILURE, -1, "Insufficient data for Wrap token V1");
+        }
+
+        if (privacy) {
+            rawData = encryptor.encryptTokenV1(null, bodyData, bodyOffset, tokenBodyLen, 0,
+                    encryptor.isArcFourHmac() ? getPlainSequenceBytes() : null, false);
+            paddingLen = rawData[rawData.length - 1];
+            rawDataOffset = CONFOUNDER_SIZE;
+        } else {
+            rawData = bodyData;
+            paddingLen = bodyData[bodyOffset + tokenBodyLen - 1];
+            rawDataOffset = bodyOffset + CONFOUNDER_SIZE;
+        }
+        rawDataLength = tokenBodyLen - CONFOUNDER_SIZE - paddingLen;
+
+        verifyToken(null, rawData, rawDataOffset - CONFOUNDER_SIZE, tokenBodyLen, 0);
+    }
+
+    // Get plain text data from token data bytes
+    public byte[] unwrap() throws GSSException {
+        byte[] ret = new byte[rawDataLength];
+        System.arraycopy(rawData, rawDataOffset, ret, 0, rawDataLength);
+        return ret;
+    }
+
+    public void unwrap(OutputStream os) throws GSSException {
+        try {
+            os.write(rawData, rawDataOffset, rawDataLength);
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1,
+                    "Error in output wrap token v1 data bytes:" + e.getMessage());
+        }
+    }
+
+    public byte[] wrap() throws GSSException {
+        ByteArrayOutputStream os = new ByteArrayOutputStream(getTokenSizeWithoutGssHeader() + inLen + 64);
+        wrap(os);
+        return os.toByteArray();
+    }
+
+    public void wrap(OutputStream os) throws GSSException {
+        try {
+            encodeHeader(os);
+            if (privacy) {
+                byte[] enc = encryptor.encryptTokenV1(confounder, inData, inOffset, inLen, paddingLen,
+                        encryptor.isArcFourHmac() ? getPlainSequenceBytes() : null, true);
+                os.write(enc);
+            } else {
+                os.write(confounder);
+                os.write(inData, inOffset, inLen);
+                os.write(getPaddingBytes(paddingLen));
+            }
+        } catch (IOException e) {
+            throw new GSSException(GSSException.FAILURE, -1, "Error in output wrap token v1 bytes:" + e.getMessage());
+        }
+    }
+
+    protected int getTokenSizeWithoutGssHeader() {
+        return tokenBodyLen + getTokenHeaderSize();
+    }
+
+    private int getPaddingLength(int dataLen) {
+        if (encryptor.isArcFourHmac()) {
+            return 1;
+        }
+        return 8 - (dataLen % 8);
+    }
+
+    private byte[] getPaddingBytes(int len) {
+        byte[] ret = new byte[len];
+        int i = 0;
+        while (i < len) {
+            ret[i++] = (byte) len;
+        }
+        return ret;
+    }
+
+    public static int getMsgSizeLimit(int qop, boolean confReq, int maxTokSize, KerbyGssEncryptor encryptor)
+            throws GSSException {
+        return GSSHeader.getMaxMechTokenSize(objId, maxTokSize)
+                - encryptor.getCheckSumSize()
+                - TOKEN_HEADER_COMM_SIZE - TOKEN_HEADER_SEQ_SIZE
+                - CONFOUNDER_SIZE - 8;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/aa009825/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
index 3a128a9..57f9e45 100644
--- 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
@@ -147,7 +147,12 @@ public class WrapTokenV2 extends KerbyGssTokenV2 {
         }
     }
 
-    static int getSizeLimit(int qop, boolean confReq, int maxTokSize) {
-        return maxTokSize; // TODO: to be implemented
+    public static int getMsgSizeLimit(int qop, boolean confReq, int maxTokSize, KerbyGssEncryptor encryptor)
+            throws GSSException {
+        if (confReq) {
+            return maxTokSize - encryptor.getCheckSumSize() - TOKEN_HEADER_SIZE * 2 - CONFOUNDER_SIZE;
+        } else {
+            return maxTokSize - encryptor.getCheckSumSize() - TOKEN_HEADER_SIZE;
+        }
     }
 }