忽略主机名验证的证书固定

Certificate pinning ignoring hostname validation

我刚刚按照找到的教程实施了证书固定。但现在我注意到它忽略了主机名验证,天知道它还忽略了什么。

这是我拥有的:

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

我的问题是如何解决缺少的主机名验证以及任何其他可能被破坏的缺少验证。

感谢您提供的任何帮助。

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

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