Home | History | Annotate | Download | only in asn1
      1 package org.bouncycastle.asn1;
      2 
      3 import java.io.IOException;
      4 import java.text.ParseException;
      5 import java.text.SimpleDateFormat;
      6 import java.util.Date;
      7 import java.util.SimpleTimeZone;
      8 import java.util.TimeZone;
      9 
     10 /**
     11  * Generalized time object.
     12  */
     13 public class DERGeneralizedTime
     14     extends ASN1Object
     15 {
     16     String      time;
     17 
     18     /**
     19      * return a generalized time from the passed in object
     20      *
     21      * @exception IllegalArgumentException if the object cannot be converted.
     22      */
     23     public static DERGeneralizedTime getInstance(
     24         Object  obj)
     25     {
     26         if (obj == null || obj instanceof DERGeneralizedTime)
     27         {
     28             return (DERGeneralizedTime)obj;
     29         }
     30 
     31         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
     32     }
     33 
     34     /**
     35      * return a Generalized Time object from a tagged object.
     36      *
     37      * @param obj the tagged object holding the object we want
     38      * @param explicit true if the object is meant to be explicitly
     39      *              tagged false otherwise.
     40      * @exception IllegalArgumentException if the tagged object cannot
     41      *               be converted.
     42      */
     43     public static DERGeneralizedTime getInstance(
     44         ASN1TaggedObject obj,
     45         boolean          explicit)
     46     {
     47         DERObject o = obj.getObject();
     48 
     49         if (explicit || o instanceof DERGeneralizedTime)
     50         {
     51             return getInstance(o);
     52         }
     53         else
     54         {
     55             return new DERGeneralizedTime(((ASN1OctetString)o).getOctets());
     56         }
     57     }
     58 
     59     /**
     60      * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
     61      * for local time, or Z+-HHMM on the end, for difference between local
     62      * time and UTC time. The fractional second amount f must consist of at
     63      * least one number with trailing zeroes removed.
     64      *
     65      * @param time the time string.
     66      * @exception IllegalArgumentException if String is an illegal format.
     67      */
     68     public DERGeneralizedTime(
     69         String  time)
     70     {
     71         this.time = time;
     72         try
     73         {
     74             this.getDate();
     75         }
     76         catch (ParseException e)
     77         {
     78             throw new IllegalArgumentException("invalid date string: " + e.getMessage());
     79         }
     80     }
     81 
     82     /**
     83      * base constructer from a java.util.date object
     84      */
     85     public DERGeneralizedTime(
     86         Date time)
     87     {
     88         SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
     89 
     90         dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
     91 
     92         this.time = dateF.format(time);
     93     }
     94 
     95     DERGeneralizedTime(
     96         byte[]  bytes)
     97     {
     98         //
     99         // explicitly convert to characters
    100         //
    101         char[]  dateC = new char[bytes.length];
    102 
    103         for (int i = 0; i != dateC.length; i++)
    104         {
    105             dateC[i] = (char)(bytes[i] & 0xff);
    106         }
    107 
    108         this.time = new String(dateC);
    109     }
    110 
    111     /**
    112      * Return the time.
    113      * @return The time string as it appeared in the encoded object.
    114      */
    115     public String getTimeString()
    116     {
    117         return time;
    118     }
    119 
    120     /**
    121      * return the time - always in the form of
    122      *  YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
    123      * <p>
    124      * Normally in a certificate we would expect "Z" rather than "GMT",
    125      * however adding the "GMT" means we can just use:
    126      * <pre>
    127      *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
    128      * </pre>
    129      * To read in the time and get a date which is compatible with our local
    130      * time zone.
    131      */
    132     public String getTime()
    133     {
    134         //
    135         // standardise the format.
    136         //
    137         if (time.charAt(time.length() - 1) == 'Z')
    138         {
    139             return time.substring(0, time.length() - 1) + "GMT+00:00";
    140         }
    141         else
    142         {
    143             int signPos = time.length() - 5;
    144             char sign = time.charAt(signPos);
    145             if (sign == '-' || sign == '+')
    146             {
    147                 return time.substring(0, signPos)
    148                     + "GMT"
    149                     + time.substring(signPos, signPos + 3)
    150                     + ":"
    151                     + time.substring(signPos + 3);
    152             }
    153             else
    154             {
    155                 signPos = time.length() - 3;
    156                 sign = time.charAt(signPos);
    157                 if (sign == '-' || sign == '+')
    158                 {
    159                     return time.substring(0, signPos)
    160                         + "GMT"
    161                         + time.substring(signPos)
    162                         + ":00";
    163                 }
    164             }
    165         }
    166         return time + calculateGMTOffset();
    167     }
    168 
    169     private String calculateGMTOffset()
    170     {
    171         String sign = "+";
    172         TimeZone timeZone = TimeZone.getDefault();
    173         int offset = timeZone.getRawOffset();
    174         if (offset < 0)
    175         {
    176             sign = "-";
    177             offset = -offset;
    178         }
    179         int hours = offset / (60 * 60 * 1000);
    180         int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000);
    181 
    182         try
    183         {
    184             if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate()))
    185             {
    186                 hours += sign.equals("+") ? 1 : -1;
    187             }
    188         }
    189         catch (ParseException e)
    190         {
    191             // we'll do our best and ignore daylight savings
    192         }
    193 
    194         return "GMT" + sign + convert(hours) + ":" + convert(minutes);
    195     }
    196 
    197     private String convert(int time)
    198     {
    199         if (time < 10)
    200         {
    201             return "0" + time;
    202         }
    203 
    204         return Integer.toString(time);
    205     }
    206 
    207     public Date getDate()
    208         throws ParseException
    209     {
    210         SimpleDateFormat dateF;
    211         String d = time;
    212 
    213         if (time.endsWith("Z"))
    214         {
    215             if (hasFractionalSeconds())
    216             {
    217                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'");
    218             }
    219             else
    220             {
    221                 dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
    222             }
    223 
    224             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
    225         }
    226         else if (time.indexOf('-') > 0 || time.indexOf('+') > 0)
    227         {
    228             d = this.getTime();
    229             if (hasFractionalSeconds())
    230             {
    231                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz");
    232             }
    233             else
    234             {
    235                 dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
    236             }
    237 
    238             dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
    239         }
    240         else
    241         {
    242             if (hasFractionalSeconds())
    243             {
    244                 dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS");
    245             }
    246             else
    247             {
    248                 dateF = new SimpleDateFormat("yyyyMMddHHmmss");
    249             }
    250 
    251             dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
    252         }
    253 
    254         if (hasFractionalSeconds())
    255         {
    256             // java misinterprets extra digits as being milliseconds...
    257             String frac = d.substring(14);
    258             int    index;
    259             for (index = 1; index < frac.length(); index++)
    260             {
    261                 char ch = frac.charAt(index);
    262                 if (!('0' <= ch && ch <= '9'))
    263                 {
    264                     break;
    265                 }
    266             }
    267 
    268             if (index - 1 > 3)
    269             {
    270                 frac = frac.substring(0, 4) + frac.substring(index);
    271                 d = d.substring(0, 14) + frac;
    272             }
    273             else if (index - 1 == 1)
    274             {
    275                 frac = frac.substring(0, index) + "00" + frac.substring(index);
    276                 d = d.substring(0, 14) + frac;
    277             }
    278             else if (index - 1 == 2)
    279             {
    280                 frac = frac.substring(0, index) + "0" + frac.substring(index);
    281                 d = d.substring(0, 14) + frac;
    282             }
    283         }
    284 
    285         return dateF.parse(d);
    286     }
    287 
    288     private boolean hasFractionalSeconds()
    289     {
    290         return time.indexOf('.') == 14;
    291     }
    292 
    293     private byte[] getOctets()
    294     {
    295         char[]  cs = time.toCharArray();
    296         byte[]  bs = new byte[cs.length];
    297 
    298         for (int i = 0; i != cs.length; i++)
    299         {
    300             bs[i] = (byte)cs[i];
    301         }
    302 
    303         return bs;
    304     }
    305 
    306 
    307     void encode(
    308         DEROutputStream  out)
    309         throws IOException
    310     {
    311         out.writeEncoded(GENERALIZED_TIME, this.getOctets());
    312     }
    313 
    314     boolean asn1Equals(
    315         DERObject  o)
    316     {
    317         if (!(o instanceof DERGeneralizedTime))
    318         {
    319             return false;
    320         }
    321 
    322         return time.equals(((DERGeneralizedTime)o).time);
    323     }
    324 
    325     public int hashCode()
    326     {
    327         return time.hashCode();
    328     }
    329 }
    330