1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * 27 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 28 * Copyright 1997 The Open Group Research Institute. All rights reserved. 29 */ 30 31 package sun.security.krb5.internal; 32 33 import sun.security.krb5.Asn1Exception; 34 import sun.security.krb5.Config; 35 import sun.security.krb5.KrbException; 36 import sun.security.util.DerInputStream; 37 import sun.security.util.DerOutputStream; 38 import sun.security.util.DerValue; 39 40 import java.io.IOException; 41 import java.time.Instant; 42 import java.util.Calendar; 43 import java.util.Date; 44 import java.util.TimeZone; 45 46 /** 47 * Implements the ASN.1 KerberosTime type. This is an immutable class. 48 * 49 * {@code KerberosTime ::= GeneralizedTime} -- with no fractional seconds 50 * 51 * The timestamps used in Kerberos are encoded as GeneralizedTimes. A 52 * KerberosTime value shall not include any fractional portions of the 53 * seconds. As required by the DER, it further shall not include any 54 * separators, and it shall specify the UTC time zone (Z). 55 * 56 * <p> 57 * This definition reflects the Network Working Group RFC 4120 58 * specification available at 59 * <a href="http://www.ietf.org/rfc/rfc4120.txt"> 60 * http://www.ietf.org/rfc/rfc4120.txt</a>. 61 * 62 * The implementation also includes the microseconds info so that the 63 * same class can be used as a precise timestamp in Authenticator etc. 64 */ 65 66 public class KerberosTime { 67 68 private final long kerberosTime; // milliseconds since epoch, Date.getTime() 69 private final int microSeconds; // last 3 digits of the real microsecond 70 71 // The time when this class is loaded. Used in setNow() 72 private static long initMilli = System.currentTimeMillis(); 73 private static long initMicro = System.nanoTime() / 1000; 74 75 private static boolean DEBUG = Krb5.DEBUG; 76 77 // Do not make this public. It's a little confusing that micro 78 // is only the last 3 digits of microsecond. KerberosTime(long time, int micro)79 private KerberosTime(long time, int micro) { 80 kerberosTime = time; 81 microSeconds = micro; 82 } 83 84 /** 85 * Creates a KerberosTime object from milliseconds since epoch. 86 */ KerberosTime(long time)87 public KerberosTime(long time) { 88 this(time, 0); 89 } 90 91 // Warning: called by NativeCreds.c and nativeccache.c KerberosTime(String time)92 public KerberosTime(String time) throws Asn1Exception { 93 this(toKerberosTime(time), 0); 94 } 95 toKerberosTime(String time)96 private static long toKerberosTime(String time) throws Asn1Exception { 97 // ASN.1 GeneralizedTime format: 98 99 // "19700101000000Z" 100 // | | | | | | | 101 // 0 4 6 8 | | | 102 // 10 | | 103 // 12 | 104 // 14 105 106 if (time.length() != 15) 107 throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); 108 if (time.charAt(14) != 'Z') 109 throw new Asn1Exception(Krb5.ASN1_BAD_TIMEFORMAT); 110 int year = Integer.parseInt(time.substring(0, 4)); 111 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 112 calendar.clear(); // so that millisecond is zero 113 calendar.set(year, 114 Integer.parseInt(time.substring(4, 6)) - 1, 115 Integer.parseInt(time.substring(6, 8)), 116 Integer.parseInt(time.substring(8, 10)), 117 Integer.parseInt(time.substring(10, 12)), 118 Integer.parseInt(time.substring(12, 14))); 119 return calendar.getTimeInMillis(); 120 } 121 122 /** 123 * Creates a KerberosTime object from a Date object. 124 */ KerberosTime(Date time)125 public KerberosTime(Date time) { 126 this(time.getTime(), 0); 127 } 128 129 /** 130 * Creates a KerberosTime object from an Instant object 131 */ KerberosTime(Instant instant)132 public KerberosTime(Instant instant) { 133 this(instant.getEpochSecond()*1000 + instant.getNano()/1000000L, 134 instant.getNano()/1000%1000); 135 } 136 137 /** 138 * Creates a KerberosTime object for now. It uses System.nanoTime() 139 * to get a more precise time than "new Date()". 140 */ now()141 public static KerberosTime now() { 142 long newMilli = System.currentTimeMillis(); 143 long newMicro = System.nanoTime() / 1000; 144 long microElapsed = newMicro - initMicro; 145 long calcMilli = initMilli + microElapsed/1000; 146 if (calcMilli - newMilli > 100 || newMilli - calcMilli > 100) { 147 if (DEBUG) { 148 System.out.println("System time adjusted"); 149 } 150 initMilli = newMilli; 151 initMicro = newMicro; 152 return new KerberosTime(newMilli, 0); 153 } else { 154 return new KerberosTime(calcMilli, (int)(microElapsed % 1000)); 155 } 156 } 157 158 /** 159 * Returns a string representation of KerberosTime object. 160 * @return a string representation of this object. 161 */ toGeneralizedTimeString()162 public String toGeneralizedTimeString() { 163 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 164 calendar.clear(); 165 166 calendar.setTimeInMillis(kerberosTime); 167 return String.format("%04d%02d%02d%02d%02d%02dZ", 168 calendar.get(Calendar.YEAR), 169 calendar.get(Calendar.MONTH) + 1, 170 calendar.get(Calendar.DAY_OF_MONTH), 171 calendar.get(Calendar.HOUR_OF_DAY), 172 calendar.get(Calendar.MINUTE), 173 calendar.get(Calendar.SECOND)); 174 } 175 176 /** 177 * Encodes this object to a byte array. 178 * @return a byte array of encoded data. 179 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. 180 * @exception IOException if an I/O error occurs while reading encoded data. 181 */ asn1Encode()182 public byte[] asn1Encode() throws Asn1Exception, IOException { 183 DerOutputStream out = new DerOutputStream(); 184 out.putGeneralizedTime(this.toDate()); 185 return out.toByteArray(); 186 } 187 getTime()188 public long getTime() { 189 return kerberosTime; 190 } 191 toDate()192 public Date toDate() { 193 return new Date(kerberosTime); 194 } 195 getMicroSeconds()196 public int getMicroSeconds() { 197 int temp_int = (int) ((kerberosTime % 1000L) * 1000L); 198 return temp_int + microSeconds; 199 } 200 201 /** 202 * Returns a new KerberosTime object with the original seconds 203 * and the given microseconds. 204 */ withMicroSeconds(int usec)205 public KerberosTime withMicroSeconds(int usec) { 206 return new KerberosTime( 207 kerberosTime - kerberosTime%1000L + usec/1000L, 208 usec%1000); 209 } 210 inClockSkew(int clockSkew)211 private boolean inClockSkew(int clockSkew) { 212 return java.lang.Math.abs(kerberosTime - System.currentTimeMillis()) 213 <= clockSkew * 1000L; 214 } 215 inClockSkew()216 public boolean inClockSkew() { 217 return inClockSkew(getDefaultSkew()); 218 } 219 greaterThanWRTClockSkew(KerberosTime time, int clockSkew)220 public boolean greaterThanWRTClockSkew(KerberosTime time, int clockSkew) { 221 if ((kerberosTime - time.kerberosTime) > clockSkew * 1000L) 222 return true; 223 return false; 224 } 225 greaterThanWRTClockSkew(KerberosTime time)226 public boolean greaterThanWRTClockSkew(KerberosTime time) { 227 return greaterThanWRTClockSkew(time, getDefaultSkew()); 228 } 229 greaterThan(KerberosTime time)230 public boolean greaterThan(KerberosTime time) { 231 return kerberosTime > time.kerberosTime || 232 kerberosTime == time.kerberosTime && 233 microSeconds > time.microSeconds; 234 } 235 equals(Object obj)236 public boolean equals(Object obj) { 237 if (this == obj) { 238 return true; 239 } 240 241 if (!(obj instanceof KerberosTime)) { 242 return false; 243 } 244 245 return kerberosTime == ((KerberosTime)obj).kerberosTime && 246 microSeconds == ((KerberosTime)obj).microSeconds; 247 } 248 hashCode()249 public int hashCode() { 250 int result = 37 * 17 + (int)(kerberosTime ^ (kerberosTime >>> 32)); 251 return result * 17 + microSeconds; 252 } 253 isZero()254 public boolean isZero() { 255 return kerberosTime == 0 && microSeconds == 0; 256 } 257 getSeconds()258 public int getSeconds() { 259 return (int) (kerberosTime / 1000L); 260 } 261 262 /** 263 * Parse (unmarshal) a kerberostime from a DER input stream. This form 264 * parsing might be used when expanding a value which is part of 265 * a constructed sequence and uses explicitly tagged type. 266 * 267 * @exception Asn1Exception on error. 268 * @param data the Der input stream value, which contains 269 * one or more marshaled value. 270 * @param explicitTag tag number. 271 * @param optional indicates if this data field is optional 272 * @return an instance of KerberosTime. 273 * 274 */ parse( DerInputStream data, byte explicitTag, boolean optional)275 public static KerberosTime parse( 276 DerInputStream data, byte explicitTag, boolean optional) 277 throws Asn1Exception, IOException { 278 if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) 279 return null; 280 DerValue der = data.getDerValue(); 281 if (explicitTag != (der.getTag() & (byte)0x1F)) { 282 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 283 } 284 else { 285 DerValue subDer = der.getData().getDerValue(); 286 Date temp = subDer.getGeneralizedTime(); 287 return new KerberosTime(temp.getTime(), 0); 288 } 289 } 290 getDefaultSkew()291 public static int getDefaultSkew() { 292 int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; 293 try { 294 if ((tdiff = Config.getInstance().getIntValue( 295 "libdefaults", "clockskew")) 296 == Integer.MIN_VALUE) { //value is not defined 297 tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; 298 } 299 } catch (KrbException e) { 300 if (DEBUG) { 301 System.out.println("Exception in getting clockskew from " + 302 "Configuration " + 303 "using default value: " + 304 e.getMessage()); 305 } 306 } 307 return tdiff; 308 } 309 toString()310 public String toString() { 311 return toGeneralizedTimeString(); 312 } 313 } 314