pátek 1. července 2011

SSL certifikát pro více subdomén spravovaných jedním serverem glassfish

Případ, kdy je více domén 3.řádu (k jedné doméně 2. řádu) obsluhováno jedním serverem je celkem častý. Pokud tyto domény využívají SSL je zde možnost využít  certifikát vystavený pro všechny domény třetího řádu (*.mojedomena.com)
Popíši teď postup pro případ, kdy si vytvořím certifikát podepsaný sám sebou. Budu předpokládat, že alias certifikátu nastavený v http listeneru serveru glassfish se jmenuje s1as. V Admin consoli je toto nastavení v Configuration - Network Config - Network Listeners - http-listener-nazev - záložka SSL - Certificate NickName. Všechni níže uváděné příkazy jsou prováděny v
1) Nejprve smažu dosud používaný privátní klíč:
 keytool -delete -alias s1as -keystore keystore.jks -storepass changeit  
2) Smažu certifikát:
 keytool -delete -alias s1as -keystore cacerts.jks -storepass changeit  
3) Vytvořím privátní klíč:
 keytool -genkey -alias s1as -keypass changeit -storepass changeit -keystore keystore.jks  
4) Předchozí příkaz je interaktivní. Je nutné na otázku "What is your first and last name?" napsat "*.mojedomena.com" další odpovědi již nejsou tolik významené.
5) Exportuji do souboru certifikát:
 keytool -export -alias s1as -storepass changeit -file cert.cer -keystore keystore.jks  
6) Importuji certifikát do cacerts:
 keytool -import -v -trustcacerts -alias s1as -file servercpost.cer -keystore cacerts.jks -keypass changeit -storepass changeit  

Hotovo, nyní by již měl glassfish po restartu na příchozí https požadavky využít nový "hvězdičkový" certifikát.

úterý 7. června 2011

Zkušenost s Českou spořitelnou

U ČS jsem měl hypotéku. Nebyla z nejvýhodnjších a tak jsem se rozhodl refinancovat ji za pomoci jiného finančního ústavu. Asi měsíc před koncem fixace jsem se tedy chtěl spojit s ČS a záležitost probrat. Zkoušel jsem tedy zavolat své osobní finančí poradkyni, která mi byla přidělena. Číslo neexistovalo. Zavolal jsem tedy na ústřednu pobočky ČS, kde poradkyně sídlila. Paní na ústředně mi sdělila, že tato zaměstnankyně u nich již nepracuje. Chtěl jsem tedy někoho jiného, kdo by byl schopen můj požadavek konzultovat. Byl jsem přepojen na jinou poradkyni. Rozhovor s ní, po vysvětlení detailů, vypadal asi následovně:
- Ale ke mě nepatříte, to musíte řešit s někým jiným.
- Tak pak nechápu proč mě přepojili na vás.
- Protože jsem tady jediná, kdo aspoň trochu rozumí hypotékám.
(Pozn.: podotýkám, že jde o pobočku ve městě, které má s okolím cca 70 000 obyvatel)
- A ke komu tedy patřím?
- Já se vám podívám a zavolám vám zpět.
Opravdu zavolala a sdělila mi jméno včetně kontaktu na dalšího člověka. To bylo naposledy, kdy mi z ČS někdo opravdu zavolal, poté co mi to slíbil.
Na uvedeném kontaktu jsem si domluvil schůzku. Vysvětlil o co mi jde. Dostal jsem nabídku na úroky, které mi nabídla konkurence (snížení oproti původní nabídce asi o 1%) a to bez dalších podmínek typu "nechci slevu zadarmo" (maximální možná splátka při konci fixace, minimální doba splácení atp.). Nechtěl jsem však již dál platit hypotéku z běžného účtu u ČS, ani jsem nechtěl platit poplatek za změnu tohoto účtu. Poplatek za změnu by činil 500 kč, přestože mě bývalá poradkyně tvrdila, že je to za 5000 kč. Poradcem mi bylo tedy nabídnuto, že by mi z běžného účtu u ČS nebyly účtovány poplatky, abych ho dále využíval. I toto jsem odmítl, protože mě už nebavilo se s ČS dále handrkovat o jednotlivé "slevy", jejichž výsledkem je cena jako u konkurence.
Také jsem si poradci postěžoval na předchozí poradkyni, u které jsem chtěl zrušit svoji debetní kartu, protože jsem nechtěl za ní platit poplatek 200 kč ročně, když ji stejně nepoužívám. Poradkyně mi řekla, že když si ji žena (majitel účtu) neaktivoval, tak mě poplatek strhávat nebudou. Informace se neukázala pravdivou, když mi byl poplatek na začátku roku opět stržen. Na to mi poradce poradil, abych napsal stížnoust a poplatek mi bude vrácen.
Část hypotéky jsem zaplatil ze svého. Potřeboval jsem však potvrzení pro novou banku, že do ČS peníze opravdu dorazili. Osobní poradce mi sdělil, že neví, jestli nějaké takové potvrzení je vůbec možné vydat. Za týden ve středu však měl mít z dovolené v práci opět kolegyni (asi ta, co jediná na pobočce aspoň trochu rozumí hypotékám), která bude vědět, zda je možné toto vydat a pak mi zavolá. Asi týden poté, kdy již měla kolegyně nastoupit do pracovního procesu, jsem poradci volal, jak to vypadá s tím potvrzením, když se podle slibu neozval on mě. Říkal, že ve čtvrtek to bude. Ve čtvrtek jsem tedy přišel na pobočku a potvrzení opravdu bylo.
Tím jsem měl hypotéku vyřešenou a nastal problém se zrušením účtu, kterýmžto vlastníkem je moje žena. Ta si naivně zašla na pobočku, vzala lístek a čekala až na ni příjde řada. Číslo se objevilo s tím, že musí na přepážku o patro víš. Než tam došla bylo tam již jiné číslo. Další číslo si tedy vzala již na vyšším patře a čekala znovu. Když se na ní dostala řada, paní u přepážky ji řekla, že stiskla nesprávné tlačítko, že s jejím požadavkem musí k jiné přepážce a rozloučila se s ní. Šla tedy k tiskárně čísel znovu, stiskla doporučené tlačítko a čekala opět. Již byla u správné přepážky. Nikoli však pro ní.
(Možná si teď říkáte, že moje žena je asi úplně blbá, když není schopná stisknout ve správném patře správné tlačítko. To je ale v rozporu s mými zkušenostmi a také s tím, že byla schopná bez problémů absolvovat vysokou školu technického směru, kde byl odpad studentů přes 80%) Paní u další přepážky jí sdělila, že jí byl přidělen osobního poradce a toto má řešit u něj, nejprve pracovnice zkoušela poradci zavolat, ale nedovolala se. Dala tedy manželce kontak na tohoto osobního poradce, aby si s ním dohodla schůzku a po 1,5 hodině čekání a tisknutí lístků ji poslala domů s nepořízenou.
Poradci jsem poslal mail s tím, že bychom si chtěli domluvit schůzku kvůli rušení účtu a informaci o možnosti připojištění dítěte. Poradce navrhl termín, já ho potvrdil. V domluvený termín jsme se se ženou dostavili na pobočku a ...poradce přítomen nebyl. Zeptal jsem se tedy na něho jeho kolegyně od vedlejšího stolu. Ta mi řekla, že jsem si s ním měl domluvit schůzku. Já odvětil, že tu jsem měl domluvenou na teď. Řekla, ať chvíli počkáme a někam odešla. Pak přišla s úsměvem na tváři a říkala, že to s námi tedy vyřídí ona. Začala mluvit o připojištění dítěte (takže věděla, kvůli čemu jsme přišli). Připojištění jsme nakonec podepsali. Pak nám najednou řekla nashledanou. Tomu jsem se podivil, protože obvykle se ptají jestli potřebujeme ještě něco. Moje žena odvětila, že ale ještě chtěla zrušit běžný účet. Paní na to: "Ale to není jen tak, to se musí připravit a já na vás nemám už teď čas." Nakonec řekla, že dá vědět našemu poradci, aby se nám ozval. Pak jsme odešli. Poradce se samozřejmě neozval.

středa 6. dubna 2011

JAX-WS enterprise application client důvěřující všem serverovým certifikátům

Nedávno jsem vytvořil webovou službu využívající SSL a k ní enterprise aplikačního klienta pro server glassfish za použití JAX-WS. Bylo třeba aby tato dvojice byla spustitelná i na jiném počítači. Tedy klient musel akceptovat i jiné, v době vývoje neznámé, serverové certifikáty. Jedním z možných řešení je vytvořit klienta tak, aby automaticky důvěřoval všem serverovým certifikátům ať obsahují cokoli. Upozorňuji že toto řešení v žádném případě není vhodné pro produkční prostředí.
Aby klient důvěřoval všem certifikátům je třeba vytvořit následující třídu:
1:  import javax.net.ssl.X509TrustManager;  
2:  public class TrustEverythingTrustManager implements X509TrustManager {  
3:      public java.security.cert.X509Certificate[] getAcceptedIssuers() {  
4:        return null;  
5:      }  
6:      public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {  }  
7:      public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {  }  
8:    }  

Navíc WSDL, jehož kopie je i lokálně na straně klienta, obsahuje následující adresu: <soap:address location="https://localhost:8181/services/testPort"/>. Pokud server vrátí certifikát jehož CN bude jiné než localhost, což s největší pravděpodobností bude, nastane následující vyjímka:
 java.lang.reflect.InvocationTargetException  
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)  
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)  
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)  
     at java.lang.reflect.Method.invoke(Method.java:597)  
     at org.glassfish.appclient.client.acc.AppClientContainer.launch(AppClientContainer.java:424)  
     at org.glassfish.appclient.client.AppClientFacade.main(AppClientFacade.java:134)  
 Caused by: com.sun.xml.ws.client.ClientTransportException: HTTP transport error: java.io.IOException: HTTPS hostname wrong: should be <localhost>  
     at com.sun.xml.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java:135)  
     at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:163)  
     at com.sun.xml.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:95)  
     at com.sun.xml.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:105)  
     at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)  
     at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)  
     at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)  
     at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)  
     at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:112)  
     at com.sun.enterprise.security.webservices.ClientSecurityPipe.processSecureRequest(ClientSecurityPipe.java:192)  
     at com.sun.enterprise.security.webservices.ClientSecurityPipe.process(ClientSecurityPipe.java:180)  
     at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:115)  
     at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)  
     at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)  
     at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)  
     at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)  
     at com.sun.xml.ws.client.Stub.process(Stub.java:319)  
     at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:157)  
     at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:109)  
     at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)  
     at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:140)  
     at $Proxy46.testOperation(Unknown Source)  
     at testwsappclient.Main.main(Main.java:62)  
     ... 6 more  
 Caused by: java.io.IOException: HTTPS hostname wrong: should be <localhost>  
     at sun.net.www.protocol.https.HttpsClient.checkURLSpoofing(HttpsClient.java:524)  
     at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:448)  
     at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)  
     at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1014)  
     at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)  
     at com.sun.xml.ws.transport.http.client.HttpClientTransport.getOutput(HttpClientTransport.java:123)  
     ... 28 more  

Pro tento případ potřebujeme vytvořit další třídu:
1:  import javax.net.ssl.HostnameVerifier;  
2:  import javax.net.ssl.SSLSession;  
3:  public class VerifyEverythingHostnameVerifier implements HostnameVerifier {  
4:    public boolean verify(String string, SSLSession sslSession) {  
5:      return true;  
6:    }  
7:  }  

A nyní obě třídy využijeme v kódu klienta:
1:  public class Main {  
2:    @WebServiceRef(wsdlLocation = "META-INF/test.wsdl")  
3:    private static TestService service;  
4:    public static void main(String[] args) {  
5:      Main main = new Main();  
6:      TestPortType port = service.getTestPort();  
7:      ((BindingProvider) port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "user");  
8:      ((BindingProvider) port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "password");  
9:      HostnameVerifier hostNameVerifier = new VerifyEverythingHostnameVerifier();  
10:      ((BindingProvider) port).getRequestContext().put("com.sun.xml.ws.transport.https.client.hostname.verifier", hostNameVerifier);  
11:      SSLContext sslContext;  
12:      try {  
13:        TrustManager[] trustManager = new TrustManager[]{new TrustEverythingTrustManager()};  
14:        sslContext = SSLContext.getInstance("SSL");  
15:        sslContext.init(null, trustManager, new java.security.SecureRandom());  
16:        HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());  
17:      } catch (NoSuchAlgorithmException ex) {  
18:        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);  
19:      } catch (KeyManagementException ex) {  
20:        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);  
21:      }  
22:      ObjectFactory of = new ObjectFactory();  
23:      TestOperationRequest req = of.createTestOperationRequest();  
24:      req.setFirstName("Fist");  
25:      req.setLastName("Last");  
26:      try {  
27:        String back = port.testOperation(req);  
28:        System.out.println(back);  
29:      } catch (TestOperationFault ex) {  
30:        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);  
31:      }  
32:    }  
33:  }  

Obdobný problém u REST služby je řešen zde.

úterý 15. února 2011

Import privátních klíčů PKCS12 do JKS keystoru glassfishe

Výchozí stav: mám soubor od certifikační autority s příponou p12, heslo k privátnímu klíči a potřebuji ho použít pro SSL v glassfishi jako náhradu za prošlý certifikát
Krok 1: nejprve je třeba z keystore.jks odstranit onen prošlý certifikát:
 keytool -delete -alias alias -keystore keystore.jks -storepass changeit -v  

Krok 2: import obsahu p12 do keystore.jks
 keytool -v -importkeystore -srckeystore cert_auth.p12 -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS  

Krok 3: Dost možná nemá importovaný klíč stejný alias jaký bychom si představovali. My bychom např. chtěli aby se jmenoval ap1as1 a on se jmenuje authkey#1, to lze vyřešit následovně:
 keytool -changealias -alias authkey#1 -destalias ap1as1 -keystore keystore.jks  

Po zadání tohoto příkazu se keytool nejprve zeptá na heslo pro keystore a poté na heslo ke klíči, který jsme v předchozím kroku importovali.
Krok 4: V tomto stavu by po restartu glassfishe pravděpodobně nastala chyba java.security.UnrecoverableKeyException: Cannot recover key Důvodem je jiné heslo pro nově importovaný klíč a jiné heslo pro keystore. Hesla lze sjednotit následovně:
 keytool -keypasswd -alias ap1as1 -keypass oldKeyPassword -new changeit -keystore keystore.jks -storepass changeit -v  

Nyní by mělo stačit restartovat glassfish.