FULL PRODUCT VERSION :
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
macOS Sierra v. 10.12.4
Darwin Mac-mini-de-Oscar.local 16.5.0 Darwin Kernel Version 16.5.0: Fri Mar 3 16:52:33 PST 2017; root:xnu-3789.51.2~3/RELEASE_X86_64 x86_64
A DESCRIPTION OF THE PROBLEM :
If you need your HTTPS connection to authenticate with a client certificate, it is necessary to define a URLStreamHandler. Then, after calling openConnection you must set a SSLSocketFactory. This socket factory must use a KeyManager object with the client certificate.
So, it is necessary to cast to HttpsURLConnection, and then, a ClassCastException is thrown.
...
URL endpoint = new URL(null, url.toString(), new URLStreamHandler()
{@Override protected URLConnection openConnection(URL url) throws IOException
{
URL cloneURL = new URL(url.toString());
HttpsURLConnection urlCon = (HttpsURLConnection)cloneURL.openConnection();
urlCon.setSSLSocketFactory(getMySocketFactory());
return urlCon;
}});
...
SSLSocketFactory getMyFactory() throws...
{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(new File(keyFile));
keyStore.load(keyInput, keyPassword.toCharArray());
keyInput.close();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
return context.getSocketFactory();
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the test case.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If you apply the workaround, the test executes silently.
ACTUAL -
class com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection
ERROR MESSAGES/STACK TRACES THAT OCCUR :
class com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public class Test
{
public static void main(String args[]) throws Exception
{
String sURL = "https://www.google.es";
// If you uncomment the following line, urlCon is HttpsURLConnection.
//URL url = new URL(sURL);
// I redirect standard error output to null, because
// I'm only interested in HttpsURLConnection error.
System.setErr(new PrintStream(new OutputStream() {
@Override public void write(int b) {}}));
URL endpoint = new URL(null, sURL, new URLStreamHandler()
{@Override protected URLConnection openConnection(URL url)
throws IOException
{
URL urlClone = new URL(url.toString());
URLConnection urlCon = urlClone.openConnection();
if(!(urlCon instanceof HttpsURLConnection))
{
System.out.println(urlCon.getClass() +" cannot be cast "+
"to javax.net.ssl.HttpsURLConnection");
System.exit(0);
}
// Here I could define a custom SSLSocketFactory in
// order to define a client certificate authentication.
// So I would need to cast to HttpsURLConnection.
return urlCon;
}});
// The SOAP call is wrong, but it is used to check if
// the URLConnection is instance of HttpsURLConnection.
SOAPMessage msg = MessageFactory.newInstance().createMessage();
SOAPConnection con = SOAPConnectionFactory.newInstance().createConnection();
con.call(msg, endpoint);
con.close();
}
} // Test
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Uncomment the line:
URL url = new URL(sURL);
This workaround means that there is a collateral effect when creating a URL object.
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
macOS Sierra v. 10.12.4
Darwin Mac-mini-de-Oscar.local 16.5.0 Darwin Kernel Version 16.5.0: Fri Mar 3 16:52:33 PST 2017; root:xnu-3789.51.2~3/RELEASE_X86_64 x86_64
A DESCRIPTION OF THE PROBLEM :
If you need your HTTPS connection to authenticate with a client certificate, it is necessary to define a URLStreamHandler. Then, after calling openConnection you must set a SSLSocketFactory. This socket factory must use a KeyManager object with the client certificate.
So, it is necessary to cast to HttpsURLConnection, and then, a ClassCastException is thrown.
...
URL endpoint = new URL(null, url.toString(), new URLStreamHandler()
{@Override protected URLConnection openConnection(URL url) throws IOException
{
URL cloneURL = new URL(url.toString());
HttpsURLConnection urlCon = (HttpsURLConnection)cloneURL.openConnection();
urlCon.setSSLSocketFactory(getMySocketFactory());
return urlCon;
}});
...
SSLSocketFactory getMyFactory() throws...
{
KeyStore keyStore = KeyStore.getInstance("PKCS12");
InputStream keyInput = new FileInputStream(new File(keyFile));
keyStore.load(keyInput, keyPassword.toCharArray());
keyInput.close();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), null, null);
return context.getSocketFactory();
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the test case.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If you apply the workaround, the test executes silently.
ACTUAL -
class com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection
ERROR MESSAGES/STACK TRACES THAT OCCUR :
class com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl cannot be cast to javax.net.ssl.HttpsURLConnection
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public class Test
{
public static void main(String args[]) throws Exception
{
String sURL = "https://www.google.es";
// If you uncomment the following line, urlCon is HttpsURLConnection.
//URL url = new URL(sURL);
// I redirect standard error output to null, because
// I'm only interested in HttpsURLConnection error.
System.setErr(new PrintStream(new OutputStream() {
@Override public void write(int b) {}}));
URL endpoint = new URL(null, sURL, new URLStreamHandler()
{@Override protected URLConnection openConnection(URL url)
throws IOException
{
URL urlClone = new URL(url.toString());
URLConnection urlCon = urlClone.openConnection();
if(!(urlCon instanceof HttpsURLConnection))
{
System.out.println(urlCon.getClass() +" cannot be cast "+
"to javax.net.ssl.HttpsURLConnection");
System.exit(0);
}
// Here I could define a custom SSLSocketFactory in
// order to define a client certificate authentication.
// So I would need to cast to HttpsURLConnection.
return urlCon;
}});
// The SOAP call is wrong, but it is used to check if
// the URLConnection is instance of HttpsURLConnection.
SOAPMessage msg = MessageFactory.newInstance().createMessage();
SOAPConnection con = SOAPConnectionFactory.newInstance().createConnection();
con.call(msg, endpoint);
con.close();
}
} // Test
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Uncomment the line:
URL url = new URL(sURL);
This workaround means that there is a collateral effect when creating a URL object.