Java Programming Notes # 734
- Preface
- Preview
- Discussion and
Sample Code - The Sockets15 Class
- The Sockets14 Class
- The Sockets10 Class
- The Sockets11 Class
- The Sockets11a Class
- The Sockets12 Class
- The Sockets13 Class
- Run the Programs
- Summary
- Good Network Practice
- References
- Complete Program
Listing
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.)
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:
- Standard
basic authentication - Nonstandard authentication
- Keep-alive features
- GET
and HEAD methods with query strings - POST
methods with formatted message bodies
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; |
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 |
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); |
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 |
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 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); |
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 |
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**"); |
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(); |
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); |
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 |
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 |
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**"); |
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 |
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 //-----------------------------------------------------// |
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"; |
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); |
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(); |
(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); |
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 |
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 |
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:
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 //-----------------------------------------------------// |
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); |
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 |
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); |
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); |
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 |
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(); |
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; |
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 |
(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 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 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(); |
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); |
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 |
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); |
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 |
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 |
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 |
(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); |
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(); |
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); |
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 |
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 //-----------------------------------------------------// |
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(); |
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**"); |
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 |
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); |
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 //=======================================================// |
/*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 //=======================================================// |
/*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 //=======================================================// |
/*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 //=======================================================// |
/*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 |
/*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 //=======================================================// |
/*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 //=======================================================// |
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.