How to achieve Secured HTTP communication in Java?
The Java Secure Socket Extension (JSSE) libraries are introduced as a standard from J2SE version 1.4 platform. The libraries offer support for handling secure HTTP requests and responses, also known as HTTPS or HTTP over SSL. More specifically, JSSE libraries provide the functionality necessary for authentication, encryption, and integrity. This support ensures that data sent over the network is private and not sent as clear text (that is, text that is publicly viewable and understandable).
The javax.net.ssl package contains the API for communicating through the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols. SSL and TLS are two closely related protocols for securing network traffic. Authentication is provided through the use of X.509 certificates -- this authentication is available for both the client and server, not just the server. In addition, the JSSE libraries offer encryption through secret key exchanges and certificate public keys. Encryption ensures message integrity, preventing a person in the middle of the communication from intercepting the message, altering it, and passing it on as if it was unchanged.
If you're not interested in direct socket usage and sending HTTP commands themselves, you can use the HttpsURLConnection class. The HttpsURLConnection class includes full support for features such as redirection, connection retries, proxy negotiations and cookies.
If you are interested in working with sockets directly, either for HTTP/HTTPS transactions, or for custom application-level usage, you can combine the javax.net.ssl package with the factories in javax.net. This allows you to communicate over a secured connection over which HTTPS runs. If you're familiar with communicating over an HTTP Socket connection, you'll find that working with an SSL-based socket isn't much different. You simply tend to talk over a different port, 443 instead of 80, for normal web traffic.
To securely communicate, you first need to acquire a socket factory. From the factory, you create a socket for the specific host and port. Instead of calling the Socket constructor, you ask theSocketFactory for a socket. The SocketFactory you use is the default SSL version of the factory,javax.net.ssl.SSLSocketFactory:
SocketFactory factory = SSLSocketFactory.getDefault();
Socket socket = factory.createSocket(hostname, HTTPS_PORT);
After that, the process is essentially the same as using an HTTP-based socket. You send a request to the server, and listen for a reply:
// Send Request
OutputStream outputStream =
socket.getOutputStream();
PrintWriter out = new PrintWriter(outputStream);
out.print("GET / HTTP/1.0\r\n\r\n");
out.flush();
// Get Response
InputStream inputStream =
socket.getInputStream();
InputStreamReader inputStreamReader =
new InputStreamReader(inputStream);
BufferedReader in =
new BufferedReader(inputStreamReader);
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
Here is a program, SSLClient,that puts all the pieces together:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
public class SSLClient {
private static final int HTTPS_PORT = 443;
public static void main(String args[]) {
if (args.length == 0) {
System.err.println
("Please provide a hostname to connect to");
System.exit(-1);
}
String hostname = args[0];
try {
// Get the default SSL socket factory
SocketFactory factory =
SSLSocketFactory.getDefault();
// Using socket factory, get SSL socket to port on host
Socket socket =
factory.createSocket(hostname, HTTPS_PORT);
// Send request to get root
// Be sure to end string with two sets of carriage
// return - newline characters.
OutputStream outputStream =
socket.getOutputStream();
PrintWriter out = new PrintWriter(outputStream);
out.print("GET / HTTP/1.0\r\n\r\n");
out.flush();
// Fetch response
InputStream inputStream =
socket.getInputStream();
InputStreamReader inputStreamReader =
new InputStreamReader(inputStream);
BufferedReader in =
new BufferedReader(inputStreamReader);
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
// Close everything
out.close();
in.close();
socket.close();
} catch (IOException e) {
System.err.println("Problems talking to " + hostname);
e.printStackTrace();
}
}
}
When you run the program, pass in the name of the host you want to communicate with. Consider running Tomcat locally, or connect to java.com. Note that the port constant in SSLClient is 443. You can alter the HTTPS port constant in the program if you need to, or you can change the program so that the constant is specified on the command line.
The results of communicating over the HTTPS port might be different than the results you get when communicating over the regular HTTP port. Consider saving the output and view it in your browser. You should trim off the standard header values, such as the date and time and server. Of course, you should leave in the server value if you're interested in what server a site is using. For instance, connecting to www.sun.com through the SSLClient program:
java SSLClient www.sun.com
reveals the use of the SunONE WebServer 6.0 in the various header fields:
HTTP/1.1 200 OK
Server: SunONE WebServer 6.0
Date: Tue, 02 Feb 2010 22:30:32 GMT
P3p: policyref="http://www.sun.com/p3p/Java_P3P_Policy.xml",
...
Clients should examine the credentials received to make sure that they are talking to the host they think they are. Use socket.getSession(), getPeerCertificates() or getPeerPrincipal(). It's non-trivial for someone to reroute the connection to a malicious host, and this is one way to check against that. You can use the HostnameVerifier interface to verify the host name.
0 comments:
Post a Comment