FileDocCategorySizeDatePackage
NtlmPasswordAuthentication.javaAPI DocJCIFS 1.3.17 API23573Tue Oct 18 15:26:24 BST 2011jcifs.smb

NtlmPasswordAuthentication

public final class NtlmPasswordAuthentication extends Object implements Serializable, Principal
This class stores and encrypts NTLM user credentials. The default credentials are retrieved from the jcifs.smb.client.domain, jcifs.smb.client.username, and jcifs.smb.client.password properties.

Read jCIFS Exceptions and NtlmAuthenticator for related information.

Fields Summary
private static final int
LM_COMPATIBILITY
private static final Random
RANDOM
private static LogStream
log
private static final byte[]
S8
static String
DEFAULT_DOMAIN
static String
DEFAULT_USERNAME
static String
DEFAULT_PASSWORD
static final String
BLANK
public static final NtlmPasswordAuthentication
ANONYMOUS
static final NtlmPasswordAuthentication
NULL
static final NtlmPasswordAuthentication
GUEST
static final NtlmPasswordAuthentication
DEFAULT
String
domain
String
username
String
password
byte[]
ansiHash
byte[]
unicodeHash
boolean
hashesExternal
byte[]
clientChallenge
byte[]
challenge
Constructors Summary
public NtlmPasswordAuthentication(String userInfo)
Create an NtlmPasswordAuthentication object from the userinfo component of an SMB URL like "domain;user:pass". This constructor is used internally be jCIFS when parsing SMB URLs.


                          

         
        domain = username = password = null;

        if( userInfo != null ) {
            try {
                userInfo = unescape( userInfo );
            } catch( UnsupportedEncodingException uee ) {
            }
            int i, u, end;
            char c;

            end = userInfo.length();
            for( i = 0, u = 0; i < end; i++ ) {
                c = userInfo.charAt( i );
                if( c == ';" ) {
                    domain = userInfo.substring( 0, i );
                    u = i + 1;
                } else if( c == ':" ) {
                    password = userInfo.substring( i + 1 );
                    break;
                }
            }
            username = userInfo.substring( u, i );
        }

        initDefaults();

        if( domain == null ) this.domain = DEFAULT_DOMAIN;
        if( username == null ) this.username = DEFAULT_USERNAME;
        if( password == null ) this.password = DEFAULT_PASSWORD;
    
public NtlmPasswordAuthentication(String domain, String username, String password)
Create an NtlmPasswordAuthentication object from a domain, username, and password. Parameters that are null will be substituted with jcifs.smb.client.domain, jcifs.smb.client.username, jcifs.smb.client.password property values.

        int ci;

        if (username != null) {
            ci = username.indexOf('@");
            if (ci > 0) {
                domain = username.substring(ci + 1);
                username = username.substring(0, ci);
            } else {
                ci = username.indexOf('\\");
                if (ci > 0) {
                    domain = username.substring(0, ci);
                    username = username.substring(ci + 1);
                }
            }
        }

        this.domain = domain;
        this.username = username;
        this.password = password;

        initDefaults();

        if( domain == null ) this.domain = DEFAULT_DOMAIN;
        if( username == null ) this.username = DEFAULT_USERNAME;
        if( password == null ) this.password = DEFAULT_PASSWORD;
    
public NtlmPasswordAuthentication(String domain, String username, byte[] challenge, byte[] ansiHash, byte[] unicodeHash)
Create an NtlmPasswordAuthentication object with raw password hashes. This is used exclusively by the jcifs.http.NtlmSsp class which is in turn used by NTLM HTTP authentication functionality.

        if( domain == null || username == null ||
                                    ansiHash == null || unicodeHash == null ) {
            throw new IllegalArgumentException( "External credentials cannot be null" );
        }
        this.domain = domain;
        this.username = username;
        this.password = null;
        this.challenge = challenge;
        this.ansiHash = ansiHash;
        this.unicodeHash = unicodeHash;
        hashesExternal = true;
    
Methods Summary
private static voidE(byte[] key, byte[] data, byte[] e)

    /* Accepts key multiple of 7
     * Returns enc multiple of 8
     * Multiple is the same like: 21 byte key gives 24 byte result
     */
               
        byte[] key7 = new byte[7];
        byte[] e8 = new byte[8];

        for( int i = 0; i < key.length / 7; i++ ) {
            System.arraycopy( key, i * 7, key7, 0, 7 );
            DES des = new DES( key7 );
            des.encrypt( data, e8 );
            System.arraycopy( e8, 0, e, i * 8, 8 );
        }
    
static byte[]computeResponse(byte[] responseKey, byte[] serverChallenge, byte[] clientData, int offset, int length)

        HMACT64 hmac = new HMACT64(responseKey);
        hmac.update(serverChallenge);
        hmac.update(clientData, offset, length);
        byte[] mac = hmac.digest();
        byte[] ret = new byte[mac.length + clientData.length];
        System.arraycopy(mac, 0, ret, 0, mac.length);
        System.arraycopy(clientData, 0, ret, mac.length, clientData.length);
        return ret;
    
public booleanequals(java.lang.Object obj)
Compares two NtlmPasswordAuthentication objects for equality. Two NtlmPasswordAuthentication objects are equal if their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one NtlmPasswordAuthentication object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method.

        if( obj instanceof NtlmPasswordAuthentication ) {
            NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj;
            if( ntlm.domain.toUpperCase().equals( domain.toUpperCase() ) &&
                        ntlm.username.toUpperCase().equals( username.toUpperCase() )) {
                if( hashesExternal && ntlm.hashesExternal ) {
                    return Arrays.equals( ansiHash, ntlm.ansiHash ) &&
                                Arrays.equals( unicodeHash, ntlm.unicodeHash );
                    /* This still isn't quite right. If one npa object does not have external
                     * hashes and the other does then they will not be considered equal even
                     * though they may be.
                     */
                } else if( !hashesExternal && password.equals( ntlm.password )) {
                    return true;
                }
            }
        }
        return false;
    
public byte[]getAnsiHash(byte[] challenge)
Computes the 24 byte ANSI password hash given the 8 byte server challenge.

        if( hashesExternal ) {
            return ansiHash;
        }
        switch (LM_COMPATIBILITY) {
        case 0:
        case 1:
            return getPreNTLMResponse( password, challenge );
        case 2:
            return getNTLMResponse( password, challenge );
        case 3:
        case 4:
        case 5:
            if( clientChallenge == null ) {
                clientChallenge = new byte[8];
                RANDOM.nextBytes( clientChallenge );
            }
            return getLMv2Response(domain, username, password, challenge,
                    clientChallenge);
        default:
            return getPreNTLMResponse( password, challenge );
        }
    
public java.lang.StringgetDomain()
Returns the domain.

        return domain;
    
public static byte[]getLMv2Response(byte[] responseKeyLM, byte[] serverChallenge, byte[] clientChallenge)

        return NtlmPasswordAuthentication.computeResponse(responseKeyLM,
                    serverChallenge,
                    clientChallenge,
                    0,
                    clientChallenge.length);
    
public static byte[]getLMv2Response(java.lang.String domain, java.lang.String user, java.lang.String password, byte[] challenge, byte[] clientChallenge)
Creates the LMv2 response for the supplied information.

param
domain The domain in which the username exists.
param
user The username.
param
password The user's password.
param
challenge The server challenge.
param
clientChallenge The client challenge (nonce).

        try {
            byte[] hash = new byte[16];
            byte[] response = new byte[24];
// The next 2-1/2 lines of this should be placed with nTOWFv1 in place of password
            MD4 md4 = new MD4();
            md4.update(password.getBytes(SmbConstants.UNI_ENCODING));
            HMACT64 hmac = new HMACT64(md4.digest());
            hmac.update(user.toUpperCase().getBytes(SmbConstants.UNI_ENCODING));
            hmac.update(domain.toUpperCase().getBytes(SmbConstants.UNI_ENCODING));
            hmac = new HMACT64(hmac.digest());
            hmac.update(challenge);
            hmac.update(clientChallenge);
            hmac.digest(response, 0, 16);
            System.arraycopy(clientChallenge, 0, response, 16, 8);
            return response;
        } catch (Exception ex) {
            if( log.level > 0 )
                ex.printStackTrace( log );
            return null;
        }
    
public static byte[]getNTLM2Response(byte[] nTOWFv1, byte[] serverChallenge, byte[] clientChallenge)

        byte[] sessionHash = new byte[8];

        try {
            MessageDigest md5;
            md5 = MessageDigest.getInstance("MD5");
            md5.update(serverChallenge);
            md5.update(clientChallenge, 0, 8);
            System.arraycopy(md5.digest(), 0, sessionHash, 0, 8);
        } catch (GeneralSecurityException gse) {
            if (log.level > 0)
                gse.printStackTrace(log);
            throw new RuntimeException("MD5", gse);
        }

        byte[] key = new byte[21];
        System.arraycopy(nTOWFv1, 0, key, 0, 16);
        byte[] ntResponse = new byte[24];
        E(key, sessionHash, ntResponse);

        return ntResponse;
    
public static byte[]getNTLMResponse(java.lang.String password, byte[] challenge)
Generate the Unicode MD4 hash for the password associated with these credentials.

        byte[] uni = null;
        byte[] p21 = new byte[21];
        byte[] p24 = new byte[24];

        try {
            uni = password.getBytes( SmbConstants.UNI_ENCODING );
        } catch( UnsupportedEncodingException uee ) {
            if( log.level > 0 )
                uee.printStackTrace( log );
        }
        MD4 md4 = new MD4();
        md4.update( uni );
        try {
            md4.digest(p21, 0, 16);
        } catch (Exception ex) {
            if( log.level > 0 )
                ex.printStackTrace( log );
        }
        E( p21, challenge, p24 );
        return p24;
    
public static byte[]getNTLMv2Response(byte[] responseKeyNT, byte[] serverChallenge, byte[] clientChallenge, long nanos1601, byte[] targetInfo)

        int targetInfoLength = targetInfo != null ? targetInfo.length : 0;
        byte[] temp = new byte[28 + targetInfoLength + 4];

        Encdec.enc_uint32le(0x00000101, temp, 0); // Header
        Encdec.enc_uint32le(0x00000000, temp, 4); // Reserved
        Encdec.enc_uint64le(nanos1601, temp, 8);
        System.arraycopy(clientChallenge, 0, temp, 16, 8);
        Encdec.enc_uint32le(0x00000000, temp, 24); // Unknown
        if (targetInfo != null)
            System.arraycopy(targetInfo, 0, temp, 28, targetInfoLength);
        Encdec.enc_uint32le(0x00000000, temp, 28 + targetInfoLength); // mystery bytes!

        return NtlmPasswordAuthentication.computeResponse(responseKeyNT,
                        serverChallenge,
                        temp,
                        0,
                        temp.length);
    
public java.lang.StringgetName()
Return the domain and username in the format: domain\\username. This is equivalent to toString().

        boolean d = domain.length() > 0 && domain.equals( "?" ) == false;
        return d ? domain + "\\" + username : username;
    
public java.lang.StringgetPassword()
Returns the password in plain text or null if the raw password hashes were used to construct this NtlmPasswordAuthentication object which will be the case when NTLM HTTP Authentication is used. There is no way to retrieve a users password in plain text unless it is supplied by the user at runtime.

        return password;
    
public static byte[]getPreNTLMResponse(java.lang.String password, byte[] challenge)
Generate the ANSI DES hash for the password associated with these credentials.

        byte[] p14 = new byte[14];
        byte[] p21 = new byte[21];
        byte[] p24 = new byte[24];
        byte[] passwordBytes;
        try {
            passwordBytes = password.toUpperCase().getBytes( ServerMessageBlock.OEM_ENCODING );
        } catch( UnsupportedEncodingException uee ) {
            throw new RuntimeException("Try setting jcifs.encoding=US-ASCII", uee);
        }
        int passwordLength = passwordBytes.length;

        // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM
        if( passwordLength > 14) {
            passwordLength = 14;
        }
        System.arraycopy( passwordBytes, 0, p14, 0, passwordLength );
        E( p14, S8, p21);
        E( p21, challenge, p24);
        return p24;
    
public byte[]getSigningKey(byte[] challenge)

        switch (LM_COMPATIBILITY) {
            case 0:
            case 1:
            case 2:
                byte[] signingKey = new byte[40];
                getUserSessionKey(challenge, signingKey, 0);
                System.arraycopy(getUnicodeHash(challenge), 0, signingKey, 16, 24);
                return signingKey;
            case 3:
            case 4:
            case 5:
                /* This code is only called if extended security is not on. This will
                 * all be cleaned up an normalized in JCIFS 2.x.
                 */
                throw new SmbException("NTLMv2 requires extended security (jcifs.smb.client.useExtendedSecurity must be true if jcifs.smb.lmCompatibility >= 3)");
        }
        return null;
    
public byte[]getUnicodeHash(byte[] challenge)
Computes the 24 byte Unicode password hash given the 8 byte server challenge.

        if( hashesExternal ) {
            return unicodeHash;
        }
        switch (LM_COMPATIBILITY) {
        case 0:
        case 1:
        case 2:
            return getNTLMResponse( password, challenge );
        case 3:
        case 4:
        case 5:
            /*
            if( clientChallenge == null ) {
                clientChallenge = new byte[8];
                RANDOM.nextBytes( clientChallenge );
            }
            return getNTLMv2Response(domain, username, password, null,
                    challenge, clientChallenge);
            */
            return new byte[0];
        default:
            return getNTLMResponse( password, challenge );
        }
    
public byte[]getUserSessionKey(byte[] challenge)
Returns the effective user session key.

param
challenge The server challenge.
return
A byte[] containing the effective user session key, used in SMB MAC signing and NTLMSSP signing and sealing.

        if (hashesExternal) return null;
        byte[] key = new byte[16];
        try {
            getUserSessionKey(challenge, key, 0); 
        } catch (Exception ex) {
            if( log.level > 0 )
                ex.printStackTrace( log );
        }
        return key; 
    
voidgetUserSessionKey(byte[] challenge, byte[] dest, int offset)
Calculates the effective user session key.

param
challenge The server challenge.
param
dest The destination array in which the user session key will be placed.
param
offset The offset in the destination array at which the session key will start.

        if (hashesExternal) return;
        try {
            MD4 md4 = new MD4();
            md4.update(password.getBytes(SmbConstants.UNI_ENCODING)); 
            switch (LM_COMPATIBILITY) {
            case 0:
            case 1:
            case 2:
                md4.update(md4.digest()); 
                md4.digest(dest, offset, 16); 
                break; 
            case 3:
            case 4:
            case 5:
                if( clientChallenge == null ) {
                    clientChallenge = new byte[8];
                    RANDOM.nextBytes( clientChallenge );
                }

                HMACT64 hmac = new HMACT64(md4.digest());
                hmac.update(username.toUpperCase().getBytes(
                        SmbConstants.UNI_ENCODING));
                hmac.update(domain.toUpperCase().getBytes(
                        SmbConstants.UNI_ENCODING));
                byte[] ntlmv2Hash = hmac.digest();
                hmac = new HMACT64(ntlmv2Hash);
                hmac.update(challenge);
                hmac.update(clientChallenge); 
                HMACT64 userKey = new HMACT64(ntlmv2Hash); 
                userKey.update(hmac.digest()); 
                userKey.digest(dest, offset, 16); 
                break; 
            default: 
                md4.update(md4.digest()); 
                md4.digest(dest, offset, 16); 
                break; 
            } 
        } catch (Exception e) {
            throw new SmbException("", e);
        }
    
public java.lang.StringgetUsername()
Returns the username.

        return username;
    
public inthashCode()
Return the upcased username hash code.

        return getName().toUpperCase().hashCode();
    
static voidinitDefaults()


       
        if (DEFAULT_DOMAIN != null) return;
        DEFAULT_DOMAIN = Config.getProperty("jcifs.smb.client.domain", "?");
        DEFAULT_USERNAME = Config.getProperty("jcifs.smb.client.username", "GUEST");
        DEFAULT_PASSWORD = Config.getProperty("jcifs.smb.client.password", BLANK);
    
public static byte[]nTOWFv1(java.lang.String password)

        if (password == null)
            throw new RuntimeException("Password parameter is required");
        try {
            MD4 md4 = new MD4();
            md4.update(password.getBytes(SmbConstants.UNI_ENCODING));
            return md4.digest();
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException(uee.getMessage());
        }
    
public static byte[]nTOWFv2(java.lang.String domain, java.lang.String username, java.lang.String password)

        try {
            MD4 md4 = new MD4();
            md4.update(password.getBytes(SmbConstants.UNI_ENCODING));
            HMACT64 hmac = new HMACT64(md4.digest());
            hmac.update(username.toUpperCase().getBytes(SmbConstants.UNI_ENCODING));
            hmac.update(domain.getBytes(SmbConstants.UNI_ENCODING));
            return hmac.digest();
        } catch (UnsupportedEncodingException uee) {
            throw new RuntimeException(uee.getMessage());
        }
    
public java.lang.StringtoString()
Return the domain and username in the format: domain\\username. This is equivalent to getName().

        return getName();
    
static java.lang.Stringunescape(java.lang.String str)

        char ch;
        int i, j, state, len;
        char[] out;
        byte[] b = new byte[1];

        if( str == null ) {
            return null;
        }

        len = str.length();
        out = new char[len];
        state = 0;
        for( i = j = 0; i < len; i++ ) {
            switch( state ) {
                case 0:
                    ch = str.charAt( i );
                    if( ch == '%" ) {
                        state = 1;
                    } else {
                        out[j++] = ch;
                    }
                    break;
                case 1:
                    /* Get ASCII hex value and convert to platform dependant
                     * encoding like EBCDIC perhaps
                     */
                    b[0] = (byte)(Integer.parseInt( str.substring( i, i + 2 ), 16 ) & 0xFF);
                    out[j++] = (new String( b, 0, 1, "ASCII" )).charAt( 0 );
                    i++;
                    state = 0;
            }
        }

        return new String( out, 0, j );