001package org.apache.commons.ssl.org.bouncycastle.asn1; 002 003import java.io.IOException; 004import java.text.ParseException; 005import java.text.SimpleDateFormat; 006import java.util.Date; 007import java.util.Locale; 008import java.util.SimpleTimeZone; 009 010import org.bouncycastle.util.Arrays; 011import org.bouncycastle.util.Strings; 012 013/** 014- * UTC time object. 015 * Internal facade of {@link ASN1UTCTime}. 016 * <p> 017 * This datatype is valid only from 1950-01-01 00:00:00 UTC until 2049-12-31 23:59:59 UTC. 018 * <p> 019 * <hr> 020 * <p><b>X.690</b></p> 021 * <p><b>11: Restrictions on BER employed by both CER and DER</b></p> 022 * <p><b>11.8 UTCTime </b></p> 023 * <b>11.8.1</b> The encoding shall terminate with "Z", 024 * as described in the ITU-T X.680 | ISO/IEC 8824-1 clause on UTCTime. 025 * <p> 026 * <b>11.8.2</b> The seconds element shall always be present. 027 * <p> 028 * <b>11.8.3</b> Midnight (GMT) shall be represented in the form: 029 * <blockquote> 030 * "YYMMDD000000Z" 031 * </blockquote> 032 * where "YYMMDD" represents the day following the midnight in question. 033 */ 034public class ASN1UTCTime 035 extends ASN1Primitive 036{ 037 private byte[] time; 038 039 /** 040 * return an UTC Time from the passed in object. 041 * 042 * @param obj an ASN1UTCTime or an object that can be converted into one. 043 * @exception IllegalArgumentException if the object cannot be converted. 044 * @return an ASN1UTCTime instance, or null. 045 */ 046 public static ASN1UTCTime getInstance( 047 Object obj) 048 { 049 if (obj == null || obj instanceof ASN1UTCTime) 050 { 051 return (ASN1UTCTime)obj; 052 } 053 054 if (obj instanceof byte[]) 055 { 056 try 057 { 058 return (ASN1UTCTime)fromByteArray((byte[])obj); 059 } 060 catch (Exception e) 061 { 062 throw new IllegalArgumentException("encoding error in getInstance: " + e.toString()); 063 } 064 } 065 066 throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); 067 } 068 069 /** 070 * return an UTC Time from a tagged object. 071 * 072 * @param obj the tagged object holding the object we want 073 * @param explicit true if the object is meant to be explicitly 074 * tagged false otherwise. 075 * @exception IllegalArgumentException if the tagged object cannot 076 * be converted. 077 * @return an ASN1UTCTime instance, or null. 078 */ 079 public static ASN1UTCTime getInstance( 080 ASN1TaggedObject obj, 081 boolean explicit) 082 { 083 ASN1Object o = obj.getObject(); 084 085 if (explicit || o instanceof ASN1UTCTime) 086 { 087 return getInstance(o); 088 } 089 else 090 { 091 return new ASN1UTCTime(((ASN1OctetString)o).getOctets()); 092 } 093 } 094 095 /** 096 * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were 097 * never encoded. When you're creating one of these objects from scratch, that's 098 * what you want to use, otherwise we'll try to deal with whatever gets read from 099 * the input stream... (this is why the input format is different from the getTime() 100 * method output). 101 * <p> 102 * 103 * @param time the time string. 104 */ 105 public ASN1UTCTime( 106 String time) 107 { 108 this.time = Strings.toByteArray(time); 109 try 110 { 111 this.getDate(); 112 } 113 catch (ParseException e) 114 { 115 throw new IllegalArgumentException("invalid date string: " + e.getMessage()); 116 } 117 } 118 119 /** 120 * base constructor from a java.util.date object 121 * @param time the Date to build the time from. 122 */ 123 public ASN1UTCTime( 124 Date time) 125 { 126 SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'"); 127 128 dateF.setTimeZone(new SimpleTimeZone(0,"Z")); 129 130 this.time = Strings.toByteArray(dateF.format(time)); 131 } 132 133 /** 134 * Base constructor from a java.util.date and Locale - you may need to use this if the default locale 135 * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations. 136 * 137 * @param time a date object representing the time of interest. 138 * @param locale an appropriate Locale for producing an ASN.1 UTCTime value. 139 */ 140 public ASN1UTCTime( 141 Date time, 142 Locale locale) 143 { 144 SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", locale); 145 146 dateF.setTimeZone(new SimpleTimeZone(0,"Z")); 147 148 this.time = Strings.toByteArray(dateF.format(time)); 149 } 150 151 ASN1UTCTime( 152 byte[] time) 153 { 154 this.time = time; 155 } 156 157 /** 158 * return the time as a date based on whatever a 2 digit year will return. For 159 * standardised processing use getAdjustedDate(). 160 * 161 * @return the resulting date 162 * @exception ParseException if the date string cannot be parsed. 163 */ 164 public Date getDate() 165 throws ParseException 166 { 167 SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz"); 168 169 return dateF.parse(getTime()); 170 } 171 172 /** 173 * return the time as an adjusted date 174 * in the range of 1950 - 2049. 175 * 176 * @return a date in the range of 1950 to 2049. 177 * @exception ParseException if the date string cannot be parsed. 178 */ 179 public Date getAdjustedDate() 180 throws ParseException 181 { 182 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); 183 184 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 185 186 return dateF.parse(getAdjustedTime()); 187 } 188 189 /** 190 * return the time - always in the form of 191 * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). 192 * <p> 193 * Normally in a certificate we would expect "Z" rather than "GMT", 194 * however adding the "GMT" means we can just use: 195 * <pre> 196 * dateF = new SimpleDateFormat("yyMMddHHmmssz"); 197 * </pre> 198 * To read in the time and get a date which is compatible with our local 199 * time zone. 200 * <p> 201 * <b>Note:</b> In some cases, due to the local date processing, this 202 * may lead to unexpected results. If you want to stick the normal 203 * convention of 1950 to 2049 use the getAdjustedTime() method. 204 */ 205 public String getTime() 206 { 207 String stime = Strings.fromByteArray(time); 208 209 // 210 // standardise the format. 211 // 212 if (stime.indexOf('-') < 0 && stime.indexOf('+') < 0) 213 { 214 if (stime.length() == 11) 215 { 216 return stime.substring(0, 10) + "00GMT+00:00"; 217 } 218 else 219 { 220 return stime.substring(0, 12) + "GMT+00:00"; 221 } 222 } 223 else 224 { 225 int index = stime.indexOf('-'); 226 if (index < 0) 227 { 228 index = stime.indexOf('+'); 229 } 230 String d = stime; 231 232 if (index == stime.length() - 3) 233 { 234 d += "00"; 235 } 236 237 if (index == 10) 238 { 239 return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); 240 } 241 else 242 { 243 return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); 244 } 245 } 246 } 247 248 /** 249 * return a time string as an adjusted date with a 4 digit year. This goes 250 * in the range of 1950 - 2049. 251 */ 252 public String getAdjustedTime() 253 { 254 String d = this.getTime(); 255 256 if (d.charAt(0) < '5') 257 { 258 return "20" + d; 259 } 260 else 261 { 262 return "19" + d; 263 } 264 } 265 266 boolean isConstructed() 267 { 268 return false; 269 } 270 271 int encodedLength() 272 { 273 int length = time.length; 274 275 return 1 + StreamUtil.calculateBodyLength(length) + length; 276 } 277 278 void encode( 279 ASN1OutputStream out) 280 throws IOException 281 { 282 out.write(BERTags.UTC_TIME); 283 284 int length = time.length; 285 286 out.writeLength(length); 287 288 for (int i = 0; i != length; i++) 289 { 290 out.write((byte)time[i]); 291 } 292 } 293 294 boolean asn1Equals( 295 ASN1Primitive o) 296 { 297 if (!(o instanceof ASN1UTCTime)) 298 { 299 return false; 300 } 301 302 return Arrays.areEqual(time, ((ASN1UTCTime)o).time); 303 } 304 305 public int hashCode() 306 { 307 return Arrays.hashCode(time); 308 } 309 310 public String toString() 311 { 312 return Strings.fromByteArray(time); 313 } 314}