1 31 32 package org.apache.commons.httpclient; 33 34 import java.io.UnsupportedEncodingException ; 35 import java.security.InvalidKeyException ; 36 import java.security.NoSuchAlgorithmException ; 37 38 import javax.crypto.BadPaddingException; 39 import javax.crypto.Cipher; 40 import javax.crypto.IllegalBlockSizeException; 41 import javax.crypto.NoSuchPaddingException; 42 import javax.crypto.spec.SecretKeySpec; 43 44 import org.apache.commons.httpclient.util.Base64; 45 import org.apache.commons.logging.Log; 46 import org.apache.commons.logging.LogFactory; 47 48 72 public final class NTLM { 73 74 75 private byte[] currentResponse; 76 77 78 private int currentPosition = 0; 79 80 81 private static final Log LOG = LogFactory.getLog(NTLM.class); 82 83 84 public static final String DEFAULT_CHARSET = "ASCII"; 85 86 97 public final String getResponseFor(String message, 98 String username, String password, String host, String domain) 99 throws HttpException { 100 101 final String response; 102 if (message == null || message.trim().equals("")) { 103 response = getType1Message(host, domain); 104 } else { 105 response = getType3Message(username, password, host, domain, 106 parseType2Message(message)); 107 } 108 return response; 109 } 110 111 117 private Cipher getCipher(byte[] key) throws HttpException { 118 try { 119 final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding"); 120 key = setupKey(key); 121 ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES")); 122 return ecipher; 123 } catch (NoSuchAlgorithmException e) { 124 throw new HttpException("DES encryption is not available."); 125 } catch (InvalidKeyException e) { 126 throw new HttpException("Invalid key for DES encryption."); 127 } catch (NoSuchPaddingException e) { 128 throw new HttpException( 129 "NoPadding option for DES is not available."); 130 } 131 } 132 133 138 private byte[] setupKey(byte[] key56) { 139 byte[] key = new byte[8]; 140 key[0] = (byte) ((key56[0] >> 1) & 0xff); 141 key[1] = (byte) ((((key56[0] & 0x01) << 6) 142 | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff); 143 key[2] = (byte) ((((key56[1] & 0x03) << 5) 144 | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff); 145 key[3] = (byte) ((((key56[2] & 0x07) << 4) 146 | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff); 147 key[4] = (byte) ((((key56[3] & 0x0f) << 3) 148 | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff); 149 key[5] = (byte) ((((key56[4] & 0x1f) << 2) 150 | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff); 151 key[6] = (byte) ((((key56[5] & 0x3f) << 1) 152 | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff); 153 key[7] = (byte) (key56[6] & 0x7f); 154 155 for (int i = 0; i < key.length; i++) { 156 key[i] = (byte) (key[i] << 1); 157 } 158 return key; 159 } 160 161 168 private byte[] encrypt(byte[] key, byte[] bytes) 169 throws HttpException { 170 Cipher ecipher = getCipher(key); 171 try { 172 byte[] enc = ecipher.doFinal(bytes); 173 return enc; 174 } catch (IllegalBlockSizeException e) { 175 throw new HttpException("Invalid block size for DES encryption."); 176 } catch (BadPaddingException e) { 177 throw new HttpException( 178 "Data not padded correctly for DES encryption."); 179 } 180 } 181 182 186 private void prepareResponse(int length) { 187 currentResponse = new byte[length]; 188 currentPosition = 0; 189 } 190 191 195 private void addByte(byte b) { 196 currentResponse[currentPosition] = b; 197 currentPosition++; 198 } 199 200 204 private void addBytes(byte[] bytes) { 205 for (int i = 0; i < bytes.length; i++) { 206 currentResponse[currentPosition] = bytes[i]; 207 currentPosition++; 208 } 209 } 210 211 216 private String getResponse() { 217 byte[] resp; 218 if (currentResponse.length > currentPosition) { 219 byte[] tmp = new byte[currentPosition]; 220 for (int i = 0; i < currentPosition; i++) { 221 tmp[i] = currentResponse[i]; 222 } 223 resp = tmp; 224 } else { 225 resp = currentResponse; 226 } 227 return HttpConstants.getString(Base64.encode(resp)); 228 } 229 230 238 private String getType1Message(String host, String domain) { 239 host = host.toUpperCase(); 240 domain = domain.toUpperCase(); 241 byte[] hostBytes = getBytes(host); 242 byte[] domainBytes = getBytes(domain); 243 244 int finalLength = 32 + hostBytes.length + domainBytes.length; 245 prepareResponse(finalLength); 246 247 byte[] protocol = getBytes("NTLMSSP"); 249 addBytes(protocol); 250 addByte((byte) 0); 251 252 addByte((byte) 1); 254 addByte((byte) 0); 255 addByte((byte) 0); 256 addByte((byte) 0); 257 258 addByte((byte) 6); 260 addByte((byte) 82); 261 addByte((byte) 0); 262 addByte((byte) 0); 263 264 int iDomLen = domainBytes.length; 266 byte[] domLen = convertShort(iDomLen); 267 addByte(domLen[0]); 268 addByte(domLen[1]); 269 270 addByte(domLen[0]); 272 addByte(domLen[1]); 273 274 byte[] domOff = convertShort(hostBytes.length + 32); 276 addByte(domOff[0]); 277 addByte(domOff[1]); 278 addByte((byte) 0); 279 addByte((byte) 0); 280 281 byte[] hostLen = convertShort(hostBytes.length); 283 addByte(hostLen[0]); 284 addByte(hostLen[1]); 285 286 addByte(hostLen[0]); 288 addByte(hostLen[1]); 289 290 byte[] hostOff = convertShort(32); 292 addByte(hostOff[0]); 293 addByte(hostOff[1]); 294 addByte((byte) 0); 295 addByte((byte) 0); 296 297 addBytes(hostBytes); 299 300 addBytes(domainBytes); 302 303 return getResponse(); 304 } 305 306 313 private byte[] parseType2Message(String message) { 314 byte[] msg = Base64.decode(getBytes(message)); 316 byte[] nonce = new byte[8]; 317 for (int i = 0; i < 8; i++) { 319 nonce[i] = msg[i + 24]; 320 } 321 return nonce; 322 } 323 324 337 private String getType3Message(String user, String password, 338 String host, String domain, byte[] nonce) 339 throws HttpException { 340 341 int ntRespLen = 0; 342 int lmRespLen = 24; 343 domain = domain.toUpperCase(); 344 host = host.toUpperCase(); 345 user = user.toUpperCase(); 346 byte[] domainBytes = getBytes(domain); 347 byte[] hostBytes = getBytes(host); 348 byte[] userBytes = getBytes(user); 349 int domainLen = domainBytes.length; 350 int hostLen = hostBytes.length; 351 int userLen = userBytes.length; 352 int finalLength = 64 + ntRespLen + lmRespLen + domainLen 353 + userLen + hostLen; 354 prepareResponse(finalLength); 355 byte[] ntlmssp = getBytes("NTLMSSP"); 356 addBytes(ntlmssp); 357 addByte((byte) 0); 358 addByte((byte) 3); 359 addByte((byte) 0); 360 addByte((byte) 0); 361 addByte((byte) 0); 362 363 addBytes(convertShort(24)); 365 addBytes(convertShort(24)); 366 367 addBytes(convertShort(finalLength - 24)); 369 addByte((byte) 0); 370 addByte((byte) 0); 371 372 addBytes(convertShort(0)); 374 addBytes(convertShort(0)); 375 376 addBytes(convertShort(finalLength)); 378 addByte((byte) 0); 379 addByte((byte) 0); 380 381 addBytes(convertShort(domainLen)); 383 addBytes(convertShort(domainLen)); 384 385 addBytes(convertShort(64)); 387 addByte((byte) 0); 388 addByte((byte) 0); 389 390 addBytes(convertShort(userLen)); 392 addBytes(convertShort(userLen)); 393 394 addBytes(convertShort(64 + domainLen)); 396 addByte((byte) 0); 397 addByte((byte) 0); 398 399 addBytes(convertShort(hostLen)); 401 addBytes(convertShort(hostLen)); 402 403 addBytes(convertShort(64 + domainLen + userLen)); 405 406 for (int i = 0; i < 6; i++) { 407 addByte((byte) 0); 408 } 409 410 addBytes(convertShort(finalLength)); 412 addByte((byte) 0); 413 addByte((byte) 0); 414 415 addByte((byte) 6); 417 addByte((byte) 82); 418 addByte((byte) 0); 419 addByte((byte) 0); 420 421 addBytes(domainBytes); 422 addBytes(userBytes); 423 addBytes(hostBytes); 424 addBytes(hashPassword(password, nonce)); 425 return getResponse(); 426 } 427 428 436 private byte[] hashPassword(String password, byte[] nonce) 437 throws HttpException { 438 byte[] passw = getBytes(password.toUpperCase()); 439 byte[] lmPw1 = new byte[7]; 440 byte[] lmPw2 = new byte[7]; 441 442 int len = passw.length; 443 if (len > 7) { 444 len = 7; 445 } 446 447 int idx; 448 for (idx = 0; idx < len; idx++) { 449 lmPw1[idx] = passw[idx]; 450 } 451 for (; idx < 7; idx++) { 452 lmPw1[idx] = (byte) 0; 453 } 454 455 len = passw.length; 456 if (len > 14) { 457 len = 14; 458 } 459 for (idx = 7; idx < len; idx++) { 460 lmPw2[idx - 7] = passw[idx]; 461 } 462 for (; idx < 14; idx++) { 463 lmPw2[idx - 7] = (byte) 0; 464 } 465 466 byte[] magic = { 468 (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21, 469 (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25 470 }; 471 472 byte[] lmHpw1; 473 lmHpw1 = encrypt(lmPw1, magic); 474 475 byte[] lmHpw2 = encrypt(lmPw2, magic); 476 477 byte[] lmHpw = new byte[21]; 478 for (int i = 0; i < lmHpw1.length; i++) { 479 lmHpw[i] = lmHpw1[i]; 480 } 481 for (int i = 0; i < lmHpw2.length; i++) { 482 lmHpw[i + 8] = lmHpw2[i]; 483 } 484 for (int i = 0; i < 5; i++) { 485 lmHpw[i + 16] = (byte) 0; 486 } 487 488 byte[] lmResp = new byte[24]; 490 calcResp(lmHpw, nonce, lmResp); 491 492 return lmResp; 493 } 494 495 505 private void calcResp(byte[] keys, byte[] plaintext, byte[] results) 506 throws HttpException { 507 byte[] keys1 = new byte[7]; 508 byte[] keys2 = new byte[7]; 509 byte[] keys3 = new byte[7]; 510 for (int i = 0; i < 7; i++) { 511 keys1[i] = keys[i]; 512 } 513 514 for (int i = 0; i < 7; i++) { 515 keys2[i] = keys[i + 7]; 516 } 517 518 for (int i = 0; i < 7; i++) { 519 keys3[i] = keys[i + 14]; 520 } 521 byte[] results1 = encrypt(keys1, plaintext); 522 523 byte[] results2 = encrypt(keys2, plaintext); 524 525 byte[] results3 = encrypt(keys3, plaintext); 526 527 for (int i = 0; i < 8; i++) { 528 results[i] = results1[i]; 529 } 530 for (int i = 0; i < 8; i++) { 531 results[i + 8] = results2[i]; 532 } 533 for (int i = 0; i < 8; i++) { 534 results[i + 16] = results3[i]; 535 } 536 } 537 538 543 private byte[] convertShort(int num) { 544 byte[] val = new byte[2]; 545 String hex = Integer.toString(num, 16); 546 while (hex.length() < 4) { 547 hex = "0" + hex; 548 } 549 String low = hex.substring(2, 4); 550 String high = hex.substring(0, 2); 551 552 val[0] = (byte) Integer.parseInt(low, 16); 553 val[1] = (byte) Integer.parseInt(high, 16); 554 return val; 555 } 556 557 562 private static byte[] getBytes(final String s) { 563 if (s == null) { 564 throw new IllegalArgumentException ("Parameter may not be null"); 565 } 566 try { 567 return s.getBytes(DEFAULT_CHARSET); 568 } catch (UnsupportedEncodingException unexpectedEncodingException) { 569 throw new RuntimeException ("NTLM requires ASCII support"); 570 } 571 } 572 } 573 | Popular Tags |