关于HttpClient绕过SSL认证以及NTLM认证

栏目: 编程工具 · 发布时间: 6年前

内容简介:本篇文章只涉及本人在工作上使用HttpClient遇到的情况,并不会详细地展开讲如何使用HttpClient.一开始其实是考虑使用RestTemplate的,但遇到的难题自然是SSL认证以及NTLM的认证.以目前的RestTemplate还做不到NTLM认证.而且使用SSL认证的过程也是挺复杂的.至于为什么要绕过SSL认证,因为装证书的这些操作我并不会.同时也想试试能不能忽略这个证书认证调用接口.

本篇文章只涉及本人在工作上使用HttpClient遇到的情况,并不会详细地展开讲如何使用HttpClient.

关于HttpClient绕过SSL认证以及NTLM认证

1. 为什么使用HttpClient?

一开始其实是考虑使用RestTemplate的,但遇到的难题自然是SSL认证以及NTLM的认证.以目前的RestTemplate还做不到NTLM认证.而且使用SSL认证的过程也是挺复杂的. 复杂的是:居然还是要借助HttpClient .

@Bean
    public RestTemplate buildRestTemplate(List<CustomHttpRequestInterceptor> interceptors) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
    HttpComponentsClientHttpRequestFactory factory = new                                                    
        HttpComponentsClientHttpRequestFactory();
    factory.setConnectionRequestTimeout(requestTimeout);
    factory.setConnectTimeout(connectTimeout);
    factory.setReadTimeout(readTimeout);
    // https
    SSLContextBuilder builder = new SSLContextBuilder();
    builder.loadTrustMaterial(null, (X509Certificate[] x509Certificates, String s) -> true);
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", new PlainConnectionSocketFactory())
            .register("https", socketFactory).build();
    PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry);
    phccm.setMaxTotal(200);
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).setConnectionManager(phccm).setConnectionManagerShared(true).build();
    factory.setHttpClient(httpClient);

    RestTemplate restTemplate = new RestTemplate(factory);
    List<ClientHttpRequestInterceptor> clientInterceptorList = new ArrayList<>();
    for (CustomHttpRequestInterceptor i : interceptors) {
        ClientHttpRequestInterceptor interceptor = i;
        clientInterceptorList.add(interceptor);
    }
    restTemplate.setInterceptors(clientInterceptorList);
    
    return restTemplate;
}
复制代码

2. 为什么要绕过SSL认证?

至于为什么要绕过SSL认证,因为装证书的这些操作我并不会.同时也想试试能不能忽略这个证书认证调用接口.

  • 首先如果想绕过证书,都必先创建X509TrustManager这个对象并且重写它的方法.

X509TrustManager该接口是一个用于Https的证书信任管理器,我们可以在这里添加我们的证书,让该管理器知道我们有那些证书是可以信任的.

该接口会有三个方法:

void checkClientTrusted(X509Certificate[] xcs, String str)
 
 void checkServerTrusted(X509Certificate[] xcs, String str)
 
  X509Certificate[] getAcceptedIssuers()
复制代码
  1. 第一个方法checkClientTrusted.该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。

  2. 第二个方法checkServerTrusted.该方法检查服务器的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。

  3. 第三个方法getAcceptedIssusers,返回受信任的X509证书数组。

而我们只需要重写这三个方法,并且不需要修改里面的内容.然后再交给HttpClient就可以实现绕过SSL认证了.

X509TrustManager trustManager = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
    return null;
}

@Override
public void checkClientTrusted(X509Certificate[] xcs, String str) {
}

@Override
public void checkServerTrusted(X509Certificate[] xcs, String str) {
}

SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL);
ctx.init(null, new TrustManager[]{trustManager}, null);

//生成工厂
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);

//并注册到HttpClient中
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", socketFactory).build();

HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(connectionManager);

CloseableHttpClient httpClient = httpClientBuilder.build();
复制代码

回顾一下步骤:

  1. 创建X509TrustManager对象并重写方法.
  2. 创建SSLContext实例,并交到工厂管理.
  3. 注册到HttpClient中.通过ConnectionManager最后生成httpClient.

3. 什么是NTLM?

NTLM是NT LAN Manager的缩写,这也说明了协议的来源。NTLM 是 Windows NT 早期版本的标准安全协议,Windows 2000 支持 NTLM 是为了保持向后兼容。Windows 2000内置三种基本安全协议之一。

NTLM的原理

NTLM的工作原理描述

其实我对这个了解得不是很深,因为遇上这种情况的感觉不会很多,所以网上的资源也不太多. 这里只是针对HttpClient遇上NTLM认证的情况详细描述一下.有兴趣的朋友可以通过以上的链接了解下.

4. 如何使用HttpClient进行NTLM认证?

这个查阅了官网的文档.官网也给出了解决方案.

hc.apache.org/httpcompone…

需要把这几个类编写一下.

JCIFSEngine:

public final 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;


    @Override
    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());
    }

    @Override
    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));
        } 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());
    }
}
复制代码

JCIFSNTLMSchemeFactory:

public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {

    public AuthScheme create(final HttpContext context){
        return new NTLMScheme(new JCIFSEngine());
    }
}
复制代码

最后就在HttpClient注册:

Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
        .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
        .register(AuthSchemes.BASIC, new BasicSchemeFactory())
        .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
        .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
        .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
        .build();
CloseableHttpClient httpClient = HttpClients.custom()
        .setDefaultAuthSchemeRegistry(authSchemeRegistry)
        .build();
复制代码

最后就同时使用绕过SSL验证以及NTLM验证:

private static PoolingHttpClientConnectionManager connectionManager;
private static RequestConfig requestConfig;
private static Registry<AuthSchemeProvider> authSchemeRegistry;
private static Registry<ConnectionSocketFactory> socketFactoryRegistry;
private static CredentialsProvider credsProvider;


public void init() {
try {
X509TrustManager trustManager = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }

@Override
    public void checkClientTrusted(X509Certificate[] xcs, String str) {
    }

    @Override
    public void checkServerTrusted(X509Certificate[] xcs, String str) {
    }
};
SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL);
ctx.init(null, new TrustManager[]{trustManager}, null);
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
NTCredentials creds = new NTCredentials("用户名", "密码", "工作站(workstation)", "域名");
credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, creds);
socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
        .register("http", PlainConnectionSocketFactory.INSTANCE)
        .register("https", socketFactory).build();
connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(18);
connectionManager.setDefaultMaxPerRoute(6);
requestConfig = RequestConfig.custom()
        .setSocketTimeout(30000)
        .setConnectTimeout(30000)
        .build();
authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
        .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
        .register(AuthSchemes.BASIC, new BasicSchemeFactory())
        .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
        .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
        .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
        .build();
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Rework

Rework

Jason Fried、David Heinemeier Hansson / Crown Business / 2010-3-9 / USD 22.00

"Jason Fried and David Hansson follow their own advice in REWORK, laying bare the surprising philosophies at the core of 37signals' success and inspiring us to put them into practice. There's no jarg......一起来看看 《Rework》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具