NTLM.java revision 12816:1de2065763c1
1/* 2 * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package com.sun.security.ntlm; 27 28import static com.sun.security.ntlm.Version.*; 29import java.io.IOException; 30import java.io.UnsupportedEncodingException; 31import java.security.InvalidKeyException; 32import java.security.MessageDigest; 33import java.security.NoSuchAlgorithmException; 34import java.security.spec.InvalidKeySpecException; 35import java.util.Arrays; 36import java.util.Locale; 37import javax.crypto.BadPaddingException; 38import javax.crypto.Cipher; 39import javax.crypto.IllegalBlockSizeException; 40import javax.crypto.Mac; 41import javax.crypto.NoSuchPaddingException; 42import javax.crypto.SecretKey; 43import javax.crypto.SecretKeyFactory; 44import javax.crypto.spec.DESKeySpec; 45import javax.crypto.spec.SecretKeySpec; 46 47/** 48 * NTLM authentication implemented according to MS-NLMP, version 12.1 49 * @since 1.7 50 */ 51class NTLM { 52 53 private final SecretKeyFactory fac; 54 private final Cipher cipher; 55 private final MessageDigest md4; 56 private final Mac hmac; 57 private final MessageDigest md5; 58 private static final boolean DEBUG = 59 java.security.AccessController.doPrivileged( 60 new sun.security.action.GetBooleanAction("ntlm.debug")) 61 .booleanValue(); 62 63 final Version v; 64 65 final boolean writeLM; 66 final boolean writeNTLM; 67 68 protected NTLM(String version) throws NTLMException { 69 if (version == null) version = "LMv2/NTLMv2"; 70 switch (version) { 71 case "LM": v = NTLM; writeLM = true; writeNTLM = false; break; 72 case "NTLM": v = NTLM; writeLM = false; writeNTLM = true; break; 73 case "LM/NTLM": v = NTLM; writeLM = writeNTLM = true; break; 74 case "NTLM2": v = NTLM2; writeLM = writeNTLM = true; break; 75 case "LMv2": v = NTLMv2; writeLM = true; writeNTLM = false; break; 76 case "NTLMv2": v = NTLMv2; writeLM = false; writeNTLM = true; break; 77 case "LMv2/NTLMv2": v = NTLMv2; writeLM = writeNTLM = true; break; 78 default: throw new NTLMException(NTLMException.BAD_VERSION, 79 "Unknown version " + version); 80 } 81 try { 82 fac = SecretKeyFactory.getInstance ("DES"); 83 cipher = Cipher.getInstance ("DES/ECB/NoPadding"); 84 md4 = sun.security.provider.MD4.getInstance(); 85 hmac = Mac.getInstance("HmacMD5"); 86 md5 = MessageDigest.getInstance("MD5"); 87 } catch (NoSuchPaddingException e) { 88 throw new AssertionError(); 89 } catch (NoSuchAlgorithmException e) { 90 throw new AssertionError(); 91 } 92 } 93 94 /** 95 * Prints out a formatted string, called in various places inside then NTLM 96 * implementation for debugging/logging purposes. When the system property 97 * "ntlm.debug" is set, <code>System.out.printf(format, args)</code> is 98 * called. This method is designed to be overridden by child classes to 99 * match their own debugging/logging mechanisms. 100 * @param format a format string 101 * @param args the arguments referenced by <code>format</code> 102 * @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[]) 103 */ 104 public void debug(String format, Object... args) { 105 if (DEBUG) { 106 System.out.printf(format, args); 107 } 108 } 109 110 /** 111 * Prints out the content of a byte array, called in various places inside 112 * the NTLM implementation for debugging/logging purposes. When the system 113 * property "ntlm.debug" is set, the hexdump of the array is printed into 114 * System.out. This method is designed to be overridden by child classes to 115 * match their own debugging/logging mechanisms. 116 * @param bytes the byte array to print out 117 */ 118 public void debug(byte[] bytes) { 119 if (DEBUG) { 120 try { 121 new sun.misc.HexDumpEncoder().encodeBuffer(bytes, System.out); 122 } catch (IOException ioe) { 123 // Impossible 124 } 125 } 126 } 127 128 /** 129 * Reading an NTLM packet 130 */ 131 static class Reader { 132 133 private final byte[] internal; 134 135 Reader(byte[] data) { 136 internal = data; 137 } 138 139 int readInt(int offset) throws NTLMException { 140 try { 141 return (internal[offset] & 0xff) + 142 ((internal[offset+1] & 0xff) << 8) + 143 ((internal[offset+2] & 0xff) << 16) + 144 ((internal[offset+3] & 0xff) << 24); 145 } catch (ArrayIndexOutOfBoundsException ex) { 146 throw new NTLMException(NTLMException.PACKET_READ_ERROR, 147 "Input message incorrect size"); 148 } 149 } 150 151 int readShort(int offset) throws NTLMException { 152 try { 153 return (internal[offset] & 0xff) + 154 ((internal[offset+1] & 0xff << 8)); 155 } catch (ArrayIndexOutOfBoundsException ex) { 156 throw new NTLMException(NTLMException.PACKET_READ_ERROR, 157 "Input message incorrect size"); 158 } 159 } 160 161 byte[] readBytes(int offset, int len) throws NTLMException { 162 try { 163 return Arrays.copyOfRange(internal, offset, offset + len); 164 } catch (ArrayIndexOutOfBoundsException ex) { 165 throw new NTLMException(NTLMException.PACKET_READ_ERROR, 166 "Input message incorrect size"); 167 } 168 } 169 170 byte[] readSecurityBuffer(int offset) throws NTLMException { 171 int pos = readInt(offset+4); 172 if (pos == 0) return null; 173 try { 174 return Arrays.copyOfRange( 175 internal, pos, pos + readShort(offset)); 176 } catch (ArrayIndexOutOfBoundsException ex) { 177 throw new NTLMException(NTLMException.PACKET_READ_ERROR, 178 "Input message incorrect size"); 179 } 180 } 181 182 String readSecurityBuffer(int offset, boolean unicode) 183 throws NTLMException { 184 byte[] raw = readSecurityBuffer(offset); 185 try { 186 return raw == null ? null : new String( 187 raw, unicode ? "UnicodeLittleUnmarked" : "ISO8859_1"); 188 } catch (UnsupportedEncodingException ex) { 189 throw new NTLMException(NTLMException.PACKET_READ_ERROR, 190 "Invalid input encoding"); 191 } 192 } 193 } 194 195 /** 196 * Writing an NTLM packet 197 */ 198 static class Writer { 199 200 private byte[] internal; // buffer 201 private int current; // current written content interface buffer 202 203 /** 204 * Starts writing a NTLM packet 205 * @param type NEGOTIATE || CHALLENGE || AUTHENTICATE 206 * @param len the base length, without security buffers 207 */ 208 Writer(int type, int len) { 209 assert len < 256; 210 internal = new byte[256]; 211 current = len; 212 System.arraycopy ( 213 new byte[] {'N','T','L','M','S','S','P',0,(byte)type}, 214 0, internal, 0, 9); 215 } 216 217 void writeShort(int offset, int number) { 218 internal[offset] = (byte)(number); 219 internal[offset+1] = (byte)(number >> 8); 220 } 221 222 void writeInt(int offset, int number) { 223 internal[offset] = (byte)(number); 224 internal[offset+1] = (byte)(number >> 8); 225 internal[offset+2] = (byte)(number >> 16); 226 internal[offset+3] = (byte)(number >> 24); 227 } 228 229 void writeBytes(int offset, byte[] data) { 230 System.arraycopy(data, 0, internal, offset, data.length); 231 } 232 233 void writeSecurityBuffer(int offset, byte[] data) { 234 if (data == null) { 235 writeShort(offset+4, current); 236 } else { 237 int len = data.length; 238 if (current + len > internal.length) { 239 internal = Arrays.copyOf(internal, current + len + 256); 240 } 241 writeShort(offset, len); 242 writeShort(offset+2, len); 243 writeShort(offset+4, current); 244 System.arraycopy(data, 0, internal, current, len); 245 current += len; 246 } 247 } 248 249 void writeSecurityBuffer(int offset, String str, boolean unicode) { 250 try { 251 writeSecurityBuffer(offset, str == null ? null : str.getBytes( 252 unicode ? "UnicodeLittleUnmarked" : "ISO8859_1")); 253 } catch (UnsupportedEncodingException ex) { 254 assert false; 255 } 256 } 257 258 byte[] getBytes() { 259 return Arrays.copyOf(internal, current); 260 } 261 } 262 263 // LM/NTLM 264 265 /* Convert a 7 byte array to an 8 byte array (for a des key with parity) 266 * input starts at offset off 267 */ 268 byte[] makeDesKey (byte[] input, int off) { 269 int[] in = new int [input.length]; 270 for (int i=0; i<in.length; i++ ) { 271 in[i] = input[i]<0 ? input[i]+256: input[i]; 272 } 273 byte[] out = new byte[8]; 274 out[0] = (byte)in[off+0]; 275 out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1)); 276 out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); 277 out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); 278 out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); 279 out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); 280 out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); 281 out[7] = (byte)((in[off+6] << 1) & 0xFF); 282 return out; 283 } 284 285 byte[] calcLMHash (byte[] pwb) { 286 byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; 287 byte[] pwb1 = new byte [14]; 288 int len = pwb.length; 289 if (len > 14) 290 len = 14; 291 System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */ 292 293 try { 294 DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0)); 295 DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7)); 296 297 SecretKey key1 = fac.generateSecret (dks1); 298 SecretKey key2 = fac.generateSecret (dks2); 299 cipher.init (Cipher.ENCRYPT_MODE, key1); 300 byte[] out1 = cipher.doFinal (magic, 0, 8); 301 cipher.init (Cipher.ENCRYPT_MODE, key2); 302 byte[] out2 = cipher.doFinal (magic, 0, 8); 303 byte[] result = new byte [21]; 304 System.arraycopy (out1, 0, result, 0, 8); 305 System.arraycopy (out2, 0, result, 8, 8); 306 return result; 307 } catch (InvalidKeyException ive) { 308 // Will not happen, all key material are 8 bytes 309 assert false; 310 } catch (InvalidKeySpecException ikse) { 311 // Will not happen, we only feed DESKeySpec to DES factory 312 assert false; 313 } catch (IllegalBlockSizeException ibse) { 314 // Will not happen, we encrypt 8 bytes 315 assert false; 316 } catch (BadPaddingException bpe) { 317 // Will not happen, this is encryption 318 assert false; 319 } 320 return null; // will not happen, we returned already 321 } 322 323 byte[] calcNTHash (byte[] pw) { 324 byte[] out = md4.digest (pw); 325 byte[] result = new byte [21]; 326 System.arraycopy (out, 0, result, 0, 16); 327 return result; 328 } 329 330 /* key is a 21 byte array. Split it into 3 7 byte chunks, 331 * Convert each to 8 byte DES keys, encrypt the text arg with 332 * each key and return the three results in a sequential [] 333 */ 334 byte[] calcResponse (byte[] key, byte[] text) { 335 try { 336 assert key.length == 21; 337 DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0)); 338 DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7)); 339 DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14)); 340 SecretKey key1 = fac.generateSecret(dks1); 341 SecretKey key2 = fac.generateSecret(dks2); 342 SecretKey key3 = fac.generateSecret(dks3); 343 cipher.init(Cipher.ENCRYPT_MODE, key1); 344 byte[] out1 = cipher.doFinal(text, 0, 8); 345 cipher.init(Cipher.ENCRYPT_MODE, key2); 346 byte[] out2 = cipher.doFinal(text, 0, 8); 347 cipher.init(Cipher.ENCRYPT_MODE, key3); 348 byte[] out3 = cipher.doFinal(text, 0, 8); 349 byte[] result = new byte[24]; 350 System.arraycopy(out1, 0, result, 0, 8); 351 System.arraycopy(out2, 0, result, 8, 8); 352 System.arraycopy(out3, 0, result, 16, 8); 353 return result; 354 } catch (IllegalBlockSizeException ex) { // None will happen 355 assert false; 356 } catch (BadPaddingException ex) { 357 assert false; 358 } catch (InvalidKeySpecException ex) { 359 assert false; 360 } catch (InvalidKeyException ex) { 361 assert false; 362 } 363 return null; 364 } 365 366 // LMv2/NTLMv2 367 368 byte[] hmacMD5(byte[] key, byte[] text) { 369 try { 370 SecretKeySpec skey = 371 new SecretKeySpec(Arrays.copyOf(key, 16), "HmacMD5"); 372 hmac.init(skey); 373 return hmac.doFinal(text); 374 } catch (InvalidKeyException ex) { 375 assert false; 376 } catch (RuntimeException e) { 377 assert false; 378 } 379 return null; 380 } 381 382 byte[] calcV2(byte[] nthash, String text, byte[] blob, byte[] challenge) { 383 try { 384 byte[] ntlmv2hash = hmacMD5(nthash, 385 text.getBytes("UnicodeLittleUnmarked")); 386 byte[] cn = new byte[blob.length+8]; 387 System.arraycopy(challenge, 0, cn, 0, 8); 388 System.arraycopy(blob, 0, cn, 8, blob.length); 389 byte[] result = new byte[16+blob.length]; 390 System.arraycopy(hmacMD5(ntlmv2hash, cn), 0, result, 0, 16); 391 System.arraycopy(blob, 0, result, 16, blob.length); 392 return result; 393 } catch (UnsupportedEncodingException ex) { 394 assert false; 395 } 396 return null; 397 } 398 399 // NTLM2 LM/NTLM 400 401 static byte[] ntlm2LM(byte[] nonce) { 402 return Arrays.copyOf(nonce, 24); 403 } 404 405 byte[] ntlm2NTLM(byte[] ntlmHash, byte[] nonce, byte[] challenge) { 406 byte[] b = Arrays.copyOf(challenge, 16); 407 System.arraycopy(nonce, 0, b, 8, 8); 408 byte[] sesshash = Arrays.copyOf(md5.digest(b), 8); 409 return calcResponse(ntlmHash, sesshash); 410 } 411 412 // Password in ASCII and UNICODE 413 414 static byte[] getP1(char[] password) { 415 try { 416 return new String(password).toUpperCase( 417 Locale.ENGLISH).getBytes("ISO8859_1"); 418 } catch (UnsupportedEncodingException ex) { 419 return null; 420 } 421 } 422 423 static byte[] getP2(char[] password) { 424 try { 425 return new String(password).getBytes("UnicodeLittleUnmarked"); 426 } catch (UnsupportedEncodingException ex) { 427 return null; 428 } 429 } 430} 431