1 /*
   2  * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.security.jgss.krb5;
  27 
  28 import org.ietf.jgss.*;
  29 import sun.security.jgss.*;
  30 import sun.security.krb5.*;
  31 import java.io.InputStream;
  32 import java.io.OutputStream;
  33 import java.io.IOException;
  34 import java.io.ByteArrayInputStream;
  35 import java.security.GeneralSecurityException;
  36 import java.security.MessageDigest;
  37 
  38 /**
  39  * This class is a base class for other token definitions that pertain to
  40  * per-message GSS-API calls. Conceptually GSS-API has two types of
  41  * per-message tokens: WrapToken and MicToken. They differ in the respect
  42  * that a WrapToken carries additional plaintext or ciphertext application
  43  * data besides just the sequence number and checksum. This class
  44  * encapsulates the commonality in the structure of the WrapToken and the
  45  * MicToken. This structure can be represented as:
  46  * <p>
  47  * <pre>
  48  *     0..1           TOK_ID          Identification field.
  49  *                                    01 01 - Mic token
  50  *                                    02 01 - Wrap token
  51  *     2..3           SGN_ALG         Checksum algorithm indicator.
  52  *                                    00 00 - DES MAC MD5
  53  *                                    01 00 - MD2.5
  54  *                                    02 00 - DES MAC
  55  *                                    04 00 - HMAC SHA1 DES3-KD
  56  *                                    11 00 - RC4-HMAC
  57  *     4..5           SEAL_ALG        ff ff - none
  58  *                                    00 00 - DES
  59  *                                    02 00 - DES3-KD
  60  *                                    10 00 - RC4-HMAC
  61  *     6..7           Filler          Contains ff ff
  62  *     8..15          SND_SEQ         Encrypted sequence number field.
  63  *     16..s+15       SGN_CKSUM       Checksum of plaintext padded data,
  64  *                                   calculated according to algorithm
  65  *                                  specified in SGN_ALG field.
  66  *     s+16..last     Data            encrypted or plaintext padded data
  67  * </pre>
  68  * Where "s" indicates the size of the checksum.
  69  * <p>
  70  * As always, this is preceeded by a GSSHeader.
  71  *
  72  * @author Mayank Upadhyay
  73  * @author Ram Marti
  74  * @see sun.security.jgss.GSSHeader
  75  */
  76 
  77 abstract class MessageToken extends Krb5Token {
  78     /* Fields in header minus checksum size */
  79     private static final int TOKEN_NO_CKSUM_SIZE = 16;
  80 
  81     /**
  82      * Filler data as defined in the specification of the Kerberos v5 GSS-API
  83      * Mechanism.
  84      */
  85     private static final int FILLER = 0xffff;
  86 
  87      // Signing algorithm values (for the SNG_ALG field)
  88 
  89      // From RFC 1964
  90      /* Use a DES MAC MD5 checksum */
  91     static final int SGN_ALG_DES_MAC_MD5 = 0x0000;
  92 
  93      /* Use DES MAC checksum. */
  94     static final int SGN_ALG_DES_MAC     = 0x0200;
  95 
  96      // From draft-raeburn-cat-gssapi-krb5-3des-00
  97      /* Use a HMAC SHA1 DES3 -KD checksum */
  98     static final int SGN_ALG_HMAC_SHA1_DES3_KD = 0x0400;
  99 
 100      // Sealing algorithm values (for the SEAL_ALG field)
 101 
 102      // RFC 1964
 103     /**
 104      * A value for the SEAL_ALG field that indicates that no encryption was
 105      * used.
 106      */
 107     static final int SEAL_ALG_NONE    = 0xffff;
 108      /* Use DES CBC encryption algorithm. */
 109     static final int SEAL_ALG_DES = 0x0000;
 110 
 111     // From draft-raeburn-cat-gssapi-krb5-3des-00
 112     /**
 113      * Use DES3-KD sealing algorithm. (draft-raeburn-cat-gssapi-krb5-3des-00)
 114      * This algorithm uses triple-DES with key derivation, with a usage
 115      * value KG_USAGE_SEAL.  Padding is still to 8-byte multiples, and the
 116      * IV for encrypting application data is zero.
 117      */
 118     static final int SEAL_ALG_DES3_KD = 0x0200;
 119 
 120     // draft draft-brezak-win2k-krb-rc4-hmac-04.txt
 121     static final int SEAL_ALG_ARCFOUR_HMAC = 0x1000;
 122     static final int SGN_ALG_HMAC_MD5_ARCFOUR = 0x1100;
 123 
 124     private static final int TOKEN_ID_POS = 0;
 125     private static final int SIGN_ALG_POS = 2;
 126     private static final int SEAL_ALG_POS = 4;
 127 
 128     private int seqNumber;
 129 
 130     private boolean confState = true;
 131     private boolean initiator = true;
 132 
 133     private int tokenId = 0;
 134     private GSSHeader gssHeader = null;
 135     private MessageTokenHeader tokenHeader = null;
 136     private byte[] checksum = null;
 137     private byte[] encSeqNumber = null;
 138     private byte[] seqNumberData = null;
 139 
 140     /* cipher instance used by the corresponding GSSContext */
 141     CipherHelper cipherHelper = null;
 142 
 143 
 144     /**
 145      * Constructs a MessageToken from a byte array. If there are more bytes
 146      * in the array than needed, the extra bytes are simply ignroed.
 147      *
 148      * @param tokenId the token id that should be contained in this token as
 149      * it is read.
 150      * @param context the Kerberos context associated with this token
 151      * @param tokenBytes the byte array containing the token
 152      * @param tokenOffset the offset where the token begins
 153      * @param tokenLen the length of the token
 154      * @param prop the MessageProp structure in which the properties of the
 155      * token should be stored.
 156      * @throws GSSException if there is a problem parsing the token
 157      */
 158     MessageToken(int tokenId, Krb5Context context,
 159                  byte[] tokenBytes, int tokenOffset, int tokenLen,
 160                  MessageProp prop) throws GSSException {
 161         this(tokenId, context,
 162              new ByteArrayInputStream(tokenBytes, tokenOffset, tokenLen),
 163              prop);
 164     }
 165 
 166     /**
 167      * Constructs a MessageToken from an InputStream. Bytes will be read on
 168      * demand and the thread might block if there are not enough bytes to
 169      * complete the token.
 170      *
 171      * @param tokenId the token id that should be contained in this token as
 172      * it is read.
 173      * @param context the Kerberos context associated with this token
 174      * @param is the InputStream from which to read
 175      * @param prop the MessageProp structure in which the properties of the
 176      * token should be stored.
 177      * @throws GSSException if there is a problem reading from the
 178      * InputStream or parsing the token
 179      */
 180     MessageToken(int tokenId, Krb5Context context, InputStream is,
 181                  MessageProp prop) throws GSSException {
 182         init(tokenId, context);
 183 
 184         try {
 185             gssHeader = new GSSHeader(is);
 186 
 187             if (!gssHeader.getOid().equals(OID)) {
 188                 throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
 189                                        getTokenName(tokenId));
 190             }
 191             if (!confState) {
 192                 prop.setPrivacy(false);
 193             }
 194 
 195             tokenHeader = new MessageTokenHeader(is, prop);
 196 
 197             encSeqNumber = new byte[8];
 198             readFully(is, encSeqNumber);
 199 
 200             // debug("\n\tRead EncSeq#=" +
 201             // getHexBytes(encSeqNumber, encSeqNumber.length));
 202 
 203             checksum = new byte[cipherHelper.getChecksumLength()];
 204             readFully(is, checksum);
 205 
 206             // debug("\n\tRead checksum=" +
 207             // getHexBytes(checksum, checksum.length));
 208             // debug("\nLeaving MessageToken.Cons\n");
 209 
 210         } catch (IOException e) {
 211             throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
 212                 getTokenName(tokenId) + ":" + e.getMessage());
 213         }
 214     }
 215 
 216     /**
 217      * Used to obtain the GSSHeader that was at the start of this
 218      * token.
 219      */
 220     public final GSSHeader getGSSHeader() {
 221         return gssHeader;
 222     }
 223 
 224     /**
 225      * Used to obtain the token id that was contained in this token.
 226      * @return the token id in the token
 227      */
 228     public final int getTokenId() {
 229         return tokenId;
 230     }
 231 
 232     /**
 233      * Used to obtain the encrypted sequence number in this token.
 234      * @return the encrypted sequence number in the token
 235      */
 236     public final byte[] getEncSeqNumber() {
 237         return encSeqNumber;
 238     }
 239 
 240     /**
 241      * Used to obtain the checksum that was contained in this token.
 242      * @return the checksum in the token
 243      */
 244     public final byte[] getChecksum() {
 245         return checksum;
 246     }
 247 
 248     /**
 249      * Used to determine if this token contains any encrypted data.
 250      * @return true if it contains any encrypted data, false if there is only
 251      * plaintext data or if there is no data.
 252      */
 253     public final boolean getConfState() {
 254         return confState;
 255     }
 256 
 257     /**
 258      * Generates the checksum field and the encrypted sequence number
 259      * field. The encrypted sequence number uses the 8 bytes of the checksum
 260      * as an initial vector in a fixed DesCbc algorithm.
 261      *
 262      * @param prop the MessageProp structure that determines what sort of
 263      * checksum and sealing algorithm should be used. The lower byte
 264      * of qop determines the checksum algorithm while the upper byte
 265      * determines the signing algorithm.
 266      *       Checksum values are:
 267      *           0 - default (DES_MAC)
 268      *           1 - MD5
 269      *           2 - DES_MD5
 270      *           3 - DES_MAC
 271      *           4 - HMAC_SHA1
 272      *       Sealing values are:
 273      *           0 - default (DES)
 274      *           1 - DES
 275      *           2 - DES3-KD
 276      *
 277      * @param optionalHeader an optional header that will be processed first
 278      * during  checksum calculation
 279      *
 280      * @param data the application data to checksum
 281      * @param offset the offset where the data starts
 282      * @param len the length of the data
 283      *
 284      * @param optionalTrailer an optional trailer that will be processed
 285      * last during checksum calculation. e.g., padding that should be
 286      * appended to the application data
 287      *
 288      * @throws GSSException if an error occurs in the checksum calculation or
 289      * encryption sequence number calculation.
 290      */
 291     public void genSignAndSeqNumber(MessageProp prop,
 292                                     byte[] optionalHeader,
 293                                     byte[] data, int offset, int len,
 294                                     byte[] optionalTrailer)
 295         throws GSSException {
 296 
 297         //    debug("Inside MessageToken.genSignAndSeqNumber:\n");
 298 
 299         int qop = prop.getQOP();
 300         if (qop != 0) {
 301             qop = 0;
 302             prop.setQOP(qop);
 303         }
 304 
 305         if (!confState) {
 306             prop.setPrivacy(false);
 307         }
 308 
 309         // Create a token header with the correct sign and seal algorithm
 310         // values.
 311         tokenHeader =
 312             new MessageTokenHeader(tokenId, prop.getPrivacy(), qop);
 313 
 314         // Calculate SGN_CKSUM
 315 
 316         checksum =
 317             getChecksum(optionalHeader, data, offset, len, optionalTrailer);
 318 
 319         // debug("\n\tCalc checksum=" +
 320         // getHexBytes(checksum, checksum.length));
 321 
 322         // Calculate SND_SEQ
 323 
 324         seqNumberData = new byte[8];
 325 
 326         // When using this RC4 based encryption type, the sequence number is
 327         // always sent in big-endian rather than little-endian order.
 328         if (cipherHelper.isArcFour()) {
 329             writeBigEndian(seqNumber, seqNumberData);
 330         } else {
 331             // for all other etypes
 332             writeLittleEndian(seqNumber, seqNumberData);
 333         }
 334         if (!initiator) {
 335             seqNumberData[4] = (byte)0xff;
 336             seqNumberData[5] = (byte)0xff;
 337             seqNumberData[6] = (byte)0xff;
 338             seqNumberData[7] = (byte)0xff;
 339         }
 340 
 341         encSeqNumber = cipherHelper.encryptSeq(checksum, seqNumberData, 0, 8);
 342 
 343         // debug("\n\tCalc seqNum=" +
 344         //    getHexBytes(seqNumberData, seqNumberData.length));
 345         // debug("\n\tCalc encSeqNum=" +
 346         //    getHexBytes(encSeqNumber, encSeqNumber.length));
 347     }
 348 
 349     /**
 350      * Verifies that the checksum field and sequence number direction bytes
 351      * are valid and consistent with the application data.
 352      *
 353      * @param optionalHeader an optional header that will be processed first
 354      * during checksum calculation.
 355      *
 356      * @param data the application data
 357      * @param offset the offset where the data begins
 358      * @param len the length of the application data
 359      *
 360      * @param optionalTrailer an optional trailer that will be processed last
 361      * during checksum calculation. e.g., padding that should be appended to
 362      * the application data
 363      *
 364      * @throws GSSException if an error occurs in the checksum calculation or
 365      * encryption sequence number calculation.
 366      */
 367     public final boolean verifySignAndSeqNumber(byte[] optionalHeader,
 368                                         byte[] data, int offset, int len,
 369                                         byte[] optionalTrailer)
 370         throws GSSException {
 371          // debug("\tIn verifySign:\n");
 372 
 373          // debug("\t\tchecksum:   [" + getHexBytes(checksum) + "]\n");
 374 
 375         byte[] myChecksum =
 376             getChecksum(optionalHeader, data, offset, len, optionalTrailer);
 377 
 378         // debug("\t\tmychecksum: [" + getHexBytes(myChecksum) +"]\n");
 379         // debug("\t\tchecksum:   [" + getHexBytes(checksum) + "]\n");
 380 
 381         if (MessageDigest.isEqual(checksum, myChecksum)) {
 382 
 383             seqNumberData = cipherHelper.decryptSeq(
 384                 checksum, encSeqNumber, 0, 8);
 385 
 386             // debug("\t\tencSeqNumber:   [" + getHexBytes(encSeqNumber)
 387             //  + "]\n");
 388             // debug("\t\tseqNumberData:   [" + getHexBytes(seqNumberData)
 389             //  + "]\n");
 390 
 391             /*
 392              * The token from the initiator has direction bytes 0x00 and
 393              * the token from the acceptor has direction bytes 0xff.
 394              */
 395             byte directionByte = 0;
 396             if (initiator)
 397                 directionByte = (byte) 0xff; // Received token from acceptor
 398 
 399             if ((seqNumberData[4] == directionByte) &&
 400                   (seqNumberData[5] == directionByte) &&
 401                   (seqNumberData[6] == directionByte) &&
 402                   (seqNumberData[7] == directionByte))
 403                 return true;
 404         }
 405 
 406         return false;
 407 
 408     }
 409 
 410     public final int getSequenceNumber() {
 411         int sequenceNum = 0;
 412         if (cipherHelper.isArcFour()) {
 413             sequenceNum = readBigEndian(seqNumberData, 0, 4);
 414         } else {
 415             sequenceNum = readLittleEndian(seqNumberData, 0, 4);
 416         }
 417         return sequenceNum;
 418     }
 419 
 420     /**
 421      * Computes the checksum based on the algorithm stored in the
 422      * tokenHeader.
 423      *
 424      * @param optionalHeader an optional header that will be processed first
 425      * during checksum calculation.
 426      *
 427      * @param data the application data
 428      * @param offset the offset where the data begins
 429      * @param len the length of the application data
 430      *
 431      * @param optionalTrailer an optional trailer that will be processed last
 432      * during checksum calculation. e.g., padding that should be appended to
 433      * the application data
 434      *
 435      * @throws GSSException if an error occurs in the checksum calculation.
 436      */
 437     private byte[] getChecksum(byte[] optionalHeader,
 438                                byte[] data, int offset, int len,
 439                                byte[] optionalTrailer)
 440         throws GSSException {
 441 
 442         //      debug("Will do getChecksum:\n");
 443 
 444         /*
 445          * For checksum calculation the token header bytes i.e., the first 8
 446          * bytes following the GSSHeader, are logically prepended to the
 447          * application data to bind the data to this particular token.
 448          *
 449          * Note: There is no such requirement wrt adding padding to the
 450          * application data for checksumming, although the cryptographic
 451          * algorithm used might itself apply some padding.
 452          */
 453 
 454         byte[] tokenHeaderBytes = tokenHeader.getBytes();
 455         byte[] existingHeader = optionalHeader;
 456         byte[] checksumDataHeader = tokenHeaderBytes;
 457 
 458         if (existingHeader != null) {
 459             checksumDataHeader = new byte[tokenHeaderBytes.length +
 460                                          existingHeader.length];
 461             System.arraycopy(tokenHeaderBytes, 0,
 462                              checksumDataHeader, 0, tokenHeaderBytes.length);
 463             System.arraycopy(existingHeader, 0,
 464                              checksumDataHeader, tokenHeaderBytes.length,
 465                              existingHeader.length);
 466         }
 467 
 468         return cipherHelper.calculateChecksum(tokenHeader.getSignAlg(),
 469              checksumDataHeader, optionalTrailer, data, offset, len, tokenId);
 470     }
 471 
 472 
 473     /**
 474      * Constructs an empty MessageToken for the local context to send to
 475      * the peer. It also increments the local sequence number in the
 476      * Krb5Context instance it uses after obtaining the object lock for
 477      * it.
 478      *
 479      * @param tokenId the token id that should be contained in this token
 480      * @param context the Kerberos context associated with this token
 481      */
 482     MessageToken(int tokenId, Krb5Context context) throws GSSException {
 483         /*
 484           debug("\n============================");
 485           debug("\nMySessionKey=" +
 486           getHexBytes(context.getMySessionKey().getBytes()));
 487           debug("\nPeerSessionKey=" +
 488           getHexBytes(context.getPeerSessionKey().getBytes()));
 489           debug("\n============================\n");
 490         */
 491         init(tokenId, context);
 492         this.seqNumber = context.incrementMySequenceNumber();
 493     }
 494 
 495     private void init(int tokenId, Krb5Context context) throws GSSException {
 496         this.tokenId = tokenId;
 497         // Just for consistency check in Wrap
 498         this.confState = context.getConfState();
 499 
 500         this.initiator = context.isInitiator();
 501 
 502         this.cipherHelper = context.getCipherHelper(null);
 503         //    debug("In MessageToken.Cons");
 504     }
 505 
 506     /**
 507      * Encodes a GSSHeader and this token onto an OutputStream.
 508      *
 509      * @param os the OutputStream to which this should be written
 510      * @throws GSSException if an error occurs while writing to the OutputStream
 511      */
 512     public void encode(OutputStream os) throws IOException, GSSException {
 513         gssHeader = new GSSHeader(OID, getKrb5TokenSize());
 514         gssHeader.encode(os);
 515         tokenHeader.encode(os);
 516         // debug("Writing seqNumber: " + getHexBytes(encSeqNumber));
 517         os.write(encSeqNumber);
 518         // debug("Writing checksum: " + getHexBytes(checksum));
 519         os.write(checksum);
 520     }
 521 
 522     /**
 523      * Obtains the size of this token. Note that this excludes the size of
 524      * the GSSHeader.
 525      * @return token size
 526      */
 527     protected int getKrb5TokenSize() throws GSSException {
 528         return getTokenSize();
 529     }
 530 
 531     protected final int getTokenSize() throws GSSException {
 532         return TOKEN_NO_CKSUM_SIZE + cipherHelper.getChecksumLength();
 533     }
 534 
 535     protected static final int getTokenSize(CipherHelper ch)
 536         throws GSSException {
 537          return TOKEN_NO_CKSUM_SIZE + ch.getChecksumLength();
 538     }
 539 
 540     /**
 541      * Obtains the conext key that is associated with this token.
 542      * @return the context key
 543      */
 544     /*
 545     public final byte[] getContextKey() {
 546         return contextKey;
 547     }
 548     */
 549 
 550     /**
 551      * Obtains the encryption algorithm that should be used in this token
 552      * given the state of confidentiality the application requested.
 553      * Requested qop must be consistent with negotiated session key.
 554      * @param confRequested true if the application desired confidentiality
 555      * on this token, false otherwise
 556      * @param qop the qop requested by the application
 557      * @throws GSSException if qop is incompatible with the negotiated
 558      *         session key
 559      */
 560     protected abstract int getSealAlg(boolean confRequested, int qop)
 561         throws GSSException;
 562 
 563     // ******************************************* //
 564     //  I N N E R    C L A S S E S    F O L L O W
 565     // ******************************************* //
 566 
 567     /**
 568      * This inner class represents the initial portion of the message token
 569      * and contains information about the checksum and encryption algorithms
 570      * that are in use. It constitutes the first 8 bytes of the
 571      * message token:
 572      * <pre>
 573      *     0..1           TOK_ID          Identification field.
 574      *                                    01 01 - Mic token
 575      *                                    02 01 - Wrap token
 576      *     2..3           SGN_ALG         Checksum algorithm indicator.
 577      *                                    00 00 - DES MAC MD5
 578      *                                    01 00 - MD2.5
 579      *                                    02 00 - DES MAC
 580      *                                    04 00 - HMAC SHA1 DES3-KD
 581      *                                    11 00 - RC4-HMAC
 582      *     4..5           SEAL_ALG        ff ff - none
 583      *                                    00 00 - DES
 584      *                                    02 00 - DES3-KD
 585      *                                    10 00 - RC4-HMAC
 586      *     6..7           Filler          Contains ff ff
 587      * </pre>
 588      */
 589     class MessageTokenHeader {
 590 
 591          private int tokenId;
 592          private int signAlg;
 593          private int sealAlg;
 594 
 595          private byte[] bytes = new byte[8];
 596 
 597         /**
 598          * Constructs a MessageTokenHeader for the specified token type with
 599          * appropriate checksum and encryption algorithms fields.
 600          *
 601          * @param tokenId the token id for this mesage token
 602          * @param conf true if confidentiality will be resuested with this
 603          * message token, false otherwise.
 604          * @param qop the value of the quality of protection that will be
 605          * desired.
 606          */
 607         public MessageTokenHeader(int tokenId, boolean conf, int qop)
 608          throws GSSException {
 609 
 610             this.tokenId = tokenId;
 611 
 612             signAlg = MessageToken.this.getSgnAlg(qop);
 613 
 614             sealAlg = MessageToken.this.getSealAlg(conf, qop);
 615 
 616             bytes[0] = (byte) (tokenId >>> 8);
 617             bytes[1] = (byte) (tokenId);
 618 
 619             bytes[2] = (byte) (signAlg >>> 8);
 620             bytes[3] = (byte) (signAlg);
 621 
 622             bytes[4] = (byte) (sealAlg >>> 8);
 623             bytes[5] = (byte) (sealAlg);
 624 
 625             bytes[6] = (byte) (MessageToken.FILLER >>> 8);
 626             bytes[7] = (byte) (MessageToken.FILLER);
 627         }
 628 
 629         /**
 630          * Constructs a MessageTokenHeader by reading it from an InputStream
 631          * and sets the appropriate confidentiality and quality of protection
 632          * values in a MessageProp structure.
 633          *
 634          * @param is the InputStream to read from
 635          * @param prop the MessageProp to populate
 636          * @throws IOException is an error occurs while reading from the
 637          * InputStream
 638          */
 639         public MessageTokenHeader(InputStream is, MessageProp prop)
 640             throws IOException {
 641             readFully(is, bytes);
 642             tokenId = readInt(bytes, TOKEN_ID_POS);
 643             signAlg = readInt(bytes, SIGN_ALG_POS);
 644             sealAlg = readInt(bytes, SEAL_ALG_POS);
 645             //          debug("\nMessageTokenHeader read tokenId=" +
 646             //                getHexBytes(bytes) + "\n");
 647             // XXX compare to FILLER
 648             int temp = readInt(bytes, SEAL_ALG_POS + 2);
 649 
 650             //              debug("SIGN_ALG=" + signAlg);
 651 
 652             switch (sealAlg) {
 653             case SEAL_ALG_DES:
 654             case SEAL_ALG_DES3_KD:
 655             case SEAL_ALG_ARCFOUR_HMAC:
 656                 prop.setPrivacy(true);
 657                 break;
 658 
 659             default:
 660                 prop.setPrivacy(false);
 661             }
 662 
 663             prop.setQOP(0);  // default
 664         }
 665 
 666         /**
 667          * Encodes this MessageTokenHeader onto an OutputStream
 668          * @param os the OutputStream to write to
 669          * @throws IOException is an error occurs while writing
 670          */
 671         public final void encode(OutputStream os) throws IOException {
 672             os.write(bytes);
 673         }
 674 
 675 
 676         /**
 677          * Returns the token id for the message token.
 678          * @return the token id
 679          * @see sun.security.jgss.krb5.Krb5Token#MIC_ID
 680          * @see sun.security.jgss.krb5.Krb5Token#WRAP_ID
 681          */
 682         public final int getTokenId() {
 683             return tokenId;
 684         }
 685 
 686         /**
 687          * Returns the sign algorithm for the message token.
 688          * @return the sign algorithm
 689          * @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC
 690          * @see sun.security.jgss.krb5.MessageToken#SIGN_DES_MAC_MD5
 691          */
 692         public final int getSignAlg() {
 693             return signAlg;
 694         }
 695 
 696         /**
 697          * Returns the seal algorithm for the message token.
 698          * @return the seal algorithm
 699          * @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_DES
 700          * @see sun.security.jgss.krb5.MessageToken#SEAL_ALG_NONE
 701          */
 702         public final int getSealAlg() {
 703             return sealAlg;
 704         }
 705 
 706         /**
 707          * Returns the bytes of this header.
 708          * @return 8 bytes that form this header
 709          */
 710         public final byte[] getBytes() {
 711             return bytes;
 712         }
 713     } // end of class MessageTokenHeader
 714 
 715 
 716     /**
 717      * Determine signing algorithm based on QOP.
 718      */
 719     protected int getSgnAlg(int qop) throws GSSException {
 720          // QOP ignored
 721          return cipherHelper.getSgnAlg();
 722     }
 723 }