| java.lang.Object com.luigidragone.net.ntlm.NTLM
NTLM | public class NTLM (Code) | |
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 perform an authentication the following information are needed:
- the host name (with its own domain);
- the user name (with its own domain);
- the user password.
Alternatively, the user password can be replaced with its Lan Manager and
NT hashed versions. On a Windows system these data can be collected in the
registry, otherwise they can be extracted from a SAMBA password file.
Notice that the host and user domain could not be the same.
To start an NTLM authentication procedure (e.g. with a proxy server) build a
request message calling
NTLM.formatRequest(String,String) formatRequest and send it to the server.
Once the challenge packet has been correctly received extract from it the nonce
with
NTLM.getNonce(byte[]) getNonce function and use it to compute the
reply and build the response message with
NTLM.formatResponse(String,String,String,byte[],byte[],byte[]) 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
NTLM.computeLMPassword(String) computeLMPassword ,
NTLM.computeNTPassword(String) computeNTPassword and
NTLM.computeNTLMResponse(byte[],byte[],byte[],byte[],byte[]) computeNTLMResponse can be used to perform a SMB authentication too.
This implementation is based on:
- the reverse engineering of the NTLM protocol made by Ronald Tschalär
and available here;
- the documentation about NTLM provided with
L0phtCrack 1.5;
- the "Handbook of Applied Cryptography", freely available
here;
- the C source code of NTLM library in the
SAMBA Project.
Nevertheless, because there isn't any official protocol specification publicly
available there is any warranty that code works correctly and that it is
conforming to Microsoft NTLM protocol.
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).
author: Luigi Dragone (luigi@luigidragone.com) version: 1.0.1 See Also: NTLM Authentication Scheme for HTTP See Also: LanMan and NT Password Encryption in Samba 2.x See Also: "Handbook of Applied Cryptography" See Also: JCE See Also: Cryptix |
Field Summary | |
final protected static byte[] | MAGIC The magic number used to compute the Lan Manager hashed password. |
Constructor Summary | |
protected | NTLM() |
Method Summary | |
protected static Key | computeDESKey(byte[] keyData, int offset)
Computes an odd DES key from 56 bits represented as a 7-bytes array. | public static byte[] | computeLMPassword(String password) Computes the Lan Manager hashed version of a password. | public 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. | public static byte[] | computeNTPassword(String password) Computes the NT hashed version of a password. | protected static byte[] | encrypt(byte[] keys, byte[] plaintext) Encrypts the 8-bytes plain text three times with the 3 56-bits DES keys and
puts the result in a 24-bytes array. | public static byte[] | formatRequest(String host, 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.
The returned message should be encoded according to protocol specific rules
(e.g. | public static byte[] | formatResponse(String host, String user, String userDomain, byte[] lmPassword, byte[] ntPassword, byte[] nonce)
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
NTLM.computeNTPassword(String) computeNTPassword and
NTLM.computeNTLMResponse(byte[],byte[],byte[],byte[],byte[]) computeNTLMResponse ,
and the nonce obtained from the server by
NTLM.getNonce(byte[]) getNonce .
The returned message should be encoded according to protocol specific rules
(e.g. | protected static byte | getHiByte(char c) | protected static byte | getLoByte(char c) | public static byte[] | getNonce(byte[] msg)
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. | protected static short | swapBytes(short s) | protected static int | unsignedByteToInt(byte b)
Converts an unsigned byte to an unsigned integer. |
MAGIC | final protected static byte[] MAGIC(Code) | | The magic number used to compute the Lan Manager hashed password.
|
computeLMPassword | public static byte[] computeLMPassword(String password) throws IllegalArgumentException, NoSuchPaddingException, NoSuchAlgorithmException(Code) | | Computes the Lan Manager hashed version of a password.
Parameters: password - the user password the Lan Manager hashed version of the password in a 16-bytes array exception: IllegalArgumentException - if the supplied password is null exception: javax.crypto.NoSuchPaddingException - if there isn't any suitable padding method exception: NoSuchAlgorithmException - if there isn't any suitable cipher algorithm |
computeNTLMResponse | public static void computeNTLMResponse(byte[] lmPassword, byte[] ntPassword, byte[] nonce, byte[] lmResponse, byte[] ntResponse) throws IllegalArgumentException, NoSuchPaddingException, NoSuchAlgorithmException(Code) | |
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
NTLM.computeLMPassword(String) computeLMPassword and
NTLM.computeNTPassword(String) computeNTPassword methods.
Parameters: lmPassword - a 16-bytes array containing the Lan Manager hashed password Parameters: ntPassword - a 16-bytes array containing the Lan Manager hashed password Parameters: nonce - a 8-bytes array representing the server's nonce Parameters: lmResponse - a 24-bytes array that will contain the Lan Manager response after the method invocation Parameters: ntResponse - a 24-bytes array that will contain the NT response after the method invocation exception: IllegalArgumentException - if a parameter has an illegal size exception: javax.crypto.NoSuchPaddingException - if there isn't any suitable padding method exception: NoSuchAlgorithmException - if there isn't any suitable cipher algorithm |
encrypt | protected static byte[] encrypt(byte[] keys, byte[] plaintext) throws InvalidKeyException, NoSuchAlgorithmException, javax.crypto.NoSuchPaddingException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, ShortBufferException(Code) | | Encrypts the 8-bytes plain text three times with the 3 56-bits DES keys and
puts the result in a 24-bytes array.
Parameters: keys - a 21-bytes array containing 3 56-bits DES keys Parameters: plaintext - a 8-bytes array to be encrypted a 24-bytes array containing the plaintext DES encrypted with the supplied keys exception: InvalidKeyException - exception: NoSuchAlgorithmException - exception: javax.crypto.NoSuchPaddingException - exception: InvalidKeySpecException - exception: BadPaddingException - exception: IllegalBlockSizeException - exception: ShortBufferException - |
formatRequest | public static byte[] formatRequest(String host, String hostDomain) throws IOException(Code) | |
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.
Parameters: host - the name of the host that is authenticating Parameters: hostDomain - the name of the domain to which the host belongs the request message to send to server to open an authentication procedure exception: IOException - if an error occurs during the message formatting See Also: NTLM Authentication Scheme for HTTP |
formatResponse | public static byte[] formatResponse(String host, String user, String userDomain, byte[] lmPassword, byte[] ntPassword, byte[] nonce) throws IllegalArgumentException, IOException, NoSuchAlgorithmException, NoSuchPaddingException(Code) | |
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
NTLM.computeNTPassword(String) computeNTPassword and
NTLM.computeNTLMResponse(byte[],byte[],byte[],byte[],byte[]) computeNTLMResponse ,
and the nonce obtained from the server by
NTLM.getNonce(byte[]) getNonce .
The returned message should be encoded according to protocol specific rules
(e.g. base 64 encoding).
The message format is discussed here.
Parameters: host - the name of the host that is authenticating Parameters: user - the name of the user Parameters: userDomain - the name of the domain to which the user belongs Parameters: lmPassword - a 16-bytes array containing the Lan Manager hashed password Parameters: ntPassword - a 16-bytes array containing the NT hashed password Parameters: nonce - a 8-byte array containing the nonce sent by server to reply to the request message the challenge response message to send to server to complete the authentication procedure exception: IOException - if an error occurs during the message formatting exception: IllegalArgumentException - if a parameter has an illegal size exception: javax.crypto.NoSuchPaddingException - if there isn't any suitable padding method exception: NoSuchAlgorithmException - if there isn't any suitable cipher algorithm See Also: NTLM Authentication Scheme for HTTP |
getHiByte | protected static byte getHiByte(char c)(Code) | | |
getLoByte | protected static byte getLoByte(char c)(Code) | | |
getNonce | public static byte[] getNonce(byte[] msg) throws IllegalArgumentException(Code) | |
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.
Parameters: msg - a byte array containing the server challenge message exception: IllegalArgumentException - if a parameter has an illegal size See Also: NTLM Authentication Scheme for HTTP |
swapBytes | protected static short swapBytes(short s)(Code) | | |
unsignedByteToInt | protected static int unsignedByteToInt(byte b)(Code) | |
Converts an unsigned byte to an unsigned integer.
Notice that Java bytes are always signed, but the cryptographic algorithms
rely on unsigned ones, that can be simulated in this way.
A bit mask is employed to prevent that the signum bit is extended to MSBs.
|
|
|