形式に変換ASN1_TIME
するにはどうすればよいですか? の戻り値を秒time_t
に変換したかったのです。X509_get_notAfter()
7 に答える
時刻は、YYmmddHHMMSS
またはの形式で内部的に文字列として保存されます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);
}
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()を使用して、2 つの ASN1_TIME* の間の日数 / 秒を取得できることに注意してください。ASN1_TIME *from に NULL を渡すと、現在時刻との差を取得できます。
残りの部分についてはわかりませんが、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);
}
私はジャンとジャックに反対しなければなりません。誰かが実際に私が働いている特定のコードをコピーして使用しましたが、失敗しました。C99標準からの理由は次のとおりです。
前のシーケンス ポイントと次のシーケンス ポイントの間で、オブジェクトの格納値は、式の評価によって最大 1 回変更されます。" -- 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');
どちらも i. i を複数回更新するすべての行には、同じ問題があります。
簡単な修正は、「i」を取り除き、これらすべての行を sscanf() への 1 回の呼び出しに置き換えることです。
その修正があっても、私はコードが好きではありません。タイムゾーン サフィックスを無視するだけでなく、エラーや予期しない値をチェックしません。証明書はセキュリティ メカニズムであり、セキュリティ コードには堅牢性に関する厳しい要件があります。プログラムが正しく処理しないコーナー ケースは、攻撃者がプログラムに与えるものです。
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);
}
手遅れでopenssl
、関数ASN1_TIME_to_tmが導入されていることはわかっていますが、このメソッドを持たない古いバージョンの openssl を使用する必要がありました。
この質問に対するさまざまな回答があり、彼らはコード内の時間文字列を解析していましたが、ロジックの解析で何かを見落としている可能性があり、コードが壊れたり、すべてのコーナー ケースを処理できない可能性があると考えたため、そのアプローチには満足できませんでした。openssl
そのため、変換を行うためだけに関数を使用する C++ 用の関数を実装しました。
ASN1_TIME_diffを使用して、エポックからの秒数を計算します。ASN1_TIME
エポックを取得するために、引数を 0 として渡してASN1_TIME_SETを使用しました。time_t
気軽にコメントしてテストしてください。
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;
}