【问题标题】:TLS connection with self signed certificate带有自签名证书的 TLS 连接
【发布时间】:2015-02-09 18:27:18
【问题描述】:

我知道这个问题已经被问过很多次,并且我已经阅读了很多解决方案,但是我无法让它们中的任何一个起作用。 所以我的主要问题是我需要使用自签名证书连接到我的服务器 - 我正在使用 tomcat 并且我已经将它配置为与 JKS 一起使用(我使用 openssl 生成了一个 .pem 文件并将它们转换为 JKS 作为在这里解释:Tomcat HTTPS keystore certificate) 从我的浏览器一切正常,但现在我需要我的应用程序通过 ssl 连接。 当我无法进行任何工作时,我尝试允许所有这样的证书:

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import javax.net.ssl.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    new Thread() {
        public void run() {
            try {
                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
                trustStore.load(null, null);

                SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
                sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

                HttpParams params = new BasicHttpParams();
                HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
                HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

                SchemeRegistry registry = new SchemeRegistry();
                registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
                registry.register(new Scheme("https", sf, 443));

                ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);


                HttpGet httpGet = new HttpGet("https://MY_IP:8443");

                HttpClient client = new DefaultHttpClient(ccm, params);
                HttpResponse res = client.execute(httpGet);
                Log.i("client", res.getStatusLine().toString());


            } catch (Exception e) {
                Log.e("client", "problem with connection");
            }
        }
    }.start();
}

public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };

        sslContext.init(null, new TrustManager[]{tm}, null);
    }
}

}

我得到这个错误:

Catch exception while startHandshake: javax.net.ssl.SSLHandshakeException:     java.security.cert.CertPathValidatorException: Trust anchor for certification path not

return an invalid session with invalid cipher suite of SSL_NULL_WITH_NULL_NULL

使用 http 连接它可以工作:

HttpGet httpGet = new HttpGet("http://MY_IP:8080");

我的问题是:

  1. 如何通过接受所有证书使其工作?
  2. 如何定义它只接受我的证书? (并且它将与我的 tomcat 服务器中的 JKS 匹配)

【问题讨论】:

  • 我认为这是个坏主意:ALLOW_ALL_HOSTNAME_VERIFIER.

标签: java android tomcat ssl


【解决方案1】:

好吧,我终于成功了! 这就是我所做的:

  1. 使用 java key 工具创建 keystore 文件:

    keytool -genkey -alias tomcat -keyalg RSA
    

    在要求输入名字和姓氏时输入您的 主机 名称(因此,如果您使用带有 localhost 的 tomcat,请输入您的 ip)。这将在您的主目录中创建 .keystore 文件。

  2. 将 .ketstore 文件移动到 %CATALINA_HOME%/conf 并在 server.xml 文件中添加以下内容:

     <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
      SSLEnabled="true" maxThreads="150" scheme="https" secure="true"
      clientAuth="false" sslProtocol="TLS" keystoreFile="*%CATALINA_HOME%*\conf\.keystore" 
      keystorePass="*yourpass*"/>
    
  3. 现在对于客户端应用程序,您将不得不使用充气城堡,因为 android 内置了对它创建的 .bks 文件的支持。它可以从这里下载: http://www.bouncycastle.org/latest_releases.html 您还需要 openssl 来创建需要的充气城堡证书。 打开控制台并输入:

    openssl s_client -connect *yourhostname*:8443/>cert.pem
    

    完成后打开 cert.pem 文件并删除 BEGIN CERTIFICATE 之前的所有内容和 END CERTIFICATE 之后的所有内容。

  4. 要创建 .bks 文件,请输入控制台:

     keytool -import -alias tomcat -file *pathtToCertificate*\cert.pem -keypass *yourPassword* -keystore *pathtToSaveBks*\*nameOfYourKey*.bks -storetype BKS -storepass *yourPassword* -providerClass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath *fullPathToTheBouncyCastleJar*
    

    将 .bks 文件放入您的 appproject\res\raw 文件夹(如果不存在则创建它)。

  5. 这是一个通过 https 创建连接的基本应用程序(来自这里的 Vipul:How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain):

    import java.io.InputStream;
    import java.security.KeyStore;
    import org.apache.http.conn.ClientConnectionManager;
    import org.apache.http.conn.scheme.PlainSocketFactory;
    import org.apache.http.conn.scheme.Scheme;
    import org.apache.http.conn.scheme.SchemeRegistry;
    import org.apache.http.conn.ssl.SSLSocketFactory;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.impl.conn.SingleClientConnManager;
    import com.arisglobal.aglite.activity.R;
    import android.content.Context;
    
    public class MyHttpClient extends DefaultHttpClient{
    final Context context;
    
    public MyHttpClient(Context context) {
        this.context = context;
    }
    
    @Override
    protected ClientConnectionManager createClientConnectionManager() {
        SchemeRegistry registry = new SchemeRegistry();
    
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
    
        // Register for port 443 our SSLSocketFactory with our keystore to the ConnectionManager
        registry.register(new Scheme("https", newSslSocketFactory(), 443));
        return new SingleClientConnManager(getParams(), registry);
    }
    
    private SSLSocketFactory newSslSocketFactory() {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");
    
            // Get the raw resource, which contains the keystore with your trusted certificates (root and any intermediate certs)
            InputStream in = context.getResources().openRawResource(R.raw.*nameOfYourKey*);
            try {
                // Initialize the keystore with the provided trusted certificates.
                // Also provide the password of the keystore
                trusted.load(in, *yourPassword*.toCharArray());
            } finally {
                in.close();
            }
    
            // Pass the keystore to the SSLSocketFactory. The factory is responsible for the verification of the server certificate.
            SSLSocketFactory sf = new SSLSocketFactory(trusted);
    
            // Hostname verification from certificate
            // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }
    }
    

    并在你的主活动中创建新线程(网络操作不能在主线程上完成)它的运行方法:

    DefaultHttpClient client = new MyHttpClient(getApplicationContext());
    HttpResponse response = client.execute(https://*yourIP|HostName:8443);
    

    并启动它。如果你遇到 getApplicationContext() 的问题,那是因为你在错误的地方调用它。将上下文上下文添加到您的函数签名并使用它而不是 getApplicationContext()

【讨论】:

    【解决方案2】:

    盲目信任所有证书不是一个好主意。

    要信任您自己的自签名证书,您需要在 android 应用程序的信任库中拥有服务器的证书。

    这里的代码太长了,所以看看我几年前写的关于这个主题的博客文章:http://chariotsolutions.com/blog/post/https-with-client-certificates-on

    源代码在:https://github.com/rfreedman/android-ssl

    【讨论】:

      猜你喜欢
      • 2014-05-05
      • 2012-08-14
      • 2016-09-01
      • 2019-04-26
      • 1970-01-01
      • 2018-12-23
      • 1970-01-01
      • 2012-01-14
      • 2011-08-25
      相关资源
      最近更新 更多