mirror of
https://github.com/phpseclib/phpseclib.git
synced 2025-02-10 07:38:48 +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.
|
* 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 $decoded
|
||||||
* @param Array $mapping
|
* @param Array $mapping
|
||||||
|
* @param Array $special
|
||||||
* @return Array
|
* @return Array
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
function asn1map($decoded, $mapping)
|
function asn1map($decoded, $mapping, $special = array())
|
||||||
{
|
{
|
||||||
if (isset($mapping['explicit'])) {
|
if (isset($mapping['explicit'])) {
|
||||||
$decoded = $decoded['content'][0];
|
$decoded = $decoded['content'][0];
|
||||||
@ -519,7 +522,7 @@ class File_ASN1 {
|
|||||||
}
|
}
|
||||||
$inmap = $this->ANYmap[$intype];
|
$inmap = $this->ANYmap[$intype];
|
||||||
if (is_string($inmap)) {
|
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;
|
break;
|
||||||
case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
|
case $mapping['type'] == FILE_ASN1_TYPE_CHOICE:
|
||||||
@ -527,15 +530,18 @@ class File_ASN1 {
|
|||||||
switch (true) {
|
switch (true) {
|
||||||
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
|
case isset($option['constant']) && $option['constant'] == $decoded['constant']:
|
||||||
case !isset($option['constant']) && $option['type'] == $decoded['type']:
|
case !isset($option['constant']) && $option['type'] == $decoded['type']:
|
||||||
$value = $this->asn1map($decoded, $option);
|
$value = $this->asn1map($decoded, $option, $special);
|
||||||
break;
|
break;
|
||||||
case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
|
case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
|
||||||
$v = $this->asn1map($decoded, $option);
|
$v = $this->asn1map($decoded, $option, $special);
|
||||||
if (isset($v)) {
|
if (isset($v)) {
|
||||||
$value = $v;
|
$value = $v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isset($value)) {
|
if (isset($value)) {
|
||||||
|
if (isset($special[$key])) {
|
||||||
|
$value = $special[$key]($value);
|
||||||
|
}
|
||||||
return array($key => $value);
|
return array($key => $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,7 +566,7 @@ class File_ASN1 {
|
|||||||
if (isset($mapping['min']) && isset($mapping['max'])) {
|
if (isset($mapping['min']) && isset($mapping['max'])) {
|
||||||
$child = $mapping['children'];
|
$child = $mapping['children'];
|
||||||
foreach ($decoded['content'] as $content) {
|
foreach ($decoded['content'] as $content) {
|
||||||
if (($map[] = $this->asn1map($content, $child)) === NULL) {
|
if (($map[] = $this->asn1map($content, $child, $special)) === NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -604,12 +610,15 @@ class File_ASN1 {
|
|||||||
|
|
||||||
if ($maymatch) {
|
if ($maymatch) {
|
||||||
// Attempt submapping.
|
// Attempt submapping.
|
||||||
$candidate = $this->asn1map($temp, $child);
|
$candidate = $this->asn1map($temp, $child, $special);
|
||||||
$maymatch = $candidate !== NULL;
|
$maymatch = $candidate !== NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($maymatch) {
|
if ($maymatch) {
|
||||||
// Got the match: use it.
|
// Got the match: use it.
|
||||||
|
if (isset($special[$key])) {
|
||||||
|
$candidate = $special[$key]($candidate);
|
||||||
|
}
|
||||||
$map[$key] = $candidate;
|
$map[$key] = $candidate;
|
||||||
$i++;
|
$i++;
|
||||||
} elseif (isset($child['default'])) {
|
} elseif (isset($child['default'])) {
|
||||||
@ -630,7 +639,7 @@ class File_ASN1 {
|
|||||||
if (isset($mapping['min']) && isset($mapping['max'])) {
|
if (isset($mapping['min']) && isset($mapping['max'])) {
|
||||||
$child = $mapping['children'];
|
$child = $mapping['children'];
|
||||||
foreach ($decoded['content'] as $content) {
|
foreach ($decoded['content'] as $content) {
|
||||||
if (($map[] = $this->asn1map($content, $child)) === NULL) {
|
if (($map[] = $this->asn1map($content, $child, $special)) === NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,7 +682,7 @@ class File_ASN1 {
|
|||||||
|
|
||||||
if ($maymatch) {
|
if ($maymatch) {
|
||||||
// Attempt submapping.
|
// Attempt submapping.
|
||||||
$candidate = $this->asn1map($temp, $child);
|
$candidate = $this->asn1map($temp, $child, $special);
|
||||||
$maymatch = $candidate !== NULL;
|
$maymatch = $candidate !== NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -682,6 +691,9 @@ class File_ASN1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Got the match: use it.
|
// Got the match: use it.
|
||||||
|
if (isset($special[$key])) {
|
||||||
|
$candidate = $special[$key]($candidate);
|
||||||
|
}
|
||||||
$map[$key] = $candidate;
|
$map[$key] = $candidate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -774,18 +786,30 @@ class File_ASN1 {
|
|||||||
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
|
* DER-encodes an ASN.1 semantic mapping ($mapping). Some libraries would probably call this function
|
||||||
* an ASN.1 compiler.
|
* an ASN.1 compiler.
|
||||||
*
|
*
|
||||||
|
* "Special" mappings can be applied via $special.
|
||||||
|
*
|
||||||
* @param String $source
|
* @param String $source
|
||||||
* @param String $mapping
|
* @param String $mapping
|
||||||
* @param Integer $idx
|
* @param Integer $idx
|
||||||
* @return String
|
* @return String
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
function encodeDER($source, $mapping)
|
function encodeDER($source, $mapping, $special = array())
|
||||||
{
|
{
|
||||||
$this->location = 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)
|
* ASN.1 Encode (Helper function)
|
||||||
*
|
*
|
||||||
@ -795,7 +819,7 @@ class File_ASN1 {
|
|||||||
* @return String
|
* @return String
|
||||||
* @access private
|
* @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') {
|
if (is_object($source) && strtolower(get_class($source)) == 'file_asn1_element') {
|
||||||
return $source->element;
|
return $source->element;
|
||||||
@ -807,6 +831,9 @@ class File_ASN1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($idx)) {
|
if (isset($idx)) {
|
||||||
|
if (isset($special[$idx])) {
|
||||||
|
$source = $special[$idx]($source);
|
||||||
|
}
|
||||||
$this->location[] = $idx;
|
$this->location[] = $idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,7 +850,7 @@ class File_ASN1 {
|
|||||||
$child = $mapping['children'];
|
$child = $mapping['children'];
|
||||||
|
|
||||||
foreach ($source as $content) {
|
foreach ($source as $content) {
|
||||||
$temp = $this->_encode_der($content, $child);
|
$temp = $this->_encode_der($content, $child, NULL, $special);
|
||||||
if ($temp === false) {
|
if ($temp === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -840,7 +867,7 @@ class File_ASN1 {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$temp = $this->_encode_der($source[$key], $child, $key);
|
$temp = $this->_encode_der($source[$key], $child, $key, $special);
|
||||||
if ($temp === false) {
|
if ($temp === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -881,7 +908,7 @@ class File_ASN1 {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$temp = $this->_encode_der($source[$key], $child, $key);
|
$temp = $this->_encode_der($source[$key], $child, $key, $special);
|
||||||
if ($temp === false) {
|
if ($temp === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1003,19 +1030,19 @@ class File_ASN1 {
|
|||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case !isset($source):
|
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_int($source):
|
||||||
case is_object($source) && strtolower(get_class($source)) == 'math_biginteger':
|
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):
|
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):
|
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:
|
case is_array($source) && count($source) == 1:
|
||||||
$typename = implode('', array_keys($source));
|
$typename = implode('', array_keys($source));
|
||||||
$outtype = array_search($typename, $this->ANYmap, true);
|
$outtype = array_search($typename, $this->ANYmap, true);
|
||||||
if ($outtype !== false) {
|
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));
|
user_error('No filters defined for ' . implode('/', $loc));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return $this->_encode_der($source, $filters + $mapping);
|
return $this->_encode_der($source, $filters + $mapping, NULL, $special);
|
||||||
case FILE_ASN1_TYPE_NULL:
|
case FILE_ASN1_TYPE_NULL:
|
||||||
$value = '';
|
$value = '';
|
||||||
break;
|
break;
|
||||||
|
@ -1562,7 +1562,7 @@ class File_X509 {
|
|||||||
corresponding to the extension type identified by extnID */
|
corresponding to the extension type identified by extnID */
|
||||||
$map = $this->_getMapping($id);
|
$map = $this->_getMapping($id);
|
||||||
if (!is_bool($map)) {
|
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;
|
$value = $mapped === false ? $decoded[0] : $mapped;
|
||||||
|
|
||||||
if ($id == 'id-ce-certificatePolicies') {
|
if ($id == 'id-ce-certificatePolicies') {
|
||||||
@ -1644,7 +1644,7 @@ class File_X509 {
|
|||||||
unset($extensions[$i]);
|
unset($extensions[$i]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$temp = $asn1->encodeDER($value, $map);
|
$temp = $asn1->encodeDER($value, $map, array('iPAddress' => array($this, '_encodeIP')));
|
||||||
$value = base64_encode($temp);
|
$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
|
* "Normalizes" a Distinguished Name property
|
||||||
*
|
*
|
||||||
@ -3203,6 +3233,21 @@ class File_X509 {
|
|||||||
array_map(array('File_X509', '_dnsName'), $subject->domains));
|
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) {
|
if ($this->caFlag) {
|
||||||
$keyUsage = $this->getExtension('id-ce-keyUsage');
|
$keyUsage = $this->getExtension('id-ce-keyUsage');
|
||||||
if (!$keyUsage) {
|
if (!$keyUsage) {
|
||||||
@ -4098,6 +4143,23 @@ class File_X509 {
|
|||||||
$this->setDNProp('id-at-commonName', $this->domains[0]);
|
$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
|
* Helper function to build domain array
|
||||||
*
|
*
|
||||||
@ -4110,6 +4172,20 @@ class File_X509 {
|
|||||||
return array('dNSName' => $domain);
|
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.
|
* Get the index of a revoked certificate.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user