3

我有一个在 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();
}
4

0 回答 0