it.greenvulcano.gvesb.http.ntlm.JCIFS_NTLMScheme.java Source code

Java tutorial

Introduction

Here is the source code for it.greenvulcano.gvesb.http.ntlm.JCIFS_NTLMScheme.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2016 GreenVulcano ESB Open Source Project.
 * All rights reserved.
 *
 * This file is part of GreenVulcano ESB.
 *
 * GreenVulcano ESB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GreenVulcano ESB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with GreenVulcano ESB. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package it.greenvulcano.gvesb.http.ntlm;

import java.io.IOException;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.auth.AuthChallengeParser;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.InvalidCredentialsException;
import org.apache.commons.httpclient.auth.MalformedChallengeException;

/**
 * 
 * This is a reimplementation of HTTPClient 3.x's
 * 
 * org.apache.commons.httpclient.auth.NTLMScheme.<BR/>
 * 
 * It will basically use JCIFS (v1.3.15) in order to provide added support for
 * 
 * NTLMv2 (instead of trying to create its own Type, 2 and 3 messages). <BR/>
 * 
 * This class has to be registered manually with HTTPClient before setting
 * 
 * NTCredentials: AuthPolicy.registerAuthScheme(AuthPolicy.NTLM,
 * 
 * JCIFS_NTLMScheme.class); <BR/>
 * 
 * Will <B>not</B> work with HttpClient 4.x which requires AuthEngine to be
 * overriden instead of AuthScheme.
 * 
 * 
 * 
 * @author Sachin M
 */
/**
 * @version 3.3.0 22/ott/2012
 * @author GreenVulcano Developer Team
 */

public class JCIFS_NTLMScheme implements AuthScheme {
    /** NTLM challenge string. */
    private String ntlmchallenge = null;

    private static final int UNINITIATED = 0;
    private static final int INITIATED = 1;
    private static final int TYPE1_MSG_GENERATED = 2;
    private static final int TYPE2_MSG_RECEIVED = 3;
    private static final int TYPE3_MSG_GENERATED = 4;
    private static final int FAILED = Integer.MAX_VALUE;

    /** Authentication process state */
    private int state;

    public JCIFS_NTLMScheme() throws AuthenticationException {
        // Check if JCIFS is present. If not present, do not proceed.
        try {
            Class.forName("jcifs.ntlmssp.NtlmMessage", false, this.getClass().getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new AuthenticationException("Unable to proceed as JCIFS library is not found.");
        }
    }

    @Override
    public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
        if (this.state == UNINITIATED) {
            throw new IllegalStateException("NTLM authentication process has not been initiated");
        }

        NTCredentials ntcredentials = null;
        try {
            ntcredentials = (NTCredentials) credentials;
        } catch (ClassCastException e) {
            throw new InvalidCredentialsException(
                    "Credentials cannot be used for NTLM authentication: " + credentials.getClass().getName());
        }

        NTLM ntlm = new NTLM();
        ntlm.setCredentialCharset(method.getParams().getCredentialCharset());

        String response = null;
        if ((this.state == INITIATED) || (this.state == FAILED)) {
            response = ntlm.generateType1Msg(ntcredentials.getHost(), ntcredentials.getDomain());
            this.state = TYPE1_MSG_GENERATED;
        } else {
            response = ntlm.generateType3Msg(ntcredentials.getUserName(), ntcredentials.getPassword(),
                    ntcredentials.getHost(), ntcredentials.getDomain(), this.ntlmchallenge);
            this.state = TYPE3_MSG_GENERATED;
        }

        return "NTLM " + response;
    }

    @Override
    public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
        throw new RuntimeException("Not implemented as it is deprecated anyway in Httpclient 3.x");
    }

    @Override
    public String getID() {
        throw new RuntimeException("Not implemented as it is deprecated anyway in Httpclient 3.x");
    }

    /**
     * 
     * Returns the authentication parameter with the given name, if available.
     * <p>
     * There are no valid parameters for NTLM authentication so this method
     * always returns <tt>null</tt>.
     * </p>
     * 
     * @param name
     *        The name of the parameter to be returned
     * @return the parameter with the given name
     */
    @Override
    public String getParameter(String name) {
        if (name == null) {
            throw new IllegalArgumentException("Parameter name may not be null");
        }

        return null;
    }

    /**
     * The concept of an authentication realm is not supported by the NTLM
     * authentication scheme. Always returns <code>null</code>.
     * 
     * @return <code>null</code>
     */
    @Override
    public String getRealm() {
        return null;
    }

    /**
     * Returns textual designation of the NTLM authentication scheme.
     * 
     * @return <code>ntlm</code>
     */
    @Override
    public String getSchemeName() {
        return "ntlm";
    }

    /**
     * Tests if the NTLM authentication process has been completed.
     * 
     * @return <tt>true</tt> if Basic authorization has been processed,
     *         <tt>false</tt> otherwise.
     * @since 3.0
     */
    @Override
    public boolean isComplete() {
        return (this.state == TYPE3_MSG_GENERATED) || (this.state == FAILED);
    }

    /**
     * Returns <tt>true</tt>. NTLM authentication scheme is connection based.
     * 
     * @return <tt>true</tt>.
     * @since 3.0
     */
    @Override
    public boolean isConnectionBased() {
        return true;
    }

    /**
     * Processes the NTLM challenge.
     * 
     * @param challenge
     *        the challenge string
     * @throws MalformedChallengeException
     *         is thrown if the authentication challenge is malformed
     * @since 3.0
     */
    @Override
    public void processChallenge(final String challenge) throws MalformedChallengeException {
        String s = AuthChallengeParser.extractScheme(challenge);

        if (!s.equalsIgnoreCase(getSchemeName())) {
            throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
        }

        int i = challenge.indexOf(' ');
        if (i != -1) {
            s = challenge.substring(i, challenge.length());
            this.ntlmchallenge = s.trim();
            this.state = TYPE2_MSG_RECEIVED;
        } else {
            this.ntlmchallenge = "";
            if (this.state == UNINITIATED) {
                this.state = INITIATED;
            } else {
                this.state = FAILED;
            }
        }
    }

    private class NTLM {
        /** Character encoding */
        public static final String DEFAULT_CHARSET = "ASCII";

        /**
         * The character was used by 3.x's NTLM to encode the username and
         * password. Apparently, this is not needed in when passing username,
         * password from NTCredentials to the JCIFS library
         */
        @SuppressWarnings("unused")
        private String credentialCharset = DEFAULT_CHARSET;

        void setCredentialCharset(String credentialCharset) {
            this.credentialCharset = credentialCharset;
        }

        private String generateType1Msg(String host, String domain) {
            jcifs.ntlmssp.Type1Message t1m = new jcifs.ntlmssp.Type1Message(
                    jcifs.ntlmssp.Type1Message.getDefaultFlags(), domain, host);
            return jcifs.util.Base64.encode(t1m.toByteArray());
        }

        private String generateType3Msg(String username, String password, String host, String domain,
                String challenge) {
            jcifs.ntlmssp.Type2Message t2m;
            try {
                t2m = new jcifs.ntlmssp.Type2Message(jcifs.util.Base64.decode(challenge));
            } catch (IOException e) {
                throw new RuntimeException("Invalid Type2 message", e);
            }

            jcifs.ntlmssp.Type3Message t3m = new jcifs.ntlmssp.Type3Message(t2m, password, domain, username, host,
                    0);

            return jcifs.util.Base64.encode(t3m.toByteArray());
        }
    }
}