Telnet に似た Java のソケット クラスを使用して単純な Web クライアントを作成しようとしています。ユーザーが任意のポートで任意の Web サーバーと自由に通信できるようにしたい。HTTP/HTTPS 通信のサポートも組み込みたいと思います。HTTP 部分は問題なく動作しましたが、HTTPS で多くの問題が発生しています。
Java の SSLSocket クラスを使用したいと考えています。多くの Web サイトと例を見て、次のコードをまとめて Google のホームページを取得しました。
package httpssandbox;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
public class HttpsSandbox {
static int port = 443;
static String addressString = "google.com";
static InetAddress address;
SSLSocket socket;
PrintWriter out;
BufferedReader in;
public static void main(String[] args) {
System.out.println("Connecting to: " + addressString);
System.out.println("Using port: " + port);
//Connect to the website
HttpsSandbox hs = new HttpsSandbox();
hs.connect();
}
public void connect(){
try {
//Resolve IP address
address = InetAddress.getByName(addressString);
System.out.println("IP address: " + address.getHostAddress());
//Connect using a secure SSL conenction
SSLSocketFactory socketf = HttpsURLConnection.getDefaultSSLSocketFactory();
socket = (SSLSocket) socketf.createSocket("google.com", port);
socket.startHandshake();
printSocketInfo();
//Get the input and output streams of the socket
out = new PrintWriter(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sendHTTPGET();
System.out.println(receive());
} catch (UnknownHostException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void sendHTTPGET(){
//Construct a string with the message
out.println("GET / HTTP/1.1");
out.println();
out.flush();
}
private String receive(){
String message = "";
//Wait for a message to arrive.
//Wouldn't want to miss any messages delayed by network connection
try {
while(!in.ready()) {}
while(in.ready()){
message += in.readLine() + "\n";
}
return(message);
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
}
private void printSocketInfo(){
System.out.println("Socket class: "+ socket.getClass());
System.out.println(" Remote address = " + socket.getInetAddress().toString());
System.out.println(" Remote port = " + socket.getPort());
System.out.println(" Local socket address = " + socket.getLocalSocketAddress().toString());
System.out.println(" Local address = " + socket.getLocalAddress().toString());
System.out.println(" Local port = " + socket.getLocalPort());
System.out.println(" Need client authentication = " + socket.getNeedClientAuth());
SSLSession ss = socket.getSession();
System.out.println(" Cipher suite = " + ss.getCipherSuite());
System.out.println(" Protocol = " + ss.getProtocol());
System.out.println();
Certificate[] serverCerts = null;
try {
serverCerts = socket.getSession().getPeerCertificates();
} catch (SSLPeerUnverifiedException ex) {
ex.printStackTrace();
}
System.out.println("Retreived Server's Certificate Chain");
System.out.println(serverCerts.length + "Certifcates Found\n\n\n");
for (int i = 0; i < serverCerts.length; i++) {
Certificate myCert = serverCerts[i];
System.out.println("====Certificate:" + (i+1) + "====");
System.out.println("-Public Key-\n" + myCert.getPublicKey());
System.out.println("-Certificate Type-\n " + myCert.getType());
System.out.println();
}
}
}
コードを実行すると、次の出力が表示されます。
Connecting to: google.com
Using port: 443
IP address: 173.194.46.101
Socket class: class sun.security.ssl.SSLSocketImpl
Remote address = google.com/173.194.46.101
Remote port = 443
Local socket address = /192.168.15.126:50357
Local address = /192.168.15.126
Local port = 50357
Need client authentication = false
Cipher suite = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
Protocol = TLSv1
Retreived Server's Certificate Chain
3Certifcates Found
====Certificate:1====
-Public Key-
Sun EC public key, 256 bits
public x coord: 92211319072714844469959217656780286932148107234802524635747648609523069275349
public y coord: 36581585054743121309038603897530740476813606346857238295887416801699179162876
parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7)
-Certificate Type-
X.509
====Certificate:2====
-Public Key-
Sun RSA public key, 2048 bits
modulus: 19713895149719550196537065661910573762693934593220985668782860735427060889140793885919063737778303548724916253252606564904177491762533295616984617709378739783748100146882543612565825906799282133510087546060971220666055151463898734279731009956582933624646298029265838127046200538496591314458940937082185029845612274584845875286257057247598474925565775989866310636633768255501748172403430876460228793912189332026189491067186811703150477068536877439284697584041860237489395099402658887745588613142391209024263265842301844868193180477031165936332420984796347731387363914950895491332976177715889375379088870580457661428329
public exponent: 65537
-Certificate Type-
X.509
====Certificate:3====
-Public Key-
Sun RSA public key, 2048 bits
modulus: 27620593608073140957439440929253438012688864718977347268272053725994928948867769687165112265058896553974818505070806430256424431940072485024407486246475597522063246121214348496326377341879755851197260401080498544606788760407243324127929930612201002157618691487713632251700065187865963692723720912135393438861302779432180613616167225206519123176430362410262429702404863434904116727055203524505580952824336979641923534005571504410997292144760317953739063178352809680844232935574095508445145910310675421726257114605895831426222686272114090063230017292595425393719031924942422176213538487957041730136782988405751614792953
public exponent: 65537
-Certificate Type-
X.509
Sending message:
GET / HTTP/1.1
Host: google.com
この時点で、サーバーからの応答を待っている間にコードがフリーズします。この応答は決して来ません。別のWebサイトで同様のHTTPリクエストを使用して同様のコードを試しましたが、現在心配していない400エラーが返されました. 私の今の目標は、Google に接続してサーバーからの応答を取得することです。私が理解していないのは、サーバーから応答が得られない理由です。
ご協力いただきありがとうございます!私は解決策を探し回りましたが、うまくいきませんでした。
アップデート:
user3586195 が投稿したコードを試してみましたが、うまくいきました! トリックは receive() メソッドにあります。準備ができていることを示すために、ストリームから読み取る必要がありました。みんなの助けに感謝します!