I found a peculiar issue while using client stub which was generated in past using JAX-WS(wsimport) for SSRS server. Once the test connection is successful with configured username and password, then all the subsequent connection passes, even for the incorrect configuration.
Application uses default Authenticator to set credentials.
Authenticator.setDefault(
new Authenticator() {
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
getUserName(),getPassword().toCharArray());
}
});
Later, I dig deeper and found out that sun package HttpURLConnection uses the cached authorization key for connection from the AuthCache implementation. Worth to mention that it is part of sun package.
package sun.net.www.protocol.http;
public class AuthCacheImpl implements AuthCache {
...
...
}
Now, current project is running with JDK11 which restricts to use the protected classes from sun package.
Now, the client stub is fairly straightforward to understand.
ReportingService2010 reportingService2010 = new ReportingService2010(reportServerUrl);
ReportingService2010Soap reportingService2010Soap = reportingService2010.getReportingService2010Soap();
reportingService2010Soap.listChildern(itemPath,isRecursive);
Request from above mentioned code calls the HttpURLConnection implementation of sun package and tries to read the auth key from cache whether it exists or not. If yes, than use it without considering new credentials and connects, else add the auth key to the AuthCache for future usage.
package sun.net.www.protocol.http;
public class HttpURLConnection extends java.net.HttpURLConnection {
...
...
private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
...
...
serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme,
getAuthenticatorKey());
ret = AuthenticationInfo.getServerAuth(serverAuthKey);
...
...
}
...
...
}
Is there any way to handle this issue ? Or any other alternative ?
PS - restart of server clears the cache but thats an ugly option.
Related
We were using our own custom keystore and also provided the custom class implementation using JSSEImplementation and ServerSocketFactory and configured both in server.xml for "store" and "sslImplementation" attributes.
But now upgrading to 8.5, I started getting lot of ClassNotFoundException for JSSESocketFactory etc.
Doing little more research I found that they have removed many classes and methods like JSSESocketFactory.java, getServerSocketFactory(), getSSLUtil(AbstractEndpoint endpoint) etc.
So now, my question is:
is there any way in apache tomcat 8.5 in which I can configure my custom keystore under "store" in server.xml and use my own sslImplementation?
I was using AbstractEndpoint in the method signature to get the store name set in server.xml and then loading that keystore in MyJSSESocketFactory like this:
public class MySSLImplementation extends JSSEImplementation
{
#Override
public org.apache.tomcat.util.net.ServerSocketFactory getServerSocketFactory(
AbstractEndpoint endpoint) {
kStore = endpoint.getProperty("store");
return new MyJSSESocketFactory(endpoint, kStore);
}
}
public class MyJSSESocketFactory extends JSSESocketFactory {
private final AbstractEndpoint _endpoint;
private final String store;
public MyJSSESocketFactory(AbstractEndpoint endpoint, String store) {
super(endpoint);
this._endpoint = endpoint;
this.store = store;
}
/*
* Gets the SSL server's keystore.
*/
#Override
protected KeyStore getKeystore(String type, String provider, String pass)
throws IOException {
if ("MYKS".equalsIgnoreCase(type)) {
String keystoreName = store;
KeyStore ks = null;
try {
if (provider == null || provider.isEmpty()) {
ks = KeyStore.getInstance(type);
} else {
ks = KeyStore.getInstance(type, provider);
}
MyStoreParameter params = new MyStoreParameter(
keystoreName);
ks.load(params);
} catch (Exception ex) {
throw new IOException(
"Failed to load keystore " + keystoreName, ex);
}
return ks;
} else {
return super.getKeystore(type, provider, pass);
}
}
}
"MYKS" is set in server.xml for "store" attribute
For whatever it's worth, this is the commit that broke it:
Remove BIo specific JSSE code
Here is some of the rationale for removing it:
https://github.com/spring-projects/spring-boot/issues/6164
#markt-asf There appears to be a number of breaking changes in 8.5.3
(upgrading from 8.0.33):
Maven artifact org.apache.tomcat.embed:tomcat-embed-logging-juli no longer exists
Class org.apache.tomcat.util.net.ServerSocketFactory no longer exists
Class org.apache.tomcat.util.net.jsse.JSSESocketFactory no longer exists
Method JSSEImplementaton.getServerSockerFactory(AbstractEndpoint) no longer exists
Method JSSEImplementaton.getSSLUtil(AbstractEndpoint) no longer exists
Http11NioProtocol.getEndpoint() is no longer visible
Field org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML no longer exists
AbstractHttp11Protocol.setCompressableMimeTypes no longer exists
Reply:
1 is only required to enable container logging via log4j 1.x and that
version is no longer supported by the log4j community. log4j 2.x can
be used for container logging without any extra libraries.
2-6 are side effects of the connector refactoring in 8.5.x / 9.0.x.
They are all low-level internal APIs that I'm a little surprised to
find boot is using.
7 was part of the mechanism used to pass web.xml to Jasper for
processing. It was removed as it was no longer required as of Servlet
3.0 as all the necessary information was available via the standard Servlet API.
8 That was some API clean-up. That one could be restored fairly easily
for 8.5.x.
The commit was made in Nov, 2014.
As of Tomcat 8.0, the class was still there - and NOT on the *deprecated" list:
https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/tomcat/util/net/jsse/JSSESocketFactory.html
Here is the changelog that discussed "removing BIO" ("Blocking I/O"):
Migrating from Tomcat 8.0 to 8.5
Finallly, comparing these two links might help:
SSL/TLS Configuration HOW-TO Tomcat 8.0.39
SSL/TLS Configuration HOW-TO Tomcat 8.5.9
I'm trying to access a Message Queue configured in GlassFish but have been facing a lot of problems. The first one is regarding the Trust Store password on the client side. I can only make it work if I use the default password (changeit), I'm always getting
Keystore was tampered with, or password was incorrect
if I try to use a different password. And yes, I'm pretty sure I'm using the correct password.
In fact, if I set the Trust Store with the default password and use the following property with a wrong value, it still works:
-Djavax.net.ssl.trustStorePassword=<wrong value>
Which leads me to conclude that it completely ignores this setting.
This is what I've so far:
System.setProperty("com.sun.CSIV2.ssl.standalone.client.required", "true");
System.setProperty("javax.net.ssl.trustStore", "<my path>/truststore.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "<my password>");
Properties properties = new Properties();
System.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
System.setProperty("org.omg.CORBA.ORBInitialPort", "3820");
InitialContext ctx = new InitialContext(properties);
com.sun.messaging.ConnectionFactory connectionFactory = new com.sun.messaging.ConnectionFactory();
connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, "127.0.0.1:7676");
connectionFactory.setProperty(ConnectionConfiguration.imqDefaultUsername, "<app user>");
connectionFactory.setProperty(ConnectionConfiguration.imqDefaultPassword, "<app user password>");
QueueConnection queueConnection = connectionFactory.createQueueConnection();
queueConnection.start();
So, my first question is: How can I use the non-default password on my Trust Store? Do I have to use some kind of encoding/hashing on the value?
My second question is regarding authentication. The standard way of getting the Connection Factory (via JNDI) and authenticate (via createQueueConnection(user, pass)) does not seem to be working, it always tries to use the guest account. Is there a way to use the standard approach on GlassFish or am I limited to use proprietary code?
EDIT #1
After digging a little deeper, I've detected a behavior that seems a bug.
Looking at the stacktrace, one of the first methods being invoked is initJKS() from com.sun.enterprise.security.ssl.impl.SecuritySupportImpl. With the help of javassist, I realized that the following section is being invoked:
if (masterPasswordHelper == null && Globals.getDefaultHabitat() != null) {
masterPasswordHelper = Globals.getDefaultHabitat().getByType(MasterPasswordImpl.class);
}
if (masterPasswordHelper instanceof MasterPasswordImpl) {
keyStorePass = masterPasswordHelper.getMasterPassword();
trustStorePass = keyStorePass;
}
Causing the bypass of the next set of instructions:
if (keyStorePass == null) {
keyStorePass = System.getProperty(KEYSTORE_PASS_PROP, DEFAULT_KEYSTORE_PASS).toCharArray();
trustStorePass = System.getProperty(TRUSTSTORE_PASS_PROP, DEFAULT_TRUSTSTORE_PASS).toCharArray();}
And, as a consequence, to ignore the property javax.net.ssl.trustStorePassword. Why is Globals.getDefaultHabitat() returning a non null value?
I use the following code and it works:
Properties props = new Properties();
props.setProperty(AdminClient.CONNECTOR_HOST, "localhost");
props.setProperty(AdminClient.CONNECTOR_PORT, port); //2809
props.setProperty(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_RMI);
props.setProperty(AdminClient.CONNECTOR_SECURITY_ENABLED, "true");
props.setProperty(AdminClient.USERNAME, user);
props.setProperty(AdminClient.PASSWORD, password);
adminClient = AdminClientFactory.createAdminClient(props);
but I'd like to find a way to not use a user name or password, does anyone have any idea how to achieve this? Maybe a J2C authentication alias of WAS could be used here?
The only alternative you have (as far as I am aware) would be to configure your soap.client.props with the username/password and run the wsadmin script without connection/authentication information. In that scenario, you can leverage WebSphere's ability to protect the password within the properties file. There are two caveats to that scenario though.
If you utilize the soap.client.props in the WebSphere profile, anyone running wsadmin would be leveraging that username/password. This could be a security issue. Though you can work around this by using the 'wsadmin.sh -p' option and specifying a special props file at execution time.
WebSphere sucks at protecting the password out of the box. It only applies an XOR, which can be easily reversed. Unless you've written your own custom encryption for WebSphere.
AdminClient uses the current run-as subject (as determined by the static getRunAsSubject/setRunAsSubject methods in WSSubject). If you provide a user and password during the creation of the AdminClient, then WebSphere actually performs a login and changes the run-as subject on the current thread.
I would expect (to be tested) that if there is already a run-as user and you don't provide a user/password when creating the AdminClient, then WebSphere uses the current run-as subject. If that is correct, then it should be enough to use the #RunAs annotation on the servlet or EJB and map the corresponding role to the user you want in the admin console.
Yes, it is possible to use J2C authentication alias, check the following snippet
import com.ibm.wsspi.security.auth.callback.Constants;
import com.ibm.wsspi.security.auth.callback.WSMappingCallbackHandlerFactory;
import javax.resource.spi.security.PasswordCredential;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
...
Map map = new HashMap();
map.put(Constants.MAPPING_ALIAS, YOUR_J2C_AUTHENTICATION_ALIAS);
CallbackHandler callbackHandler = null;
try {
callbackHandler = WSMappingCallbackHandlerFactory.getInstance().getCallbackHandler(map, null);
} catch (NotImplementedException e) {
logger.error(e);
return;
}
LoginContext loginContext;
try {
loginContext = new LoginContext("DefaultPrincipalMapping", callbackHandler);
} catch (LoginException e) {
logger.error(e);
return;
}
try {
loginContext.login();
} catch (LoginException e) {
logger.error(e);
return;
}
Subject subject = loginContext.getSubject();
Set credentials = subject.getPrivateCredentials();
PasswordCredential passwordCredential = (PasswordCredential) credentials.iterator().next();
Properties props = new Properties();
props.setProperty(AdminClient.CONNECTOR_HOST, "localhost");
props.setProperty(AdminClient.CONNECTOR_PORT, port); //2809
props.setProperty(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_RMI);
props.setProperty(AdminClient.CONNECTOR_SECURITY_ENABLED, "true");
props.setProperty(AdminClient.USERNAME, passwordCredential.getUserName());
props.setProperty(AdminClient.PASSWORD, new String(passwordCredential.getPassword()));
adminClient = AdminClientFactory.createAdminClient(props);
I'm trying to write a small java program that connects to a twitter search URL (which returns a JSON list of tweets) using the URL connection libary.
My code which is taken from the java tutorials looks like :
public static void main(String[] args) throws Exception {
URL oracle = new URL("http://search.twitter.com/search.json?q=hi");
URLConnection yc = oracle.openConnection();
BufferedReader in = new BufferedReader(
new InputStreamReader(
yc.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
but for some reason I keep getting the following Exception:
in thread "main" java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
I don't know if this is something due to the way I've written the code, and eclipse setting or something to do with my network. I do have a proxy server configured for internet access. As far as I know this is properly configured because I am getting updates and can install new software through eclipse. Do I need to put the proxy information in the URL method somehow or is something else the problem.
URL rely on System properties for proxy, try setting proxy like this:
System.setProperty("http.proxyHost", "yourproxyserver");
System.setProperty("http.proxyPort", "portnumber");
Unfortunately, a correct proxy setup in Eclipse seems not to help with proxying Java programs started in Eclipse. Similarly, setting the Java system settings to use the systemwide proxy settings doesn't either. Not when you have a proxy that requires authentication, anyway.
As Thomas Johan Eggum said, if you have a "normal," non-authenticating proxy then setting the two JVM variables http.proxyHost and http.proxyPort either in the command line via -D or programmatically (see below) will take care of things.
For an authenticating proxy server, i.e. one that wants to see a user ID and password, many people recommend setting http.proxyUser and http.proxyPassword. That's bad advice, because these don't work. Apparently they are not defined in the Java docs.
Unfortunately, it looks like the way to "do" authentication is to use an Authenticator, programmatically. If you're going to do that, you might as well do the whole thing programmatically, i.e. including host and port. Here's how I got that to work:
public static void main(String[] args) {
try {
System.setProperty("http.proxyHost", "my.proxy.host");
System.setProperty("http.proxyPort", "8080-or-whatever-proxy-port");
Authenticator.setDefault(new DummyAuthenticator());
/* do your main program stuff */
} catch (Exception e) {
e.printStackTrace();
}
}
private static class DummyAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(
"my-user-id", "my-password".toCharArray()
);
}
}
I have a very simple code that uses HttpURLConnection to access some web site via proxy
System.setProperty("java.net.useSystemProxies", "true");
System.out.println("Proxy: " + ProxySelector.getDefault().select(new URI(urlS)));
URL url = new URL(urlS);
HttpURLConnection ic = (HttpURLConnection)url.openConnection();
ic.connect();
For some reason, Java thinks that I need SOCKS proxy, not http, throwing the following exception:
ERROR: Can't connect to SOCKS proxy:Connection timed out: connect
If you are having this issues on Windows, you may run into a Java bug.
Java treats any system proxy setting as SOCKS. You have to either disable useSystemProxies or don't use proxy in Windows.
If proxy is needed, try to uncheck "Use the same proxy server for all protocols", making sure the field for the SOCKS proxy is blank. That fixed our problem.
The real problem is that Java assumes that the "Use the same proxy server for all protocols" check affects SOCKS proxy too (I don't know the logic behind this dialog in Windows, but it is, at least, confusing)
If the check is set, you get proxies enabled for both HTTP and SOCKS, wich is very unlikely to be the desired configuration.
One way to solve it is unchecking the check and leaving blank the SOCKS field.
I finally solved it creating a ProxySelector wich first calls the default selector and if it finds the same configuration for HTTP and SOCKS connections, it omits the SOCKS proxy.
public class SocksFixerProxySelector extends ProxySelector {
ProxySelector base;
public SocksFixerProxySelector() {
base = ProxySelector.getDefault();
}
#Override
public List<Proxy> select(URI uri) {
List<Proxy> baseList = base.select(uri);
try {
if (uri.getScheme().equals("socket")) {
Proxy socksProxy = findByType(baseList, Type.SOCKS);
if (socksProxy != null) {
URI httpTestUri = new URI("http", uri.getHost(), uri.getPath(), uri.getFragment());
Proxy httpProxy = findByType(base.select(httpTestUri), Type.HTTP);
if (httpProxy != null && socksProxy.address().equals(httpProxy.address())) {
// Quitamos SOCKS
List<Proxy> filteredList = new ArrayList<>(baseList);
filteredList.remove(socksProxy);
return filteredList;
}
}
}
} catch (Exception e) {
}
return baseList;
}
#Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
base.connectFailed(uri, sa, ioe);
}
private Proxy findByType(List<Proxy> proxies, Proxy.Type type) {
for (Proxy proxy : proxies) {
if (proxy.type() == type)
return proxy;
}
return null;
}
Maybe a better solution would be to inspect the registry and detect the right settings, but I didn't want to mess with Windows specific code (and all those script settings looked bad, too )
You need to use the http.proxyHost system property instead. See http://java.sun.com/javase/6/docs/technotes/guides/net/proxies.html for details.
java -Dhttp.proxyHost=webcache.mydomain.com GetURL
Check that something has not set the "socksProxyHost" property in the Systems properties.
EDIT
The "useSystemProxies" property is described thus:
"On recent Windows systems and on Gnome 2.x platforms it is possible to tell the default ProxySelector to use the system proxy settings (both recent versions of Windows and Gnome 2.x let you set proxies globally through their user interface). If the system property java.net.useSystemProxies is set to true (by default it is set to false for compatibility sake), then the default ProxySelector will try to use these settings."
So, assuming that you have not supplied your own ProxySelector class, you should also check the system proxy settings to ensure that they don't say to use SOCKS.