来自 google 的接口 X509TrustManager 的不安全实现

an unsafe implementation of the interface X509TrustManager from google

我在 Google Play 中有一个应用程序,我收到一封来自 Google 的邮件说:

Your app(s) listed at the end of this email use an unsafe implementation of the interface X509TrustManager. Specifically, the implementation ignores all SSL certificate validation errors when establishing an HTTPS connection to a remote host, thereby making your app vulnerable to man-in-the-middle attacks.

To properly handle SSL certificate validation, change your code in the checkServerTrusted method of your custom X509TrustManager interface to raise either CertificateException or IllegalArgumentException whenever the certificate presented by the server does not meet your expectations.

我的应用程序使用 "https",我的 checkServerTrusted() 如下:

 TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

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

        }

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

那我修改这个函数:

 TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

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

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

自定义 SSLSocketFactory:

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

public MySSLSocketFactory(KeyStore ctx) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
    super(ctx);

    TrustManager tm = new X509TrustManager() {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

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

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    };

    sslContext.init(null, new TrustManager[]{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();
}

}

HttpClient函数:

private static HttpClient getHttpClient(int timeout) {
    if (null == mHttpClient) {

        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore
                    .getDefaultType());
            trustStore.load(null, null);
            SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 

            HttpParams params = new BasicHttpParams();

            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params,
                    HTTP.DEFAULT_CONTENT_CHARSET);
            HttpProtocolParams.setUseExpectContinue(params, true);


            ConnManagerParams.setTimeout(params, timeout);

            HttpConnectionParams.setConnectionTimeout(params, timeout);

            HttpConnectionParams.setSoTimeout(params, timeout);


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

            ClientConnectionManager conManager = new ThreadSafeClientConnManager(
                    params, schReg);

            mHttpClient = new DefaultHttpClient(conManager, params);
        } catch (Exception e) {
            e.printStackTrace();
            return new DefaultHttpClient();
        }
    }
    return mHttpClient;
}

但是我不太了解这个,我只是按照邮件中的内容修改了我的代码,我想我还没有解决这个问题problem.What这个警告是怎么回事?如何解决?

您提出的修改没有修复安全漏洞。无论有效性如何,您的代码仍将接受任何格式正确的证书。

如果您不确定如何正确验证证书,您应该只删除自定义信任管理器。你不需要一个,除非你在做一些不寻常的事情。

我找到了this solution,效果不错!

X509TrustManager:

public class EasyX509TrustManager
    implements X509TrustManager {

private X509TrustManager standardTrustManager = null;

/**
 * Constructor for EasyX509TrustManager.
 */
public EasyX509TrustManager(KeyStore keystore)
        throws NoSuchAlgorithmException, KeyStoreException {
    super();
    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];
}

/**
 * @see X509TrustManager#checkClientTrusted(X509Certificate[], String authType)
 */
public void checkClientTrusted(X509Certificate[] certificates, String authType)
        throws CertificateException {
    standardTrustManager.checkClientTrusted(certificates, authType);
}

/**
 * @see X509TrustManager#checkServerTrusted(X509Certificate[], String authType)
 */
public void checkServerTrusted(X509Certificate[] certificates, String authType)
        throws CertificateException {
    if ((certificates != null) && (certificates.length == 1)) {
        certificates[0].checkValidity();
    } else {
        standardTrustManager.checkServerTrusted(certificates, authType);
    }
}

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

}

SSLSocketFactory:

public class EasySSLSocketFactory implements LayeredSocketFactory {

private SSLContext sslcontext = null;

private static SSLContext createEasySSLContext() throws IOException {
    try {
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, new TrustManager[]{new EasyX509TrustManager(
                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;
}

/**
 * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(Socket,
 * String, int, InetAddress, int,
 * HttpParams)
 */
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;

}

/**
 * @see org.apache.http.conn.scheme.SocketFactory#createSocket()
 */
public Socket createSocket() throws IOException {
    return getSSLContext().getSocketFactory().createSocket();
}

/**
 * @see org.apache.http.conn.scheme.SocketFactory#isSecure(Socket)
 */
public boolean isSecure(Socket socket) throws IllegalArgumentException {
    return true;
}

/**
 * @see LayeredSocketFactory#createSocket(Socket,
 * String, int, boolean)
 */
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(
            EasySSLSocketFactory.class));
}

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

}

然后:

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

最简单的方法是不提供自己的自定义 TrustManager。只需使用默认 TrustManager,它就会为您执行 public 密钥(X.509)验证和验证。

仅使用默认的 X509trustmanager 方法,即 checkServerTrusted(chain, authType),它们将适当地处理所有验证。