Java and HTTP NTLM(v2) authentication with HttpClient
0Hancock posted on 2024/04/12 16:07:08
/*
Dependencies:
HttpClient v4.5.14
https://dlcdn.apache.org//httpcomponents/httpclient/binary/httpcomponents-client-4.5.14-bin.zip
JCIFS v1.3.19
https://www.jcifs.org/src/jcifs-1.3.19.zip
*/
package com.example;
import java.io.*;
import java.util.*;
import org.apache.http.*;
import org.apache.http.auth.*;
import org.apache.http.client.*;
import org.apache.http.client.config.*;
import org.apache.http.client.methods.*;
import org.apache.http.config.*;
import org.apache.http.impl.auth.*;
import org.apache.http.impl.client.*;
import org.apache.http.message.*;
import org.apache.http.protocol.*;
import org.apache.http.util.*;
import jcifs.ntlmssp.*;
import jcifs.util.Base64;
public class SampleClient {
public static void main (String args []) throws Exception {
System.out.println("Hello World");
Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
.build();
RequestConfig config = RequestConfig.custom()
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM ,AuthSchemes.SPNEGO))
.build();
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials(user, password, null, domain));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultAuthSchemeRegistry(authSchemeRegistry)
.setDefaultCredentialsProvider(credsProvider)
.setDefaultRequestConfig(config)
.build();
try {
/*
HttpPost request = new HttpPost(url);
StringEntity input = new StringEntity(payLoad);
input.setContentType("application/json");
request.setEntity(input);
*/
HttpGet request = new HttpGet(url);
System.out.println("Executing request " + request.getRequestLine());
CloseableHttpResponse response = httpclient.execute(request);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
}
class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {
@Override
public AuthScheme create(final HttpContext context) {
return new SpNegoNTLMScheme(new JCIFSEngine());
}
}
class JCIFSEngine implements NTLMEngine {
private static final int TYPE_1_FLAGS =
NtlmFlags.NTLMSSP_NEGOTIATE_56 |
NtlmFlags.NTLMSSP_NEGOTIATE_128 |
NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
NtlmFlags.NTLMSSP_REQUEST_TARGET;
public String generateType1Msg(final String domain, final String workstation)
throws NTLMEngineException {
final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
return Base64.encode(type1Message.toByteArray());
}
public String generateType3Msg(final String username, final String password,
final String domain, final String workstation, final String challenge)
throws NTLMEngineException {
Type2Message type2Message;
try {
type2Message = new Type2Message(Base64.decode(challenge));//debug
} catch (final IOException exception) {
throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
}
final int type2Flags = type2Message.getFlags();
final int type3Flags = type2Flags
& (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
final Type3Message type3Message = new Type3Message(type2Message, password, domain,
username, workstation, type3Flags);
return Base64.encode(type3Message.toByteArray());
}
}
/**
* Based on {@link NTLMScheme}
* @author m.oberwasserlechner@mum-software.com
*
*/
class SpNegoNTLMScheme extends AuthSchemeBase {
enum State {
UNINITIATED,
CHALLENGE_RECEIVED,
MSG_TYPE1_GENERATED,
MSG_TYPE2_RECEVIED,
MSG_TYPE3_GENERATED,
FAILED,
}
private final NTLMEngine engine;
private State state;
private String challenge;
public SpNegoNTLMScheme(final NTLMEngine engine) {
super();
if (engine == null) {
throw new IllegalArgumentException("NTLM engine may not be null");
}
this.engine = engine;
this.state = State.UNINITIATED;
this.challenge = null;
}
@Override
public String getSchemeName() {
return "ntlm";
}
public String getParameter(String name) {
// String parameters not supported
return null;
}
public String getRealm() {
// NTLM does not support the concept of an authentication realm
return null;
}
public boolean isConnectionBased() {
return true;
}
@Override
protected void parseChallenge(
final CharArrayBuffer buffer,
int beginIndex, int endIndex) throws MalformedChallengeException {
String challenge = buffer.substringTrimmed(beginIndex, endIndex);
if (challenge.length() == 0) {
if (this.state == State.UNINITIATED) {
this.state = State.CHALLENGE_RECEIVED;
} else {
this.state = State.FAILED;
}
this.challenge = null;
} else {
this.state = State.MSG_TYPE2_RECEVIED;
this.challenge = challenge;
}
}
public Header authenticate(final Credentials credentials, final HttpRequest request) throws AuthenticationException {
NTCredentials ntcredentials = null;
try {
Thread.sleep(500);//nhadh: wait to make it work
ntcredentials = (NTCredentials) credentials;//debug
} catch (ClassCastException e) {
throw new InvalidCredentialsException(
"Credentials cannot be used for NTLM authentication: "
+ credentials.getClass().getName());
}
catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
String response = null;
if (this.state == State.CHALLENGE_RECEIVED || this.state == State.FAILED) {
response = this.engine.generateType1Msg(
ntcredentials.getDomain(),
ntcredentials.getWorkstation());
this.state = State.MSG_TYPE1_GENERATED;
} else if (this.state == State.MSG_TYPE2_RECEVIED) {
response = this.engine.generateType3Msg(
ntcredentials.getUserName(),
ntcredentials.getPassword(),
ntcredentials.getDomain(),
ntcredentials.getWorkstation(),
this.challenge);
this.state = State.MSG_TYPE3_GENERATED;
} else {
throw new AuthenticationException("Unexpected state: " + this.state);
}
CharArrayBuffer buffer = new CharArrayBuffer(32);
if (isProxy()) {
buffer.append(AUTH.PROXY_AUTH_RESP);
} else {
buffer.append(AUTH.WWW_AUTH_RESP);
}
buffer.append(": ");
buffer.append(getSchemeName().toUpperCase());
buffer.append(" ");
buffer.append(response);
return new BufferedHeader(buffer);
}
public boolean isComplete() {
return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
}
}