在Android上使用客户端/服务器证书进行双向身份validationSSL套接字

我正在开发一个需要客户端和服务器证书authentication的Android应用程序。 我有一个SSLClient类,我创build了在常规桌面Java SE 6上的漂亮的作品。我已经将它移到我的Android项目中,并且出现以下错误:“KeyStore JKS实现未find”。

我在网上看了一下,它看起来有可能Java密钥库不支持Android(真棒!),但我有一种感觉还有更多的比它,因为我没有发现样本代码类似于我一点都不想做。 我发现的一切都是关于使用http客户端而不是原始SSL套接字。 我需要这个应用程序的SSL套接字。

以下是我的SSLClient.java文件中的代码。 它读取密钥库和信任库,创build一个到服务器的SSL套接字连接,然后在等待来自服务器的input行时运行一个循环,然后通过调用另一个类中的方法来处理它们。 我非常有兴趣听到任何有经验的人在Android平台上做SSL套接字。

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确实不被支持,也没有直接selectSunX509types。 我已经更新了我的代码以反映这些更改。 我仍然有一个问题,显然没有加载密钥库和信任库。 我会更新,因为我知道更多。


UPDATE2:
我正在用桌面Java方式加载我的密钥库和信任库文件,而不是正确的Android方式。 这些文件必须放在res / raw文件夹中,并使用getResources()加载。 我现在得到1和1的密钥库和信任库大小,这意味着他们正在加载计数。 我仍然崩溃的例外,但越来越接近! 我会更新,当我得到这个工作。


UPDATE3:
看起来像一切正在工作,除了我的密钥库设置不正确。 如果我禁用服务器上的客户端身份validation,它连接没有问题。 当我离开它启用,我得到一个handling exception: javax.net.ssl.SSLHandshakeException: null cert chain错误。 所以看起来我没有正确设置证书链。 我已经发布了另一个问题,询问如何使用正确的证书链以BKS格式创build客户机密钥库: 如何创build包含客户机证书链的BKS(BouncyCastle)格式的Java密钥库

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

对于BKS格式:使用portecle将证书(.p12和.crt)转换为.bks。

/res/raw文件夹中需要2个文件:服务器的truststore.bks信任证书(从.cer文件转换而来)

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

 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:

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

KeyStore与客户端证书一样,您可以直接使用.p12文件,而不必将其转换为BKS。