1
0
mirror of https://github.com/danog/phpseclib.git synced 2024-12-03 18:18:05 +01:00

Merge branch 'datetime-2.0' into datetime-master

This commit is contained in:
terrafrost 2017-08-24 12:50:47 -05:00
commit c43ebe3a0a
2 changed files with 57 additions and 35 deletions

View File

@ -27,6 +27,8 @@ use ParagonIE\ConstantTime\Base64;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use phpseclib\Common\Functions\Strings;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP ASN.1 Parser
@ -739,7 +741,7 @@ abstract class ASN1
if (isset($mapping['implicit'])) {
$decoded['content'] = self::decodeTime($decoded['content'], $decoded['type']);
}
return @date(self::$format, $decoded['content']);
return $decoded['content'] ? $decoded['content']->format(self::$format) : false;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
$offset = ord($decoded['content'][0]);
@ -989,7 +991,8 @@ abstract class ASN1
case self::TYPE_GENERALIZED_TIME:
$format = $mapping['type'] == self::TYPE_UTC_TIME ? 'y' : 'Y';
$format.= 'mdHis';
$value = @gmdate($format, strtotime($source)) . 'Z';
$date = new DateTime($source, new DateTimeZone('GMT'));
$value = $date->format($format) . 'Z';
break;
case self::TYPE_BIT_STRING:
if (isset($mapping['mapping'])) {
@ -1151,33 +1154,32 @@ abstract class ASN1
http://tools.ietf.org/html/rfc5280#section-4.1.2.5.2
http://www.obj-sys.com/asn1tutorial/node14.html */
$pattern = $tag == self::TYPE_UTC_TIME ?
'#^(..)(..)(..)(..)(..)(..)?(.*)$#' :
'#(....)(..)(..)(..)(..)(..).*([Z+-].*)$#';
preg_match($pattern, $content, $matches);
list(, $year, $month, $day, $hour, $minute, $second, $timezone) = $matches;
$format = 'YmdHis';
if ($tag == self::TYPE_UTC_TIME) {
$year = $year >= 50 ? "19$year" : "20$year";
// https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#page=28 says "the seconds
// element shall always be present" but none-the-less I've seen X509 certs where it isn't and if the
// browsers parse it phpseclib ought to too
if (preg_match('#^(\d{10})(Z|[+-]\d{4})$#', $content, $matches)) {
$content = $matches[1] . '00' . $matches[2];
}
$prefix = substr($content, 0, 2) >= 50 ? '19' : '20';
$content = $prefix . $content;
} elseif (strpos($content, '.') !== false) {
$format.= '.u';
}
if ($timezone == 'Z') {
$mktime = 'gmmktime';
$timezone = 0;
} elseif (preg_match('#([+-])(\d\d)(\d\d)#', $timezone, $matches)) {
$mktime = 'gmmktime';
$timezone = 60 * $matches[3] + 3600 * $matches[2];
if ($matches[1] == '-') {
$timezone = -$timezone;
}
} else {
$mktime = 'mktime';
$timezone = 0;
if ($content[strlen($content) - 1] == 'Z') {
$content = substr($content, 0, -1) . '+0000';
}
return @$mktime((int)$hour, (int)$minute, (int)$second, (int)$month, (int)$day, (int)$year) + $timezone;
if (strpos($content, '-') !== false || strpos($content, '+') !== false) {
$format.= 'O';
}
// error supression isn't necessary as of PHP 7.0:
// http://php.net/manual/en/migration70.other-changes.php
return @DateTime::createFromFormat($format, $content);
}
/**

View File

@ -35,7 +35,8 @@ use phpseclib\Exception\UnsupportedAlgorithmException;
use phpseclib\File\ASN1\Element;
use phpseclib\Math\BigInteger;
use phpseclib\File\ASN1\Maps;
use DateTime;
use DateTimeZone;
/**
* Pure-PHP X.509 Parser
@ -1061,7 +1062,7 @@ class X509
}
if (!isset($date)) {
$date = time();
$date = new DateTime($date, new DateTimeZone(date_default_timezone_get()));
}
$notBefore = $this->currentCert['tbsCertificate']['validity']['notBefore'];
@ -1071,8 +1072,8 @@ class X509
$notAfter = isset($notAfter['generalTime']) ? $notAfter['generalTime'] : $notAfter['utcTime'];
switch (true) {
case $date < @strtotime($notBefore):
case $date > @strtotime($notAfter):
case $date < new DateTime($notBefore, new DateTimeZone(@date_default_timezone_get())):
case $date > new DateTime($notAfter, new DateTimeZone(@date_default_timezone_get())):
return false;
}
@ -2290,7 +2291,11 @@ class X509
*/
private function timeField($date)
{
$year = @gmdate("Y", @strtotime($date)); // the same way ASN1.php parses this
if ($date instanceof Element) {
return $date;
}
$dateObj = new DateTime($date, new DateTimeZone('GMT'));
$year = $dateObj->format('Y'); // the same way ASN1.php parses this
if ($year < 2050) {
return ['utcTime' => $date];
} else {
@ -2355,8 +2360,12 @@ class X509
return false;
}
$startDate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$endDate = !empty($this->endDate) ? $this->endDate : @date('D, d M Y H:i:s O', strtotime('+1 year'));
$startDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$startDate = !empty($this->startDate) ? $this->startDate : $startDate->format('D, d M Y H:i:s O');
$endDate = new DateTime('+1 year', new DateTimeZone(@date_default_timezone_get()));
$endDate = !empty($this->endDate) ? $this->endDate : $endDate->format('D, d M Y H:i:s O');
/* "The serial number MUST be a positive integer"
"Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
-- https://tools.ietf.org/html/rfc5280#section-4.1.2.2
@ -2624,7 +2633,9 @@ class X509
$currentCert = isset($this->currentCert) ? $this->currentCert : null;
$signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : null;
$thisUpdate = !empty($this->startDate) ? $this->startDate : @date('D, d M Y H:i:s O');
$thisUpdate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$thisUpdate = !empty($this->startDate) ? $this->startDate : $thisUpdate->format('D, d M Y H:i:s O');
if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) {
$this->currentCert = $crl->currentCert;
@ -2777,7 +2788,11 @@ class X509
*/
public function setStartDate($date)
{
$this->startDate = @date('D, d M Y H:i:s O', @strtotime($date));
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date);
}
$this->startDate = $date->format('D, d M Y H:i:s O');
}
/**
@ -2800,7 +2815,11 @@ class X509
$temp = chr(ASN1::TYPE_GENERALIZED_TIME) . ASN1::encodeLength(strlen($temp)) . $temp;
$this->endDate = new Element($temp);
} else {
$this->endDate = @date('D, d M Y H:i:s O', @strtotime($date));
if (!is_object($date) || !is_a($date, 'DateTime')) {
$date = new DateTime($date);
}
$this->endDate = $date->format('D, d M Y H:i:s O');
}
}
@ -3530,8 +3549,9 @@ class X509
}
$i = count($rclist);
$revocationDate = new DateTime('now', new DateTimeZone(@date_default_timezone_get()));
$rclist[] = ['userCertificate' => $serial,
'revocationDate' => $this->timeField(@date('D, d M Y H:i:s O'))];
'revocationDate' => $this->timeField($revocationDate->format('D, d M Y H:i:s O'))];
return $i;
}