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 }