| /* |
| * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.jgss.krb5; |
| |
| import org.ietf.jgss.*; |
| import sun.security.jgss.*; |
| import sun.security.krb5.*; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.IOException; |
| import java.io.ByteArrayInputStream; |
| import java.security.GeneralSecurityException; |
| import java.security.MessageDigest; |
| |
| /** |
| * This class is a base class for new GSS token definitions, as defined |
| * in draft-ietf-krb-wg-gssapi-cfx-07.txt, that pertain to per-message |
| * GSS-API calls. Conceptually GSS-API has two types of per-message tokens: |
| * WrapToken and MicToken. They differ in the respect that a WrapToken |
| * carries additional plaintext or ciphertext application data besides |
| * just the sequence number and checksum. This class encapsulates the |
| * commonality in the structure of the WrapToken and the MicToken. |
| * This structure can be represented as: |
| * <p> |
| * <pre> |
| * Wrap Tokens |
| * |
| * Octet no Name Description |
| * --------------------------------------------------------------- |
| * 0..1 TOK_ID Identification field. Tokens emitted by |
| * GSS_Wrap() contain the the hex value 05 04 |
| * expressed in big endian order in this field. |
| * 2 Flags Attributes field, as described in section |
| * 4.2.2. |
| * 3 Filler Contains the hex value FF. |
| * 4..5 EC Contains the "extra count" field, in big |
| * endian order as described in section 4.2.3. |
| * 6..7 RRC Contains the "right rotation count" in big |
| * endian order, as described in section 4.2.5. |
| * 8..15 SND_SEQ Sequence number field in clear text, |
| * expressed in big endian order. |
| * 16..last Data Encrypted data for Wrap tokens with |
| * confidentiality, or plaintext data followed |
| * by the checksum for Wrap tokens without |
| * confidentiality, as described in section |
| * 4.2.4. |
| * MIC Tokens |
| * |
| * Octet no Name Description |
| * ----------------------------------------------------------------- |
| * 0..1 TOK_ID Identification field. Tokens emitted by |
| * GSS_GetMIC() contain the hex value 04 04 |
| * expressed in big endian order in this field. |
| * 2 Flags Attributes field, as described in section |
| * 4.2.2. |
| * 3..7 Filler Contains five octets of hex value FF. |
| * 8..15 SND_SEQ Sequence number field in clear text, |
| * expressed in big endian order. |
| * 16..last SGN_CKSUM Checksum of the "to-be-signed" data and |
| * octet 0..15, as described in section 4.2.4. |
| * |
| * </pre> |
| * <p> |
| * |
| * @author Seema Malkani |
| */ |
| |
| abstract class MessageToken_v2 extends Krb5Token { |
| |
| private static final int TOKEN_ID_POS = 0; |
| private static final int TOKEN_FLAG_POS = 2; |
| private static final int TOKEN_EC_POS = 4; |
| private static final int TOKEN_RRC_POS = 6; |
| |
| // token header size |
| static final int TOKEN_HEADER_SIZE = 16; |
| |
| private int tokenId = 0; |
| private int seqNumber; |
| |
| // EC and RRC fields |
| private int ec = 0; |
| private int rrc = 0; |
| |
| private boolean confState = true; |
| private boolean initiator = true; |
| |
| byte[] confounder = null; |
| byte[] checksum = null; |
| |
| private int key_usage = 0; |
| private byte[] seqNumberData = null; |
| |
| private MessageTokenHeader tokenHeader = null; |
| |
| /* cipher instance used by the corresponding GSSContext */ |
| CipherHelper cipherHelper = null; |
| |
| // draft-ietf-krb-wg-gssapi-cfx-07 |
| 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; |
| |
| // draft-ietf-krb-wg-gssapi-cfx-07 |
| private static final int FLAG_SENDER_IS_ACCEPTOR = 1; |
| private static final int FLAG_WRAP_CONFIDENTIAL = 2; |
| private static final int FLAG_ACCEPTOR_SUBKEY = 4; |
| private static final int FILLER = 0xff; |
| |
| /** |
| * Constructs a MessageToken from a byte array. If there are more bytes |
| * in the array than needed, the extra bytes are simply ignroed. |
| * |
| * @param tokenId the token id that should be contained in this token as |
| * it is read. |
| * @param context the Kerberos context associated with this token |
| * @param tokenBytes the byte array containing the token |
| * @param tokenOffset the offset where the token begins |
| * @param tokenLen the length of the token |
| * @param prop the MessageProp structure in which the properties of the |
| * token should be stored. |
| * @throws GSSException if there is a problem parsing the token |
| */ |
| MessageToken_v2(int tokenId, Krb5Context context, |
| byte[] tokenBytes, int tokenOffset, int tokenLen, |
| MessageProp prop) throws GSSException { |
| this(tokenId, context, |
| new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen), |
| prop); |
| } |
| |
| /** |
| * Constructs a MessageToken from an InputStream. Bytes will be read on |
| * demand and the thread might block if there are not enough bytes to |
| * complete the token. |
| * |
| * @param tokenId the token id that should be contained in this token as |
| * it is read. |
| * @param context the Kerberos context associated with this token |
| * @param is the InputStream from which to read |
| * @param prop the MessageProp structure in which the properties of the |
| * token should be stored. |
| * @throws GSSException if there is a problem reading from the |
| * InputStream or parsing the token |
| */ |
| MessageToken_v2(int tokenId, Krb5Context context, InputStream is, |
| MessageProp prop) throws GSSException { |
| init(tokenId, context); |
| |
| try { |
| if (!confState) { |
| prop.setPrivacy(false); |
| } |
| tokenHeader = new MessageTokenHeader(is, prop, tokenId); |
| |
| // set key_usage |
| if (tokenId == Krb5Token.WRAP_ID_v2) { |
| key_usage = (!initiator ? KG_USAGE_INITIATOR_SEAL |
| : KG_USAGE_ACCEPTOR_SEAL); |
| } else if (tokenId == Krb5Token.MIC_ID_v2) { |
| key_usage = (!initiator ? KG_USAGE_INITIATOR_SIGN |
| : KG_USAGE_ACCEPTOR_SIGN); |
| } |
| |
| // Read checksum |
| int tokenLen = is.available(); |
| byte[] data = new byte[tokenLen]; |
| readFully(is, data); |
| checksum = new byte[cipherHelper.getChecksumLength()]; |
| System.arraycopy(data, tokenLen-cipherHelper.getChecksumLength(), |
| checksum, 0, cipherHelper.getChecksumLength()); |
| // debug("\nLeaving MessageToken.Cons\n"); |
| |
| // validate EC for Wrap tokens without confidentiality |
| if (!prop.getPrivacy() && |
| (tokenId == Krb5Token.WRAP_ID_v2)) { |
| if (checksum.length != ec) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, |
| getTokenName(tokenId) + ":" + "EC incorrect!"); |
| } |
| } |
| |
| |
| } catch (IOException e) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, |
| getTokenName(tokenId) + ":" + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Used to obtain the token id that was contained in this token. |
| * @return the token id in the token |
| */ |
| public final int getTokenId() { |
| return tokenId; |
| } |
| |
| /** |
| * Used to obtain the key_usage type for this token. |
| * @return the key_usage for the token |
| */ |
| public final int getKeyUsage() { |
| return key_usage; |
| } |
| |
| /** |
| * Used to determine if this token contains any encrypted data. |
| * @return true if it contains any encrypted data, false if there is only |
| * plaintext data or if there is no data. |
| */ |
| public final boolean getConfState() { |
| return confState; |
| } |
| |
| /** |
| * Generates the checksum field and the sequence number field. |
| * |
| * @param prop the MessageProp structure |
| * @param data the application data to checksum |
| * @param offset the offset where the data starts |
| * @param len the length of the data |
| * |
| * @throws GSSException if an error occurs in the checksum calculation or |
| * sequence number calculation. |
| */ |
| public void genSignAndSeqNumber(MessageProp prop, |
| byte[] data, int offset, int len) |
| throws GSSException { |
| |
| // debug("Inside MessageToken.genSignAndSeqNumber:\n"); |
| |
| int qop = prop.getQOP(); |
| if (qop != 0) { |
| qop = 0; |
| prop.setQOP(qop); |
| } |
| |
| if (!confState) { |
| prop.setPrivacy(false); |
| } |
| |
| // Create a new gss token header as defined in |
| // draft-ietf-krb-wg-gssapi-cfx-07 |
| tokenHeader = new MessageTokenHeader(tokenId, |
| prop.getPrivacy(), true); |
| // debug("\n\t Message Header = " + |
| // getHexBytes(tokenHeader.getBytes(), tokenHeader.getBytes().length)); |
| |
| // set key_usage |
| if (tokenId == Krb5Token.WRAP_ID_v2) { |
| key_usage = (initiator ? KG_USAGE_INITIATOR_SEAL |
| : KG_USAGE_ACCEPTOR_SEAL); |
| } else if (tokenId == Krb5Token.MIC_ID_v2) { |
| key_usage = (initiator ? KG_USAGE_INITIATOR_SIGN |
| : KG_USAGE_ACCEPTOR_SIGN); |
| } |
| |
| // Calculate SGN_CKSUM |
| if ((tokenId == MIC_ID_v2) || |
| (!prop.getPrivacy() && (tokenId == WRAP_ID_v2))) { |
| checksum = getChecksum(data, offset, len); |
| // debug("\n\tCalc checksum=" + |
| // getHexBytes(checksum, checksum.length)); |
| } |
| |
| // In Wrap tokens without confidentiality, the EC field SHALL be used |
| // to encode the number of octets in the trailing checksum |
| if (!prop.getPrivacy() && (tokenId == WRAP_ID_v2)) { |
| byte[] tok_header = tokenHeader.getBytes(); |
| tok_header[4] = (byte) (checksum.length >>> 8); |
| tok_header[5] = (byte) (checksum.length); |
| } |
| } |
| |
| /** |
| * Verifies the validity of checksum field |
| * |
| * @param data the application data |
| * @param offset the offset where the data begins |
| * @param len the length of the application data |
| * |
| * @throws GSSException if an error occurs in the checksum calculation |
| */ |
| public final boolean verifySign(byte[] data, int offset, int len) |
| throws GSSException { |
| |
| // debug("\t====In verifySign:====\n"); |
| // debug("\t\t checksum: [" + getHexBytes(checksum) + "]\n"); |
| // debug("\t\t data = [" + getHexBytes(data) + "]\n"); |
| |
| byte[] myChecksum = getChecksum(data, offset, len); |
| // debug("\t\t mychecksum: [" + getHexBytes(myChecksum) +"]\n"); |
| |
| if (MessageDigest.isEqual(checksum, myChecksum)) { |
| // debug("\t\t====Checksum PASS:====\n"); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Rotate bytes as per the "RRC" (Right Rotation Count) received. |
| * Our implementation does not do any rotates when sending, only |
| * when receiving, we rotate left as per the RRC count, to revert it. |
| * |
| * @return true if bytes are rotated |
| */ |
| public boolean rotate_left(byte[] in_bytes, int tokenOffset, |
| byte[] out_bytes, int bufsize) { |
| |
| int offset = 0; |
| // debug("\nRotate left: (before rotation) in_bytes = [ " + |
| // getHexBytes(in_bytes, tokenOffset, bufsize) + "]"); |
| if (rrc > 0) { |
| if (bufsize == 0) { |
| return false; |
| } |
| rrc = rrc % (bufsize - TOKEN_HEADER_SIZE); |
| if (rrc == 0) { |
| return false; |
| } |
| |
| // if offset is not zero |
| if (tokenOffset > 0) { |
| offset += tokenOffset; |
| } |
| |
| // copy the header |
| System.arraycopy(in_bytes, offset, out_bytes, 0, TOKEN_HEADER_SIZE); |
| offset += TOKEN_HEADER_SIZE; |
| |
| // copy rest of the bytes |
| System.arraycopy(in_bytes, offset+rrc, out_bytes, |
| TOKEN_HEADER_SIZE, bufsize-TOKEN_HEADER_SIZE-rrc); |
| |
| // copy the bytes specified by rrc count |
| System.arraycopy(in_bytes, offset, out_bytes, |
| bufsize-TOKEN_HEADER_SIZE-rrc, rrc); |
| |
| // debug("\nRotate left: (after rotation) out_bytes = [ " + |
| // getHexBytes(out_bytes, 0, bufsize) + "]"); |
| return true; |
| } |
| return false; |
| } |
| |
| public final int getSequenceNumber() { |
| return (readBigEndian(seqNumberData, 0, 4)); |
| } |
| |
| /** |
| * Computes the checksum based on the algorithm stored in the |
| * tokenHeader. |
| * |
| * @param data the application data |
| * @param offset the offset where the data begins |
| * @param len the length of the application data |
| * |
| * @throws GSSException if an error occurs in the checksum calculation. |
| */ |
| byte[] getChecksum(byte[] data, int offset, int len) |
| throws GSSException { |
| |
| // debug("Will do getChecksum:\n"); |
| |
| /* |
| * For checksum calculation the token header bytes i.e., the first 16 |
| * bytes following the GSSHeader, are logically prepended to the |
| * application data to bind the data to this particular token. |
| * |
| * Note: There is no such requirement wrt adding padding to the |
| * application data for checksumming, although the cryptographic |
| * algorithm used might itself apply some padding. |
| */ |
| |
| byte[] tokenHeaderBytes = tokenHeader.getBytes(); |
| |
| // check confidentiality |
| int conf_flag = tokenHeaderBytes[TOKEN_FLAG_POS] & |
| FLAG_WRAP_CONFIDENTIAL; |
| |
| // clear EC in token header for checksum calculation |
| if ((conf_flag == 0) && (tokenId == WRAP_ID_v2)) { |
| tokenHeaderBytes[4] = 0; |
| tokenHeaderBytes[5] = 0; |
| } |
| return cipherHelper.calculateChecksum(tokenHeaderBytes, data, |
| offset, len, key_usage); |
| } |
| |
| |
| /** |
| * Constructs an empty MessageToken for the local context to send to |
| * the peer. It also increments the local sequence number in the |
| * Krb5Context instance it uses after obtaining the object lock for |
| * it. |
| * |
| * @param tokenId the token id that should be contained in this token |
| * @param context the Kerberos context associated with this token |
| */ |
| MessageToken_v2(int tokenId, Krb5Context context) throws GSSException { |
| /* |
| debug("\n============================"); |
| debug("\nMySessionKey=" + |
| getHexBytes(context.getMySessionKey().getBytes())); |
| debug("\nPeerSessionKey=" + |
| getHexBytes(context.getPeerSessionKey().getBytes())); |
| debug("\n============================\n"); |
| */ |
| init(tokenId, context); |
| this.seqNumber = context.incrementMySequenceNumber(); |
| } |
| |
| private void init(int tokenId, Krb5Context context) throws GSSException { |
| this.tokenId = tokenId; |
| // Just for consistency check in Wrap |
| this.confState = context.getConfState(); |
| |
| this.initiator = context.isInitiator(); |
| |
| this.cipherHelper = context.getCipherHelper(null); |
| // debug("In MessageToken.Cons"); |
| |
| // draft-ietf-krb-wg-gssapi-cfx-07 |
| this.tokenId = tokenId; |
| } |
| |
| /** |
| * Encodes a GSSHeader and this token onto an OutputStream. |
| * |
| * @param os the OutputStream to which this should be written |
| * @throws GSSException if an error occurs while writing to the OutputStream |
| */ |
| public void encode(OutputStream os) throws IOException, GSSException { |
| // debug("Writing tokenHeader " + getHexBytes(tokenHeader.getBytes()); |
| // (16 bytes of token header that includes sequence Number) |
| tokenHeader.encode(os); |
| // debug("Writing checksum: " + getHexBytes(checksum)); |
| if (tokenId == MIC_ID_v2) { |
| os.write(checksum); |
| } |
| } |
| |
| /** |
| * Obtains the size of this token. Note that this excludes the size of |
| * the GSSHeader. |
| * @return token size |
| */ |
| protected int getKrb5TokenSize() throws GSSException { |
| return getTokenSize(); |
| } |
| |
| protected final int getTokenSize() throws GSSException { |
| return (TOKEN_HEADER_SIZE + cipherHelper.getChecksumLength()); |
| } |
| |
| protected static final int getTokenSize(CipherHelper ch) |
| throws GSSException { |
| return (TOKEN_HEADER_SIZE + ch.getChecksumLength()); |
| } |
| |
| protected final byte[] getTokenHeader() { |
| return (tokenHeader.getBytes()); |
| } |
| |
| // ******************************************* // |
| // I N N E R C L A S S E S F O L L O W |
| // ******************************************* // |
| |
| /** |
| * This inner class represents the initial portion of the message token. |
| * It constitutes the first 16 bytes of the message token: |
| * <pre> |
| * Wrap Tokens |
| * |
| * Octet no Name Description |
| * --------------------------------------------------------------- |
| * 0..1 TOK_ID Identification field. Tokens emitted by |
| * GSS_Wrap() contain the the hex value 05 04 |
| * expressed in big endian order in this field. |
| * 2 Flags Attributes field, as described in section |
| * 4.2.2. |
| * 3 Filler Contains the hex value FF. |
| * 4..5 EC Contains the "extra count" field, in big |
| * endian order as described in section 4.2.3. |
| * 6..7 RRC Contains the "right rotation count" in big |
| * endian order, as described in section 4.2.5. |
| * 8..15 SND_SEQ Sequence number field in clear text, |
| * expressed in big endian order. |
| * |
| * MIC Tokens |
| * |
| * Octet no Name Description |
| * ----------------------------------------------------------------- |
| * 0..1 TOK_ID Identification field. Tokens emitted by |
| * GSS_GetMIC() contain the hex value 04 04 |
| * expressed in big endian order in this field. |
| * 2 Flags Attributes field, as described in section |
| * 4.2.2. |
| * 3..7 Filler Contains five octets of hex value FF. |
| * 8..15 SND_SEQ Sequence number field in clear text, |
| * expressed in big endian order. |
| * </pre> |
| */ |
| class MessageTokenHeader { |
| |
| private int tokenId; |
| private byte[] bytes = new byte[TOKEN_HEADER_SIZE]; |
| |
| // new token header draft-ietf-krb-wg-gssapi-cfx-07 |
| public MessageTokenHeader(int tokenId, boolean conf, |
| boolean have_acceptor_subkey) throws GSSException { |
| |
| this.tokenId = tokenId; |
| |
| bytes[0] = (byte) (tokenId >>> 8); |
| bytes[1] = (byte) (tokenId); |
| |
| // Flags (Note: MIT impl requires subkey) |
| int flags = 0; |
| flags = ((initiator ? 0 : FLAG_SENDER_IS_ACCEPTOR) | |
| ((conf && tokenId != MIC_ID_v2) ? |
| FLAG_WRAP_CONFIDENTIAL : 0) | |
| (have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); |
| bytes[2] = (byte) flags; |
| |
| // filler |
| bytes[3] = (byte) FILLER; |
| |
| // EC and RRC fields |
| if (tokenId == WRAP_ID_v2) { |
| // EC field |
| bytes[4] = (byte) 0; |
| bytes[5] = (byte) 0; |
| // RRC field |
| bytes[6] = (byte) 0; |
| bytes[7] = (byte) 0; |
| } else if (tokenId == MIC_ID_v2) { |
| // octets of filler FF |
| for (int i = 4; i < 8; i++) { |
| bytes[i] = (byte) FILLER; |
| } |
| } |
| |
| // Calculate SND_SEQ |
| seqNumberData = new byte[8]; |
| writeBigEndian(seqNumber, seqNumberData, 4); |
| System.arraycopy(seqNumberData, 0, bytes, 8, 8); |
| } |
| |
| /** |
| * Constructs a MessageTokenHeader by reading it from an InputStream |
| * and sets the appropriate confidentiality and quality of protection |
| * values in a MessageProp structure. |
| * |
| * @param is the InputStream to read from |
| * @param prop the MessageProp to populate |
| * @throws IOException is an error occurs while reading from the |
| * InputStream |
| */ |
| public MessageTokenHeader(InputStream is, MessageProp prop, int tokId) |
| throws IOException, GSSException { |
| |
| readFully(is, bytes, 0, TOKEN_HEADER_SIZE); |
| tokenId = readInt(bytes, TOKEN_ID_POS); |
| |
| /* |
| * Validate new GSS TokenHeader |
| */ |
| // valid acceptor_flag is set |
| int acceptor_flag = (initiator ? FLAG_SENDER_IS_ACCEPTOR : 0); |
| int flag = bytes[TOKEN_FLAG_POS] & FLAG_SENDER_IS_ACCEPTOR; |
| if (!(flag == acceptor_flag)) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, |
| getTokenName(tokenId) + ":" + "Acceptor Flag Missing!"); |
| } |
| |
| // check for confidentiality |
| int conf_flag = bytes[TOKEN_FLAG_POS] & FLAG_WRAP_CONFIDENTIAL; |
| if ((conf_flag == FLAG_WRAP_CONFIDENTIAL) && |
| (tokenId == WRAP_ID_v2)) { |
| prop.setPrivacy(true); |
| } else { |
| prop.setPrivacy(false); |
| } |
| |
| // validate Token ID |
| if (tokenId != tokId) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, |
| getTokenName(tokenId) + ":" + "Defective Token ID!"); |
| } |
| |
| // validate filler |
| if ((bytes[3] & 0xff) != FILLER) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1, |
| getTokenName(tokenId) + ":" + "Defective Token Filler!"); |
| } |
| |
| // validate next 4 bytes of filler for MIC tokens |
| if (tokenId == MIC_ID_v2) { |
| for (int i = 4; i < 8; i++) { |
| if ((bytes[i] & 0xff) != FILLER) { |
| throw new GSSException(GSSException.DEFECTIVE_TOKEN, |
| -1, getTokenName(tokenId) + ":" + |
| "Defective Token Filler!"); |
| } |
| } |
| } |
| |
| // read EC field |
| ec = readBigEndian(bytes, TOKEN_EC_POS, 2); |
| |
| // read RRC field |
| rrc = readBigEndian(bytes, TOKEN_RRC_POS, 2); |
| |
| // set default QOP |
| prop.setQOP(0); |
| |
| // sequence number |
| seqNumberData = new byte[8]; |
| System.arraycopy(bytes, 8, seqNumberData, 0, 8); |
| } |
| |
| /** |
| * Encodes this MessageTokenHeader onto an OutputStream |
| * @param os the OutputStream to write to |
| * @throws IOException is an error occurs while writing |
| */ |
| public final void encode(OutputStream os) throws IOException { |
| os.write(bytes); |
| } |
| |
| |
| /** |
| * Returns the token id for the message token. |
| * @return the token id |
| * @see sun.security.jgss.krb5.Krb5Token#MIC_ID_v2 |
| * @see sun.security.jgss.krb5.Krb5Token#WRAP_ID_v2 |
| */ |
| public final int getTokenId() { |
| return tokenId; |
| } |
| |
| /** |
| * Returns the bytes of this header. |
| * @return 8 bytes that form this header |
| */ |
| public final byte[] getBytes() { |
| return bytes; |
| } |
| } // end of class MessageTokenHeader |
| } |