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 009 /** UTC time object. */ 010 public class DERUTCTime 011 extends ASN1Object { 012 String time; 013 014 /** 015 * return an UTC Time from the passed in object. 016 * 017 * @throws IllegalArgumentException if the object cannot be converted. 018 */ 019 public static DERUTCTime getInstance( 020 Object obj) { 021 if (obj == null || obj instanceof DERUTCTime) { 022 return (DERUTCTime) obj; 023 } 024 025 if (obj instanceof ASN1OctetString) { 026 return new DERUTCTime(((ASN1OctetString) obj).getOctets()); 027 } 028 029 throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); 030 } 031 032 /** 033 * return an UTC Time from a tagged object. 034 * 035 * @param obj the tagged object holding the object we want 036 * @param explicit true if the object is meant to be explicitly 037 * tagged false otherwise. 038 * @throws IllegalArgumentException if the tagged object cannot 039 * be converted. 040 */ 041 public static DERUTCTime getInstance( 042 ASN1TaggedObject obj, 043 boolean explicit) { 044 return getInstance(obj.getObject()); 045 } 046 047 /** 048 * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were 049 * never encoded. When you're creating one of these objects from scratch, that's 050 * what you want to use, otherwise we'll try to deal with whatever gets read from 051 * the input stream... (this is why the input format is different from the getTime() 052 * method output). 053 * <p/> 054 * 055 * @param time the time string. 056 */ 057 public DERUTCTime( 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 DERUTCTime( 070 Date time) { 071 SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'"); 072 073 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 074 075 this.time = dateF.format(time); 076 } 077 078 DERUTCTime( 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 as a date based on whatever a 2 digit year will return. For 094 * standardised processing use getAdjustedDate(). 095 * 096 * @return the resulting date 097 * @throws ParseException if the date string cannot be parsed. 098 */ 099 public Date getDate() 100 throws ParseException { 101 SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz"); 102 103 return dateF.parse(getTime()); 104 } 105 106 /** 107 * return the time as an adjusted date 108 * in the range of 1950 - 2049. 109 * 110 * @return a date in the range of 1950 to 2049. 111 * @throws ParseException if the date string cannot be parsed. 112 */ 113 public Date getAdjustedDate() 114 throws ParseException { 115 SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); 116 117 dateF.setTimeZone(new SimpleTimeZone(0, "Z")); 118 119 return dateF.parse(getAdjustedTime()); 120 } 121 122 /** 123 * return the time - always in the form of 124 * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). 125 * <p/> 126 * Normally in a certificate we would expect "Z" rather than "GMT", 127 * however adding the "GMT" means we can just use: 128 * <pre> 129 * dateF = new SimpleDateFormat("yyMMddHHmmssz"); 130 * </pre> 131 * To read in the time and get a date which is compatible with our local 132 * time zone. 133 * <p/> 134 * <b>Note:</b> In some cases, due to the local date processing, this 135 * may lead to unexpected results. If you want to stick the normal 136 * convention of 1950 to 2049 use the getAdjustedTime() method. 137 */ 138 public String getTime() { 139 // 140 // standardise the format. 141 // 142 if (time.indexOf('-') < 0 && time.indexOf('+') < 0) { 143 if (time.length() == 11) { 144 return time.substring(0, 10) + "00GMT+00:00"; 145 } else { 146 return time.substring(0, 12) + "GMT+00:00"; 147 } 148 } else { 149 int index = time.indexOf('-'); 150 if (index < 0) { 151 index = time.indexOf('+'); 152 } 153 String d = time; 154 155 if (index == time.length() - 3) { 156 d += "00"; 157 } 158 159 if (index == 10) { 160 return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); 161 } else { 162 return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); 163 } 164 } 165 } 166 167 /** 168 * return a time string as an adjusted date with a 4 digit year. This goes 169 * in the range of 1950 - 2049. 170 */ 171 public String getAdjustedTime() { 172 String d = this.getTime(); 173 174 if (d.charAt(0) < '5') { 175 return "20" + d; 176 } else { 177 return "19" + d; 178 } 179 } 180 181 private byte[] getOctets() { 182 char[] cs = time.toCharArray(); 183 byte[] bs = new byte[cs.length]; 184 185 for (int i = 0; i != cs.length; i++) { 186 bs[i] = (byte) cs[i]; 187 } 188 189 return bs; 190 } 191 192 void encode( 193 DEROutputStream out) 194 throws IOException { 195 out.writeEncoded(UTC_TIME, this.getOctets()); 196 } 197 198 boolean asn1Equals( 199 DERObject o) { 200 if (!(o instanceof DERUTCTime)) { 201 return false; 202 } 203 204 return time.equals(((DERUTCTime) o).time); 205 } 206 207 public int hashCode() { 208 return time.hashCode(); 209 } 210 211 public String toString() { 212 return time; 213 } 214 }