Openssl ASN1_时间到时间的转换

Openssl ASN1_时间到时间的转换,openssl,ssl-certificate,asn.1,Openssl,Ssl Certificate,Asn.1,如何将ASN1\u TIME转换为TIME\t格式?我想将X509\u get\u notAfter()的返回值转换为秒。时间在内部以字符串形式存储,格式为yymmddhhmss或YYYYmmddHHMMSS 在字符串的末尾有几秒钟和时区的空间,但是现在我们忽略这一点,使用一些(未测试的)代码 注意:另请参见下面Bryan Olson的回答,其中讨论了由于i++而导致的未定义行为。另请参见Seak的答案,其中删除了未定义的行为 static time_t ASN1_GetTimeT(ASN1_T

如何将
ASN1\u TIME
转换为
TIME\t
格式?我想将
X509\u get\u notAfter()
的返回值转换为秒。

时间在内部以字符串形式存储,格式为
yymmddhhmss
YYYYmmddHHMMSS

在字符串的末尾有几秒钟和时区的空间,但是现在我们忽略这一点,使用一些(未测试的)代码

注意:另请参见下面Bryan Olson的回答,其中讨论了由于
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);
}
static time\t ASN1\u gettime(ASN1\u time*time)
{
struct-tm-t;
常量字符*str=(常量字符*)时间->数据;
尺寸i=0;
memset(&t,0,sizeof(t));
如果(时间->类型==V_ASN1_UTCTIME)/*两位数年份*/
{
t、 tm_year=(str[i++]-'0')*10+(str[++i]-'0');
如果(t.tm_年<70)
t、 tm_年+=100;
}
如果(时间->类型==V_ASN1_广义时间)/*四位数年份*/
{
t、 tm_year=(str[i++]-'0')*1000+(str[++i]-'0')*100+(str[++i]-'0')*10+(str[++i]-'0');
t、 tm_年-=1900;
}
t、 tm_mon=((str[i++]-'0')*10+(str[++i]-'0'))-1;//-1,因为一月是0而不是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');
/*注意:我们没有根据时区信息调整时间*/
返回时间(&t);
}

Jan的答案主要适用于这种情况,但是,累加器
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);
}
static time\t ASN1\u gettime(ASN1\u time*time)
{
struct-tm-t;
常量字符*str=(常量字符*)时间->数据;
尺寸i=0;
memset(&t,0,sizeof(t));
如果(时间->类型==V_ASN1_UTCTIME)/*两位数年份*/
{
t、 tm_year=(str[i++]-'0')*10+(str[i++]-'0');
如果(t.tm_年<70)
t、 tm_年+=100;
}
如果(时间->类型==V_ASN1_广义时间)/*四位数年份*/
{
t、 tm_year=(str[i++]-'0')*1000+(str[i++]-'0')*100+(str[i++]-'0')*10+(str[i++]-'0');
t、 tm_年-=1900;
}
t、 tm_mon=((str[i++]-'0')*10+(str[i++]-'0'))-1;//-1,因为一月是0而不是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');
/*注意:我们没有根据时区信息调整时间*/
返回时间(&t);
}

我不同意简和杰克的观点。有人在我工作的地方复制并使用了给定的代码,但失败了。以下是C99标准的原因:

在上一个序列点和下一个序列点之间,对象应 通过评估最多修改一次其存储值 一种表达方式。” --ISO/IEC 9899:1999,“编程语言-C”,第6.5节,第1条

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

警告:“i”上的操作可能未定义

代码有未定义的行为。我实际看到的错误是“13”年被解读为11年。这是因为:

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

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

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

请阅读“13”中的“1”,因为它们都发生在更新i之前。所有多次更新i的行都有相同的问题

简单的解决方法是去掉“i”,用对sscanf()的一次调用替换所有这些行


即使有这样的修复,我也不喜欢代码。除了忽略时区后缀外,它不会检查错误或意外值。证书是一种安全机制,安全代码对健壮性有严格的要求。您的程序没有正确处理的角落案例就是攻击者填充的案例。

好吧,我不知道其他的,但是对于ASN1_时间是UTCTime格式的情况,代码是错误的:YYMMDDHHMMSZ

我尝试过并返回了错误的值,即使从++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);
}
static time\t ASN1\u gettime(ASN1\u time*time){
struct-tm-t;
常量字符*str=(常量字符*)时间->数据;
尺寸i=0;
memset(&t,0,sizeof(t));
如果(时间->类型==V_ASN1_UTCTIME){/*两位数年份*/
t、 tm_year=(str[i++]-'0')*10;
t、 tm_year+=(str[i++]-'0');
如果(t.tm_年<70)
t、 tm_年+=100;
}如果(time->type==V_ASN1_GENERALIZEDTIME){/*四位数年份*/
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_年-=1900;
}
t、 tm_mon=(str[i++]-'0')*10;
t、 tm_mon+=(str[i++]-'0')-1;//-1,因为一月是0而不是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');
/*注意:我们没有根据时区信息调整时间*/
返回时间(&t);
}

从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.
 */
bool _ASN1_TIME_to_tm(const ASN1_TIME *pTime, struct tm *pTm)
{
    int days = 0, seconds = 0;
    ASN1_TIME *epochTime = ASN1_TIME_new();
    ASN1_TIME_set(epochTime, time_t(0));

    if (!ASN1_TIME_diff(&days, &seconds, epochTime, pTime))
        return false;
    time_t sinceEpoch = time_t(86400LL * days + seconds); // No of seconds in a day = 86400
    gmtime_r(&sinceEpoch, pTm);
    std::cout << "DateTime: " << TOS::convertTmToStr(*pTm) << std::endl;
    ASN1_TIME_free(epochTime);
    return true;
}
bool _ASN1_TIME_to_tm(const ASN1_TIME *pTime, struct tm *pTm)
{
    bool result = false;
    time_t sinceEpoch = 0;
    int days = 0, seconds = 0;
    if (!pTime)
        return false;

    ASN1_TIME *epochTime = ASN1_TIME_new();
    if (!epochTime)
        return false;
    do {
        if (!ASN1_TIME_set(epochTime, time_t(0)))
            break;
        if (!ASN1_TIME_diff(&days, &seconds, epochTime, pTime))
            break;
        // No of seconds in a day = 86400
        sinceEpoch = time_t(86400LL * days + seconds);
        gmtime_r(&sinceEpoch, pTm);
        std::cout << "DateTime: " << TOS::convertTmToStr(*pTm) << std::endl;
        result = true;
    } while (0);

    ASN1_TIME_free(epochTime);
    return result;
}