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 }