2012-06-11 5 views
12

मैं ASN1_TIME से time_t प्रारूप कैसे परिवर्तित कर सकता हूं? मैं X509_get_notAfter() के रिटर्न वैल्यू को सेकंड में कनवर्ट करना चाहता था।ASN1_TIME समय_टी रूपांतरण

उत्तर

7

टाइम्स को YYmmddHHMMSS या YYYYmmddHHMMSS प्रारूप पर आंतरिक रूप से स्ट्रिंग के रूप में संग्रहीत किया जाता है।

स्ट्रिंग के अंत में सेकंड और टाइमज़ोन के अंशों के लिए जगह होती है, लेकिन आइए अब इसे अनदेखा करें, और कुछ (अनचाहे) कोड रखें।

नोट: भी नीचे ब्रायन ओल्सन के जवाब है, जो 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); 
} 
+8

यह कोड गलत है क्योंकि यह एक ही अभिव्यक्ति में 'i ++' (या '++ i') होने के अपरिभाषित व्यवहार पर निर्भर करता है: मूल्यांकन का क्रम _not_ गारंटीकृत है। –

1

जनवरी का जवाब ज्यादातर इस स्थिति में काम करता है, हालांकि, संचायक 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); 
} 
+0

i ++ का तात्पर्य है कि कथन पूरा होने के बाद मुझे वृद्धि हुई है। इसका मतलब है कि साल के लिए कहें, यह 2014 है, यह टीएम स्ट्रक्चर के अंदर 322 (2222) होता है, इसलिए सही उत्तर मैं पहले के लिए ++ करता हूं, और ++ मैं प्रत्येक के बाद। –

+3

नहीं, आपका कोड _still_ गलत है। मूल्यांकन के क्रम के बाद आप बस एक ही अभिव्यक्ति में एक से अधिक 'i ++' नहीं कर सकते हैं (बाएं से दाएं बनाम दाएं से बाएं)। –

3

मैं जनवरी और जैक से सहमत नहीं है। किसी ने वास्तव में कॉपी किया और दिए गए कोड का उपयोग किया जहां मैं काम करता हूं, और यह विफल रहता है।

पिछले और अगले अनुक्रम बिंदु के बीच

एक वस्तु अपने संग्रहीत मूल्य से अधिक एक बार एक अभिव्यक्ति के मूल्यांकन द्वारा संशोधित किया है करेगा " - आईएसओ/आईईसी 9899: यहाँ क्यों, C99 मानक से है।: 1999, "प्रोग्रामिंग भाषाओं - सी", खंड 6.5, खण्ड 1.

जब दी कोड संकलन, जीसीसी (संस्करण 4.1.2) का कहना है, नौ बार,

चेतावनी: आपरेशन 'पर मैं अनिश्चित हो सकता है एड

कोड में अपरिभाषित व्यवहार है। वास्तव में मैंने देखा कि बग वर्ष "13" 11 के रूप में पढ़ा जा रहा था। ऐसा इसलिए है क्योंकि:

पोस्टफिक्स ++ ऑपरेटर का परिणाम ऑपरेंड का मूल्य है। परिणाम प्राप्त होने के बाद, ऑपरेंड का मूल्य बढ़ जाता है। [...] ऑपरेंड के संग्रहीत मूल्य को अपडेट करने का दुष्प्रभाव पिछले और अगले अनुक्रम बिंदु के बीच होता है। - वही, धारा 6.5.2.4, खण्ड 2.

str के दोनों उदाहरणों [i ++] में:

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

"13" में '1' पढ़ें, क्योंकि वे दोनों मेरे अपडेट से पहले हुए थे। सभी लाइनें जो मुझे कई बार अपडेट करती हैं, वही समस्याएं होती हैं।

आसान फिक्स 'i' से छुटकारा पाने के लिए और उन सभी पंक्तियों को एक कॉल के साथ sscanf() में प्रतिस्थापित करना है।

यहां तक ​​कि उस फिक्स के साथ, मुझे कोड पसंद नहीं होगा।टाइमज़ोन प्रत्यय को अनदेखा करने के अलावा, यह त्रुटियों या अप्रत्याशित मानों की जांच नहीं करता है। सर्टिफिकेट एक सुरक्षा तंत्र हैं, और सुरक्षा कोड में मजबूती के लिए सख्त आवश्यकताएं हैं। कोने के मामले जो आपके प्रोग्राम को सही ढंग से संभाला नहीं जाता है वे हैं जिन पर आपके हमलावर इसे भरते हैं।

+0

* "आसान फिक्स 'i' से छुटकारा पाने के लिए है और उन सभी पंक्तियों को एक कॉल के साथ sscanf()" * में बदलना है - आपको शायद एक उदाहरण प्रदान करना चाहिए क्योंकि इसका उपयोग गलत तरीके से 'sscanf' का उपयोग करना आसान है। इसका कोई मतलब नहीं है कि यह किसी अन्य के लिए एक बग का व्यापार कर रहा है। – jww

6

ठीक है, मुझे बाकी के बारे में पता नहीं है, लेकिन यह कोड केवल उन मामलों के लिए गलत है, 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); 
} 
+0

आरएफसी 5280 का कहना है कि 1- इनपुट समय यूटीसी में है और इसलिए 'mktime()' यहां गलत परिणाम लौटा सकता है ('mktime() 'स्थानीय टाइमज़ोन में इनपुट समय की अपेक्षा करता है)। 2- 'वाई वाई> = 50' का अर्थ '1 9YY' 3-' 99 991231235959Z' के रूप में किया जाएगा एक विशेष मूल्य है। यहां एक [कोड उदाहरण है कि इन मुद्दों को कैसे ठीक किया जा सकता है] (https://stackoverflow.com/a/47015958/4279)। – jfs

4

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 * के बीच दिनों/सेकंड की संख्या प्राप्त करने के लिए ASN1_TIME_diff() का उपयोग करें। यदि आप NULL को ASN1_TIME * से पास करते हैं, तो आप वर्तमान समय से अंतर प्राप्त कर सकते हैं।

1

time_t में ASN1_TIME की तुलना में एक संक्षिप्त सीमा हो सकती है और इसलिए ASN1_TIME_* फ़ंक्शन अधिक मजबूत विकल्प हो सकते हैं। उदाहरण के लिए, समय की तुलना करने के लिए, आप ASN1_TIME_diff() का उपयोग कर सकते हैं (यदि time_t का उपयोग किया जाता है तो यह ओवरफ़्लो के साथ संभावित सुरक्षा समस्याओं से बचाता है)। एक मानव पठनीय प्रारूप में मुद्रित करने के लिए फोन ASN1_TIME_print(), आदि

अब तक जवाब में से कोई भी पालन rfc 5280 जो यह बताता है कि इनपुट समय UTC में हैं (mktime() स्थानीय समय क्षेत्र यानी में समय की उम्मीद है, जवाब है, तो सही नहीं हैं स्थानीय समय क्षेत्र यूटीसी नहीं है)। Also:

अनुरूप प्रणालियों चाहिए साल क्षेत्र (YY) के रूप में इस व्याख्या: कहाँ YY से अधिक या 50 के बराबर है, साल 19YY रूप में व्याख्या की जाएगी; और जहां वाई वाई 50 से कम है, वर्ष 20YY के रूप में व्याख्या किया जाएगा।

i.e., if (tm_year < 70) tm_year += 100; आरएफसी का उल्लंघन करता है। यह उत्तर year += year < 50 ? 2000 : 1900 का उपयोग करता है।

इसके अतिरिक्त, 99991231235959Z in the input का अर्थ है कि प्रमाणपत्र में कोई अच्छी तरह परिभाषित समाप्ति तिथि नहीं है (फ़ंक्शन (time_t)-1 - एक त्रुटि लौटाएगा)।

seconds since Epoch (time_t) को UTCTime या GeneralizedTime तार (ASN1_TIME*) बदलने के लिए:

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 समय परिवर्तित करने के लिए प्रयोग किया जाता है। Seconds Since the Epoch:

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's answer से कर रहे हैं।

संबंधित मुद्दे