クライアントとサーバーの両方の証明書認証を必要とするAndroidアプリで作業しています。通常のデスクトップJava SE 6で正常に動作するSSLClientクラスを作成しました。これをAndroidプロジェクトに移動すると、「KeyStore JKS implementation not found」というエラーが表示されます。 「。
私は少しオンラインで調べましたが、JavaキーストアがAndroidでサポートされていない可能性があるようです(すごい!)私が見つけたコードは、私がやろうとしているものに似ています。私が見つけたすべてのものは、生のSSLソケットではなくhttpクライアントを使用することについて語っています。このアプリケーションには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
{
}
}
}
}
更新:
この問題をある程度改善する。 JKSは実際にはサポートされておらず、SunX509タイプを直接選択していないこともわかりました。これらの変更を反映するために上記のコードを更新しました。キーストアとトラストストアがロードされていないようです。詳細がわかり次第更新します。
Update2:
キーストアとトラストストアのファイルを、正しいJavaの方法ではなく、デスクトップのAndroidの方法でロードしていました。ファイルはres/rawフォルダーに配置し、getResources()を使用してロードする必要があります。現在、キーストアとトラストストアのサイズについて1と1のカウントを取得しています。これは、それらがロードされていることを意味します。私はまだ例外でクラッシュしていますが、近づいています!これが機能するようになったら更新します。
Update3:
キーストアが正しく設定されていないことを除き、すべてが正常に機能しているようです。サーバーでクライアント側認証を無効にすると、問題なく接続します。有効のままにすると、handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
エラーが表示されます。そのため、証明書チェーンを正しく設定していないようです。適切な証明書チェーンを使用してBKS形式でクライアントキーストアを作成する方法を尋ねる別の質問を投稿しました: クライアント証明書チェーンを含む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();
}
}
}
更新:
BKSに変換せずに、トラストストアの.crtファイルを直接読み込むこともできます。
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;
}
クライアント証明書を使用するキーストアについても同様です。BKSに変換せずに、.p12ファイルを直接使用できます。