Sockets と Java 7 を使用した SSL を介した簡単な通信について質問があります。
ここまでで、サーバー用とクライアント用の2 つのRunnablesを作成しました。最終的には、アプリケーションが SSL を使用してモックアップ サーバーにデータを送信する簡単なテストを作成したいと考えています。サーバーが行うことになっているのは、クライアントから送信された文字列を受信し、それをファイルの内容と比較し、比較が成功したかどうかに応じて応答文字列を返信することだけです。
ただし、明示的にクライアントを認証したくないのですが、 javax.net.ssl.SSLHandshakeExceptionで立ち往生しています! クライアント側では、証明書を検証しないトラスト マネージャーを作成します。サーバー側では、this.sslServerSocket.setWantClientAuth(false)および/またはthis.sslServerSocket.setNeedClientAuth(false)を設定しても役に立ちません。テスト目的で、クライアントとサーバーの両方が「keystore.ks」と呼ばれる同じキー ストア ファイルを使用します。
例外が発生するポイントを下に矢印 (----->) でマークしました。誰かが私に何が悪いのかヒントをくれるかもしれません!
これは私の SSLServer クラスです:
public class SSLServer implements Runnable
{
private SSLServerSocketFactory sslServerSocketFactory;
private SSLServerSocket sslServerSocket;
private SSLSocket sslSocket;
private KeyStore keystore;
private KeyManager[] keyManagers;
private SSLContext sslContext;
private SSLSession sslSession;
private int port = 8081;
private static final Character EOL = '\n';
private static final Character EOF = '\u0017';
public SSLServer() throws IOException, NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, UnrecoverableKeyException
{
char[] passphrase = "changeit".toCharArray();
this.keystore = KeyStore.getInstance(KeyStore.getDefaultType());
this.keystore.load(new FileInputStream("keystore.ks"), passphrase);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(this.keystore, passphrase);
this.sslContext = SSLContext.getInstance("TLS");
this.keyManagers = keyManagerFactory.getKeyManagers();
this.sslContext.init(this.keyManagers, null, null);
this.sslServerSocketFactory = this.sslContext.getServerSocketFactory();
this.sslServerSocket = (SSLServerSocket) this.sslServerSocketFactory.createServerSocket(this.port);
this.sslServerSocket.setSoTimeout(30000);
this.sslServerSocket.setEnabledProtocols(new String [] { "TLSv1", "TLSv1.1", "TLSv1.2" });
this.sslServerSocket.setUseClientMode(false);
this.sslServerSocket.setWantClientAuth(false);
this.sslServerSocket.setNeedClientAuth(false);
}
@Override
public void run()
{
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader input = null;
OutputStream outputStream = null;
PrintWriter output = null;
try
{
System.out.println("Server started");
System.out.println(" Waiting for connection from client...");
this.sslSocket = (SSLSocket) this.sslServerSocket.accept();
// Connection was accepted
System.out.println(" Accepted connection from " + this.sslSocket.getInetAddress().getHostAddress() + ", port " + this.sslSocket.getPort());
// set up a SSL session
this.sslSession = this.sslSocket.getSession();
System.out.println(" Cipher suite used for this session: " + this.sslSession.getCipherSuite());
inputStream = (InputStream) this.sslSocket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
input = new BufferedReader(inputStreamReader);
outputStream = this.sslSocket.getOutputStream();
output = new PrintWriter(outputStream);
System.out.println(" Server -> receiving...");
StringBuffer receiver = new StringBuffer();
Character serverReceived;
while ((serverReceived = (char)input.read()) != EOF)
{
receiver.append(serverReceived);
}
System.out.println(" Server received: " + serverReceived);
System.out.println(" Server -> sending...");
String serverSendSuccess = "Hello client, how are you?" + EOL + EOF;
String serverSendFail = "Who are you?" + EOL + EOF;
if (receiver.toString().contains("Hello server! I am the client!"))
{
System.out.println(" Server sent: " + serverSendSuccess);
output.println(serverSendSuccess);
output.flush();
}
else
{
System.out.println(" Server sent: " + serverSendFail);
output.println(serverSendFail);
output.flush();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
inputStream.close();
outputStream.close();
this.sslSocket.close();
}
catch(Exception ex) {}
System.out.println("Server ended");
}
}
}
これは私のクライアントクラスです:
public class SSLClient implements Runnable
{
private SSLSocketFactory sslSocketFactory;
private SSLSocket sslSocket;
private KeyStore keystore;
private KeyManager[] keyManagers;
private TrustManager[] trustManagers;
private SSLContext sslContext;
private String hostname = "localhost";
private int port = 8081;
private static final Character EOL = '\n';
private static final Character EOF = '\u0017';
public SSLClient() throws UnknownHostException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException, CertificateException, NoSuchProviderException, UnrecoverableKeyException
{
char[] passphrase = "changeit".toCharArray();
this.keystore = KeyStore.getInstance(KeyStore.getDefaultType());
this.keystore.load(new FileInputStream("keystore.ks"), passphrase);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(this.keystore, passphrase);
this.sslContext = SSLContext.getInstance("TLS");
this.keyManagers = keyManagerFactory.getKeyManagers();
this.trustManagers = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return null;
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
}
};
this.sslContext.init(this.keyManagers, this.trustManagers, new SecureRandom());
this.sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}
@Override
public void run()
{
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
BufferedReader input = null;
OutputStream outputStream = null;
PrintWriter output = null;
try
{
System.out.println("Start client");
this.sslSocket = (SSLSocket) this.sslSocketFactory.createSocket(this.hostname, this.port);
if(this.sslSocket.isConnected())
{
inputStream = (InputStream) this.sslSocket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
input = new BufferedReader(inputStreamReader);
outputStream = this.sslSocket.getOutputStream();
output = new PrintWriter(outputStream);
System.out.println(" Client -> sending...");
String clientSend = "Hello server! I am the client!" + EOL + EOF;
output.println(clientSend);
-----> output.flush(); // exception occurs here!
System.out.println(" Client sent: " + clientSend);
StringBuffer receiver = new StringBuffer();
Character clientReceived;
while ((clientReceived = (char)input.read()) != EOF)
{
receiver.append(clientReceived);
}
System.out.println(" Client received: " + receiver.toString());
}
else
{
System.err.println("Connection to server lost!");
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
inputStream.close();
outputStream.close();
this.sslSocket.close();
}
catch(Exception ex) {}
System.out.println("End client");
}
}
}
最後に、出力:
---Start test---
>> Start server thread
>> Start client thread
Start client
Server started
Waiting for connection from client...
Accepted connection from 127.0.0.1, port 55287
Client -> sending...
Client sent: Hello server! I am the client!
Cipher suite used for this session: SSL_NULL_WITH_NULL_NULL
Server -> receiving...
javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.SSLSocketImpl.checkEOF(SSLSocketImpl.java:1458)
[...]
Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
[...]
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
[...]
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
[...]
Server ended
End client
---End test---