2012-01-07 27 views
7

Estoy desarrollando una aplicación Android que acceda al servicio web Sharepoint y realiza el procesamiento SOAP. Intenté varias formas como JCIFS, etc. Nada funcionó.Autenticación NTLM en Android

¿Alguien podría ayudarme aquí? Lo busco en Google desde hace muchos días, pero todos los que tienen este problema se sienten frustrados.

Gracias, Indrajit

Respuesta

5

No soy experto en NTLM pero me conecté exitosamente a nuestro backend usando la biblioteca JCIFS y un poco de trabajo manual con los encabezados.

También uso la biblioteca OkHttp 3 para la conexión de red, pero probablemente podría adaptar mi código a otras bibliotecas.

La idea principal es que debe negociar con el servidor para conectarse.

Paso 1:

Cuando intenta conectar la primera vez que va a fallar y recibir información en el encabezado:

WWW-Authenticate: negociar

WWW-Authenticate: NTLM

Paso 2:

Debe generar una clave de tipo 1 (con los parámetros de estación de trabajo de dominio & opcionales) utilizando la biblioteca jcifs e intente conectarse de nuevo. Vas a fracasar de nuevo, pero recibe información útil en la cabecera:

WWW-Authenticate: NTLM very_long_challenge_key

Paso 3:

Es necesario generar una clave de tipo 3 con una dura entrada clave + inicio de sesión + contraseña, usando la biblioteca jcifs. ¡Entonces la conexión tendrá éxito!

Ahora algo de código, agregue la dependencia de las bibliotecas en el archivo build.gradle de su aplicación:

compile files('libs/jcifs-1.3.18.jar') 
compile 'com.squareup.okhttp3:okhttp:3.4.1' 

El tarro se puede encontrar aquí: https://jcifs.samba.org/src/

Entonces la clase NTLMAuthenticator

import android.support.annotation.NonNull; 

import java.io.IOException; 
import java.util.List; 

import jcifs.ntlmssp.NtlmFlags; 
import jcifs.ntlmssp.Type1Message; 
import jcifs.ntlmssp.Type2Message; 
import jcifs.ntlmssp.Type3Message; 
import jcifs.util.Base64; 
import okhttp3.Authenticator; 
import okhttp3.Credentials; 
import okhttp3.Request; 
import okhttp3.Response; 
import okhttp3.Route; 

/** 
* Created by Arnaud Guyon on 07.02.17. 
*/ 

public class NTLMAuthenticator implements Authenticator { 

    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; 

    private String mLogin; 
    private String mPassword; 
    private String mDomain; 
    private String mWorkstation; 

    public NTLMAuthenticator(@NonNull String login, @NonNull String password) { 
     this(login, password, "", ""); 
    } 

    public NTLMAuthenticator(@NonNull String login, @NonNull String password, @NonNull String domain, @NonNull String workstation) { 
     mLogin = login; 
     mPassword = password; 
     mDomain = domain; 
     mWorkstation = workstation; 
    } 

    @Override 
    public Request authenticate(Route route, Response response) throws IOException { 

     List<String> authHeaders = response.headers("WWW-Authenticate"); 
     if (authHeaders != null) { 
      boolean negociate = false; 
      boolean ntlm = false; 
      String ntlmValue = null; 
      for (String authHeader : authHeaders) { 
       if (authHeader.equalsIgnoreCase("Negotiate")) { 
        negociate = true; 
       } 
       if (authHeader.equalsIgnoreCase("NTLM")) { 
        ntlm = true; 
       } 
       if (authHeader.startsWith("NTLM ")) { 
        ntlmValue = authHeader.substring(5); 
       } 
      } 

      if (negociate && ntlm) { 
       String type1Msg = generateType1Msg(mDomain, mWorkstation); 
       String header = "NTLM " + type1Msg; 
       return response.request().newBuilder().header("Authorization", header).build(); 
      } else if (ntlmValue != null) { 
       String type3Msg = generateType3Msg(mLogin, mPassword, mDomain, mWorkstation, ntlmValue); 
       String ntlmHeader = "NTLM " + type3Msg; 
       return response.request().newBuilder().header("Authorization", ntlmHeader).build(); 
      } 
     } 

     if (responseCount(response) <= 3) { 
      String credential = Credentials.basic(mLogin, mPassword); 
      return response.request().newBuilder().header("Authorization", credential).build(); 
     } 

     return null; 
    } 

    private String generateType1Msg(@NonNull String domain, @NonNull String workstation) { 
     final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation); 
     byte[] source = type1Message.toByteArray(); 
     return Base64.encode(source); 
    } 

    private String generateType3Msg(final String login, final String password, final String domain, final String workstation, final String challenge) { 
     Type2Message type2Message; 
     try { 
      byte[] decoded = Base64.decode(challenge); 
      type2Message = new Type2Message(decoded); 
     } catch (final IOException exception) { 
      exception.printStackTrace(); 
      return null; 
     } 
     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, 
       login, workstation, type3Flags); 
     return Base64.encode(type3Message.toByteArray()); 
    } 

    private int responseCount(Response response) { 
     int result = 1; 
     while ((response = response.priorResponse()) != null) { 
      result++; 
     } 
     return result; 
    } 

} 

Luego cuando crea su OkHttpClient, agregue este autenticador:

OkHttpClient okHttpClient = new OkHttpClient.Builder() 
     .authenticator(new NTLMAuthenticator(login, password)) 
     // .some other init here if necessary 
     .build(); 

Y luego haga sus solicitudes como de costumbre.

+0

¿Podría dar más detalles sobre "hacer sus solicitudes como de costumbre"? No estoy familiarizado con OkHttp –

+1

Hay muestras simples para OkHttp aquí: https://square.github.io/okhttp/#overview –

Cuestiones relacionadas