证书固定忽略主机名验证

时间:2015-07-03 16:38:24

标签: android validation ssl ssl-certificate

我刚刚在我发现的教程之后实施了证书固定。但现在我已经注意到它忽略了主机名验证,而上帝知道它忽略了什么。

这就是我所拥有的:

SSLSocketFactory的:

public class PinningSSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    PubKeyManager pkm;

    public PinningSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
        KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        pkm  = new PubKeyManager();

        TrustManager tm[] = { pkm };

        sslContext.init(null, tm, null);

    }

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

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }

    public void setContext(Context context) {
        pkm.setContext(context);
    }
} 

X509TrustManager:

public class PubKeyManager implements X509TrustManager {

    public Context mContext;

    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

        if (chain == null) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
        }

        if (!(chain.length > 0)) {
            throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
        }

        if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
            throw new CertificateException("checkServerTrusted: AuthType is not RSA");
        }

        // Perform customary SSL/TLS checks
        // get request cert
        try {
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
            tmf.init((KeyStore) null);

            for (TrustManager trustManager : tmf.getTrustManagers()) {
                ((X509TrustManager) trustManager).checkServerTrusted(chain, authType);
            }
        } catch (Exception e) {
            throw new CertificateException(e);
        }

        //get stored certificate

        expected = //compare both certs

        if (!expected) {
            throw new CertificateException("checkServerTrusted");
        }
    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }

    public void setContext(Context context) {
        mContext = context;
    }
}

获取HttpClient:

public DefaultHttpClient getPinningHttpClient(){

    try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);

        PinningSSLSocketFactory sf = new PinningSSLSocketFactory(trustStore);
        sf.setContext(context);

        HttpParams params = new BasicHttpParams();
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
        HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        registry.register(new Scheme("https", sf, 443));

        ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

        return new DefaultHttpClient(ccm, params);

    } catch (Exception e) {
        e.printStackTrace();
    }

    return new DefaultHttpClient();
}

我的问题是如何解决丢失的主机名验证以及可能以这种方式破坏的任何其他遗漏验证。

感谢您给予的任何帮助。

1 个答案:

答案 0 :(得分:2)

证书/ pubkey固定的想法是您知道特定主机具有特定证书或公钥。只有在您不知道证书应该是什么的情况下,才需要验证主机名和证书链。固定是您与预期同行交谈的有力证据。但是,您当然应该在应用程序中关联主机名和固定信息,即不要使用来自一个站点的固定信息来验证其他站点。

除此之外,不要只使用" ...我找到的教程"。在OWASP,您可以找到关于该主题的good information以及示例代码。