konczak / erecepta-poc-first

4 stars 5 forks source link

Sign Soap Header 'v201:kontekstWywolania' #1

Open Puliczek opened 5 years ago

Puliczek commented 5 years ago

Hi,

Did you manage to sign kontekstWywolania header in WSS4j?

konczak commented 5 years ago

Hi @Puliczek, as you could notice this branch is an proof of concept for expected solution (making calls to CSIOZ API to save e-recepta).

Currently you can take a look into this class: https://github.com/konczak/erecepta-poc-first/blob/master/ereceptapoc/src/main/java/pl/konczak/nzoz/ereceptapoc/soaprequesttool/WSSecurityHandler.java and the original source of it: https://sourceforge.net/p/signsoaprequest/code/HEAD/tree/SignSOAPRequest/trunk/src/main/java/br/gov/dataprev/soaptools/sign/

however it doesn't do exactly what you are asking about "sign kontekstWywolania header". As far as I understood while making call to CSIOZ API we need to sign body of request as patient treatment station using given certificate and apply calculated signature as part of SOAP request headers compliant to WS-Security and Token Profile which also includes public part of certificate. So as kontekstWywolania is part of headers we do not calculate signature from it - fix me if I'm wrong on that.

Currently solution creates SoapMessage class which printed as String seems to be correct because is in structure compliant to SOAP-UI sample - just calculated data are different as expected. However when send that request to CSIOZ it results with server error HTTP 500. I have already emailed them about it but still didn't receive answer what is wrong about my request.

I'm not an expert from SOAP client requests so tried to use an very low level solution with just concat Strings into XML + HTTP request but signatures occured to be a bit tricky. Currently I have made research what are the options for more efficient client side handling and so I'm learning Apache CXF which believe will also use WSS4j to handle WS-Security part - but I'm far from the finals yet.

PS. If it is not a secret share some info how you have got to this repo and what you are tring to achive :D

Puliczek commented 5 years ago

I have found your repo via search bar on github. Using "erecepta" query.

My main goal is to send and receive msg to CSIOZ API for erecepta.

Yeah, I started my project with Apache CXF but had some other problems... So I decided to go with your code. Normally I work in .net world, so my knowledge about WS and SOAP in Java is too weak.

I have discovered some issues with your code: 1) You probably have to add header to your CsiozClient.java httpPost.addHeader("SOAPAction", "urn:zapisPakietuRecept"); 2) You probably have to use random id in WSS parameter - ws-id

public Document signSoapMessage(SOAPMessage message) {
    try {
        Document doc = message.getSOAPBody().getOwnerDocument();
        Crypto crypto = CryptoFactory.getInstance(properties); //File

        WSSecSignature sign = new WSSecSignature();
        sign.setUserInfo(properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias"), properties.getProperty("privatekeypassword"));
        sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); // Binary Security Token - SecurityTokenReference
        sign.setUseSingleCertificate(true);
        sign.setDigestAlgo(DigestMethod.SHA1);

        // --- way better wsu:id ( instead of 'id-1' there is 'id-EIJDW201394832049234' )
        WsuIdAllocator idAllocator = new WsuIdAllocator() {
            public String createId(String prefix, Object o) {
                return "id-" + UUIDGenerator.getUUID();
            }

            public String createSecureId(String prefix, Object o) {
                return "id-" + UUIDGenerator.getUUID();
            }
        };

        WSSConfig wsConfig = new WSSConfig();
        wsConfig.setIdAllocator(idAllocator);
        sign.setWsConfig(wsConfig);

        WSSecHeader secHeader = new WSSecHeader();
        secHeader.insertSecurityHeader(doc);
        //We don't need this
        //secHeader.setMustUnderstand(true);

        Document signedDoc = sign.build(doc, crypto, secHeader);

        return signedDoc;
    } catch (SOAPException e) {
        e.printStackTrace();
        return null;
    } catch (WSSecurityException e) {
        e.printStackTrace();
        throw new RuntimeException("Error: " + e.getMessage());
    }
}

3) Soap Headers is not signed. Here is my comparison with SoupUi

Screenshot_1

I think we just need to sign this header to have working poc. However, I didn't figure out how to do it.

Thanks for help.

Puliczek commented 5 years ago

Ok, I have solved the last problem.

1) You have to setX509cert 2) Sign kontekstWywolania soap Header ( yeah I know that in CSIOZ documentation there are only words about signing body content but it is not true )

Right now I have Http 200 answer from CSIOZ.

If you need any help just get in touch.

konczak commented 5 years ago

So let's go first through points from earlier anser:

  1. You probably have to add header to your CsiozClient.java httpPost.addHeader("SOAPAction", "urn:zapisPakietuRecept");

thank you very much - I have missed that - set an Content-Type header but missed SOAPAction - will add it as well.

  1. You probably have to use random id in WSS parameter - ws-id yeah definitely - for start and proof of concept it was just enough for me and also made it easier to debug result and link signature and signed element.

  2. Soap Headers is not signed. Here is my comparison with SoupUi

I have no idea how I could missed that during compare of code result and SoapUI - petty that it is missed in documentation. Maybe they assumed that if mentioned that request has to be signed we will find out that everything has to be secured. At some moments I have that strange feeling that they are repeating some encryptions and signups requirements accross single request :/ especially that anyway everything has to go through TLS and 2 way auth.


And now about next answer - that is awesome that you have find out a way to handle that - I guess that code is secret so won't ask about it 馃槣

  1. You have to setX509cert

Sounds reasonable - strange that it didn't throw an exception during preparing singature though public part of certificate has to be applied to request.

  1. Sign kontekstWywolania soap Header ( yeah I know that in CSIOZ documentation there are only words about signing body content but it is not true )

well as you have mentioned it has worked for you and SoapUI does the same - now I also see that message send via SoapUI contains multiple references inside signature. If it is not a problem a tip how you have manage to sign other parts is welcome seen 馃槃

Puliczek commented 5 years ago

At some moments I have that strange feeling that they are repeating some encryptions and signups requirements accross single request :/ especially that anyway everything has to go through TLS and 2 way auth.

Sometimes I feel that the whole CSIOZ e-recepta API is only to get things harder... No code examples, only theory in the documentation...

And now about next answer - that is awesome that you have find out a way to handle that - I guess that code is secret so won't ask about it stuck_out_tongue_winking_eye

Here is my question and answer: https://stackoverflow.com/questions/56701257/wsse-sign-an-element-inside-soapenvheader

Sounds reasonable - strange that it didn't throw an exception during preparing singature though public part of certificate has to be applied to request.

No worries, you will get 10+ errors after successive connection with CSIOZ. :P

Right now I am fighting with several issues with my xml. For example the wrong id of a drug? I don't know why, because it is an offical example from CSIOZ -.-

konczak commented 5 years ago

I have checked your question and answer in StackOverflow and understood that you have altered code from wss4j to add your own to sign part of SOAP header. However did you really used ("something","somethingNamespace","") for the part? And did it work? Or it is just placeholder? Because I would believe that after specify namespace of "v201" where "kontekstWywolania" exists it could work.

About errors after successful connection - I expect them because even right now SoapUI project does not work and returns errors and validation warnings - and there is a lot of them. Server rejects my requests claiming that identified company does not match they record (address is different). Also it returns multiple errors about used medicaments that they are not known - while actually I'm using their sample project 馃檮 I have identified that some data could be found under those files: Rejestr_Produktow_Leczniczych_stan_na_dzien_20190208230018.xml SOLR_LR-I-20190501-4282.xml but as long as they are only warnings I'm ignoring them 馃槢

Thanks for info about using BST - definitely it is incorrect - it shuld be set to use X509 certificate and apply only public part of certificate inside request signature.

konczak commented 5 years ago

I have also found out info about CXF and wss4j: http://ws.apache.org/wss4j/using.html http://www.itzone.pl/articles/java/cxf_ws_security.php that when using outgoing message interceptor we can specify what parts should be signed/encrypted like:

<entry key="signatureParts" value="{Element}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp;{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body"/>

I think it can be useful for our case as well - just need to find a way how to apply this to WSSecSignature config - maybe properties are the way.

Puliczek commented 5 years ago

I have checked your question and answer in StackOverflow and understood that you have altered code from wss4j to add your own to sign part of SOAP header. However did you really used ("something","somethingNamespace","") for the part? And did it work? Or it is just placeholder?

No, those are fakes, exact working values: WSEncryptionPart aaa = new WSEncryptionPart("kontekstWywolania","http://csioz.gov.pl/p1/kontekst/mt/v20170510","");

Server rejects my requests claiming that identified company does not match they record (address is different).

Yeah, the server is going to validate everything: IDs, Address, Regon14, etc.

Also it returns multiple errors about used medicaments that they are not known - while actually I'm using their sample project 馃檮 I have identified that some data could be found under those files: Rejestr_Produktow_Leczniczych_stan_na_dzien_20190208230018.xml SOLR_LR-I-20190501-4282.xml but as long as they are only warnings I'm ignoring them 馃槢

Right now I am struggling with this problem. I have to check those files. Thanks.

Thanks for info about using BST - definitely it is incorrect - it shuld be set to use X509 certificate and apply only public part of certificate inside request signature.

Btw. You can always use or check podpisKwalifikowany-1.4.1.jar from SoupUi CSIOZ's sample project. There is an answer on how to sign XML in a proper way.

I have also found out info about CXF and wss4j: http://ws.apache.org/wss4j/using.html http://www.itzone.pl/articles/java/cxf_ws_security.php that when using outgoing message interceptor we can specify what parts should be signed/encrypted like:

I think it can be useful for our case as well - just need to find a way how to apply this to WSSecSignature config - maybe properties are the way.

Cool, I think that this solution can save us from editing official Apache library :P Nevertheless, as I mentioned on StackOverflow, there is probably a bug in Apache library, in terms of adding extra parts to encode in programmatically way.

konczak commented 5 years ago

Hi @Puliczek as already mentioned I have been looking for more correct Java solution than manual handling of SOAPs with e-recepta WS and so I have created this repository: https://github.com/konczak/erecepta it uses Apache CXF as SOAP client engine.

At the current stage it still does not work (it is impossible to create e-recepta in server) and it is like few steps back comparing to this project (need to solve same problems) but I will apply required steps with time up there.

If you wish to still monitor my progress probably will like to add watch over https://github.com/konczak/erecepta repository.

Best regards

MarcinGladkowski commented 4 years ago

Hi! It's my solution to generate property signature o header based on previous messages. @konczak @Puliczek Thanks!

public Document signSoapMessage(SOAPMessage message) {
        try {
            Document doc = message.getSOAPBody().getOwnerDocument();
            Crypto crypto = CryptoFactory.getInstance(properties); //File

            WSSecSignature sign = new WSSecSignature();
            sign.setUserInfo(properties.getProperty("org.apache.ws.security.crypto.merlin.keystore.alias"), properties.getProperty("privatekeypassword"));
            sign.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE); // Binary Security Token - SecurityTokenReference
            sign.setUseSingleCertificate(true);
            sign.setDigestAlgo(DigestMethod.SHA1);

            // dodanie generatora id
            WsuIdAllocator idAllocator = new WsuIdAllocator() {
                public String createId(String prefix, Object o) {
                    return "id-" + UUIDGenerator.getUUID();
                }

                public String createSecureId(String prefix, Object o) {
                    return "id-" + UUIDGenerator.getUUID();
                }
            };

            WSSConfig wsConfig = new WSSConfig();
            wsConfig.setIdAllocator(idAllocator);
            sign.setWsConfig(wsConfig);

            WSSecHeader secHeader = new WSSecHeader();
            secHeader.insertSecurityHeader(doc);

            // dodanie do podpisu kontekstu wywolania - bez tego podpisze jedynie Body!
            List<WSEncryptionPart> parts = new ArrayList<WSEncryptionPart>();
            // kontekst wywolania
            WSEncryptionPart kontekstWywolania = new WSEncryptionPart("kontekstWywolania","http://csioz.gov.pl/p1/kontekst/mt/v20170510","");
            parts.add(kontekstWywolania);
            // body 
            WSEncryptionPart bodyPart = new WSEncryptionPart(WSConstants.ELEM_BODY, WSConstants.URI_SOAP11_ENV, "");
            parts.add(bodyPart);

            sign.setParts(parts);

            Document signedDoc = sign.build(doc, crypto, secHeader);

            return signedDoc;
        } catch (SOAPException e) {
            e.printStackTrace();
            return null;
        } catch (WSSecurityException e) {
            e.printStackTrace();
            throw new RuntimeException("Error: " + e.getMessage());
        }
    }
Puliczek commented 4 years ago

//nie bede pisac po angielsku bo i tak program dla polakow @Martin89PL @konczak Panowie a macie co艣 do generowania dokument贸w recept hl7? Aktualnie robie to manualnie, ale oni tak du偶o zmian wprowadzaj膮, 偶e nie da si臋 nad膮偶y膰 za utrzymaniem...

Widzia艂em na drugim repo @konczak tworzenie modeli na podstawie xsd, ale serio nie da si臋 tego pro艣ciej zrobi膰?

MarcinGladkowski commented 4 years ago

@Puliczek Ja aktualnie jestem na etapie poprawnej komunikacji dla WyszukajReceptyWystawiaj膮cego czy jako艣 tak :) Nie ja zajmuj臋 si臋 podpisem recept. Tworzeniem dokument贸w pewnie zajm臋 si臋聽tak od poniedzia艂ku :/ Tak偶e dopiero do tego przysi膮d臋.

konczak commented 4 years ago

Hej, @Martin89PL gratuluj臋 opracowania rozwi膮zania do podpisywania request贸w SOAP praktycznie od zera :)

Ja zrezygnowa艂em z w艂asnego rozwi膮zania na rzecz Apache CXF.

@Puliczek W repo i branch: https://github.com/konczak/erecepta/tree/update-erecepta-generation znajdziesz kod kt贸ry pozwala na pe艂n膮 obslug臋 e-recepty tzn:

  1. przygotowanie dokumentu instancji obiekt贸w klas Java reprezentuj膮cych recept臋 z refundacj膮 https://www.csioz.gov.pl/HL7POL-1.3.1/plcda-html-1.3.1/plcda-html-1.3.1/tmp-2.16.840.1.113883.3.4424.13.10.1.26-2018-09-30T000000.html,
  2. zserializowanie przygotowanego obiektu do pliku XML,
  3. podpisanie dokumentu e-recepty jako lekarz z pomoc膮 biblioteki https://github.com/esig/dss oraz algorytmu XADES,
  4. przygotowanie SOAP request z weryfikacjaPakietuRecept z w/w e-recept膮 (kt贸ry mo偶na zamieni膰 na zapis tylko z powodu wymogu unikalnego ID przez serwer tak si臋 mi 艂atwiej testowa艂o),
  5. wys艂anie request SOAP i podpisanie go certyfikatem podmiotu za pomoc膮 Apache CXF.

PS. To repo ju偶 raczej nie b臋d臋 kontynuowa艂 - by艂 to raczej PoC. PS2. To drugie repo musz臋 jeszcze posprz膮ta膰 w kodzie i zrobi膰 艂adnego publicznego mastera :) PS3. To drugie repo r贸wnie偶 nie jest finalnym - zawiera jedynie kod przygotowania e-recepty - aplikacja kt贸ra robi znacznie wi臋cej a wystawianie e-recept b臋dzie jedynie jej modu艂em jest w prywatnym repo. PS4. Bardzo mo偶liwe 偶e w tym drugim repo jeszcze b臋d臋 walczy艂 z aktualizacj膮 dokumentu e-recepty bo CSIOZ aktualnie wprowadza nowe wymogi odno艣nie walidacji bloku narracyjnego (nareszcie) https://ezdrowie.gov.pl/portal/artykul/wprowadzenie_walidacji_bloku_narracyjnego_dokumentu_e-recepty_-_aktualizacja - ale czekam na co艣 bardziej stabilnego. PS5. W drugim repo u偶y艂em Apache CXF i JAXB do generowania modelu HL7 i sko艅czy艂em z manualnymi zmianami :( Inny develop pisa艂 偶e on u偶ywa XmlBeans i jest zadowolony - zdecydowanie rozwa偶am mo偶liwo艣膰 przesiadki - i najpewniej pr贸by znajd膮 si臋 w drugim repo.

Puliczek commented 4 years ago

@konczak No w艂a艣nie podgl膮da艂em Twoje drugie repo, kawa艂 dobrej roboty tam zrobi艂e艣. Dzi臋ki za podzielenie si臋 wiedz膮.

  1. Tak ale to nie jest jedyny szablon kt贸ry trzeba u偶ywa膰 do e-recept, np. do e-recepty refundowanej trzeba u偶y膰 https://www.csioz.gov.pl/HL7ENG/pl-cda-html-en-US/tmp-2.16.840.1.113883.3.4424.13.10.1.3-2017-01-28T000000.html oraz inna https://www.csioz.gov.pl/HL7POL-1.3.1/plcda-html-1.3.1/plcda-html-1.3.1/tmp-2.16.840.1.113883.3.4424.13.10.1.7-2018-09-30T000000.html . No i problem jest nad zapanowaniem, szczeg贸lnie jak b臋d膮 zmiany to nawet Twoim sposobem, nazwy klas typu II, ED, CS, IVLINT nie s膮 zbyt czytalne jak dla mnie. Kurcze, sam nie wiem.
  2. Do podpisu biblioteki u偶y艂em biblioteki CSIOZ z projektu testowego, PodpisKwalifikowany.jar, uzna艂em 偶e tam b臋dzie poprawnie to zaimplementowane, chocia偶 podpisanie u mnie e-recepty trwa nawet do 3 sekund...
  3. Tak, dobrze 偶e po czasie dodali ten request weryfikacjaPakietuRecept, gdy偶 mo偶na sprawdzi膰 poprawno艣膰 xml na produkcji.
  4. A to obs艂uguje r臋cznie.

PS2. ooo to ja czekam :) PS4 (te偶 wole od xboxa). No super, ale to juz powinno by膰 p贸l roku temu wprowadzone, A nie 3 tygodnie przed wymogiem wejscia na produkcji przychodni... Jest gotowa transformata do tego, j膮 mo偶na u偶y膰 do stworzenia gotowego textu na podstawie xml e-recepty. PS5. Obadam tego XmlBeans ale wydaje mi si臋 偶e nie b臋dzie tam jakiego艣 super u艂atwienia.

Ja og贸lnie mam napisane wi臋kszo艣膰 systemu ju偶 pod obs艂ug臋 e-recept no ale co z tego skoro CSIOZ co chwile wprowadza zmiane i zaczynaja wyskakiwa膰 b艂臋dy REG z API kt贸rych nie ma na najnowszej wersji P1-DS-Z5-Lista_regul_P1.xlsx

Gdyby kto艣 z was by艂 zainteresowany wymian膮 wiedzy na temat pisania program贸w dla przychodni to stworzy艂em grup臋 na facebook-u: https://www.facebook.com/groups/598282080920579/ Nie mog艂em znale藕膰 innego miejsca, gdzie mog臋 wymienia膰 si臋 wiedz膮 na ten temat, nie tylko o CSIOZ. A temat贸w b臋dzie coraz wi臋cej :P

hakamairi commented 4 years ago

艢wietne repo i dobra dyskusja. Zastanawia艂em si臋 jeszcze nad generowaniem zamiast z xsd z plik贸w decor. Wpad艂em na taki filmik szukaj膮c informacji o tego typu narz臋dziach z CDA H7, co ciekawe jest tam nawet wspomniana implementacja z PL, tam generuja z plik贸w .MIF, kt贸re to przez CSIOZ nie s膮 udost臋pniane. P贸ki co bez sukces贸w.

Znalazlem tez ich repo na githubie - https://github.com/MohawkMEDIC/everest/tree/master

konczak commented 3 years ago

Ja pad艂em jak wprowadzili generowanie sekcji czytelnej z transformaty. Nie mog艂em pozby膰 si臋 problem贸w walidacji. Generalnie projekt zosta艂 przerwany - ilo艣膰 czasu potrzebnego na dostarczenie rozwi膮zania przeros艂a moje oczekiwania.

Puliczek commented 3 years ago

Tak to racja, mi si臋 uda艂o zrobi膰 e-recepty, e-skierowania, e-deklaracja, baze lek贸w. Ale czas jaki trzeba by艂o na to po艣wi臋ci膰 jest ogromny ;( Setki godzin... Jedna z gorszych integracji jakie przysz艂o mi pisa膰. Na si艂e utrudniana przez tw贸rc贸w, b艂臋dy w dokumentacji, brak przyk艂ad贸w kodu.

tomasz-kuchta commented 3 years ago

@Puliczek a najlepsze w tym wszystkim jest to 偶e po calej implementacji wprowadzaj膮 zmiany/ulepszenia struktur i po艂owa roboty od nowa :(

tomasz-kuchta commented 3 years ago

Zastanawialem sie tez nad projektem opensource z bibliotekami javy/c#/php czy cokolwiek na co by chetni do pisania byli z implementacja konkretnej wersji hl7 i innych struktur xml zeby dla kazdego chetnego pozostawala implementacja samej logiki biznesowej bez szarpania sie ze wszystki wywolaniami. Ewentualnie zebrac sie i napisac platne biblioteki i dzielic zarobkami ze sprzedazy (moze nawet sam NFZ by zakupil takie cos :) )

Puliczek commented 3 years ago

Tez myslalem o tym, ale masz odpowiedz z tym repo ile osob by robilo update :) A komercyjnie sie nie oplaca bo juz sam tworca P1 czyli Pentacomp sprzedaje "Platforma e-Zdrowia" czyli API do latwieszego wystawiania e-recept itp. :) To sa dopiero jaja, a drogie w cholere raczej liczcie w dziesi膮tkach tysi臋cy rocznie :D