我正在开发一款需要客户端和服务器证书身份验证的Android应用 . 我有一个SSLClient类,我创建它在常规桌面Java SE 6上运行得很漂亮 . 我已将它移动到我的Android项目中,我收到以下错误:“未找到KeyStore JKS实现” .
我在网上看了一下,看起来有可能在Android上不支持Java Keystores(太棒了!)但是我觉得它还有更多的东西,因为我发现的示例代码都不像我我试图去做 . 我发现的一切都谈到了使用http客户端而不是原始SSL套接字 . 我需要这个应用程序的SSL套接字 .
下面是我的SSLClient.java文件中的代码 . 它读取密钥库和信任库,创建到服务器的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
{
}
}
}
}
Update:
在这个问题上取得了一些进展 . 发现JKS确实不受支持,也没有直接选择SunX509类型 . 我've updated my code above to reflect these changes. I'仍然有一个问题,显然没有加载密钥库和信任库 . 随着我想出更多,我会更新 .
Update2:
我正在以桌面Java方式加载我的密钥库和信任库文件,而不是正确的Android方式 . 必须将文件放在res / raw文件夹中并使用getResources()加载 . 我'm now getting a count of 1 and 1 for the keystore and truststore size which means they'重装 . 当我开始工作时,我会更新'm still crashing on an exception, but getting closer! I' ll .
Update3:
看起来现在一切正常,但我的密钥库设置不正确 . 如果我在服务器上禁用客户端身份验证,它会毫无问题地连接 . 当我启用它时,我收到 handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
错误 . 所以看起来我发布了另一个问题,询问如何使用适当的证书链创建BKS格式的客户端密钥库:How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain
1 回答
Android支持BKS,P12和其他格式的证书 .
对于BKS格式:使用portecle将证书(.p12和.crt)转换为.bks .
您的
/res/raw
文件夹中需要2个文件:truststore.bks
服务器的信任证书(从.cer文件转换)client.bks/client.p12
- 客户端证书(从包含客户端证书和客户端密钥的.p12文件转换而来)Update:
您还可以直接为信任存储加载.crt文件,而无需将其转换为BKS:
具有客户端证书的KeyStore也是如此,您可以直接使用.p12文件而无需将其转换为BKS .