Het algoritme lijkt het juiste password digest en nonce te generen voor de tijd en het wachtwoord. Met de volgende code is het namelijk mogelijk om een gegenereerde password digest te valideren tegen de meegeleverde nonce, timestamp en het bekende echte referentiewachtwoord:
--
-- Validate a nonce, creation timestamp and expected password
-- following wsse:Password Digest string against
-- the provided password digest.
--
-- See:
-- Web Services Security, Username Token Profile 1.1
-- 28 June 2005
--
-- Values taken from sample:
-- http://www.herongyang.com/Web-Services/WS-Security-Validate-Password-Digest-String.html
--
select base64_encode
( hex_to_blob
( sha1
( base64_decode('0TBQcVnd9H4uGi1jGxqJWg==') /* wsse:Nonce from SOAP */
|| to_binary('2014-06-21T12:43:21.791Z') /* wsu:Created timestamp */
|| to_binary('iLoveDogs') /* Expected password */
)
)
) = 'PfZyE8nQQR2rAsODn7iVGaf8hD8=' /* wsse:Password password digest */
authentication_success
Het result is true
indien de combinatie bij elkaar hoort, en false
anders. Dit algoritme is getest tegen een werkende koppeling met KBO via SoapUI.
Maar de web service is erg gevoelig voor afwijkingen. En elke soort afwijking leidt tot dezelfde foutmelding ns1:SecurityError
.
De makkelijkste manier om de KBO Public Search API uit te lezen is om eerst in de SoapUI de requests werkend te krijgen, daarna de boodschappen te sniffen via de MockService, en daarna na te bouwen.
Merk op dat elk bericht dat in de MockService zichtbaar is, maximaal 1 keer opgevraagd kan worden. Een herhaling van het bericht zal ook weer leiden tot ns1:SecurityError
. Met SoapUI kan iedere keer handmatig een nieuw gewenst bericht samengesteld worden.
Een werkende implementatie is:
--
-- Tested according to http://www.herongyang.com/Web-Services/WS-Security-Validate-Password-Digest-String.html
-- using:
-- l_created := '2014-06-21T12:43:21.791Z';
-- l_password := 'iLoveDogs';
-- l_username := 'herong';
--
declare
l_nonce blob;
l_created varchar2;
l_expires varchar2;
l_wsse_created varchar2;
l_password_digest varchar2;
l_xml varchar2;
l_out_contents_char varchar2;
l_out_http_status_code int32;
l_created_date datetime;
l_expires_date datetime;
--
l_enterprise_number varchar2 := '0670979187';
l_username varchar2 := 'wsot9999';
l_password varchar2 := 'secret';
l_language_code varchar2 := 'nl';
l_msg_id varchar2 := to_char(newid());
begin
l_created_date := sysdateutc;
--
-- Default maximum offset of wss4j is 300 seconds.
--
l_expires_date := l_created_date + 300/86400;
--
l_created := to_char(l_created_date, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"');
l_expires := to_char(l_expires_date, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"');
l_wsse_created := to_char(l_created_date, 'YYYY-MM-DD"T"HH24:MI:SS.FF3"Z"');
--
l_nonce:=random_blob(24);
--
-- Calculate digested password.
--
l_password_digest := base64_encode(hex_to_blob(sha1(l_nonce || to_binary(l_wsse_created) || to_binary(l_password))));
--
l_xml := '<soapenv:Envelope xmlns:dat="http://economie.fgov.be/kbopub/webservices/v1/datamodel" xmlns:mes="http://economie.fgov.be/kbopub/webservices/v1/messages" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">';
l_xml := l_xml || chr(13) || chr(10) || ' <soapenv:Header>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">';
l_xml := l_xml || chr(13) || chr(10) || ' <wsu:Timestamp wsu:Id="TS-A4DC6AF47AFDBFB025168519485171276">';
l_xml := l_xml || chr(13) || chr(10) || ' <wsu:Created>' || l_created || '</wsu:Created>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsu:Expires>' || l_expires || '</wsu:Expires>';
l_xml := l_xml || chr(13) || chr(10) || ' </wsu:Timestamp>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsse:UsernameToken wsu:Id="UsernameToken-A4DC6AF47AFDBFB025168519485171275">';
l_xml := l_xml || chr(13) || chr(10) || ' <wsse:Username>' || xmlencode(l_username) || '</wsse:Username>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' || xmlencode(l_password_digest) || '</wsse:Password>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' || xmlencode(l_nonce) || '</wsse:Nonce>';
l_xml := l_xml || chr(13) || chr(10) || ' <wsu:Created>' || l_wsse_created || '</wsu:Created>';
l_xml := l_xml || chr(13) || chr(10) || ' </wsse:UsernameToken>';
l_xml := l_xml || chr(13) || chr(10) || ' </wsse:Security>';
l_xml := l_xml || chr(13) || chr(10) || ' <mes:RequestContext>';
l_xml := l_xml || chr(13) || chr(10) || ' <mes:Id>' || xmlencode(l_msg_id) || '</mes:Id>';
l_xml := l_xml || chr(13) || chr(10) || ' <mes:Language>' || xmlencode(l_language_code) || '</mes:Language>';
l_xml := l_xml || chr(13) || chr(10) || ' </mes:RequestContext>';
l_xml := l_xml || chr(13) || chr(10) || ' </soapenv:Header>';
l_xml := l_xml || chr(13) || chr(10) || ' <soapenv:Body>';
l_xml := l_xml || chr(13) || chr(10) || ' <mes:ReadEnterpriseRequest>';
l_xml := l_xml || chr(13) || chr(10) || ' <dat:EnterpriseNumber>' || xmlencode(l_enterprise_number) || '</dat:EnterpriseNumber>';
l_xml := l_xml || chr(13) || chr(10) || ' </mes:ReadEnterpriseRequest>';
l_xml := l_xml || chr(13) || chr(10) || ' </soapenv:Body>';
l_xml := l_xml || chr(13) || chr(10) || '</soapenv:Envelope>';
--
select htp.CONTENTS_CHAR
, htp.http_status_code
into l_out_contents_char
, l_out_http_status_code
from HTTPDownload@DataDictionary
( url => 'https://kbopub-acc.economie.fgov.be/kbopubws110000/services/wsKBOPub'
, acceptMimeType => '*/*'
, contentType => 'text/xml;charset=UTF-8'
, method => 'POST'
, textPayload => l_xml
, ignoreWebError => false
) htp
;
dbms_output.put_line(l_out_contents_char);
end;
Meer informatie is beschikbaar in: