与自签名证书的TLS连接

时间:2014-12-11 10:01:47

标签: java android tomcat ssl

我知道这已经被多次询问了,我已经阅读了很多解决方案,但是我无法让它们中的任何一个工作。 所以我的主要问题是我需要使用自签名证书连接到我的服务器 - 我正在使用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)

2 个答案:

答案 0 :(得分:1)

好的,所以我终于成功了! 这就是iv完成的事情:

  1. 使用java密钥工具创建密钥库文件:

    keytool -genkey -alias tomcat -keyalg RSA
    

    在询问姓名时输入您的主机名称(因此,如果您使用tomcat和localhost输入您的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文件,输入console:

     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文件夹中(如果dosn不存在则创建它。)

  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()的问题,那是因为你在错误的地方调用它。将Context上下文添加到函数签名中并使用它而不是getApplicationContext()

答案 1 :(得分:0)

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

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

代码太长了,无法在此处发布,因此请查看几年前我就此主题撰写的博客文章:http://chariotsolutions.com/blog/post/https-with-client-certificates-on

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