From 26b842be5b4a2963921498b5be28043cc2ab13de Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Mon, 29 Oct 2012 18:21:25 +0100 Subject: [PATCH] X509: compute public key identifiers. Force subject public key identifier when signing a CA. --- phpseclib/Crypt/RSA.php | 3 +- phpseclib/File/X509.php | 97 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/phpseclib/Crypt/RSA.php b/phpseclib/Crypt/RSA.php index 567cbff8..7df1ded7 100644 --- a/phpseclib/Crypt/RSA.php +++ b/phpseclib/Crypt/RSA.php @@ -856,6 +856,7 @@ class Crypt_RSA { ); if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) { + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA $RSAPublicKey = chr(0) . $RSAPublicKey; $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey; @@ -2636,4 +2637,4 @@ class Crypt_RSA { return $this->_rsassa_pss_verify($message, $signature); } } -} \ No newline at end of file +} diff --git a/phpseclib/File/X509.php b/phpseclib/File/X509.php index 6b90ba79..d63cfcf6 100644 --- a/phpseclib/File/X509.php +++ b/phpseclib/File/X509.php @@ -2343,13 +2343,21 @@ class File_X509 { return $this->publicKey; } - if (!isset($this->currentCert) || !is_array($this->currentCert) || !isset($this->currentCert['tbsCertificate'])) { + if (isset($this->currentCert) && is_array($this->currentCert)) { + foreach (array('tbsCertificate/subjectPublicKeyInfo', 'certificationRequestInfo/subjectPKInfo') as $path) { + $keyinfo = $this->_subArray($this->currentCert, $path); + if (!empty($keyinfo)) { + break; + } + } + } + if (empty($keyinfo)) { return false; } - $key = $this->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']; + $key = $keyinfo['subjectPublicKey']; - switch ($this->currentCert['tbsCertificate']['subjectPublicKeyInfo']['algorithm']['algorithm']) { + switch ($keyinfo['algorithm']['algorithm']) { case 'rsaEncryption': if (!class_exists('Crypt_RSA')) { require_once('Crypt/RSA.php'); @@ -2686,6 +2694,10 @@ class File_X509 { $this->setExtension('id-ce-basicConstraints', array_unique(array_merge(array('cA' => true), $basicConstraints)), true); + + if (!isset($subject->currentKeyIdentifier)) { + $this->setExtension('id-ce-subjectKeyIdentifier', base64_encode($this->computeKeyIdentifier($this->currentCert)), false, false); + } } // resync $this->signatureSubject @@ -3195,7 +3207,7 @@ class File_X509 { } /** - * Sets the authority key identifier + * Sets the subject key identifier * * This is used by the id-ce-authorityKeyIdentifier and the id-ce-subjectKeyIdentifier extensions. * @@ -3211,6 +3223,83 @@ class File_X509 { } } + /** + * Compute a public key identifier. + * + * Although key identifiers may be set to any unique value, this function + * computes key identifiers from public key according to the two + * recommended methods (4.2.1.2 RFC 3280). + * Highly polymorphic: try to accept all possible forms of key: + * - Key object + * - File_X509 object with public or private key defined + * - Certificate or CSR array + * - File_ASN1_Element object + * - PEM or DER string + * + * @param Mixed $key optional + * @param Integer $method optional + * @access public + * @return String binary key identifier + */ + function computeKeyIdentifier($key = NULL, $method = 1) + { + if (is_null($key)) { + $key = $this; + } + + switch (true) { + case is_string($key): + break; + case is_array($key) && isset($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'], $method); + case is_array($key) && isset($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey']): + return $this->computeKeyIdentifier($key['certificationRequestInfo']['subjectPKInfo']['subjectPublicKey'], $method); + case !is_object($key): + return false; + case strtolower(get_class($key)) == 'file_asn1_element': + $asn1 = new File_ASN1(); + $decoded = $asn1->decodeBER($cert); + if (empty($decoded)) { + return false; + } + $key = $asn1->asn1map($decoded[0], array('type' => FILE_ASN1_TYPE_BIT_STRING)); + break; + case strtolower(get_class($key)) == 'file_x509': + if (isset($key->publicKey)) { + return $this->computeKeyIdentifier($key->publicKey, $method); + } + if (isset($key->privateKey)) { + return $this->computeKeyIdentifier($key->privateKey, $method); + } + if (isset($key->currentCert['tbsCertificate']) || isset($key->currentCert['certificationRequestInfo'])) { + return $this->computeKeyIdentifier($key->currentCert, $method); + } + return false; + default: // Should be a key object (i.e.: Crypt_RSA). + $key = $key->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW); + break; + } + + // If in PEM format, convert to binary. + if (preg_match('#^-----BEGIN #', $key)) { + $key = base64_decode(preg_replace('#-.+-|[\r\n]#', '', $key)); + } + + // Now we have the key string: compute its sha-1 sum. + if (!class_exists('Crypt_Hash')) { + require_once('Crypt/Hash.php'); + } + $hash = new Crypt_Hash('sha1'); + $hash = $hash->hash($key); + + if ($method == 2) { + $hash = substr($hash, -8); + $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); + } + + return $hash; + } + /** * Format a public key as appropriate *