čtvrtek 23. dubna 2020

Transport error: 401 Error: Unauthorized ve WSO2

Při použití Basic auth a patřičném nastavení property Authorization může přesto nastat výjimka
 org.apache.axis2.AxisFault: Transport error: 401 Error: Unauthorized  
      at org.apache.axis2.transport.http.HTTPSender.handleResponse(HTTPSender.java:371)  
      at org.apache.axis2.transport.http.HTTPSender.sendViaGet(HTTPSender.java:109)  

To může být způsobeno firewallem, za kterým WSO2 produkt je a který vyžaduje onu autorizaci. Řešením může být nastavení property Proxy-Authorization. Více v dokumentaci.

středa 22. dubna 2020

Peer not authenticated + SSL peer failed hostname validation for name: null

Při volání serveru přes https, v tomto případě volání endpointu z WSO2, nastala chyba:
 HTTPSender Unable to sendViaGet to url[https://myserver/context]  
 javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for name: null  
      at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.verifyHostname(TLSProtocolSocketFactory.java:233)  
      at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:186)  

Zkusil jsem spustit znovu s parametrem
 -Djdk.internal.httpclient.disableHostnameVerification=true  

a následovala výjimka:
 HTTPSender Unable to sendViaGet to url[https://myserver/context]  
 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated  
      at sun.security.ssl.SSLSessionImpl.getPeerCertificates(Unknown Source)  
      at org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory.verifyHostName(SSLProtocolSocketFactory.java:276)  

Pokud byl request proveden pomocí curl, tak fungoval
 curl -X GET --cacert /C/certs/root_cert.cer 'https://myserver/context ' -H 'Authorization: Basic QVBJOk1vbmV0YTIwMjA=' -v  

Zkusil jsem openssl
 OpenSSL> s_client -connect myserver:443  
 Loading 'screen' into random state - done  
 CONNECTED(000008C0)  
 write:errno=10054  
 ---  
 no peer certificate available  
 ---  
 No client certificate CA names sent  
 ---  
 SSL handshake has read 0 bytes and written 317 bytes  
 ---  
 New, (NONE), Cipher is (NONE)  
 Secure Renegotiation IS NOT supported  
 Compression: NONE  
 Expansion: NONE  
 No ALPN negotiated  
 ---  
 error in s_client  
Zde je vidět, že server nevrátil certifikát.

Zkusil jsem znovu s parametrem -servername
 openssl s_client -connect myserver:443 -servername myserver  

Také je vhodné ověřit, že server podporuje TLSv1.2:
 openssl s_client -connect myserver:443 -tls1_2 -servername myserver  

V obou případech bylo vidět, že server certifikát vrací. Parametr -servername nastavuje TLS SNI (Server Name Indication) extension v ClientHello message během handshake, jak je popsáno v dokumentaci.
Při následném prohlédnutí ClientHello zprávy pomocí wiresharku bylo patrné, že neobsahuje server_name extension (více o tématu zde). Tato je pro některé servery nutná (i když dle RFC6066 nepovinná) - jeden webový server může sloužit více doménám a jaký certifikát použít se rozhoduje právě na základě obsahu server_name.
V javě lze vypnout nebo povolit (defaultní stav) SNI pomocí parametru
 -Djsse.enableSNIExtension=true  

Naštěstí jsem narazil na informaci, že Java nastavuje extension server_name pouze pro FQDN, což myserver není (podobně jako např. localhost).
Pokud tedy chceme z javy volat server, který v handshake protokolu Client Hello vyžaduje extension server_name, je třeba aby tento měl FQDN a samozřejmě odpovídající certifikát, který máme umístěn i v našem truststore.