Java 在Android上使用客户端/服务器证书进行双向身份验证SSL套接字
我正在开发一款需要客户端和服务器证书认证的Android应用程序。我创建了一个SSLClient类,该类在常规桌面JavaSE6上运行良好。我已经将它转移到我的Android项目中,我得到了以下错误:“KeyStore JKS实现未找到” 我在网上看过一点,看起来有可能Java密钥库在Android上不受支持(太棒了!),但我觉得还有更多的原因,因为我发现的示例代码与我正在尝试做的完全不同。我发现的一切都是关于使用http客户端而不是原始SSL套接字的。我需要这个应用程序的SSL套接字 下面是我的SSLClient.java文件中的代码。它读取密钥库和信任库,创建到服务器的SSL套接字连接,然后在等待来自服务器的输入行时运行循环,然后通过调用其他类中的方法来处理它们。我非常有兴趣听到任何有在Android平台上使用SSL套接字经验的人的消息Java 在Android上使用客户端/服务器证书进行双向身份验证SSL套接字,java,android,ssl,Java,Android,Ssl,我正在开发一款需要客户端和服务器证书认证的Android应用程序。我创建了一个SSLClient类,该类在常规桌面JavaSE6上运行良好。我已经将它转移到我的Android项目中,我得到了以下错误:“KeyStore JKS实现未找到” 我在网上看过一点,看起来有可能Java密钥库在Android上不受支持(太棒了!),但我觉得还有更多的原因,因为我发现的示例代码与我正在尝试做的完全不同。我发现的一切都是关于使用http客户端而不是原始SSL套接字的。我需要这个应用程序的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类型。我已经更新了上面的代码以反映这些更改。我仍然有一个问题,显然没有加载密钥库和信任库。我会在了解更多信息后更新
更新2:
我是用桌面Java方式而不是正确的Android方式加载密钥库和信任库文件的。这些文件必须放在res/raw文件夹中,并使用getResources()加载。我现在得到了密钥库和信任库大小的计数1和1,这意味着它们正在加载。我仍然遇到了一个例外,但是越来越近了!我会在工作时更新
更新3:
看起来除了我的密钥库设置不正确外,其他一切都正常。如果我在服务器上禁用客户端身份验证,它将无问题地连接。当我将其保持启用状态时,会出现
处理异常:javax.net.ssl.SSLHandshakeException:null cert chain
错误。看来我没有正确设置证书链。我发布了另一个问题,询问如何使用正确的证书链以BKS格式创建客户机密钥库:Android支持BKS、P12和其他格式的证书
对于BKS格式:
用于将证书(.p12和.crt)转换为.bks
您的/res/raw
文件夹中需要2个文件:
truststore.bks
服务器的信任证书(从.cer文件转换)
client.bks/client.p12
-客户端证书(从包含客户端证书和客户端密钥的.p12文件转换而来)
更新: 您也可以直接为信任存储加载.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;
}
私有静态密钥库loadTrustStore(字符串[]certificateFilenames){
AssetManager=GirdersApp.getInstance().getAssets();
int length=certificateFilenames.length;
列表证书=新的ArrayList(长度);
用于(字符串certificateFilename:certificateFilenames){
输入流为;
试一试{
is=AssetManager.open(certificateFilename,AssetManager.ACCESS\u缓冲区);
Certificate=KeyStoreManager.loadX509Certificate(is);
证书。添加(证书);
}捕获(例外e){
抛出新的运行时异常(e);
}
}
证书[]certificatesArray=certificates.toArray(新证书[certificates.size()]);
返回新的generateKeystore(证书阵列);
}
/**
*生成包含指定证书的密钥库。
*
*@param证书要添加到密钥库的证书
*@返回具有指定证书的密钥库
*@如果无法生成密钥库,则抛出KeyStoreException。
*/
公共密钥库generateKeystore(证书[]证书)引发RuntimeException{
//构造空密钥库
KeyStore KeyStore=KeyStore.getInstance(keyStoreType);
//初始化密钥库
load(null,null);
//将证书加载到密钥库中
int length=证书长度;
for(int i=0;i
客户端证书的密钥库也是如此,您可以直接使用.p12文件,而无需将其转换为BKS。我确信这段代码可以在Android 1.6+上运行。它在我们的老应用程序中使用,该应用程序已经在安卓市场上运行了几年。也许我编辑了一些东西,但它应该是有效的。我不再参与这个项目,所以我个人没有确认这个答案,但我接受它,因为它包含详细的复制信息,根据投票结果,它似乎是一个有效的解决方案。谢谢。这个答案有点不完整:Android也支持其他格式的证书。我们不需要创建BKS密钥库。P12文件也可以开箱即用。
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;
}