ASN1_TIME到time_t的转换

use*_*697 15 openssl asn.1 ssl-certificate

我怎样才能转换ASN1_TIMEtime_t格式?我想将返回值转换X509_get_notAfter()为秒.

小智 8

时间在内部,格式YYmmddHHMMSS或存储为字符串YYYYmmddHHMMSS.

在字符串的末尾有几分之一秒和时区的空间,但是现在让我们忽略它,并且有一些(未经测试的)代码.

注意:请参阅下面的Bryan Olson的回答,该回答讨论了由于i++s的未定义行为.另请参阅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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这段代码是错误的,因为它依赖于在同一表达式中使用`i ++`(或`++ i`)的未定义行为:评估顺序是_not_保证. (11认同)

r0r*_*0ro 8

从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.
 */
Run Code Online (Sandbox Code Playgroud)

请注意,您可以使用ASN1_TIME_diff()来获取两个ASN1_TIME*之间的天数/秒.如果从ASN1_TIME*传递NULL,则可以获得与当前时间的差异.


小智 7

好吧,我不知道其余的,但是对于ASN1_TIME采用UTCTime格式的情况,该代码是错误的:YYMMDDHHMMSSZ.

我尝试并返回错误的值,即使从++ i到i ++的更正,然而......代码不是良好编码的示例.

我设法修复它,它是char类型的总和:

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);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我不同意Jan和Jack的观点.实际上有人在我工作的地方复制并使用了给定的代码,但它失败了.这就是为什么,从C99标准:

在前一个和下一个序列点之间,一个对象的存储值最多只能通过表达式的评估修改一次." - ISO/IEC 9899:1999,"编程语言 - C",第6.5节,第1条.

在编译给定代码时,gcc(版本4.1.2)说九次,

警告:'i'上的操作可能未定义.

代码具有未定义的行为.我实际看到的错误是年份"13"被读为11.这是因为:

postfix ++运算符的结果是操作数的值.获得结果后,操作数的值递增.[...]更新操作数的存储值的副作用应发生在前一个和下一个序列点之间. - 同上,第6.5.2.4节,第2条.

str [i ++]的两个实例都在:

t.tm_year =(str [i ++] - '0')*10 +(str [i ++] - '0');

读"13"中的'1',因为它们都发生在i的更新之前.多次更新i的所有行都有相同的问题.

简单的解决方法是摆脱'i'并通过一次调用sscanf()来替换所有这些行.

即使有了这个修复,我也不喜欢这些代码.除了忽略时区后缀之外,它不会检查错误或意外值.证书是一种安全机制,安全代码对健壮性有严格的要求.您的程序未正确处理的极端情况是攻击者填充的情况.