简体   繁体   English

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

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

I have a pkcs12 file. 我有一个pkcs12文件。 I need to use this to connect to a webpage using https protocol. 我需要使用它来使用https协议连接到网页。 I came across some code where in order to connect to a secure web page i need to 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_cert.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "newpass");

I have the p12(pkcs12) file. 我有p12(pkcs12)文件。 All I need is a truststore file. 我只需要一个信任库文件。

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. 请帮忙。

For anyone encountering a similar situation I was able to solve the issue above as follows: 对于遇到类似情况的任何人,我能够解决上面的问题,如下所示:

  1. Regenerate your pkcs12 file as follows: 重新生成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. 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 ). 将CA证书从服务器导入TrustStore(您自己的,或$JAVA_HOME/jre/lib/security/cacerts的java密钥库,密码: 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. 测试你的网址。

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

I cannot comment because of the 50pts threshhold, but I don't think that the answer provided in https://stackoverflow.com/a/537344/1341220 is correct. 由于50ps阈值,我无法评论,但我不认为https://stackoverflow.com/a/537344/1341220中提供的答案是正确的。 What you are actually describing is how you insert server certificates into the systems default truststore: 您实际描述的是如何将服务器证书插入系统默认信任库:

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

It appears that you are extracting you certificate from the PKCS #12 key store and creating a new Java key store (with type "JKS"). 您似乎从PKCS#12密钥库中提取证书并创建新的Java密钥库(类型为“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). 您不必严格提供信任存储密码(尽管使用一个密码可以测试根证书的完整性)。

So, try your program with only the following SSL properties set. 因此,尝试使用以下SSL属性设置的程序。 The list shown in your question is over-specified and may be causing problems. 您的问题中显示的列表是过度指定的,可能会导致问题。

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. 此外,只要将CA证书检测为“受信任”条目,就可以将PKCS#12文件直接用作信任存储。 But in that case, you'll have to specify the javax.net.ssl.trustStoreType property as "PKCS12" too. 但在这种情况下,您还必须将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.trustStore property to a file that doesn't exist; 新错误“trustAnchors参数必须为非空”可能是由于将javax.net.ssl.trustStore属性设置为不存在的文件; if the file cannot be opened, an empty key store created, which would lead to this error. 如果无法打开文件,则会创建一个空的密钥库,这会导致此错误。

This is an example to use ONLY p12 file it's not optimazed but it work. 这是一个仅使用p12文件的示例,它不是最佳的,但它可以工作。 The pkcs12 file where generated by OpenSSL by me. 由我在OpenSSL生成的pkcs12文件。 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文件并构建Trust区域...它从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);
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;
}

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. 使用此文件作为您的p12,然后尝试。 This indeed is the solution.:) 这确实是解决方案。:)

This example shows how you can layer SSL on top of an existing socket, obtaining the client cert from a PKCS#12 file. 此示例显示如何在现有套接字上层叠SSL,从PKCS#12文件获取客户端证书。 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. 当您需要通过代理连接到上游服务器并且您希望自己处理完整协议时,它是合适的。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM