📄 ntlm.java
字号:
//Gets the first 14-bytes of the ASCII upper cased password
int len = password.length();
if(len > 14)
len = 14;
Cipher c = Cipher.getInstance("DES/ECB/NoPadding");
byte[] lm_pw = new byte[14];
byte[] bytes = password.toUpperCase().getBytes();
int i;
for(i = 0; i < len; i++)
lm_pw[i] = bytes[i];
for(; i < 14; i++)
lm_pw[i] = 0;
byte[] lm_hpw = new byte[16];
//Builds a first DES key with its first 7 bytes
Key k = computeDESKey(lm_pw, 0);
c.init(Cipher.ENCRYPT_MODE, k);
//Hashes the MAGIC number with this key into the first 8 bytes of the result
c.doFinal(MAGIC, 0, 8, lm_hpw, 0);
//Repeats the work with the last 7 bytes to gets the last 8 bytes of the result
k = computeDESKey(lm_pw, 7);
c.init(Cipher.ENCRYPT_MODE, k);
c.doFinal(MAGIC, 0, 8, lm_hpw, 8);
return lm_hpw;
} catch(InvalidKeySpecException ex) {
return null;
} catch(InvalidKeyException ex) {
return null;
} catch(BadPaddingException ex) {
return null;
} catch(IllegalBlockSizeException ex) {
return null;
} catch(ShortBufferException ex) {
return null;
}
}
/**
* Computes the NT hashed version of a password.
*
* @param password the user password
*
* @return the NT hashed version of the password in a 16-bytes array
*
* @exception IllegalArgumentException if the supplied password is null
* @exception NoSuchAlgorithmException if there isn't any suitable cipher algorithm
*/
public static byte[] computeNTPassword(String password) throws IllegalArgumentException, NoSuchAlgorithmException {
if(password == null)
throw new IllegalArgumentException("password : null value not allowed");
//Gets the first 14-bytes of the UNICODE password
int len = password.length();
if(len > 14)
len = 14;
byte[] nt_pw = new byte[2 * len];
for(int i = 0; i < len; i++) {
char ch = password.charAt(i);
nt_pw[2 * i] = getLoByte(ch);
nt_pw[2 * i + 1] = getHiByte(ch);
}
//Return its MD4 digest as the hashed version
MessageDigest md = MessageDigest.getInstance("MD4");
return md.digest(nt_pw);
}
/**
* <p>
* Computes the NTLM response to the nonce based on the supplied hashed passwords.
* </p>
* <p>
* If the hashed password are not available they can be computed from the cleartext password
* by the means of {@link #computeLMPassword(String) computeLMPassword} and
* {@link #computeNTPassword(String) computeNTPassword} methods.
* </p>
*
* @param lmPassword a 16-bytes array containing the Lan Manager hashed password
* @param ntPassword a 16-bytes array containing the Lan Manager hashed password
* @param nonce a 8-bytes array representing the server's nonce
* @param lmResponse a 24-bytes array that will contain the Lan Manager response after the method invocation
* @param 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
*/
public static void computeNTLMResponse(byte[] lmPassword, byte[] ntPassword, byte[] nonce, byte[] lmResponse, byte[] ntResponse) throws IllegalArgumentException, NoSuchPaddingException, NoSuchAlgorithmException {
if(lmPassword.length != 16)
throw new IllegalArgumentException("lmPassword : illegal size");
if(ntPassword.length != 16)
throw new IllegalArgumentException("ntPassword : illegal size");
if(nonce.length != 8)
throw new IllegalArgumentException("nonce : illegal size");
if(lmResponse.length != 24)
throw new IllegalArgumentException("lmResponse : illegal size");
if(ntResponse.length != 24)
throw new IllegalArgumentException("ntResponse : illegal size");
try {
//Puts the hashed passwords into 21-bytes arrays with trailing 0s
byte[] lmHPw = new byte[21];
byte[] ntHPw = new byte[21];
System.arraycopy(lmPassword, 0, lmHPw, 0, 16);
System.arraycopy(ntPassword, 0, ntHPw, 0, 16);
for(int i = 16; i < 21; i++) {
lmHPw[i] = 0;
ntHPw[i] = 0;
}
//Encrypts the nonce with the padded hashed passwords to compute the responses
System.arraycopy(encrypt(lmHPw, nonce), 0, lmResponse, 0, 24);
System.arraycopy(encrypt(ntHPw, nonce), 0, ntResponse, 0, 24);
} catch(ShortBufferException ex) {
} catch(IllegalBlockSizeException ex) {
} catch(BadPaddingException ex) {
} catch(InvalidKeySpecException ex) {
} catch(InvalidKeyException ex) {
}
}
/**
* <p>
* Builds a request message for the host of the specified domain that can be send
* to the server to start the NTLM protocol.
* </p>
* <p>
* The returned message should be encoded according to protocol specific rules
* (e.g. base 64 encoding).<br>
* The message format is discussed <a href="http://www.innovation.ch/java/ntlm.html">here</a>.
* </p>
*
* @param host the name of the host that is authenticating
* @param hostDomain the name of the domain to which the host belongs
*
* @return the request message to send to server to open an authentication procedure
*
* @exception IOException if an error occurs during the message formatting
*
* @see <a href="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
*
*/
public static byte[] formatRequest(String host, String hostDomain) throws IOException {
hostDomain = hostDomain.toUpperCase();
host = host.toUpperCase();
short domainLen = (short)hostDomain.length();
short hostLen = (short)host.length();
short hostOff = 0x20;
short domainOff = (short)(hostOff + hostLen);
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
DataOutputStream dataOut = new DataOutputStream(os);
dataOut.writeBytes("NTLMSSP\0");
dataOut.writeByte(0x01);
dataOut.writeByte(0x00);
dataOut.writeByte(0x00);
dataOut.writeByte(0x00);
dataOut.writeShort(swapBytes((short)0xb203));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(domainLen));
dataOut.writeShort(swapBytes(domainLen));
dataOut.writeShort(swapBytes(domainOff));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(hostLen));
dataOut.writeShort(swapBytes(hostLen));
dataOut.writeShort(swapBytes(hostOff));
dataOut.writeShort(0x0000);
dataOut.write(host.getBytes());
dataOut.write(hostDomain.getBytes());
dataOut.flush();
return os.toByteArray();
}
/**
* <p>
* Extracts from the server challenge response the nonce required to perform
* the authentication.
* </p>
* <p>
* The received message should be decoded according to protocol specific rules
* (e.g. base 64 encoding).<br>
* The message format is discussed <a href="http://www.innovation.ch/java/ntlm.html">here</a>.
* </p>
*
* @param msg a byte array containing the server challenge message
*
* @exception IllegalArgumentException if a parameter has an illegal size
*
* @see <a href="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
*/
public static byte[] getNonce(byte[] msg) throws IllegalArgumentException {
if(msg.length < 32)
throw new IllegalArgumentException("msg : illegal size");
byte[] nonce = new byte[8];
System.arraycopy(msg, 24, nonce, 0, 8);
return nonce;
}
/**
* <p>
* Builds the nonce response message.
* </p>
* <p>It requires the Lan Manager and NT hashed
* version of user password, that can be computed from the cleartext version by
* {@link #computeNTPassword(String) computeNTPassword} and
* {@link #computeNTLMResponse(byte[], byte[], byte[], byte[], byte[]) computeNTLMResponse},
* and the nonce obtained from the server by {@link #getNonce(byte[]) getNonce}.<br>
* The returned message should be encoded according to protocol specific rules
* (e.g. base 64 encoding).<br>
* The message format is discussed <a href="http://www.innovation.ch/java/ntlm.html">here</a>.
* </p>
*
* @param host the name of the host that is authenticating
* @param user the name of the user
* @param userDomain the name of the domain to which the user belongs
* @param lmPassword a 16-bytes array containing the Lan Manager hashed password
* @param ntPassword a 16-bytes array containing the NT hashed password
* @param nonce a 8-byte array containing the nonce sent by server to reply to the request message
*
* @return 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 <a href="http://www.innovation.ch/java/ntlm.html">NTLM Authentication Scheme for HTTP</a>
*/
public static byte[] formatResponse(String host, String user, String userDomain, byte[] lmPassword, byte[] ntPassword, byte[] nonce) throws IllegalArgumentException, IOException, NoSuchAlgorithmException, NoSuchPaddingException {
if(host == null)
throw new IllegalArgumentException("host : null value not allowed");
if(user == null)
throw new IllegalArgumentException("user : null value not allowed");
if(userDomain == null)
throw new IllegalArgumentException("userDomain : null value not allowed");
if(lmPassword == null)
throw new IllegalArgumentException("lmPassword : null value not allowed");
if(ntPassword == null)
throw new IllegalArgumentException("ntPassword : null value not allowed");
if(nonce == null)
throw new IllegalArgumentException("nonce : null value not allowed");
if(lmPassword.length != 16)
throw new IllegalArgumentException("lmPassword : illegal size");
if(ntPassword.length != 16)
throw new IllegalArgumentException("ntPassword : illegal size");
if(nonce.length != 8)
throw new IllegalArgumentException("nonce : illegal size");
byte[] lmResponse = new byte[24];
byte[] ntResponse = new byte[24];
computeNTLMResponse(lmPassword, ntPassword, nonce, lmResponse, ntResponse);
userDomain = userDomain.toUpperCase();
host = host.toUpperCase();
short lmRespLen = (short)0x18;
short ntRespLen = (short)0x18;
short domainLen = (short)(2 * userDomain.length());
short hostLen = (short)(2 * host.length());
short userLen = (short)(2 * user.length());
short domainOff = (short)0x40;
short userOff = (short)(domainOff + domainLen);
short hostOff = (short)(userOff + userLen);
short lmRespOff = (short)(hostOff + hostLen);
short ntRespOff = (short)(lmRespOff + lmRespLen);
short msgLen = (short)(ntRespOff + ntRespLen);
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
DataOutputStream dataOut = new DataOutputStream(os);
dataOut.writeBytes("NTLMSSP\0");
dataOut.writeByte(0x03);
dataOut.writeByte(0x00);
dataOut.writeByte(0x00);
dataOut.writeByte(0x00);
dataOut.writeShort(swapBytes(lmRespLen));
dataOut.writeShort(swapBytes(lmRespLen));
dataOut.writeShort(swapBytes(lmRespOff));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(ntRespLen));
dataOut.writeShort(swapBytes(ntRespLen));
dataOut.writeShort(swapBytes(ntRespOff));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(domainLen));
dataOut.writeShort(swapBytes(domainLen));
dataOut.writeShort(swapBytes(domainOff));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(userLen));
dataOut.writeShort(swapBytes(userLen));
dataOut.writeShort(swapBytes(userOff));
dataOut.writeShort(0x0000);
dataOut.writeShort(swapBytes(hostLen));
dataOut.writeShort(swapBytes(hostLen));
dataOut.writeShort(swapBytes(hostOff));
dataOut.writeShort(0x0000);
dataOut.writeInt(0x00000000);
dataOut.writeShort(swapBytes(msgLen));
dataOut.writeShort(0x0000);
dataOut.writeShort(0x0000); // dataOut.writeShort(swapBytes((short)0x8201));
dataOut.writeShort(0x0000);
for(int i = 0; i < userDomain.length(); i++)
dataOut.writeShort(swapBytes((short)userDomain.charAt(i)));
for(int i = 0; i < user.length(); i++)
dataOut.writeShort(swapBytes((short)user.charAt(i)));
for(int i = 0; i < host.length(); i++)
dataOut.writeShort(swapBytes((short)host.charAt(i)));
dataOut.write(lmResponse);
dataOut.write(ntResponse);
dataOut.flush();
return os.toByteArray();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -