Общая проблема


  • API в достаточной степени не документировано.
  • Поддержка оставляет желать лучшего.
  • На форумах ни одного закрытого вопроса по данной теме.


Что имеем


Для получения необходимых нам сертификатов, необходимо сгенерировать приватный ключ и на его основе создать электронную подпись запроса на сертификат, как раз этот момент в документации описан неплохо и проблемы в реализации не составляет. Эти данные мы отправляем в Яндекс, и получаем от них тестовый шлюз, два сертификата и документацию по запросам к серверу (формат запросов, формат ответов, наборы атрибутов, коды ошибок и т.д.)

Трудности


В документации нет ни каких упоминаний о способах кодирования, декодирования и отправки запросов. Если немного поискать способы генерирования pkcs7 пакетов на php, это как раз тот формат данных который необходимо отправлять в запросах, то можно наткнуться на набор функций , которые в свою очередь тоже весьма плохо документированы. Использовать их не получится, как бы они не были похожи на необходимый нам функционал, это не то что нужно Яндексу.

Решения


Используемые константы

		// FILE_IN [define ('FILE_IN', dirname(__FILE__) . '/cert/in');] - файл доступный на чтение запись
		// FILE_OUT [define ('FILE_OUT', dirname(__FILE__) . '/cert/out');] - файл доступный на чтение запись
		// PUBLIC_ENCODE_KEY__ [define ('PUBLIC_ENCODE_KEY__', dirname(__FILE__) . '/cert/prDigital.cer');] - публичнуй ключ для кодирования, присланный яндексом
		// PRIVATE_KEY__ [define ('PRIVATE_KEY__', dirname(__FILE__) . '/cert/private.key');] - приватный ключ сгенерированный вами
		// PASSWORD__ [define ('PASSWORD__', 'q1w2e3r4t5');] - пароль к приватному ключу
		// PUBLIC_KEY__ [define ('PUBLIC_KEY__', dirname(__FILE__) . '/cert/deposit.cer');] - публичный ключ для декодирования, присланный яндексом


Отправка запроса через Curl

	public function sendRequest($url,$data){
                // $url - тестоввый шлюз/webservice/deposition/api/запрос
                // $data - закодированный в pkcs7 пакет, XML - структура  c необходимыми атрибутами запроса
		$headers = array(
			"Content-type: application/pkcs7-mime; charset=\"utf-8\"",
			"Content-length: ".strlen($data)
		 );
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
		curl_setopt($ch, CURLOPT_POST,true);
		curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
		curl_setopt($ch, CURLOPT_HEADER,0);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,30);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,false);
		curl_setopt($ch, CURLOPT_SSLCERT,PUBLIC_ENCODE_KEY__);
		curl_setopt($ch, CURLOPT_SSLKEY,PRIVATE_KEY__);
		curl_setopt($ch, CURLOPT_SSLKEYPASSWD,PASSWORD__);
		$dt = curl_exec($ch);
		if (curl_errno($ch)){
			$err = curl_error($ch).' '.curl_errno($ch);
			return null;
		}
		else{
			return $dt;
		}
	}


Кодирование данных

	private function encrypt_proc($data){
		// $data - XML структура запроса
		file_put_contents(FILE_IN,$data);
		file_put_contents(FILE_OUT,'');
		
		system('openssl smime -sign -in '.FILE_IN.' -out '.FILE_OUT.' -signer '.PUBLIC_ENCODE_KEY__.' -inkey '.PRIVATE_KEY__.' -passin pass:'.PASSWORD__.' -nodetach -nochain -nocerts -outform PEM');
		return file_get_contents(FILE_OUT);
	}


Декодирование данных

	private function decrypt_proc($data){
		// $data - ответ сервера
		$descriptorspec = array(
				0 => array("pipe", "r"),
				1 => array("pipe", "w"),
				2 => array("pipe", "w"));
		$process = proc_open(
				'openssl smime -verify -inform PEM -nointern -certfile '.PUBLIC_KEY__.' -CAfile '.PUBLIC_KEY__ ,
				$descriptorspec, $pipes);
		if (is_resource($process)) {
				fwrite($pipes[0], $data);
				fclose($pipes[0]);
				$content = stream_get_contents($pipes[1]);
				fclose($pipes[1]);
				$resCode = proc_close($process);
				if ($resCode != 0) {
						if ($resCode == 2 || $resCode == 4){
								return null;
						}
				}
				return $content;
		}
	}