我有一个在 1.6 中编译的签名小程序(具有 1.5 兼容性),它可以建立一些 HTTPS 获取连接。
服务器上的 HTTPS 证书是自签名的,但小程序嵌入了一个 .pem 文件,其中包含允许验证证书链的根证书。
小程序使用有效的商业证书进行签名。
在我的单元测试中,HTTPS 连接没有任何问题。如果没有此 .pem 导入
,则会出现预期的连接错误:
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
但是当我在我的网站中使用小程序时,当 HTTPS 连接完成时会弹出以下安全警告:
无论我单击继续还是取消,HTTPS 连接都没有任何问题,并且此警告不再显示。
有没有办法删除这个无用的弹出窗口?
在我的MANIFEST.MF中,我添加了:
Trusted-Library: true
我也对此进行了测试,没有任何更改:
Trusted-Only: true
Permissions: all-permissions
我的 java 代码是这里代码的略微修改版本:
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));
}
解决方法 (09/30/2013)
如果通过 Java 控制面板添加 .pem,则在 Secure Site CA 中,不再有 Java 弹出窗口。
因此,解决方法是以编程方式将 .pem 添加到此密钥库(jsse cacerts)。
我修改了函数getSocketFactoryFromPEM的代码,以便将密钥库存储在正确的位置:
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();
}