如何通过pkcs12文件在Java中使用SSL连接到安全网站?

如何通过pkcs12文件在Java中使用SSL连接到安全网站?,java,ssl,Java,Ssl,我有一个pkcs12文件。我需要使用此连接到使用https协议的网页。我遇到一些代码,为了连接到安全的网页,我需要设置以下系统属性: System.setProperty("javax.net.ssl.trustStore", "myTrustStore"); System.setProperty("javax.net.ssl.trustStorePassword", "changeit"); System.setProperty("javax.net.ssl.keyStoreType", "p

我有一个pkcs12文件。我需要使用此连接到使用https协议的网页。我遇到一些代码,为了连接到安全的网页,我需要设置以下系统属性:

System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", "new_cert.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "newpass");
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", "new.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "newpasswd");
我有p12(pkcs12)文件。我只需要一个信任库文件

我使用以下方法提取证书:

openssl.exe pkcs12 -in c:/mykey.p12 -out c:/cert.txt -nokeys -clcerts
现在已将cert PEM文件转换为der

openssl.exe x509 -in c:/cert.txt -outform DER -out c:/CAcert.der 
现在将der文件添加到密钥库

keytool -import -file C:/Cacert.der -keystore mytruststore
现在我有了信任库,但是当我使用它时,我得到了以下错误

Exception in thread "main" java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
更新: 删除某些属性并仅设置“trustStore”、“trustStorePassword”和“trustStoreType”属性后,我得到了以下异常

java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty

请提供帮助。

您似乎正在从PKCS#12密钥存储中提取证书,并创建一个新的Java密钥存储(类型为“JKS”)。您不必严格地提供信任存储密码(尽管使用一个密码可以测试根证书的完整性)

因此,请仅在设置以下SSL属性的情况下尝试您的程序。问题中显示的列表过多,可能会导致问题

System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
此外,只要CA证书被检测为“受信任”条目,直接使用PKCS#12文件作为信任存储就可以工作。但是在这种情况下,您还必须将
javax.net.ssl.trustStoreType
属性指定为
“PKCS12”

请仅尝试使用这些属性。如果您遇到同样的错误,我怀疑您的问题不是密钥存储。如果问题仍然存在,请在问题中发布更多堆栈跟踪以缩小问题范围



新错误“trustAnchors参数必须为非空”,可能是由于将
javax.net.ssl.trustStore
属性设置为不存在的文件;如果无法打开文件,将创建一个空密钥存储,这将导致此错误。

对于遇到类似情况的任何人,我能够解决上述问题,如下所示:

openssl pkcs12 -in oldpkcs.p12 -out keys -passout pass:tmp
openssl pkcs12 -in keys -export -out new.p12 -passin pass:tmp -passout pass:newpasswd
  • 重新生成pkcs12文件,如下所示:

    openssl pkcs12 -in oldpkcs.p12 -out keys -passout pass:tmp
    openssl pkcs12 -in keys -export -out new.p12 -passin pass:tmp -passout pass:newpasswd
    
  • 将CA证书从服务器导入信任库(您自己的或
    $java_HOME/jre/lib/security/cacerts
    ,密码:
    changeit
    )中的java密钥库)

  • 设置以下系统属性:

    System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
    System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
    System.setProperty("javax.net.ssl.keyStore", "new_cert.p12");
    System.setProperty("javax.net.ssl.keyStorePassword", "newpass");
    
    System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
    System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
    System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
    System.setProperty("javax.net.ssl.keyStore", "new.p12");
    System.setProperty("javax.net.ssl.keyStorePassword", "newpasswd");
    
  • 测试你的url


  • 礼貌@

    这是一个仅使用p12文件的示例—虽然没有优化,但它可以工作。 我使用OpenSSL生成的pkcs12文件。 示例如何加载p12文件并从中构建信任区。。。 它从p12文件输出证书,并将好的证书添加到信任库

    KeyStore ks=KeyStore.getInstance("pkcs12");
    ks.load(new FileInputStream("client_t_c1.p12"),"c1".toCharArray());
    
    KeyStore jks=KeyStore.getInstance("JKS");
    jks.load(null);
    
    for (Enumeration<String>t=ks.aliases();t.hasMoreElements();)
    {
        String alias = t.nextElement();
        System.out.println("@:" + alias);
        if (ks.isKeyEntry(alias)){
            Certificate[] a = ks.getCertificateChain(alias);
            for (int i=0;i<a.length;i++)
            {
                X509Certificate x509 = (X509Certificate)a[i];
                System.out.println(x509.getSubjectDN().toString());
                if (i>0)
                    jks.setCertificateEntry(x509.getSubjectDN().toString(), x509);
                System.out.println(ks.getCertificateAlias(x509));
                System.out.println("ok");
            }
        }
    }
    
    System.out.println("init Stores...");
    
    KeyManagerFactory kmf=KeyManagerFactory.getInstance("SunX509");
    kmf.init(ks, "c1".toCharArray());
    
    TrustManagerFactory tmf=TrustManagerFactory.getInstance("SunX509");
    tmf.init(jks);
    
    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    
    KeyStore ks=KeyStore.getInstance(“pkcs12”);
    load(新文件输入流(“client_t_c1.p12”),“c1.tocharray());
    KeyStore jks=KeyStore.getInstance(“jks”);
    jks.load(空);
    对于(Enumerationt=ks.alias();t.hasMoreElements();)
    {
    字符串别名=t.nextElement();
    System.out.println(“@:”+别名);
    if(ks.isKeyEntry(别名)){
    证书[]a=ks.getCertificateChain(别名);
    对于(int i=0;i0)
    setCertificateEntry(x509.getSubjectDN().toString(),x509);
    系统输出打印LN(ks.getCertificateAlias(x509));
    System.out.println(“ok”);
    }
    }
    }
    System.out.println(“初始化存储…”);
    KeyManagerFactory kmf=KeyManagerFactory.getInstance(“SunX509”);
    kmf.init(ks,“c1.toCharArray());
    TrustManagerFactory tmf=TrustManagerFactory.getInstance(“SunX509”);
    tmf.init(jks);
    SSLContext ctx=SSLContext.getInstance(“TLS”);
    init(kmf.getKeyManager(),tmf.getTrustManager(),null);
    
    我意识到这篇文章可能已经过时了,但我仍然想请smithsv更正他的源代码,其中包含许多错误,我设法更正了大部分错误,但仍然不知道x509可能是什么类型的对象。以下是我认为应该是的源代码:

    import java.io.FileInputStream;
    import java.security.KeyStore;
    import java.security.cert.Certificate;
    import java.util.Enumeration;
    
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManagerFactory;
    
    public class Connection2 {
        public void connect() {
            /*
             * This is an example to use ONLY p12 file it's not optimazed but it
             * work. The pkcs12 file where generated by OpenSSL by me. Example how
             * to load p12 file and build Trust zone from it... It outputs
             * certificates from p12 file and add good certs to TrustStore
             */
            KeyStore ks = KeyStore.getInstance( "pkcs12" );
            ks.load( new FileInputStream( cert.pfx ), "passwrd".toCharArray() );
    
            KeyStore jks = KeyStore.getInstance( "JKS" );
            jks.load( null );
    
            for( Enumeration t = ks.aliases(); t.hasMoreElements(); ) {
                String alias = (String )t.nextElement();
                System.out.println( "@:" + alias );
                if( ks.isKeyEntry( alias ) ) {
                    Certificate[] a = ks.getCertificateChain( alias );
                    for( int i = 0; i == 0; )
                        jks.setCertificateEntry( x509Cert.getSubjectDN().toString(), x509 );
    
                    System.out.println( ks.getCertificateAlias( x509 ) );
                    System.out.println( "ok" );
                }
            }
    
            System.out.println( "init Stores..." );
    
            KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
            kmf.init( ks, "c1".toCharArray() );
    
            TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
            tmf.init( jks );
    
            SSLContext ctx = SSLContext.getInstance( "TLS" );
            ctx.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
        }
    }
    

    此示例显示如何在现有套接字上分层SSL,从PKCS#12文件获取客户端证书。当您需要通过代理连接到上游服务器,并且希望自己处理完整的协议时,这是合适的

    但是,基本上,一旦您拥有了SSL上下文,就可以将其应用于HttpsURLConnection等

    KeyStore ks = KeyStore.getInstance("PKCS12");
    InputStream is = ...;
    char[] ksp = storePassword.toCharArray();
    ks.load(is, ksp);
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    char[] kp = keyPassword.toCharArray();
    kmf.init(ks, kp);
    sslContext = SSLContext.getInstance("SSLv3");
    sslContext.init(kmf.getKeyManagers(), null, null);
    SSLSocketFactory factory = sslContext.getSocketFactory();
    SSLSocket sslsocket = (SSLSocket) factory.createSocket(socket, socket
        .getInetAddress().getHostName(), socket.getPort(), true);
    sslsocket.setUseClientMode(true);
    sslsocket.setSoTimeout(soTimeout);
    sslsocket.startHandshake();
    

    我不能评论,因为50分的阈值,但我不认为中提供的答案是正确的。 您实际描述的是如何将服务器证书插入系统默认信任库:

    $JAVA_HOME/jre/lib/security/cacerts, password: changeit)
    
    这确实有效,但这意味着您并没有真正指定项目本地的信任存储,而是在系统中普遍接受证书

    实际上,您从未使用您在此处定义的自己的信任库:


    以下步骤将帮助您解决问题

    步骤:
    developer_identity.cer我只设置了您指定的属性,现在我得到了以下异常:“InvalidAlogrithmException:trustAnchors参数必须为非空”我只想感谢这个答案,因为我不知道“直接使用PKCS#12文件作为信任存储应该有效”这解决了我自己的问题如果你可以发布更多的堆栈跟踪(来自堆栈帧的信息,而不仅仅是异常消息),我会查看一下;如果指定了javax.net.ssl.trustStore但不存在,则会动态创建一个空的信任存储。你的新错误消息听起来好像有可能发生这种情况。如果你问我,写一个你自己问题的答案并接受它是有点可疑的。有点像提升你自己(当然这是不可能的)@Fredrik-对我来说似乎完全合理。我想FAQ中可能有一些问题,我明白你的意思了。但是,我想知道,语句
    System.setProperty
    是否会更改默认的信任存储?您在回答中提到,由于he/sh,因此不可能在代码中使用信任存储定义