|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
* |
|
* (C) Copyright IBM Corp. 1999 All Rights Reserved. |
|
* Copyright 1997 The Open Group Research Institute. All rights reserved. |
|
*/ |
|
|
|
package sun.security.krb5.internal; |
|
|
|
import sun.security.krb5.Asn1Exception; |
|
import sun.security.krb5.Config; |
|
import sun.security.krb5.KrbException; |
|
import sun.security.util.DerInputStream; |
|
import sun.security.util.DerOutputStream; |
|
import sun.security.util.DerValue; |
|
|
|
import java.io.IOException; |
|
import java.time.Instant; |
|
import java.util.Calendar; |
|
import java.util.Date; |
|
import java.util.TimeZone; |
|
|
|
/** |
|
* Implements the ASN.1 KerberosTime type. This is an immutable class. |
|
* |
|
* {@code KerberosTime ::= GeneralizedTime} -- with no fractional seconds |
|
* |
|
* The timestamps used in Kerberos are encoded as GeneralizedTimes. A |
|
* KerberosTime value shall not include any fractional portions of the |
|
* seconds. As required by the DER, it further shall not include any |
|
* separators, and it shall specify the UTC time zone (Z). |
|
* |
|
* <p> |
|
* This definition reflects the Network Working Group RFC 4120 |
|
* specification available at |
|
* <a href="http://www.ietf.org/rfc/rfc4120.txt"> |
|
* http://www.ietf.org/rfc/rfc4120.txt</a>. |
|
* |
|
* The implementation also includes the microseconds info so that the |
|
* same class can be used as a precise timestamp in Authenticator etc. |
|
*/ |
|
|
|
public class KerberosTime { |
|
|
|
private final long kerberosTime; |
|
private final int microSeconds; |
|
|
|
|
|
private static long initMilli = System.currentTimeMillis(); |
|
private static long initMicro = System.nanoTime() / 1000; |
|
|
|
private static boolean DEBUG = Krb5.DEBUG; |
|
|
|
// Do not make this public. It's a little confusing that micro |
|
|
|
private KerberosTime(long time, int micro) { |
|
kerberosTime = time; |
|
microSeconds = micro; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public KerberosTime(long time) { |
|
this(time, 0); |
|
} |
|
|
|
// This constructor is used in the native code |
|
|
|
public KerberosTime(String time) throws Asn1Exception { |
|
this(toKerberosTime(time), 0); |
|
} |
|
|
|
private static long toKerberosTime(String time) throws Asn1Exception { |
|
// ASN.1 GeneralizedTime format: |
|
|
|
// "19700101000000Z" |
|
// | | | | | | | |
|
// 0 4 6 8 | | | |
|
// 10 | | |
|
// 12 | |
|
// 14 |
|
|
|
if (time.length() != 15) |
|
throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); |
|
if (time.charAt(14) != 'Z') |
|
throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); |
|
int year = Integer.parseInt(time.substring(0, 4)); |
|
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); |
|
calendar.clear(); |
|
calendar.set(year, |
|
Integer.parseInt(time.substring(4, 6)) - 1, |
|
Integer.parseInt(time.substring(6, 8)), |
|
Integer.parseInt(time.substring(8, 10)), |
|
Integer.parseInt(time.substring(10, 12)), |
|
Integer.parseInt(time.substring(12, 14))); |
|
return calendar.getTimeInMillis(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public KerberosTime(Date time) { |
|
this(time.getTime(), 0); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public KerberosTime(Instant instant) { |
|
this(instant.getEpochSecond()*1000 + instant.getNano()/1000000L, |
|
instant.getNano()/1000%1000); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static KerberosTime now() { |
|
long newMilli = System.currentTimeMillis(); |
|
long newMicro = System.nanoTime() / 1000; |
|
long microElapsed = newMicro - initMicro; |
|
long calcMilli = initMilli + microElapsed/1000; |
|
if (calcMilli - newMilli > 100 || newMilli - calcMilli > 100) { |
|
if (DEBUG) { |
|
System.out.println("System time adjusted"); |
|
} |
|
initMilli = newMilli; |
|
initMicro = newMicro; |
|
return new KerberosTime(newMilli, 0); |
|
} else { |
|
return new KerberosTime(calcMilli, (int)(microElapsed % 1000)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public String toGeneralizedTimeString() { |
|
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); |
|
calendar.clear(); |
|
|
|
calendar.setTimeInMillis(kerberosTime); |
|
return String.format("%04d%02d%02d%02d%02d%02dZ", |
|
calendar.get(Calendar.YEAR), |
|
calendar.get(Calendar.MONTH) + 1, |
|
calendar.get(Calendar.DAY_OF_MONTH), |
|
calendar.get(Calendar.HOUR_OF_DAY), |
|
calendar.get(Calendar.MINUTE), |
|
calendar.get(Calendar.SECOND)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public byte[] asn1Encode() throws Asn1Exception, IOException { |
|
DerOutputStream out = new DerOutputStream(); |
|
out.putGeneralizedTime(this.toDate()); |
|
return out.toByteArray(); |
|
} |
|
|
|
public long getTime() { |
|
return kerberosTime; |
|
} |
|
|
|
public Date toDate() { |
|
return new Date(kerberosTime); |
|
} |
|
|
|
public int getMicroSeconds() { |
|
Long temp_long = new Long((kerberosTime % 1000L) * 1000L); |
|
return temp_long.intValue() + microSeconds; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public KerberosTime withMicroSeconds(int usec) { |
|
return new KerberosTime( |
|
kerberosTime - kerberosTime%1000L + usec/1000L, |
|
usec%1000); |
|
} |
|
|
|
private boolean inClockSkew(int clockSkew) { |
|
return java.lang.Math.abs(kerberosTime - System.currentTimeMillis()) |
|
<= clockSkew * 1000L; |
|
} |
|
|
|
public boolean inClockSkew() { |
|
return inClockSkew(getDefaultSkew()); |
|
} |
|
|
|
public boolean greaterThanWRTClockSkew(KerberosTime time, int clockSkew) { |
|
if ((kerberosTime - time.kerberosTime) > clockSkew * 1000L) |
|
return true; |
|
return false; |
|
} |
|
|
|
public boolean greaterThanWRTClockSkew(KerberosTime time) { |
|
return greaterThanWRTClockSkew(time, getDefaultSkew()); |
|
} |
|
|
|
public boolean greaterThan(KerberosTime time) { |
|
return kerberosTime > time.kerberosTime || |
|
kerberosTime == time.kerberosTime && |
|
microSeconds > time.microSeconds; |
|
} |
|
|
|
public boolean equals(Object obj) { |
|
if (this == obj) { |
|
return true; |
|
} |
|
|
|
if (!(obj instanceof KerberosTime)) { |
|
return false; |
|
} |
|
|
|
return kerberosTime == ((KerberosTime)obj).kerberosTime && |
|
microSeconds == ((KerberosTime)obj).microSeconds; |
|
} |
|
|
|
public int hashCode() { |
|
int result = 37 * 17 + (int)(kerberosTime ^ (kerberosTime >>> 32)); |
|
return result * 17 + microSeconds; |
|
} |
|
|
|
public boolean isZero() { |
|
return kerberosTime == 0 && microSeconds == 0; |
|
} |
|
|
|
public int getSeconds() { |
|
Long temp_long = new Long(kerberosTime / 1000L); |
|
return temp_long.intValue(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static KerberosTime parse( |
|
DerInputStream data, byte explicitTag, boolean optional) |
|
throws Asn1Exception, IOException { |
|
if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) |
|
return null; |
|
DerValue der = data.getDerValue(); |
|
if (explicitTag != (der.getTag() & (byte)0x1F)) { |
|
throw new Asn1Exception(Krb5.ASN1_BAD_ID); |
|
} |
|
else { |
|
DerValue subDer = der.getData().getDerValue(); |
|
Date temp = subDer.getGeneralizedTime(); |
|
return new KerberosTime(temp.getTime(), 0); |
|
} |
|
} |
|
|
|
public static int getDefaultSkew() { |
|
int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; |
|
try { |
|
if ((tdiff = Config.getInstance().getIntValue( |
|
"libdefaults", "clockskew")) |
|
== Integer.MIN_VALUE) { |
|
tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; |
|
} |
|
} catch (KrbException e) { |
|
if (DEBUG) { |
|
System.out.println("Exception in getting clockskew from " + |
|
"Configuration " + |
|
"using default value " + |
|
e.getMessage()); |
|
} |
|
} |
|
return tdiff; |
|
} |
|
|
|
public String toString() { |
|
return toGeneralizedTimeString(); |
|
} |
|
} |