简体   繁体   English

带有自签名证书的TLS连接

[英]TLS connection with self signed certificate

I know this has been asked many times and I've read a lot of solutions, but I'm having trouble making any of them to work. 我知道这个问题已经被问了很多遍了,我已经阅读了很多解决方案,但是我很难使它们中的任何一个都能起作用。 So my main problem is that I need to use a self signed certificate to connect to my server - I'm using tomcat and I already configured it to work with the JKS (I generated a .pem files with openssl and transformed them into JKS as explained here: Tomcat HTTPS keystore certificate ) From my browser everything works fine, but now I need my app to connect via ssl. 所以我的主要问题是我需要使用自签名证书连接到服务器-我正在使用tomcat,并且已经将其配置为与JKS一起使用(我使用openssl生成了.pem文件,并将它们转换为JKS在这里解释: Tomcat HTTPS keystore certificate )从我的浏览器中,一切正常,但是现在我需要我的应用程序通过ssl连接。 When I couldn't make anything work I tried to allow all certificates like this: 当我无法进行任何工作时,我尝试允许所有这样的证书:

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

} }

And I get this error: 我得到这个错误:

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

with http connection it works : 与HTTP连接有效:

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

My questions are: 我的问题是:

  1. how can I make it work by accepting all certificates? 如何通过接受所有证书使它起作用?
  2. how do I define it to accept only my cert? 如何定义它仅接受我的证书? (and that it will match the JKS I have in my tomcat server) (并且它将与我在tomcat服务器中拥有的JKS相匹配)

Ok so I finally managed to make it work! 好的,所以我终于设法使它起作用了! This is what iv'e done: 这就是我所做的:

  1. Create keystore file with the java key tool: 使用Java密钥工具创建密钥库文件:

     keytool -genkey -alias tomcat -keyalg RSA 

    type your host name when asked for first and last name(so if you use tomcat with localhost enter your ip). 在要求输入名字和姓氏时键入主机名(因此,如果您将tomcat与localhost一起使用,请输入您的ip)。 this will create .keystore file in your home dir. 这将在您的主目录中创建.keystore文件。

  2. Move the .ketstore file to %CATALINA_HOME%/conf and add to server.xml file the following: 将.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. Now for the client app you'll have to use bouncy castle because android has a built-in support for the .bks file it creates. 现在,对于客户端应用程序,您将必须使用充气城堡,因为android对其创建的.bks文件具有内置支持。 It can be downloaded from here: http://www.bouncycastle.org/latest_releases.html you'll also need openssl to create the certificate bouncy castle needs. 可以从这里下载: http : //www.bouncycastle.org/latest_releases.html您还将需要openssl来创建证书弹力城堡所需的证书。 open console and type : 打开控制台并输入:

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

    when done open the cert.pem file and delete everything before BEGIN CERTIFICATE and everything after END CERTIFICATE. 完成后,打开cert.pem文件并删除BEGIN CERTIFICATE之前的所有内容和END CERTIFICATE之后的所有内容。

  4. to create to .bks file, type in console : 要创建为.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* 

    put the .bks file in your appproject\\res\\raw folder (create it if dosn't exist). 将.bks文件放入您的appproject \\ res \\ raw文件夹中(如果不存在则创建它)。

  5. this is a basic app that creates a connection via https(credit to Vipul from here: How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain ): 这是一个基本的应用程序,它通过https(从此处贷至Vipul:从此处创建链接如何创建包含客户端证书链的BKS(BouncyCastle)格式Java密钥库 ):

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

    and in your main activity create new thread (network operations cannot be done on the main thread)that it's run method does: 并在您的主活动中创建一个新线程(无法在主线程上执行网络操作),该线程的run方法可以:

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

    and start it. 并开始。 if you encounter problems with getApplicationContext() it's because you are calling it in the wrong place. 如果您在使用getApplicationContext()时遇到问题,那是因为您在错误的地方调用了它。 add Context context to your function signature and use it instead of getApplicationContext() 将Context上下文添加到您的函数签名中,并使用它代替getApplicationContext()

Blindly trusting all certificates is not a good idea. 盲目相信所有证书不是一个好主意。

To trust your own self-signed cert, you need to have the server's certificate in the android application's trust store. 要信任自己的自签名证书,您需要在android应用程序的信任存储区中拥有服务器的证书。

The code is too long to post here, so take a look at the blog post I wrote on this subject a couple of years ago: http://chariotsolutions.com/blog/post/https-with-client-certificates-on 该代码太长,无法在此处发布,因此请看一下几年前我就此主题撰写的博客文章: http : //chariotsolutions.com/blog/post/https-with-client-certificates-on

and the source code at: https://github.com/rfreedman/android-ssl 以及源代码: https : //github.com/rfreedman/android-ssl

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

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