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