使用客户端/服务器证书在Android双向认证的SSL套接字 [英] Using client/server certificates for two way authentication SSL socket on Android

查看:247
本文介绍了使用客户端/服务器证书在Android双向认证的SSL套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我工作的一个Android应用程序,要求客户端和服务器证书验证。我有我创建了一个精美的作品经常桌面Java SE 6,我感动到我的Andr​​oid项目,我发现了以下错误上SSLClient类:找不到密钥库JKS实施

I'm working on an Android app that requires both client and server certificate authentication. I have an SSLClient class that I created that works beautifully on regular desktop Java SE 6. I've moved it into my Android project and I'm getting the following error: "KeyStore JKS implementation not found".

我在网上查了一下,它看起来像有这么的Java密钥库不支持在Android(真棒!)一个可能性,但我有一种感觉,还有更给它比,因为没有一个样品code我的已经找到类似于我想要做的。一切我发现关于使用HTTP客户端,而不是原始的SSL套接字会谈。我需要SSL套接字此应用程序。

I've looked online a bit and it looks like there's a possibility that Java Keystores are not supported on Android (awesome!) but I have a feeling there's more to it than that because none of the sample code I've found resembles what I'm trying to do at all. Everything I found talks about using an http client rather than raw SSL sockets. I need SSL sockets for this application.

下面是code。在我的SSLClient.java文件。它读取密钥库和信任,创建一个SSL套接字连接到服务器,然后运行一个循环,而等待输入线从服务器,然后处理他们,因为他们进来通过调用不同的类中的方法。我很感兴趣的任何人与任何Android平台上做SSL套接字经验听到。

Below is the code in my SSLClient.java file. It reads the keystore and truststore, creates an SSL socket connection to the server, then runs a loop while waiting for input lines from the server then handles them as they come in by calling a method in a different class. I'm very interested to hear from anyone with any experience doing SSL sockets on the Android platform.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.AccessControlException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import otherpackege.OtherClass;

import android.content.Context;
import android.util.Log;

public class SSLClient 
{
    static SSLContext ssl_ctx;

    public SSLClient(Context context)
    {
        try
        {
            // Setup truststore
            KeyStore trustStore = KeyStore.getInstance("BKS");
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            InputStream trustStoreStream = context.getResources().openRawResource(R.raw.mysrvtruststore);
            trustStore.load(trustStoreStream, "testtest".toCharArray());
            trustManagerFactory.init(trustStore);

            // Setup keystore
            KeyStore keyStore = KeyStore.getInstance("BKS");
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            InputStream keyStoreStream = context.getResources().openRawResource(R.raw.clientkeystore);
keyStore.load(keyStoreStream, "testtest".toCharArray());
            keyManagerFactory.init(keyStore, "testtest".toCharArray());

            Log.d("SSL", "Key " + keyStore.size());
            Log.d("SSL", "Trust " + trustStore.size());

            // Setup the SSL context to use the truststore and keystore
            ssl_ctx = SSLContext.getInstance("TLS");
            ssl_ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);

            Log.d("SSL", "keyManagerFactory " + keyManagerFactory.getKeyManagers().length);
            Log.d("SSL", "trustManagerFactory " + trustManagerFactory.getTrustManagers().length);
        }
        catch (NoSuchAlgorithmException nsae)
        {
            Log.d("SSL", nsae.getMessage());
        }
        catch (KeyStoreException kse)
        {
            Log.d("SSL", kse.getMessage());
        }
        catch (IOException ioe)
        {
            Log.d("SSL", ioe.getMessage());
        }
        catch (CertificateException ce)
        {
            Log.d("SSL", ce.getMessage());
        }
        catch (KeyManagementException kme)
        {
            Log.d("SSL", kme.getMessage());
        }
        catch(AccessControlException ace)
        {
            Log.d("SSL", ace.getMessage());
        }
        catch(UnrecoverableKeyException uke)
        {
            Log.d("SSL", uke.getMessage());
        }

        try
        {
            Handler handler = new Handler();
            handler.start();
        }
        catch (IOException ioException) 
        {
            ioException.printStackTrace();
        }
     }  
}

//class Handler implements Runnable 
class Handler extends Thread
{
    private SSLSocket socket;
    private BufferedReader input;
    static public PrintWriter output;

    private String serverUrl = "174.61.103.206";
    private String serverPort = "6000";

    Handler(SSLSocket socket) throws IOException
    {

    }
    Handler() throws IOException
    {

    }

    public void sendMessagameInfoge(String message)
    {
        Handler.output.println(message);
    }

    @Override
    public void run() 
    {
        String line;

        try 
        {
            SSLSocketFactory socketFactory = (SSLSocketFactory) SSLClient.ssl_ctx.getSocketFactory();
            socket = (SSLSocket) socketFactory.createSocket(serverUrl, Integer.parseInt(serverPort));
            this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            Handler.output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
            Log.d("SSL", "Created the socket, input, and output!!");

            do
            {
                line = input.readLine();
                while (line == null)
                {
                    line = input.readLine();
                }

                // Parse the message and do something with it
                // Done in a different class
                OtherClass.parseMessageString(line);
            }
            while ( !line.equals("exit|") );
        }
        catch (IOException ioe)
        {
            System.out.println(ioe);
        }
        finally 
        {
            try 
            {
                input.close();
                output.close();
                socket.close();
            } 
            catch(IOException ioe) 
            {
            } 
            finally 
            {

            }
        }
    }
}


更新:
使在这个问题上的一些良好的进展。发现了JKS的确不支持,也不是直接选择SunX509类型。我已经更新了我的code以上,以反映这些变化。我仍然有一个问题,它显然不加载密钥库和信任。我会更新我弄清楚了。


Update:
Making some good progress on this problem. Found out that JKS is indeed not supported, neither is directly choosing the SunX509 type. I've updated my code above to reflect these changes. I'm still having an issue with it apparently not loading the keystore and truststore. I'll update as I figure out more.

UPDATE2:
我在做我的密钥库和信任文件加载在桌面Java的方式,而不是正确的Andr​​oid方式。该文件必须使用getResources放在在res /原始文件夹和加载()。我现在得到的1和1计数的密钥库和信任的大小,这意味着他们正在加载。我仍然崩溃的异常,但是越来越近了!我会更新,当我得到这个工作。

Update2:
I was doing my keystore and truststore file loading in a desktop Java way rather than the correct Android way. The files must be put in the res/raw folder and loaded using getResources(). I'm now getting a count of 1 and 1 for the keystore and truststore size which means they're loading. I'm still crashing on an exception, but getting closer! I'll update when I get this working.

UPDATE3:
看起来一切正常,现在除了我的密钥库被设置不正确。如果我禁用服务器上的客户端身份验证,它连接没有问题。当我离开它使我获得了处理异常:javax.net.ssl​​.SSLHandshakeException:空证书链错误。因此,它看起来像我没有正确地设置证书链。我已经发布了另外一个问题,询问如何在BKS格式正确的证书链创建一个客户端密钥库:<一href="http://stackoverflow.com/questions/4065379/how-to-create-a-bks-bouncycastle-format-java-keystore-that-contains-a-client-ce">How创建一个BKS(BouncyCastle的)格式的Java密钥库,其中包含客户端证书链

Update3:
Looks like everything is working now with the exception of my keystore being set up incorrectly. If I disable client side authentication on the server, it connects without issue. When I leave it enabled, I get a handling exception: javax.net.ssl.SSLHandshakeException: null cert chain error. So it looks like I'm not setting up the certificate chain correctly. I've posted another question asking how to create a client keystore in the BKS format with the proper certificate chain: How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain

推荐答案

Android支持的BKS,P12等格式证书。

Android supports certificates in the BKS, P12 and other formats.

有关BKS格式: 使用 portecle 您的证书(或.p12和.CRT)转换为.bks。

For BKS format: Use portecle to convert your certificates (.p12 and .crt) to .bks.

您需要在您的 / RES /生文件夹2个文件: truststore.bks 服务器(从.cer文件转换)信任证书

You need 2 files in your /res/raw folder: truststore.bks trust certificate for the server (converted from .cer file)

client.bks / client.p12 - 客户端证书(从.p12文件包含客户端证书和客户端密钥转换)

client.bks/client.p12 - the client certificate (converted from a .p12 file that contains the client certificate and the client key)

import java.io.*;
import java.security.KeyStore;

import javax.net.ssl.*;

import org.apache.http.*;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.*;
import org.apache.http.conn.scheme.*;
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.*;

import android.app.Activity;
import android.os.Bundle;

public class SslTestActivity extends Activity {

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    try {
      // setup truststore to provide trust for the server certificate

      // load truststore certificate
      InputStream clientTruststoreIs = getResources().openRawResource(R.raw.truststore);
      KeyStore trustStore = null;
      trustStore = KeyStore.getInstance("BKS");
      trustStore.load(clientTruststoreIs, "MyPassword".toCharArray());

      System.out.println("Loaded server certificates: " + trustStore.size());

      // initialize trust manager factory with the read truststore
      TrustManagerFactory trustManagerFactory = null;
      trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init(trustStore);

      // setup client certificate

      // load client certificate
      InputStream keyStoreStream = getResources().openRawResource(R.raw.client);
      KeyStore keyStore = null;
      keyStore = KeyStore.getInstance("BKS");
      keyStore.load(keyStoreStream, "MyPassword".toCharArray());

      System.out.println("Loaded client certificates: " + keyStore.size());

      // initialize key manager factory with the read client certificate
      KeyManagerFactory keyManagerFactory = null;
      keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
      keyManagerFactory.init(keyStore, "MyPassword".toCharArray());


      // initialize SSLSocketFactory to use the certificates
      SSLSocketFactory socketFactory = null;
      socketFactory = new SSLSocketFactory(SSLSocketFactory.TLS, keyStore, "MyTestPassword2010",
          trustStore, null, null);

      // Set basic data
      HttpParams params = new BasicHttpParams();
      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
      HttpProtocolParams.setContentCharset(params, "UTF-8");
      HttpProtocolParams.setUseExpectContinue(params, true);
      HttpProtocolParams.setUserAgent(params, "Android app/1.0.0");

      // Make pool
      ConnPerRoute connPerRoute = new ConnPerRouteBean(12);
      ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
      ConnManagerParams.setMaxTotalConnections(params, 20);

      // Set timeout
      HttpConnectionParams.setStaleCheckingEnabled(params, false);
      HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
      HttpConnectionParams.setSoTimeout(params, 20 * 1000);
      HttpConnectionParams.setSocketBufferSize(params, 8192);

      // Some client params
      HttpClientParams.setRedirecting(params, false);

      // Register http/s shemas!
      SchemeRegistry schReg = new SchemeRegistry();
      schReg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
      schReg.register(new Scheme("https", socketFactory, 443));
      ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params, schReg);
      DefaultHttpClient sClient = new DefaultHttpClient(conMgr, params);

      HttpGet httpGet = new HttpGet("https://server/path/service.wsdl");
      HttpResponse response = sClient.execute(httpGet);
      HttpEntity httpEntity = response.getEntity();

      InputStream is = httpEntity.getContent();
      BufferedReader read = new BufferedReader(new InputStreamReader(is));
      String query = null;
      while ((query = read.readLine()) != null)
        System.out.println(query);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}


更新:

您也可以载入.CRT的信任存储文件的情况下直接将其转换为BKS:

You can also load .crt files for the trust store directly without converting them to BKS:

    private static KeyStore loadTrustStore(String[] certificateFilenames) {
        AssetManager assetsManager = GirdersApp.getInstance().getAssets();

        int length = certificateFilenames.length;
        List<Certificate> certificates = new ArrayList<Certificate>(length);
        for (String certificateFilename : certificateFilenames) {
          InputStream is;
          try {
            is = assetsManager.open(certificateFilename, AssetManager.ACCESS_BUFFER);
            Certificate certificate = KeyStoreManager.loadX509Certificate(is);
            certificates.add(certificate);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }
        }

        Certificate[] certificatesArray = certificates.toArray(new Certificate[certificates.size()]);
          return new generateKeystore(certificatesArray);
      }

 /**
   * Generates keystore congaing the specified certificates.
   *
   * @param certificates certificates to add in keystore
   * @return keystore with the specified certificates
   * @throws KeyStoreException if keystore can not be generated.
   */
  public KeyStore generateKeystore(Certificate[] certificates) throws RuntimeException {
      // construct empty keystore
      KeyStore keyStore = KeyStore.getInstance(keyStoreType);

      // initialize keystore
      keyStore.load(null, null);

      // load certificates into keystore
      int length = certificates.length;
      for (int i = 0; i < length; i++) {
        Certificate certificate = certificates[i];
        keyStore.setEntry(String.valueOf(i), new KeyStore.TrustedCertificateEntry(certificate),
            null);
      }
      return keyStore;
  }

也是一样的与客户端证书的密钥库,你可以直接使用中.P12文件,而无需将其转换为BKS。

Same goes for the KeyStore with the client certificate, you can use the .p12 file directly without converting it to BKS.

这篇关于使用客户端/服务器证书在Android双向认证的SSL套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆