mirror of
https://github.com/phpseclib/phpseclib.git
synced 2024-11-09 15:20:58 +00:00
- id-at-organizationalUnitName was misnamed as id-at-dnQualifier
- make it so CA's can't be loaded if the keyusage extension doesn't permit their being loaded - implement validateURL() function stub - add support for a few more DN attributes - add removeDNProp(), getDNProp() and setDomain() - fixed some issues preventing new certs from being signed git-svn-id: http://phpseclib.svn.sourceforge.net/svnroot/phpseclib/trunk@213 21d32557-59b3-4da0-833f-c5933fad653e
This commit is contained in:
parent
392ff50c00
commit
3dd9e2b318
@ -1029,17 +1029,17 @@ class File_X509 {
|
|||||||
'2.5.4.7' => 'id-at-localityName',
|
'2.5.4.7' => 'id-at-localityName',
|
||||||
'2.5.4.8' => 'id-at-stateOrProvinceName',
|
'2.5.4.8' => 'id-at-stateOrProvinceName',
|
||||||
'2.5.4.10' => 'id-at-organizationName',
|
'2.5.4.10' => 'id-at-organizationName',
|
||||||
'2.5.4.11' => 'id-at-dnQualifier',
|
'2.5.4.11' => 'id-at-organizationalUnitName',
|
||||||
'2.5.4.12' => 'id-at-title',
|
'2.5.4.12' => 'id-at-title',
|
||||||
'2.5.4.46' => 'id-at-dnQualifier',
|
'2.5.4.46' => 'id-at-dnQualifier',
|
||||||
'2.5.4.6' => 'id-at-countryName',
|
'2.5.4.6' => 'id-at-countryName',
|
||||||
'2.5.4.5' => 'id-at-serialNumber',
|
'2.5.4.5' => 'id-at-serialNumber',
|
||||||
'2.5.4.65' => 'id-at-pseudonym',
|
'2.5.4.65' => 'id-at-pseudonym',
|
||||||
|
'2.5.4.17' => 'id-at-postalCode',
|
||||||
|
'2.5.4.9' => 'id-at-streetAddress',
|
||||||
'0.9.2342.19200300.100.1.25' => 'id-domainComponent',
|
'0.9.2342.19200300.100.1.25' => 'id-domainComponent',
|
||||||
'1.2.840.113549.1.9' => 'pkcs-9',
|
'1.2.840.113549.1.9' => 'pkcs-9',
|
||||||
'1.2.840.113549.1.9.1' => 'id-emailAddress',
|
'1.2.840.113549.1.9.1' => 'id-emailAddress',
|
||||||
'2.5.4.17' => 'id-at-postalCode',
|
|
||||||
'2.5.4.9' => 'id-at-streetAddress',
|
|
||||||
'2.5.29' => 'id-ce',
|
'2.5.29' => 'id-ce',
|
||||||
'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
|
'2.5.29.35' => 'id-ce-authorityKeyIdentifier',
|
||||||
'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
|
'2.5.29.14' => 'id-ce-subjectKeyIdentifier',
|
||||||
@ -1249,6 +1249,7 @@ class File_X509 {
|
|||||||
$key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
|
$key = $this->_reformatKey($x509['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm'], $key);
|
||||||
|
|
||||||
$this->currentCert = $x509;
|
$this->currentCert = $x509;
|
||||||
|
$this->dn = $x509['tbsCertificate']['subject'];
|
||||||
|
|
||||||
return $x509;
|
return $x509;
|
||||||
}
|
}
|
||||||
@ -1419,23 +1420,96 @@ class File_X509 {
|
|||||||
*
|
*
|
||||||
* @param String $cert
|
* @param String $cert
|
||||||
* @access public
|
* @access public
|
||||||
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
function loadCA($cert)
|
function loadCA($cert)
|
||||||
{
|
{
|
||||||
$this->CAs[] = $this->loadX509($cert);
|
/* From RFC5280 "PKIX Certificate and CRL Profile":
|
||||||
|
|
||||||
|
If the keyUsage extension is present, then the subject public key
|
||||||
|
MUST NOT be used to verify signatures on certificates or CRLs unless
|
||||||
|
the corresponding keyCertSign or cRLSign bit is set. */
|
||||||
|
$cert = $this->loadX509($cert);
|
||||||
|
if (!$cert) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyUsage = $x509->getExtension('id-ce-keyUsage');
|
||||||
|
if ($keyUsage && !in_array('keyCertSign', $keyUsage)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->CAs[] = $cert;
|
||||||
unset($this->currentCert);
|
unset($this->currentCert);
|
||||||
unset($this->signatureSubject);
|
unset($this->signatureSubject);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate an X.509 certificate against a URL
|
* Validate an X.509 certificate against a URL
|
||||||
*
|
*
|
||||||
|
* From RFC2818 "HTTP over TLS":
|
||||||
|
*
|
||||||
|
* Matching is performed using the matching rules specified by
|
||||||
|
* [RFC2459]. If more than one identity of a given type is present in
|
||||||
|
* the certificate (e.g., more than one dNSName name, a match in any one
|
||||||
|
* of the set is considered acceptable.) Names may contain the wildcard
|
||||||
|
* character * which is considered to match any single domain name
|
||||||
|
* component or component fragment. E.g., *.a.com matches foo.a.com but
|
||||||
|
* not bar.foo.a.com. f*.com matches foo.com but not bar.com.
|
||||||
|
*
|
||||||
* @param String $url
|
* @param String $url
|
||||||
* @access public
|
* @access public
|
||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
function validateURL($url)
|
function validateURL($url)
|
||||||
{
|
{
|
||||||
|
if (!is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$components = parse_url($url);
|
||||||
|
if (!isset($components['host'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($names = $this->getExtension('id-ce-subjectAltName')) {
|
||||||
|
foreach ($names as $key => $value) {
|
||||||
|
$value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value);
|
||||||
|
switch ($key) {
|
||||||
|
case 'dNSName':
|
||||||
|
/* From RFC2818 "HTTP over TLS":
|
||||||
|
|
||||||
|
If a subjectAltName extension of type dNSName is present, that MUST
|
||||||
|
be used as the identity. Otherwise, the (most specific) Common Name
|
||||||
|
field in the Subject field of the certificate MUST be used. Although
|
||||||
|
the use of the Common Name is existing practice, it is deprecated and
|
||||||
|
Certification Authorities are encouraged to use the dNSName instead. */
|
||||||
|
if (preg_match('#^' . $value . '$#', $components['host'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'iPAddress':
|
||||||
|
/* From RFC2818 "HTTP over TLS":
|
||||||
|
|
||||||
|
In some cases, the URI is specified as an IP address rather than a
|
||||||
|
hostname. In this case, the iPAddress subjectAltName must be present
|
||||||
|
in the certificate and must exactly match the IP in the URI. */
|
||||||
|
if (preg_match('#(?:\d{1-3}\.){4}#', $components['host'] . '.') && preg_match('#^' . $value . '$#', $components['host'])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value = $this->getDNProp('id-at-commonName')) {
|
||||||
|
$value = str_replace(array('.', '*'), array('\.', '[^.]*'), $value[0]);
|
||||||
|
return preg_match('#^' . $value . '$#', $components['host']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1680,6 +1754,33 @@ class File_X509 {
|
|||||||
case 'streetaddress':
|
case 'streetaddress':
|
||||||
$type = 'id-at-streetAddress';
|
$type = 'id-at-streetAddress';
|
||||||
break;
|
break;
|
||||||
|
case 'id-at-name':
|
||||||
|
case 'name':
|
||||||
|
$type = 'id-at-name';
|
||||||
|
case 'id-at-givenname':
|
||||||
|
case 'givenname':
|
||||||
|
$type = 'id-at-givenName';
|
||||||
|
break;
|
||||||
|
case 'id-at-surname':
|
||||||
|
case 'surname':
|
||||||
|
$type = 'id-at-surname';
|
||||||
|
break;
|
||||||
|
case 'id-at-initials':
|
||||||
|
case 'initials':
|
||||||
|
$type = 'id-at-initials';
|
||||||
|
break;
|
||||||
|
case 'id-at-generationqualifier':
|
||||||
|
case 'generationqualifier':
|
||||||
|
$type = 'id-at-generationQualifier';
|
||||||
|
break;
|
||||||
|
case 'id-at-organizationalunitname':
|
||||||
|
case 'organizationalunitname':
|
||||||
|
$type = 'id-at-organizationalUnitName';
|
||||||
|
break;
|
||||||
|
case 'id-at-pseudonym':
|
||||||
|
case 'pseudonym':
|
||||||
|
$type = 'id-at-pseudonym';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1694,6 +1795,53 @@ class File_X509 {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove Distinguished Name properties
|
||||||
|
*
|
||||||
|
* @param String $propName
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
function removeDNProp($propName)
|
||||||
|
{
|
||||||
|
if (empty($this->dn)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dn = &$this->dn['rdnSequence'];
|
||||||
|
$size = count($dn);
|
||||||
|
for ($i = 0; $i < $size; $i++) {
|
||||||
|
if ($dn[$i][0]['type'] == $propName) {
|
||||||
|
unset($dn[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dn = array_values($dn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Distinguished Name properties
|
||||||
|
*
|
||||||
|
* @param String $propName
|
||||||
|
* @return Mixed
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
function getDNProp($propName)
|
||||||
|
{
|
||||||
|
if (empty($this->dn)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$dn = $this->dn['rdnSequence'];
|
||||||
|
$result = array();
|
||||||
|
for ($i = 0; $i < $size; $i++) {
|
||||||
|
if ($dn[$i][0]['type'] == $propName) {
|
||||||
|
$result[] = $propName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a Distinguished Name
|
* Set a Distinguished Name
|
||||||
*
|
*
|
||||||
@ -1741,7 +1889,7 @@ class File_X509 {
|
|||||||
function getDN($string = false, $dn = NULL)
|
function getDN($string = false, $dn = NULL)
|
||||||
{
|
{
|
||||||
if (!isset($dn)) {
|
if (!isset($dn)) {
|
||||||
$dn = $this->currentCert['tbsCertificate']['subject'];
|
$dn = $this->dn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$string) {
|
if (!$string) {
|
||||||
@ -1809,7 +1957,6 @@ class File_X509 {
|
|||||||
*
|
*
|
||||||
* @param Object $key
|
* @param Object $key
|
||||||
* @access public
|
* @access public
|
||||||
* @return Boolean
|
|
||||||
*/
|
*/
|
||||||
function setPrivateKey($key)
|
function setPrivateKey($key)
|
||||||
{
|
{
|
||||||
@ -1817,17 +1964,14 @@ class File_X509 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the public key
|
* Gets the public key
|
||||||
*
|
*
|
||||||
* Keys need to be Crypt_RSA objects
|
|
||||||
*
|
|
||||||
* @param Object $key
|
|
||||||
* @access public
|
* @access public
|
||||||
* @return Boolean
|
* @return Object
|
||||||
*/
|
*/
|
||||||
function getPublicKey($key)
|
function getPublicKey()
|
||||||
{
|
{
|
||||||
$this->publicKey = $key;
|
//return
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1908,6 +2052,9 @@ class File_X509 {
|
|||||||
$signatureSubject = $this->signatureSubject;
|
$signatureSubject = $this->signatureSubject;
|
||||||
|
|
||||||
if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
|
if (isset($subject->currentCert) && is_array($subject->currentCert) && isset($subject->currentCert['tbsCertificate'])) {
|
||||||
|
$this->currentCert['tbsCertificate']['signature']['algorithm'] =
|
||||||
|
$this->currentCert['signatureAlgorithm']['algorithm'] =
|
||||||
|
$signatureAlgorithm;
|
||||||
$this->currentCert = $subject->currentCert;
|
$this->currentCert = $subject->currentCert;
|
||||||
if (!empty($this->startDate)) {
|
if (!empty($this->startDate)) {
|
||||||
$this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime'] = $this->startDate;
|
$this->currentCert['tbsCertificate']['validity']['notBefore']['utcTime'] = $this->startDate;
|
||||||
@ -1927,21 +2074,24 @@ class File_X509 {
|
|||||||
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
|
$this->currentCert['tbsCertificate']['subjectPublicKeyInfo'] = $subjectPublicKey;
|
||||||
}
|
}
|
||||||
$this->removeExtension('id-ce-authorityKeyIdentifier');
|
$this->removeExtension('id-ce-authorityKeyIdentifier');
|
||||||
|
if (isset($subject->domains)) {
|
||||||
|
$this->removeExtension('id-ce-subjectAltName');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isset($subject->publicKey)) {
|
if (!isset($subject->publicKey)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$startDate = empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
|
$startDate = !empty($this->startDate) ? $this->startDate : @date('M j H:i:s Y T');
|
||||||
$endDate = empty($this->endDate) ? $this->endDate : @date('M j H:i:s Y T', strtotime('+1 year'));
|
$endDate = !empty($this->endDate) ? $this->endDate : @date('M j H:i:s Y T', strtotime('+1 year'));
|
||||||
$serialNumber = empty($this->serialNumber) ? $this->serialNumber : "\0";
|
$serialNumber = !empty($this->serialNumber) ? $this->serialNumber : new Math_BigInteger();
|
||||||
|
|
||||||
$this->currentCert = array(
|
$this->currentCert = array(
|
||||||
'tbsCertificate' =>
|
'tbsCertificate' =>
|
||||||
array(
|
array(
|
||||||
'version' => 'v3',
|
'version' => 'v3',
|
||||||
'serialNumber' => $serialNumber, // $this->setserialNumber()
|
'serialNumber' => $serialNumber, // $this->setserialNumber()
|
||||||
'signature' => $signatureAlgorithm,
|
'signature' => array('algorithm' => $signatureAlgorithm),
|
||||||
'issuer' => false, // this is going to be overwritten later
|
'issuer' => false, // this is going to be overwritten later
|
||||||
'validity' => array(
|
'validity' => array(
|
||||||
'notBefore' => array('utcTime' => $startDate), // $this->setStartDate()
|
'notBefore' => array('utcTime' => $startDate), // $this->setStartDate()
|
||||||
@ -1950,7 +2100,7 @@ class File_X509 {
|
|||||||
'subject' => $subject->dn,
|
'subject' => $subject->dn,
|
||||||
'subjectPublicKeyInfo' => $subjectPublicKey
|
'subjectPublicKeyInfo' => $subjectPublicKey
|
||||||
),
|
),
|
||||||
'signatureAlgorithm' => $signatureAlgorithm,
|
'signatureAlgorithm' => array('algorithm' => $signatureAlgorithm),
|
||||||
'signature' => false // this is going to be overwritten later
|
'signature' => false // this is going to be overwritten later
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1986,6 +2136,18 @@ class File_X509 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resync $this->signatureSubject
|
// resync $this->signatureSubject
|
||||||
$this->loadX509($this->saveX509($this->currentCert));
|
$this->loadX509($this->saveX509($this->currentCert));
|
||||||
|
|
||||||
@ -2022,7 +2184,6 @@ class File_X509 {
|
|||||||
$key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
$key->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
|
||||||
|
|
||||||
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
|
$this->currentCert['signature'] = base64_encode("\0" . $key->sign($this->signatureSubject));
|
||||||
|
|
||||||
return $this->currentCert;
|
return $this->currentCert;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@ -2152,7 +2313,7 @@ class File_X509 {
|
|||||||
* Sets the authority key identifier
|
* Sets the authority key identifier
|
||||||
*
|
*
|
||||||
* This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
|
* This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions.
|
||||||
*
|
*
|
||||||
* @param String $value
|
* @param String $value
|
||||||
* @access public
|
* @access public
|
||||||
*/
|
*/
|
||||||
@ -2167,8 +2328,8 @@ class File_X509 {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a public key as appropriate
|
* Format a public key as appropriate
|
||||||
*
|
*
|
||||||
* @access public
|
* @access private
|
||||||
* @return Array
|
* @return Array
|
||||||
*/
|
*/
|
||||||
function _formatSubjectPublicKey()
|
function _formatSubjectPublicKey()
|
||||||
@ -2187,4 +2348,17 @@ class File_X509 {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the domain name's which the cert is to be valid for
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @return Array
|
||||||
|
*/
|
||||||
|
function setDomain()
|
||||||
|
{
|
||||||
|
$this->domains = func_get_args();
|
||||||
|
$this->removeDNProp('id-at-commonName');
|
||||||
|
$this->setDNProp('id-at-commonName', $this->domains[0]);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user