November 27, 2014
Hot Topics:

Wireless Home Security and Java

  • June 13, 2006
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 734


Preface

There are many good reasons for learning to write Java programs that communicate with network servers, particularly HTTP servers.

Cleaning up your bookmark library

For example, in the earlier lesson entitled Using Java to Clean Up Your Bookmark Library, I showed you how to write a Java program that will help you to identify the broken links in your bookmark (Favorites) library so that you can either delete or repair them.

Basically, the program in that earlier lesson cycles through your bookmark library, attempting to contact the servers listed there to download the headers for the specified resources.  It then interprets those headers providing you with information that you can use to clean up the library.

Setting the encryption key in your wireless router

In this lesson, I will show you how to write a Java program that makes it very easy to modify the encryption key in your wireless router, either manually or on an automated scheduled basis.

The premise is that if it is easy for you to change the encryption key, you may be inclined to change it more frequently.  If you change the encryption key more frequently, this may (and I emphasize the word may) cause your home or small office wireless network to be more secure.

(On the other hand, because I am not a security expert and don't have a full understanding of all of the ramifications of wireless network security, it is entirely possible that this may make your wireless network less secure.  Please pay careful attention to the disclaimer that follows.)

A disclaimer

I am not a security expert.  My expertise is in the area of Java programming.  The programs in this lesson are provided for educational purposes only.  There is no suggestion that these programs are suitable for any purpose other than education in the area of Java programming.  If you use the programs for any purpose whatsoever, you are doing so at your own risk.  I will accept no responsibility for any outcome that you may experience.

What can you expect from this lesson?

My goal in this lesson is to teach you how to write HTTP client programs in Java, which communicate with HTTP servers, and which deal with the following HTTP/1.0 and/or HTTP/ 1.1 features:

Regardless of whether or not you elect to use the program for setting the encryption key on your wireless router, the lesson should still teach you quite a lot about writing Java programs to communicate with HTTP servers.

Seven sample programs

I will provide and explain seven sample programs, each designed to illustrate certain HTTP programming concepts.  One of the programs will be designed to run against an HTTP server on the World Wide Web.  The other five will be designed to run against the HTTP server that is built into a typical wireless router.  (Such HTTP servers are used by the router administrator to configure and manage the router.)

Packet sniffing

Along the way, I will teach you a little about packet sniffing and show you how to eavesdrop on conversations between HTTP servers and HTTP clients.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings and figures while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

In particular, I recommend that you review the lessons referred to in the References section in preparation for understanding the material in this lesson.

Preview

Typically, in tutorial lessons that involve one or two large programs, this is the place where I explain in detail what you can expect to find in each of the programs.  However, this lesson will explain seven relatively small programs.  To keep the level of confusion down, it will probably be better for me to defer the detailed technical discussion until we get to the sections where I explain the code for the programs.

Topics covered by the programs

The following list provides a very brief summary description of each of the seven programs:

  • Sockets15:  Illustrates general communication between an HTTP client and an HTTP/1.1 server, which doesn't require authentication.  Also shows how to cause the server to forego keep-alive and to close the connection at the end of each response by the server.
  • Sockets14:  Illustrates basic authentication using a Linksys WRT54G wireless router as the experimental platform.
  • Sockets10:  Shows how to automatically install a new WEP encryption key on a Linksys WRT54G wireless router requiring basic authentication.
  • Sockets11:  Shows how to automatically install a new WPA or WPA2 shared encryption key on two different versions of a Linksys WRT54G wireless router, one that supports WPA2, and one that does not support WPA2 but does support WPA.
  • Sockets11A:  A stand alone, non-network program that generates and displays the same encryption key generated by Sockets11 for any particular data.
  • Sockets12:  Illustrates the authentication methodology on a Belkin 54G wireless router using the custom scheme provided by that router for that purpose.  Note that this is completely different from the basic authentication methodology used on the Linksys WRT54G router.
  • Sockets13:  Shows how to automatically install a new WEP encryption key on a Belkin 54G wireless router requiring custom authentication.

Packet sniffing

Sprinkled throughout the discussions of these programs will be a discussion of the use of the Ethereal Network Protocol Analyzer program to discover exactly what is required to automate the normally manual process of installing encryption keys in these wireless routers.

Discussion and Sample Code

Complete listings of all of the programs are provided beginning in Listing 49 near the end of the lesson.

I will discuss the programs in fragments in the sections that follow.

The Sockets15 Class

A complete listing of this program is provided in Listing 49 near the end of the lesson.

The program named Sockets15 illustrates general communication between a program and an HTTP/1.1server, which doesn't require authentication.  It also illustrates how to cause the server to forego the keep-alive feature and to close the connection at the end of each response message.

In operation, this programs attempts to get the headers for two different resources from the same server.  The server that was used for the testing of this program normally implements keep-alive by default.  Although the use of the keep-alive feature can improve network efficiency, programming an HTTP client program to support the keep-alive feature can be a very difficult task.  Therefore, this program instructs the server to forego the keep-alive feature and to close the connection following the transmission of the response to each client request.

Sample output from the program is shown in the comments at the beginning of the program in Listing 49.  In addition, I will show you portions of that output in the discussion that follows.

Will discuss in fragments

As is my custom, I will explain this program by discussing important parts of the program in fragments.  The first such fragment is shown in Listing 1.

Listing 1 shows the beginning of the class including the declaration of three instance variables and the initialization of one of them.

class Sockets15{

  final String server = "www.austincc.edu";
  boolean closedFlag;
  SocketWrapper socketWrapper;

Listing 1

The instance variable named server is initialized to contain the name of the server that I used to test this program.  However, you could test the program against any HTTP/1.1 server that doesn't require authentication, and which implements keep-alive by default.

What is keep-alive anyway?

The original HTTP protocol was a simple request/response protocol.  Using this simple protocol, the following steps occur during each request/response cycle:

  • The client establishes a socket connection with the server.
  • The client sends a request for a resource to the server.
  • The server either delivers the resource back to the client or sends an error message.
  • The server disconnects.

While this is a very simple protocol to implement, it is also slow.  Often, more time is required to make and break the connection than is required to exchange the request and the response.

The protocol was enhanced

As a result, somewhere along the way, the protocol was enhanced to make it possible for the client to send a sequence of requests to the server during a single connection, and for the server to send a sequence of matching responses back to the client during the same connection.  This is generally referred to as keep-alive, and I will discuss some of the technical manifestations of the feature later.

Examples of complexity

While the advent of keep-alive can greatly improve the efficiency of the communications between the client and the server, it also causes the client program to be considerably more complex.  For example, the client may send a request for resources A and B to the server before learning from the server that resource A is not available but resource B is available and is being delivered.  However, resource B may be of no value to the client without resource A.

In addition, there is no guarantee that the server will support keep-alive over the long haul.  The server may simply decide on its own to switch from keep-alive operation to the older request/response format after the client has already sent a long sequence of requests.  The client program must be able to take issues like that into account and to make the necessary adjustments.

Can disable keep-alive

As a result, the HTTP/1.1 protocol makes it possible for client programs that don't have the ability to support keep-alive to instruct the server to revert back to the simple request/response form of the protocol by including a specific instruction in the request headers.  As you will see shortly, this simple HTTP client program does not support keep-alive operation, and it does provide such an instruction to the server.

The main method

The main method is shown in its entirety in Listing 2.

  public static void main(String[] args){
    new Sockets15().doIt();
  }//end main

Listing 2

The main method simply instantiates an object of the class and invokes the instance method named doIt on that object.

The method named doIt

The method named doIt is the primary processing method for the class named Sockets15.

(In fact, the primary processing method for each of the seven sample programs discussed in this lesson will be named doIt.)

The method named doIt for the class named Sockets15 begins in Listing 3.

  void doIt(){

    socketWrapper = getSocket(server);

Listing 3

The code in Listing 3 invokes the getSocket method to get a socket, which is connected to the specified server, on port 80.  Port 80 is the standard HTTP port.  The getSocket method also gets input and output streams on the socket that can be used to communicate with the server.  References to the Socket object as well as the input and output stream objects are returned encapsulated in an object of the simple wrapper class named SocketWrapper.

The SocketWrapper class

The SocketWrapper class is shown in its entirety in Listing 4.

  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper

Listing 4

An object of this class is used to encapsulate the references to the three objects described above, making it easy to return three values from the getSocket method.  An object of the class consists of nothing more than three encapsulated instance variables, which will be populated with references to the three objects.

The getSocket method

The getSocket method begins in Listing 5.

  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);

Listing 5

The purpose of the getSocket method is to get a new Socket object connected to a server on port 80 along with input and output stream objects that can be used to communicate with the server by way of the connected socket.

References to the Socket object and the two stream objects are returned in a wrapper object of type SocketWrapper.

(The getSocket method will be used in six of the programs provided in this lesson.  Therefore, I will explain it only once in conjunction with the class named Sockets15 and will simply refer to it in the discussions of the other five programs.)

Port 80

Listing 5 begins by declaring a variable named port and initializing it with the value of the standard HTTP port.

(Much of the background material that you will need to know in order to understand the programs in this lesson, such as the standard HTTP port, was provided in my earlier lessons.  This includes lessons that are referred to in the References section.)

Then the code in Listing 5 instantiates a new empty object of the SocketWrapper class, to be populated later with references to the three objects.

A new Socket object

Finally, Listing 5 instantiates a new Socket object that is connected to the specified server on port 80.  When the constructor returns the connection will have been established.  In the event that it is not possible to establish the connection, the constructor will throw an exception.  As you will see later, this will cause the program to abort printing a stack trace in the process.

Get an input stream

Listing 6 gets an input stream object used later for reading the response sent by the server.  The reference to the stream object is saved in the wrapper object of type SocketWrapper.

      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

Listing 6

The syntax for creating input and output streams in Java is rather complicated.  You can read more about that syntax in my earlier lessons referred to in the References section.

Get an output stream

Listing 7 gets an eight-bit byte output stream object on the socket, (which will autoflush), and saves the reference to the stream object in the wrapper object.

      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket

Listing 7

What does Sun have to say?

Sun has this to say about the PrintStream class:

"All characters printed by a PrintStream are converted into bytes using the platform's default character encoding. The PrintWriter class should be used in situations that require writing characters rather than bytes."

I initially tried using an output stream of the PrintWriter class, (which delivers sixteen-bit character data instead of eight-bit byte data).  When I began testing one of the later programs that uses the POST method to post data to the server, I discovered that the program did not work properly using the PrintWriter class.  In order to cause the POST method to work properly, it was necessary for me to revert back to the PrintStream class that delivers eight-bit byte data instead of 16-bit character data.

Listing 7 returns a reference to the populated SocketWrapper object, which signals the end of the getSocket method.

Returning to the doIt method ...

Returning now to the discussion of the doIt method, Listing 8 displays a message informing the user that the program is going to invoke the HEAD method on the server.

    System.out.println("**Send initial HEAD command**");

Listing 8

Invoke the HEAD method on the server

Listing 9 uses the output stream to invoke the HEAD method on the server.

    socketWrapper.outputStream.println("HEAD / HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    //Ask the server to close the connection following the
    // response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here.
    socketWrapper.outputStream.println();

Listing 9

The HEAD method

The HEAD method is one of several methods that a client can invoke on a server.  This lesson will discuss the following methods:

  • HEAD
  • GET
  • POST

HTTP transactions

According to HTTP Made Really Easy,

"Like most network protocols, HTTP uses the client-server model: An HTTP client opens a connection and sends a request message to an HTTP server; the server then returns a response message, usually containing the resource that was requested. After delivering the response, the server closes the connection (making HTTP a stateless protocol, i.e. not maintaining any connection information between transactions)."

Note that the above statement doesn't necessarily take the keep-alive feature that was introduced in version HTTP/1.1 into account.  That's OK for us, however, because we are going to prevent the use of the keep-alive feature in these programs.

Format of request and response messages

According to the same source, the request and response messages are similar consisting of:

  • An initial line
  • Zero or more header lines (at least one header line is required in an HTTP/1.1 request message)
  • A blank line consisting of a carriage return (0x0D) followed by a line feed (0x0A), often referred to as a CRLF, on a line by itself.
  • An optional message body (such as query data or query output)

Because this lesson is mostly concerned with the creation and transmission of request messages (as opposed to the creation or interpretation of response messages) I will concentrate mostly on the format of requests.

(On the other hand, the earlier lesson entitled Using Java to Clean Up Your Bookmark Library is very concerned with the interpretation of response messages.)

The initial request line

The initial request line is very similar for HEAD, GET, and POST request messages.  It has three parts as shown in Figure 1.

HEAD /path to requested resource HTTP/x.x
Figure 1

The method

The first part of the initial request line is the name of the method to be invoked on the server (HEAD in the case of Figure 1).  This part is shown in black in Figure 1.  Method names are always specified in uppercase.

The requested resource

The second part of the initial request line (shown in red in Figure 1) consists of a forward slash character followed by the path to the requested resource.

As I understand it, for the HEAD and GET methods, the requested resource specifies data that is to be sent by the server to the client.  For the POST method, the requested resource names a script or program that is to be executed on the server to consume the POST data that will be sent later.

The GET method attempts to send the entire requested resource back to the client.  The HEAD method attempts to send only the headers associated with the requested resource to the client.  (I will have more to say about headers later.)

The HTTP version

The third part of the initial request line (shown in blue in Figure 1), specifies the HTTP version in the format shown, (such as HTTP/1.0 or HTTP/1.1).  This part is always in uppercase characters.

The case in point

Referring back to Listing 9, the first line requests that the server execute the HEAD method using HTTP version 1.1, applying that method to a specified resource whose name is blank.  The use of a blank resource (a forward slash followed by a blank character) requests that the server use whichever resource it considers to be the default.

(In the early days of the web, the default resource was often a file named index.html, but that seems to be less the case now.  In many cases, the default resource is the so-called "home page" for the server.)

Because the requested method in Listing 9 is HEAD, the client is requesting that only the headers for the requested resource be delivered and not the entire resource.

The request headers

The initial request line in Listing 9 is followed by two header lines, which in turn are followed by the required blank line (CRLF).

Many different header lines are possible.  For example, Figure 2 shows a request sent by a Firefox browser to http://www.dickbaldwin.com.

GET / HTTP/1.1
Host: www.dickbaldwin.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US;
 rv:1.7.10) Gecko/20050716 Firefox/1.0.6
Accept: text/xml,application/xml,application/xhtml+xml,text
/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: (cookie content deleted by Baldwin)
Figure 2

Captured using the Ethereal program

The material in Figure 2 was captured using the Ethereal program.  (Note that line breaks were manually inserted into Figure 2 to force the material to fit into this narrow publication format.)

I'm not going to try to teach you how to use the Ethereal program.  Rather, I will simply refer you to the Ethereal User's Guide.  There are many ways to view data that has been captured.  As a hint, I will tell you that it is often convenient to save captured data to a text file and then to view the text file using a typical text editor program such as Notepad.  That is how I was able to reproduce the material shown in Figure 2.

Getting back to header lines ...

Header lines are intended to provide information about the request or the response.  Generally, they are formatted as follows:

  • Each header resides on a separate line terminated by a CRLF.
  • The information is provided in a name:value format with the name and the value separated by a colon.
  • The header name is not case sensitive.
  • Any number of space or tab characters may follow the colon.
  • A header may be continued onto the next line by beginning the continuation line with a space or a tab character.

Once again, according to HTTP Made Really Easy:

"HTTP 1.0 defines 16 headers, though none are required.

 HTTP 1.1 defines 46 headers, and one (Host:) is required in requests."

Thus, the first header line in Listing 9 and Figure 2 is the required Host: header line, specifying the name of the server to which the request is directed.

Disabling keep-alive

As mentioned earlier, this program does not support the keep-alive feature that is (apparently) the default operating mode for HTTP/1.1 servers.  Therefore, the second header line in Listing 9, beginning with Connection: is a request (or instruction) to the server to close the connection following the response to the request.  This makes it possible for the program to operate in a simple request/response ping-pong mode, which is much less complex than trying to support the keep-alive operating mode.

Supporting keep-alive

The Firefox browser, on the other hand, is happy to support keep-alive.  Figure 2 shows two different header lines associated with the keep-alive feature.  I'm confident that if you are interested, you can find more information about those header lines using Google.

The required blank line

The second header line in Listing 9 is followed by the required blank line consisting of a CRLF.

(Invoking the println method with no parameters produces the correct CRLF byte stream on a Windows system.  I don't know if that is the case for all operating systems.  If not, you can get the same result by invoking the print method and printing the hexadecimal character values 0x0D and 0x0A.)

The captured request

Using Ethereal to capture the conversation (and deleting the non-readable characters involved in the conversation) produces the request text shown in Figure 3.  This is the request that was sent from the client (this program) to the server.  (Figure 3 includes the required blank line.)

HEAD / HTTP/1.1 
Host: www.austincc.edu
Connection: close
Figure 3

The captured response

The server response to the above request is shown in Figure 4.  (A line break was manually inserted into Figure 4 to force the material to fit into this narrow publication format.)

HTTP/1.1 200 OK
Date: Tue, 20 Dec 2005 14:36:21 GMT
Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10-1.
dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
X-Powered-By: PHP/4.3.10-1.dotdeb.0
Connection: close
Content-Type: text/html; charset=iso-8859-1

Figure 4

Perhaps the most interesting parts of the response are the initial response line and the header line that begins with the word Connection, both of which are highlighted in boldface in Figure 4.

HTTP/1.1 200 OK

According to HTTP Made Really Easy, the status code value of 200 in the initial response line tells us:

"The request succeeded, and the resulting resource (e.g. file or script output) is returned in the message body."

Of course, since this is a HEAD request, there is no output other than the header lines.  Had the request been a GET request, however, there would most likely have been quite a lot of output.

Connection: close

The header line in the response in Figure 4 that begins with the word Connection tells us that the server is acknowledging our special request and that the connection will be closed at the end of the response.  This tells us that in order to send another request to the server, we will need to get another socket object connected to the server.

Returning to the doIt method ...

The next statement in the doIt method, shown in Listing 10, invokes the local method named print to display up to fifteen lines of server response.  The print method also gets and returns information about the closing of the connection.  That information is saved in the variable named closedFlag to be used later.

    closedFlag = print(15,socketWrapper.inputStream);

Listing 10

The print method

The print method is shown in its entirety in Listing 11.  The purpose of this method is to get and display a specified number of lines of the server response.  In the process, the method checks to determine if the server closed the connection at the end of its response.  The method returns a boolean value indicating whether or not the server closed the connection.

  boolean print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "dummy";
    boolean closedFlag = false;
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);

        if(line.contains("Connection: close")){
          //The server closed the connection at the end of
          // the response.
          closedFlag = true;
        }//end if
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return closedFlag;
    
  }//end local method named print

Listing 11

The print method is straightforward and shouldn't require further explanation.

Returning once more to the doIt method ...

Listing 12 begins by displaying a message.

    System.out.println("\n**Send another HEAD command.**");
    
    if(closedFlag){
      System.out.println("**Must get new socket**");
      socketWrapper = getSocket(server);
    }else{
      System.out.println(
                 "**Synchronization error, terminating**");
      System.exit(1);
    }//end else

Listing 12

Then the code in Listing 12 confirms that the connection was properly closed by the server at the end of the previous response.

  • If the connection was properly closed, the code in Listing 12 gets a new Socket connection with the server.
  • If the connection was not closed, the code in Listing 12 displays an error message and terminates the program.

The comments at the beginning of Listing 49 show the result of both possibilities.

Invoke the HEAD method for a different resource

Listing 13 invokes the HEAD method on the server to request that the headers be provided for a different resource.

    socketWrapper.outputStream.println(
                      "HEAD /baldwin/index.html HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    socketWrapper.outputStream.println(
                                      "User-Agent: Dummy");
    //Ask the server to close the connection following the 
    // response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here
    socketWrapper.outputStream.println();

    //Print 15 lines of server response data.
    print(15,socketWrapper.inputStream);

    System.out.println("\n**End program output**");

Listing 13

In this case, the resource is not blank.  In other words, the client program is not requesting the default resource from the server.  Rather the client program is requesting the headers for the file specified by the following path:

/baldwin/index.html

The captured request

The captured request information for this part of the conversation is shown in Figure 5.

HEAD /baldwin/index.html HTTP/1.1
Host: www.austincc.edu
User-Agent: Dummy
Connection: close
Figure 5

Note that I added a dummy header in Figure 5 that I didn't put in Figure 3.  As far as I know, unlike the other two header lines in Figure 5, the User-Agent header line is for information purposes only, and the server will take no special action based on that header line.  Thus, it is really of no consequence, except perhaps that it may irritate the webmaster at the server end if she is keeping statistics on the different clients that communicate with her server.

What about the other two header lines?

You already know about the purpose and the result of the Connection header line, so what about the Host header line?  As I indicated earlier, this is the only header line that is required for HTTP/1.1.  (No header lines are required for HTTP/1.0.)

Figure 6 shows the response headers that would be received from this particular server if the Host header line is omitted.  The most important item in Figure 6 is the boldface first line indicating unhappiness on the part of the server.

HTTP/1.1 400 Bad Request
Date: Tue, 20 Dec 2005 15:36:38 GMT
Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10-1.
dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
Connection: close
Content-Type: text/html; charset=iso-8859-1
Figure 6

(Once again, a line break was manually inserted into Figure 6 to force the material to fit into this narrow publication format.)

A captured response to a correct request

Figure 7 shows the server response to the correct client request shown in Figure 5.

HTTP/1.1 200 OK
Date: Tue, 20 Dec 2005 14:36:21 GMT
Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10-1.
dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
Last-Modified: Sat, 30 Jul 2005 00:31:16 GMT
ETag: "16f8002-eda-42eaca54"
Accept-Ranges: bytes
Content-Length: 3802
Connection: close
Content-Type: text/html; charset=iso-8859-1
Figure 7

Additional information

Figure 7 shows several information items (highlighted in boldface) regarding this specific requested resource that were not included in the response of Figure 4 when the request was for the default resource.  Often, this information, (particularly the date on which the resource was last modified and possibly the length of the resource), is what a client that invokes the HEAD method is looking for.  For example, the client may compare that date with the date on a local copy of the resource and then invoke the GET method to download the entire resource if the version on the server is newer than the local copy.

Close the socket

Although this is probably unnecessary and redundant, the code in Listing 14 closes the socket before ending the program just in case it hasn't been closed by the server.

    try{
      //Close the socket
      socketWrapper.socket.close();
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt

Listing 14

And that brings us to the end of the program named Sockets15.  Hopefully, you will have learned something new about HTTP communications in this section.  The discussions of the other programs in the following sections will build upon this material.

The Sockets14 Class

A complete listing of this class is provided in Listing 50 near the end of the lesson.  The purpose of the class is to illustrate basic authentication.

In order to avoid dealing with the complexities of keep-alive, the program asks the server to close the connection at the end of each response.

The class was tested and determined to authenticate properly with J2SE 5.0, WinXP, and a very recent Linksys WRT54G wireless router that supports WEP, WPA, and WPA2 encryption.

(This version of the router can be identified by the inclusion of SES on the front panel.)

The class was also determined to authenticate properly with a recent Linksys WRT54G wireless router that supports WEP and WPA encryption, but does not support WPA2 encryption.

(Note that both of these wireless routers have the same model number, WRT54G.)

The class definition for Sockets14

The class definition begins in Listing 15.

class Sockets14{

  final String server = "192.168.1.1";

  StateWrapper stateWrapper;
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets14().doIt();
  }//end main
  //-----------------------------------------------------//

Listing 15

Note that in this case, the server is specified by its IP address rather than by a domain name.  (I discuss the difference between the two in some of the lessons referred to in the References section.)

Listing 15 also shows the main method, which instantiates an object of the class and invokes the method named doIt on that object.

The doIt method

This doIt method will attempt a login on the administrator panel for the router using the administrator's user name and password.  The doIt method begins in Listing 16.

  void doIt(){

    final String adminUserPwd = "OmFkbWlu";

Listing 16

The constant named adminUserPwd is initialized to contain the administrator password expressed in base64 format.  The use of base64 encoding is a requirement of standard HTTP Basic Authentication.

(See my earlier lesson entitled Understanding Base64 Data for an explanation of base64 encoding.  Also see http://www.motobit.com/util/base64-decoder-encoder.asp for an online base64 encoder/decoder that you can use to easily encode and decode string data.)

What does OmFkbWlu mean?

The base64 value of OmFkbWlu given in Listing 16 is the value required for the default administrator username and password for a Linksys WRT54G wireless router.

(For example, go to http://www.motobit.com/util/base64-decoder-encoder.asp and use the decoder that you will find there to decode the base64 value of OmFkbWlu and see what you get.  You should get :admin.)

For a Linksys WRT54G wireless router, the default username is blank and the default password is admin.  Basic authentication requires the base64 value to be the encoded version of the concatenation of the username and the password separated by a colon.  Since the default username is blank and the default password is admin, the base64 value given in Listing 16 is the encoded version of :admin(Note the leading colon.)

Get a Socket object

The statement in Listing 17 invokes the getSocket method to get a Socket object connected to the specified server on port 80.

    socketWrapper = getSocket(server);

Listing 17

This is the same getSocket method that was discussed earlier.  The statement in Listing 17 also gets input and output streams that can be used to communicate with the server.

Send initial GET command

The code in Listing 18 displays a message on the screen and then invokes the GET method on the server, requesting that the default resource be downloaded in its entirety.

    System.out.println("**Send initial GET command**");

    socketWrapper.outputStream.println("GET / HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    socketWrapper.outputStream.println(
                          "User-Agent: Dummy Header Line");
    //Ask the server to close the connection following
    // the response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here.
    socketWrapper.outputStream.println();

Listing 18

(Recall that the main difference between the HEAD method and the GET method is that invocation of the HEAD method requests only the headers for the specified resource whereas invocation of the GET method requests the entire specified resource.)

Three header lines

The statement that invokes the GET method in Listing 18 is followed by the same three header lines and the blank line that you saw in Listing 13 (except that the dummy value given to the User-Agent is different.).

(If the default resource for the server were not a protected resource, the server would respond by sending that resource to the client.  However, that is not the case here.  The administrator panel for the Linksys router is a protected resource.)

The captured request

The code in Listing 18 resulted in the request shown in Figure 8, as captured by the Ethereal program.

GET / HTTP/1.1 
Host: 192.168.1.1
User-Agent: Dummy Header Line
Connection: close
Figure 8

Display the response

Listing 19 invokes the print method to display up to fifteen lines of the server response.

    stateWrapper = print(15,socketWrapper.inputStream);

Listing 19

The print method in this class is very similar to the version that was discussed earlier.  The only real difference between the two is that the earlier version monitored the response lines to determine if the server closed the connection following the response.  This version does that also.  In addition, this version also monitors the response lines to determine if authentication is required.

New code in the print method

You can view the entire print method in Listing 50.  Listing 20 shows the code in the print method that monitors for the requirement to authenticate the user.

//This is code from the print method, not the doIt method.

        if(line.contains("401")){
          //Authentication is required
          stateWrapper.authFlag = true;
        }//end if

Listing 20

As you will see shortly, if the server responds with a status code value of 401, that means that the requested resource is protected and the user must provide proper authentication credentials in order to gain access to the resource.  The code in Listing 20 simply monitors for the substring 401 in the response.

(Upon reflection during the writing of this lesson, I realized that it would be possible for the server to deliver an unprotected resource containing the substring 401.  That would cause the program to falsely conclude that authentication is required.  I should have confirmed that the line containing the substring 401 was the first line of the response before reaching that conclusion.  This is a bug in the program.)

Another difference

Another difference between this version of the print method and the earlier version has to do with how the results are returned.  In the earlier version, only one return value was required.  In this version, two return values are required:

  • One value for authentication information
  • One value for connection closing status

Therefore, in this version, the two values are encapsulated in an object of the simple StateWrapper class and a reference to that object is returned.

The response

Figure 9 shows the text returned by the server in the Linksys WRT54G wireless router when a request is made for the default resource as shown in Figure 8.

HTTP/1.0 401 Unauthorized
Server: httpd
Date: Thu, 01 Jan 1970 00:00:29 GMT
WWW-Authenticate: Basic realm="WRT54G"
Content-Type: text/html
Connection: close

<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>
<BODY BGCOLOR="#cc9999"><H4>401 Unauthorized</H4>
Authorization required.
</BODY></HTML>
Figure 9

HTTP/1.0 401 Unauthorized

Regarding the 401 Unauthorized response, one resource has this to say:

"The request requires user authentication. The response must include a WWW-Authenticate header field ... containing a challenge applicable to the requested resource. The client may repeat the request with a suitable Authorization header field ..."

Thus, the inclusion of the 401 status code indicates that the user must be authenticated before gaining access to the protected resource.  This would cause a typical browser to display a form into which the user could enter the username and the password.  The form would also provide a Submit button, by which the user could cause the request to be retransmitted to the server with authentication credentials included in the request.

What about the Basic realm?

Here is some of what the folks at Apache have to say about the system in general and the realm in particular:

"When a ... resource has been protected using basic authentication, Apache sends a 401 Authentication Required header with the response ... to notify the client that user credentials must be supplied ...

... the client's browser, ... will ask the user to supply a username and password to be sent to the server. ... If the username is in the approved list, and if the password supplied is correct, the resource will be returned to the client.

... each request will be treated in the same way, even though they are from the same client. ... every resource which is requested ... will have to supply authentication credentials ... to receive the resource.

... the browser takes care of the details ... you only have to type in your username and password one time per browser session...

... other information will be passed back to the client. ... [the server] sends a name which is ... the protected area of the web site. This is called the realm.... The client browser caches the username and password that you supplied, and stores it along with the authentication realm ... if other resources are requested from the same realm, the same username and password can be returned to authenticate that request without requiring the user to type them in again."

A very important conclusion

This leads us to an important conclusion.  Since the Linksys WRT54G router requires authentication simply to view the default page, this tells us that we will be required to send authentication credentials with every request that we send to the Linksys router for any resource whatsoever.

(Later you will see that this is not the case for a Belkin 54G router.  The server in the Belkin 54G router will deliver the default page, containing lots of routine information, without a requirement for authentication.  It is only when the user needs to make changes to the router configuration that authentication is required.)

A new request with authentication

On the strength of web research and packet capture using the Ethereal program, I concluded that in order to access the default resource on the Linksys WRT54G router, I would need to send the request shown in the captured data in Figure 10.

GET / HTTP/1.1 
Host: 192.168.1.1
User-Agent: Dummy
Authorization: Basic OmFkbWlu
Connection: close
Figure 10

Note the inclusion of the boldface header that begins with the word Authorization, followed by a space and the word Basic, followed in turn by another space and the base64-encoded user name and password.

Correct Authentication Request Header?

According to this web resource,

"Upon receipt of the server's 401 response, your web browser prompts you for the username and password associated with that realm. The Authentication header of your browser's follow-up request again contains the token "Basic" and the base64-encoded concatenation of the username, a colon, and the password.

Authorization: Basic [base64-username:password]

The server base64-decodes the credentials and compares them against his username-password database. If it finds a match, you are in."

The response

Sending the request with the authentication credentials shown in Figure 10 caused the server to respond as shown in Figure 11.

1: HTTP/1.0 200 Ok
2: Server: httpd
3: Date: Thu, 01 Jan 1970 00:00:33 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
10:
11: <!--
12: *******************************************************
13: *   Copyright 2003, CyberTAN  Inc.  All Rights Reserved
14: *******************************************************
15:
Figure 11

Figure 11 shows only the first fifteen lines of the response with the line numbers being inserted by the program.  Also, the long lines were truncated to force the material to fit into this narrow publication format.

As you can see, lines 11 through 15 contain the beginning of the HTML code describing the default home page for the Linksys router.

What about HTTP/1.0 200 Ok?

According to this web resource,

200 OK

The request has succeeded. The information returned with the response is dependent on the method used in the request, as follows:

GET an entity corresponding to the requested resource is sent in the response;

HEAD the response must only contain the header information and no Entity-Body;

POST an entity describing or containing the result of the action.

Since the GET method was used in Figure 10, and a specific resource was not specified, the information returned with the response was an entity corresponding to the default home page for the server.  If you were to view this page with a regular browser, you would see that it is the main page used by the administrator for setting configuration parameters for the wireless router.

The remaining code

Listing 21 shows the remaining code in the doIt method.  Note in particular the boldface code that causes the authentication credentials to be sent with a new request for the default server resource.

    if(stateWrapper.authFlag){
      System.out.println("**Authorization required**");

      if(stateWrapper.closedFlag){
        System.out.println("**Must get new socket**");
        socketWrapper = getSocket(server);
      }else{
        System.out.println(
                 "**Synchronization error, terminating**");
        System.exit(1);
      }//end else
     
      System.out.println("\n**Re-send the GET command "
                               + "with authentication.**");
      socketWrapper.outputStream.println("GET / HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                                      "User-Agent: Dummy");
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);
      //Ask the server to close the connection following
      // the response.
      socketWrapper.outputStream.println(
                                      "Connection: close");
      //The following CRLF is required here
      socketWrapper.outputStream.println();

      //Print 15 lines of server response data.
      print(15,socketWrapper.inputStream);
    }//end if on authFlag

    System.out.println("\n**End program output**");
      
    try{
      //Close the socket
      socketWrapper.socket.close();
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt

Listing 21

Knowing what you know now, there should be no mystery as to how the code shown in Listing 21 produced the results shown in Figure 10 and Figure 11.

All is not perfect however

Although this program properly authenticates with the server for two different relatively new versions of the Linksys WRT54G wireless router, despite many hours spent doing web research and pouring over Ethereal capture dumps, I was unable to cause the program to properly authenticate with two other Linksys routers.

One of the problem routers was an old Linksys BEFW11S4 802.11B wireless router.  The other problem router was an old Linksys BEFSR41Cable router.  The same symptoms were exhibited by both of the older Linksys routers.  They wouldn't close the connection following the 401 message response even when requested to do so.  Then they ignored a following GET command containing the authentication request header.

Old routers work OK with a standard browser

Both of the older routers authenticate properly when operated using a Firefox browser or an Internet Explorer browser.  I finally gave up and concluded that there is something that the browsers know how to do to make the older routers happy that I was unable to identify from either the web research or the Ethereal dumps.

You may need to customize the programs

The important message here is that unless you are using a Linksys WRT54G router, you shouldn't necessarily expect the programs in this lesson to work perfectly correctly with your router.  Depending on the specifics of your router, you may have to download the Ethereal program (or some similar program) and examine the dumps of captured conversations between your browser and your router to determine how to customize these programs to make them compatible with your router.

And that is probably more than you ever wanted to know about HTTP basic authentication.

The Sockets10 Class

A complete listing of this class is provided in Listing 51 near the end of this lesson.

This class can be used to connect to a Linksys WRT54G wireless router (with or without SES/WPA2) to change the WEP key.

The program installs a ten-character hexadecimal WEP key in the router.  It would be a simple matter to modify the program to cause it to install a 26-character WEP key instead.

Note that for simplicity, this class uses some deprecated methods.  Note also that for simplicity, it asks the server to forego the use of keep-alive.

The new key is displayed on the screen along with some other information.  When the program terminates successfully, the new WEP key shown on the screen has been installed in the wireless router.

This class was tested with J2SE 5.0, WinXP, and two different versions of the Linksys WRT54G wireless router.

What have we learned so far?

We have learned that we can disable keep-alive operation on the server by including the following line in the request headers:

Connection: close

Authentication

We have learned that we can satisfy the authentication requirements of the Linksys server by including the following line in the request headers:

Authorization: Basic OmFkbWlu

where OmFkbWlu is the base64 encoded version of the concatenated username and password with the two being separated by a colon.  The default user name and password are :admin where the default username is blank.

(Hopefully you have changed the default user name and password on your wireless router.  You can encode your username and password at http://www.motobit.com/util/base64-decoder-encoder.asp and use the result to replace the encoded value shown above.)

Each individual request must be authenticated

We have learned that with basic authentication, each request must be separately authenticated because the HTTP protocol is a stateless protocol.  As a result, if we already know that the resource that we are requesting is protected using basic authentication, then there is no need for us to go through the "401 Unauthorized" drill just to be notified by the server that the resource is protected.  Rather, we can skip all of that and start the process by requesting the resource of interest and including the authentication credentials in the request headers.

The resource of interest

By paying careful attention to what was going on when I manually set the WEP password using a browser, and by using the Ethereal program to confirm my observations, I determined that the resource of interest for setting the WEP password in a Linksys WRT54G wireless router is:

/apply.cgi

Must invoke the POST method

Also, by using the Ethereal program to monitor the conversation when I manually set the WEP key, I determined that the method invocation that is required to set the key is:

POST /apply.cgi HTTP/1.1

The body of the POST message

Finally, by using the Ethereal program to intercept, capture, and analyze the conversation (and this is where the Ethereal program is indispensable), I concluded that the syntax of the body of the POST message used to set the WEP key is as shown in Figure 12.  (Line breaks were manually inserted into Figure 12 to force the material to fit into this narrow publication format.)

submit_button=WL_WPATable&change_action=&submit_type=&actio
n=Apply&security_mode_last=&wl_wep_last=&security_mode2=wep
&wl_key=1&wl_WEP_key=&wl_wep=restricted&wl_wep_bit=64&wl_pa
ssphrase=&generateButton=Null&wl_key1=a4cb6e0679&wl_key2=&w
l_key3=&wl_key4=
Figure 12

The new WEP key is shown in boldface in Figure 12.

Figuring it all out

Perhaps it should have been possible for me to analyze the source code for the web page containing the form that is used to change the WEP key and to determine the format shown in Figure 12 without the help of the Ethereal program.  However, I have neither the patience nor the HTML skills to pull that off.

Unless you have such patience and HTML skills, it will probably be necessary for you to use a similar program for analyzing your router if it is not a Linksys WRT54G router.

Writing the program

Once I realized that I knew all of the things that I have discussed above, it was a simple matter to write the Java program to automatically change the WEP key.  A discussion of that program follows.

The class definition

The beginning of the class definition is shown in Listing 22.

class Sockets10{
  
  String server = "192.168.1.1";
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets10().doIt();
  }//end main
  //-----------------------------------------------------//

Listing 22

There is nothing new in Listing 22.  It shows the declaration of a couple of instance variables and the main method. 

The doIt method

The doIt method begins in Listing 23.

  void doIt(){
    String wepKey = getWepKey();

    System.out.println("New WEP key: " + wepKey);

Listing 23

Get and display the new WEP key

Listing 23 invokes the method named getWepKey to get a new ten-character hexadecimal WEP key based on the current date and a random number generator.

The method named getWepKey

This method generates a ten-character hexadecimal key based on the current date and a random number generator.

The algorithm will generate the same key every time it is run on a given date, but will generate different keys on different dates.

You can easily modify this algorithm to come up with a different recipe for the key. 

Also, you can easily modify this method to cause it to generate a 26-character hexadecimal key instead of a ten-character key.

(If you do that, you will need to make some changes to the body of the POST message in the doIt method to cause that message to accommodate 26-character keys.)

Note that for simplicity involving dates, this method uses some deprecated methods.

The getWepKey method begins in Listing 24.

  String getWepKey(){
    final int bias = 12345;//a secret value
    final int lim = 8;//another secret value

Listing 24

Two secret values

The code in Listing 24 declares and initializes the secret values for two constants that impact the overall security of the WEP key that is generated.  If you use this class for your own purposes, you should change these two values.

Get the current date

Listing 25 begins by instantiating a Date object that encapsulates the current date and time according to the system clock.

    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);

Listing 25

For this algorithm, we want to use the current date but we don't want to use the current time.  In particular, if this algorithm is executed on different computers on the same date, we want the algorithm to produce the same key on both machines.  We don't want that result to be influenced by the fact that the algorithm is executed at different times within the same date on the two machines.

The code in Listing 25 manipulates the Date object, ending up with a Date object in which the time has been set to zero.  Thus, the object contains the date only.

Instantiate a pseudorandom number generator

An instance of the Random class is used to generate a stream of pseudorandom numbers.  A seed is passed to the constructor for the class when the object is instantiated.  If two instances of Random are created with the same seed, and the same sequence of method calls is made on each object, they will generate and return identical sequences of numbers.  Conversely, if the two instances are created with different seeds, they will generate and return different sequences of numbers.

An object of the class Random

Listing 26 instantiates an object of the class Random using a seed that is based on the current date as modified by a secret value named bias known only to you.

    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);

Listing 26

Unless an attacker knows the secret value, the probability is extremely low that he will be able to instantiate a Random object that generates and returns the same sequence of numbers as your object.

On the other hand, if you install the algorithm using the same secret value on two or more machines and ascertain that the system clocks are showing the same dates on all of the machines, they will all produce the same sequence of numbers on any given date.

Advance the random sequence

Listing 27 advances through the random sequence of numbers by discarding the first n numbers, where n is another secret value named lim known only to you.

    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt();
    }//end for loop

Listing 27

Once again, if you install the algorithm on two or more machines as described above, and use the same secret value for n on each of the machines, the same quantity of random numbers will be discarded on all machines, causing all of the machines to be in synchronism once the numbers are discarded.

(The use of two secret values is probably redundant.  It may be just as secure to use only one secret value.  It just feels better to use two of them.)

Get a random number

Finally, Listing 28 gets the actual random number that will be used to generate the hexadecimal key.

    long val = randomGen.nextLong();

Listing 28

The random number will be a 64-bit long value.  However, it is possible that it may have leading bits with a value of zero.  This is undesirable.  Because the long value is maintained in two's complement format, one way to guarantee that there are no leading zero bits is to force the value to be a negative value.  In this case, the first bit will always have a value of 1.

Make it negative

Listing 29 forces the long value to be negative, thus guaranteeing that there is a 1 in the first bit position.

    if(val == 0)val = -1;
    if(val > 0) val = -val;

Listing 29

Extract a ten-character hexadecimal substring

Listing 30 converts the long value to a String of sixteen hexadecimal characters, and then extracts the first ten characters from the string.

    String stringInProgress = 
                     Long.toHexString(val).substring(0,10);

    return stringInProgress;
    
  }//end getWepKey

Listing 30

(This is another place where you could change the recipe.  For example, you could extract and return the middle ten characters instead of the first ten characters.)

Listing 30 returns the hexadecimal string to be used as the new WEP key. 

Listing 30 also signals the end of the getWepKey method.

Just how secure is this scheme?

My concept is to have this program running automatically on a scheduled basis in the middle of the night on a machine that is connected to the router by a cable.

(Never run this program on a machine that has a wireless connection to the router.  If you do, you will simply be broadcasting the new WEP key.  Keep in mind however, that the router has a built-in wireless access point, and that raises the question that I will pose below.)

Running the program in the automatic mode will cause a new WEP key to be installed on the wireless router each time the program is run.

Once again, just how secure is this scheme?

With regard to the question regarding the security of the scheme, I don't have the expertise to answer that question.  (See the earlier disclaimer.)  I will simply teach you how to write the program.  It will be up to you to do the necessary research to decide for yourself whether or not you should use it.

The real question is the following, and it is a question for which I don't know the answer.  If you know the answer, I will be interested in hearing from you via email. 

The real question is, if you use a computer connected by cable to a router that has a built-in wireless access point, is it possible for the communications between that computer and the router that are conducted via cable to be intercepted by another computer on a wireless basis using a packet sniffing program?

If the answer to that question is yes, then the scheme isn't secure at all, and you probably shouldn't use it.

A local program on each machine

My scheme also has a simplified (strictly local) version (see Sockets11a, for example) of the program with the same algorithm installed on all of the other machines in the wireless network.  When the users of those machines come to work in the morning, they will run the local version of the program on their machines to learn the new encryption key for the day.  Then they will connect to the wireless router by typing in the new key on their machine when the encryption key is requested.

(If they were connected to the router on a wireless basis when the key was changed, they should have been disconnected automatically by the router.  In other words, as far as the router is concerned, if they were connected using the old key, they are no longer authorized to maintain a connection unless they can provide the new encryption key.)

Alternatives to a local program

Encryption key management is one of the most difficult aspects of security.  If having a local version of the program on each machine in the network is a concern (and it might be in a small office environment but probably not in a home), there are a couple of other alternatives that might work.

Using Public Key Cryptography

One alternative would be to update the program to cause it to use Public Key Cryptography and to send an email message with an encrypted version of the new wireless router encryption key to each employee each time it is changed.  I believe that this would be a pretty good approach.  With this approach, the two secret values could be changed frequently by the network administrator without her having to install a recompiled version of the program on each machine.  Then, even if an ex-employee were to know the algorithm, he wouldn't know the secret values necessary to use it.

Printing a list of encryption keys

A second alternative would be to write a simple program containing the same algorithm by which the network administrator could print the key for every day of the month in advance and distribute a copy to each employee at the beginning of the month.  Of course, those employees would need to keep the list locked in their desks.

(Somehow this sounds like the worst approach of all.  Some employees would probably post the list on the side of a file cabinet in their office using refrigerator magnets to keep it in place.)

I am not a network security expert

As I mentioned early in this lesson, I am not a security expert.  However, this scheme looks to me like a reasonable way to easily change the encryption key on a daily basis to keep the attackers off balance.  Unless the attackers can crack the encryption key within 24 hours, this should prevent them from being able to break into the wireless network.

And then I learn the truth ...

I will probably receive a flood of email messages after publication of this lesson showing me that this scheme can easily be cracked by a ten-year old kid with a hand calculator.

More on this scheme later, let's get back to the program

Listing 31 picks back up in the doIt method where the new WEP key has been generated and it is time to install it in the wireless router.

    final String adminUserPwd = "OmFkbWlu";

Listing 31

Listing 31 declares and initializes the constant that contains the administrator username and password in base64 format.  There's nothing new here.

Get a Socket object

Listing 32 gets a new Socket object connected to the server on port 80.

    try{
      socketWrapper = getSocket(server);
      
      System.out.println("POST /apply.cgi HTTP/1.1");

Listing 32

Listing 32 also displays a message to the effect that a POST is about ready to begin.

Send the POST request

Listing 33 sends the POST request followed by three header lines and a blank line as required.

      socketWrapper.outputStream.println(
                               "POST /apply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);
      socketWrapper.outputStream.println(
                                    "Content-Length: 252");
      //The following CRLF is required here.
      socketWrapper.outputStream.println();

Listing 33

The new material here is the format of the POST request and the header line that specifies the length of the message body that is to follow, shown in boldface in Listing 33.

(I believe that I determined experimentally that the header line that specifies the length of the message body is required.  This is another place where the Ethereal program comes in extremely handy.)

Post the new WEP key

Listing 34 posts the new WEP key on the server just as though a human user filled in the form and clicked the Submit button.

This syntax was captured from the actual data stream produced by a browser using the Ethereal program.

      socketWrapper.outputStream.print(
        "submit_button=WL_WPATable"
        + "&change_action="
        + "&submit_type="
        + "&action=Apply"
        + "&security_mode_last="
        + "&wl_wep_last="
        + "&security_mode2=wep"//WEP security mode
        + "&wl_key=1"//Default is key 1
        + "&wl_WEP_key="
        + "&wl_wep=restricted"
        + "&wl_wep_bit=64"//64-bit WEP key
        + "&wl_passphrase="//Passphrase is blank
        + "&generateButton=Null"//Passphrase is not used
        + "&wl_key1=" + wepKey //Value for Key 1
        + "&wl_key2="//Blank key
        + "&wl_key3="//Blank key
        + "&wl_key4=");//Blank key
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);

Listing 34

A LF without a CR

Note that this statement calls the print method instead of the println method, and then adds a LF without a CR at the end.

(In doing the backup research for this lesson, I probably spent more time trying to discover that the POST message body must end with a LF instead of a CRLF than on any other topic.)

The remainder of the doIt method

The remaining code in the doIt method is shown in Listing 35.

      //Display fifteen lines of server response data.
      print(15,socketWrapper.inputStream);

      System.out.println("End POST operation");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt

Listing 35

There is nothing new in Listing 35.

Upgrade to a WPA or WPA2 encryption key

Now that you know how to write a Java program that will automatically generate and install a new ten-character hexadecimal WEP key on a Linksys WRT54G wireless router, it is time to learn how to generate and install a WPA or WPA2 encryption key on the router for improved security.

(I will let you research the difference between WEP, WPA, and WPA2 encryption keys and the security differences among them on your own.)

Remember, however, that before you can use either a WPA key or a WPA2 key on the wireless router, all of the computers on your wireless network must be able to support such a key.  For example, I still have a machine on my home network running Windows 98.  Although Windows 98 does support the use of a WEP key, as near as I can tell, Windows 98 won't support the use of a WPA key.  As a result, I'm still running my home network using a WEP key (but I change it every night on a random basis).

The Sockets11 Class

A complete listing of this class is provided in Listing 52 near the end of the lesson.

This class can be used to connect to a Linksys WRT54G wireless router (with or without SES/WPA2) to change the WPA shared key.

The router requires that the WPA key be 63 characters or less.  This class generates a fifteen-character WPA key based on the current date and a random number generator.  The class could be easily modified to extend the length of the key up to 63 characters if you choose to do so.

Different behavior for different versions

When this class is used with an older Linksys router that doesn't support WPA2 but does support WPA, the class sets the configuration to WPA Pre-Shared Key and TKIP

When used with a newer router that supports WPA2, the class sets the configuration to WPA2 Personal and TKIP+AES.

For simplicity, this class uses some deprecated methods.

The new key is displayed

The new key is displayed on the screen.  When the program terminates successfully, the new WPA key shown on the screen has been installed in the wireless router.

The class was tested using J2SE 5.0, WinXP, and two different versions of a Linksys WRT54G wireless router, one that supports WPA2 and one that doesn't.

A complete listing of the class

As mentioned above, a complete listing of the class is provided in Listing 52.  This class is very similar to the class that I explained under The Sockets10 Class.  Therefore, I won't bore you by repeating the explanation for code that is essentially the same.  Rather, I will highlight the code that is different between the two classes.

The class definition

The class definition begins in Listing 36.  The first new code occurs at the point where the method named getWpaKey is called to generate and return the WPA key.

class Sockets11{
  
  final String server = "192.168.1.1";
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets11().doIt();
  }//end main
  //-----------------------------------------------------//

  //This is the main processing method in the class.  
  void doIt(){
    //Get a fifteen-character key based on the date
    // and a random number generator.
    String wpaSharedKey = getWpaKey();
    System.out.println("New key: " + wpaSharedKey);

Listing 36

The method named getWpaKey

The method named getWpaKey begins in Listing 37.  This method generates a 15-character key in the form ccc.ccc.ccc.ccc based on the current date and a random number generator.

The characters range from the character 0 (ASCII value 48) through the character z (ASCII value 122) inclusive.

The algorithm will generate the same key every time that it is run on a given date, but will generate different keys on different dates.

You can easily modify this algorithm to come up with a different recipe for the key, or a longer key using the same general recipe, or both.

Note that for simplicity, this method uses some deprecated methods in the handling of dates.

The method code

The getWpaKey method begins in Listing 37.

  String getWpaKey(){
    int bias = 12345;//a secret value
    int lim = 8;//another secret value
    
    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);
    
    //Instantiate a random number generator seeded by the
    // date and the first secret value given above.
    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);
    
    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt(123);
    }//end for loop

Listing 37

The code in Listing 37 is essentially the same as the code that I explained earlier beginning in Listing 24.

Construct sequence of twelve characters

Listing 38 constructs a sequence of twelve characters using sequential random values between 48 and 122 inclusive.

    StringBuffer stringInProgress = new StringBuffer();
    while(stringInProgress.length() < 12){
      int val = randomGen.nextInt(123);
      if(val > 47){
        stringInProgress.append((char)val);
      }//end if
    }//end while loop

Listing 38

As mentioned earlier, the character values represent the characters from 0 through z when viewed according to an ASCII collating sequence.

Insert periods every third character

This method assumes that a user may be required to manually enter the WPA key when setting up a wireless connection on a client computer.  To make this task easier, Listing 39 inserts a period every third character.

    stringInProgress.insert(9,'.');
    stringInProgress.insert(6,'.');
    stringInProgress.insert(3,'.');
    
    return stringInProgress.toString();
    
  }//end getWpaKey

Listing 39

(In most cases, it should be possible for the user to copy the key and paste it into the form used to set up the wireless connection.  In those cases, the code to insert the periods should be eliminated, and the length of the key should be significantly increased for improved security.)

Getting back to the doIt method ...

The code in Listing 40 is essentially the same as the code that I explained in conjunction with the class named Sockets10.

    //The following constant contains the administrator
    // password expressed in base64.
    // See http://www.motobit.com/util/base64-decoder
    // -encoder.asp for an online base64 encoder/decoder.
    //The following value is for the default administrator
    // username and password for a Linkys wireless router
    // where the user name is blank and the password is
    // admin. The user name and the password must be
    // separated by a colon.  Since the username is blank,
    // this is the base64 representation of :admin
    final String adminUserPwd = "OmFkbWlu";
    
    try{
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);
      
      //Set up to post the data to the server.
      System.out.println("POST /apply.cgi HTTP/1.1");
      
      socketWrapper.outputStream.println(
                               "POST /apply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);


Listing 40

Set the content length

The code in Listing 41 differs from the code that I explained earlier in the following way.  The code that I explained in conjunction with the class named Sockets10 was based on the assumption of a fixed-length ten-character hexadecimal key. 

(Recall that the choices for the length of a WEP key are exclusively ten characters and 26 characters.  I assumed that you were not likely to modify the program to generate a 26-characteter key.)

      socketWrapper.outputStream.println("Content-Length: "
                          + (183 + wpaSharedKey.length()));
      //The following CRLF is required here.
      socketWrapper.outputStream.println();

Listing 41

The code in Listing 41 assumes that you are very likely to modify the program to increase the key length because it is so easy to do.  Therefore, the code in Listing 41 takes the length of the key into account when setting the content length for the POST message.  Obviously this code could easily be retrofitted into the code for the WEP key in Listing 33.

Post the data to the server

Listing 42 shows the message body for the POST message.  As before, the actual syntax was captured using the Ethereal program. 

      socketWrapper.outputStream.print(
        "submit_button=WL_WPATable"
        + "&change_action="
        + "&submit_type="
        + "&action=Apply"
        + "&security_mode_last="
        + "&wl_wep_last="
        + "&security_mode2=wpa2_personal"//WPA2 Personal
        + "&wl_crypto=tkip%2Baes"//TKIP+AES
        + "&wl_wpa_psk=" + wpaSharedKey
        + "&wl_wpa_gtk_rekey=3600");//Renewal @ 3600 sec
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);

Listing 42

As you can see, the POST message body for the WPA key is somewhat shorter than the POST message body for the WEP key shown in Listing 34.  This is because the Linksys data entry form for a WPA key is simpler than the data entry form for a WEP key.

The remainder of the code

Listing 43 shows the remaining code in the doIt method.  This code is essentially the same as the code that I explained earlier.

      //Display fifteen lines of server response data.
      print(15,socketWrapper.inputStream);

      System.out.println("End POST operation");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt

Listing 43

The Sockets11a Class

Now I am going to describe how you might implement this scheme in a home or small office network under Windows XP Professional.  Before you do so, make certain that you read the disclaimer that I provided earlier.

A simplified local version of the program

Listing 53 provides a class named Sockets11a, which is a simplified version of the class named Sockets11.  The Sockets11a class is designed to compute and display the WPA key for a given date using the same algorithm as in Sockets11.  It has no network connection but runs as a local Java application.

You have two computers on your network

Assume that you have two computers in your home network.  Let's call them Computer A and Computer B.

(This procedure should work for any number of computers on the network provided one of them is connected to the wireless router via a cable.)

Computer A is connected via cable

Computer A is a computer that you have connected to the wireless router using a cable.  The main purpose for keeping Computer A on the network is to use it as a disk backup machine and possibly as a file or print server.  You install the program named Sockets11 on this machine and schedule it to run at 1:00 am each morning.

(See the earlier lesson entitled Consolidating Email using Java, Part 2 to learn how to schedule Java programs to run automatically at preset times under Windows XP.)

Never run the program named Sockets11 on a computer that is connected to the wireless router using a wireless connection.  If you do, you will be broadcasting your new WPA key to anyone who may be listening.

Computer B has a wireless connection

Computer B is a laptop computer, running Windows XP, with a wireless network interface card installed.  You routinely use this computer to communicate with the Internet via your Linksys WRT54G wireless router.  You install the program named Sockets11a on Computer B.

The time sequence

Assume that you successfully used Computer B to communicate with the Internet via the Linksys WRT54G wireless router on Monday.  At 1:00 am on Tuesday morning, the WPA password on the wireless router is automatically changed by the program named Sockets11 running on Computer A.

Assume that you keep Computer B running during the night so that it will perform a complete virus scan.  When you get up on Tuesday morning, Windows XP may still be showing that Computer B is successfully connected to the wireless network.  However, when you attempt to connect to Google, you get a message from the browser telling you that the browser could not find the requested page or words to that effect.  This is because you originally connected to the wireless router on Monday using a WPA key that is no longer valid.

Re-establish the wireless connection

You need to re-establish the wireless connection using the new WPA key.  You can accomplish that by performing the following steps:

  • First run the program named Sockets11a on Computer B and note the key value that is displayed.  Just leave the command-line screen on the desktop because you are going to copy the new key from it and paste it into a Windows text field later.
  • Open the Start menu, select Connect To, and right click on Wireless Network Connection.  Select Properties in the popup menu.  This should open the Wireless Network Connection Properties dialog.
  • Select the Wireless Networks tab.  Remove the wireless network from the list of preferred networks on the basis of the network SSID (name).
  • Click the Add button.  This should expose the Wireless network properties dialog.
  • Enter the network name in the SSID field if it isn't already there.
  • Select WPA-PSK for Network Authentication.
  • Select AES for Data encryption if your router supports WPA2.  Select TKIP if your router doesn't support WPA2.
  • Copy the new network key from the command-line screen and paste it into the two text fields that require the key.
  • Click the OK button to close the Wireless network properties dialog.
  • Click the OK button to close the Wireless Network Connection Properties dialog.  Your wireless network icon in the system tray should now be showing that you have been disconnected from the wireless router.
  • Open the Start menu, select Connect To, and right click on Wireless Network Connection again.  This time select View Available Wireless Networks on the popup menu.  (You can also get there from the wireless icon in the system tray.)
  • Highlight the wireless network associated with your Linksys wireless router and click the Connect button.
  • Paste the new key into the two required text fields on the Wireless Connection Dialog if requested and click the Connect button on that dialog.  After a few seconds, the Wireless Network Dialog as well as the wireless icon in the system tray should show that you are connected to the wireless router.
  • Now go back and confirm that you can successfully connect to Google via the Internet.

Congratulations

If the stingy neighbor in the apartment next door has been trying to crack your WPA key so that he can piggy back onto your Internet connection and avoid having to pay for a connection of his own (or possibly for more malicious reasons) he will now have to start all over because the WPA key has changed.  And it will change once every twenty-four hours if you use these two programs to implement the procedure described above.

Not as complicated as it looks

The above procedure looks complicated, but it is actually quite easy to perform once you get used to it.  In addition, there are several shortcuts that you will probably discover to shorten the procedure.  However, I have described the long procedure that I believe will always work because some shortcuts may work some of the time but not all of the time.

What if you don't have a Linksys WRT54G wireless router?

Then you will simply have to take what you have learned in this lesson and write a version of the program that is compatible with your wireless router.  Once you have done that, the procedure described above should still work just fine with your program.

The Sockets12 Class

Now for something really different.  I don't actually use a Linksys WRT54G wireless router in my home network.  Rather, I use an older Belkin 54G, which, with rebates, was the cheapest one that I could find several years ago when I decided to upgrade my home network to support wireless.

The Belkin router supports both WEP and WPA.  However, as I mentioned earlier, because I still have a computer on my network running Windows 98, I haven't been able to implement the more-secure WPA, because it appears to be incompatible with Windows 98.

The program required to set the encryption key in the Belkin router is much different from the program required to set the encryption key in the Linksys router.

No basic authentication

The biggest difference between the two routers is the mechanism by which the administrator establishes credentials that allow her to modify the router configuration parameters.

When you connect to the Linksys router with your browser, the first and only thing that you see is the basic authentication login screen.  Until you enter an acceptable username and password, you are not allowed to see anything else.

When you connect to the Belkin router with your browser, you immediately see the router's home page that displays a great deal of information about the router configuration.

(That is probably not so good because an attacker who manages to crack your encryption key and connect to your router can learn a great deal about the router configuration even if he doesn't know the administrator password.)

You will need to log in

Near the top of that screen is a note that reads:

"You will need to log in before you can change any settings."

On the left side of the screen is a list of more than twenty options for setting the router configuration.

As mentioned above, a great deal of information is displayed in the center of the screen, including MAC addresses, IP addresses, SSID, etc.

(This is another good reason for trying to make certain that the neighbor can't crack the encryption key.)

Click to Login

Near the very top of the screen are three hyperlinks that read:

Home | Help | Login

When you point to the Login link with your mouse, the browser displays the link as login.html.  In other words, clicking that link will cause the file named login.html to be downloaded and displayed by your browser.

When the login.html file is displayed by your browser, there is a single text field for entry of a password along with a Clear button and a Submit button.  There is no concept of a username relative to authentication on the Belkin router.

Study the HTML source code

If you are willing to study the source code for the HTML page, you can determine that clicking the Submit button will invoke the GET method on the server, requesting the resource named login.cgi, and passing three parameters as a query string.

(However it is much easier to get that information through the use of the Ethereal program.)

Once you have logged in with an acceptable password, you can select any of the options on the left side of the screen, including the option entitled Wireless Security.

The Wireless Security page

The Wireless Security page allows you to select one of the following Security Mode options:

  • Disabled
  • WPA-PSK (no server)
  • 128bit WEP
  • 64bitWEP
  • WPA (with Radius Server)

Depending on the Security Mode that you select, the data entry form will change to become appropriate for the data required for that Security Mode.  The program that I will describe later is based on a Security Mode selection of 64bit WEP.

In addition to the normal data entry fields for WEP security, this page provides a Submit button labeled Apply Changes.

Examine the HTML source code

Once again, by examining the source code for the HTML page, it can be determined that clicking the Submit button will invoke the POST method on the server, requesting the resource named postapply.cgi, and passing a very large number of parameters in the body of the POST message.

(Also, once again, it is much easier to determine this through the use of the Ethereal program, particularly with regard to the format of the body of the POST message.)

The Logout hyperlink

At this point, the Login hyperlink has changed to a Logout hyperlink.  Pointing to the Logout hyperlink with the mouse indicates that the associated resource is named logout.cgi.  Clicking the Logout link returns you to the initial home page.

Login is persistent

Somehow, the router keeps track of the fact that the administrator is logged in and won't allow another login to occur until the first one logs out or the current login expires.

(A configuration parameter that must be set along with the administrator password is a parameter named Login Timeout.)

I don't know how the server keeps track of the fact that the administrator is currently logged in.  Initially I suspected that it was done using cookies, but I didn't write any support for cookies in my program and it still works fine.

The Sockets12 class

The first class that I will explain illustrates the procedure for logging in and logging out of a Belkin 54G router, without any attempt to set the encryption key.  A complete listing of this class is shown in Listing 54.

I'm going to begin by showing you the screen output produced by the program named Sockets12.  That output is shown in Figure 13.

**Login**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 15:32:34 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Login done**

**Logout**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 15:32:39 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Logout done**
**End program output**
Figure 13

HTTP/1.0 200 Ok

To make a long story short, the two server response message lines highlighted in boldface that read HTTP/1.0 200 Ok demonstrate that the program was successful in logging in and then logging out of the server as an administrator.

Much of the code in this class is very similar to code that I have already explained in this lesson.  Therefore, I won't repeat that explanation.

The class definition

The class definition for the class named Sockets12 begins in Listing 44.

class Sockets12{
  
  String server = "192.168.2.1";
  SocketWrapper socketWrapper;
  //Administrator password.  Note that this is not
  // expressed in Base64 as is the case with basic
  // authentication.
  final String adminUserPwd = "admin";
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets12().doIt();
  }//end main
  //-----------------------------------------------------//

Listing 44

The only thing that is new in Listing 44 is the fact that this login methodology does not require the administrator password to be expressed in base64 as is the case with HTTP basic authentication.

The doIt method

The method named doIt begins in Listing 45.

  void doIt(){

    try{
      System.out.println("\n**Login**");
      
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);

      //Send the login command
      socketWrapper.outputStream.println("GET /login.cgi?"
        + "page=login"
        + "&logout=2"
        + "&pws=" + adminUserPwd + " HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

Listing 45

Login involves the GET method

The thing that is new about Listing 45 is that the actual login occurs as a result of invoking the GET method on the server, requesting a resource named login.cgi, and passing three parameters to the resource as a query string.

I was able to capture the syntax for this request using the Ethereal program.  Given my limited knowledge of HTML, I would have been hard pressed to figure it out otherwise.

Display the response

Listing 46 invokes the local print method to get and to display up to nine lines of server response data.

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Login done**");

Listing 46

This produced the top half of the output shown in Figure 13 with the line numbers being inserted by the print method.

The logout code

Listing 47 shows the code that is used to logout from the router.

      //Now log out from the router.

      System.out.println("\n**Logout**");
      
      //Get a new Socket object and I/O streams.
      socketWrapper = getSocket(server);

      //Send the logout command
      socketWrapper.outputStream.println(
                               "GET /logout.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Logout done**");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
    System.out.println("**End program output**");
  }//end doIt

Listing 47

Knowing what you now know, there is nothing in Listing 47 that should require further explanation.

Setting the WEP key

Now it is time for us to examine the code used to set the WEP key on the Belkin router.  We will accomplish that using the class named Sockets13.

The Sockets13 Class

A complete listing of the Sockets13 class is provided in Listing 55.  This class can be used to connect to a Belkin 54G wireless router and to change the WEP key.  The class installs a ten-character hexadecimal WEP key in the router.  It would be a simple matter to modify the program to cause
it to install a 26-character WEP key.

Much of the code in the class named Sockets13 is very similar to code previously explained in this lesson.  I won't repeat that explanation here.  Rather, I will concentrate on the code that is different.

The output

Once again, I am going to begin by showing you the output produced by this program.  That output is shown in Figure 14

New WEP key: e0d5ae876d

**Login**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:24 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Login done**

**Post new WEP key**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:28 GMT
4: Content-Type: text/html
5: Connection: close
6:
7: <html lang="en"><head><title>Wireless Broadband Router</
title></head><body bgcolor=white><p><font size=3 face=arial
><p><font size=2 face=arial><p><p><font size=0 face=arial><
/b></font><br><script> timer = setTimeout('location.replace
("wireless_encrypt_64.html")', 0) </script> <p></body></htm
l>

**WEP key post is done**

**Logout**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:34 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Logout done**
**End program output**
Figure 14

The new material

The new material is shown in boldface in Figure 14.  This new material consists of:

  • The display of the new WEP key at the beginning of the output.
  • Nine lines of server response data, resulting from the invocation of the POST method to install the new WEP key.  This material appears near the middle of Figure 14.

As you can see, the server response to the invocation of the POST method began with HTTP/1.0 200 Ok, indicating that the server was happy with the process.  In addition, a physical examination of the Wireless Security page using a browser confirmed that the WEP key was properly installed.

The new code

The new code for the class named Sockets13 is shown in Listing 48.

      //Send the post command

      socketWrapper.outputStream.println(
                           "POST /postapply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                                    "Content-Length: 386");
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      //Post the data to the server as though a user 
      // clicked the submit button.  This syntax was 
      // captured from the actual data stream produced
      // by a browser using the Ethereal program.
      //Note that this statement calls the print method
      // instead of the println method, and then adds a LF
      // without a CR at the end.
      socketWrapper.outputStream.print(
        "page=wireless_enc64"
        + "&logout=2"
        + "&generate_flag=1"
        + "&webpage=wireless_encrypt_64.html"
        + "&wl0_wep=wep"
        + "&wl0_key1=" + wepKey
        + "&wl0_key2="
        + "&wl0_key3="
        + "&wl0_key4="
        + "&wl0_auth_mode=disabled"
        + "&wl0_wep_mode=64"
        + "&wl0_wep64_manual=1"
        + "&action=Apply"
        + "&wl0_sec_mode=64"
        + "&wl0_key=1"
        + "&ENC11=" + wepKey.substring(0,2)
        + "&ENC12=" + wepKey.substring(2,4)
        + "&ENC13=" + wepKey.substring(4,6)
        + "&ENC14=" + wepKey.substring(6,8)
        + "&ENC15=" + wepKey.substring(8,10)
        + "&ENC21=&ENC22=&ENC23=&ENC24=&ENC25="
        + "&ENC31=&ENC32=&ENC33=&ENC34=&ENC35="
        + "&ENC41=&ENC42=&ENC43=&ENC44=&ENC45=");
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);

Listing 48

There are no new concepts in Listing 48, only new details such as:

  • The name of the requested resource in the invocation of the POST method.
  • The syntax of and the information contained in the body of the POST message.

The most interesting items are highlighted in boldface in Listing 48.

I would have found it very difficult to construct the body of the POST message without the help of the Ethereal program.

That's all folks

And that is probably a lot more than you ever wanted to know about the topics covered in this lesson.

Run the Programs

I encourage you to copy the code from the classes in the section entitled Complete Program Listings.  Compile the code and execute it if you have a compatible wireless router.  Experiment with the code, making changes, and observing the results of your changes.  If you don't have a compatible wireless router, modify the code to make it compatible with your wireless router.

Above all, however, pay attention to the disclaimer that I provided earlier

Summary

In this lesson, I taught you how to write a Java program that will automatically set the WEP, WPA, or WPA2 encryption key on a wireless router on an unattended scheduled basis.  Hopefully in the process, I also taught you quite a bit about writing Java programs that will successfully communicate with HTTP servers.

Lest you forget, I will remind you one more time that I am not a security expert.  My expertise is in the area of Java programming.  The programs in this lesson are provided for educational purposes only.  There is no suggestion that these programs are suitable for any purpose other than education in the area of Java programming.  If you use the programs for any purpose whatsoever, you are doing so at your own risk.  I will accept no responsibility for any outcome that you may experience.

Good Network Practice

Whether or not you decide, (despite my disclaimer), to implement these ideas in hopes of improving your home or small office wireless network security, there are some other things that you should consider doing.

Suggestions from CWNA Guide to Wireless LANS

The following ideas were generally taken from the textbook entitled CWNA Guide to Wireless LANS, Second Edition, by Mark Ciampa.

  • Use the highest level of authentication and encryption supported by your wireless router and the computers on your network.  Above all, make sure that authentication and encryption are not disabled as is often the default at installation time.
  • Disable the Wireless SSID Broadcast on your router.  While this won't prevent serious attackers from finding your wireless router, it will discourage casual eavesdroppers.
  • Change the SSID on your wireless router from its default to some more cryptic name.  This will prevent attackers from finding your wireless router simply by entering the well-known SSIDs for different brands of wireless routers.
  • Enable the Wireless MAC Filter feature.  While this won't prevent the really serious attacker from breaking into your wireless network, it will force them to expend quite a lot of effort to do so.  Hopefully that will cause them to search for an easier target at someone else's house.

My suggestions

And in addition to those suggestions from Ciampa, my suggestions are:

  • Use a strong encryption key by avoiding common words and phrases, birthdays, children's names, mother's maiden name, etc, and by mixing upper case characters, lower case characters, numbers, and special characters whenever possible.
  • Change the encryption key often.  The breaking of strong encryption keys generally requires the intercept of and analysis of large amounts of traffic.  By changing the encryption key often, you eliminate the opportunity for the attacker to intercept a large amount of traffic involving the use of the same encryption key.

Physical security

And don't forget physical security for the router, particularly in a small office environment.  With many wireless routers, if an attacker can gain access to the equipment long enough to press the reset button (about ten seconds), the router will revert to its default operational configuration which typically includes no security at all.

References

550 Network Programming - General Information 
552 Network Programming - The InetAddress Class 
554 Network Programming - The URL Class and the URLEncoder Class 
556 Network Programming - The URLConnection Class 
560 Network Programming - Sockets 
562 Network Programming - Server Sockets 
564 Network Programming - Datagram Clients 
566 Network Programming - Datagram Servers 
568 Network Programming - Stubs, Skeletons, and Remote Objects
060 Input and Output Streams 
2188 Understanding Base64 Data
727 Public Key Cryptography 101 Using Java 
2400 Consolidating Email using Java
2402 Consolidating Email using Java, Part 2
2404 Uploading Old Email to Gmail using Java
2410 Using Java to Clean Up Your Bookmark Library

Also see http://www.dickbaldwin.com/toc.htm

Complete Program Listing

Complete listings of the programs discussed in this lesson are provided below.
 
/*File Sockets15.java Copyright 2006, R.G.Baldwin

Illustrates communication between a program and an HTTP/1.1
server, which doesn't require authentication.

Also illustrates how to cause the server to forego keep-
alive and close the connection at the end of each response.

This class attempts to get the headers for two different
resources from the same server.  Here is the output when
the server, which normally implements keep-alive by 
default, responds properly to the request to close the 
connection at the end of the response.  Note that line 
breaks were manually inserted to force the material to fit 
into this narrow publication format.

**Send initial HEAD command**
1: HTTP/1.1 200 OK
2: Date: Mon, 19 Dec 2005 15:18:24 GMT
3: Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10
-1.dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
4: X-Powered-By: PHP/4.3.10-1.dotdeb.0
5: Connection: close
6: Content-Type: text/html; charset=iso-8859-1
7:

**Send another HEAD command.**
**Must get new socket**
1: HTTP/1.1 200 OK
2: Date: Mon, 19 Dec 2005 15:18:25 GMT
3: Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10
-1.dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
4: Last-Modified: Sat, 30 Jul 2005 00:31:16 GMT
5: ETag: "16f8002-eda-42eaca54"
6: Accept-Ranges: bytes
7: Content-Length: 3802
8: Connection: close
9: Content-Type: text/html; charset=iso-8859-1
10:

**End program output**


Here is the output that would be produced if the server
failed to respond to the request to close the connection
following the first response.  Once again, line breaks were
manually inserted to force the material to fit into this
narrow publication format.

**Send initial HEAD command**
1: HTTP/1.1 200 OK
2: Date: Mon, 19 Dec 2005 15:22:03 GMT
3: Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/4.3.10
-1.dotdeb.0 mod_ssl/2.8.9 OpenSSL/0.9.6c mod_perl/1.26
4: X-Powered-By: PHP/4.3.10-1.dotdeb.0
5: Content-Type: text/html; charset=iso-8859-1
6:

**Send another HEAD command.**
**Synchronization error, terminating**

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets15{

  final String server = "www.austincc.edu";
  boolean closedFlag;
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets15().doIt();
  }//end main
  //-----------------------------------------------------//
  
  void doIt(){
    //Get a socket, connected to the specified server on
    // port 80, the HTTP port.  Also get input and output
    // streams that can be used to communicate with the
    // server.
    socketWrapper = getSocket(server);

    System.out.println("**Send initial HEAD command**");
    socketWrapper.outputStream.println("HEAD / HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    //Ask the server to close the connection following the
    // response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here.
    socketWrapper.outputStream.println();

    //Display fifteen lines of server response.  Get
    // information about closing of the connection in the
    // process.
    closedFlag = print(15,socketWrapper.inputStream);

    System.out.println("\n**Send another HEAD command.**");
    
    //Confirm that the connection was properly closed by
    // the server at the end of the previous response.
    // Otherwise, terminate with a synchronization error.
    if(closedFlag){
      System.out.println("**Must get new socket**");
      socketWrapper = getSocket(server);
    }else{
      System.out.println(
                 "**Synchronization error, terminating**");
      System.exit(1);
    }//end else

    socketWrapper.outputStream.println(
                      "HEAD /baldwin/index.html HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    socketWrapper.outputStream.println(
                                      "User-Agent: Dummy");
    //Ask the server to close the connection following the 
    // response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here
    socketWrapper.outputStream.println();

    //Print 15 lines of server response data.
    print(15,socketWrapper.inputStream);

    System.out.println("\n**End program output**");
      
    try{
      //Close the socket
      socketWrapper.socket.close();
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get and display a
  // specified number of lines of the server response. In
  // the process, the method checks to determine if the
  // server closed the connection at the end of its
  // response. The method returns a boolean value
  // indicating whether or not the server closed the
  // connection.
  boolean print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "dummy";
    boolean closedFlag = false;
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);

        if(line.contains("Connection: close")){
          //The server closed the connection at the end of
          // the response.
          closedFlag = true;
        }//end if
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return closedFlag;
    
  }//end local method named print
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//
}//end class Sockets15
//=======================================================//


Listing 49

 

/*File Sockets14.java Copyright 2006, R.G.Baldwin

The purpose of this class is to illustrate basic 
authentication.

The class works properly with a very recent Linksys WRT54G 
wireless router with SES/WPA2.  It also works properly with
a recent Linksys WRT54G wireless router without SES/WPA2.

The class does not work properly with an old Linksys 
BEFW11S4 802.11B wireless router or an old Linksys BEFSR41 
Cable router.  The same symptoms are exhibited for both of 
the older Linksys routers.  They don't close the connection
following the 401 message response even when requested to 
do so.  Then they ignore a following GET command containing
the Authorization header line.  However, those two boxes do
work properly with a standard commercial browser, so there
is something that a browser knows how to do that this
class doesn't do.

The class also does not work properly with a Belkin 54G
wireless router.  However, that is to expected because the
Belkin router requires a completely different 
authentication scheme.

Note that in order to avoid dealing with the complexities 
of keep-alive, the program asks the server to close the 
connection at the end of each response.

Typical output resulting from the running of the program
with a Linksys WRT54G wireless router with SES/WPA2 is 
shown below.  Note that the width of the output was 
manually truncated to force it to fit into this narrow
publication format.

**Send initial GET command**
1: HTTP/1.0 401 Unauthorized
2: Server: httpd
3: Date: Thu, 01 Jan 1970 00:00:29 GMT
4: WWW-Authenticate: Basic realm="WRT54G"
5: Content-Type: text/html
6: Connection: close
7:
8: <HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>
9: <BODY BGCOLOR="#cc9999"><H4>401 Unauthorized</H4>
10: Authorization required.
11: </BODY></HTML>
**Authorization required**
**Must get new socket**

**Re-send the GET command with authentication.**
1: HTTP/1.0 200 Ok
2: Server: httpd
3: Date: Thu, 01 Jan 1970 00:00:33 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
10:
11: <!--
12: *******************************************************
13: *   Copyright 2003, CyberTAN  Inc.  All Rights Reserved
14: *******************************************************
15:
**Print terminated on line count.**


Here is the output produced by a Linksys BEFSR41 cable 
router.  Note that line breaks were manually inserted to
force the material to fit into this narrow publication
format.  Also note that this output terminated on a
synchronization error.

**Send initial GET command**
1: HTTP/1.1 401 Authorization Required
2: WWW-Authenticate: Basic realm="Linksys BEFSR41/BEFSR11/B
EFSRU31"
3: Server: Linksys BEFSR41/BEFSR11/BEFSRU31 ver1.36
4: Content-type: text/html
5: Expires: Thu, 13 Dec 1969 10:29:00 GMT
6: Pragma: no-cache
7: Content-length: 339
8:
9: <html><head><title>401 Authorization Required</title></h
ead><body bgcolor=red text=white><h1>401 Authorization Requ
ired</h1>This server could not verify that you are authoriz
ed to access. Either you supplied the wrong credentials(e.g
., bad password), or your browser doesn't understand how to
 supply the credentials required.</body></html>
**Authorization required**
**Synchronization error, terminating**

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets14{

  final String server = "192.168.1.1";

  StateWrapper stateWrapper;
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets14().doIt();
  }//end main
  //-----------------------------------------------------//
  
  void doIt(){
    //The following constant contains the administrator
    // password expressed in base64.
    // See http://www.motobit.com/util/base64-decoder
    // -encoder.asp for an online base64 encoder/decoder.
    //The following value is for the default administrator
    // username and password for a Linksys wireless router
    // where the user name is blank and the password is
    // admin. The user name and the password must be
    // separated by a colon.  Since the username is blank,
    // this is the base64 representation of :admin
    final String adminUserPwd = "OmFkbWlu";

    //Get a socket, connected to the specified server on
    // port 80, the HTTP port.  Also get input and output
    // streams that can be used to communicate with the
    // server.
    socketWrapper = getSocket(server);
 
    System.out.println("**Send initial GET command**");
    socketWrapper.outputStream.println("GET / HTTP/1.1");
    socketWrapper.outputStream.println("Host: " + server);
    socketWrapper.outputStream.println(
                          "User-Agent: Dummy Header Line");
    //Ask the server to close the connection following
    // the response.
    socketWrapper.outputStream.println(
                                      "Connection: close");
    //The following CRLF is required here.
    socketWrapper.outputStream.println();

    //Display fifteen lines of server response.  Get
    // information about the requirement for authentication
    // and the closing of the connection in the process.
    stateWrapper = print(15,socketWrapper.inputStream);

    if(stateWrapper.authFlag){
      System.out.println("**Authorization required**");

      if(stateWrapper.closedFlag){
        System.out.println("**Must get new socket**");
        socketWrapper = getSocket(server);
      }else{
        System.out.println(
                 "**Synchronization error, terminating**");
        System.exit(1);
      }//end else
     
      System.out.println("\n**Re-send the GET command "
                               + "with authentication.**");
      socketWrapper.outputStream.println("GET / HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                                      "User-Agent: Dummy");
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);
      //Ask the server to close the connection following
      // the response.
      socketWrapper.outputStream.println(
                                      "Connection: close");
      //The following CRLF is required here
      socketWrapper.outputStream.println();

      //Print 15 lines of server response data.
      print(15,socketWrapper.inputStream);
    }//end if on authFlag

    System.out.println("\n**End program output**");
      
    try{
      //Close the socket
      socketWrapper.socket.close();
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt
  //-----------------------------------------------------//

  //This class is used to wrap two boolean values that
  // specify whether the server requires authentication and
  // whether the server closed the connection following the
  // previous response.
  class StateWrapper{
    boolean authFlag = false;
    boolean closedFlag = false;
  }//end StateWrapper
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get and display a
  // specified number of lines of the server response. In
  // the process, the method checks to determine if the
  // server requires authentication and if the server
  // closed the connection at the end of its response. The
  // method returns two boolean values in a simple wrapper
  // object containing the answers to those two questions.
  StateWrapper print(
                 int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "";
    StateWrapper stateWrapper = new StateWrapper();
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);
        if(line.contains("401")){
          //Authentication is required
          stateWrapper.authFlag = true;
        }//end if
        
        if(line.contains("Connection: close")){
          //The server closed the connection at the end of
          // the response.
          stateWrapper.closedFlag = true;
        }//end if
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return stateWrapper;
    
  }//end local method named print
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//
}//end class Sockets14
//=======================================================//


Listing 50

 

/*File Sockets10.java Copyright 2005, R.G.Baldwin

This program can be used to connect to a Linksys WRT54G 
wireless router (with or without SES/WPA2) to change 
the WEP key.  The program installs a ten-character 
hexadecimal WEP key in the router.  It would be a simple
matter to modify the program to cause it to install a 
26-character WEP key.

Note:  for simplicity, this class uses some deprecated
methods.

The new key is displayed on the screen.  The screen output
for a typical run with a Linksys WRT54G wireless router
with SES/WPA2 is shown below.  Note that the width of the 
output was manually truncated to force it to fit into this 
narrow publication format.

New WEP key: a4cb6e0679
POST /apply.cgi HTTP/1.1
1: HTTP/1.0 200 Ok
2: Server: httpd
3: Date: Sun, 18 Dec 2005 20:32:00 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
10:
11: <!--
12: *******************************************************
13: *   Copyright 2003, CyberTAN  Inc.  All Rights Reserved
14: *******************************************************
15:
**Print terminated on line count.**
End POST operation

When the program terminates, the new WEP key shown above
has been installed in the wireless router.

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets10{
  
  String server = "192.168.1.1";
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets10().doIt();
  }//end main
  //-----------------------------------------------------//
  
  //This is the main processing method in the program.
  void doIt(){
    //Get a ten-character hexadecimal WEP key based on the
    // date and a random number generator.
    String wepKey = getWepKey();
    //Display the new WEP key.
    System.out.println("New WEP key: " + wepKey);

    //The following constant contains the administrator
    // password expressed in base64.
    // See http://www.motobit.com/util/base64-decoder
    // -encoder.asp for an online base64 encoder/decoder.
    //The following value is for the default administrator
    // username and password for a Linkys wireless router
    // where the user name is blank and the password is
    // admin. The user name and the password must be
    // separated by a colon.  Since the username is blank,
    // this is the base64 representation of :admin
    final String adminUserPwd = "OmFkbWlu";
    
    try{
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);
      
      //Set up to post the data to the server.
      System.out.println("POST /apply.cgi HTTP/1.1");
      
      socketWrapper.outputStream.println(
                               "POST /apply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);
      socketWrapper.outputStream.println(
                                    "Content-Length: 252");
      //The following CRLF is required here.
      socketWrapper.outputStream.println();
      
      //Post the data to the server as though a user 
      // clicked the submit button.  This syntax was 
      // captured from the actual data stream produced
      // by a browser using the Ethereal program.
      //Note that this statement calls the print method
      // instead of the println method, and then adds a LF
      // without a CR at the end.
      socketWrapper.outputStream.print(
        "submit_button=WL_WPATable"
        + "&change_action="
        + "&submit_type="
        + "&action=Apply"
        + "&security_mode_last="
        + "&wl_wep_last="
        + "&security_mode2=wep"//WEP security mode
        + "&wl_key=1"//Default is key 1
        + "&wl_WEP_key="
        + "&wl_wep=restricted"
        + "&wl_wep_bit=64"//64-bit WEP key
        + "&wl_passphrase="//Passphrase is blank
        + "&generateButton=Null"//Passphrase is not used
        + "&wl_key1=" + wepKey //Value for Key 1
        + "&wl_key2="//Blank key
        + "&wl_key3="//Blank key
        + "&wl_key4=");//Blank key
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);
        
      //Display fifteen lines of server response data.
      print(15,socketWrapper.inputStream);

      System.out.println("End POST operation");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//

  //The purpose of this method is to get and display a
  // specified number of lines of the server response.
  void print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "";
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end local method named print
  //-----------------------------------------------------//
  //This method generates a 10-character hexadecimal key
  // based on the current date and a random number
  // generator.The algorithm will generate the same key
  // every time it is run on a given date, but will
  // generate different keys on different dates.
  //You can easily modify this algorithm to come up with
  // a different recipe for the key.  Alternately, you can
  // modify this method to cause it to generate a
  // 26-character hexadecimal key instead of a
  // ten-character key.  If you do that, you will need to
  // make some changes to the post command in the doIt
  // method to cause it to accommodate 26-character keys.
  //Note:  for simplicity, this method uses some deprecated
  // methods.
  String getWepKey(){
    //The following two secet values can be modified by the
    // user to customize this key generator for a specific
    // user.
    final int bias = 12345;//a secret value
    final int lim = 8;//another secret value
    
    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);
    
    //Instantiate a random number generator seeded by the
    // date and the first secret value given above.
    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);
    
    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt();
    }//end for loop
    
    //Construct a sequence of ten hexadecimal characters
    // using a random long negative value.
    long val = randomGen.nextLong();
    //Guarantee a 1 in the first bit position
    if(val == 0)val = -1;
    if(val > 0) val = -val;
    //Get a ten-character substring from the 16 hexadecimal
    // characters that describe the negative long value.
    String stringInProgress = 
                     Long.toHexString(val).substring(0,10);

    return stringInProgress;
    
  }//end getWepKey
  
}//end class Sockets10
//=======================================================//


Listing 51

 

/*File Sockets11.java Copyright 2005, R.G.Baldwin

This program can be used to connect to a Linksys WRT54G 
wireless router (with or without SES/WPA2) to change 
the WPA shared key.  The router requires that the key be
63 characters or less.

When used with an older router that doesn't support WPA2, 
the program sets the configuration to a WPA Pre-Shared Key
and TKIP.

When used with a newer router that supports WPA2, the 
program sets the configuration to WPA2 Personal and 
TKIP+AES.

Note:  for simplicity, this class uses some deprecated
methods.

The new key is displayed on the screen.  The screen output
for a typical run with a Linksys WRT54G wireless router
with SES/WPA2 is shown below.  Note that the width of the 
output was manually truncated to force it to fit into this 
narrow publication format.

New key: w<e.TwF.d`Q.1wu
POST /apply.cgi HTTP/1.1
1: HTTP/1.0 200 Ok
2: Server: httpd
3: Date: Sun, 18 Dec 2005 21:17:28 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
10:
11: <!--
12: *******************************************************
13: *   Copyright 2003, CyberTAN  Inc.  All Rights Reserved
14: *******************************************************
15:
**Print terminated on line count.**
End POST operation

When the program terminates, the new WPA key shown above
has been installed in the wireless router.

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets11{
  
  final String server = "192.168.1.1";
  SocketWrapper socketWrapper;
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets11().doIt();
  }//end main
  //-----------------------------------------------------//

  //This is the main processing method in the class.  
  void doIt(){
    //Get a fifteen-character key based on the date
    // and a random number generator.
    String wpaSharedKey = getWpaKey();
    System.out.println("New key: " + wpaSharedKey);
    
    //The following constant contains the administrator
    // password expressed in base64.
    // See http://www.motobit.com/util/base64-decoder
    // -encoder.asp for an online base64 encoder/decoder.
    //The following value is for the default administrator
    // username and password for a Linkys wireless router
    // where the user name is blank and the password is
    // admin. The user name and the password must be
    // separated by a colon.  Since the username is blank,
    // this is the base64 representation of :admin
    final String adminUserPwd = "OmFkbWlu";
    
    try{
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);
      
      //Set up to post the data to the server.
      System.out.println("POST /apply.cgi HTTP/1.1");
      
      socketWrapper.outputStream.println(
                               "POST /apply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                   "Authorization: Basic " + adminUserPwd);
      socketWrapper.outputStream.println("Content-Length: "
                          + (183 + wpaSharedKey.length()));
      //The following CRLF is required here.
      socketWrapper.outputStream.println();
      
      //Post the data to the server as though a user 
      // clicked the submit button.  This syntax was 
      // captured from the actual data stream produced
      // by a browser using the Ethereal program.
      //Note that this statement calls the print method
      // instead of the println method, and then adds a LF
      // without a CR at the end.
      socketWrapper.outputStream.print(
        "submit_button=WL_WPATable"
        + "&change_action="
        + "&submit_type="
        + "&action=Apply"
        + "&security_mode_last="
        + "&wl_wep_last="
        + "&security_mode2=wpa2_personal"//WPA2 Personal
        + "&wl_crypto=tkip%2Baes"//TKIP+AES
        + "&wl_wpa_psk=" + wpaSharedKey
        + "&wl_wpa_gtk_rekey=3600");//Renewal @ 3600 sec
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);
        
      //Display fifteen lines of server response data.
      print(15,socketWrapper.inputStream);

      System.out.println("End POST operation");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end doIt
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//

  //The purpose of this method is to get and display a
  // specified number of lines of the server response.
  void print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "";
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end local method named print
  //-----------------------------------------------------//
  
  //This method generates a 15-character key in the form
  // ccc.ccc.ccc.ccc based on the current date and a
  // random number generator.  The characters range from
  // 0 (48) through z (122) inclusive.  The algorithm will
  // generate the same key every time it is run on a given
  // date, but will generate different keys on different
  // dates.
  //You can easily modify this algorithm to come up with
  // a different recipe for the key, or a longer key using
  // the same general recipe.
  //Note:  for simplicity, this method uses some deprecated
  // methods.
  String getWpaKey(){
    int bias = 12345;//a secret value
    int lim = 8;//another secret secret value
    
    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);
    
    //Instantiate a random number generator seeded by the
    // date and the first secret value given above.
    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);
    
    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt(123);
    }//end for loop
    
    //Construct a sequence of twelve characters using
    // sequential random values between 48 and 122
    // inclusive according to the ASCII collating sequence.
    StringBuffer stringInProgress = new StringBuffer();
    while(stringInProgress.length() < 12){
      int val = randomGen.nextInt(123);
      if(val > 47){
        stringInProgress.append((char)val);
      }//end if
    }//end while loop
    
    //Insert periods every third character to make it
    // easier to manually enter the key on client machines.
    // This increases the length of the key to 15 total
    // characters including the periods.
    stringInProgress.insert(9,'.');
    stringInProgress.insert(6,'.');
    stringInProgress.insert(3,'.');
    
    return stringInProgress.toString();
    
  }//end getWpaKey
  
}//end class Sockets11
//=======================================================//


Listing 52

 

/*File Sockets11a.java Copyright 2005, R.G.Baldwin

The purpose of this program is to generate a WPA key that 
matches the WPA key produced by the program named Sockets11
when both programs are run on the same date.

This is a local stand-alone program that doesn't 
communicate with a network in any way.

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.util.*;

class Sockets11a{
  
  public static void main(String[] args){
    new Sockets11a().doIt();
  }//end main
  //-----------------------------------------------------//

  //This is the main processing method in the class.  
  void doIt(){
    //Get a fifteen-character key based on the date
    // and a random number generator.
    String wpaSharedKey = getWpaKey();
    System.out.println("New key: " + wpaSharedKey);
  }//end doIt
  //-----------------------------------------------------//

  //This method generates a 15-character key in the form
  // ccc.ccc.ccc.ccc based on the current date and a
  // random number generator.  The characters range from
  // 0 (48) through z (122) inclusive.  The algorithm will
  // generate the same key every time it is run on a given
  // date, but will generate different keys on different
  // dates.
  //You can easily modify this algorithm to come up with
  // a different recipe for the key, or a longer key using
  // the same general recipe.
  //Note:  for simplicity, this method uses some deprecated
  // methods.
  String getWpaKey(){
    int bias = 12345;//a secret value
    int lim = 8;//another secret secret value
    
    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);
    
    //Instantiate a random number generator seeded by the
    // date and the first secret value given above.
    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);
    
    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt(123);
    }//end for loop
    
    //Construct a sequence of twelve characters using
    // sequential random values between 48 and 122
    // inclusive according to the ASCII collating sequence.
    StringBuffer stringInProgress = new StringBuffer();
    while(stringInProgress.length() < 12){
      int val = randomGen.nextInt(123);
      if(val > 47){
        stringInProgress.append((char)val);
      }//end if
    }//end while loop
    
    //Insert periods every third character to make it
    // easier to manually enter the key on client machines.
    // This increases the length of the key to 15 total
    // characters including the periods.
    stringInProgress.insert(9,'.');
    stringInProgress.insert(6,'.');
    stringInProgress.insert(3,'.');
    
    return stringInProgress.toString();
    
  }//end getWpaKey
  
}//end class Sockets11a


Listing 53

 

/*File Sockets12.java Copyright 2006, R.G.Baldwin

This class illustrates the methodology for logging in and
logging out on a Belkin 54G wireless router.  Note that
this is completely different from the basic authentication
used on the Linksys WRT54G router.

Typical output from the program follows:

**Login**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 15:32:34 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Login done**

**Logout**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 15:32:39 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Logout done**
**End program output**

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets12{
  
  String server = "192.168.2.1";
  SocketWrapper socketWrapper;
  //Administrator password.  Note that this is not
  // expressed in Base64 as is the case with basic
  // authentication.
  final String adminUserPwd = "admin";
  //-----------------------------------------------------//
  
  public static void main(String[] args){
    new Sockets12().doIt();
  }//end main
  //-----------------------------------------------------//
  
  void doIt(){

    try{
      System.out.println("\n**Login**");
      
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);

      //Send the login command
      socketWrapper.outputStream.println("GET /login.cgi?"
        + "page=login"
        + "&logout=2"
        + "&pws=" + adminUserPwd + " HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Login done**");

      //Now log out from the router.

      System.out.println("\n**Logout**");
      
      //Get a new Socket object and I/O streams.
      socketWrapper = getSocket(server);

      //Send the logout command
      socketWrapper.outputStream.println(
                               "GET /logout.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Logout done**");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
    System.out.println("**End program output**");
  }//end doIt
  //-----------------------------------------------------//

  //The purpose of this method is to get and display a
  // specified number of lines of the server response.
  void print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "";
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end local method named print
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//
}//end class Sockets12
//=======================================================//


Listing 54

 

/*File Sockets13.java Copyright 2006, R.G.Baldwin

This program can be used to connect to a Belkin 54G 
wireless router to change the WEP key.  The program 
installs a ten-character hexadecimal WEP key in the router.
It would be a simple matter to modify the program to cause 
it to install a  26-character WEP key.

The new key is displayed on the screen.  A typical output
produced by this program follows.  Note that line breaks
were manually inserted to force the material to fit into
this narrow publication format.

New WEP key: e0d5ae876d

**Login**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:24 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Login done**

**Post new WEP key**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:28 GMT
4: Content-Type: text/html
5: Connection: close
6:
7: <html lang="en"><head><title>Wireless Broadband Router</
title></head><body bgcolor=white><p><font size=3 face=arial
><p><font size=2 face=arial><p><p><font size=0 face=arial><
/b></font><br><script> timer = setTimeout('location.replace
("wireless_encrypt_64.html")', 0) </script> <p></body></htm
l>

**WEP key post is done**

**Logout**
**Display 9 lines of the server's response**
1: HTTP/1.0 200 Ok
2: Server: micro_httpd
3: Date: Fri, 02 Jan 1970 19:54:34 GMT
4: Cache-Control: no-cache
5: Pragma: no-cache
6: Expires: 0
7: Content-Type: text/html
8: Connection: close
9:
**Print terminated on line count.**

**Logout done**
**End program output**

Tested using J2SE 5.0 and WinXP.
**********************************************************/

import java.net.*;
import java.io.*;
import java.util.*;

class Sockets13{
  
  String server = "192.168.2.1";
  SocketWrapper socketWrapper;
  //Administrator password.  Note that this is not
  // expressed in Base64 as is the case with basic
  // authentication.
  final String adminUserPwd = "admin";
  //-----------------------------------------------------//
  public static void main(String[] args){
    new Sockets13().doIt();
  }//end main
  //-----------------------------------------------------//
  
  void doIt(){
 
    //Get a ten-character WEP key based on the date and a
    // random number generator.
    String wepKey = getWepKey();
    System.out.println("New WEP key: " + wepKey);
    
    try{
      System.out.println("\n**Login**");
      
      //Get a Socket object connected to the specified
      // server on port 80.  Also get input and output
      // streams on the Socket object in the process.
      socketWrapper = getSocket(server);

      //Send the login command
      socketWrapper.outputStream.println("GET /login.cgi?"
        + "page=login"
        + "&logout=2"
        + "&pws=" + adminUserPwd + " HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Login done**");
      

      System.out.println("\n**Post new WEP key**");
      //Get a Socket object along with I/O streams.
      socketWrapper = getSocket(server);

      //Send the post command
      socketWrapper.outputStream.println(
                           "POST /postapply.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      socketWrapper.outputStream.println(
                                    "Content-Length: 386");
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      //Post the data to the server as though a user 
      // clicked the submit button.  This syntax was 
      // captured from the actual data stream produced
      // by a browser using the Ethereal program.
      //Note that this statement calls the print method
      // instead of the println method, and then adds a LF
      // without a CR at the end.
      socketWrapper.outputStream.print(
        "page=wireless_enc64"
        + "&logout=2"
        + "&generate_flag=1"
        + "&webpage=wireless_encrypt_64.html"
        + "&wl0_wep=wep"
        + "&wl0_key1=" + wepKey
        + "&wl0_key2="
        + "&wl0_key3="
        + "&wl0_key4="
        + "&wl0_auth_mode=disabled"
        + "&wl0_wep_mode=64"
        + "&wl0_wep64_manual=1"
        + "&action=Apply"
        + "&wl0_sec_mode=64"
        + "&wl0_key=1"
        + "&ENC11=" + wepKey.substring(0,2)
        + "&ENC12=" + wepKey.substring(2,4)
        + "&ENC13=" + wepKey.substring(4,6)
        + "&ENC14=" + wepKey.substring(6,8)
        + "&ENC15=" + wepKey.substring(8,10)
        + "&ENC21=&ENC22=&ENC23=&ENC24=&ENC25="
        + "&ENC31=&ENC32=&ENC33=&ENC34=&ENC35="
        + "&ENC41=&ENC42=&ENC43=&ENC44=&ENC45=");
      //The following LF is required here without a CR.
      socketWrapper.outputStream.print(0x0a);

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);

      System.out.println("\n**WEP key post is done**");

      //Now log out from the router.

      System.out.println("\n**Logout**");
      
      //Get a new Socket object and I/O streams.
      socketWrapper = getSocket(server);

      //Send the logout command
      socketWrapper.outputStream.println(
                               "GET /logout.cgi HTTP/1.1");
      socketWrapper.outputStream.println(
                                        "Host: " + server);
      //The following CRLF is required.
      socketWrapper.outputStream.println();

      System.out.println(
           "**Display 9 lines of the server's response**");
      print(9,socketWrapper.inputStream);
      System.out.println("\n**Logout done**");

      //Close the socket
      socketWrapper.socket.close();

    }//end try
    catch(Exception e){
      e.printStackTrace();
    }//end catch
    System.out.println("**End program output**");
  }//end doIt
  //-----------------------------------------------------//

  //The purpose of this method is to get and display a
  // specified number of lines of the server response.
  void print(int lineLimit,BufferedReader inputStream){
    int lineCnt = 0;
    String line = "";
    try{
      while(((line = inputStream.readLine()) != null) 
                               && (lineCnt++ < lineLimit)){
        System.out.println(lineCnt + ": " + line);
      }//end while
      if(lineCnt >= lineLimit){
        System.out.println(
                    "**Print terminated on line count.**");
      }//end if
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
  }//end local method named print
  //-----------------------------------------------------//
  
  //This class is used to wrap three objects used in
  // socket communications.
  class SocketWrapper{
    //This is a reference to the Socket object itself.
    Socket socket;
    //This is an eight-bit stream used to send commands to
    // the server.
    PrintStream outputStream;
    //This is a 16-bit stream used to receive the server
    // response lines.
    BufferedReader inputStream;
  }//end SocketWrapper
  //-----------------------------------------------------//
  
  //The purpose of this method is to get a new Socket
  // object connected to a server on port 80 along with
  // input and output stream objects that can be used to
  // communicate with the server.  References to the
  // Socket object and the two stream objects are
  // returned in a simple wrapper object of type 
  // SocketWrapper.
  SocketWrapper getSocket(String server){
    int port = 80;
    SocketWrapper socketWrapper = new SocketWrapper();
    try{
      //Get a socket, connected to the specified server
      // on the specified port.
      socketWrapper.socket = new Socket(server,port);
      
      //Get an input stream for reading the response sent
      // by the server.
      socketWrapper.inputStream = 
                  new BufferedReader(new InputStreamReader(
                   socketWrapper.socket.getInputStream()));

      //Get an 8-bit output stream to the socket that will
      // autoflush.  Note that a 16-bit Unicode output 
      // stream apparently won't work for the posted
      // content.
      socketWrapper.outputStream = new PrintStream(
              socketWrapper.socket.getOutputStream(),true);
    }catch(Exception e){
      e.printStackTrace();
    }//end catch
    
    return socketWrapper;
  }//end getSocket
  //-----------------------------------------------------//
  //This method generates a 10-character hexadecimal key
  // based on the current date and a random number
  // generator.The algorithm will generate the same key
  // every time it is run on a given date, but will
  // generate different keys on different dates.
  //You can easily modify this algorithm to come up with
  // a different recipe for the key.  Alternately, you can
  // modify this method to cause it to generate a
  // 26-character hexadecimal key instead of a
  // ten-character key.  If you do that, you will need to
  // make some changes to the post command in the main
  // method to cause it to accommodate 26-character keys.
  //Note:  for simplicity, this method uses some deprecated
  // methods.
  static String getWepKey(){
    int bias = 12345;//a secret value
    int lim = 8;//another secret secret value
    
    //Get the current date and time
    Date dateTime = new Date();
    
    //Eliminate the time preserving only the date in a new
    // object of type Date.
    int year = dateTime.getYear();
    int month = dateTime.getMonth();
    int date = dateTime.getDay();
    Date dateOnly = new Date(year,month,date);
    
    //Instantiate a random number generator seeded by the
    // date and the first secret value given above.
    Random randomGen = 
                     new Random(dateOnly.getTime() + bias);
    
    //Advance the random number generator by a number of
    // cycles equal to the second secret value given above.
    for(int cnt = 0;cnt < lim;cnt++){
      randomGen.nextInt(123);
    }//end for loop
    
    //Construct a sequence of ten hexadecimal characters
    // using a random long negative value.
    long val = randomGen.nextLong();
    //Guarantee a 1 in the first bit position
    if(val == 0)val = -1;
    if(val > 0) val = -val;
    //Get a ten-character substring from the 16 hexadecimal
    // characters that describe the negative long value.
    String stringInProgress = 
                     Long.toHexString(val).substring(0,10);

    return stringInProgress;
    
  }//end getWepKey
  
}//end class Sockets13
//=======================================================//


Listing 55


Copyright 2005, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel