|
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object | +--com.luigidragone.net.ntlm.NTLM
This class implements the Microsoft NTLM authentication protocol.
NTLM is a Microsoft proprietary network authentication protocol used in many situations (e.g. by the Microsoft Proxy Server to authenticate a browser).
It requires a JCE compatible MD4 hash algorithm implementation and
a DES with no-padding ECB cipher to compute the requested value.
An open source JCE compatible library is Cryptix JCE and it is available
here. We are assuming that the JCE provider
is correctly installed and configured. Notice that the Sun JCE implementation
proviedes the DES cipher but doesn't provide the MD4 hashing.
To start an NTLM authentication procedure (e.g. with a proxy server) build a
request message calling formatRequest
and send it to the server.
Once the challenge packet has been correctly received extract from it the nonce
with getNonce
function and use it to compute the
reply and build the response message with
formatResponse
method and send it back to the server.
Repeat the previous steps until the server authenticates the client's identity
or a large number of retries has been made. The check of a successful authentication
is protocol specific (e.g. code 200 in HTTP), thus it is not performed by this component.
We want to access to the page http://www.server.com/page.html
through an NTLM proxy proxy.domain.com
that accepts connection on
port 80.
We access to proxy from host HOSTDOMAIN\\HOST
with the user
USERDOMAIN\\user
(password "1234567890"
).
As first step we open a socket connection to proxy server and set up the
required object. Notice that we use a keep-alive connection, because NTLM
authentication is connection based and the connection must be alive through the
whole process.
Socket s = new Socket("proxy.domain.com", 80);
s.setKeepAlive(true);
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
BufferedReader r = new BufferedReader(new InputStreamReader(is));
BufferedWriter w = new BufferedWriter(new OutputStreamWriter(os));
String host = "HOST";
String hostDomain = "HOSTDOMAIN";
String user = "user";
String userDomain = "USERDOMAIN";
String password = "1234567890";
Then, we format a request message and send it in a HTTP compliant GET message.
byte[] fstMsg = NTLM.formatRequest(host, hostDomain);
byte[] fstMsg64 = Codecs.base64Encode(fstMsg);
System.out.println("NTLM Request Packet: " + new String(fstMsg64));
w.write("GET http://www.server.com/page.html HTTP/1.0\n");
w.write("Host: www.server.com\n");
w.write("Proxy-Connection: Keep-Alive\n");
w.write("Proxy-Authorization: NTLM " + new String(fstMsg64) + "\n\n");
w.flush();
System.out.println("First Message Sent");
We wait for the server response and we parse it to extract the nonce.
String resp = "";
int contentLength = 0;
while((line = r.readLine()) != null)
if(line.length() == 0)
break;
if(line.startsWith("Content-Length"))
contentLength = Integer.parseInt(line.substring(line.indexOf(":") + 1).trim());
else if(line.startsWith("Proxy-Authenticate"))
resp = line.substring(line.indexOf(":") + 1).trim();
r.skip(contentLength);
System.out.println("Second Message Received");
System.out.println("Content Length: " + contentLength);
System.out.println("Proxy-Authenticate: " + resp);
resp = resp.substring(resp.indexOf(" ")).trim();
System.out.println("NTLM Chellange Packet: " + resp);
resp = Codecs.base64Decode(resp);
byte[] sndMsg = resp.getBytes();
byte[] nonce = NTLM.getNonce(sndMsg);
With the nonce collected in the previous step we create a response message.
byte[] trdMsg = NTLM.formatResponse(host, user, userDomain,
NTLM.computeLMPassword(password), NTLM.computeNTPassword(password),
nonce);
System.out.println(trdMsg.length);
byte[] trdMsg64 = Codecs.base64Encode(trdMsg);
System.out.println("NTLM Response Packet: " + new String(trdMsg64));
We sent the message to the server.
w.write("GET http://www.server.com/page.html HTTP/1.0\n");
w.write("Proxy-Connection: Keep-Alive\n");
w.write("Host: www.server.com\n");
w.write("Proxy-Authorization: NTLM " + new String(trdMsg64) + "\n\n");
w.flush();
System.out.println("Third Message Sent");
Finally we wait the server reply.
System.out.println("Server response: " + r.readLine());
If the reply is like "HTTP/1.0 200 OK"
it has worked fine, else
the server response is containing a new nonce.
Notice that despite the computing of hashed passwords and of nonce response is
exactly the same of the SMB authentication protocol, the message format is slightly
different.
Therefore, the methods computeLMPassword
,
computeNTPassword
and
computeNTLMResponse
can be used to perform a SMB authentication too.
This implementation is based on:
For implementation reasons only the public members perform argument consistency checks. The public members also catch and hide every exceptions that can be throwed (even though interfaces specify otherwise).
Method Summary | |
static byte[] |
computeLMPassword(java.lang.String password)
Computes the Lan Manager hashed version of a password. |
static void |
computeNTLMResponse(byte[] lmPassword,
byte[] ntPassword,
byte[] nonce,
byte[] lmResponse,
byte[] ntResponse)
Computes the NTLM response to the nonce based on the supplied hashed passwords. |
static byte[] |
computeNTPassword(java.lang.String password)
Computes the NT hashed version of a password. |
static byte[] |
formatRequest(java.lang.String host,
java.lang.String hostDomain)
Builds a request message for the host of the specified domain that can be send to the server to start the NTLM protocol. |
static byte[] |
formatResponse(java.lang.String host,
java.lang.String user,
java.lang.String userDomain,
byte[] lmPassword,
byte[] ntPassword,
byte[] nonce)
Builds the nonce response message. |
static byte[] |
getNonce(byte[] msg)
Extracts from the server challenge response the nonce required to perform the authentication. |
Methods inherited from class java.lang.Object |
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Method Detail |
public static byte[] computeLMPassword(java.lang.String password) throws java.lang.IllegalArgumentException, javax.crypto.NoSuchPaddingException, java.security.NoSuchAlgorithmException
password
- the user password
java.lang.IllegalArgumentException
- if the supplied password is null
javax.crypto.NoSuchPaddingException
- if there isn't any suitable padding method
java.security.NoSuchAlgorithmException
- if there isn't any suitable cipher algorithmpublic static byte[] computeNTPassword(java.lang.String password) throws java.lang.IllegalArgumentException, java.security.NoSuchAlgorithmException
password
- the user password
java.lang.IllegalArgumentException
- if the supplied password is null
java.security.NoSuchAlgorithmException
- if there isn't any suitable cipher algorithmpublic static void computeNTLMResponse(byte[] lmPassword, byte[] ntPassword, byte[] nonce, byte[] lmResponse, byte[] ntResponse) throws java.lang.IllegalArgumentException, javax.crypto.NoSuchPaddingException, java.security.NoSuchAlgorithmException
Computes the NTLM response to the nonce based on the supplied hashed passwords.
If the hashed password are not available they can be computed from the cleartext password
by the means of computeLMPassword
and
computeNTPassword
methods.
lmPassword
- a 16-bytes array containing the Lan Manager hashed passwordntPassword
- a 16-bytes array containing the Lan Manager hashed passwordnonce
- a 8-bytes array representing the server's noncelmResponse
- a 24-bytes array that will contain the Lan Manager response after the method invocationntResponse
- a 24-bytes array that will contain the NT response after the method invocation
java.lang.IllegalArgumentException
- if a parameter has an illegal size
javax.crypto.NoSuchPaddingException
- if there isn't any suitable padding method
java.security.NoSuchAlgorithmException
- if there isn't any suitable cipher algorithmpublic static byte[] formatRequest(java.lang.String host, java.lang.String hostDomain) throws java.io.IOException
Builds a request message for the host of the specified domain that can be send to the server to start the NTLM protocol.
The returned message should be encoded according to protocol specific rules
(e.g. base 64 encoding).
The message format is discussed here.
host
- the name of the host that is authenticatinghostDomain
- the name of the domain to which the host belongs
java.io.IOException
- if an error occurs during the message formattingpublic static byte[] getNonce(byte[] msg) throws java.lang.IllegalArgumentException
Extracts from the server challenge response the nonce required to perform the authentication.
The received message should be decoded according to protocol specific rules
(e.g. base 64 encoding).
The message format is discussed here.
msg
- a byte array containing the server challenge message
java.lang.IllegalArgumentException
- if a parameter has an illegal sizepublic static byte[] formatResponse(java.lang.String host, java.lang.String user, java.lang.String userDomain, byte[] lmPassword, byte[] ntPassword, byte[] nonce) throws java.lang.IllegalArgumentException, java.io.IOException, java.security.NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException
Builds the nonce response message.
It requires the Lan Manager and NT hashed
version of user password, that can be computed from the cleartext version by
computeNTPassword
and
computeNTLMResponse
,
and the nonce obtained from the server by getNonce
.
The returned message should be encoded according to protocol specific rules
(e.g. base 64 encoding).
The message format is discussed here.
host
- the name of the host that is authenticatinguser
- the name of the useruserDomain
- the name of the domain to which the user belongslmPassword
- a 16-bytes array containing the Lan Manager hashed passwordntPassword
- a 16-bytes array containing the NT hashed passwordnonce
- a 8-byte array containing the nonce sent by server to reply to the request message
java.io.IOException
- if an error occurs during the message formatting
java.lang.IllegalArgumentException
- if a parameter has an illegal size
javax.crypto.NoSuchPaddingException
- if there isn't any suitable padding method
java.security.NoSuchAlgorithmException
- if there isn't any suitable cipher algorithm
|
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |