¿Cómo puedo convertir ASN1_TIME
en el formato time_t
? Quería convertir el valor de retorno de X509_get_notAfter()
en segundos.ASN1_TIME to time_t conversión
Respuesta
Las horas se almacenan como una cadena internamente, en el formato YYmmddHHMMSS
o YYYYmmddHHMMSS
.
Al final de la cadena hay espacio para fracciones de segundos y zona horaria, pero ignorémoslo por el momento y tengamos algún código (no probado).
Nota: ver también la respuesta de Bryan Olson a continuación, que analiza el comportamiento indefinido debido a las i++
's. También vea la respuesta de Seak, que elimina el comportamiento indefinido.
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);
}
respuesta de Jan trabaja sobre todo en esta situación, sin embargo, el acumulador i
debe utilizar constantemente 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);
}
i ++ implica que i se incrementa DESPUÉS de que la declaración se complete. Esto significa que para el año, digamos que es 2014, esto termina siendo 322 (2222) dentro de la estructura tm, por lo que la respuesta correcta es i ++ para la primera y ++ i para cada subsecuente. –
No, su código está _totalmente equivocado. Simplemente no puede tener más de un 'i ++' en la misma expresión, ya que no se garantiza el orden de evaluación (de izquierda a derecha o de derecha a izquierda). –
puedo estar de acuerdo con Jan y Jack. Alguien realmente copió y usó el código dado donde yo trabajo, y falla. He aquí por qué, de la norma C99:
entre el anterior y el siguiente punto de la secuencia de un objeto se tener su valor almacenado modificado como máximo una vez por la evaluación de una expresión " - ISO/IEC 9899.: 1999, "Lenguajes de programación - C", Sección 6.5, Cláusula 1.
al compilar el código dado, gcc (versión 4.1.2) dice, nueve veces,
aDVERTENCIA: la operación de ' puedo estar indefinido ed.
El código tiene un comportamiento indefinido. El error que realmente vi fue el año "13" leído como 11. Eso es porque:
El resultado del operador postfix ++ es el valor del operando. Después de obtener el resultado, el valor del operando se incrementa. [...] El efecto secundario de actualizar el valor almacenado del operando será entre el punto de secuencia anterior y el siguiente. - Ibid, Sección 6.5.2.4, Cláusula 2.
ambas instancias de str [i ++] en:
t.tm_year = (str [i ++] - '0') * 10 + (str [i ++] - '0');
lee el '1' en "13", porque ambos ocurrieron antes de la actualización de i. Todas las líneas que se actualizan varias veces tienen los mismos problemas.
La solución más fácil es deshacerse de 'i' y reemplazar todas esas líneas con una sola llamada a sscanf().
Incluso con esa corrección, no me gustaría el código.Además de ignorar un sufijo de zona horaria, no verifica si hay errores o valores inesperados. Los certificados son un mecanismo de seguridad y el código de seguridad tiene requisitos estrictos de solidez. Los casos de esquina que tu programa no maneja correctamente son los que tus atacantes llenan.
* "La solución más fácil es deshacerse de 'i' y reemplazar todas esas líneas con una sola llamada a sscanf()" * - probablemente debería proporcionar un ejemplo ya que es fácil de usar 'sscanf' incorrectamente. No tiene sentido que intercambie un error por otro. – jww
Bueno, no sé sobre el resto, pero ese código es simplemente incorrecto para los casos en que el ASN1_TIME está en formato UTCTime: YYMMDDHHMMSSZ.
He intentado y devuelve el valor incorrecto, incluso con la corrección de ++ i a i ++, sin embargo ... el código no es un ejemplo de buena codificación.
me las arreglo para solucionarlo, que era la suma de los tipos 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);
}
rfc 5280 dice que 1- el tiempo de entrada está en UTC y por lo tanto 'mktime()' puede devolver un resultado incorrecto aquí ('mktime()' espera el tiempo de entrada en la zona horaria local). 2- 'YY> = 50' se interpretará como' 19YY' 3- '99991231235959Z' es un valor especial. Aquí hay un [ejemplo de código de cómo se pueden solucionar estos problemas] (https://stackoverflow.com/a/47015958/4279). – jfs
a partir del código OpenSSL, que parece ser una mala idea:
/*
* 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.
*/
en cuenta que puede use ASN1_TIME_diff() para obtener la cantidad de días/segundos entre dos ASN1_TIME *. Si pasa NULL como ASN1_TIME * desde, puede obtener la diferencia de la hora actual.
time_t
pueden tener un rango más estrecho que ASN1_TIME
y por lo tanto ASN1_TIME_*
funciones podrían ser una alternativa más robusta. Por ejemplo, para comparar tiempos, puede usar ASN1_TIME_diff()
(esto evita posibles problemas de seguridad con desbordamiento si se usa time_t
). Para imprimir en un formato legible por humanos, llame ASN1_TIME_print()
, etc.
Hasta el momento ninguna de las respuestas siguen rfc 5280 que especifica que los tiempos de entrada están en UTC (mktime()
espera vez en la zona horaria local, es decir, las respuestas son incorrectas si el la zona horaria local no es UTC). Also:
sistemas conformes debe interpretar el campo año (YY) de la siguiente manera: donde yy es mayor que o igual a 50, el año serán interpretarse como 19aa; y Donde YY es menor que 50, el año SE DEBERÁ interpretar como 20YY.
es decir, if (tm_year < 70) tm_year += 100;
viola el rfc. Esta respuesta usa year += year < 50 ? 2000 : 1900
.
Además, 99991231235959Z
in the input significa que el certificado no tiene una fecha de vencimiento bien definida (la función debe devolver (time_t)-1
- un error).
para convertir cadenas UTCTime o GeneralizedTime (ASN1_TIME*
) a seconds since Epoch (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);
}
donde posix_time()
se utiliza para convertir la hora UTC-rota hasta el tiempo del calendario. 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
y year_for_leap
son de @DTiedy's answer.
- 1. Conversión de fecha/hora: representación de cadena a time_t
- 2. Ruby String to Date Conversión
- 3. Conversión a Loop to LINQ -
- 4. advertencia C4244: 'argumento': conversión de 'time_t' a 'unsigned int', posible pérdida de datos - C++
- 5. JavaDoc to (Doku) Wiki conversión/doclet
- 6. Cómo convertir una variable de cadena que contiene time to time_t escriba C++?
- 7. ¿Cómo extraer horas de time_t?
- 8. Representación de cadena de time_t?
- 9. Conversión de String to Color de Java/Android
- 10. Validar la conversión exitosa de String to enum en Java
- 11. LINQ to SQL - La conversión especificada no es válida - SingleOrDefault()
- 12. ¿Qué tipo de datos primitivos es time_t?
- 13. ¿Cómo convierto boost :: posix_time :: ptime a time_t?
- 14. Convertir cadena de __DATE__ a un time_t
- 15. ¿Es time_t devuelto por time() zona específica?
- 16. Convirtiendo un time_t al objeto NSDate?
- 17. ¿Alguien está haciendo algo acerca de 2038 time_t bug?
- 18. Obteniendo el tipo/tamaño de `time_t` usando ctypes
- 19. Homography to Projective transform
- 20. Convertir boost :: uuid to char *
- 21. lo que es c time_t equivalente para C#
- 22. Cómo leer datos en una variable time_t usando scanf()?
- 23. manera portátil de lidiar con 64/32 bit time_t
- 24. Conversión del tiempo de época a "fecha/hora" real
- 25. C/C++ header to java
- 26. html to .doc converter en Python?
- 27. object to string array
- 28. resultados de conversión de Dip to pixel en el mismo valor exacto
- 29. C++ std :: string to boolean
- 30. Safe-casting text to XML
Este código es incorrecto ya que se basa en el comportamiento indefinido de tener 'i ++' (o '++ i') en la misma expresión: el orden de evaluación no está garantizado. –