Документация сервиса говорит, что мне нужно использовать WS-Security.
От их поддержки я получил файл p12, который я должен использовать.
Что сработало
Я запустил приложение SoapUI, настроил его, добавил wsdl и т. Д., И получил сообщение
<faultstring>These policy alternatives can not be satisfied: (...)</faultstring>
Поэтому я обнаружил, что мне нужно добавить базовый Auth к запросу. И я получил свой правильный ответ.
Что я сделал до сих пор
Нашел функцию, которая генерирует почти такой же xml, что и SoapUI, так что я мог бы использовать это, вероятно.
Я называю это используя: $x = callSecRequest('https://int.pz.gov.pl/pz-services/tpSigning?wsdl', 'addDocumentToSigning', $xmltosign, $pub, $priv, $msgback);
function callSecRequest($serviceUrl,$serviceMethod,$requestContent,$pubKey,$privKey,&$msg,$style='document',$use='literal')
{
require_once('libs/xmlseclibs-master/xmlseclibs.php');
require_once('libs/nuSoap/nusoap.php');
$client=new nusoap_client($serviceUrl);
$client->soap_defencoding = 'UTF-8';
$client->decode_utf8 = true;
$client->http_encoding = 'UTF-8';
$client->xml_encoding = 'UTF-8';
$client->setCurlOption(CURLOPT_SSLVERSION, 3);
$securityStart="<wsse:Security \n"."xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"\n"."xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">\n";
$securityEnd="</wsse:Security>\n";
$BinarySecurityToken="<wsse:BinarySecurityToken \n"."wsu:Id=\"binarytoken\" \n"."ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\" \n"."EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">\n".implode("\n",array_slice(explode("\n",$pubKey),1,-1)) //a tu wycinamy napisy BEGIN CERTYFICATE i END CERTYFICATE ponieważ EPUAP oczekuje BinarySecurityToken jako base64 bez tych nagłówków
."</wsse:BinarySecurityToken>\n";
$timestamp = "<wsu:Timestamp>"."<wsu:Created>".getTimestamp()."</wsu:Created>"."<wsu:Expires>".getTimestamp(60*60)."</wsu:Expires>" //przesuniecie o godzinę
."</wsu:Timestamp>";
$sheaders = $securityStart.$BinarySecurityToken.$timestamp.$securityEnd; //komponujemy dodatek "security" który zostanie umieszczony w nagłówku SOAP:Header
$soapEnvelope = $client->serializeEnvelope($requestContent, $sheaders, array(), $style, $use); //tworzymy całą kopertę SOAP z zawartym tam nagłówkiem (jeszcze nie podpisanym i nie są przydzielone również referencje
//w tym momecie mamy już wstępnie utworzoną kopertę SOAP z zawartością SOAP:BODY jednak nie jest jeszcze w pełni utworzony nagłówek security ani nie jest tam wstawiony podpis które będzie wstawiony tam w elemencie "signature" brakuje również referecji wskazujących które elementy koperty SOAp są podpisane
//dlatego teraz zajmiemy się dalszym wstawianiem odpowiednich danych do elementu security.
$doc = new DOMDocument();
$doc->loadXML($soapEnvelope); //całą kopertę soap wczytujemy do drzewa dom i na tym drzewie będziemy już rzeźbić
//znajdujemy element body aby dodac do niego referencje poniewaz tylko body bedziemy podpisywali
$xp = new DOMXPath($doc);
$wsseNamespace = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
$wsuNamespace = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
//musimy zarejestrować właściwe namespaces z prefixami aby zapytania xpath umiały znaleść właściwe węzły
$xp->registerNamespace('soapenv', 'http://schemas.xmlsoap.org/soap/envelope/'); //namespace sekcji body
$xp->registerNamespace('wsse',$wsseNamespace); //namespace node security
$xp->registerNamespace('wsu',$wsuNamespace);
$xp->registerNamespace('ds',XMLSecurityDSig::XMLDSIGNS);
$bodyNode = $xp->query('//soapenv:Body')->item(0);
if($bodyNode) //znaleźliśmy element soap:body mozemy przypisać mu id i dodać do niego referencje w elemencie "Signature"->"SignedInfo"->"Reference[URI]"{
//echo "jest BODY";
$objDSig = new XMLSecurityDSig(); //tworzymy obiekt za pomocą którego będziemy podpisywali kopertę SOAP
$objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
$objDSig->addReference($bodyNode, XMLSecurityDSig::SHA1,NULL,array('prefix'=>'wsu','prefix_ns'=>$wsuNamespace)); //dodajemy referencje do body możemy tu użyć gotowej funkcji z biblioteki "xmlseclibs"
//teraz dodamy referencję do elementu Timestamp opisującego moment czasowy podpisu
$timestampNode = $xp->query('//wsu:Timestamp')->item(0);
if($timestampNode)
$objDSig->addReference($timestampNode, XMLSecurityDSig::SHA1,NULL,array('prefix'=>'wsu','prefix_ns'=>$wsuNamespace));
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'private'));
$objKey->loadKey($privKey); //tworzymy obiekt klucza prywatnego, w tym wypadku odczytujemy go z konfiguracji
$objDSig->sign($objKey); //podpisujemy kopertę SOAP kluczem prywatnym
//$objDSig->add509Cert($pub_key);\
//znajdujemy element do którego wstawimy sekcję podpisu w tym wypadku jest to Security
$securityNode = $xp->query('//wsse:Security')->item(0);
if($securityNode)
{
//echo "jest Security node";
//teraz musimy dodać właśnie wygenerowany podpis (na podstawie referecji do zewnętrzego obiektu) do całośći koperty SOAP
$timestampNode = $xp->query('//wsu:Timestamp')->item(0);
if($timestampNode)
$objDSig->insertSignature($securityNode,$timestampNode); //wstawiamy podpis do węzła "Security" ale ma być umieszczony przed węzłem "TimeStamp" (to ważne aby był przed nim)
//$objDSig->appendSignature($securityNode);
//Teraz ustawimy referencje do BinarySecurityToken niestety ponieważ funkcja addRefInternal jest prywatna nie możemy jej stąd wywołać i musimy dodanie atrybutu reference zrobić na piechotę
$binarySecurityTokenNode = $xp->query('//wsse:BinarySecurityToken')->item(0);
if($binarySecurityTokenNode)
{
$uri = XMLSecurityDSig::generate_GUID();
$binarySecurityTokenNode->setAttributeNS($wsuNamespace,'Id', $uri); //dodajemy atrybut Id do BinarySecurityToken aby potem użyć tego samego identyfikatora przy dodawaniu referencji w elemencie securityTokenReference
$keyInfoNode = $xp->query('//ds:KeyInfo')->item(0);
if(!$keyInfoNode)
{
$signatureNode = $xp->query('//ds:Signature')->item(0);
$keyInfoNode = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS,'ds:KeyInfo');
if($signatureNode)
$signatureNode->appendChild($keyInfoNode);
}
$securityTokenReferenceNode = $xp->query('//wsse:SecurityTokenReference')->item(0);
if($keyInfoNode && ! $securityTokenReferenceNode)
{
$securityTokenReferenceNode = $doc->createElementNS($wsseNamespace, 'wsse:SecurityTokenReference');
$referenceNode = $doc->createElementNS($wsseNamespace,'wsse:Reference');
$referenceNode->setAttribute("URI", '#'.$uri); //dodajemy teraz do referencji ten sam identyfikator co jest w BinarySecurityToken z elementu Security który właściwie to nie wchodzi do podpisu
$securityTokenReferenceNode->appendChild($referenceNode);
$keyInfoNode->appendChild($securityTokenReferenceNode);
}
}
$soapEnvelope = $doc->saveXML();
}//if($securityNode)
else
{
$msg .="Nie odnaleziono elementu wsse:Security";
}
}//if($bodyNode)
else
{
$msg .="Nie odnaleziono elementu soapenv:Body";
}
$result2 = $client->send($soapEnvelope, $serviceMethod); //wysyłamy kopertę SOAP
if($client->fault)
{
$msg .= "ERROR przy wysyłaniu metody ".$serviceMethod." :".$client->fault." ".var_export($result2,true);
}
else
{
if ($client->getError())
{
$msg .= "ERROR przy wysyłaniu metody ".$serviceMethod." :".$client->getError()." ".var_export($result2,true);
}
else
{
if(isset($result2['faultcode']))
{
$msg .= 'faultcode='.$result2['faultcode'].' faultstring='.$result2['faultstring'].' detail='.$result2['detail'];
return false;
}
return $result2;
}
}
return false;
}
Эта проблема
Я получаю ошибку:
SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure
, И понятия не имею, в чем может быть причина. Я получил сертификат и закрытый ключ от них, так что это не должно быть проблемой ..
Я пытался использовать SSL v 2 или вообще не устанавливать его, но это тоже не сработает.
Я на самом деле попробовал 8-10 решений, найденных в сети сейчас, и у меня заканчиваются идеи. Это самое близкое, что я получил, это, по крайней мере, сгенерировать XML, который он должен (или, по крайней мере, очень близко к нему)
Задача ещё не решена.
Других решений пока нет …