I have a signed applet compiled in 1.6 (with 1.5 compatibility) that make some HTTPS get connections.
The HTTPS certificate on the server is selfsigned, but the applet embeds a .pem file containing the root certificate that allows to validate the certification chain.
The applet is signed with a valid commercial certificate.
In my unit tests, the HTTPS connection is done without any problem.
And there is an expected connection error without this .pem import :
javax.net.ssl.SSLHandshakeException - sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
But when I use the applet in my website, there is the following security warning popup when the HTTPS connection is done:
Whether I click on Continue or Cancel, the HTTPS connection is done without any problem, and this warning never shows anymore.
Is there a way to remove this useless popup?
Inside my MANIFEST.MF, I have added:
Trusted-Library: true
I also tested this, without any changes:
Trusted-Only: true
Permissions: all-permissions
My java code is a slightly modified version of the code from here :
InputStream pemStream = getClass().getResourceAsStream("/Resources/cacert.pem");
HttpsURLConnection con = (HttpsURLConnection) new URL(url).openConnection();
con.setSSLSocketFactory(getSocketFactoryFromPEM(pemStream));
con.setRequestMethod("GET");
con.setDoInput(true);
con.setDoOutput(false);
con.connect();
InputStream connectionStream = con.getInputStream();
private SSLSocketFactory getSocketFactoryFromPEM(InputStream pemStream) throws Exception
{
byte[] certAndKey = streamToBytes(pemStream);
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
private static byte[] streamToBytes(InputStream fileStream) throws IOException
{
ByteArrayOutputStream ous = null;
try
{
byte[] buffer = new byte[4096];
ous = new ByteArrayOutputStream();
int read = 0;
while ((read = fileStream.read(buffer)) != -1)
ous.write(buffer, 0, read);
}
finally
{
try
{
if (ous != null)
ous.close();
}
catch (IOException e)
{
// swallow, since not that important
}
try
{
if (fileStream != null)
fileStream.close();
}
catch (IOException e)
{
// swallow, since not that important
}
}
return ous.toByteArray();
}
private static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter)
{
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
private static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException
{
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
Workaround (09/30/2013)
If the .pem is added via the Java Control Panel, in the Secure Site CA, there is no more Java popup.
So, the workaround is to add the .pem programmatically to this key store (jsse cacerts).
I modified the code of the function getSocketFactoryFromPEM in order to store the keystore at the correct place:
private SSLSocketFactory getSocketFactoryFromPEM(InputStream pemStream) throws Exception
{
byte[] certAndKey = streamToBytes(pemStream);
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
String userHome = System.getProperty("user.home");
String certPath = userHome + File.separator;
int os = getOperatingSystem();
switch (os)
{
case WINDOWS:
// <User Application Data Folder>\LocalLow\Sun\Java\Deployment\security\trusted.jssecacerts
certPath += "AppData" + File.separator + "LocalLow" + File.separator + "Sun" + File.separator + "Java" + File.separator + "Deployment";
break;
case MAC:
// ~/Library/Application Support/Oracle/Java/Deployment/security/trusted.jssecacerts
certPath += "Library" + File.separator + "Application Support" + File.separator + "Oracle" + File.separator + "Java" + File.separator + "Deployment";
break;
case LINUX:
// ${user.home}/.java/deployment/security/trusted.jssecacerts
certPath += ".java" + File.separator + "deployment";
break;
default:
break;
}
certPath += File.separator + "security" + File.separator + "trusted.jssecacerts";
File certInputFile = new File(certPath);
FileInputStream certInputStream = null;
if (certInputFile.canRead())
{
certInputStream = new FileInputStream(certInputFile);
keystore.load(certInputStream, null);
}
else
{
keystore.load(null);
}
keystore.setCertificateEntry("cert-alias", cert);
FileOutputStream certOutputFile = new FileOutputStream(certInputFile);
keystore.store(certOutputFile, "".toCharArray());
certOutputFile.close();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keystore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}