001 package org.apache.commons.ssl.asn1; 002 003 import java.io.IOException; 004 import java.text.ParseException; 005 import java.text.SimpleDateFormat; 006 import java.util.Date; 007 import java.util.SimpleTimeZone; 008 import java.util.TimeZone; 009 010 /** Generalized time object. */ 011 public class DERGeneralizedTime 012 extends ASN1Object { 013 String time; 014 015 /** 016 * return a generalized time from the passed in object 017 * 018 * @throws IllegalArgumentException if the object cannot be converted. 019 */ 020 public static DERGeneralizedTime getInstance( 021 Object obj) { 022 if (obj == null || obj instanceof DERGeneralizedTime) { 023 return (DERGeneralizedTime) obj; 024 } 025 026 if (obj instanceof ASN1OctetString) { 027 return new DERGeneralizedTime(((ASN1OctetString) obj).getOctets()); 028 } 029 030 throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); 031 } 032 033 /** 034 * return a Generalized Time object from a tagged object. 035 * 036 * @param obj the tagged object holding the object we want 037 * @param explicit true if the object is meant to be explicitly 038 * tagged false otherwise. 039 * @throws IllegalArgumentException if the tagged object cannot 040 * be converted. 041 */ 042 public static DERGeneralizedTime getInstance( 043 ASN1TaggedObject obj, 044 boolean explicit) { 045 return getInstance(obj.getObject()); 046 } 047 048 /** 049 * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z 050 * for local time, or Z+-HHMM on the end, for difference between local 051 * time and UTC time. The fractional second amount f must consist of at 052 * least one number with trailing zeroes removed. 053 * 054 * @param time the time string. 055 * @throws IllegalArgumentException if String is an illegal format. 056 */ 057 public DERGeneralizedTime( 058 String time) { 059 this.time = time; 060 try { 061 this.getDate(); 062 } 063 catch (ParseException e) { 064 throw new IllegalArgumentException("invalid date string: " + e.getMessage()); 065 } 066 } 067 068 /** base constructer from a java.util.date object */ 069 public DERGeneralizedTime( 070 Date time) { 071 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); 072 073 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 074 075 this.time = dateF.format(time); 076 } 077 078 DERGeneralizedTime( 079 byte[] bytes) { 080 // 081 // explicitly convert to characters 082 // 083 char[] dateC = new char[bytes.length]; 084 085 for (int i = 0; i != dateC.length; i++) { 086 dateC[i] = (char) (bytes[i] & 0xff); 087 } 088 089 this.time = new String(dateC); 090 } 091 092 /** 093 * Return the time. 094 * 095 * @return The time string as it appeared in the encoded object. 096 */ 097 public String getTimeString() { 098 return time; 099 } 100 101 /** 102 * return the time - always in the form of 103 * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). 104 * <p/> 105 * Normally in a certificate we would expect "Z" rather than "GMT", 106 * however adding the "GMT" means we can just use: 107 * <pre> 108 * dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); 109 * </pre> 110 * To read in the time and get a date which is compatible with our local 111 * time zone. 112 */ 113 public String getTime() { 114 // 115 // standardise the format. 116 // 117 if (time.charAt(time.length() - 1) == 'Z') { 118 return time.substring(0, time.length() - 1) + "GMT+00:00"; 119 } else { 120 int signPos = time.length() - 5; 121 char sign = time.charAt(signPos); 122 if (sign == '-' || sign == '+') { 123 return time.substring(0, signPos) 124 + "GMT" 125 + time.substring(signPos, signPos + 3) 126 + ":" 127 + time.substring(signPos + 3); 128 } else { 129 signPos = time.length() - 3; 130 sign = time.charAt(signPos); 131 if (sign == '-' || sign == '+') { 132 return time.substring(0, signPos) 133 + "GMT" 134 + time.substring(signPos) 135 + ":00"; 136 } 137 } 138 } 139 return time + calculateGMTOffset(); 140 } 141 142 private String calculateGMTOffset() { 143 String sign = "+"; 144 TimeZone timeZone = TimeZone.getDefault(); 145 int offset = timeZone.getRawOffset(); 146 if (offset < 0) { 147 sign = "-"; 148 offset = -offset; 149 } 150 int hours = offset / (60 * 60 * 1000); 151 int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); 152 153 try { 154 if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) { 155 hours += sign.equals("+") ? 1 : -1; 156 } 157 } 158 catch (ParseException e) { 159 // we'll do our best and ignore daylight savings 160 } 161 162 return "GMT" + sign + convert(hours) + ":" + convert(minutes); 163 } 164 165 private String convert(int time) { 166 if (time < 10) { 167 return "0" + time; 168 } 169 170 return Integer.toString(time); 171 } 172 173 public Date getDate() 174 throws ParseException { 175 SimpleDateFormat dateF; 176 String d = time; 177 178 if (time.endsWith("Z")) { 179 if (hasFractionalSeconds()) { 180 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSS'Z'"); 181 } else { 182 dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); 183 } 184 185 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 186 } else if (time.indexOf('-') > 0 || time.indexOf('+') > 0) { 187 d = this.getTime(); 188 if (hasFractionalSeconds()) { 189 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSSz"); 190 } else { 191 dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); 192 } 193 194 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 195 } else { 196 if (hasFractionalSeconds()) { 197 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSS"); 198 } else { 199 dateF = new SimpleDateFormat("yyyyMMddHHmmss"); 200 } 201 202 dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID())); 203 } 204 205 return dateF.parse(d); 206 } 207 208 private boolean hasFractionalSeconds() { 209 return time.indexOf('.') == 14; 210 } 211 212 private byte[] getOctets() { 213 char[] cs = time.toCharArray(); 214 byte[] bs = new byte[cs.length]; 215 216 for (int i = 0; i != cs.length; i++) { 217 bs[i] = (byte) cs[i]; 218 } 219 220 return bs; 221 } 222 223 224 void encode( 225 DEROutputStream out) 226 throws IOException { 227 out.writeEncoded(GENERALIZED_TIME, this.getOctets()); 228 } 229 230 boolean asn1Equals( 231 DERObject o) { 232 if (!(o instanceof DERGeneralizedTime)) { 233 return false; 234 } 235 236 return time.equals(((DERGeneralizedTime) o).time); 237 } 238 239 public int hashCode() { 240 return time.hashCode(); 241 } 242 }