我怎么能转换ASN1_TIME
到time_t
格式? 我想的返回值转换X509_get_notAfter()
到秒。
Answer 1:
时代在内部存储为一个字符串,其格式YYmmddHHMMSS
或YYYYmmddHHMMSS
。
在字符串的结尾有余地的秒和时区的部分,但让我们忽略了现在,并且有一定的(未经测试)的代码。
注 :另见下文布莱恩·奥尔森的答案,其中讨论了不确定的行为,由于i++
的。 另见Seak的答案,消除了不确定的行为。
static time_t ASN1_GetTimeT(ASN1_TIME* time)
{
struct tm t;
const char* str = (const char*) time->data;
size_t i = 0;
memset(&t, 0, sizeof(t));
if (time->type == V_ASN1_UTCTIME) /* two digit year */
{
t.tm_year = (str[i++] - '0') * 10 + (str[++i] - '0');
if (t.tm_year < 70)
t.tm_year += 100;
}
else if (time->type == V_ASN1_GENERALIZEDTIME) /* four digit year */
{
t.tm_year = (str[i++] - '0') * 1000 + (str[++i] - '0') * 100 + (str[++i] - '0') * 10 + (str[++i] - '0');
t.tm_year -= 1900;
}
t.tm_mon = ((str[i++] - '0') * 10 + (str[++i] - '0')) - 1; // -1 since January is 0 not 1.
t.tm_mday = (str[i++] - '0') * 10 + (str[++i] - '0');
t.tm_hour = (str[i++] - '0') * 10 + (str[++i] - '0');
t.tm_min = (str[i++] - '0') * 10 + (str[++i] - '0');
t.tm_sec = (str[i++] - '0') * 10 + (str[++i] - '0');
/* Note: we did not adjust the time based on time zone information */
return mktime(&t);
}
Answer 2:
从OpenSSL的代码,这似乎是一个坏主意:
/*
* FIXME: mktime assumes the current timezone
* instead of UTC, and unless we rewrite OpenSSL
* in Lisp we cannot locally change the timezone
* without possibly interfering with other parts
* of the program. timegm, which uses UTC, is
* non-standard.
* Also time_t is inappropriate for general
* UTC times because it may a 32 bit type.
*/
请注意,您可以使用ASN1_TIME_diff()得到两个ASN1_TIME之间的天数/秒数*。 如果您传递NULL作为ASN1_TIME *,你可以从当前的时间差。
Answer 3:
好了,我不知道要休息,但这些代码是错误的情况下,ASN1_TIME是UTCTime格式:YYMMDDHHMMSSZ。
我试图从返回值错误,甚至与校正++ i至i ++,不过...代码不是良好的编码的一个例子。
我设法解决它,这是字符类型的总和:
static time_t ASN1_GetTimeT(ASN1_TIME* time){
struct tm t;
const char* str = (const char*) time->data;
size_t i = 0;
memset(&t, 0, sizeof(t));
if (time->type == V_ASN1_UTCTIME) {/* two digit year */
t.tm_year = (str[i++] - '0') * 10;
t.tm_year += (str[i++] - '0');
if (t.tm_year < 70)
t.tm_year += 100;
} else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
t.tm_year = (str[i++] - '0') * 1000;
t.tm_year+= (str[i++] - '0') * 100;
t.tm_year+= (str[i++] - '0') * 10;
t.tm_year+= (str[i++] - '0');
t.tm_year -= 1900;
}
t.tm_mon = (str[i++] - '0') * 10;
t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1.
t.tm_mday = (str[i++] - '0') * 10;
t.tm_mday+= (str[i++] - '0');
t.tm_hour = (str[i++] - '0') * 10;
t.tm_hour+= (str[i++] - '0');
t.tm_min = (str[i++] - '0') * 10;
t.tm_min += (str[i++] - '0');
t.tm_sec = (str[i++] - '0') * 10;
t.tm_sec += (str[i++] - '0');
/* Note: we did not adjust the time based on time zone information */
return mktime(&t);
}
Answer 4:
我有扬和杰克不同意。 居然有人复制并使用给定的代码,我的工作,和它失败。 这也是为什么,从C99的标准:
前一个和下一个顺序点之间的对象应具有其存储的值由表达式的评估修饰的至多一次“ - ISO / IEC 9899:1999,‘编程语言 - C’,第6.5节,第1项目。
当编译给定的代码,GCC(4.1.2版本)说,九次,
警告:对“我”可以是不明确的操作。
该代码是未定义行为。 其实我看到的错误是今年“13”被解读为11.这是因为:
后缀++操作符的结果是操作数的值。 可获得这样的结果之后,操作数的值被递增。 [...]更新操作数的存储值的副作用应前一和下一序列点之间发生。 - 同上,第6.5.2.4,第2条。
的STR [1 ++]在这两种情况下:
t.tm_year =(STR [I ++] - '0')* 10 +(STR [I ++] - '0');
在“13”读“1”,因为他们都我的更新之前发生的事情。 所有这些更新我多次行有同样的问题。
最简单的解决方法是摆脱“我”,代之以一个呼叫所有这些行的sscanf()。
即使与修复,我不喜欢的代码。 除了忽略了一个时区的后缀,它不检查错误或意外的值。 证书是一种安全机制和安全代码具有稳健性的严格要求。 你的程序不能正确处理极端案例是那些你的攻击填写喂养它。
Answer 5:
扬的回答在这种情况大多是工作,但是,累加器i
应该坚持用i++
:
static time_t ASN1_GetTimeT(ASN1_TIME* time)
{
struct tm t;
const char* str = (const char*) time->data;
size_t i = 0;
memset(&t, 0, sizeof(t));
if (time->type == V_ASN1_UTCTIME) /* two digit year */
{
t.tm_year = (str[i++] - '0') * 10 + (str[i++] - '0');
if (t.tm_year < 70)
t.tm_year += 100;
}
else if (time->type == V_ASN1_GENERALIZEDTIME) /* four digit year */
{
t.tm_year = (str[i++] - '0') * 1000 + (str[i++] - '0') * 100 + (str[i++] - '0') * 10 + (str[i++] - '0');
t.tm_year -= 1900;
}
t.tm_mon = ((str[i++] - '0') * 10 + (str[i++] - '0')) - 1; // -1 since January is 0 not 1.
t.tm_mday = (str[i++] - '0') * 10 + (str[i++] - '0');
t.tm_hour = (str[i++] - '0') * 10 + (str[i++] - '0');
t.tm_min = (str[i++] - '0') * 10 + (str[i++] - '0');
t.tm_sec = (str[i++] - '0') * 10 + (str[i++] - '0');
/* Note: we did not adjust the time based on time zone information */
return mktime(&t);
}
Answer 6:
time_t
可以具有更窄的范围比ASN1_TIME
因此ASN1_TIME_*
函数可能是更健壮的替代方案。 例如,比较的时间,你可以使用ASN1_TIME_diff()
这避免了与溢出可能的安全问题,如果time_t
使用)。 要打印在人类可读的格式,呼叫ASN1_TIME_print()
等等。
到目前为止,没有一个答案遵循RFC 5280指定的输入时间以UTC( mktime()
预计时间在本地时区,即,答案是不正确的,如果本地时区不UTC)。 此外 :
符合系统必须解释年字段(YY)如下:其中YY为大于或等于50,年应解释为19YY; 而当YY不到50,年应解释为20YY。
即, if (tm_year < 70) tm_year += 100;
违反RFC。 这个答案使用year += year < 50 ? 2000 : 1900
year += year < 50 ? 2000 : 1900
。
此外, 99991231235959Z
在输入表示该证书没有明确定义的失效日期(函数应该返回(time_t)-1
-错误)。
要转换UTCTime或GeneralizedTime字符串( ASN1_TIME*
),以纪元以来的秒数 ( time_t
):
typedef unsigned U;
time_t ASN1_TIME_to_posix_time(const ASN1_TIME* time) {
if(!time) return -1;
const char *s = (const char*)time->data;
if (!s) return -1;
U two_digits_to_uint() // nested function: gcc extension
{
U n = 10 * (*s++ - '0');
return n + (*s++ - '0');
}
U year, month, day, hour, min, sec;
switch(time->type) {
// https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
case V_ASN1_UTCTIME: // YYMMDDHHMMSSZ
year = two_digits_to_uint();
year += year < 50 ? 2000 : 1900;
break;
case V_ASN1_GENERALIZEDTIME: // YYYYMMDDHHMMSSZ
year = 100 * two_digits_to_uint();
year += two_digits_to_uint();
break;
default:
return -1; // error
}
month = two_digits_to_uint();
day = two_digits_to_uint();
hour = two_digits_to_uint();
min = two_digits_to_uint();
sec = two_digits_to_uint();
if (*s != 'Z') return -1;
if (year == 9999 && month == 12 && day == 31 && hour == 23 && min == 59
&& sec == 59) // 99991231235959Z rfc 5280
return -1;
return posix_time(year, month, day, hour, min, sec);
}
在那里posix_time()
用于破旧的UTC时间转换为日历时间。 秒由于时代 :
time_t posix_time(U year, U month, U day, U hour, U min, U sec)
{
if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31
|| hour > 23 || min > 59 || sec > 60)
return -1;
// days upto months for non-leap years
static const U month_day[13] =
{-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
year -= 1900;
// number of Februaries since 1900
const U year_for_leap = (month > 2) ? year + 1 : year;
// XXX may overflow
return sec + min*60 + hour*3600 + (month_day[month] + day - 1)*86400 +
(year-70)*31536000 + ((year_for_leap-69)/4)*86400 -
((year_for_leap-1)/100)*86400 + ((year_for_leap+299)/400)*86400;
}
month_day
和year_for_leap
来自@ DTiedy的答案 。