Java 和 SSL 证书

2022-09-03 00:02:15

我正在尝试使用安全套接字层(HTTPS)在Java中与我的PHP脚本建立连接,但我发现为了确保最大的安全性/有效性,我必须将我的网站使用的SSL证书导入到我的应用程序中...我不知道该怎么做。

如果它有帮助,我的SSL证书不是自签名的,而是由StartSSL提供的,我正在使用Eclipse IDE。

任何人都可以为我指出正确的方向吗?即,我需要哪些文件,我应该在哪里导入它们以及我需要Java中的哪些代码等?


答案 1

我发现为了确保最大安全性/有效性,我必须将我的网站使用的SSL证书导入到我的应用程序中

当你做出这个声明时,你是部分正确的。您无需导入 SSL 证书。导入 StartSSL CA 证书就足够了。

此外,没有将证书导入 Java 应用程序这样的事情。Java 中的 SSL 支持依赖于密钥库和信任库的概念,而不是应用程序中打包的某些证书。如果要发布应用程序以供最终用户下载和执行,则无需发布证书或应用程序中的私钥。私有密钥和关联的证书将存储在只有您可以访问的密钥库中。

应用程序的最终用户将依赖于 Java 运行时中的 SSL 支持,这将使应用程序能够在验证服务器证书后与站点建立 SSL 连接。Java 运行时在信任库中附带了一组缺省 CA 证书,成功建立 SSL 连接的唯一先决条件是由信任库中的某个 CA 颁发服务器的 SSL 证书。StartSSL 的证书不存在于 Java 运行时的信任库中,至少从版本 6 开始,因此:

  • 您可以指示最终用户执行将 StartSSL CA 证书导入 Java 信任库的活动。可能有帮助的链接包括此StartSSL论坛线程(只需前4个步骤即可将CA证书导入信任库),GitHub项目此博客文章;免责声明 - 我没有尝试使用其中任何一个,您应该自行承担使用它的风险。
  • 或者,您可以使用 JVM 启动标志使用自己的信任库初始化应用程序,或者在初始化 SSL 连接之前执行以下代码:-Djavax.net.ssl.trustStore=<path_to_truststore> -Djavax.net.ssl.trustStorePassword=<truststore_password>

    System.setProperty("javax.net.ssl.trustStore","<path_to_truststore>");
    System.setProperty("javax.net.ssl.trustStorePassword","<truststore_password>");
    

    仅当您的应用程序是并非小程序的 Java SE 应用程序(或对如何指定信任库具有类似限制的应用程序)时,这是一种可行的方法。


阅读Java keytool文档也会有所帮助。


答案 2

以下方法装入缺省(cacerts)密钥库,检查证书是否已安装,如果未安装,则安装证书。它消除了在任何服务器上手动运行命令的需要。keystore

它假定缺省密钥库密码 (changeit) 保持不变,如果不是,请更新。请注意,该方法在添加证书后保存密钥库,因此在运行一次后,证书将永久位于存储区中。CACERTS_PASSWORD

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

/**
 * Add a certificate to the cacerts keystore if it's not already included
 */
public class SslUtil {
    private static final String CACERTS_PATH = "/lib/security/cacerts";

    // NOTE: DO NOT STORE PASSWORDS IN PLAIN TEXT CODE, LOAD AT RUNTIME FROM A SECURE CONFIG
    // DEFAULT CACERTS PASSWORD IS PROVIDED HERE AS A QUICK, NOT-FOR-PRODUCTION WORKING EXAMPLE
    // ALSO, CHANGE THE DEFAULT CACERTS PASSWORD, AS IT IMPLORES YOU TO!
    private static final String CACERTS_PASSWORD = "changeit";

    /**
     * Add a certificate to the cacerts keystore if it's not already included
     * 
     * @param alias The alias for the certificate, if added
     * @param certInputStream The certificate input stream
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     * @throws CertificateException
     * @throws IOException
     */
    public static void ensureSslCertIsInKeystore(String alias, InputStream certInputStream)
            throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException{
        //get default cacerts file
        final File cacertsFile = new File(System.getProperty("java.home") + CACERTS_PATH);
        if (!cacertsFile.exists()) {
            throw new FileNotFoundException(cacertsFile.getAbsolutePath());
        }

        //load cacerts keystore
        FileInputStream cacertsIs = new FileInputStream(cacertsFile);
        final KeyStore cacerts = KeyStore.getInstance(KeyStore.getDefaultType());
        cacerts.load(cacertsIs, CACERTS_PASSWORD.toCharArray());
        cacertsIs.close();

        //load certificate from input stream
        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
        final Certificate cert = cf.generateCertificate(certInputStream);
        certInputStream.close();

        //check if cacerts contains the certificate
        if (cacerts.getCertificateAlias(cert) == null) {
            //cacerts doesn't contain the certificate, add it
            cacerts.setCertificateEntry(alias, cert);
            //write the updated cacerts keystore
            FileOutputStream cacertsOs = new FileOutputStream(cacertsFile);
            cacerts.store(cacertsOs, CACERTS_PASSWORD.toCharArray());
            cacertsOs.close();
        }
    }
}

像这样使用它:

SslUtil.ensureSslCertIsInKeystore("startssl", new FileInputStream("/path/to/cert.crt"));