60 * |
62 * |
61 * The implementation also includes the microseconds info so that the |
63 * The implementation also includes the microseconds info so that the |
62 * same class can be used as a precise timestamp in Authenticator etc. |
64 * same class can be used as a precise timestamp in Authenticator etc. |
63 */ |
65 */ |
64 |
66 |
65 public class KerberosTime implements Cloneable { |
67 public class KerberosTime { |
66 |
68 |
67 private long kerberosTime; // milliseconds since epoch, a Date.getTime() value |
69 private final long kerberosTime; // milliseconds since epoch, Date.getTime() |
68 private int microSeconds; // the last three digits of the microsecond value |
70 private final int microSeconds; // last 3 digits of the real microsecond |
69 |
71 |
70 // The time when this class is loaded. Used in setNow() |
72 // The time when this class is loaded. Used in setNow() |
71 private static long initMilli = System.currentTimeMillis(); |
73 private static long initMilli = System.currentTimeMillis(); |
72 private static long initMicro = System.nanoTime() / 1000; |
74 private static long initMicro = System.nanoTime() / 1000; |
73 |
75 |
74 private static long syncTime; |
|
75 private static boolean DEBUG = Krb5.DEBUG; |
76 private static boolean DEBUG = Krb5.DEBUG; |
76 |
77 |
77 public static final boolean NOW = true; |
78 // Do not make this public. It's a little confusing that micro |
78 public static final boolean UNADJUSTED_NOW = false; |
79 // is only the last 3 digits of microsecond. |
79 |
|
80 public KerberosTime(long time) { |
|
81 kerberosTime = time; |
|
82 } |
|
83 |
|
84 private KerberosTime(long time, int micro) { |
80 private KerberosTime(long time, int micro) { |
85 kerberosTime = time; |
81 kerberosTime = time; |
86 microSeconds = micro; |
82 microSeconds = micro; |
87 } |
83 } |
88 |
84 |
89 public Object clone() { |
85 /** |
90 return new KerberosTime(kerberosTime, microSeconds); |
86 * Creates a KerberosTime object from milliseconds since epoch. |
|
87 */ |
|
88 public KerberosTime(long time) { |
|
89 this(time, 0); |
91 } |
90 } |
92 |
91 |
93 // This constructor is used in the native code |
92 // This constructor is used in the native code |
94 // src/windows/native/sun/security/krb5/NativeCreds.c |
93 // src/windows/native/sun/security/krb5/NativeCreds.c |
95 public KerberosTime(String time) throws Asn1Exception { |
94 public KerberosTime(String time) throws Asn1Exception { |
96 kerberosTime = toKerberosTime(time); |
95 this(toKerberosTime(time), 0); |
97 } |
|
98 |
|
99 /** |
|
100 * Constructs a KerberosTime object. |
|
101 * @param encoding a DER-encoded data. |
|
102 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. |
|
103 * @exception IOException if an I/O error occurs while reading encoded data. |
|
104 */ |
|
105 public KerberosTime(DerValue encoding) throws Asn1Exception, IOException { |
|
106 GregorianCalendar calendar = new GregorianCalendar(); |
|
107 Date temp = encoding.getGeneralizedTime(); |
|
108 kerberosTime = temp.getTime(); |
|
109 } |
96 } |
110 |
97 |
111 private static long toKerberosTime(String time) throws Asn1Exception { |
98 private static long toKerberosTime(String time) throws Asn1Exception { |
112 // this method only used by KerberosTime class. |
|
113 |
|
114 // ASN.1 GeneralizedTime format: |
99 // ASN.1 GeneralizedTime format: |
115 |
100 |
116 // "19700101000000Z" |
101 // "19700101000000Z" |
117 // | | | | | | | |
102 // | | | | | | | |
118 // 0 4 6 8 | | | |
103 // 0 4 6 8 | | | |
131 Integer.parseInt(time.substring(4, 6)) - 1, |
116 Integer.parseInt(time.substring(4, 6)) - 1, |
132 Integer.parseInt(time.substring(6, 8)), |
117 Integer.parseInt(time.substring(6, 8)), |
133 Integer.parseInt(time.substring(8, 10)), |
118 Integer.parseInt(time.substring(8, 10)), |
134 Integer.parseInt(time.substring(10, 12)), |
119 Integer.parseInt(time.substring(10, 12)), |
135 Integer.parseInt(time.substring(12, 14))); |
120 Integer.parseInt(time.substring(12, 14))); |
136 |
121 return calendar.getTimeInMillis(); |
137 //The Date constructor assumes the setting are local relative |
122 } |
138 //and converts the time to UTC before storing it. Since we |
123 |
139 //want the internal representation to correspond to local |
124 /** |
140 //and not UTC time we subtract the UTC time offset. |
125 * Creates a KerberosTime object from a Date object. |
141 return (calendar.getTime().getTime()); |
126 */ |
142 |
|
143 } |
|
144 |
|
145 // should be moved to sun.security.krb5.util class |
|
146 public static String zeroPad(String s, int length) { |
|
147 StringBuffer temp = new StringBuffer(s); |
|
148 while (temp.length() < length) |
|
149 temp.insert(0, '0'); |
|
150 return temp.toString(); |
|
151 } |
|
152 |
|
153 public KerberosTime(Date time) { |
127 public KerberosTime(Date time) { |
154 kerberosTime = time.getTime(); // (time.getTimezoneOffset() * 60000L); |
128 this(time.getTime(), 0); |
155 } |
129 } |
156 |
130 |
157 public KerberosTime(boolean initToNow) { |
131 /** |
158 if (initToNow) { |
132 * Creates a KerberosTime object for now. It uses System.nanoTime() |
159 setNow(); |
133 * to get a more precise time than "new Date()". |
160 } |
134 */ |
161 } |
135 public static KerberosTime now() { |
162 |
|
163 /** |
|
164 * Returns a string representation of KerberosTime object. |
|
165 * @return a string representation of this object. |
|
166 */ |
|
167 public String toGeneralizedTimeString() { |
|
168 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); |
|
169 calendar.clear(); |
|
170 |
|
171 calendar.setTimeInMillis(kerberosTime); |
|
172 return zeroPad(Integer.toString(calendar.get(Calendar.YEAR)), 4) + |
|
173 zeroPad(Integer.toString(calendar.get(Calendar.MONTH) + 1), 2) + |
|
174 zeroPad(Integer.toString(calendar.get(Calendar.DAY_OF_MONTH)), 2) + |
|
175 zeroPad(Integer.toString(calendar.get(Calendar.HOUR_OF_DAY)), 2) + |
|
176 zeroPad(Integer.toString(calendar.get(Calendar.MINUTE)), 2) + |
|
177 zeroPad(Integer.toString(calendar.get(Calendar.SECOND)), 2) + 'Z'; |
|
178 |
|
179 } |
|
180 |
|
181 /** |
|
182 * Encodes this object to a byte array. |
|
183 * @return a byte array of encoded data. |
|
184 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. |
|
185 * @exception IOException if an I/O error occurs while reading encoded data. |
|
186 */ |
|
187 public byte[] asn1Encode() throws Asn1Exception, IOException { |
|
188 DerOutputStream out = new DerOutputStream(); |
|
189 out.putGeneralizedTime(this.toDate()); |
|
190 return out.toByteArray(); |
|
191 } |
|
192 |
|
193 public long getTime() { |
|
194 return kerberosTime; |
|
195 } |
|
196 |
|
197 |
|
198 public void setTime(Date time) { |
|
199 kerberosTime = time.getTime(); // (time.getTimezoneOffset() * 60000L); |
|
200 microSeconds = 0; |
|
201 } |
|
202 |
|
203 public void setTime(long time) { |
|
204 kerberosTime = time; |
|
205 microSeconds = 0; |
|
206 } |
|
207 |
|
208 public Date toDate() { |
|
209 Date temp = new Date(kerberosTime); |
|
210 temp.setTime(temp.getTime()); |
|
211 return temp; |
|
212 } |
|
213 |
|
214 public void setNow() { |
|
215 long newMilli = System.currentTimeMillis(); |
136 long newMilli = System.currentTimeMillis(); |
216 long newMicro = System.nanoTime() / 1000; |
137 long newMicro = System.nanoTime() / 1000; |
217 long microElapsed = newMicro - initMicro; |
138 long microElapsed = newMicro - initMicro; |
218 long calcMilli = initMilli + microElapsed/1000; |
139 long calcMilli = initMilli + microElapsed/1000; |
219 if (calcMilli - newMilli > 100 || newMilli - calcMilli > 100) { |
140 if (calcMilli - newMilli > 100 || newMilli - calcMilli > 100) { |
220 if (DEBUG) { |
141 if (DEBUG) { |
221 System.out.println("System time adjusted"); |
142 System.out.println("System time adjusted"); |
222 } |
143 } |
223 initMilli = newMilli; |
144 initMilli = newMilli; |
224 initMicro = newMicro; |
145 initMicro = newMicro; |
225 setTime(newMilli); |
146 return new KerberosTime(newMilli, 0); |
226 microSeconds = 0; |
|
227 } else { |
147 } else { |
228 setTime(calcMilli); |
148 return new KerberosTime(calcMilli, (int)(microElapsed % 1000)); |
229 microSeconds = (int)(microElapsed % 1000); |
149 } |
230 } |
150 } |
|
151 |
|
152 /** |
|
153 * Returns a string representation of KerberosTime object. |
|
154 * @return a string representation of this object. |
|
155 */ |
|
156 public String toGeneralizedTimeString() { |
|
157 Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); |
|
158 calendar.clear(); |
|
159 |
|
160 calendar.setTimeInMillis(kerberosTime); |
|
161 return String.format("%04d%02d%02d%02d%02d%02dZ", |
|
162 calendar.get(Calendar.YEAR), |
|
163 calendar.get(Calendar.MONTH) + 1, |
|
164 calendar.get(Calendar.DAY_OF_MONTH), |
|
165 calendar.get(Calendar.HOUR_OF_DAY), |
|
166 calendar.get(Calendar.MINUTE), |
|
167 calendar.get(Calendar.SECOND)); |
|
168 } |
|
169 |
|
170 /** |
|
171 * Encodes this object to a byte array. |
|
172 * @return a byte array of encoded data. |
|
173 * @exception Asn1Exception if an error occurs while decoding an ASN1 encoded data. |
|
174 * @exception IOException if an I/O error occurs while reading encoded data. |
|
175 */ |
|
176 public byte[] asn1Encode() throws Asn1Exception, IOException { |
|
177 DerOutputStream out = new DerOutputStream(); |
|
178 out.putGeneralizedTime(this.toDate()); |
|
179 return out.toByteArray(); |
|
180 } |
|
181 |
|
182 public long getTime() { |
|
183 return kerberosTime; |
|
184 } |
|
185 |
|
186 public Date toDate() { |
|
187 return new Date(kerberosTime); |
231 } |
188 } |
232 |
189 |
233 public int getMicroSeconds() { |
190 public int getMicroSeconds() { |
234 Long temp_long = new Long((kerberosTime % 1000L) * 1000L); |
191 Long temp_long = new Long((kerberosTime % 1000L) * 1000L); |
235 return temp_long.intValue() + microSeconds; |
192 return temp_long.intValue() + microSeconds; |
236 } |
193 } |
237 |
194 |
238 public void setMicroSeconds(int usec) { |
195 /** |
239 microSeconds = usec % 1000; |
196 * Returns a new KerberosTime object with the original seconds |
240 Integer temp_int = new Integer(usec); |
197 * and the given microseconds. |
241 long temp_long = temp_int.longValue() / 1000L; |
198 */ |
242 kerberosTime = kerberosTime - (kerberosTime % 1000L) + temp_long; |
199 public KerberosTime withMicroSeconds(int usec) { |
243 } |
200 return new KerberosTime( |
244 |
201 kerberosTime - kerberosTime%1000L + usec/1000L, |
245 public void setMicroSeconds(Integer usec) { |
202 usec%1000); |
246 if (usec != null) { |
203 } |
247 microSeconds = usec.intValue() % 1000; |
204 |
248 long temp_long = usec.longValue() / 1000L; |
205 private boolean inClockSkew(int clockSkew) { |
249 kerberosTime = kerberosTime - (kerberosTime % 1000L) + temp_long; |
206 return java.lang.Math.abs(kerberosTime - System.currentTimeMillis()) |
250 } |
207 <= clockSkew * 1000L; |
251 } |
|
252 |
|
253 public boolean inClockSkew(int clockSkew) { |
|
254 KerberosTime now = new KerberosTime(KerberosTime.NOW); |
|
255 |
|
256 if (java.lang.Math.abs(kerberosTime - now.kerberosTime) > |
|
257 clockSkew * 1000L) |
|
258 return false; |
|
259 return true; |
|
260 } |
208 } |
261 |
209 |
262 public boolean inClockSkew() { |
210 public boolean inClockSkew() { |
263 return inClockSkew(getDefaultSkew()); |
211 return inClockSkew(getDefaultSkew()); |
264 } |
|
265 |
|
266 public boolean inClockSkew(int clockSkew, KerberosTime now) { |
|
267 if (java.lang.Math.abs(kerberosTime - now.kerberosTime) > |
|
268 clockSkew * 1000L) |
|
269 return false; |
|
270 return true; |
|
271 } |
|
272 |
|
273 public boolean inClockSkew(KerberosTime time) { |
|
274 return inClockSkew(getDefaultSkew(), time); |
|
275 } |
212 } |
276 |
213 |
277 public boolean greaterThanWRTClockSkew(KerberosTime time, int clockSkew) { |
214 public boolean greaterThanWRTClockSkew(KerberosTime time, int clockSkew) { |
278 if ((kerberosTime - time.kerberosTime) > clockSkew * 1000L) |
215 if ((kerberosTime - time.kerberosTime) > clockSkew * 1000L) |
279 return true; |
216 return true; |
313 } |
250 } |
314 |
251 |
315 public int getSeconds() { |
252 public int getSeconds() { |
316 Long temp_long = new Long(kerberosTime / 1000L); |
253 Long temp_long = new Long(kerberosTime / 1000L); |
317 return temp_long.intValue(); |
254 return temp_long.intValue(); |
318 } |
|
319 |
|
320 public void setSeconds(int sec) { |
|
321 Integer temp_int = new Integer(sec); |
|
322 kerberosTime = temp_int.longValue() * 1000L; |
|
323 } |
255 } |
324 |
256 |
325 /** |
257 /** |
326 * Parse (unmarshal) a kerberostime from a DER input stream. This form |
258 * Parse (unmarshal) a kerberostime from a DER input stream. This form |
327 * parsing might be used when expanding a value which is part of |
259 * parsing might be used when expanding a value which is part of |
328 * a constructed sequence and uses explicitly tagged type. |
260 * a constructed sequence and uses explicitly tagged type. |
329 * |
261 * |
330 * @exception Asn1Exception on error. |
262 * @exception Asn1Exception on error. |
331 * @param data the Der input stream value, which contains one or more marshaled value. |
263 * @param data the Der input stream value, which contains |
|
264 * one or more marshaled value. |
332 * @param explicitTag tag number. |
265 * @param explicitTag tag number. |
333 * @param optional indicates if this data field is optional |
266 * @param optional indicates if this data field is optional |
334 * @return an instance of KerberosTime. |
267 * @return an instance of KerberosTime. |
335 * |
268 * |
336 */ |
269 */ |
337 public static KerberosTime parse(DerInputStream data, byte explicitTag, boolean optional) throws Asn1Exception, IOException { |
270 public static KerberosTime parse( |
|
271 DerInputStream data, byte explicitTag, boolean optional) |
|
272 throws Asn1Exception, IOException { |
338 if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) |
273 if ((optional) && (((byte)data.peekByte() & (byte)0x1F)!= explicitTag)) |
339 return null; |
274 return null; |
340 DerValue der = data.getDerValue(); |
275 DerValue der = data.getDerValue(); |
341 if (explicitTag != (der.getTag() & (byte)0x1F)) { |
276 if (explicitTag != (der.getTag() & (byte)0x1F)) { |
342 throw new Asn1Exception(Krb5.ASN1_BAD_ID); |
277 throw new Asn1Exception(Krb5.ASN1_BAD_ID); |
343 } |
278 } |
344 else { |
279 else { |
345 DerValue subDer = der.getData().getDerValue(); |
280 DerValue subDer = der.getData().getDerValue(); |
346 return new KerberosTime(subDer); |
281 Date temp = subDer.getGeneralizedTime(); |
|
282 return new KerberosTime(temp.getTime(), 0); |
347 } |
283 } |
348 } |
284 } |
349 |
285 |
350 public static int getDefaultSkew() { |
286 public static int getDefaultSkew() { |
351 int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; |
287 int tdiff = Krb5.DEFAULT_ALLOWABLE_CLOCKSKEW; |