HTTPS获取服务器证书

HTTPS获取服务器证书

本文介绍了HTTPS获取服务器证书的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了好几天,找出如何从一个服务器的证书,我们正在一个SSL通信,为了让我找出我需要检查它的认证服务器。

有关code少的事情,我使用的HttpClient和 - 我不希望创建一个密钥存储了认证,并把它添加到信任存储作为的链接和许多其他建议。

所以,我为了得到认证确实是贯彻落实 X509HostnameVerifier ,并在其verify()方法做:

  session.getPeerCertificates();

而是通过异常的功能:

 时发生异常:javax.net.ssl​​.SSLPeerUnverifiedException

下面是code:

 进口java.io.IOException异常;
进口java.security.cert.Certificate中;
进口javax.net.ssl​​.SSLException;
进口javax.net.ssl​​.SSLPeerUnverifiedException;
进口javax.net.ssl​​.SSLSession中;
进口javax.net.ssl​​.SSLSocket中;公共类MyHostnameVerifier实现ch.boye.httpclientandroidlib.conn.ssl.X509HostnameVerifier {    @覆盖
    公共布尔验证(字符串主机名,会议的SSLSession){
        证书[]证书;
        尝试{
            证书= session.getPeerCertificates();            //如果连接不包含任何证书 - 放弃它,它可能是一个黑客。
            如果(证书== NULL || certificates.length == 1)
                返回true;
        }赶上(SSLPeerUnverifiedException E){
        }        返回true;
    }    @覆盖
    公共无效验证(字符串主机名,SSLSocket的插座)抛出IOException
        socket.getSession()getPeerCertificates()。 //例外
    }    @覆盖
    公共无效验证(主机名字符串,字符串[] ARG1,字符串[] ARG2)抛出异常SSLException {
    }    @覆盖
    公共无效验证(字符串为arg0,ARG1 java.security.cert.X509Certificate)抛出异常SSLException {
    }
}

和用法示例:

  PoolingClientConnectionManager厘米=新PoolingClientConnectionManager();//增加至最多10总连接
cm.setMaxTotal(GlobalConstants.HTTP_CLIENT_MAX_TOTAL_CONNECTIONS);
的HttpParams httpParameters =新BasicHttpParams();INT timeoutConnection = CONNECTION_TIMEOUT_MS_DEFAULT;
HttpConnectionParams.setConnectionTimeout(httpParameters,timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters,timeoutSocket);的HostnameVerifier的HostnameVerifier =新MyHostnameVerifier();的SSLSocketFactory的SocketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier)的HostnameVerifier);
。cm.getSchemeRegistry()寄存器(新ch.boye.httpclientandroidlib.conn.scheme.Scheme(https开头,443的SocketFactory));
DefaultHttpClient的HttpClient =新DefaultHttpClient(厘米,httpParameters);


解决方案

玉家伙,

下面是该溶液中,

所以,首先你应该了解的TrustManager是如何工作的,每个认证的SSL通信正在检查针对的TrustManager。现在,defaultly有一个包含所有已认证的证书系统的TrustManager(你可以在设置轻松地找到它)。

接下来,HTTP通信使用插座,所以我们需要找到一种方法,我们的TrustManager连接到插座使用 - 你可以在下面找到实施

因此​​,为了真正拿到证书,并将其与本地硬盘codeD一个你需要实现的TrustManager。

顺便说一句,我知道这是显而易见的,但无论如何,我会说出来,从不节省硬codeD密码/证书等。一直保存它的SHA1 / SHA256为了争黑客攻击。

下面是code:

 公共类X509TrustManager实现X509TrustManager {私人最终静态字符串变量=X509TrustManager;私有静态最终布尔DEAFULT_TRUST_ALL_SSL_CONNECTIONS = TRUE;私人X509TrustManager standardTrustManager = NULL;私人布尔trustAllSSLConnections;/ **
 *构造EasyX509TrustManager。
 * /
公共X509TrustManager(密钥库密钥库)抛出抛出:NoSuchAlgorithmException,KeyStoreException {    trustAllSSLConnections = DEAFULT_TRUST_ALL_SSL_CONNECTIONS;    的TrustManagerFactory工厂= TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    factory.init(密钥库);
    的TrustManager [] = trustmanagers factory.getTrustManagers();
    如果(trustmanagers.length == 0){
        抛出新的抛出:NoSuchAlgorithmException(不信任管理器找到了);
    }
    this.standardTrustManager =(X509TrustManager)trustmanagers [0];
}@覆盖
公共无效checkClientTrusted(x509证书[]证书,字符串的authType)抛出CertificateException {
    standardTrustManager.checkClientTrusted(证书的authType);
}/ **
 *验证服务器证书
 * /
@覆盖
公共无效checkServerTrusted(x509证书[]证书,字符串的authType)抛出CertificateException {
        x509证书证书=证书[0];
        字节[]字节= certificate.getTBSCertificate();        //你比较证书的字节到你的硬盘codeD证书。
}/ **
 * @see javax.net.ssl​​.X509TrustManager#getAcceptedIssuers()为
 * /
@覆盖
公共x509证书[] getAcceptedIssuers()为{
    返回this.standardTrustManager.getAcceptedIssuers();
}

}

通常为每个认证的要求有证书的道路,从顶部证书颁发机构,其他子机构(公司,代理..),您的证书 - 这就是为什么你的证书将可能会在的第一个单元格阵列(我在这基础理论的一些测试,不是真正的深入调查)。

为了给的TrustManager连接到插座使用以下code:

 公共类的SSLSocketFactory实现LayeredSocketFactory {私人的SSLContext的SSLContext = NULL;私有静态的SSLContext createEasySSLContext()抛出IOException
    尝试{
        的SSLContext上下文= SSLContext.getInstance(TLS);
        context.init(空,新的TrustManager [] {新X509TrustManager(空)},NULL);
        返回语境;
    }赶上(例外五){
        抛出新IOException异常(e.getMessage());
    }
}私人的SSLContext getSSLContext()抛出IOException
    如果(this.sslcontext == NULL){
        this.sslcontext = createEasySSLContext();
    }
    返回this.sslcontext;
}公共插座connectSocket(插座袜子,字符串主机,端口INT,InetAddress类将localAddress,INT将localPort的HttpParams PARAMS)
        抛出IOException异常,UnknownHostException异常,ConnectTimeoutException {
    INT connTimeout = HttpConnectionParams.getConnectionTimeout(PARAMS);
    INT soTimeout = HttpConnectionParams.getSoTimeout(PARAMS);    的InetSocketAddress remoteAddress =新的InetSocketAddress(主机,端口);
    SSLSocket的sslsock =(SSLSocket的)(!?(袜子= NULL)袜子:的createSocket());    如果((将localAddress = NULL)||(将localPort>!0)){
        //我们需要明确绑定
        如果(将localPort℃,){
            将localPort = 0; //表示任何
        }
        ISA的InetSocketAddress =新的InetSocketAddress(将localAddress,将localPort);
        sslsock.bind(赛);
    }    sslsock.connect(remoteAddress,connTimeout);
    sslsock.setSoTimeout(soTimeout);
    返回sslsock;}公共插座的createSocket()抛出IOException
    返回getSSLContext()在getSocketFactory()的createSocket()。
}公共布尔的isSecure(Socket套接字)抛出:IllegalArgumentException - {
    返回true;
}公共插座的createSocket(Socket套接字,字符串主机,端口INT,布尔自动关闭)抛出IOException异常,{UnknownHostException异常
    返回getSSLContext()在getSocketFactory()的createSocket(插座,主机,端口自动关闭)。;
}// ------------------------------------------------ -------------------
//在的javadoc说org.apache.http.conn.scheme.SocketFactory:
//两个是Object.equals()和Object.hash code()必须覆盖
//对于一些连接管理器的正确操作
// ------------------------------------------------ -------------------公共布尔等于(obj对象){
    收益率((OBJ = NULL)及!&安培; obj.getClass()等于(SSLSocketFactory.class));
}公众诠释哈希code(){
    返回SSLSocketFactory.class.hash code();
}

}

现在,为了将插座连接到HttpClient的使用此code:

  SchemeRegistry schemeRegistry =新SchemeRegistry();
    的HttpParams PARAMS =新BasicHttpParams();    params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS,HTTP_CLIENT_MAX_TOTAL_CONNECTIONS);
    params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE,新ConnPerRouteBean(HTTP_CLIENT_MAX_TOTAL_CONNECTIONS));
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE,FALSE);    HttpProtocolParams.setVersion(参数,可以HttpVersion.HTTP_1_1);    schemeRegistry.register(新计划(https开头,新的SSLSocketFactory(),443));
    schemeRegistry.register(新计划(HTTP,PlainSocketFactory.getSocketFactory(),80));    ClientConnectionManager厘米=新ThreadSafeClientConnManager(参数,可以schemeRegistry);    DefaultHttpClient客户端=新DefaultHttpClient(厘米,则params);    //启用代理网络调试(嗅探)
    ProxySelectorRoutePlanner routePlanner =新ProxySelectorRoutePlanner(client.getConnectionManager()。getSchemeRegistry()
            ProxySelector.getDefault());
    client.setRoutePlanner(routePlanner);    //禁用重试
    client.setHtt prequestRetryHandler(新DefaultHtt prequestRetryHandler(0,FALSE));    //设置用户代理
    client.getParams()的setParameter(CoreProtocolPNames.USER_AGENT,getAppContext())。

不要忘了测试对经过认证的通信。

I'm trying for a several days to find out how to get the certificate from a server, we are working on a SSL communication and in order for me to identify the server I need to check its certification.

Few thing about the code, I'm using HttpClient and - I DON'T want to create a key store out of the certification and add it to the "trust store" as this link and many other suggesting.

So, what I did in order to get the certification is to implement X509HostnameVerifier, and in its verify() method to do:

session.getPeerCertificates();

but that function through exception:

 An exception occurred: javax.net.ssl.SSLPeerUnverifiedException

Here is the code:

import java.io.IOException;
import java.security.cert.Certificate;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;

public class MyHostnameVerifier implements ch.boye.httpclientandroidlib.conn.ssl.X509HostnameVerifier {

    @Override
    public boolean verify(String hostname, SSLSession session) {
        Certificate[] certificates;
        try {
            certificates = session.getPeerCertificates();

            // if connection doesn't contain any certificate - drop it, it might be an hacker.
            if (certificates == null || certificates.length == 1)
                return true;
        } catch (SSLPeerUnverifiedException e) {
        }

        return true;
    }

    @Override
    public void verify(String hostname, SSLSocket socket) throws IOException {
        socket.getSession().getPeerCertificates(); // exception
    }

    @Override
    public void verify(String hostname, String[] arg1, String[] arg2) throws SSLException {
    }

    @Override
    public void verify(String arg0, java.security.cert.X509Certificate arg1) throws SSLException {
    }
}

and usage example:

PoolingClientConnectionManager cm = new PoolingClientConnectionManager();

// Increase max total connection to 10
cm.setMaxTotal(GlobalConstants.HTTP_CLIENT_MAX_TOTAL_CONNECTIONS);
HttpParams httpParameters = new BasicHttpParams();

int timeoutConnection = CONNECTION_TIMEOUT_MS_DEFAULT;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

HostnameVerifier hostnameVerifier = new MyHostnameVerifier();

SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
cm.getSchemeRegistry().register(new ch.boye.httpclientandroidlib.conn.scheme.Scheme("https", 443, socketFactory));
DefaultHttpClient httpClient = new DefaultHttpClient(cm, httpParameters);
解决方案

Ok guys,

Here is the solution,

So first of all you should understand how the TrustManager works, each certified ssl communication is being checked against the TrustManager. Now, defaultly there's the system TrustManager that contains all the already certified certificates(you can find it easily in the Settings).

Next, http communication uses Socket so we need to find a way to connect our TrustManager to the used socket - you can found the implementation below.

So in order to actually get the certificate and compare it to a local hardcoded one you'll need to implement TrustManager.

By the way, I know it's obvious but I'll say it anyway, never save hardcoded passwords/certificates etc.. always save its SHA1/SHA256 in order to fight hacking.

Here is the code:

public class X509TrustManager implements X509TrustManager {

private final static String TAG = "X509TrustManager";

private static final boolean DEAFULT_TRUST_ALL_SSL_CONNECTIONS = true;

private X509TrustManager standardTrustManager = null;

private boolean trustAllSSLConnections;

/**
 * Constructor for EasyX509TrustManager.
 */
public X509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {

    trustAllSSLConnections = DEAFULT_TRUST_ALL_SSL_CONNECTIONS;

    TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    factory.init(keystore);
    TrustManager[] trustmanagers = factory.getTrustManagers();
    if (trustmanagers.length == 0) {
        throw new NoSuchAlgorithmException("no trust manager found");
    }
    this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}

@Override
public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
    standardTrustManager.checkClientTrusted(certificates, authType);
}

/**
 * verified the server certificate
 */
@Override
public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {


        X509Certificate certificate = certificates[0];
        byte[] bytes = certificate.getTBSCertificate();

        // Compare your the certificate’s bytes to yours hardcoded certificate.
}

/**
 * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
 */
@Override
public X509Certificate[] getAcceptedIssuers() {
    return this.standardTrustManager.getAcceptedIssuers();
}

}

Usually for every certified request there's a path of certifications, from the top Certification Authority, to other sub-authorities(companies, proxies..), your certificate - that is why your certificate will probably be in the first cell of the array(I'm basing this theory on some tests, not real deep investigation).

In order to connect the TrustManager to the socket use the following code:

public class SSLSocketFactory implements LayeredSocketFactory {

private SSLContext sslcontext = null;

private static SSLContext createEasySSLContext() throws IOException {
    try {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new TrustManager[] { new X509TrustManager(null) }, null);
        return context;
    } catch (Exception e) {
        throw new IOException(e.getMessage());
    }
}

private SSLContext getSSLContext() throws IOException {
    if (this.sslcontext == null) {
        this.sslcontext = createEasySSLContext();
    }
    return this.sslcontext;
}

public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort, HttpParams params)
        throws IOException, UnknownHostException, ConnectTimeoutException {
    int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
    int soTimeout = HttpConnectionParams.getSoTimeout(params);

    InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
    SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());

    if ((localAddress != null) || (localPort > 0)) {
        // we need to bind explicitly
        if (localPort < 0) {
            localPort = 0; // indicates "any"
        }
        InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);
        sslsock.bind(isa);
    }

    sslsock.connect(remoteAddress, connTimeout);
    sslsock.setSoTimeout(soTimeout);
    return sslsock;

}

public Socket createSocket() throws IOException {
    return getSSLContext().getSocketFactory().createSocket();
}

public boolean isSecure(Socket socket) throws IllegalArgumentException {
    return true;
}

public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}

// -------------------------------------------------------------------
// javadoc in org.apache.http.conn.scheme.SocketFactory says :
// Both Object.equals() and Object.hashCode() must be overridden
// for the correct operation of some connection managers
// -------------------------------------------------------------------

public boolean equals(Object obj) {
    return ((obj != null) && obj.getClass().equals(SSLSocketFactory.class));
}

public int hashCode() {
    return SSLSocketFactory.class.hashCode();
}

}

Now, in order to connect the socket to the HttpClient use this code:

SchemeRegistry schemeRegistry = new SchemeRegistry();
    HttpParams params = new BasicHttpParams();

    params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, HTTP_CLIENT_MAX_TOTAL_CONNECTIONS);
    params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(HTTP_CLIENT_MAX_TOTAL_CONNECTIONS));


    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);

    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

    schemeRegistry.register(new Scheme("https", new SSLSocketFactory(), 443));
    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));

    ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

    DefaultHttpClient client = new DefaultHttpClient(cm, params);

    // enable proxy web debugging ("sniffing")
    ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(client.getConnectionManager().getSchemeRegistry(),
            ProxySelector.getDefault());
    client.setRoutePlanner(routePlanner);

    // disable retries
    client.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(0, false));

    // setup  User-Agent
    client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, getAppContext());

Don't forget to test it against a certified communication.

这篇关于HTTPS获取服务器证书的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 17:41