一个普通的webserver,只支持GET方法: <BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre> import java.net.*; import java.io.*; import java.util.*;// Small, simple HTTP server public class HTTPServer { String NAME; String VERSION; int serverPort;// No command line parameters are required public static void main(String args[]){ HTTPServer server = new HTTPServer("HTTPServer", "1.0", 80); server.run(); } // Create an HTTPServer for a particular TCP port public HTTPServer(String name, String version, int port) { this.NAME = name; this.VERSION = version; this.serverPort = port; } // Display name and version number public void displayVersionInfo(){ System.out.println(NAME+" version "+VERSION); } // Run until interrupted public void run() { displayVersionInfo(); try { // Get a server socket ServerSocket server = getServerSocket(); int localPort = server.getLocalPort(); // Let us know that you're listening System.out.println(NAME+" is listening on port "+localPort+"."); do { // Accept a connection Socket client = server.accept(); // Handle the connection with a separate thread (new HTTPServerThread(client)).start(); } while(true); } catch(Exception ex) { System.out.println("Unable to listen on "+serverPort+"."); ex.printStackTrace(); System.exit(1); } } // Get a server socket on the hard-wired server port ServerSocket getServerSocket() throws Exception { return new ServerSocket(serverPort); } }// Handle a single server connection class HTTPServerThread extends Thread { Socket client; // Keep track of the client socket public HTTPServerThread(Socket client) { this.client = client; } // Thread entry point public void run() { try { // Display info about the connection describeConnection(client); // Create a stream to send data to the client BufferedOutputStream outStream = new BufferedOutputStream(client.getOutputStream()); HTTPInputStream inStream = new HTTPInputStream(client.getInputStream()); // Get the client's request HTTPRequest request = inStream.getRequest(); // Display info about it request.log(); // Sorry, we only handle gets if(request.isGetRequest()) processGetRequest(request,outStream); System.out.println("Request completed. Closing connection."); }catch(IOException ex) { System.out.println("IOException occurred when processing request."); } try { client.close(); }catch(IOException ex) { System.out.println("IOException occurred when closing socket."); } } // Display info about the connection void describeConnection(Socket client) { String destName = client.getInetAddress().getHostName(); String destAddr = client.getInetAddress().getHostAddress(); int destPort = client.getPort(); System.out.println("Accepted connection to "+destName+" (" +destAddr+")"+" on port "+destPort+"."); } // Process an HTTP GET void processGetRequest(HTTPRequest request,BufferedOutputStream outStream) throws IOException { /* If you want to use this in a secure environment then you should place some restrictions on the requested file name */ String fileName = request.getFileName(); File file = new File(fileName); // Give them the requested file if(file.exists()) sendFile(outStream,file); else System.out.println("File "+file.getCanonicalPath()+" does not exist."); } // A simple HTTP 1.0 response void sendFile(BufferedOutputStream out,File file) { try { DataInputStream in = new DataInputStream(new FileInputStream(file)); int len = (int) file.length(); byte buffer[] = new byte[len]; in.readFully(buffer); in.close(); out.write("HTTP/1.0 200 OK\r\n".getBytes()); out.write(("Content-Length: " + buffer.length + "\r\n").getBytes()); out.write("Content-Type: text/html\r\n\r\n".getBytes()); out.write(buffer); out.flush(); out.close(); System.out.println("File sent: "+file.getCanonicalPath()); System.out.println("Number of bytes: "+len); }catch(Exception ex){ try { out.write(("HTTP/1.0 400 " + "No can do" + "\r\n").getBytes()); out.write("Content-Type: text/html\r\n\r\n".getBytes()); }catch(IOException ioe) { } System.out.println("Error retrieving "+file); } } }// Convenience class for reading client requests class HTTPInputStream extends FilterInputStream { public HTTPInputStream(InputStream in) { super(in); } // Get a line public String readLine() throws IOException { StringBuffer result=new StringBuffer(); boolean finished = false; boolean cr = false; do { int ch = -1; ch = read(); if(ch==-1) return result.toString(); result.append((char) ch); if(cr && ch==10){ result.setLength(result.length()-2); return result.toString(); } if(ch==13) cr = true; else cr=false; } while (!finished); return result.toString(); } // Get the whole request public HTTPRequest getRequest() throws IOException { HTTPRequest request = new HTTPRequest(); String line; do { line = readLine(); if(line.length()>0) request.addLine(line); else break; }while(true); return request; } }// Used to process GET requests class HTTPRequest { Vector lines = new Vector();public HTTPRequest() { } public void addLine(String line) { lines.addElement(line); } // Is this a GET or isn't it? boolean isGetRequest() { if(lines.size() > 0) { String firstLine = (String) lines.elementAt(0); if(firstLine.length() > 0) if(firstLine.substring(0,3).equalsIgnoreCase("GET")) return true; } return false; } // What do they want to get? String getFileName() { if(lines.size()>0) { String firstLine = (String) lines.elementAt(0); String fileName = firstLine.substring(firstLine.indexOf(" ")+1); int n = fileName.indexOf(" "); if(n!=-1) fileName = fileName.substring(0,n); try { if(fileName.charAt(0) == '/') fileName = fileName.substring(1); } catch(StringIndexOutOfBoundsException ex) {} if(fileName.equals("")) fileName = "index.htm"; if(fileName.charAt(fileName.length()-1)=='/') fileName+="index.htm"; return fileName; }else return ""; } // Display some info so we know what's going on void log() { System.out.println("Received the following request:"); for(int i=0;i<lines.size();++i) System.out.println((String) lines.elementAt(i)); } }
扩展这个Webserver,以支持SSL: <BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre> Extending the HTTP sever with SSL support. import java.net.*; import java.io.*; import java.util.*; import java.security.*; import javax.net.*; import javax.net.ssl.*; import com.sun.net.ssl.*;public class SecureServer extends HTTPServer { String KEYSTORE = "certs"; char[] KEYSTOREPW = "serverkspw".toCharArray(); char[] KEYPW = "serverpw".toCharArray(); boolean requireClientAuthentication;public static void main(String args[]){ SecureServer server = new SecureServer(); server.run(); } public SecureServer(String name, String version, int port, boolean requireClientAuthentication) { super(name, version, port); this.requireClientAuthentication = requireClientAuthentication; } public SecureServer() { this("SecureServer", "1.0", 443, false); } ServerSocket getServerSocket() throws Exception { // Make sure that JSSE is available Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); // A keystore is where keys and certificates are kept // Both the keystore and individual private keys should be password protected KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream(KEYSTORE), KEYSTOREPW); // A KeyManagerFactory is used to create key managers KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); // Initialize the KeyManagerFactory to work with our keystore kmf.init(keystore, KEYPW); // An SSLContext is an environment for implementing JSSE // It is used to create a ServerSocketFactory SSLContext sslc = SSLContext.getInstance("SSLv3"); // Initialize the SSLContext to work with our key managers sslc.init(kmf.getKeyManagers(), null, null); // Create a ServerSocketFactory from the SSLContext ServerSocketFactory ssf = sslc.getServerSocketFactory(); // Socket to me SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket(serverPort); // Authenticate the client? serverSocket.setNeedClientAuth(requireClientAuthentication); // Return a ServerSocket on the desired port (443) return serverSocket; } }
客户端编程一个浏览普通站点的简易浏览器: <BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre> import java.io.*; import java.net.*; import java.security.*;// A simple text-based browser public class Browser { String urlString;// You must supply the URL to be browsed public static void main(String[] args) throws Exception { if(args.length != 1) { System.out.println("Usage: java Browser url"); System.exit(1); } Browser browser = new Browser(args[0]); browser.run(); } // Construct a browser object public Browser(String urlString) { this.urlString = urlString; } // Get the URL public void run() throws Exception { URL url = new URL(urlString); HttpURLConnection urlc = (HttpURLConnection) url.openConnection(); System.out.println("THE HEADERS"); System.out.println("-----------"); for(int i=1;;++i) { String key; String value; if((key = urlc.getHeaderFieldKey(i)) == null) break; if((value = urlc.getHeaderField(i)) == null) break; System.out.println("KEY: " + key); System.out.println("VALUE: " + value); } BufferedReader reader = new BufferedReader( new InputStreamReader(urlc.getInputStream())); String line; System.out.println("THE CONTENT"); System.out.println("-----------"); while((line = reader.readLine()) != null) System.out.println(line); } } [/code] 扩展对SSL的支持: <BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre> import java.io.*; import java.net.*; import java.security.*;// Extend Browser to use SSL public class SecureBrowser extends Browser { // Must supply URL in command line public static void main(String[] args) throws Exception { if(args.length != 1) { System.out.println("Usage: java SecureBrowser url"); System.exit(1); } SecureBrowser browser = new SecureBrowser(args[0]); browser.run(); } // Construct a SecureBrowser public SecureBrowser(String urlString) { super(urlString); // Register JSSE Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider()); // Here's the trick! // Simply set the protocol handler property to use SSL. System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); } }
我也用JDK的keytool程序生成了一个文件,但不知怎么用?
如果用JDK1.4就很简单的,删掉server.xml里面的一段注释就可以了
能更详细点吗,如果用JDK的话。
然后在tomcat4.0的conf目录中的server.xml找到文字
<!-- Define an SSL HTTP/1.1 Connector on port 8443 -->
然后把原有的注释去掉,使到
<Connector className="org.apache.catalina.connector.http.HttpConnector"
port="8443" minProcessors="5" maxProcessors="75"
enableLookups="true"
acceptCount="10" debug="0" scheme="https" secure="true">
<Factory className="org.apache.catalina.net.SSLServerSocketFactory"
clientAuth="false" protocol="TLS"/>
</Connector>
生效,然后重新启动tomcat就可以了,默认断口是8443
用keytool生成的key文件放在服务器端,还是分发给客户端?我需要的是仅对客户端进行安全认证。
但,我还是没明白,服务器是怎样来验证客户端的连接是否合法允许?
但不知道它怎么生成?和SSL怎么联上?
keytool除了生成公匙私匙外还要生成证书,这些都在服务器端,客户连接时会得到服务器的证书,其中包括服务器的公匙。
一般的SSL不是来验证客户端连接是否合法的,而是让客户端验证服务器是合法的,(这个通过证书来实现),并保证客户端与服务器连接的安全。
客户端怎么去验证服务器是合法的?证书?哪它怎么知道这证书是合法的?
即使知道这证书是否合法,那假如其它人也用该证书,客户端怎么区别出他不是合法用户?
<BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre>
import java.net.*;
import java.io.*;
import java.util.*;// Small, simple HTTP server
public class HTTPServer {
String NAME;
String VERSION;
int serverPort;// No command line parameters are required
public static void main(String args[]){
HTTPServer server = new HTTPServer("HTTPServer", "1.0", 80);
server.run();
}
// Create an HTTPServer for a particular TCP port
public HTTPServer(String name, String version, int port) {
this.NAME = name;
this.VERSION = version;
this.serverPort = port;
}
// Display name and version number
public void displayVersionInfo(){
System.out.println(NAME+" version "+VERSION);
}
// Run until interrupted
public void run() {
displayVersionInfo();
try {
// Get a server socket
ServerSocket server = getServerSocket();
int localPort = server.getLocalPort();
// Let us know that you're listening
System.out.println(NAME+" is listening on port "+localPort+".");
do {
// Accept a connection
Socket client = server.accept();
// Handle the connection with a separate thread
(new HTTPServerThread(client)).start();
} while(true);
} catch(Exception ex) {
System.out.println("Unable to listen on "+serverPort+".");
ex.printStackTrace();
System.exit(1);
}
}
// Get a server socket on the hard-wired server port
ServerSocket getServerSocket() throws Exception {
return new ServerSocket(serverPort);
}
}// Handle a single server connection
class HTTPServerThread extends Thread {
Socket client;
// Keep track of the client socket
public HTTPServerThread(Socket client) {
this.client = client;
}
// Thread entry point
public void run() {
try {
// Display info about the connection
describeConnection(client);
// Create a stream to send data to the client
BufferedOutputStream outStream = new
BufferedOutputStream(client.getOutputStream());
HTTPInputStream inStream = new HTTPInputStream(client.getInputStream());
// Get the client's request
HTTPRequest request = inStream.getRequest();
// Display info about it
request.log();
// Sorry, we only handle gets
if(request.isGetRequest())
processGetRequest(request,outStream);
System.out.println("Request completed. Closing connection.");
}catch(IOException ex) {
System.out.println("IOException occurred when processing request.");
}
try {
client.close();
}catch(IOException ex) {
System.out.println("IOException occurred when closing socket.");
}
}
// Display info about the connection
void describeConnection(Socket client) {
String destName = client.getInetAddress().getHostName();
String destAddr = client.getInetAddress().getHostAddress();
int destPort = client.getPort();
System.out.println("Accepted connection to "+destName+" ("
+destAddr+")"+" on port "+destPort+".");
}
// Process an HTTP GET
void processGetRequest(HTTPRequest request,BufferedOutputStream outStream)
throws IOException {
/* If you want to use this in a secure environment then you should place some
restrictions on the requested file name */
String fileName = request.getFileName();
File file = new File(fileName);
// Give them the requested file
if(file.exists()) sendFile(outStream,file);
else System.out.println("File "+file.getCanonicalPath()+" does not exist.");
}
// A simple HTTP 1.0 response
void sendFile(BufferedOutputStream out,File file) {
try {
DataInputStream in = new DataInputStream(new FileInputStream(file));
int len = (int) file.length();
byte buffer[] = new byte[len];
in.readFully(buffer);
in.close();
out.write("HTTP/1.0 200 OK\r\n".getBytes());
out.write(("Content-Length: " + buffer.length + "\r\n").getBytes());
out.write("Content-Type: text/html\r\n\r\n".getBytes());
out.write(buffer);
out.flush();
out.close();
System.out.println("File sent: "+file.getCanonicalPath());
System.out.println("Number of bytes: "+len);
}catch(Exception ex){
try {
out.write(("HTTP/1.0 400 " + "No can do" + "\r\n").getBytes());
out.write("Content-Type: text/html\r\n\r\n".getBytes());
}catch(IOException ioe) {
}
System.out.println("Error retrieving "+file);
}
}
}// Convenience class for reading client requests
class HTTPInputStream extends FilterInputStream {
public HTTPInputStream(InputStream in) {
super(in);
}
// Get a line
public String readLine() throws IOException {
StringBuffer result=new StringBuffer();
boolean finished = false;
boolean cr = false;
do {
int ch = -1;
ch = read();
if(ch==-1) return result.toString();
result.append((char) ch);
if(cr && ch==10){
result.setLength(result.length()-2);
return result.toString();
}
if(ch==13) cr = true;
else cr=false;
} while (!finished);
return result.toString();
}
// Get the whole request
public HTTPRequest getRequest() throws IOException {
HTTPRequest request = new HTTPRequest();
String line;
do {
line = readLine();
if(line.length()>0) request.addLine(line);
else break;
}while(true);
return request;
}
}// Used to process GET requests
class HTTPRequest {
Vector lines = new Vector();public HTTPRequest() {
}
public void addLine(String line) {
lines.addElement(line);
}
// Is this a GET or isn't it?
boolean isGetRequest() {
if(lines.size() > 0) {
String firstLine = (String) lines.elementAt(0);
if(firstLine.length() > 0)
if(firstLine.substring(0,3).equalsIgnoreCase("GET"))
return true;
}
return false;
}
// What do they want to get?
String getFileName() {
if(lines.size()>0) {
String firstLine = (String) lines.elementAt(0);
String fileName = firstLine.substring(firstLine.indexOf(" ")+1);
int n = fileName.indexOf(" ");
if(n!=-1) fileName = fileName.substring(0,n);
try {
if(fileName.charAt(0) == '/') fileName = fileName.substring(1);
} catch(StringIndexOutOfBoundsException ex) {}
if(fileName.equals("")) fileName = "index.htm";
if(fileName.charAt(fileName.length()-1)=='/')
fileName+="index.htm";
return fileName;
}else return "";
}
// Display some info so we know what's going on
void log() {
System.out.println("Received the following request:");
for(int i=0;i<lines.size();++i)
System.out.println((String) lines.elementAt(i));
}
}
<BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre>
Extending the HTTP sever with SSL support.
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
import javax.net.*;
import javax.net.ssl.*;
import com.sun.net.ssl.*;public class SecureServer extends HTTPServer {
String KEYSTORE = "certs";
char[] KEYSTOREPW = "serverkspw".toCharArray();
char[] KEYPW = "serverpw".toCharArray();
boolean requireClientAuthentication;public static void main(String args[]){
SecureServer server = new SecureServer();
server.run();
}
public SecureServer(String name, String version, int port,
boolean requireClientAuthentication) {
super(name, version, port);
this.requireClientAuthentication = requireClientAuthentication;
}
public SecureServer() {
this("SecureServer", "1.0", 443, false);
}
ServerSocket getServerSocket() throws Exception {
// Make sure that JSSE is available
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
// A keystore is where keys and certificates are kept
// Both the keystore and individual private keys should be password protected
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(KEYSTORE), KEYSTOREPW);
// A KeyManagerFactory is used to create key managers
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
// Initialize the KeyManagerFactory to work with our keystore
kmf.init(keystore, KEYPW);
// An SSLContext is an environment for implementing JSSE
// It is used to create a ServerSocketFactory
SSLContext sslc = SSLContext.getInstance("SSLv3");
// Initialize the SSLContext to work with our key managers
sslc.init(kmf.getKeyManagers(), null, null);
// Create a ServerSocketFactory from the SSLContext
ServerSocketFactory ssf = sslc.getServerSocketFactory();
// Socket to me
SSLServerSocket serverSocket =
(SSLServerSocket) ssf.createServerSocket(serverPort);
// Authenticate the client?
serverSocket.setNeedClientAuth(requireClientAuthentication);
// Return a ServerSocket on the desired port (443)
return serverSocket;
}
}
<BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre>
import java.io.*;
import java.net.*;
import java.security.*;// A simple text-based browser
public class Browser {
String urlString;// You must supply the URL to be browsed
public static void main(String[] args) throws Exception {
if(args.length != 1) {
System.out.println("Usage: java Browser url");
System.exit(1);
}
Browser browser = new Browser(args[0]);
browser.run();
}
// Construct a browser object
public Browser(String urlString) {
this.urlString = urlString;
}
// Get the URL
public void run() throws Exception {
URL url = new URL(urlString);
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
System.out.println("THE HEADERS");
System.out.println("-----------");
for(int i=1;;++i) {
String key;
String value;
if((key = urlc.getHeaderFieldKey(i)) == null) break;
if((value = urlc.getHeaderField(i)) == null) break;
System.out.println("KEY: " + key);
System.out.println("VALUE: " + value);
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(urlc.getInputStream()));
String line;
System.out.println("THE CONTENT");
System.out.println("-----------");
while((line = reader.readLine()) != null) System.out.println(line);
}
}
[/code]
扩展对SSL的支持:
<BLOCKQUOTE><FONT SIZE="2" class=c face="Verdana, Arial">code:</font><HR><pre>
import java.io.*;
import java.net.*;
import java.security.*;// Extend Browser to use SSL
public class SecureBrowser extends Browser {
// Must supply URL in command line
public static void main(String[] args) throws Exception {
if(args.length != 1) {
System.out.println("Usage: java SecureBrowser url");
System.exit(1);
}
SecureBrowser browser = new SecureBrowser(args[0]);
browser.run();
}
// Construct a SecureBrowser
public SecureBrowser(String urlString) {
super(urlString);
// Register JSSE
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
// Here's the trick!
// Simply set the protocol handler property to use SSL.
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");
}
}
你自己拣着看吧 :)至于服务器配置,iis4.x和5.0的ssl配置是不一样的,apache倒没问题
非常感谢,我将会去消化一下你的代码。
to jimjxr(宝宝猫) :
证书是与服务器的地址相联系的,那就是说被确认方的IP地址是不能变的?另外,我自己能作证书吗?不通过由颁发证书的CA。
我提示一下我要解决的问题:
我要做一个在HTTP1.1协议基础上的服务器(并不是网站,仅在HTTP上传数据),但要保证此服务器的安全,仅让允许的客户端得到它的服务,而客户端可以假定服务器是确认的,不需对服务器认证。
我看了一些文章,说SSL能保证网络的安全,但还不晓得怎么跟我的问题挂上。