using System; using System.Globalization; using System.Text; namespace Org.BouncyCastle.Asn1 { /** * Generalized time object. */ public class DerGeneralizedTime : Asn1Object { private readonly string time; /** * return a generalized time from the passed in object * * @exception ArgumentException if the object cannot be converted. */ public static DerGeneralizedTime GetInstance( object obj) { if (obj == null || obj is DerGeneralizedTime) { return (DerGeneralizedTime)obj; } if (obj is Asn1OctetString) { return new DerGeneralizedTime(((Asn1OctetString)obj).GetOctets()); } throw new ArgumentException("illegal object in GetInstance: " + obj.GetType().Name, "obj"); } /** * return a Generalized Time object from a tagged object. * * @param obj the tagged object holding the object we want * @param explicitly true if the object is meant to be explicitly * tagged false otherwise. * @exception ArgumentException if the tagged object cannot * be converted. */ public static DerGeneralizedTime GetInstance( Asn1TaggedObject obj, bool explicitly) { return GetInstance(obj.GetObject()); } /** * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z * for local time, or Z+-HHMM on the end, for difference between local * time and UTC time. The fractional second amount f must consist of at * least one number with trailing zeroes removed. * * @param time the time string. * @exception ArgumentException if string is an illegal format. */ public DerGeneralizedTime( string time) { this.time = time; try { ToDateTime(); } catch (FormatException e) { throw new ArgumentException("invalid date string: " + e.Message); } } /** * base constructor from a local time object */ public DerGeneralizedTime( DateTime time) { this.time = time.ToString(@"yyyyMMddHHmmss\Z"); } internal DerGeneralizedTime( byte[] bytes) { // // explicitly convert to characters // this.time = Encoding.ASCII.GetString(bytes, 0, bytes.Length); } /** * Return the time. * @return The time string as it appeared in the encoded object. */ public string TimeString { get { return time; } } /** * return the time - always in the form of * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). *
* Normally in a certificate we would expect "Z" rather than "GMT", * however adding the "GMT" means we can just use: *
* dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
*
* To read in the time and Get a date which is compatible with our local
* time zone.
*/
public string GetTime()
{
//
// standardise the format.
//
if (time[time.Length - 1] == 'Z')
{
return time.Substring(0, time.Length - 1) + "GMT+00:00";
}
else
{
int signPos = time.Length - 5;
char sign = time[signPos];
if (sign == '-' || sign == '+')
{
return time.Substring(0, signPos)
+ "GMT"
+ time.Substring(signPos, 3)
+ ":"
+ time.Substring(signPos + 3);
}
else
{
signPos = time.Length - 3;
sign = time[signPos];
if (sign == '-' || sign == '+')
{
return time.Substring(0, signPos)
+ "GMT"
+ time.Substring(signPos)
+ ":00";
}
}
}
return time + CalculateGmtOffset();
}
private string CalculateGmtOffset()
{
char sign = '+';
// Note: GetUtcOffset incorporates Daylight Savings offset
int minutes = TimeZone.CurrentTimeZone.GetUtcOffset(ToDateTime()).Minutes;
if (minutes < 0)
{
sign = '-';
minutes = -minutes;
}
int hours = minutes / 60;
minutes %= 60;
return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
}
private static string Convert(
int time)
{
if (time < 10)
{
return "0" + time;
}
return time.ToString();
}
public DateTime ToDateTime()
{
string formatStr;
string d = time;
bool makeUniversal = false;
if (d.EndsWith("Z"))
{
if (HasFractionalSeconds)
{
int fCount = d.Length - d.IndexOf('.') - 2;
formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
}
else
{
formatStr = @"yyyyMMddHHmmss\Z";
}
}
else if (time.IndexOf('-') > 0 || time.IndexOf('+') > 0)
{
d = GetTime();
makeUniversal = true;
if (HasFractionalSeconds)
{
int fCount = d.IndexOf("GMT") - 1 - d.IndexOf('.');
formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz";
}
else
{
formatStr = @"yyyyMMddHHmmss'GMT'zzz";
}
}
else
{
if (HasFractionalSeconds)
{
int fCount = d.Length - 1 - d.IndexOf('.');
formatStr = @"yyyyMMddHHmmss." + FString(fCount);
}
else
{
formatStr = @"yyyyMMddHHmmss";
}
// TODO?
// dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
}
return ParseDateString(d, formatStr, makeUniversal);
}
private string FString(
int count)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; ++i)
{
sb.Append('f');
}
return sb.ToString();
}
private DateTime ParseDateString(
string dateStr,
string formatStr,
bool makeUniversal)
{
DateTime dt = DateTime.ParseExact(
dateStr,
formatStr,
DateTimeFormatInfo.InvariantInfo);
return makeUniversal ? dt.ToUniversalTime() : dt;
}
private bool HasFractionalSeconds
{
get { return time.IndexOf('.') == 14; }
}
private byte[] GetOctets()
{
return Encoding.ASCII.GetBytes(time);
}
internal override void Encode(
DerOutputStream derOut)
{
derOut.WriteEncoded(Asn1Tags.GeneralizedTime, GetOctets());
}
protected override bool Asn1Equals(
Asn1Object asn1Object)
{
DerGeneralizedTime other = asn1Object as DerGeneralizedTime;
if (other == null)
return false;
return this.time.Equals(other.time);
}
protected override int Asn1GetHashCode()
{
return time.GetHashCode();
}
}
}