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 }