2012-01-07 4 views
7

Pracuję nad aplikacją dla systemu Android, która uzyskuje dostęp do usługi SharePoint Web Service i przetwarza SOAP. Próbowałem różnych sposobów, takich jak JCIFS itp. Nic nie działało.Uwierzytelnianie NTLM w systemie Android

Czy ktoś może mi w tym pomóc? Prowadzę go przez wiele dni, ale każdy, kto ma ten problem, jest sfrustrowany.

Dzięki, Indrajit

Odpowiedz

5

Nie jestem ekspertem od NTLM, ale udało mi się połączyć z naszym zapleczem za pomocą biblioteki JCIFS i ręcznej pracy z nagłówkami.

Używam również biblioteki OkHttp 3 do połączenia sieciowego, ale prawdopodobnie mógłbyś dostosować mój kod do innych bibliotek.

Główną ideą jest to, że musisz negocjować z serwerem, aby się połączyć.

Krok 1:

Podczas próby podłączenia 1 raz będziesz niepowodzeniem i otrzymać pewne informacje w nagłówku:

uwierzytelniania w sieci WWW: Negocjuj

uwierzytelniania w sieci WWW: NTLM

Etap 2:

Należy wygenerować klucz typu 1 (z opcjonalną domeną & parametrów stacji roboczej) za pomocą biblioteki jcifs i spróbować ponownie nawiązać połączenie. Będziesz znów ale nie otrzymują kilka przydatnych informacji w nagłówku:

WWW uwierzytelnienie: NTLM very_long_challenge_key

Krok 3:

Trzeba wygenerować klucz typu 3 z tym wyzwaniem klucz + login + hasło, używając biblioteki jcifs. Wtedy połączenie się powiedzie!

Teraz jakiś kod, dodać zależność do bibliotek w pliku build.gradle swojej aplikacji:

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

Słoik można znaleźć tutaj: https://jcifs.samba.org/src/

wówczas klasy 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; 
    } 

} 

Następnie, po utworzeniu OkHttpClient, dodać ten uwierzytelniający:

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

A następnie wykonaj zwykłe polecenia.

+0

Czy mógłby Pan rozwinąć temat "jak zwykle?" Nie jestem zaznajomiony z OkHttp –

+1

Istnieją proste przykłady dla OkHttp tutaj: https://square.github.io/okhttp/#overview –