gpt4 book ai didi

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

转载 作者:IT老高 更新时间:2023-10-28 20:41:32 26 4
gpt4 key购买 nike

我正在开发一个需要客户端和服务器证书身份验证的 Android 应用程序。我创建了一个 SSLClient 类,它在常规桌面 Java SE 6 上运行良好。我已将它移到我的 Android 项目中,但出现以下错误:“找不到 KeyStore JKS 实现”。

我在网上查了一下,看起来 Android 上可能不支持 Java keystore (太棒了!),但我觉得还有更多的原因,因为我没有找到任何示例代码完全类似于我正在尝试做的事情。我发现的所有内容都在谈论使用 http 客户端而不是原始 SSL 套接字。我需要此应用程序的 SSL 套接字。

以下是我的 SSLClient.java 文件中的代码。它读取 keystore 和信任库,创建到服务器的 SSL 套接字连接,然后在等待来自服务器的输入行时运行一个循环,然后通过调用不同类中的方法来处理它们。我很想听听任何有在 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,也不直接选择SunX509类型。我已经更新了上面的代码以反射(reflect)这些更改。我仍然有一个问题,显然没有加载 keystore 和信任库。等我发现更多,我会更新。


更新2:
我正在以桌面 Java 方式而不是正确的 Android 方式加载我的 keystore 和信任库文件。这些文件必须放在 res/raw 文件夹中并使用 getResources() 加载。我现在得到的 keystore 和信任库大小分别为 1 和 1,这意味着它们正在加载。我仍然因异常而崩溃,但越来越近了!当我得到这个工作时,我会更新。


更新3:
看起来现在一切正常,除了我的 keystore 设置不正确。如果我在服务器上禁用客户端身份验证,它会毫无问题地连接。当我启用它时,我得到一个 handling exception: javax.net.ssl.SSLHandshakeException: null cert chain 错误。所以看起来我没有正确设置证书链。我发布了另一个问题,询问如何使用正确的证书链以 BKS 格式创建客户端 keystore :How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain

最佳答案

Android 支持 BKS、P12 和其他格式的证书。

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

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

client.bks/client.p12 - 客户端证书(从包含客户端证书和客户端 key 的 .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。

关于java - 在 Android 上使用客户端/服务器证书进行双向身份验证 SSL 套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4064810/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com