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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/537040/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:57:23  来源:igfitidea点击:

How to connect to a secure website using SSL in Java with a pkcs12 file?

javassl

提问by user27221

I have a pkcs12 file. I need to use this to connect to a webpage using https protocol. I came across some code where in order to connect to a secure web page i need to set the following system properties:

我有一个 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");

I have the p12(pkcs12) file. All I need is a truststore file.

我有 p12(pkcs12) 文件。我只需要一个信任库文件。

I extracted the certificates using:

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

openssl.exe pkcs12 -in c:/mykey.p12 -out c:/cert.txt -nokeys -clcerts

Now converted the cert PEM file to der

现在将证书 PEM 文件转换为 der

openssl.exe x509 -in c:/cert.txt -outform DER -out c:/CAcert.der 

Now adding the der file to a keystore

现在将 der 文件添加到密钥库

keytool -import -file C:/Cacert.der -keystore mytruststore

Now I have the truststore, but when I use it, I get the following error

现在我有了信任库,但是当我使用它时,出现以下错误

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)

Update: After removing certain properties and setting only the "trustStore", "trustStorePassword" and "trustStoreType" property, I got the following exception

更新:删除某些属性并仅设置“trustStore”、“trustStorePassword”和“trustStoreType”属性后,出现以下异常

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

Please Help.

请帮忙。

采纳答案by user27221

For anyone encountering a similar situation I was able to solve the issue above as follows:

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

  1. Regenerate your pkcs12 file as follows:

    openssl pkcs12 -in oldpkcs.p12 -out keys -passout pass:tmp
    openssl pkcs12 -in keys -export -out new.p12 -passin pass:tmp -passout pass:newpasswd
    
  2. Import the CA certificate from server into a TrustStore ( either your own, or the java keystore in $JAVA_HOME/jre/lib/security/cacerts, password: changeit).

  3. Set the following system properties:

    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");
    
  4. Test ur url.

  1. 重新生成您的 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
    
  2. 将 CA 证书从服务器导入 TrustStore(您自己的或 java 密钥库中的$JAVA_HOME/jre/lib/security/cacerts密码:)changeit

  3. 设置以下系统属性:

    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");
    
  4. 测试你的网址。

Courtesy@ http://forums.sun.com/thread.jspa?threadID=5296333

礼貌@ http://forums.sun.com/thread.jspa?threadID=5296333

回答by erickson

It appears that you are extracting you certificate from the PKCS #12 key store and creating a new Java key store (with type "JKS"). You don't strictly have to provide a trust store password (although using one allows you to test the integrity of your root certificates).

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

So, try your program with onlythe following SSL properties set. The list shown in your question is over-specified and may be causing problems.

因此,请使用以下 SSL 属性集来尝试您的程序。您的问题中显示的列表过多,可能会导致问题。

System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

Also, using the PKCS #12 file directly as the trust store should work, as long as the CA certificate is detected as a "trusted" entry. But in that case, you'll have to specify the javax.net.ssl.trustStoreTypeproperty as "PKCS12"too.

此外,只要将 CA 证书检测为“受信任”条目,直接使用 PKCS #12 文件作为信任库就应该可以工作。但在这种情况下,您也必须指定该javax.net.ssl.trustStoreType属性"PKCS12"

Try with these properties only. If you get the same error, I suspect your problem is not the key store. If it still occurs, post more of the stack trace in your question to narrow the problem down.

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



The new error, "the trustAnchors parameter must be non-empty," could be due to setting the javax.net.ssl.trustStoreproperty to a file that doesn't exist; if the file cannot be opened, an empty key store created, which would lead to this error.

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

回答by user122678

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

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

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);

回答by Roquen

I realise that this article may be outdated but still I would like to ask smithsv to correct his source code, it contains many mistakes, I managed to correct most of them but still don't know what kind of object x509 could be.Here is the source code as I think is should be:

我意识到这篇文章可能已经过时了,但我还是想请 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 );
    }
}

回答by Rogan Dawes

This example shows how you can layer SSL on top of an existing socket, obtaining the client cert from a PKCS#12 file. It is appropriate when you need to connect to an upstream server via a proxy, and you want to handle the full protocol by yourself.

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

Essentially, however, once you have the SSL Context, you can apply it to an HttpsURLConnection, etc, etc.

但是,本质上,一旦您拥有 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();

回答by Steffi

I cannot comment because of the 50pts threshhold, but I don't think that the answer provided in https://stackoverflow.com/a/537344/1341220is correct. What you are actually describing is how you insert server certificates into the systems default truststore:

由于 50pts 阈值,我无法发表评论,但我认为https://stackoverflow.com/a/537344/1341220 中提供的答案不正确。您实际描述的是如何将服务器证书插入系统默认信任库:

$JAVA_HOME/jre/lib/security/cacerts, password: changeit)

This works, indeed, but it means that you did not really specify a trust store local to your project, but rather accepted the certificate universially in your system.

这确实有效,但这意味着您并没有真正指定项目本地的信任存储,而是在您的系统中普遍接受了证书。

You actually never use your own truststore that you defined here:

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

System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");

回答by Mohamad Faisal

URL url = new URL("https://test.domain:443");   
String  keyStore = "server.p12"
String   keyStorePassword = "changeit";    
String  keyPassword = "changeit";    
String   KeyStoreType= "PKCS12";    
String   KeyManagerAlgorithm = "SunX509";    
String   SSLVersion = "SSLv3";    
public HttpURLConnection getHttpsURLConnection(URL url, String  keystore,
    String   keyStorePass,String  keyPassword, String  KeyStoreType
    ,String KeyManagerAlgorithm, String  SSLVersion)
    throws NoSuchAlgorithmException, KeyStoreException,
        CertificateException, FileNotFoundException, IOException,
        UnrecoverableKeyException, KeyManagementException {
    System.setProperty("javax.net.debug","ssl,handshake,record");

    SSLContext sslcontext = SSLContext.getInstance(SSLVersion);
    KeyManagerFactory kmf =  KeyManagerFactory.getInstance(KeyManagerAlgorithm);
    KeyStore ks = KeyStore.getInstance(KeyStoreType);
    ks.load(new FileInputStream(keystore), keyStorePass.toCharArray());
    kmf.init(ks, keyPassword.toCharArray());

     TrustManagerFactory tmf = TrustManagerFactory
            .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(ks);
    TrustManager[] tm = tmf.getTrustManagers();

    sslcontext.init(kmf.getKeyManagers(), tm, null);
    SSLSocketFactory sslSocketFactory = sslcontext.getSocketFactory();
    HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
    HttpsURLConnection httpsURLConnection = ( HttpsURLConnection)uRL.openConnection();

    return httpsURLConnection;
}

回答by Apurv Nerlekar

The following steps will help you to sort your problem out.

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

Steps: developer_identity.cer <= download from Apple mykey.p12 <= Your private key

步骤: developer_identity.cer <= 从 Apple 下载 mykey.p12 <= 您的私钥

Commands to follow:

要遵循的命令:

    openssl x509 -in developer_identity.cer -inform DER -out developer_identity.pem -outform PEM

    openssl pkcs12 -nocerts -in mykey.p12 -out mykey.pem

    openssl pkcs12 -export -inkey mykey.pem -in developer_identity.pem -out iphone_dev.p12

Final p12 that we will require is iphone_dev.p12 file and the passphrase.

我们需要的最终 p12 是 iphone_dev.p12 文件和密码。

use this file as your p12 and then try. This indeed is the solution.:)

将此文件用作您的 p12,然后尝试。这确实是解决方案。:)