From d9a3dafa0a7b27af91956702c6fd2afe50baff86 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 00:51:42 +0100 Subject: [PATCH 01/12] ASN1: Support enumerated types by processing them as integers. --- phpseclib/File/ASN1.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 2b17fa09..a3e05cd4 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -309,10 +309,9 @@ class File_ASN1 { $current['content'] = (bool) ord($content[0]); break; case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: $current['content'] = new Math_BigInteger($content, -256); break; - case FILE_ASN1_TYPE_ENUMERATED: // not currently supported - return false; case FILE_ASN1_TYPE_REAL: // not currently supported return false; case FILE_ASN1_TYPE_BIT_STRING: @@ -673,6 +672,7 @@ class File_ASN1 { case FILE_ASN1_TYPE_BMP_STRING: return $decoded['content']; case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: $temp = $decoded['content']; if (isset($mapping['implicit'])) { $temp = new Math_BigInteger($decoded['content'], -256); @@ -875,6 +875,7 @@ class File_ASN1 { return $temp; case FILE_ASN1_TYPE_INTEGER: + case FILE_ASN1_TYPE_ENUMERATED: if (!isset($mapping['mapping'])) { $value = $source->toBytes(true); } else { From 50962f9cd39f2c6c1dbe01db068a967a77eb20d3 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 00:58:36 +0100 Subject: [PATCH 02/12] ASN1: asn1map(): do not rely on input fields to determine mapping parameters. --- phpseclib/File/ASN1.php | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index a3e05cd4..9c5499a7 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -432,12 +432,12 @@ class File_ASN1 { * * Provides an ASN.1 semantic mapping ($mapping) from a parsed BER-encoding to a human readable format. * - * @param String $encoded - * @param Integer $start + * @param Array $decoded + * @param Array $mapping * @return Array * @access public */ - function asn1map($decoded, $mapping, $idx = NULL) + function asn1map($decoded, $mapping) { if (isset($mapping['explicit'])) { $decoded = $decoded['content'][0]; @@ -506,13 +506,18 @@ class File_ASN1 { continue; } + $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + $constant = NULL; if (isset($temp['constant'])) { $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; - $childClass = isset($child['class']) ? $child['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; - $constant = isset($temp['class']) ? $child['cast'] : $child['constant']; - } else { - $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; - $constant = NULL; + } + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } + elseif (isset($child['constant'])) { + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; } if (isset($child['optional'])) { @@ -590,14 +595,20 @@ class File_ASN1 { continue; } + $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; + $constant = NULL; if (isset($temp['constant'])) { $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; - $childClass = isset($child['class']) ? $child['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; - $constant = isset($temp['class']) ? $child['cast'] : $child['constant']; - } else { - $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; - $constant = NULL; } + if (isset($child['class'])) { + $childClass = $child['class']; + $constant = $child['cast']; + } + elseif (isset($child['constant'])) { + $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC; + $constant = $child['constant']; + } + if (isset($constant) && isset($temp['constant'])) { if (($constant == $temp['constant']) && ($childClass == $tempClass)) { $map[$key] = $this->asn1map($temp['content'], $child); From b05cff1320442b1841757ec745fa79fdd3528aa8 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 01:01:20 +0100 Subject: [PATCH 03/12] ASN1: process input NULL fields. --- phpseclib/File/ASN1.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 9c5499a7..7eccf690 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -668,6 +668,8 @@ class File_ASN1 { } case FILE_ASN1_TYPE_OCTET_STRING: return base64_encode($decoded['content']); + case FILE_ASN1_TYPE_NULL: + return ''; case FILE_ASN1_TYPE_BOOLEAN: return $decoded['content']; case FILE_ASN1_TYPE_NUMERIC_STRING: From a75de60478f24e64a32b2501ac52dae8d61037b5 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 01:04:24 +0100 Subject: [PATCH 04/12] ASN1: asn1map(): fix handling of optional CHOICE and ANY children in SEQUENCE. --- phpseclib/File/ASN1.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 7eccf690..ad57b392 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -531,14 +531,9 @@ class File_ASN1 { $temp = $decoded['content'][$i]; } } elseif (!isset($child['constant'])) { - if ($child['type'] == FILE_ASN1_TYPE_CHOICE) { - $map[$key] = $this->asn1map($temp, $child); - $i++; - continue; - } // we could do this, as well: // $buffer = $this->asn1map($temp, $child); if (isset($buffer)) { $map[$key] = $buffer; } - if ($child['type'] == $temp['type']) { + if ($child['type'] == $temp['type'] || $child['type'] == FILE_ASN1_TYPE_ANY) { $map[$key] = $this->asn1map($temp, $child); $i++; if (count($decoded['content']) == $i) { From 2c8ad5ee720fea5b1948a8ff0f27d966444ba9d7 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 01:07:01 +0100 Subject: [PATCH 05/12] ASN1: _encode_der(): do not encode optional fields with value set to default. --- phpseclib/File/ASN1.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index ad57b392..41d0a502 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -728,6 +728,11 @@ class File_ASN1 { return $source->element; } + // do not encode optional fields with value set to default + if (!empty($mapping['optional']) && isset($mapping['default']) && $source === $mapping['default']) { + return ''; + } + if (isset($idx)) { $this->location[] = $idx; } From 4f634aaca884435ac582154e32aebe19a68bdcc4 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 02:37:55 +0100 Subject: [PATCH 06/12] X509: avoid some "undefined" errors. --- phpseclib/File/X509.php | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 4eabcb64..17330a4b 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1216,7 +1216,10 @@ class File_X509 { $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($cert); - $x509 = $asn1->asn1map($decoded[0], $this->Certificate); + + if (!empty($decoded)) { + $x509 = $asn1->asn1map($decoded[0], $this->Certificate); + } if (!isset($x509) || $x509 === false) { $this->currentCert = false; return false; @@ -2153,6 +2156,12 @@ class File_X509 { $asn1->loadOIDs($this->oids); $decoded = $asn1->decodeBER($csr); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + $csr = $asn1->asn1map($decoded[0], $this->CertificationRequest); if (!isset($csr) || $csr === false) { $this->currentCert = false; @@ -2242,8 +2251,8 @@ class File_X509 { return false; } - $currentCert = $this->currentCert; - $signatureSubject = $this->signatureSubject; + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL; if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) { $this->currentCert = $subject->currentCert; @@ -2404,8 +2413,8 @@ class File_X509 { } $this->publicKey = $origPublicKey; - $currentCert = $this->currentCert; - $signatureSubject = $this->signatureSubject; + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject: NULL; if (isset($this->currentCert) && is_array($this->currentCert) && isset($this->currentCert['certificationRequestInfo'])) { $this->currentCert['signatureAlgorithm']['algorithm'] = From 73b0d05ddc16a3ec3127050671b4c2b0bf1375bc Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 02:42:19 +0100 Subject: [PATCH 07/12] X509: set-up key identifier upon certificate loading. --- phpseclib/File/X509.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 17330a4b..1ed2dc73 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1269,6 +1269,9 @@ class File_X509 { $this->currentCert = $x509; $this->dn = $x509['tbsCertificate']['subject']; + $keyIdentifier = $this->getExtension('id-ce-subjectKeyIdentifier'); + $this->keyIdentifier = is_string($keyIdentifier) ? $keyIdentifier : NULL; + return $x509; } @@ -2189,6 +2192,7 @@ class File_X509 { $this->publicKey = NULL; } + $this->keyIdentifier = NULL; $this->currentCert = $csr; return $csr; From bc7a59bc8cea4f63a023dd84fc3d2b08c6546254 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 02:56:23 +0100 Subject: [PATCH 08/12] X509: factorize some code in prevision of CRL support. --- phpseclib/File/X509.php | 204 +++++++++++++++++++++++++++------------- 1 file changed, 137 insertions(+), 67 deletions(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 1ed2dc73..9db5489f 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1227,41 +1227,7 @@ class File_X509 { $this->signatureSubject = substr($cert, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); - if (isset($x509['tbsCertificate']['extensions'])) { - for ($i = 0; $i < count($x509['tbsCertificate']['extensions']); $i++) { - $id = $x509['tbsCertificate']['extensions'][$i]['extnId']; - $value = &$x509['tbsCertificate']['extensions'][$i]['extnValue']; - $value = base64_decode($value); - $decoded = $asn1->decodeBER($value); - /* [extnValue] contains the DER encoding of an ASN.1 value - corresponding to the extension type identified by extnID */ - $map = $this->_getMapping($id); - if (!is_bool($map)) { - $mapped = $asn1->asn1map($decoded[0], $map); - $value = $mapped === false ? $decoded[0] : $mapped; - - if ($id == 'id-ce-certificatePolicies') { - for ($j = 0; $j < count($value); $j++) { - if (!isset($value[$j]['policyQualifiers'])) { - continue; - } - for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { - $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; - $map = $this->_getMapping($subid); - $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; - if ($map !== false) { - $decoded = $asn1->decodeBER($subvalue); - $mapped = $asn1->asn1map($decoded[0], $map); - $subvalue = $mapped === false ? $decoded[0] : $mapped; - } - } - } - } - } elseif ($map) { - $value = base64_encode($value); - } - } - } + $this->_mapInExtensions($x509, 'tbsCertificate/extensions', $asn1); $key = &$x509['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; $key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key); @@ -1321,11 +1287,81 @@ class File_X509 { $asn1->loadFilters($filters); - if (isset($cert['tbsCertificate']['extensions'])) { - $size = count($cert['tbsCertificate']['extensions']); + $this->_mapOutExtensions($cert, 'tbsCertificate/extensions', $asn1); + + $cert = $asn1->encodeDER($cert, $this->Certificate); + + return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert)) . '-----END CERTIFICATE-----'; + } + + /** + * Map extension values from octet string to extension-specific internal + * format. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapInExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArray($root, $path); + + if (is_array($extensions)) { + for ($i = 0; $i < count($extensions); $i++) { + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; + $value = base64_decode($value); + $decoded = $asn1->decodeBER($value); + /* [extnValue] contains the DER encoding of an ASN.1 value + corresponding to the extension type identified by extnID */ + $map = $this->_getMapping($id); + if (!is_bool($map)) { + $mapped = $asn1->asn1map($decoded[0], $map); + $value = $mapped === false ? $decoded[0] : $mapped; + + if ($id == 'id-ce-certificatePolicies') { + for ($j = 0; $j < count($value); $j++) { + if (!isset($value[$j]['policyQualifiers'])) { + continue; + } + for ($k = 0; $k < count($value[$j]['policyQualifiers']); $k++) { + $subid = $value[$j]['policyQualifiers'][$k]['policyQualifierId']; + $map = $this->_getMapping($subid); + $subvalue = &$value[$j]['policyQualifiers'][$k]['qualifier']; + if ($map !== false) { + $decoded = $asn1->decodeBER($subvalue); + $mapped = $asn1->asn1map($decoded[0], $map); + $subvalue = $mapped === false ? $decoded[0] : $mapped; + } + } + } + } + } elseif ($map) { + $value = base64_encode($value); + } + } + } + } + + /** + * Map extension values from extension-specific internal format to + * octet string. + * + * @param Array ref $root + * @param String $path + * @param Object $asn1 + * @access private + */ + function _mapOutExtensions(&$root, $path, $asn1) + { + $extensions = &$this->_subArray($root, $path); + + if (is_array($extensions)) { + $size = count($extensions); for ($i = 0; $i < $size; $i++) { - $id = $cert['tbsCertificate']['extensions'][$i]['extnId']; - $value = &$cert['tbsCertificate']['extensions'][$i]['extnValue']; + $id = $extensions[$i]['extnId']; + $value = &$extensions[$i]['extnValue']; switch ($id) { case 'id-ce-certificatePolicies': @@ -1360,7 +1396,7 @@ class File_X509 { if (is_bool($map)) { if (!$map) { user_error($id . ' is not a currently supported extension', E_USER_NOTICE); - unset($cert['tbsCertificate']['extensions'][$i]); + unset($extensions[$i]); } } else { $temp = $asn1->encodeDER($value, $map); @@ -1368,10 +1404,6 @@ class File_X509 { } } } - - $cert = $asn1->encodeDER($cert, $this->Certificate); - - return "-----BEGIN CERTIFICATE-----\r\n" . chunk_split(base64_encode($cert)) . '-----END CERTIFICATE-----'; } /** @@ -2543,23 +2575,59 @@ class File_X509 { $this->caFlag = true; } + /** + * Get a reference to a subarray + * + * @param array $root + * @param String $path absolute path with / as component separator + * @param Boolean $create optional + * @access private + * @return array item ref or false + */ + function &_subArray(&$root, $path, $create = false) + { + $false = false; + + if (!is_array($root)) { + return $false; + } + + foreach (explode('/', $path) as $i) { + if (!is_array($root)) { + return $false; + } + + if (!isset($root[$i])) { + if (!$create) { + return $false; + } + + $root[$i] = array(); + } + + $root = &$root[$i]; + } + + return $root; + } + /** * Remove an Extension * * @param String $id + * @param String $path optional * @access public * @return Boolean */ - function removeExtension($id) + function removeExtension($id, $path = 'tbsCertificate/extensions') { - switch (true) { - case !is_array($this->currentCert): - case !isset($this->currentCert['tbsCertificate']['extensions']): + $extensions = &$this->_subArray($this->currentCert, $path); + + if (!is_array($extensions)) { return false; - } + } $result = false; - $extensions = &$this->currentCert['tbsCertificate']['extensions']; foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { unset($extensions[$key]); @@ -2578,22 +2646,24 @@ class File_X509 { * Returns the extension if it exists and false if not * * @param String $id + * @param Array $cert optional + * @param String $path optional * @access public * @return Mixed */ - function getExtension($id, $cert = NULL) + function getExtension($id, $cert = NULL, $path = 'tbsCertificate/extensions') { if (!isset($cert)) { $cert = $this->currentCert; } - switch (true) { - case !is_array($cert): - case !isset($cert['tbsCertificate']['extensions']): - return false; - } + $extensions = $this->_subArray($cert, $path); - foreach ($cert['tbsCertificate']['extensions'] as $key => $value) { + if (!is_array($extensions)) { + return false; + } + + foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { return $value['extnValue']; } @@ -2605,25 +2675,25 @@ class File_X509 { /** * Returns a list of all extensions in use * + * @param array $cert optional + * @param String $path optional * @access public * @return Array */ - function getExtensions($cert = NULL) + function getExtensions($cert = NULL, $path = 'tbsCertificate/extensions') { if (!isset($cert)) { $cert = $this->currentCert; } - switch (true) { - case !is_array($cert): - case !isset($cert['tbsCertificate']['extensions']): - return array(); - } - + $exts = $this->_subArray($cert, $path); $extensions = array(); - foreach ($cert['tbsCertificate']['extensions'] as $extension) { - $extensions[] = $extension['extnId']; - } + + if (is_array($exts)) { + foreach ($exts as $extension) { + $extensions[] = $extension['extnId']; + } + } return $extensions; } From 6da490d00ad382d44d5252d028d7d994f1833628 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 03:03:21 +0100 Subject: [PATCH 09/12] X509: new setExtension() method. --- phpseclib/File/X509.php | 79 ++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 9db5489f..26d44ac7 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -2347,11 +2347,7 @@ class File_X509 { $this->currentCert['tbsCertificate']['issuer'] = $issuer->dn; if (isset($issuer->keyIdentifier)) { - $extensions = &$this->currentCert['tbsCertificate']['extensions']; - $extensions[] = array( - 'extnId' => 'id-ce-authorityKeyIdentifier', - 'critical' => false, - 'extnValue'=> array( + $this->setExtension('id-ce-authorityKeyIdentifier', array( //'authorityCertIssuer' => array( // array( // 'directoryName' => $issuer->dn @@ -2360,31 +2356,21 @@ class File_X509 { 'keyIdentifier' => $issuer->keyIdentifier ) ); + //$extensions = &$this->currentCert['tbsCertificate']['extensions']; //if (isset($issuer->serialNumber)) { // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; //} - unset($extensions); + //unset($extensions); } if (isset($subject->keyIdentifier)) { - $this->removeExtension('id-ce-subjectKeyIdentifier'); - $this->currentCert['tbsCertificate']['extensions'][] = array( - 'extnId' => 'id-ce-subjectKeyIdentifier', - 'critical' => false, - 'extnValue'=> $subject->keyIdentifier - ); + $this->setExtension('id-ce-subjectKeyIdentifier', $subject->keyIdentifier); } if (isset($subject->domains) && count($subject->domains) > 1) { - $this->currentCert['tbsCertificate']['extensions'][] = array( - 'extnId' => 'id-ce-subjectAltName', - 'critical' => false, - 'extnValue' => array() - ); - $last = count($this->currentCert['tbsCertificate']['extensions']) - 1; - foreach ($subject->domains as $domain) { - $this->currentCert['tbsCertificate']['extensions'][$last]['extnValue'][] = array('dNSName' => $domain); - } + $this->setExtension('id-ce-subjectAltName', + array_map(create_function('$domain', + 'return array("dNSName" => $domain);'), $subject->domains)); } if ($this->caFlag) { @@ -2392,25 +2378,18 @@ class File_X509 { if (!$keyUsage) { $keyUsage = array(); } - $this->removeExtension('id-ce-keyUsage'); - $this->currentCert['tbsCertificate']['extensions'][] = array( - 'extnId' => 'id-ce-keyUsage', - 'critical' => false, - 'extnValue' => array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) + $this->setExtension('id-ce-keyUsage', + array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) ); $basicConstraints = $this->getExtension('id-ce-basicConstraints'); if (!$basicConstraints) { $basicConstraints = array(); } - $this->removeExtension('id-ce-basicConstraints'); - $this->currentCert['tbsCertificate']['extensions'][] = array( - 'extnId' => 'id-ce-basicConstraints', - 'critical' => true, - 'extnValue' => array_unique(array_merge(array('cA' => true), $basicConstraints)) - ); + $this->setExtension('id-ce-basicConstraints', + array_unique(array_merge(array('cA' => true), $basicConstraints)), true); } // resync $this->signatureSubject @@ -2698,6 +2677,42 @@ class File_X509 { return $extensions; } + /** + * Set an Extension + * + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @param String $path optional + * @access public + * @return Boolean + */ + function setExtension($id, $value, $critical = false, $replace = true, $path = 'tbsCertificate/extensions') + { + $extensions = &$this->_subArray($this->currentCert, $path, true); + + if (!is_array($extensions)) { + return false; + } + + $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value); + + foreach ($extensions as $key => $value) { + if ($value['extnId'] == $id) { + if (!$replace) { + return false; + } + + $extensions[$key] = $newext; + return true; + } + } + + $extensions[] = $newext; + return true; + } + /** * Sets the authority key identifier * From 711d44f0e55ca91cef2b0d852a251791de89a464 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 03:13:39 +0100 Subject: [PATCH 10/12] X509: implement CRLs. --- phpseclib/File/X509.php | 670 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 669 insertions(+), 1 deletion(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 26d44ac7..e65ec893 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -100,6 +100,12 @@ class File_X509 { var $netscape_cert_type; var $netscape_comment; + + var $CRLNumber; + var $CRLReason; + var $IssuingDistributionPoint; + var $InvalidityDate; + var $CertificateIssuer; /**#@-*/ /** @@ -110,6 +116,14 @@ class File_X509 { */ var $CertificationRequest; + /** + * ASN.1 syntax for Certificate Revocation Lists (RFC5280) + * + * @var Array + * @access private + */ + var $CertificateList; + /** * Distinguished Name * @@ -1016,6 +1030,119 @@ class File_X509 { ) ); + $RevokedCertificate = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'userCertificate' => $CertificateSerialNumber, + 'revocationDate' => $Time, + 'crlEntryExtensions' => array( + 'optional' => true + ) + $Extensions + ) + ); + + $TBSCertList = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'version' => array( + 'optional' => true, + 'default' => 'v1' + ) + $Version, + 'signature' => $AlgorithmIdentifier, + 'issuer' => $Name, + 'thisUpdate' => $Time, + 'nextUpdate' => array( + 'optional' => true + ) + $Time, + 'revokedCertificates' => array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'optional' => true, + 'min' => 0, + 'max' => -1, + 'children' => $RevokedCertificate + ), + 'crlExtensions' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $Extensions + ) + ); + + $this->CertificateList = array( + 'type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'tbsCertList' => $TBSCertList, + 'signatureAlgorithm' => $AlgorithmIdentifier, + 'signature' => array('type' => FILE_ASN1_TYPE_BIT_STRING) + ) + ); + + $this->CRLNumber = array('type' => FILE_ASN1_TYPE_INTEGER); + + $this->CRLReason = array('type' => FILE_ASN1_TYPE_ENUMERATED, + 'mapping' => array( + 'unspecified', + 'keyCompromise', + 'cACompromise', + 'affiliationChanged', + 'superseded', + 'cessationOfOperation', + 'certificateHold', + // Value 7 is not used. + 8 => 'removeFromCRL', + 'privilegeWithdrawn', + 'aACompromise' + ) + ); + + $this->IssuingDistributionPoint = array('type' => FILE_ASN1_TYPE_SEQUENCE, + 'children' => array( + 'distributionPoint' => array( + 'constant' => 0, + 'optional' => true, + 'explicit' => true + ) + $DistributionPointName, + 'onlyContainsUserCerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 1, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsCACerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 2, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlySomeReasons' => array( + 'constant' => 3, + 'optional' => true, + 'implicit' => true + ) + $ReasonFlags, + 'indirectCRL' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 4, + 'optional' => true, + 'default' => false, + 'implicit' => true + ), + 'onlyContainsAttributeCerts' => array( + 'type' => FILE_ASN1_TYPE_BOOLEAN, + 'constant' => 5, + 'optional' => true, + 'default' => false, + 'implicit' => true + ) + ) + ); + + $this->InvalidityDate = array('type' => FILE_ASN1_TYPE_GENERALIZED_TIME); + + $this->CertificateIssuer = $GeneralNames; + // OIDs from RFC5280 and those RFCs mentioned in RFC5280#section-4.1.1.2 $this->oids = array( '1.3.6.1.5.5.7' => 'id-pkix', @@ -1465,6 +1592,22 @@ class File_X509 { // http://www.maithean.com/docs/set_bk3.pdf case '2.23.42.7.0': // id-set-hashedRootKey return true; + + // CRL extensions. + case 'id-ce-cRLNumber': + return $this->CRLNumber; + case 'id-ce-deltaCRLIndicator': + return $this->CRLNumber; + case 'id-ce-issuingDistributionPoint': + return $this->IssuingDistributionPoint; + case 'id-ce-freshestCRL': + return $this->CRLDistributionPoints; + case 'id-ce-cRLReasons': + return $this->CRLReason; + case 'id-ce-invalidityDate': + return $this->InvalidityDate; + case 'id-ce-certificateIssuer': + return $this->CertificateIssuer; } return false; @@ -1617,7 +1760,7 @@ class File_X509 { /** * Validate a signature * - * Works on both X.509 certs and CSR's. + * Works on X.509 certs, CSR's and CRL's. * Returns 1 if the signature is verified, 0 if it is not correct or -1 on error * * To know if a signature is valid one should do validateSignature() === 1 @@ -1690,6 +1833,32 @@ class File_X509 { substr(base64_decode($this->currentCert['signature']), 1), $this->signatureSubject ); + case isset($this->currentCert['tbsCertList']): + if (!empty($this->CAs)) { + for ($i = 0; $i < count($this->CAs); $i++) { + $ca = $this->CAs[$i]; + if ($this->currentCert['tbsCertList']['issuer'] === $ca['tbsCertificate']['subject']) { + $authorityKey = $this->getCRLExtension('id-ce-authorityKeyIdentifier'); + $subjectKeyID = $this->getExtension('id-ce-subjectKeyIdentifier', $ca); + switch (true) { + case !is_array($authorityKey): + case is_array($authorityKey) && isset($authorityKey['keyIdentifier']) && $authorityKey['keyIdentifier'] === $subjectKeyID: + $signingCert = $ca; // working cert + break 2; + } + } + } + } + if (!isset($signingCert)) { + return 0; + } + return $this->_validateSignature( + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], + $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], + $this->currentCert['signatureAlgorithm']['algorithm'], + substr(base64_decode($this->currentCert['signature']), 1), + $this->signatureSubject + ); default: return 0; } @@ -2264,6 +2433,103 @@ class File_X509 { return "-----BEGIN CERTIFICATE REQUEST-----\r\n" . chunk_split(base64_encode($csr)) . '-----END CERTIFICATE REQUEST-----'; } + /** + * Load a Certificate Revocation List + * + * @param String $crl + * @access public + * @return Mixed + */ + function loadCRL($crl) + { + $asn1 = new File_ASN1(); + + $crl = preg_replace('#^(?:[^-].+[\r\n]+)+|-.+-|[\r\n]#', '', $crl); + $orig = $crl = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $crl) ? base64_decode($crl) : false; + + if ($crl === false) { + $this->currentCert = false; + return false; + } + + $asn1->loadOIDs($this->oids); + $decoded = $asn1->decodeBER($crl); + + if (empty($decoded)) { + $this->currentCert = false; + return false; + } + + $crl = $asn1->asn1map($decoded[0], $this->CertificateList); + if (!isset($crl) || $crl === false) { + $this->currentCert = false; + return false; + } + + $this->signatureSubject = substr($orig, $decoded[0]['content'][0]['start'], $decoded[0]['content'][0]['length']); + + $this->_mapInExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); + } + } + + $this->keyIdentifier = NULL; + $this->currentCert = $crl; + + return $crl; + } + + /** + * Save Certificate Revocation List. + * + * @param Array $crl + * @access public + * @return String + */ + function saveCRL($crl) + { + if (!is_array($crl) || !isset($crl['tbsCertList'])) { + return false; + } + + $asn1 = new File_ASN1(); + + $asn1->loadOIDs($this->oids); + + $filters = array(); + $filters['tbsCertList']['issuer']['rdnSequence']['value'] = + $filters['tbsCertList']['signature']['parameters'] = + $filters['signatureAlgorithm']['parameters'] = + array('type' => FILE_ASN1_TYPE_UTF8_STRING); + + if (empty($crl['tbsCertList']['signature']['parameters'])) { + $filters['tbsCertList']['signature']['parameters'] = + array('type' => FILE_ASN1_TYPE_NULL); + } + + if (empty($crl['signatureAlgorithm']['parameters'])) { + $filters['signatureAlgorithm']['parameters'] = + array('type' => FILE_ASN1_TYPE_NULL); + } + + $asn1->loadFilters($filters); + + $this->_mapOutExtensions($crl, 'tbsCertList/crlExtensions', $asn1); + $rclist = &$this->_subArray($crl,'tbsCertList/revokedCertificates'); + if (is_array($rclist)) { + foreach ($rclist as $i => $extension) { + $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); + } + } + + $crl = $asn1->encodeDER($crl, $this->CertificateList); + + return "-----BEGIN X509 CRL-----\r\n" . chunk_split(base64_encode($crl)) . '-----END X509 CRL-----'; + } + /** * Sign an X.509 certificate * @@ -2316,6 +2582,8 @@ class File_X509 { if (isset($subject->domains)) { $this->removeExtension('id-ce-subjectAltName'); } + } else if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertList'])) { + return false; } else { if (!isset($subject->publicKey)) { return false; @@ -2465,6 +2733,136 @@ class File_X509 { return $result; } + /** + * Sign a CRL + * + * $issuer's private key needs to be loaded. + * + * @param File_X509 $issuer + * @param File_X509 $crl + * @param String $signatureAlgorithm optional + * @access public + * @return Mixed + */ + function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') + { + if (!is_object($issuer->privateKey) || empty($issuer->dn)) { + return false; + } + + $currentCert = isset($this->currentCert) ? $this->currentCert : NULL; + $signatureSubject = isset($this->signatureSubject) ? $this->signatureSubject : NULL; + $thisUpdate = !empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T'); + + if (isset($crl->currentCert) && is_array($crl->currentCert) && isset($crl->currentCert['tbsCertList'])) { + $this->currentCert = $crl->currentCert; + $this->currentCert['tbsCertList']['signature']['algorithm'] = $signatureAlgorithm; + $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; + } else { + $this->currentCert = array( + 'tbsCertList' => + array( + 'version' => 'v2', + 'signature' => array('algorithm' => $signatureAlgorithm), + 'issuer' => false, // this is going to be overwritten later + 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate() + ), + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later + ); + } + + $tbsCertList = &$this->currentCert['tbsCertList']; + $tbsCertList['issuer'] = $issuer->dn; + $tbsCertList['thisUpdate'] = array('generalTime' => $thisUpdate); + + if (!empty($this->endDate)) { + $tbsCertList['nextUpdate'] = array('generalTime' => $this->endDate); // $this->setEndDate() + } + else { + unset($tbsCertList['nextUpdate']); + } + + if (!empty($this->serialNumber)) { + $crlNumber = $this->serialNumber; + } + else { + $crlNumber = $this->getCRLExtension('id-ce-cRLNumber'); + $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : NULL; + } + + $this->removeCRLExtension('id-ce-authorityKeyIdentifier'); + $this->removeCRLExtension('id-ce-issuerAltName'); + + // Be sure version >= v2 if some extension found. + $version = isset($tbsCertList['version']) ? $tbsCertList['version'] : 0; + if (!$version) { + if (!empty($tbsCertList['crlExtensions'])) { + $version = 1; // v2. + } + elseif (!empty($tbsCertList['revokedCertificates'])) { + foreach ($tbsCertList['revokedCertificates'] as $cert) { + if (!empty($cert['crlEntryExtensions'])) { + $version = 1; // v2. + } + } + } + + if ($version) { + $tbsCertList['version'] = $version; + } + } + + // Store additional extensions. + if (!empty($tbsCertList['version'])) { // At least v2. + if (!empty($crlNumber)) { + $this->setCRLExtension('id-ce-cRLNumber', $crlNumber); + } + + if (isset($issuer->keyIdentifier)) { + $this->setCRLExtension('id-ce-authorityKeyIdentifier', array( + //'authorityCertIssuer' => array( + // array( + // 'directoryName' => $issuer->dn + // ) + //), + 'keyIdentifier' => $issuer->keyIdentifier + ) + ); + //$extensions = &$tbsCertList['crlExtensions']; + //if (isset($issuer->serialNumber)) { + // $extensions[count($extensions) - 1]['authorityCertSerialNumber'] = $issuer->serialNumber; + //} + //unset($extensions); + } + + $issuerAltName = $this->getExtension('id-ce-subjectAltName', $issuer->currentCert); + + if ($issuerAltName !== false) { + $this->setCRLExtension('id-ce-issuerAltName', $issuerAltName); + } + } + + if (empty($tbsCertList['revokedCertificates'])) { + unset($tbsCertList['revokedCertificates']); + } + + unset($tbsCertList); + + // resync $this->signatureSubject + // save $tbsCertList in case there are any File_ASN1_Element objects in it + $tbsCertList = $this->currentCert['tbsCertList']; + $this->loadCRL($this->saveCRL($this->currentCert)); + + $result = $this->_sign($issuer->privateKey, $signatureAlgorithm); + $result['tbsCertList'] = $tbsCertList; + + $this->currentCert = $currentCert; + $this->signatureSubject = $signatureSubject; + + return $result; + } + /** * X.509 certificate signing helper function. * @@ -2619,6 +3017,18 @@ class File_X509 { return $result; } + /** + * Remove a CRL Extension + * + * @param String $id + * @access public + * @return Boolean + */ + function removeCRLExtension($id) + { + return $this->removeExtension($id, 'tbsCertList/crlExtensions'); + } + /** * Get an Extension * @@ -2651,6 +3061,21 @@ class File_X509 { return false; } + /** + * Get a CRL Extension + * + * Returns the extension if it exists and false if not + * + * @param String $id + * @param Array $crl optional + * @access public + * @return Mixed + */ + function getCRLExtension($id, $crl = NULL) + { + return $this->getExtension($id, $crl, 'tbsCertList/crlExtensions'); + } + /** * Returns a list of all extensions in use * @@ -2677,6 +3102,18 @@ class File_X509 { return $extensions; } + /** + * Returns a list of all CRL extensions in use + * + * @param array $crl optional + * @access public + * @return Array + */ + function getCRLExtensions($crl = NULL) + { + return $this->getExtensions($crl, 'tbsCertList/crlExtensions'); + } + /** * Set an Extension * @@ -2713,6 +3150,21 @@ class File_X509 { return true; } + /** + * Set a CRL Extension + * + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @access public + * @return Boolean + */ + function setCRLExtension($id, $value, $critical = false, $replace = true) + { + return $this->setExtension($id, $value, $critical, $replace, 'tbsCertList/crlExtensions'); + } + /** * Sets the authority key identifier * @@ -2768,4 +3220,220 @@ class File_X509 { $this->removeDNProp('id-at-commonName'); $this->setDNProp('id-at-commonName', $this->domains[0]); } + + /** + * Get the index of a revoked certificate. + * + * @param array $rclist + * @param String $serial + * @param Boolean $create optional + * @access private + * @return Integer or false + */ + + function _revokedCertificate(&$rclist, $serial, $create = false) + { + $serial = new Math_BigInteger($serial); + + foreach ($rclist as $i => $rc) { + if (!($serial->compare($rc['userCertificate']))) { + return $i; + } + } + + if (!$create) { + return false; + } + + $i = count($rclist); + $rclist[] = array('userCertificate' => $serial, + 'revocationDate' => array('generalTime' => @date('M j H:i:s Y T'))); + return $i; + } + + /** + * Revoke a certificate. + * + * @param String $serial + * @param String $date optional + * @access public + * @return Boolean + */ + function revoke($serial, $date = NULL) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if ($this->_revokedCertificate($rclist, $serial) === false) { // If not yet revoked + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + + if (!empty($date)) { + $rclist[$i]['revocationDate'] = array('generalTime' => $date); + } + + return true; + } + } + } + } + + return false; + } + + /** + * Unrevoke a certificate. + * + * @param String $serial + * @access public + * @return Boolean + */ + function unrevoke($serial) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + unset($rclist[$i]); + $rclist = array_values($rclist); + return true; + } + } + + return false; + } + + /** + * Get a revoked certificate. + * + * @param String $serial + * @access public + * @return Mixed + */ + function getRevoked($serial) + { + if (is_array($rclist = $this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $rclist[$i]; + } + } + + return false; + } + + /** + * List revoked certificates + * + * @param array $crl optional + * @access public + * @return array + */ + function listRevoked($crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (!isset($crl['tbsCertList'])) { + return false; + } + + $result = array(); + + if (!is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + foreach ($rclist as $rc) { + $result[] = $rc['userCertificate']->toString(); + } + } + + return $result; + } + + /** + * Remove a Revoked Certificate Extension + * + * @param String $serial + * @param String $id + * @access public + * @return Boolean + */ + function removeRevokedCertificateExtension($serial, $id) + { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Get a Revoked Certificate Extension + * + * Returns the extension if it exists and false if not + * + * @param String $serial + * @param String $id + * @param Array $crl optional + * @access public + * @return Mixed + */ + function getRevokedCertificateExtension($serial, $id, $crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Returns a list of all extensions in use for a given revoked certificate + * + * @param String $serial + * @param array $crl optional + * @access public + * @return Array + */ + function getRevokedCertificateExtensions($serial, $crl = NULL) + { + if (!isset($crl)) { + $crl = $this->currentCert; + } + + if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { + if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { + return $this->getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + + return false; + } + + /** + * Set a Revoked Certificate Extension + * + * @param String $serial + * @param String $id + * @param Mixed $value + * @param Boolean $critical optional + * @param Boolean $replace optional + * @access public + * @return Boolean + */ + function setRevokedCertificateExtension($serial, $id, $value, $critical = false, $replace = true) + { + if (isset($this->currentCert['tbsCertList'])) { + if (is_array($rclist = &$this->_subArray($this->currentCert, 'tbsCertList/revokedCertificates', true))) { + if (($i = $this->_revokedCertificate($rclist, $serial, true)) !== false) { + return $this->setExtension($id, $value, $critical, $replace, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); + } + } + } + + return false; + } } From ef96f777c3a93a68ae6719fd742bebfcd1c0fba7 Mon Sep 17 00:00:00 2001 From: monnerat Date: Fri, 12 Oct 2012 03:29:25 +0100 Subject: [PATCH 11/12] X509: setSerialNumber(): new optional parameter $base --- phpseclib/File/X509.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index e65ec893..c5532c9b 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -2935,11 +2935,12 @@ class File_X509 { * Set Serial Number * * @param String $serial + * @param $base optional * @access public */ - function setSerialNumber($serial) + function setSerialNumber($serial, $base = -256) { - $this->serialNumber = new Math_BigInteger($serial, -256); + $this->serialNumber = new Math_BigInteger($serial, $base); } /** From 2c7c7b9679dc7cde800146268d4168e6d30bf013 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Fri, 12 Oct 2012 16:17:34 +0200 Subject: [PATCH 12/12] X509, ASN1: Fix CS and indent. Remove tabs. --- phpseclib/File/ASN1.php | 4 +- phpseclib/File/X509.php | 106 ++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index 41d0a502..dc536655 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -728,10 +728,10 @@ class File_ASN1 { return $source->element; } - // do not encode optional fields with value set to default + // do not encode optional fields with value set to default if (!empty($mapping['optional']) && isset($mapping['default']) && $source === $mapping['default']) { return ''; - } + } if (isset($idx)) { $this->location[] = $idx; diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index c5532c9b..991f11c0 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -1593,7 +1593,7 @@ class File_X509 { case '2.23.42.7.0': // id-set-hashedRootKey return true; - // CRL extensions. + // CRL extensions. case 'id-ce-cRLNumber': return $this->CRLNumber; case 'id-ce-deltaCRLIndicator': @@ -1851,7 +1851,7 @@ class File_X509 { } if (!isset($signingCert)) { return 0; - } + } return $this->_validateSignature( $signingCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $signingCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], @@ -2473,8 +2473,8 @@ class File_X509 { if (is_array($rclist)) { foreach ($rclist as $i => $extension) { $this->_mapInExtensions($rclist, "$i/crlEntryExtensions", $asn1); - } - } + } + } $this->keyIdentifier = NULL; $this->currentCert = $crl; @@ -2508,12 +2508,12 @@ class File_X509 { if (empty($crl['tbsCertList']['signature']['parameters'])) { $filters['tbsCertList']['signature']['parameters'] = array('type' => FILE_ASN1_TYPE_NULL); - } + } if (empty($crl['signatureAlgorithm']['parameters'])) { $filters['signatureAlgorithm']['parameters'] = array('type' => FILE_ASN1_TYPE_NULL); - } + } $asn1->loadFilters($filters); @@ -2522,8 +2522,8 @@ class File_X509 { if (is_array($rclist)) { foreach ($rclist as $i => $extension) { $this->_mapOutExtensions($rclist, "$i/crlEntryExtensions", $asn1); - } - } + } + } $crl = $asn1->encodeDER($crl, $this->CertificateList); @@ -2594,7 +2594,7 @@ class File_X509 { $serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger(); $this->currentCert = array( - 'tbsCertificate' => + 'tbsCertificate' => array( 'version' => 'v3', 'serialNumber' => $serialNumber, // $this->setserialNumber() @@ -2607,8 +2607,8 @@ class File_X509 { 'subject' => $subject->dn, 'subjectPublicKeyInfo' => $subjectPublicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } @@ -2648,7 +2648,7 @@ class File_X509 { } $this->setExtension('id-ce-keyUsage', - array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) + array_values(array_unique(array_merge($keyUsage, array('cRLSign', 'keyCertSign')))) ); $basicConstraints = $this->getExtension('id-ce-basicConstraints'); @@ -2657,7 +2657,7 @@ class File_X509 { } $this->setExtension('id-ce-basicConstraints', - array_unique(array_merge(array('cA' => true), $basicConstraints)), true); + array_unique(array_merge(array('cA' => true), $basicConstraints)), true); } // resync $this->signatureSubject @@ -2708,14 +2708,14 @@ class File_X509 { $this->currentCert['certificationRequestInfo']['subjectPKInfo'] = $publicKey; } else { $this->currentCert = array( - 'certificationRequestInfo' => + 'certificationRequestInfo' => array( 'version' => 'v1', 'subject' => $this->dn, 'subjectPKInfo' => $publicKey ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } @@ -2760,15 +2760,15 @@ class File_X509 { $this->currentCert['signatureAlgorithm']['algorithm'] = $signatureAlgorithm; } else { $this->currentCert = array( - 'tbsCertList' => + 'tbsCertList' => array( 'version' => 'v2', 'signature' => array('algorithm' => $signatureAlgorithm), 'issuer' => false, // this is going to be overwritten later 'thisUpdate' => array('generalTime' => $thisUpdate) // $this->setStartDate() ), - 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), - 'signature' => false // this is going to be overwritten later + 'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm), + 'signature' => false // this is going to be overwritten later ); } @@ -2785,7 +2785,7 @@ class File_X509 { if (!empty($this->serialNumber)) { $crlNumber = $this->serialNumber; - } + } else { $crlNumber = $this->getCRLExtension('id-ce-cRLNumber'); $crlNumber = $crlNumber !== false ? $crlNumber->add(new Math_BigInteger(1)) : NULL; @@ -2799,25 +2799,25 @@ class File_X509 { if (!$version) { if (!empty($tbsCertList['crlExtensions'])) { $version = 1; // v2. - } + } elseif (!empty($tbsCertList['revokedCertificates'])) { foreach ($tbsCertList['revokedCertificates'] as $cert) { if (!empty($cert['crlEntryExtensions'])) { $version = 1; // v2. - } - } - } + } + } + } if ($version) { $tbsCertList['version'] = $version; - } + } } // Store additional extensions. if (!empty($tbsCertList['version'])) { // At least v2. if (!empty($crlNumber)) { $this->setCRLExtension('id-ce-cRLNumber', $crlNumber); - } + } if (isset($issuer->keyIdentifier)) { $this->setCRLExtension('id-ce-authorityKeyIdentifier', array( @@ -2840,12 +2840,12 @@ class File_X509 { if ($issuerAltName !== false) { $this->setCRLExtension('id-ce-issuerAltName', $issuerAltName); - } + } } if (empty($tbsCertList['revokedCertificates'])) { unset($tbsCertList['revokedCertificates']); - } + } unset($tbsCertList); @@ -2968,17 +2968,17 @@ class File_X509 { if (!is_array($root)) { return $false; - } + } foreach (explode('/', $path) as $i) { if (!is_array($root)) { return $false; - } + } if (!isset($root[$i])) { if (!$create) { return $false; - } + } $root[$i] = array(); } @@ -3002,8 +3002,8 @@ class File_X509 { $extensions = &$this->_subArray($this->currentCert, $path); if (!is_array($extensions)) { - return false; - } + return false; + } $result = false; foreach ($extensions as $key => $value) { @@ -3050,8 +3050,8 @@ class File_X509 { $extensions = $this->_subArray($cert, $path); if (!is_array($extensions)) { - return false; - } + return false; + } foreach ($extensions as $key => $value) { if ($value['extnId'] == $id) { @@ -3095,10 +3095,10 @@ class File_X509 { $extensions = array(); if (is_array($exts)) { - foreach ($exts as $extension) { - $extensions[] = $extension['extnId']; - } - } + foreach ($exts as $extension) { + $extensions[] = $extension['extnId']; + } + } return $extensions; } @@ -3131,8 +3131,8 @@ class File_X509 { $extensions = &$this->_subArray($this->currentCert, $path, true); if (!is_array($extensions)) { - return false; - } + return false; + } $newext = array('extnId' => $id, 'critical' => $critical, 'extnValue' => $value); @@ -3140,7 +3140,7 @@ class File_X509 { if ($value['extnId'] == $id) { if (!$replace) { return false; - } + } $extensions[$key] = $newext; return true; @@ -3239,12 +3239,12 @@ class File_X509 { foreach ($rclist as $i => $rc) { if (!($serial->compare($rc['userCertificate']))) { return $i; - } - } + } + } if (!$create) { return false; - } + } $i = count($rclist); $rclist[] = array('userCertificate' => $serial, @@ -3295,7 +3295,7 @@ class File_X509 { $rclist = array_values($rclist); return true; } - } + } return false; } @@ -3313,7 +3313,7 @@ class File_X509 { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $rclist[$i]; } - } + } return false; } @@ -3333,7 +3333,7 @@ class File_X509 { if (!isset($crl['tbsCertList'])) { return false; - } + } $result = array(); @@ -3360,7 +3360,7 @@ class File_X509 { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->removeExtension($id, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } - } + } return false; } @@ -3380,13 +3380,13 @@ class File_X509 { { if (!isset($crl)) { $crl = $this->currentCert; - } + } if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->getExtension($id, $crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } - } + } return false; } @@ -3403,13 +3403,13 @@ class File_X509 { { if (!isset($crl)) { $crl = $this->currentCert; - } + } if (is_array($rclist = $this->_subArray($crl, 'tbsCertList/revokedCertificates'))) { if (($i = $this->_revokedCertificate($rclist, $serial)) !== false) { return $this->getExtensions($crl, "tbsCertList/revokedCertificates/$i/crlEntryExtensions"); } - } + } return false; }