Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/security/ntlm/NTLM.java
38924 views
/*1* Copyright (c) 2010, 2013, 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 static com.sun.security.ntlm.Version.*;28import java.io.IOException;29import java.io.UnsupportedEncodingException;30import java.security.InvalidKeyException;31import java.security.MessageDigest;32import java.security.NoSuchAlgorithmException;33import java.security.spec.InvalidKeySpecException;34import java.util.Arrays;35import java.util.Locale;36import javax.crypto.BadPaddingException;37import javax.crypto.Cipher;38import javax.crypto.IllegalBlockSizeException;39import javax.crypto.Mac;40import javax.crypto.NoSuchPaddingException;41import javax.crypto.SecretKey;42import javax.crypto.SecretKeyFactory;43import javax.crypto.spec.DESKeySpec;44import javax.crypto.spec.SecretKeySpec;4546/**47* NTLM authentication implemented according to MS-NLMP, version 12.148* @since 1.749*/50class NTLM {5152private final SecretKeyFactory fac;53private final Cipher cipher;54private final MessageDigest md4;55private final Mac hmac;56private final MessageDigest md5;57private static final boolean DEBUG =58System.getProperty("ntlm.debug") != null;5960final Version v;6162final boolean writeLM;63final boolean writeNTLM;6465protected NTLM(String version) throws NTLMException {66if (version == null) version = "LMv2/NTLMv2";67switch (version) {68case "LM": v = NTLM; writeLM = true; writeNTLM = false; break;69case "NTLM": v = NTLM; writeLM = false; writeNTLM = true; break;70case "LM/NTLM": v = NTLM; writeLM = writeNTLM = true; break;71case "NTLM2": v = NTLM2; writeLM = writeNTLM = true; break;72case "LMv2": v = NTLMv2; writeLM = true; writeNTLM = false; break;73case "NTLMv2": v = NTLMv2; writeLM = false; writeNTLM = true; break;74case "LMv2/NTLMv2": v = NTLMv2; writeLM = writeNTLM = true; break;75default: throw new NTLMException(NTLMException.BAD_VERSION,76"Unknown version " + version);77}78try {79fac = SecretKeyFactory.getInstance ("DES");80cipher = Cipher.getInstance ("DES/ECB/NoPadding");81md4 = sun.security.provider.MD4.getInstance();82hmac = Mac.getInstance("HmacMD5");83md5 = MessageDigest.getInstance("MD5");84} catch (NoSuchPaddingException e) {85throw new AssertionError();86} catch (NoSuchAlgorithmException e) {87throw new AssertionError();88}89}9091/**92* Prints out a formatted string, called in various places inside then NTLM93* implementation for debugging/logging purposes. When the system property94* "ntlm.debug" is set, <code>System.out.printf(format, args)</code> is95* called. This method is designed to be overridden by child classes to96* match their own debugging/logging mechanisms.97* @param format a format string98* @param args the arguments referenced by <code>format</code>99* @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[])100*/101public void debug(String format, Object... args) {102if (DEBUG) {103System.out.printf(format, args);104}105}106107/**108* Prints out the content of a byte array, called in various places inside109* the NTLM implementation for debugging/logging purposes. When the system110* property "ntlm.debug" is set, the hexdump of the array is printed into111* System.out. This method is designed to be overridden by child classes to112* match their own debugging/logging mechanisms.113* @param bytes the byte array to print out114*/115public void debug(byte[] bytes) {116if (DEBUG) {117try {118new sun.misc.HexDumpEncoder().encodeBuffer(bytes, System.out);119} catch (IOException ioe) {120// Impossible121}122}123}124125/**126* Reading an NTLM packet127*/128static class Reader {129130private final byte[] internal;131132Reader(byte[] data) {133internal = data;134}135136int readInt(int offset) throws NTLMException {137try {138return (internal[offset] & 0xff) +139((internal[offset+1] & 0xff) << 8) +140((internal[offset+2] & 0xff) << 16) +141((internal[offset+3] & 0xff) << 24);142} catch (ArrayIndexOutOfBoundsException ex) {143throw new NTLMException(NTLMException.PACKET_READ_ERROR,144"Input message incorrect size");145}146}147148int readShort(int offset) throws NTLMException {149try {150return (internal[offset] & 0xff) +151((internal[offset+1] & 0xff << 8));152} catch (ArrayIndexOutOfBoundsException ex) {153throw new NTLMException(NTLMException.PACKET_READ_ERROR,154"Input message incorrect size");155}156}157158byte[] readBytes(int offset, int len) throws NTLMException {159try {160return Arrays.copyOfRange(internal, offset, offset + len);161} catch (ArrayIndexOutOfBoundsException ex) {162throw new NTLMException(NTLMException.PACKET_READ_ERROR,163"Input message incorrect size");164}165}166167byte[] readSecurityBuffer(int offset) throws NTLMException {168int pos = readInt(offset+4);169if (pos == 0) return new byte[0];170try {171return Arrays.copyOfRange(172internal, pos, pos + readShort(offset));173} catch (ArrayIndexOutOfBoundsException ex) {174throw new NTLMException(NTLMException.PACKET_READ_ERROR,175"Input message incorrect size");176}177}178179String readSecurityBuffer(int offset, boolean unicode)180throws NTLMException {181byte[] raw = readSecurityBuffer(offset);182try {183return raw == null ? null : new String(184raw, unicode ? "UnicodeLittleUnmarked" : "ISO8859_1");185} catch (UnsupportedEncodingException ex) {186throw new NTLMException(NTLMException.PACKET_READ_ERROR,187"Invalid input encoding");188}189}190}191192/**193* Writing an NTLM packet194*/195static class Writer {196197private byte[] internal; // buffer198private int current; // current written content interface buffer199200/**201* Starts writing a NTLM packet202* @param type NEGOTIATE || CHALLENGE || AUTHENTICATE203* @param len the base length, without security buffers204*/205Writer(int type, int len) {206assert len < 256;207internal = new byte[256];208current = len;209System.arraycopy (210new byte[] {'N','T','L','M','S','S','P',0,(byte)type},2110, internal, 0, 9);212}213214void writeShort(int offset, int number) {215internal[offset] = (byte)(number);216internal[offset+1] = (byte)(number >> 8);217}218219void writeInt(int offset, int number) {220internal[offset] = (byte)(number);221internal[offset+1] = (byte)(number >> 8);222internal[offset+2] = (byte)(number >> 16);223internal[offset+3] = (byte)(number >> 24);224}225226void writeBytes(int offset, byte[] data) {227System.arraycopy(data, 0, internal, offset, data.length);228}229230void writeSecurityBuffer(int offset, byte[] data) {231if (data == null) {232writeShort(offset+4, current);233} else {234int len = data.length;235if (current + len > internal.length) {236internal = Arrays.copyOf(internal, current + len + 256);237}238writeShort(offset, len);239writeShort(offset+2, len);240writeShort(offset+4, current);241System.arraycopy(data, 0, internal, current, len);242current += len;243}244}245246void writeSecurityBuffer(int offset, String str, boolean unicode) {247try {248writeSecurityBuffer(offset, str == null ? null : str.getBytes(249unicode ? "UnicodeLittleUnmarked" : "ISO8859_1"));250} catch (UnsupportedEncodingException ex) {251assert false;252}253}254255byte[] getBytes() {256return Arrays.copyOf(internal, current);257}258}259260// LM/NTLM261262/* Convert a 7 byte array to an 8 byte array (for a des key with parity)263* input starts at offset off264*/265byte[] makeDesKey (byte[] input, int off) {266int[] in = new int [input.length];267for (int i=0; i<in.length; i++ ) {268in[i] = input[i]<0 ? input[i]+256: input[i];269}270byte[] out = new byte[8];271out[0] = (byte)in[off+0];272out[1] = (byte)(((in[off+0] << 7) & 0xFF) | (in[off+1] >> 1));273out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2));274out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3));275out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4));276out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5));277out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6));278out[7] = (byte)((in[off+6] << 1) & 0xFF);279return out;280}281282byte[] calcLMHash (byte[] pwb) {283byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};284byte[] pwb1 = new byte [14];285int len = pwb.length;286if (len > 14)287len = 14;288System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */289290try {291DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0));292DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7));293294SecretKey key1 = fac.generateSecret (dks1);295SecretKey key2 = fac.generateSecret (dks2);296cipher.init (Cipher.ENCRYPT_MODE, key1);297byte[] out1 = cipher.doFinal (magic, 0, 8);298cipher.init (Cipher.ENCRYPT_MODE, key2);299byte[] out2 = cipher.doFinal (magic, 0, 8);300byte[] result = new byte [21];301System.arraycopy (out1, 0, result, 0, 8);302System.arraycopy (out2, 0, result, 8, 8);303return result;304} catch (InvalidKeyException ive) {305// Will not happen, all key material are 8 bytes306assert false;307} catch (InvalidKeySpecException ikse) {308// Will not happen, we only feed DESKeySpec to DES factory309assert false;310} catch (IllegalBlockSizeException ibse) {311// Will not happen, we encrypt 8 bytes312assert false;313} catch (BadPaddingException bpe) {314// Will not happen, this is encryption315assert false;316}317return null; // will not happen, we returned already318}319320byte[] calcNTHash (byte[] pw) {321byte[] out = md4.digest (pw);322byte[] result = new byte [21];323System.arraycopy (out, 0, result, 0, 16);324return result;325}326327/* key is a 21 byte array. Split it into 3 7 byte chunks,328* Convert each to 8 byte DES keys, encrypt the text arg with329* each key and return the three results in a sequential []330*/331byte[] calcResponse (byte[] key, byte[] text) {332try {333assert key.length == 21;334DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0));335DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7));336DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14));337SecretKey key1 = fac.generateSecret(dks1);338SecretKey key2 = fac.generateSecret(dks2);339SecretKey key3 = fac.generateSecret(dks3);340cipher.init(Cipher.ENCRYPT_MODE, key1);341byte[] out1 = cipher.doFinal(text, 0, 8);342cipher.init(Cipher.ENCRYPT_MODE, key2);343byte[] out2 = cipher.doFinal(text, 0, 8);344cipher.init(Cipher.ENCRYPT_MODE, key3);345byte[] out3 = cipher.doFinal(text, 0, 8);346byte[] result = new byte[24];347System.arraycopy(out1, 0, result, 0, 8);348System.arraycopy(out2, 0, result, 8, 8);349System.arraycopy(out3, 0, result, 16, 8);350return result;351} catch (IllegalBlockSizeException ex) { // None will happen352assert false;353} catch (BadPaddingException ex) {354assert false;355} catch (InvalidKeySpecException ex) {356assert false;357} catch (InvalidKeyException ex) {358assert false;359}360return null;361}362363// LMv2/NTLMv2364365byte[] hmacMD5(byte[] key, byte[] text) {366try {367SecretKeySpec skey =368new SecretKeySpec(Arrays.copyOf(key, 16), "HmacMD5");369hmac.init(skey);370return hmac.doFinal(text);371} catch (InvalidKeyException ex) {372assert false;373} catch (RuntimeException e) {374assert false;375}376return null;377}378379byte[] calcV2(byte[] nthash, String text, byte[] blob, byte[] challenge) {380try {381byte[] ntlmv2hash = hmacMD5(nthash,382text.getBytes("UnicodeLittleUnmarked"));383byte[] cn = new byte[blob.length+8];384System.arraycopy(challenge, 0, cn, 0, 8);385System.arraycopy(blob, 0, cn, 8, blob.length);386byte[] result = new byte[16+blob.length];387System.arraycopy(hmacMD5(ntlmv2hash, cn), 0, result, 0, 16);388System.arraycopy(blob, 0, result, 16, blob.length);389return result;390} catch (UnsupportedEncodingException ex) {391assert false;392}393return null;394}395396// NTLM2 LM/NTLM397398static byte[] ntlm2LM(byte[] nonce) {399return Arrays.copyOf(nonce, 24);400}401402byte[] ntlm2NTLM(byte[] ntlmHash, byte[] nonce, byte[] challenge) {403byte[] b = Arrays.copyOf(challenge, 16);404System.arraycopy(nonce, 0, b, 8, 8);405byte[] sesshash = Arrays.copyOf(md5.digest(b), 8);406return calcResponse(ntlmHash, sesshash);407}408409// Password in ASCII and UNICODE410411static byte[] getP1(char[] password) {412try {413return new String(password).toUpperCase(414Locale.ENGLISH).getBytes("ISO8859_1");415} catch (UnsupportedEncodingException ex) {416return null;417}418}419420static byte[] getP2(char[] password) {421try {422return new String(password).getBytes("UnicodeLittleUnmarked");423} catch (UnsupportedEncodingException ex) {424return null;425}426}427}428429430