1 /*
2 * Copyright 2000-2008 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package javax.security.auth.kerberos;
27
28 import java.io;
29 import java.util.Date;
30 import java.util.Arrays;
31 import java.net.InetAddress;
32 import javax.crypto.SecretKey;
33 import javax.security.auth.Refreshable;
34 import javax.security.auth.Destroyable;
35 import javax.security.auth.RefreshFailedException;
36 import javax.security.auth.DestroyFailedException;
37 import sun.misc.HexDumpEncoder;
38 import sun.security.krb5.EncryptionKey;
39 import sun.security.krb5.Asn1Exception;
40 import sun.security.util;
41
42 /**
43 * This class encapsulates a Kerberos ticket and associated
44 * information as viewed from the client's point of view. It captures all
45 * information that the Key Distribution Center (KDC) sends to the client
46 * in the reply message KDC-REP defined in the Kerberos Protocol
47 * Specification (<a href=http://www.ietf.org/rfc/rfc4120.txt>RFC 4120</a>).
48 * <p>
49 * All Kerberos JAAS login modules that authenticate a user to a KDC should
50 * use this class. Where available, the login module might even read this
51 * information from a ticket cache in the operating system instead of
52 * directly communicating with the KDC. During the commit phase of the JAAS
53 * authentication process, the JAAS login module should instantiate this
54 * class and store the instance in the private credential set of a
55 * {@link javax.security.auth.Subject Subject}.<p>
56 *
57 * It might be necessary for the application to be granted a
58 * {@link javax.security.auth.PrivateCredentialPermission
59 * PrivateCredentialPermission} if it needs to access a KerberosTicket
60 * instance from a Subject. This permission is not needed when the
61 * application depends on the default JGSS Kerberos mechanism to access the
62 * KerberosTicket. In that case, however, the application will need an
63 * appropriate
64 * {@link javax.security.auth.kerberos.ServicePermission ServicePermission}.
65 * <p>
66 * Note that this class is applicable to both ticket granting tickets and
67 * other regular service tickets. A ticket granting ticket is just a
68 * special case of a more generalized service ticket.
69 *
70 * @see javax.security.auth.Subject
71 * @see javax.security.auth.PrivateCredentialPermission
72 * @see javax.security.auth.login.LoginContext
73 * @see org.ietf.jgss.GSSCredential
74 * @see org.ietf.jgss.GSSManager
75 *
76 * @author Mayank Upadhyay
77 * @since 1.4
78 */
79 public class KerberosTicket implements Destroyable, Refreshable,
80 java.io.Serializable {
81
82 private static final long serialVersionUID = 7395334370157380539L;
83
84 // XXX Make these flag indices public
85 private static final int FORWARDABLE_TICKET_FLAG = 1;
86 private static final int FORWARDED_TICKET_FLAG = 2;
87 private static final int PROXIABLE_TICKET_FLAG = 3;
88 private static final int PROXY_TICKET_FLAG = 4;
89 private static final int POSTDATED_TICKET_FLAG = 6;
90 private static final int RENEWABLE_TICKET_FLAG = 8;
91 private static final int INITIAL_TICKET_FLAG = 9;
92
93 private static final int NUM_FLAGS = 32;
94
95 /**
96 *
97 * ASN.1 DER Encoding of the Ticket as defined in the
98 * Kerberos Protocol Specification RFC4120.
99 *
100 * @serial
101 */
102
103 private byte[] asn1Encoding;
104
105 /**
106 *<code>KeyImpl</code> is serialized by writing out the ASN1 Encoded bytes
107 * of the encryption key. The ASN1 encoding is defined in RFC4120 and as
108 * follows:
109 * <pre>
110 * EncryptionKey ::= SEQUENCE {
111 * keytype [0] Int32 -- actually encryption type --,
112 * keyvalue [1] OCTET STRING
113 * }
114 * </pre>
115 *
116 * @serial
117 */
118
119 private KeyImpl sessionKey;
120
121 /**
122 *
123 * Ticket Flags as defined in the Kerberos Protocol Specification RFC4120.
124 *
125 * @serial
126 */
127
128 private boolean[] flags;
129
130 /**
131 *
132 * Time of initial authentication
133 *
134 * @serial
135 */
136
137 private Date authTime;
138
139 /**
140 *
141 * Time after which the ticket is valid.
142 * @serial
143 */
144 private Date startTime;
145
146 /**
147 *
148 * Time after which the ticket will not be honored. (its expiration time).
149 *
150 * @serial
151 */
152
153 private Date endTime;
154
155 /**
156 *
157 * For renewable Tickets it indicates the maximum endtime that may be
158 * included in a renewal. It can be thought of as the absolute expiration
159 * time for the ticket, including all renewals. This field may be null
160 * for tickets that are not renewable.
161 *
162 * @serial
163 */
164
165 private Date renewTill;
166
167 /**
168 *
169 * Client that owns the service ticket
170 *
171 * @serial
172 */
173
174 private KerberosPrincipal client;
175
176 /**
177 *
178 * The service for which the ticket was issued.
179 *
180 * @serial
181 */
182
183 private KerberosPrincipal server;
184
185 /**
186 *
187 * The addresses from where the ticket may be used by the client.
188 * This field may be null when the ticket is usable from any address.
189 *
190 * @serial
191 */
192
193
194 private InetAddress[] clientAddresses;
195
196 private transient boolean destroyed = false;
197
198 /**
199 * Constructs a KerberosTicket using credentials information that a
200 * client either receives from a KDC or reads from a cache.
201 *
202 * @param asn1Encoding the ASN.1 encoding of the ticket as defined by
203 * the Kerberos protocol specification.
204 * @param client the client that owns this service
205 * ticket
206 * @param server the service that this ticket is for
207 * @param sessionKey the raw bytes for the session key that must be
208 * used to encrypt the authenticator that will be sent to the server
209 * @param keyType the key type for the session key as defined by the
210 * Kerberos protocol specification.
211 * @param flags the ticket flags. Each element in this array indicates
212 * the value for the corresponding bit in the ASN.1 BitString that
213 * represents the ticket flags. If the number of elements in this array
214 * is less than the number of flags used by the Kerberos protocol,
215 * then the missing flags will be filled in with false.
216 * @param authTime the time of initial authentication for the client
217 * @param startTime the time after which the ticket will be valid. This
218 * may be null in which case the value of authTime is treated as the
219 * startTime.
220 * @param endTime the time after which the ticket will no longer be
221 * valid
222 * @param renewTill an absolute expiration time for the ticket,
223 * including all renewal that might be possible. This field may be null
224 * for tickets that are not renewable.
225 * @param clientAddresses the addresses from where the ticket may be
226 * used by the client. This field may be null when the ticket is usable
227 * from any address.
228 */
229 public KerberosTicket(byte[] asn1Encoding,
230 KerberosPrincipal client,
231 KerberosPrincipal server,
232 byte[] sessionKey,
233 int keyType,
234 boolean[] flags,
235 Date authTime,
236 Date startTime,
237 Date endTime,
238 Date renewTill,
239 InetAddress[] clientAddresses) {
240
241 init(asn1Encoding, client, server, sessionKey, keyType, flags,
242 authTime, startTime, endTime, renewTill, clientAddresses);
243 }
244
245 private void init(byte[] asn1Encoding,
246 KerberosPrincipal client,
247 KerberosPrincipal server,
248 byte[] sessionKey,
249 int keyType,
250 boolean[] flags,
251 Date authTime,
252 Date startTime,
253 Date endTime,
254 Date renewTill,
255 InetAddress[] clientAddresses) {
256 if (sessionKey == null)
257 throw new IllegalArgumentException("Session key for ticket"
258 + " cannot be null");
259 init(asn1Encoding, client, server,
260 new KeyImpl(sessionKey, keyType), flags, authTime,
261 startTime, endTime, renewTill, clientAddresses);
262 }
263
264 private void init(byte[] asn1Encoding,
265 KerberosPrincipal client,
266 KerberosPrincipal server,
267 KeyImpl sessionKey,
268 boolean[] flags,
269 Date authTime,
270 Date startTime,
271 Date endTime,
272 Date renewTill,
273 InetAddress[] clientAddresses) {
274 if (asn1Encoding == null)
275 throw new IllegalArgumentException("ASN.1 encoding of ticket"
276 + " cannot be null");
277 this.asn1Encoding = asn1Encoding.clone();
278
279 if (client == null)
280 throw new IllegalArgumentException("Client name in ticket"
281 + " cannot be null");
282 this.client = client;
283
284 if (server == null)
285 throw new IllegalArgumentException("Server name in ticket"
286 + " cannot be null");
287 this.server = server;
288
289 // Caller needs to make sure `sessionKey` will not be null
290 this.sessionKey = sessionKey;
291
292 if (flags != null) {
293 if (flags.length >= NUM_FLAGS)
294 this.flags = flags.clone();
295 else {
296 this.flags = new boolean[NUM_FLAGS];
297 // Fill in whatever we have
298 for (int i = 0; i < flags.length; i++)
299 this.flags[i] = flags[i];
300 }
301 } else
302 this.flags = new boolean[NUM_FLAGS];
303
304 if (this.flags[RENEWABLE_TICKET_FLAG]) {
305 if (renewTill == null)
306 throw new IllegalArgumentException("The renewable period "
307 + "end time cannot be null for renewable tickets.");
308
309 this.renewTill = new Date(renewTill.getTime());
310 }
311
312 if (authTime != null) {
313 this.authTime = new Date(authTime.getTime());
314 }
315 if (startTime != null) {
316 this.startTime = new Date(startTime.getTime());
317 } else {
318 this.startTime = this.authTime;
319 }
320
321 if (endTime == null)
322 throw new IllegalArgumentException("End time for ticket validity"
323 + " cannot be null");
324 this.endTime = new Date(endTime.getTime());
325
326 if (clientAddresses != null)
327 this.clientAddresses = clientAddresses.clone();
328 }
329
330 /**
331 * Returns the client principal associated with this ticket.
332 *
333 * @return the client principal.
334 */
335 public final KerberosPrincipal getClient() {
336 return client;
337 }
338
339 /**
340 * Returns the service principal associated with this ticket.
341 *
342 * @return the service principal.
343 */
344 public final KerberosPrincipal getServer() {
345 return server;
346 }
347
348 /**
349 * Returns the session key associated with this ticket.
350 *
351 * @return the session key.
352 */
353 public final SecretKey getSessionKey() {
354 if (destroyed)
355 throw new IllegalStateException("This ticket is no longer valid");
356 return sessionKey;
357 }
358
359 /**
360 * Returns the key type of the session key associated with this
361 * ticket as defined by the Kerberos Protocol Specification.
362 *
363 * @return the key type of the session key associated with this
364 * ticket.
365 *
366 * @see #getSessionKey()
367 */
368 public final int getSessionKeyType() {
369 if (destroyed)
370 throw new IllegalStateException("This ticket is no longer valid");
371 return sessionKey.getKeyType();
372 }
373
374 /**
375 * Determines if this ticket is forwardable.
376 *
377 * @return true if this ticket is forwardable, false if not.
378 */
379 public final boolean isForwardable() {
380 return flags[FORWARDABLE_TICKET_FLAG];
381 }
382
383 /**
384 * Determines if this ticket had been forwarded or was issued based on
385 * authentication involving a forwarded ticket-granting ticket.
386 *
387 * @return true if this ticket had been forwarded or was issued based on
388 * authentication involving a forwarded ticket-granting ticket,
389 * false otherwise.
390 */
391 public final boolean isForwarded() {
392 return flags[FORWARDED_TICKET_FLAG];
393 }
394
395 /**
396 * Determines if this ticket is proxiable.
397 *
398 * @return true if this ticket is proxiable, false if not.
399 */
400 public final boolean isProxiable() {
401 return flags[PROXIABLE_TICKET_FLAG];
402 }
403
404 /**
405 * Determines is this ticket is a proxy-ticket.
406 *
407 * @return true if this ticket is a proxy-ticket, false if not.
408 */
409 public final boolean isProxy() {
410 return flags[PROXY_TICKET_FLAG];
411 }
412
413
414 /**
415 * Determines is this ticket is post-dated.
416 *
417 * @return true if this ticket is post-dated, false if not.
418 */
419 public final boolean isPostdated() {
420 return flags[POSTDATED_TICKET_FLAG];
421 }
422
423 /**
424 * Determines is this ticket is renewable. If so, the {@link #refresh()
425 * refresh} method can be called, assuming the validity period for
426 * renewing is not already over.
427 *
428 * @return true if this ticket is renewable, false if not.
429 */
430 public final boolean isRenewable() {
431 return flags[RENEWABLE_TICKET_FLAG];
432 }
433
434 /**
435 * Determines if this ticket was issued using the Kerberos AS-Exchange
436 * protocol, and not issued based on some ticket-granting ticket.
437 *
438 * @return true if this ticket was issued using the Kerberos AS-Exchange
439 * protocol, false if not.
440 */
441 public final boolean isInitial() {
442 return flags[INITIAL_TICKET_FLAG];
443 }
444
445 /**
446 * Returns the flags associated with this ticket. Each element in the
447 * returned array indicates the value for the corresponding bit in the
448 * ASN.1 BitString that represents the ticket flags.
449 *
450 * @return the flags associated with this ticket.
451 */
452 public final boolean[] getFlags() {
453 return (flags == null? null: flags.clone());
454 }
455
456 /**
457 * Returns the time that the client was authenticated.
458 *
459 * @return the time that the client was authenticated
460 * or null if not set.
461 */
462 public final java.util.Date getAuthTime() {
463 return (authTime == null) ? null : (Date)authTime.clone();
464 }
465
466 /**
467 * Returns the start time for this ticket's validity period.
468 *
469 * @return the start time for this ticket's validity period
470 * or null if not set.
471 */
472 public final java.util.Date getStartTime() {
473 return (startTime == null) ? null : (Date)startTime.clone();
474 }
475
476 /**
477 * Returns the expiration time for this ticket's validity period.
478 *
479 * @return the expiration time for this ticket's validity period.
480 */
481 public final java.util.Date getEndTime() {
482 return (Date) endTime.clone();
483 }
484
485 /**
486 * Returns the latest expiration time for this ticket, including all
487 * renewals. This will return a null value for non-renewable tickets.
488 *
489 * @return the latest expiration time for this ticket.
490 */
491 public final java.util.Date getRenewTill() {
492 return (renewTill == null) ? null: (Date)renewTill.clone();
493 }
494
495 /**
496 * Returns a list of addresses from where the ticket can be used.
497 *
498 * @return ths list of addresses or null, if the field was not
499 * provided.
500 */
501 public final java.net.InetAddress[] getClientAddresses() {
502 return (clientAddresses == null) ? null: clientAddresses.clone();
503 }
504
505 /**
506 * Returns an ASN.1 encoding of the entire ticket.
507 *
508 * @return an ASN.1 encoding of the entire ticket.
509 */
510 public final byte[] getEncoded() {
511 if (destroyed)
512 throw new IllegalStateException("This ticket is no longer valid");
513 return asn1Encoding.clone();
514 }
515
516 /** Determines if this ticket is still current. */
517 public boolean isCurrent() {
518 return (System.currentTimeMillis() <= getEndTime().getTime());
519 }
520
521 /**
522 * Extends the validity period of this ticket. The ticket will contain
523 * a new session key if the refresh operation succeeds. The refresh
524 * operation will fail if the ticket is not renewable or the latest
525 * allowable renew time has passed. Any other error returned by the
526 * KDC will also cause this method to fail.
527 *
528 * Note: This method is not synchronized with the the accessor
529 * methods of this object. Hence callers need to be aware of multiple
530 * threads that might access this and try to renew it at the same
531 * time.
532 *
533 * @throws RefreshFailedException if the ticket is not renewable, or
534 * the latest allowable renew time has passed, or the KDC returns some
535 * error.
536 *
537 * @see #isRenewable()
538 * @see #getRenewTill()
539 */
540 public void refresh() throws RefreshFailedException {
541
542 if (destroyed)
543 throw new RefreshFailedException("A destroyed ticket "
544 + "cannot be renewd.");
545
546 if (!isRenewable())
547 throw new RefreshFailedException("This ticket is not renewable");
548
549 if (System.currentTimeMillis() > getRenewTill().getTime())
550 throw new RefreshFailedException("This ticket is past "
551 + "its last renewal time.");
552 Throwable e = null;
553 sun.security.krb5.Credentials krb5Creds = null;
554
555 try {
556 krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
557 client.toString(),
558 server.toString(),
559 sessionKey.getEncoded(),
560 sessionKey.getKeyType(),
561 flags,
562 authTime,
563 startTime,
564 endTime,
565 renewTill,
566 clientAddresses);
567 krb5Creds = krb5Creds.renew();
568 } catch (sun.security.krb5.KrbException krbException) {
569 e = krbException;
570 } catch (java.io.IOException ioException) {
571 e = ioException;
572 }
573
574 if (e != null) {
575 RefreshFailedException rfException
576 = new RefreshFailedException("Failed to renew Kerberos Ticket "
577 + "for client " + client
578 + " and server " + server
579 + " - " + e.getMessage());
580 rfException.initCause(e);
581 throw rfException;
582 }
583
584 /*
585 * In case multiple threads try to refresh it at the same time.
586 */
587 synchronized (this) {
588 try {
589 this.destroy();
590 } catch (DestroyFailedException dfException) {
591 // Squelch it since we don't care about the old ticket.
592 }
593 init(krb5Creds.getEncoded(),
594 new KerberosPrincipal(krb5Creds.getClient().getName()),
595 new KerberosPrincipal(krb5Creds.getServer().getName(),
596 KerberosPrincipal.KRB_NT_SRV_INST),
597 krb5Creds.getSessionKey().getBytes(),
598 krb5Creds.getSessionKey().getEType(),
599 krb5Creds.getFlags(),
600 krb5Creds.getAuthTime(),
601 krb5Creds.getStartTime(),
602 krb5Creds.getEndTime(),
603 krb5Creds.getRenewTill(),
604 krb5Creds.getClientAddresses());
605 destroyed = false;
606 }
607 }
608
609 /**
610 * Destroys the ticket and destroys any sensitive information stored in
611 * it.
612 */
613 public void destroy() throws DestroyFailedException {
614 if (!destroyed) {
615 Arrays.fill(asn1Encoding, (byte) 0);
616 client = null;
617 server = null;
618 sessionKey.destroy();
619 flags = null;
620 authTime = null;
621 startTime = null;
622 endTime = null;
623 renewTill = null;
624 clientAddresses = null;
625 destroyed = true;
626 }
627 }
628
629 /**
630 * Determines if this ticket has been destroyed.
631 */
632 public boolean isDestroyed() {
633 return destroyed;
634 }
635
636 public String toString() {
637 if (destroyed)
638 throw new IllegalStateException("This ticket is no longer valid");
639 StringBuffer caddrBuf = new StringBuffer();
640 if (clientAddresses != null) {
641 for (int i = 0; i < clientAddresses.length; i++) {
642 caddrBuf.append("clientAddresses[" + i + "] = " +
643 clientAddresses[i].toString());
644 }
645 }
646 return ("Ticket (hex) = " + "\n" +
647 (new HexDumpEncoder()).encodeBuffer(asn1Encoding) + "\n" +
648 "Client Principal = " + client.toString() + "\n" +
649 "Server Principal = " + server.toString() + "\n" +
650 "Session Key = " + sessionKey.toString() + "\n" +
651 "Forwardable Ticket " + flags[FORWARDABLE_TICKET_FLAG] + "\n" +
652 "Forwarded Ticket " + flags[FORWARDED_TICKET_FLAG] + "\n" +
653 "Proxiable Ticket " + flags[PROXIABLE_TICKET_FLAG] + "\n" +
654 "Proxy Ticket " + flags[PROXY_TICKET_FLAG] + "\n" +
655 "Postdated Ticket " + flags[POSTDATED_TICKET_FLAG] + "\n" +
656 "Renewable Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
657 "Initial Ticket " + flags[RENEWABLE_TICKET_FLAG] + "\n" +
658 "Auth Time = " + String.valueOf(authTime) + "\n" +
659 "Start Time = " + String.valueOf(startTime) + "\n" +
660 "End Time = " + endTime.toString() + "\n" +
661 "Renew Till = " + String.valueOf(renewTill) + "\n" +
662 "Client Addresses " +
663 (clientAddresses == null ? " Null " : caddrBuf.toString() +
664 "\n"));
665 }
666
667 /**
668 * Returns a hashcode for this KerberosTicket.
669 *
670 * @return a hashCode() for the <code>KerberosTicket</code>
671 * @since 1.6
672 */
673 public int hashCode() {
674 int result = 17;
675 if (isDestroyed()) {
676 return result;
677 }
678 result = result * 37 + Arrays.hashCode(getEncoded());
679 result = result * 37 + endTime.hashCode();
680 result = result * 37 + client.hashCode();
681 result = result * 37 + server.hashCode();
682 result = result * 37 + sessionKey.hashCode();
683
684 // authTime may be null
685 if (authTime != null) {
686 result = result * 37 + authTime.hashCode();
687 }
688
689 // startTime may be null
690 if (startTime != null) {
691 result = result * 37 + startTime.hashCode();
692 }
693
694 // renewTill may be null
695 if (renewTill != null) {
696 result = result * 37 + renewTill.hashCode();
697 }
698
699 // clientAddress may be null, the array's hashCode is 0
700 result = result * 37 + Arrays.hashCode(clientAddresses);
701 return result * 37 + Arrays.hashCode(flags);
702 }
703
704 /**
705 * Compares the specified Object with this KerberosTicket for equality.
706 * Returns true if the given object is also a
707 * <code>KerberosTicket</code> and the two
708 * <code>KerberosTicket</code> instances are equivalent.
709 *
710 * @param other the Object to compare to
711 * @return true if the specified object is equal to this KerberosTicket,
712 * false otherwise. NOTE: Returns false if either of the KerberosTicket
713 * objects has been destroyed.
714 * @since 1.6
715 */
716 public boolean equals(Object other) {
717
718 if (other == this)
719 return true;
720
721 if (! (other instanceof KerberosTicket)) {
722 return false;
723 }
724
725 KerberosTicket otherTicket = ((KerberosTicket) other);
726 if (isDestroyed() || otherTicket.isDestroyed()) {
727 return false;
728 }
729
730 if (!Arrays.equals(getEncoded(), otherTicket.getEncoded()) ||
731 !endTime.equals(otherTicket.getEndTime()) ||
732 !server.equals(otherTicket.getServer()) ||
733 !client.equals(otherTicket.getClient()) ||
734 !sessionKey.equals(otherTicket.getSessionKey()) ||
735 !Arrays.equals(clientAddresses, otherTicket.getClientAddresses()) ||
736 !Arrays.equals(flags, otherTicket.getFlags())) {
737 return false;
738 }
739
740 // authTime may be null
741 if (authTime == null) {
742 if (otherTicket.getAuthTime() != null)
743 return false;
744 } else {
745 if (!authTime.equals(otherTicket.getAuthTime()))
746 return false;
747 }
748
749 // startTime may be null
750 if (startTime == null) {
751 if (otherTicket.getStartTime() != null)
752 return false;
753 } else {
754 if (!startTime.equals(otherTicket.getStartTime()))
755 return false;
756 }
757
758 if (renewTill == null) {
759 if (otherTicket.getRenewTill() != null)
760 return false;
761 } else {
762 if (!renewTill.equals(otherTicket.getRenewTill()))
763 return false;
764 }
765
766 return true;
767 }
768
769 private void readObject(ObjectInputStream s)
770 throws IOException, ClassNotFoundException {
771 s.defaultReadObject();
772 if (sessionKey == null) {
773 throw new InvalidObjectException("Session key cannot be null");
774 }
775 try {
776 init(asn1Encoding, client, server, sessionKey,
777 flags, authTime, startTime, endTime,
778 renewTill, clientAddresses);
779 } catch (IllegalArgumentException iae) {
780 throw (InvalidObjectException)
781 new InvalidObjectException(iae.getMessage()).initCause(iae);
782 }
783 }
784 }