mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-11-05 21:17:53 +00:00
ASN1,X509: add support for ip addresses in subjaltname
(among other places)
This commit is contained in:
parent
9c5563503e
commit
9b53c45f04
@ -500,12 +500,15 @@ class File_ASN1 {
|
||||
*
|
||||
* Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format.
|
||||
*
|
||||
* "Special" mappings may be applied on a per tag-name basis via $special.
|
||||
*
|
||||
* @param Array $decoded
|
||||
* @param Array $mapping
|
||||
* @param Array $special
|
||||
* @return Array
|
||||
* @access public
|
||||
*/
|
||||
function asn1map($decoded, $mapping)
|
||||
function asn1map($decoded, $mapping, $special = array())
|
||||
{
|
||||
if (isset($mapping['explicit'])) {
|
||||
$decoded = $decoded['content'][0];
|
||||
@ -519,7 +522,7 @@ class File_ASN1 {
|
||||
}
|
||||
$inmap = $this->ANYmap[$intype];
|
||||
if (is_string($inmap)) {
|
||||
return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping));
|
||||
return array($inmap => $this->asn1map($decoded, array('type' => $intype) + $mapping, $special));
|
||||
}
|
||||
break;
|
||||
case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
|
||||
@ -527,15 +530,18 @@ class File_ASN1 {
|
||||
switch (true) {
|
||||
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
|
||||
case !isset($option['constant']) && $option['type'] == $decoded['type']:
|
||||
$value = $this->asn1map($decoded, $option);
|
||||
$value = $this->asn1map($decoded, $option, $special);
|
||||
break;
|
||||
case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
|
||||
$v = $this->asn1map($decoded, $option);
|
||||
$v = $this->asn1map($decoded, $option, $special);
|
||||
if (isset($v)) {
|
||||
$value = $v;
|
||||
}
|
||||
}
|
||||
if (isset($value)) {
|
||||
if (isset($special[$key])) {
|
||||
$value = $special[$key]($value);
|
||||
}
|
||||
return array($key => $value);
|
||||
}
|
||||
}
|
||||
@ -560,7 +566,7 @@ class File_ASN1 {
|
||||
if (isset($mapping['min']) && isset($mapping['max'])) {
|
||||
$child = $mapping['children'];
|
||||
foreach ($decoded['content'] as $content) {
|
||||
if (($map[] = $this->asn1map($content, $child)) === NULL) {
|
||||
if (($map[] = $this->asn1map($content, $child, $special)) === NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -604,12 +610,15 @@ class File_ASN1 {
|
||||
|
||||
if ($maymatch) {
|
||||
// Attempt submapping.
|
||||
$candidate = $this->asn1map($temp, $child);
|
||||
$candidate = $this->asn1map($temp, $child, $special);
|
||||
$maymatch = $candidate !== NULL;
|
||||
}
|
||||
|
||||
if ($maymatch) {
|
||||
// Got the match: use it.
|
||||
if (isset($special[$key])) {
|
||||
$candidate = $special[$key]($candidate);
|
||||
}
|
||||
$map[$key] = $candidate;
|
||||
$i++;
|
||||
} elseif (isset($child['default'])) {
|
||||
@ -630,7 +639,7 @@ class File_ASN1 {
|
||||
if (isset($mapping['min']) && isset($mapping['max'])) {
|
||||
$child = $mapping['children'];
|
||||
foreach ($decoded['content'] as $content) {
|
||||
if (($map[] = $this->asn1map($content, $child)) === NULL) {
|
||||
if (($map[] = $this->asn1map($content, $child, $special)) === NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -673,7 +682,7 @@ class File_ASN1 {
|
||||
|
||||
if ($maymatch) {
|
||||
// Attempt submapping.
|
||||
$candidate = $this->asn1map($temp, $child);
|
||||
$candidate = $this->asn1map($temp, $child, $special);
|
||||
$maymatch = $candidate !== NULL;
|
||||
}
|
||||
|
||||
@ -682,6 +691,9 @@ class File_ASN1 {
|
||||
}
|
||||
|
||||
// Got the match: use it.
|
||||
if (isset($special[$key])) {
|
||||
$candidate = $special[$key]($candidate);
|
||||
}
|
||||
$map[$key] = $candidate;
|
||||
break;
|
||||
}
|
||||
@ -774,18 +786,30 @@ class File_ASN1 {
|
||||
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
|
||||
* an ASN.1 compiler.
|
||||
*
|
||||
* "Special" mappings can be applied via $special.
|
||||
*
|
||||
* @param String $source
|
||||
* @param String $mapping
|
||||
* @param Integer $idx
|
||||
* @return String
|
||||
* @access public
|
||||
*/
|
||||
function encodeDER($source, $mapping)
|
||||
function encodeDER($source, $mapping, $special = array())
|
||||
{
|
||||
$this->location = array();
|
||||
return $this->_encode_der($source, $mapping);
|
||||
return $this->_encode_der($source, $mapping, NULL, $special);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASN.1 Encode (Helper function)
|
||||
*
|
||||
* @param String $source
|
||||
* @param Array $mapping
|
||||
* @param Integer $idx
|
||||
* @param Array $special
|
||||
* @return String
|
||||
* @access private
|
||||
*/
|
||||
/**
|
||||
* ASN.1 Encode (Helper function)
|
||||
*
|
||||
@ -795,7 +819,7 @@ class File_ASN1 {
|
||||
* @return String
|
||||
* @access private
|
||||
*/
|
||||
function _encode_der($source, $mapping, $idx = NULL)
|
||||
function _encode_der($source, $mapping, $idx = NULL, $special = array())
|
||||
{
|
||||
if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') {
|
||||
return $source->element;
|
||||
@ -807,6 +831,9 @@ class File_ASN1 {
|
||||
}
|
||||
|
||||
if (isset($idx)) {
|
||||
if (isset($special[$idx])) {
|
||||
$source = $special[$idx]($source);
|
||||
}
|
||||
$this->location[] = $idx;
|
||||
}
|
||||
|
||||
@ -823,7 +850,7 @@ class File_ASN1 {
|
||||
$child = $mapping['children'];
|
||||
|
||||
foreach ($source as $content) {
|
||||
$temp = $this->_encode_der($content, $child);
|
||||
$temp = $this->_encode_der($content, $child, NULL, $special);
|
||||
if ($temp === false) {
|
||||
return false;
|
||||
}
|
||||
@ -840,7 +867,7 @@ class File_ASN1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp = $this->_encode_der($source[$key], $child, $key);
|
||||
$temp = $this->_encode_der($source[$key], $child, $key, $special);
|
||||
if ($temp === false) {
|
||||
return false;
|
||||
}
|
||||
@ -881,7 +908,7 @@ class File_ASN1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp = $this->_encode_der($source[$key], $child, $key);
|
||||
$temp = $this->_encode_der($source[$key], $child, $key, $special);
|
||||
if ($temp === false) {
|
||||
return false;
|
||||
}
|
||||
@ -1003,19 +1030,19 @@ class File_ASN1 {
|
||||
|
||||
switch (true) {
|
||||
case !isset($source):
|
||||
return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping);
|
||||
return $this->_encode_der(NULL, array('type' => FILE_ASN1_TYPE_NULL) + $mapping, NULL, $special);
|
||||
case is_int($source):
|
||||
case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping);
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_INTEGER) + $mapping, NULL, $special);
|
||||
case is_float($source):
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping);
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_REAL) + $mapping, NULL, $special);
|
||||
case is_bool($source):
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping);
|
||||
return $this->_encode_der($source, array('type' => FILE_ASN1_TYPE_BOOLEAN) + $mapping, NULL, $special);
|
||||
case is_array($source) && count($source) == 1:
|
||||
$typename = implode('', array_keys($source));
|
||||
$outtype = array_search($typename, $this->ANYmap, true);
|
||||
if ($outtype !== false) {
|
||||
return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping);
|
||||
return $this->_encode_der($source[$typename], array('type' => $outtype) + $mapping, NULL, $special);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1031,7 +1058,7 @@ class File_ASN1 {
|
||||
user_error('No filters defined for ' . implode('/', $loc));
|
||||
return false;
|
||||
}
|
||||
return $this->_encode_der($source, $filters + $mapping);
|
||||
return $this->_encode_der($source, $filters + $mapping, NULL, $special);
|
||||
case FILE_ASN1_TYPE_NULL:
|
||||
$value = '';
|
||||
break;
|
||||
|
@ -1562,7 +1562,7 @@ class File_X509 {
|
||||
corresponding to the extension type identified by extnID */
|
||||
$map = $this->_getMapping($id);
|
||||
if (!is_bool($map)) {
|
||||
$mapped = $asn1->asn1map($decoded[0], $map);
|
||||
$mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => array($this, '_decodeIP')));
|
||||
$value = $mapped === false ? $decoded[0] : $mapped;
|
||||
|
||||
if ($id == 'id-ce-certificatePolicies') {
|
||||
@ -1644,7 +1644,7 @@ class File_X509 {
|
||||
unset($extensions[$i]);
|
||||
}
|
||||
} else {
|
||||
$temp = $asn1->encodeDER($value, $map);
|
||||
$temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP')));
|
||||
$value = base64_encode($temp);
|
||||
}
|
||||
}
|
||||
@ -2178,6 +2178,36 @@ class File_X509 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an IP address
|
||||
*
|
||||
* Takes in a base64 encoded "blob" and returns a human readable IP address
|
||||
*
|
||||
* @param String $ip
|
||||
* @access private
|
||||
* @return String
|
||||
*/
|
||||
function _decodeIP($ip)
|
||||
{
|
||||
$ip = base64_decode($ip);
|
||||
list(, $ip) = unpack('N', $ip);
|
||||
return long2ip($ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes an IP address
|
||||
*
|
||||
* Takes a human readable IP address into a base64-encoded "blob"
|
||||
*
|
||||
* @param String $ip
|
||||
* @access private
|
||||
* @return String
|
||||
*/
|
||||
function _encodeIP($ip)
|
||||
{
|
||||
return base64_encode(pack('N', ip2long($ip)));
|
||||
}
|
||||
|
||||
/**
|
||||
* "Normalizes" a Distinguished Name property
|
||||
*
|
||||
@ -3203,6 +3233,21 @@ class File_X509 {
|
||||
array_map(array('File_X509', '_dnsName'), $subject->domains));
|
||||
}
|
||||
|
||||
if (isset($subject->ipAddresses) && count($subject->ipAddresses)) {
|
||||
// should an IP address appear as the CN if no domain name is specified? idk
|
||||
//$ips = count($subject->domains) ? $subject->ipAddresses : array_slice($subject->ipAddresses, 1);
|
||||
$ipAddresses = array();
|
||||
foreach ($subject->ipAddresses as $ipAddress) {
|
||||
$encoded = $subject->_ipAddress($ipAddress);
|
||||
if ($encoded !== false) {
|
||||
$ipAddresses[] = $encoded;
|
||||
}
|
||||
}
|
||||
if (count($ipAddresses)) {
|
||||
$this->setExtension('id-ce-subjectAltName', $ipAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->caFlag) {
|
||||
$keyUsage = $this->getExtension('id-ce-keyUsage');
|
||||
if (!$keyUsage) {
|
||||
@ -4098,6 +4143,23 @@ class File_X509 {
|
||||
$this->setDNProp('id-at-commonName', $this->domains[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IP Addresses's which the cert is to be valid for
|
||||
*
|
||||
* @access public
|
||||
* @param String $ipAddress optional
|
||||
*/
|
||||
function setIPAddress()
|
||||
{
|
||||
$this->ipAddresses = func_get_args();
|
||||
/*
|
||||
if (!isset($this->domains)) {
|
||||
$this->removeDNProp('id-at-commonName');
|
||||
$this->setDNProp('id-at-commonName', $this->ipAddresses[0]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build domain array
|
||||
*
|
||||
@ -4110,6 +4172,20 @@ class File_X509 {
|
||||
return array('dNSName' => $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to build IP Address array
|
||||
*
|
||||
* (IPv6 is not currently supported)
|
||||
*
|
||||
* @access private
|
||||
* @param String $address
|
||||
* @return Array
|
||||
*/
|
||||
function _iPAddress($address)
|
||||
{
|
||||
return array('iPAddress' => $address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of a revoked certificate.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user