1 30 31 package org.apache.commons.httpclient.auth; 32 33 import java.security.InvalidKeyException ; 34 import java.security.NoSuchAlgorithmException ; 35 36 import javax.crypto.BadPaddingException; 37 import javax.crypto.Cipher; 38 import javax.crypto.IllegalBlockSizeException; 39 import javax.crypto.NoSuchPaddingException; 40 import javax.crypto.spec.SecretKeySpec; 41 42 import org.apache.commons.codec.binary.Base64; 43 import org.apache.commons.httpclient.util.EncodingUtil; 44 45 67 final class NTLM { 68 69 70 public static final String DEFAULT_CHARSET = "ASCII"; 71 72 73 private byte[] currentResponse; 74 75 76 private int currentPosition = 0; 77 78 79 private String credentialCharset = DEFAULT_CHARSET; 80 81 92 public final String getResponseFor(String message, 93 String username, String password, String host, String domain) 94 throws AuthenticationException { 95 96 final String response; 97 if (message == null || message.trim().equals("")) { 98 response = getType1Message(host, domain); 99 } else { 100 response = getType3Message(username, password, host, domain, 101 parseType2Message(message)); 102 } 103 return response; 104 } 105 106 112 private Cipher getCipher(byte[] key) throws AuthenticationException { 113 try { 114 final Cipher ecipher = Cipher.getInstance("DES/ECB/NoPadding"); 115 key = setupKey(key); 116 ecipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "DES")); 117 return ecipher; 118 } catch (NoSuchAlgorithmException e) { 119 throw new AuthenticationException("DES encryption is not available.", e); 120 } catch (InvalidKeyException e) { 121 throw new AuthenticationException("Invalid key for DES encryption.", e); 122 } catch (NoSuchPaddingException e) { 123 throw new AuthenticationException( 124 "NoPadding option for DES is not available.", e); 125 } 126 } 127 128 133 private byte[] setupKey(byte[] key56) { 134 byte[] key = new byte[8]; 135 key[0] = (byte) ((key56[0] >> 1) & 0xff); 136 key[1] = (byte) ((((key56[0] & 0x01) << 6) 137 | (((key56[1] & 0xff) >> 2) & 0xff)) & 0xff); 138 key[2] = (byte) ((((key56[1] & 0x03) << 5) 139 | (((key56[2] & 0xff) >> 3) & 0xff)) & 0xff); 140 key[3] = (byte) ((((key56[2] & 0x07) << 4) 141 | (((key56[3] & 0xff) >> 4) & 0xff)) & 0xff); 142 key[4] = (byte) ((((key56[3] & 0x0f) << 3) 143 | (((key56[4] & 0xff) >> 5) & 0xff)) & 0xff); 144 key[5] = (byte) ((((key56[4] & 0x1f) << 2) 145 | (((key56[5] & 0xff) >> 6) & 0xff)) & 0xff); 146 key[6] = (byte) ((((key56[5] & 0x3f) << 1) 147 | (((key56[6] & 0xff) >> 7) & 0xff)) & 0xff); 148 key[7] = (byte) (key56[6] & 0x7f); 149 150 for (int i = 0; i < key.length; i++) { 151 key[i] = (byte) (key[i] << 1); 152 } 153 return key; 154 } 155 156 163 private byte[] encrypt(byte[] key, byte[] bytes) 164 throws AuthenticationException { 165 Cipher ecipher = getCipher(key); 166 try { 167 byte[] enc = ecipher.doFinal(bytes); 168 return enc; 169 } catch (IllegalBlockSizeException e) { 170 throw new AuthenticationException("Invalid block size for DES encryption.", e); 171 } catch (BadPaddingException e) { 172 throw new AuthenticationException("Data not padded correctly for DES encryption.", e); 173 } 174 } 175 176 180 private void prepareResponse(int length) { 181 currentResponse = new byte[length]; 182 currentPosition = 0; 183 } 184 185 189 private void addByte(byte b) { 190 currentResponse[currentPosition] = b; 191 currentPosition++; 192 } 193 194 198 private void addBytes(byte[] bytes) { 199 for (int i = 0; i < bytes.length; i++) { 200 currentResponse[currentPosition] = bytes[i]; 201 currentPosition++; 202 } 203 } 204 205 210 private String getResponse() { 211 byte[] resp; 212 if (currentResponse.length > currentPosition) { 213 byte[] tmp = new byte[currentPosition]; 214 for (int i = 0; i < currentPosition; i++) { 215 tmp[i] = currentResponse[i]; 216 } 217 resp = tmp; 218 } else { 219 resp = currentResponse; 220 } 221 return EncodingUtil.getAsciiString(Base64.encodeBase64(resp)); 222 } 223 224 232 public String getType1Message(String host, String domain) { 233 host = host.toUpperCase(); 234 domain = domain.toUpperCase(); 235 byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET); 236 byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET); 237 238 int finalLength = 32 + hostBytes.length + domainBytes.length; 239 prepareResponse(finalLength); 240 241 byte[] protocol = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET); 243 addBytes(protocol); 244 addByte((byte) 0); 245 246 addByte((byte) 1); 248 addByte((byte) 0); 249 addByte((byte) 0); 250 addByte((byte) 0); 251 252 addByte((byte) 6); 254 addByte((byte) 82); 255 addByte((byte) 0); 256 addByte((byte) 0); 257 258 int iDomLen = domainBytes.length; 260 byte[] domLen = convertShort(iDomLen); 261 addByte(domLen[0]); 262 addByte(domLen[1]); 263 264 addByte(domLen[0]); 266 addByte(domLen[1]); 267 268 byte[] domOff = convertShort(hostBytes.length + 32); 270 addByte(domOff[0]); 271 addByte(domOff[1]); 272 addByte((byte) 0); 273 addByte((byte) 0); 274 275 byte[] hostLen = convertShort(hostBytes.length); 277 addByte(hostLen[0]); 278 addByte(hostLen[1]); 279 280 addByte(hostLen[0]); 282 addByte(hostLen[1]); 283 284 byte[] hostOff = convertShort(32); 286 addByte(hostOff[0]); 287 addByte(hostOff[1]); 288 addByte((byte) 0); 289 addByte((byte) 0); 290 291 addBytes(hostBytes); 293 294 addBytes(domainBytes); 296 297 return getResponse(); 298 } 299 300 307 public byte[] parseType2Message(String message) { 308 byte[] msg = Base64.decodeBase64(EncodingUtil.getBytes(message, DEFAULT_CHARSET)); 310 byte[] nonce = new byte[8]; 311 for (int i = 0; i < 8; i++) { 313 nonce[i] = msg[i + 24]; 314 } 315 return nonce; 316 } 317 318 331 public String getType3Message(String user, String password, 332 String host, String domain, byte[] nonce) 333 throws AuthenticationException { 334 335 int ntRespLen = 0; 336 int lmRespLen = 24; 337 domain = domain.toUpperCase(); 338 host = host.toUpperCase(); 339 user = user.toUpperCase(); 340 byte[] domainBytes = EncodingUtil.getBytes(domain, DEFAULT_CHARSET); 341 byte[] hostBytes = EncodingUtil.getBytes(host, DEFAULT_CHARSET); 342 byte[] userBytes = EncodingUtil.getBytes(user, credentialCharset); 343 int domainLen = domainBytes.length; 344 int hostLen = hostBytes.length; 345 int userLen = userBytes.length; 346 int finalLength = 64 + ntRespLen + lmRespLen + domainLen 347 + userLen + hostLen; 348 prepareResponse(finalLength); 349 byte[] ntlmssp = EncodingUtil.getBytes("NTLMSSP", DEFAULT_CHARSET); 350 addBytes(ntlmssp); 351 addByte((byte) 0); 352 addByte((byte) 3); 353 addByte((byte) 0); 354 addByte((byte) 0); 355 addByte((byte) 0); 356 357 addBytes(convertShort(24)); 359 addBytes(convertShort(24)); 360 361 addBytes(convertShort(finalLength - 24)); 363 addByte((byte) 0); 364 addByte((byte) 0); 365 366 addBytes(convertShort(0)); 368 addBytes(convertShort(0)); 369 370 addBytes(convertShort(finalLength)); 372 addByte((byte) 0); 373 addByte((byte) 0); 374 375 addBytes(convertShort(domainLen)); 377 addBytes(convertShort(domainLen)); 378 379 addBytes(convertShort(64)); 381 addByte((byte) 0); 382 addByte((byte) 0); 383 384 addBytes(convertShort(userLen)); 386 addBytes(convertShort(userLen)); 387 388 addBytes(convertShort(64 + domainLen)); 390 addByte((byte) 0); 391 addByte((byte) 0); 392 393 addBytes(convertShort(hostLen)); 395 addBytes(convertShort(hostLen)); 396 397 addBytes(convertShort(64 + domainLen + userLen)); 399 400 for (int i = 0; i < 6; i++) { 401 addByte((byte) 0); 402 } 403 404 addBytes(convertShort(finalLength)); 406 addByte((byte) 0); 407 addByte((byte) 0); 408 409 addByte((byte) 6); 411 addByte((byte) 82); 412 addByte((byte) 0); 413 addByte((byte) 0); 414 415 addBytes(domainBytes); 416 addBytes(userBytes); 417 addBytes(hostBytes); 418 addBytes(hashPassword(password, nonce)); 419 return getResponse(); 420 } 421 422 430 private byte[] hashPassword(String password, byte[] nonce) 431 throws AuthenticationException { 432 byte[] passw = EncodingUtil.getBytes(password.toUpperCase(), credentialCharset); 433 byte[] lmPw1 = new byte[7]; 434 byte[] lmPw2 = new byte[7]; 435 436 int len = passw.length; 437 if (len > 7) { 438 len = 7; 439 } 440 441 int idx; 442 for (idx = 0; idx < len; idx++) { 443 lmPw1[idx] = passw[idx]; 444 } 445 for (; idx < 7; idx++) { 446 lmPw1[idx] = (byte) 0; 447 } 448 449 len = passw.length; 450 if (len > 14) { 451 len = 14; 452 } 453 for (idx = 7; idx < len; idx++) { 454 lmPw2[idx - 7] = passw[idx]; 455 } 456 for (; idx < 14; idx++) { 457 lmPw2[idx - 7] = (byte) 0; 458 } 459 460 byte[] magic = { 462 (byte) 0x4B, (byte) 0x47, (byte) 0x53, (byte) 0x21, 463 (byte) 0x40, (byte) 0x23, (byte) 0x24, (byte) 0x25 464 }; 465 466 byte[] lmHpw1; 467 lmHpw1 = encrypt(lmPw1, magic); 468 469 byte[] lmHpw2 = encrypt(lmPw2, magic); 470 471 byte[] lmHpw = new byte[21]; 472 for (int i = 0; i < lmHpw1.length; i++) { 473 lmHpw[i] = lmHpw1[i]; 474 } 475 for (int i = 0; i < lmHpw2.length; i++) { 476 lmHpw[i + 8] = lmHpw2[i]; 477 } 478 for (int i = 0; i < 5; i++) { 479 lmHpw[i + 16] = (byte) 0; 480 } 481 482 byte[] lmResp = new byte[24]; 484 calcResp(lmHpw, nonce, lmResp); 485 486 return lmResp; 487 } 488 489 499 private void calcResp(byte[] keys, byte[] plaintext, byte[] results) 500 throws AuthenticationException { 501 byte[] keys1 = new byte[7]; 502 byte[] keys2 = new byte[7]; 503 byte[] keys3 = new byte[7]; 504 for (int i = 0; i < 7; i++) { 505 keys1[i] = keys[i]; 506 } 507 508 for (int i = 0; i < 7; i++) { 509 keys2[i] = keys[i + 7]; 510 } 511 512 for (int i = 0; i < 7; i++) { 513 keys3[i] = keys[i + 14]; 514 } 515 byte[] results1 = encrypt(keys1, plaintext); 516 517 byte[] results2 = encrypt(keys2, plaintext); 518 519 byte[] results3 = encrypt(keys3, plaintext); 520 521 for (int i = 0; i < 8; i++) { 522 results[i] = results1[i]; 523 } 524 for (int i = 0; i < 8; i++) { 525 results[i + 8] = results2[i]; 526 } 527 for (int i = 0; i < 8; i++) { 528 results[i + 16] = results3[i]; 529 } 530 } 531 532 537 private byte[] convertShort(int num) { 538 byte[] val = new byte[2]; 539 String hex = Integer.toString(num, 16); 540 while (hex.length() < 4) { 541 hex = "0" + hex; 542 } 543 String low = hex.substring(2, 4); 544 String high = hex.substring(0, 2); 545 546 val[0] = (byte) Integer.parseInt(low, 16); 547 val[1] = (byte) Integer.parseInt(high, 16); 548 return val; 549 } 550 551 554 public String getCredentialCharset() { 555 return credentialCharset; 556 } 557 558 561 public void setCredentialCharset(String credentialCharset) { 562 this.credentialCharset = credentialCharset; 563 } 564 565 } 566 | Popular Tags |