/** * @group github602 */ public function testEmptyContextTag() { $asn1 = new ASN1(); $decoded = $asn1->decodeBER("�"); $this->assertInternalType('array', $decoded); $this->assertCount(0, $decoded[0]['content']); }
public function testSaveUnsupportedExtension() { $x509 = new X509(); $cert = $x509->loadX509('-----BEGIN CERTIFICATE----- MIIDITCCAoqgAwIBAgIQT52W2WawmStUwpV8tBV9TTANBgkqhkiG9w0BAQUFADBM MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0xMTEwMjYwMDAwMDBaFw0x MzA5MzAyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC gYEA3rcmQ6aZhc04pxUJuc8PycNVjIjujI0oJyRLKl6g2Bb6YRhLz21ggNM1QDJy wI8S2OVOj7my9tkVXlqGMaO6hqpryNlxjMzNJxMenUJdOPanrO/6YvMYgdQkRn8B d3zGKokUmbuYOR2oGfs5AER9G5RqeC1prcB6LPrQ2iASmNMCAwEAAaOB5zCB5DAM BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0 ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF AAOBgQAhrNWuyjSJWsKrUtKyNGadeqvu5nzVfsJcKLt0AMkQH0IT/GmKHiSgAgDp ulvKGQSy068Bsn5fFNum21K5mvMSf3yinDtvmX3qUA12IxL/92ZzKbeVCq3Yi7Le IOkKcGQRCMha8X2e7GmlpdWC1ycenlbN0nbVeSv3JUMcafC4+Q== -----END CERTIFICATE-----'); $asn1 = new ASN1(); $value = $this->_encodeOID('1.2.3.4'); $ext = chr(ASN1::TYPE_OBJECT_IDENTIFIER) . $asn1->_encodeLength(strlen($value)) . $value; $value = 'zzzzzzzzz'; $ext .= chr(ASN1::TYPE_OCTET_STRING) . $asn1->_encodeLength(strlen($value)) . $value; $ext = chr(ASN1::TYPE_SEQUENCE | 0x20) . $asn1->_encodeLength(strlen($ext)) . $ext; $cert['tbsCertificate']['extensions'][4] = new Element($ext); $result = $x509->loadX509($x509->saveX509($cert)); $this->assertCount(5, $result['tbsCertificate']['extensions']); }
/** * @todo This can be simplified once Responses are created by a factory. */ public function testHandleReturnsResponseIfSuccessful() { $pathToResponderCert = vfsStream::newFile('issuerCert.pem')->at(vfsStream::setUp())->withContent($this->getCertInPem())->url(); $process = $this->getMockProcess(); $process->method('isSuccessful')->willReturn(true); $process->method('setCommandLine')->will($this->returnCallback(function ($commandLine) { // NB! This assumes the outfile is the last argument. If things go // south, assume somone has fiddled with the argument order. $commandLine = explode('-respout', $commandLine); $fileName = trim(end($commandLine), '\' '); $parser = new Asn1Parser(); $asn1 = new Asn1(); file_put_contents($fileName, $parser->encodeDER(array('responseStatus' => Asn1::OCSP_SUCCESSFUL, 'responseBytes' => array('responseType' => Asn1::OID_ID_PKIX_OCSP_BASIC, 'response' => 'MIICOTCCASGhgYYwgYMxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMQ0wCwYDVQQLDARPQ1NQMScwJQYDVQQDDB5URVNUIG9mIFNLIE9DU1AgUkVTUE9OREVSIDIwMTExGDAWBgkqhkiG9w0BCQEWCXBraUBzay5lZRgPMjAxNDEyMjYyMzE1NDVaMGAwXjBJMAkGBSsOAwIaBQAEFJ8hzI+QiAAqq1ikY3MvViFZKzWuBBR7avJVUFy42XoIh0Gu+qIrPVtXdgIQH/v/rqwJX11SX33gZ4PrfYAAGA8yMDE0MTIyNjIzMTU0NVqhIzAhMB8GCSsGAQUFBzABAgQSBBDXw6pZv+/fMYQlxV3ACvKZMA0GCSqGSIb3DQEBBQUAA4IBAQBxe4hdQYCqR+O5wLFP1nY5HiP4w348YXfFiEvVmC9JCoaoSqmXdoner0sJxYdnOleu7/WdRAvO+hAnl73aOm0l+woGpm1fud8pl7Bz0F8cIiYL4g5xorArkdHZLwMmxi09ZzhBgM93xyOtpUj1c2onIXLEyV4ENv6DPBIAPNOVVTiaeFBVGba7g4RZxgvHWeuO+OmCAezjYJNZfXaYshvudAxaqmrhBCd3xDAYjgQlarhRn6aXpNsVRZG8NK4XW6+rH+4q+9S2ZsA6KTVkfGC218unYUkA0FswJH1JO7D+G9kooZHGIuV7SL5l4bpGwNxcbtdu+xYtNqNr4xSkHBTn')), $asn1->OCSPResponse)); })); $responder = new Responder('http://example.com', $pathToResponderCert, null, $process); $response = $responder->handle($this->getMockRequest()); $this->assertInstanceOf('KG\\DigiDoc\\OCSP\\Response', $response); }
/** * {@inheritdoc} */ protected function supportsKey($key) { if (false === parent::supportsKey($key)) { return false; } // openssl_sign with EC keys was introduced in this PHP release $minVersions = array('5.4' => '5.4.26', '5.5' => '5.5.10', '5.6' => '5.6.0'); if (isset($minVersions[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION]) && version_compare(PHP_VERSION, $minVersions[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION], '<')) { return false; } $keyDetails = openssl_pkey_get_details($key); if (0 === preg_match('/-----BEGIN PUBLIC KEY-----([^-]+)-----END PUBLIC KEY-----/', $keyDetails['key'], $matches)) { return false; } $publicKey = trim($matches[1]); $asn1 = new ASN1(); /* * http://tools.ietf.org/html/rfc3279#section-2.2.3 * AlgorithmIdentifier ::= SEQUENCE { * algorithm OBJECT IDENTIFIER, * parameters ANY DEFINED BY algorithm OPTIONAL * } * For ECDSA Signature Algorithm: * algorithm: ansi-X9-62 => 1.2.840.10045.2.1 * parameters: id-ecSigType => 1.2.840.10045.x.y.z * */ $asnAlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('ansi-X9-62' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'id-ecSigType' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER))); /* * http://tools.ietf.org/html/rfc5280#section-4.1 * SubjectPublicKeyInfo ::= SEQUENCE { * algorithm AlgorithmIdentifier, * subjectPublicKey BIT STRING * } */ $asnSubjectPublicKeyInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => $asnAlgorithmIdentifier, 'subjectPublicKey' => array('type' => ASN1::TYPE_BIT_STRING))); $decoded = $asn1->decodeBER(base64_decode($publicKey)); $mappedDetails = $asn1->asn1map($decoded[0], $asnSubjectPublicKeyInfo); return isset($mappedDetails['algorithm']['id-ecSigType']) ? $this->getSupportedECDSACurve() === $mappedDetails['algorithm']['id-ecSigType'] : false; }
/** * Wrap a public key appropriately * * @access public * @param string $key * @return string */ static function wrapPublicKey($key, $algorithm) { $asn1 = new ASN1(); $key = ['publicKeyAlgorithm' => ['algorithm' => $algorithm, 'parameters' => null], 'publicKey' => Base64::encode("" . $key)]; $key = $asn1->encodeDER($key, PublicKeyInfo); return "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split(Base64::encode($key), 64) . "-----END PUBLIC KEY-----"; }
/** * 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 * - \phpseclib\File\X509 object with public or private key defined * - Certificate or CSR array * - \phpseclib\File\ASN1\Element object * - PEM or DER string * * @param mixed $key optional * @param int $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 $key instanceof Element: // Assume the element is a bitstring-packed key. $asn1 = new ASN1(); $decoded = $asn1->decodeBER($key->element); if (empty($decoded)) { return false; } $raw = $asn1->asn1map($decoded[0], array('type' => ASN1::TYPE_BIT_STRING)); if (empty($raw)) { return false; } $raw = base64_decode($raw); // If the key is private, compute identifier from its corresponding public key. $key = new RSA(); if (!$key->loadKey($raw)) { return false; // Not an unencrypted RSA key. } if ($key->getPrivateKey() !== false) { // If private. return $this->computeKeyIdentifier($key, $method); } $key = $raw; // Is a public key. break; case $key instanceof 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.: \phpseclib\Crypt\RSA). $key = $key->getPublicKey(RSA::PUBLIC_FORMAT_PKCS1); break; } // If in PEM format, convert to binary. $key = $this->_extractBER($key); // Now we have the key string: compute its sha-1 sum. $hash = new Hash('sha1'); $hash = $hash->hash($key); if ($method == 2) { $hash = substr($hash, -8); $hash[0] = chr((ord($hash[0]) & 0x0F) | 0x40); } return $hash; }
/** * Decodes and maps the BasicOCSPResponse part of the response. * * @return array */ private function getResponseMapped() { $parser = new Asn1Parser(); $responseBasicDecoded = $parser->decodeBER(base64_decode($this->response)); return $parser->asn1map($responseBasicDecoded[0], $this->asn1->BasicOCSPResponse); }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $key = ['modulus' => $n, 'publicExponent' => $e]; $asn1 = new ASN1(); $key = $asn1->encodeDER($key, RSAPublicKey); return self::wrapPublicKey($key, 'RSA'); }
/** * Convert a public key to the appropriate format * * @access public * @param \phpseclib\Math\BigInteger $n * @param \phpseclib\Math\BigInteger $e * @return string */ static function savePublicKey(BigInteger $n, BigInteger $e) { $key = PKCS1::savePublicKey($n, $e); $key = ASN1::extractBER($key); return self::wrapPublicKey($key, '1.2.840.113549.1.1.1'); }
/** * RSASSA-PKCS1-V1_5-VERIFY (relaxed matching) * * Per {@link http://tools.ietf.org/html/rfc3447#page-43 RFC3447#page-43} PKCS1 v1.5 * specified the use BER encoding rather than DER encoding that PKCS1 v2.0 specified. * This means that under rare conditions you can have a perfectly valid v1.5 signature * that fails to validate with _rsassa_pkcs1_v1_5_verify(). PKCS1 v2.1 also recommends * that if you're going to validate these types of signatures you "should indicate * whether the underlying BER encoding is a DER encoding and hence whether the signature * is valid with respect to the specification given in [PKCS1 v2.0+]". so if you do * $rsa->getLastPadding() and get RSA::PADDING_RELAXED_PKCS1 back instead of * RSA::PADDING_PKCS1... that means BER encoding was used. * * @access private * @param string $m * @param string $s * @return bool */ function _rsassa_pkcs1_v1_5_relaxed_verify($m, $s) { // Length checking if (strlen($s) != $this->k) { return false; } // RSA verification $s = $this->_os2ip($s); $m2 = $this->_rsavp1($s); if ($m2 === false) { return false; } $em = $this->_i2osp($m2, $this->k); if ($em === false) { return false; } if ($this->_string_shift($em, 2) != "") { return false; } $em = ltrim($em, "ÿ"); if ($this->_string_shift($em) != "") { return false; } $asn1 = new ASN1(); $decoded = $asn1->decodeBER($em); if (!is_array($decoded) || empty($decoded[0]) || strlen($em) > $decoded[0]['length']) { return false; } $AlgorithmIdentifier = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('algorithm' => array('type' => ASN1::TYPE_OBJECT_IDENTIFIER), 'parameters' => array('type' => ASN1::TYPE_ANY, 'optional' => true))); $DigestInfo = array('type' => ASN1::TYPE_SEQUENCE, 'children' => array('digestAlgorithm' => $AlgorithmIdentifier, 'digest' => array('type' => ASN1::TYPE_OCTET_STRING))); $oids = array('1.2.840.113549.2.2' => 'md2', '1.2.840.113549.2.4' => 'md4', '1.2.840.113549.2.5' => 'md5', '1.3.14.3.2.26' => 'sha1', '2.16.840.1.101.3.4.2.1' => 'sha256', '2.16.840.1.101.3.4.2.2' => 'sha384', '2.16.840.1.101.3.4.2.3' => 'sha512'); $asn1->loadOIDs($oids); $decoded = $asn1->asn1map($decoded[0], $DigestInfo); if (!isset($decoded) || $decoded === false) { return false; } if (!in_array($decoded['digestAlgorithm']['algorithm'], $oids)) { return false; } $hash = new Hash($decoded['digestAlgorithm']['algorithm']); $em = $hash->hash($m); $em2 = Base64::decode($decoded['digest']); return $this->_equals($em, $em2); }
/** * Break a public or private key down into its constituent components * * @access public * @param string $key * @param string $password optional * @return array */ static function load($key, $password) { if (!is_string($key)) { return false; } /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here: http://tools.ietf.org/html/rfc1421#section-4.6.1.1 http://tools.ietf.org/html/rfc1421#section-4.6.1.3 DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell. DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's own implementation. ie. the implementation *is* the standard and any bugs that may exist in that implementation are part of the standard, as well. * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */ if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) { $iv = Hex::decode(trim($matches[2])); // remove the Proc-Type / DEK-Info sections as they're no longer needed $key = preg_replace('#^(?:Proc-Type|DEK-Info): .*#m', '', $key); $ciphertext = ASN1::extractBER($key); if ($ciphertext === false) { $ciphertext = $key; } $crypto = self::getEncryptionObject($matches[1]); $crypto->setKey(self::generateSymmetricKey($password, $iv, $crypto->getKeyLength() >> 3)); $crypto->setIV($iv); $key = $crypto->decrypt($ciphertext); } else { if (self::$format != self::MODE_DER) { $decoded = ASN1::extractBER($key); if ($decoded !== false) { $key = $decoded; } elseif (self::$format == self::MODE_PEM) { return false; } } } return $key; }
/** * @param $publicKey * * @return array|bool */ protected static function loadPublicKey($publicKey) { $asn1 = new ASN1(); $asnAlgorithmIdentifier = ['type' => ASN1::TYPE_SEQUENCE, 'children' => ['ansi-X9-62' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER], 'id-ecSigType' => ['type' => ASN1::TYPE_OBJECT_IDENTIFIER]]]; $asnSubjectPublicKeyInfo = ['type' => ASN1::TYPE_SEQUENCE, 'children' => ['algorithm' => $asnAlgorithmIdentifier, 'subjectPublicKey' => ['type' => ASN1::TYPE_BIT_STRING]]]; $decoded = $asn1->decodeBER(base64_decode($publicKey)); $mappedDetails = $asn1->asn1map($decoded[0], $asnSubjectPublicKeyInfo); if (null === $mappedDetails) { return false; } $details = Base64Url::decode($mappedDetails['subjectPublicKey']); if (!self::isAlgorithmSupported($mappedDetails['algorithm']['id-ecSigType'])) { return false; } if (substr($details, 0, 1) !== "") { return false; } if (substr($details, 1, 1) !== "") { return false; } $X = substr($details, 2, (strlen($details) - 2) / 2); $Y = substr($details, (strlen($details) - 2) / 2 + 2, (strlen($details) - 2) / 2); return ['x' => Base64Url::encode($X), 'y' => Base64Url::encode($Y)]; }
/** * Creates a DER response for testing * * @param mixed $source * * @return string */ private function createDerResponse($source) { $parser = new Asn1Parser(); $asn1 = new Asn1(); return $parser->encodeDER($source, $asn1->OCSPResponse); }
/** * Set certificate end date * * @param String $date * * @access public */ function setEndDate($date) { /* To indicate that a certificate has no well-defined expiration date, the notAfter SHOULD be assigned the GeneralizedTime value of 99991231235959Z. -- http://tools.ietf.org/html/rfc5280#section-4.1.2.5 */ if (strtolower($date) == 'lifetime') { $temp = '99991231235959Z'; $asn1 = new ASN1(); $temp = chr(ASN1::TYPE_GENERALIZED_TIME) . $asn1->_encodeLength(strlen($temp)) . $temp; $this->endDate = new Element($temp); } else { $this->endDate = @date('D, d M Y H:i:s O', @strtotime($date)); } }