Path: blob/master/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java
41161 views
/*1* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.security.ntlm;2627import sun.security.action.GetBooleanAction;2829import static com.sun.security.ntlm.Version.*;30import java.io.IOException;31import java.nio.charset.StandardCharsets;32import java.security.InvalidKeyException;33import java.security.MessageDigest;34import java.security.NoSuchAlgorithmException;35import java.security.spec.InvalidKeySpecException;36import java.util.Arrays;37import java.util.Locale;38import javax.crypto.BadPaddingException;39import javax.crypto.Cipher;40import javax.crypto.IllegalBlockSizeException;41import javax.crypto.Mac;42import javax.crypto.NoSuchPaddingException;43import javax.crypto.SecretKey;44import javax.crypto.SecretKeyFactory;45import javax.crypto.spec.DESKeySpec;46import javax.crypto.spec.SecretKeySpec;4748/**49* NTLM authentication implemented according to MS-NLMP, version 12.150* @since 1.751*/52class NTLM {5354private final SecretKeyFactory fac;55private final Cipher cipher;56private final MessageDigest md4;57private final Mac hmac;58private final MessageDigest md5;59private static final boolean DEBUG60= GetBooleanAction.privilegedGetProperty("ntlm.debug");6162final Version v;6364final boolean writeLM;65final boolean writeNTLM;6667protected NTLM(String version) throws NTLMException {68if (version == null) version = "LMv2/NTLMv2";69switch (version) {70case "LM": v = NTLM; writeLM = true; writeNTLM = false; break;71case "NTLM": v = NTLM; writeLM = false; writeNTLM = true; break;72case "LM/NTLM": v = NTLM; writeLM = writeNTLM = true; break;73case "NTLM2": v = NTLM2; writeLM = writeNTLM = true; break;74case "LMv2": v = NTLMv2; writeLM = true; writeNTLM = false; break;75case "NTLMv2": v = NTLMv2; writeLM = false; writeNTLM = true; break;76case "LMv2/NTLMv2": v = NTLMv2; writeLM = writeNTLM = true; break;77default: throw new NTLMException(NTLMException.BAD_VERSION,78"Unknown version " + version);79}80try {81fac = SecretKeyFactory.getInstance ("DES");82cipher = Cipher.getInstance ("DES/ECB/NoPadding");83md4 = sun.security.provider.MD4.getInstance();84hmac = Mac.getInstance("HmacMD5");85md5 = MessageDigest.getInstance("MD5");86} catch (NoSuchPaddingException e) {87throw new AssertionError();88} catch (NoSuchAlgorithmException e) {89throw new AssertionError();90}91}9293/**94* Prints out a formatted string, called in various places inside then NTLM95* implementation for debugging/logging purposes. When the system property96* "ntlm.debug" is set, <code>System.out.printf(format, args)</code> is97* called. This method is designed to be overridden by child classes to98* match their own debugging/logging mechanisms.99* @param format a format string100* @param args the arguments referenced by <code>format</code>101* @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[])102*/103public void debug(String format, Object... args) {104if (DEBUG) {105System.out.printf(format, args);106}107}108109/**110* Prints out the content of a byte array, called in various places inside111* the NTLM implementation for debugging/logging purposes. When the system112* property "ntlm.debug" is set, the hexdump of the array is printed into113* System.out. This method is designed to be overridden by child classes to114* match their own debugging/logging mechanisms.115* @param bytes the byte array to print out116*/117public void debug(byte[] bytes) {118if (DEBUG) {119try {120new sun.security.util.HexDumpEncoder().encodeBuffer(bytes, System.out);121} catch (IOException ioe) {122// Impossible123}124}125}126127/**128* Reading an NTLM packet129*/130static class Reader {131132private final byte[] internal;133134Reader(byte[] data) {135internal = data;136}137138int readInt(int offset) throws NTLMException {139try {140return (internal[offset] & 0xff) +141((internal[offset+1] & 0xff) << 8) +142((internal[offset+2] & 0xff) << 16) +143((internal[offset+3] & 0xff) << 24);144} catch (ArrayIndexOutOfBoundsException ex) {145throw new NTLMException(NTLMException.PACKET_READ_ERROR,146"Input message incorrect size");147}148}149150int readShort(int offset) throws NTLMException {151try {152return (internal[offset] & 0xff) +153((internal[offset+1] & 0xff << 8));154} catch (ArrayIndexOutOfBoundsException ex) {155throw new NTLMException(NTLMException.PACKET_READ_ERROR,156"Input message incorrect size");157}158}159160byte[] readBytes(int offset, int len) throws NTLMException {161try {162return Arrays.copyOfRange(internal, offset, offset + len);163} catch (ArrayIndexOutOfBoundsException ex) {164throw new NTLMException(NTLMException.PACKET_READ_ERROR,165"Input message incorrect size");166}167}168169byte[] readSecurityBuffer(int offset) throws NTLMException {170int pos = readInt(offset+4);171if (pos == 0) return new byte[0];172try {173return Arrays.copyOfRange(174internal, pos, pos + readShort(offset));175} catch (ArrayIndexOutOfBoundsException ex) {176throw new NTLMException(NTLMException.PACKET_READ_ERROR,177"Input message incorrect size");178}179}180181String readSecurityBuffer(int offset, boolean unicode)182throws NTLMException {183byte[] raw = readSecurityBuffer(offset);184return raw == null ? null : new String(185raw, unicode ? StandardCharsets.UTF_16LE186: StandardCharsets.ISO_8859_1);187}188}189190/**191* Writing an NTLM packet192*/193static class Writer {194195private byte[] internal; // buffer196private int current; // current written content interface buffer197198/**199* Starts writing a NTLM packet200* @param type NEGOTIATE || CHALLENGE || AUTHENTICATE201* @param len the base length, without security buffers202*/203Writer(int type, int len) {204assert len < 256;205internal = new byte[256];206current = len;207System.arraycopy (208new byte[] {'N','T','L','M','S','S','P',0,(byte)type},2090, internal, 0, 9);210}211212void writeShort(int offset, int number) {213internal[offset] = (byte)(number);214internal[offset+1] = (byte)(number >> 8);215}216217void writeInt(int offset, int number) {218internal[offset] = (byte)(number);219internal[offset+1] = (byte)(number >> 8);220internal[offset+2] = (byte)(number >> 16);221internal[offset+3] = (byte)(number >> 24);222}223224void writeBytes(int offset, byte[] data) {225System.arraycopy(data, 0, internal, offset, data.length);226}227228void writeSecurityBuffer(int offset, byte[] data) {229if (data == null) {230writeShort(offset+4, current);231} else {232int len = data.length;233if (current + len > internal.length) {234internal = Arrays.copyOf(internal, current + len + 256);235}236writeShort(offset, len);237writeShort(offset+2, len);238writeShort(offset+4, current);239System.arraycopy(data, 0, internal, current, len);240current += len;241}242}243244void writeSecurityBuffer(int offset, String str, boolean unicode) {245writeSecurityBuffer(offset, str == null ? null : str.getBytes(246unicode ? StandardCharsets.UTF_16LE247: StandardCharsets.ISO_8859_1));248}249250byte[] getBytes() {251return Arrays.copyOf(internal, current);252}253}254255// LM/NTLM256257/* Convert a 7 byte array to an 8 byte array (for a des key with parity)258* input starts at offset off259*/260byte[] makeDesKey (byte[] input, int off) {261int[] in = new int [input.length];262for (int i=0; i<in.length; i++ ) {263in[i] = input[i]<0 ? input[i]+256: input[i];264}265byte[] out = new byte[8];266out[0] = (byte)in[off+0];267out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1));268out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2));269out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3));270out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4));271out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5));272out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6));273out[7] = (byte)((in[off+6] << 1) & 0xFF);274return out;275}276277byte[] calcLMHash (byte[] pwb) {278byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};279byte[] pwb1 = new byte [14];280int len = pwb.length;281if (len > 14)282len = 14;283System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */284285try {286DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0));287DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7));288289SecretKey key1 = fac.generateSecret (dks1);290SecretKey key2 = fac.generateSecret (dks2);291cipher.init (Cipher.ENCRYPT_MODE, key1);292byte[] out1 = cipher.doFinal (magic, 0, 8);293cipher.init (Cipher.ENCRYPT_MODE, key2);294byte[] out2 = cipher.doFinal (magic, 0, 8);295byte[] result = new byte [21];296System.arraycopy (out1, 0, result, 0, 8);297System.arraycopy (out2, 0, result, 8, 8);298return result;299} catch (InvalidKeyException ive) {300// Will not happen, all key material are 8 bytes301assert false;302} catch (InvalidKeySpecException ikse) {303// Will not happen, we only feed DESKeySpec to DES factory304assert false;305} catch (IllegalBlockSizeException ibse) {306// Will not happen, we encrypt 8 bytes307assert false;308} catch (BadPaddingException bpe) {309// Will not happen, this is encryption310assert false;311}312return null; // will not happen, we returned already313}314315byte[] calcNTHash (byte[] pw) {316byte[] out = md4.digest (pw);317byte[] result = new byte [21];318System.arraycopy (out, 0, result, 0, 16);319return result;320}321322/* key is a 21 byte array. Split it into 3 7 byte chunks,323* Convert each to 8 byte DES keys, encrypt the text arg with324* each key and return the three results in a sequential []325*/326byte[] calcResponse (byte[] key, byte[] text) {327try {328assert key.length == 21;329DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0));330DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7));331DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14));332SecretKey key1 = fac.generateSecret(dks1);333SecretKey key2 = fac.generateSecret(dks2);334SecretKey key3 = fac.generateSecret(dks3);335cipher.init(Cipher.ENCRYPT_MODE, key1);336byte[] out1 = cipher.doFinal(text, 0, 8);337cipher.init(Cipher.ENCRYPT_MODE, key2);338byte[] out2 = cipher.doFinal(text, 0, 8);339cipher.init(Cipher.ENCRYPT_MODE, key3);340byte[] out3 = cipher.doFinal(text, 0, 8);341byte[] result = new byte[24];342System.arraycopy(out1, 0, result, 0, 8);343System.arraycopy(out2, 0, result, 8, 8);344System.arraycopy(out3, 0, result, 16, 8);345return result;346} catch (IllegalBlockSizeException ex) { // None will happen347assert false;348} catch (BadPaddingException ex) {349assert false;350} catch (InvalidKeySpecException ex) {351assert false;352} catch (InvalidKeyException ex) {353assert false;354}355return null;356}357358// LMv2/NTLMv2359360byte[] hmacMD5(byte[] key, byte[] text) {361try {362SecretKeySpec skey =363new SecretKeySpec(Arrays.copyOf(key, 16), "HmacMD5");364hmac.init(skey);365return hmac.doFinal(text);366} catch (InvalidKeyException ex) {367assert false;368} catch (RuntimeException e) {369assert false;370}371return null;372}373374byte[] calcV2(byte[] nthash, String text, byte[] blob, byte[] challenge) {375byte[] ntlmv2hash = hmacMD5(nthash, text.getBytes(StandardCharsets.UTF_16LE));376byte[] cn = new byte[blob.length+8];377System.arraycopy(challenge, 0, cn, 0, 8);378System.arraycopy(blob, 0, cn, 8, blob.length);379byte[] result = new byte[16+blob.length];380System.arraycopy(hmacMD5(ntlmv2hash, cn), 0, result, 0, 16);381System.arraycopy(blob, 0, result, 16, blob.length);382return result;383}384385// NTLM2 LM/NTLM386387static byte[] ntlm2LM(byte[] nonce) {388return Arrays.copyOf(nonce, 24);389}390391byte[] ntlm2NTLM(byte[] ntlmHash, byte[] nonce, byte[] challenge) {392byte[] b = Arrays.copyOf(challenge, 16);393System.arraycopy(nonce, 0, b, 8, 8);394byte[] sesshash = Arrays.copyOf(md5.digest(b), 8);395return calcResponse(ntlmHash, sesshash);396}397398// Password in ASCII and UNICODE399400static byte[] getP1(char[] password) {401return new String(password).toUpperCase(Locale.ENGLISH)402.getBytes(StandardCharsets.ISO_8859_1);403}404405static byte[] getP2(char[] password) {406return new String(password).getBytes(StandardCharsets.UTF_16LE);407}408}409410411