Zobrazují se příspěvky se štítkemwebová služba. Zobrazit všechny příspěvky
Zobrazují se příspěvky se štítkemwebová služba. Zobrazit všechny příspěvky

pondělí 4. března 2013

Něco málo o SSL (HTTPS)

Jen malá poznámka ohledně dnešní zdlouhavé a otravné a frustrující zkušenosti s HTTPS, SoapUI a AS WebLogic. Prostě se dá říct, že nastávaly dvě možnosti:
  • handshake failed
  • HTTP 403, Permission Denied.

HTTPS a WS-Security

S čím jsem ale dosud neměl zkušenost, je WS-Security; ještě dopoledne jsem si myslel, že šifrování SOAP komunikace je prostě dílo HTTPS. Během dne jsem pomalu začínal chápat, že jsem vedle jak ta jedle.
Zatímco HTTPS (HTTP over TLS/SSL) šifruje veškerou komunikaci, WS-Security používá obyčejné HTTP (nebo i HTTPS) a šifruje se jen payload (tj. data v SOAP) - pro představu, uvidíte něco takového (namespaces jsem umazal a trochu to naformátoval):

<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <S:Header>
    <wsse:Security>
      <xenc:ReferenceList>
        <xenc:DataReference URI="#ED-1"/>
      </xenc:ReferenceList>
    </wsse:Security>
  </S:Header>
  <S:Body>

    <xenc:EncryptedData Id="ED-1" Type="http://www.w3.org/2001/04/xmlenc#Content">
      <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
      <ds:KeyInfo>
        <wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
          <wsse:Reference URI="#null"/>
        </wsse:SecurityTokenReference>
      </ds:KeyInfo>
      <xenc:CipherData>
        <xenc:CipherValue>[zašifrovaná data v Base64]</xenc:CipherValue>
      </xenc:CipherData>
    </xenc:EncryptedData>
  </S:Body>
</S:Envelope>

SoapUI

Návodem se zdržovat nebudu, najdete jich na internetu hromadu.
Nastavení SoapUI ani jeho dokumentace navíc vůbec nepočítá s lidskou naivitou, a tak velká část blogů a fór radí neúplné nesmysly na podobně neúplné otázky. Člověk pak ze zoufalství všude nastavuje keystore, všude nastavuje klíče, certifikáty, prostě klasický shotgun debugging, který ale k cíli nevede.
SoapUI mi tedy rozhodně s řešením nepomohlo, neb navíc vytrvale odmítalo vzít na vědomí nastavení -Djavax.net.debug=all a v logu nebylo stále nic než jen tříslovné chybové hlášení.
Jen aby bylo jasno - k nastavení HTTPS stačí jen otevřít globální nastavení SoapUI, tj. File - Preferences - SSL Settings, nastavit keystore a jeho heslo - hotovo. Nezapomeňte nastavení uložit, což má SoapUI taky celkem nepřirozeně jako další položku menu.
Všechna ostatní nastavení v projektu (Outgoing/Incoming keystore, Encryption, atd.) se týkají WS-Security a pokud vám jde jen o HTTPS, můžete je ignorovat.

OpenSSL

Po šesti hodinách střílení z brokovnice jste rádi, že jste se nezastřelili sami. Přišel čas zapnout mozek. Dobrá, zkusíme to přes OpenSSL, blbé SoapUI!
Vyexportujete si klíče i všechny certifikáty z JKS do PEM souborů. Spustíte OpenSSL a postupně mu nasázíte všechny argumenty - klíč, klientský certifikát, důvěryhodné certifikáty. Vypnete ověření hostname i serverového certifikátu, pro všechny případy. A - ono to funguje! Blbé SoapUI!!! No počkat, ale něco tu nehraje ...

Meanwhile ...

Moje panika nakazila i místní. Někteří také stříleli z brokovnic, někteří ale zavětřili, že tu opravdu něco nehraje. Všechno jsem dělal dobře (jeden znal i SoapUI, byť také úplně nerozuměl kouzlům WS-Security, ale věděl, že to mám všechno vyházet. Nepomohlo to, ale budiž.
Mezitím mi ale server začal odpovídat jinak, namísto věčného HTTP 403 - tentokrát na žádné HTTP nedošlo a přišlo rovnou "Handshake failed: Bad certificate."
Asi hodinu mi trvalo, než mi došlo, že server mluví o mém certifikátu, který mi místní autorita s pomocí Windows bezmyšlenkovitě podepsala a já si bláhově myslel, že když to funguje přes OpenSSL, musí být problém v nastavení SoapUI.

Pointa

Když moje největší posila (toho člověka jsem začal mít rád, neb mě citelně táhl správným směrem) už asi počtvrté přišla - a kdesi mezi jejími větami jsem zachytil slovo "Extension" ... svitlo mi. Jen jsem nevěděl, jak se vlastně Extensions k certifikátům přidávají ... to už teď taky vím: dokumentace keytool

Kdybyste náhodou zařizovali klíč a klientský certifikát pro SSL (HTTPS) a divili se, proč to pořád končí handshake failed, zatímco přes OpenSSL, kterému dáte privátní klíč (PEM), klientský certifikát (PEM) a ca-certs (další PEM), to chodí ... Váš klientský certifikát postrádá Extensions, čili schválená použití certifikátu, který pak tím pádem není možno použít pro šifrování ani autentizaci.
Extensions k certifikátu generujete s žádostí o podpis, příp. je tam může přihodit i podepisující, soudě podle dokumentace. Např. šifrování, dešifrování, podpis ... Každá Extension má své vlastní OID, ostatně viz parametr -ext v dokumentaci keytool a např. MSDN: Certificate Extensions

Závěrem budiž konstatováno, že SoapUI blbé není.

Zamyšlení

Shodou okolností řeším problém s HTTPS - stařičký CISCO loadbalancer mu totiž nerozumí, tudíž nemůže balancovat. Jen mě tak napadlo, že s použitím WS-Security se nějaké HTTPS vůbec nemusí řešit.
Jenže - u HTTPS je komplikací i samotné sledování komunikace, tj. řekl bych, že je co se týče bezpečnosti o řád jinde.
Na druhou stranu, každá proxy potřebuje požadavek rozšifrovat, než ho pošle dál. To by u HTTP a WS-Security nemusela.
A zase ... když bude proxy s loadbalancerem v cestě jediná, resp. do cesty se strčí Apache, který komunikaci na vstupu do uzavřené části zbaví šifrování,  nač řešit WS-Security. Tu bych řešil v případě nějaké ohromné infrastruktury, do které přistupuje kde kdo ...

Nebo se pletu?





pondělí 17. prosince 2012

Implementace webové služby a anotace

Typicky máte WSDL, ponastavujete si jaxws-maven-plugin, binding, a vygenerujete si javovské interface (a taky klienta služby, ale toho si tu všímat nebudu). A co dál? Implementace rozhraní je už na vás. Co služba bude dělat, tím se tu taky nebudu zabývat. Co chvíli se ale zamotám do atributů anotace @WebService, nemůžu se trefit na @HandlerChain, nebo zapomenu říct, že chci automatickou  validaci požadavků i odpovědí. Jak na to?
Mé implementace služeb mají nakonec ve výsledku obvykle čtyři anotace, jsou kompatibilní s JEE5 i JEE6 a Glassfish je vypisuje v seznamu webových služeb vystavených na instanci - k tomu navíc aplikáč (alespoň SGES 2.1.1) potřebuje sun-ejb.xml, u Glassfish 3 si nejsem jist, jestli nějaké XML vůbec je zapotřebí.

Stateless

@Stateless(name = "Hell", mappedName = "ejb/Hell")
- name odpovídá ejb-name v sun-ejb.xml; slouží k propojení bean přes její název napříč komponentou. - mappedName je globální identifikátor bean přes JNDI. To je trochu kámen úrazu mezi JEE5 a JEE6, jelikož v JEE6 došlo na standradizaci JNDI názvů, ale u JEE5 to dělá každý aplikáč jinak.

SchemaValidation

@SchemaValidation
Tato anotace způsobí, že pokud klient pošle nevalidní SOAP dotaz, vrátí mu AS jen SoapFault s "client error". Ovšem je to dvojsečné a stejně tak pokud v implementaci služby vytvoříte odpověď, která neprojde validací, klient dostane opět SoapFault, ale se "server error" a HTTP kódem 500. To by se samozřejmě nikdy nemělo stát a je to zodpovědnost autora implementace služby.
Atributem lze také říct, že chceme použít jinou než default implementaci validace.
Poznámka pod čarou - na klienta anotace nemá vůbec žádný vliv.

HandlerChain

@HandlerChain(file="generated/HellPort_handler.xml")
jaxws-maven-plugin, resp. wsimport, umí vygenerovat i tento soubor - dělá jeden zvlášť pro službu i port, stačí dodat příslušné elementy do xjb souboru používaného pro custom binding. Navíc přidá i tuto anotaci k rozhraní i klientovi, jenže má to háček - cesta k souboru se generuje relativně k souboru třídy a proto jí musíte zopakovat (pokud tedy nejste ve stejném balíku, což by pro změnu mohlo dopadnout nepěkně).
Druhá možnost je všechny handlery přidávat programově, pokud ovšem považujete určitý handler za neoddělitelnou vlastnost služby, se kterou si navíc nechcete "plevelit" kód implementace služby, toto je vhodný způsob. 
U mých služeb takto konfiguruji handler pro zalogování SOAP požadavku i odpovědi; téměř téhož mohu docílit i nastavením JVM option, leč bez možnosti logování vypínat nebo směrovat za běhu aplikace. O tom zase jindy ;-)

WebService

@WebService(
  serviceName = "HellService",
  portName="HellPort",
  targetNamespace = "urn:cz:dmatej:services:Hell",
  endpointInterface = "cz.dmatej.ws.generated.HellPort",
  wsdlLocation = "META-INF/wsdl/Hell.wsdl"
)
Na tuhle anotaci se mrkneme podrobněji, ať už nikoho nebolí hlava :-)

name

- tenhle atribut chybí, protože v JEE5 se <port-component-name> v sun-ejb-xml váže na název implementující třídy. Pokud atribut přeci jen přidáte a vyplníte i shodné port-component-name, Glassfish3 s tím nemá problém a vystaví službu pod novým názvem. Problém ale přijde se SGES2, kde sice nasazení projde, ale do logu hlásí WARNING se stacktrace, že atribut name se vylučuje s atributem endpointInterface.
- otázkou pro mě je, jestli je to drobná chybička SGES, nebo požadovaná vlastnost JEE5 (?). Buď jak buď, zřejmě je lepší věci nekomplikovat a implementaci služby pojmenovat, jak se sluší.

serviceName

- musí odpovídat tagu <service name=... ve WSDL
- pokud neodpovídá, glassfish upozorní, že ve WSDL jsou jiné služby ale takováhle ne, a deploy selže.

portName

- musí odpovídat tagu <port name=... ve WSDL
- pokud neodpovídá, glassfish několikrát do logu zařve, že "... could not get binding from WSDL! service ...", a deploy selže.

targetNamespace

- implicitně se generuje z názvu balíku a třídy (a s http:// na začátku), ale musí odpovídat WSDL. 
- pokud wsdlLocation nenastavíte, nasazení bude nejspíš úspěšné, jenže žádný klient se se službou neodmluví - namespaces jsou směrodatné pro (un)marshalling SOAP zpráv.
- ve výsledku je tedy nejsnazší zkopírovat namespace z interface

endpointInterface

- jak říká javadoc, celý název implementovaného rozhraní
- finta je v tom, že samotná třída nemusí plně implementovat všechny metody rozhraní, k čemuž by nás Java jinak nutila. Nikdy jsem to ovšem nezkoušel ;)
- pokud chybí, Glassfish odmítne nasazení, protože nenajde mapování operací na metody.

wsdlLocation

- cesta k WSDL, přičemž za kořen se bere adresář classes (v jar). 
- pokud atribut chybí nebo soubor není nalezen, aplikáč vygeneruje svoje vlastní WSDL podle anotací.
- pozor ještě na jednu věc, WSDL musí být v podadresáři wsdl, jinak aplikáč odmítne nasazení aplikace