Общая проблема
- 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;
}
}