简体   繁体   中英

How can I enable NTLM and TLS 1.2 with Xamarin on Android?

I am working with a client app in Xamarin on Android and I need TLS 1.2 and NTLM.

So far, I have been using the regular System.Net.HttpClientHandler and it has worked fine - it looks like this:

new System.Net.Http.HttpClientHandler()
        {
            Credentials = credentials
        };

But now I have a new customer and I need TLS 1.2. So I made this code for Android:

new Xamarin.Android.Net.AndroidClientHandler()
        {
            Credentials = credentials
        };

With the environment variable:

XA_HTTP_CLIENT_HANDLER_TYPE=Xamarin.Android.Net.AndroidClientHandler

Now, this AndroidClientHandler works as far as the certificate goes. But I also need NTLM to work. To me it seems like the AndroidClientHandler only has support for Basic and Digest authentication schemes (see Xamarin.Android.Net.AuthenticationScheme).

I also tried with the ModernHttpClient, but it seems to me that it uses Mono the same way System.Net.Http.HttpClientHandler does, so TLS 1.2 doesn't work there either.

It seems to me that this should be a pretty common case, but I still can't find a relevant example on the web. I hope I am just missing something obvious. How have you guys solved this?

I believe it would be beneficial for you to read through:

https://github.com/xamarin/xamarin-android/blob/0c3597869bc4493895e755bda8a26f778e4fe9e0/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs#L40-L56

/// <para>
/// The class supports pre-authentication of requests albeit in a slightly "manual" way. Namely, whenever a request to a server requiring authentication
/// is made and no authentication credentials are provided in the <see cref="PreAuthenticationData"/> property (which is usually the case on the first
/// request), the <see cref="RequestNeedsAuthorization"/> property will return <c>true</c> and the <see cref="RequestedAuthentication"/> property will
/// contain all the authentication information gathered from the server. The application must then fill in the blanks (i.e. the credentials) and re-send
/// the request configured to perform pre-authentication. The reason for this manual process is that the underlying Java HTTP client API supports only a 
/// single, VM-wide, authentication handler which cannot be configured to handle credentials for several requests. AndroidClientHandler, therefore, implements
/// the authentication in managed .NET code. Message handler supports both Basic and Digest authentication. If an authentication scheme that's not supported
/// by AndroidClientHandler is requested by the server, the application can provide its own authentication module (<see cref="AuthenticationData"/>, 
/// <see cref="PreAuthenticationData"/>) to handle the protocol authorization.</para>
/// <para>AndroidClientHandler also supports requests to servers with "invalid" (e.g. self-signed) SSL certificates. Since this process is a bit convoluted using
/// the Java APIs, AndroidClientHandler defines two ways to handle the situation. First, easier, is to store the necessary certificates (either CA or server certificates)
/// in the <see cref="TrustedCerts"/> collection or, after deriving a custom class from AndroidClientHandler, by overriding one or more methods provided for this purpose
/// (<see cref="ConfigureTrustManagerFactory"/>, <see cref="ConfigureKeyManagerFactory"/> and <see cref="ConfigureKeyStore"/>). The former method should be sufficient
/// for most use cases, the latter allows the application to provide fully customized key store, trust manager and key manager, if needed. Note that the instance of
/// AndroidClientHandler configured to accept an "invalid" certificate from the particular server will most likely fail to validate certificates from other servers (even
/// if they use a certificate with a fully validated trust chain) unless you store the CA certificates from your Android system in <see cref="TrustedCerts"/> along with
/// the self-signed certificate(s).</para>

Basically this says: It supports Basic and Digest authentication. If there's an authentication scheme that's not supported in AndroidClientHandler that is requested by the server, the application can provide it's own authentication module to handle the protocol authorization.

We then can see that the RequestedAuthentication property will list out the about each scheme supported by the server.:

https://github.com/xamarin/xamarin-android/blob/0c3597869bc4493895e755bda8a26f778e4fe9e0/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs#L116-L124

/// <summary>
/// If the website requires authentication, this property will contain data about each scheme supported
/// by the server after the response. Note that unauthorized request will return a valid response - you
/// need to check the status code and and (re)configure AndroidClientHandler instance accordingly by providing
/// both the credentials and the authentication scheme by setting the <see cref="PreAuthenticationData"/> 
/// property. If AndroidClientHandler is not able to detect the kind of authentication scheme it will store an
/// instance of <see cref="AuthenticationData"/> with its <see cref="AuthenticationData.Scheme"/> property
/// set to <c>AuthenticationScheme.Unsupported</c> and the application will be responsible for providing an
/// instance of <see cref="IAndroidAuthenticationModule"/> which handles this kind of authorization scheme
/// (<see cref="AuthenticationData.AuthModule"/>
/// </summary>

This tells us that if it returns Unsupported as our AuthenticationScheme , then we need to submit our own IAndroidAuthenticationModule that handles the challenge.

Here is an enum of the AuthenticationScheme :

https://github.com/xamarin/xamarin-android/blob/24f2aec113857b5c583e14959b9af08ad45b22b1/src/Mono.Android/Xamarin.Android.Net/AuthenticationScheme.cs

namespace Xamarin.Android.Net
{
    /// <summary>
    /// Authentication schemes supported by <see cref="AndroidClientHandler"/>
    /// </summary>
    public enum AuthenticationScheme
    {
        /// <summary>
        /// Default value used in <see cref="AuthenticationData.Scheme"/>
        /// </summary>
        None,

        /// <summary>
        /// <see cref="AndroidClientHandler"/> doesn't support this scheme, the application must provide its own value. See <see cref="AuthenticationData.Scheme"/>
        /// </summary>
        Unsupported,

        /// <summary>
        /// The HTTP Basic authentication scheme
        /// </summary>
        Basic,

        /// <summary>
        /// The HTTP Digest authentication scheme
        /// </summary>
        Digest
    }
}

Thus we would have to implement a custom IAndroidAuthenticationModule by extending this interface on your implementation:

https://github.com/xamarin/xamarin-android/blob/24f2aec113857b5c583e14959b9af08ad45b22b1/src/Mono.Android/Xamarin.Android.Net/IAndroidAuthenticationModule.cs

Then you'd pass that implementation into the AuthenticationData.AuthModule property:

https://github.com/xamarin/xamarin-android/blob/24f2aec113857b5c583e14959b9af08ad45b22b1/src/Mono.Android/Xamarin.Android.Net/AuthenticationData.cs#L37

You would then pass that into the main client's PreAuthenticationData property.

https://github.com/xamarin/xamarin-android/blob/0c3597869bc4493895e755bda8a26f778e4fe9e0/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs#L113

I hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM