【问题标题】:How do I prompt the user to accept or reject a HTTPS certificate?如何提示用户接受或拒绝 HTTPS 证书?
【发布时间】:2014-09-30 23:46:48
【问题描述】:

我的 Android 应用使用 DefaultHttpClient API 向外部 HTTP 服务器发送请求。我无法切换到 HTTPS。

我的服务器使用自签名证书,默认情况下显然不受信任。

当应用程序连接到服务器时,我想向用户显示服务器证书的指纹,让他接受或拒绝证书。

DefaultHttpClient 可以做到这一点吗?如果是,我可以看到一个代码 sn-p 显示它是如何完成的。

我已经看到了许多解决此问题和类似问题的假设解决方案,但没有一个对我有用。

【问题讨论】:

  • “我已经看到了许多针对这个和类似问题的假设解决方案,但没有一个对我有用”——列出它们并解释您遇到的具体问题。
  • 下次我尝试失败时,我会在这里发布具体问题。问题是我昨晚尝试了几种不同的方法,但没有一个对我有用,在我受够了之后,我没有费心去记录每个假设的解决方案。

标签: java android https


【解决方案1】:

我终于找到了一个可行的解决方案。这是sn-p:

public class MySocketFactory implements SocketFactory, LayeredSocketFactory {
private SSLContext mSslContext = null;

{
    try {
        mSslContext = SSLContext.getInstance("TLS");

        TrustManager trustManager = new X509TrustManager() {

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

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                    throws CertificateException {
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // NOTE : This is where we can calculate the certificate's fingerprint,
                // show it to the user and throw an exception in case he doesn't like it
            }

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

        mSslContext.init(null, new TrustManager[]{ trustManager }, new SecureRandom());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

@Override
public Socket connectSocket(Socket socket, String host, int port,
        InetAddress localAddress, int localPort, HttpParams params)
                throws IOException, UnknownHostException, ConnectTimeoutException {

    int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
    int soTimeout = HttpConnectionParams.getSoTimeout(params);

    SocketAddress socketAddress = new InetSocketAddress(localAddress, localPort);
    socket.bind(socketAddress);
    socket.connect(new InetSocketAddress(host, port), connTimeout);
    socket.setSoTimeout(soTimeout);

    return socket;
}

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

@Override
public boolean isSecure(Socket sock) throws IllegalArgumentException {
    return true;
}



@Override
public Socket createSocket(Socket socket, String host, int port,
        boolean autoClose) throws IOException, UnknownHostException {

    return mSslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}

}

然后你可以像这样实例化你的 DefaultHttpClient:

    HttpParams httpParameters = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpParameters, 5000);
    DefaultHttpClient result = new DefaultHttpClient(httpParameters);


    MySocketFactory mySocketFactory = new MySocketFactory();
    Scheme scheme = new Scheme("https", mySocketFactory, port);

    result.getConnectionManager().getSchemeRegistry().register(scheme);

我试过了,它确实有效!

【讨论】:

    【解决方案2】:

    DefaultHTTPClient, 不可能,因为信任匹配发生在较低级别,在 SSL 堆栈(JSSE 或 OpenSSL 或任何 Android 使用)中。

    最简单和最便宜的解决方案是根本不使用自签名证书。使用 CA 签名的证书。你已经花费了超过它的成本,你将花费更多的时间,它可能是值得的。把钱付给 CA。

    【讨论】:

    • 您说“不可能”是因为您知道这是事实,还是仅仅因为您不知道答案?我问是因为我已经知道如何让 DefaultHttpClient 使用应用程序的自定义密钥库,信任匹配也发生在较低级别,所以你提供的原因并不能完全说服我。此外,我的应用程序旨在与用户的个人、启用 DynDNS 的 Raspberry Pi 服务器一起使用,因此无法提前知道使用了哪个证书。这就是为什么我希望用户能够通过指纹接受或拒绝。
    猜你喜欢
    • 1970-01-01
    • 2016-09-16
    • 1970-01-01
    • 2018-12-13
    • 2014-10-03
    • 1970-01-01
    • 2019-06-06
    • 1970-01-01
    • 2017-03-19
    相关资源
    最近更新 更多