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