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

Android 证书固定忽略主机名验证,android,validation,ssl,ssl-certificate,Android,Validation,Ssl,Ssl Certificate,我刚刚根据找到的教程实现了证书固定。但现在我注意到它忽略了主机名验证,天知道它还忽略了什么 以下是我所拥有的: SSLSocketFactory: public class PinningSSLSocketFactory extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); PubKeyManager pkm; public PinningSSLSocketF

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

以下是我所拥有的:

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();
}
我的问题是如何解决缺少的主机名验证和任何其他缺少的验证,这些验证可能会以这种方式被破坏


感谢您提供的帮助。

证书/公钥固定的概念是您知道特定主机具有特定的证书或公钥。只有在您不知道证书应该是什么的情况下,才需要验证主机名和证书链。钉住是你与预期同伴交谈的有力证据。当然,您应该在应用程序中关联主机名和固定信息,也就是说,不要使用一个站点的固定信息来验证其他站点


除此之外,不要只使用“…我找到的教程”。在OWASP中,您可以找到有关主题和示例代码的信息。

我基本上按照该页面上的示例项目来完成我现在所做的工作。问题是,我们将证书放在质量服务器上,它接受了请求,即使url与证书上的url不匹配。Sói假设使用我的
pinningsslssocketfactory
忽略了主机名验证。如果我使用默认的
SSLSocketFactory
,由于主机名验证,它不允许连接。@HugoAlves:再次-钉扎是更强大的验证方法,因为您知道证书或公钥。只有当您需要检查您获得的以前未知的证书是否可信时,才需要验证链和主机名。这意味着可以省略主机名检查。ok thks可获得帮助。感觉到这是一个我不太擅长的领域,我担心这是一种可能的攻击方式,通过将用户的请求重定向到攻击者的机器并窃取他们的信息,或者我打开了其他攻击方式。我认为主机名验证可以保护它。我已经测试过将证书副本放在随机机器上并访问它。仅验证了公钥,计算机的url与证书不同。如果我删除证书固定代码,Android会简单地拒绝连接,并出现以下异常:
java.io.IOException:Hostname'myurl.com'未经验证
。这是否为轻松重定向到攻击者的服务器打开了一扇巨大的大门?如果我理解正确,您的问题是,在服务器受损并丢失私钥后,攻击者可以在其他服务器上使用相同的证书,而您不会注意到。这是真的,但问题不是攻击者可以这样做,而是私钥被泄露,因此不再是私钥。因此,关于TLS安全性的基本假设被打破。在这种情况下,攻击者是否从您拥有的服务器、从另一台服务器或使用私钥对同一服务器名进行中间人攻击并不重要。