【问题标题】:Android How to allow specific SSL certificate from a specific serverAndroid 如何允许来自特定服务器的特定 SSL 证书
【发布时间】:2014-01-07 14:48:22
【问题描述】:

我正在尝试使用 DefaultHttpClient 使用 HTTPS 连接到我的安全服务器。

我的服务器正在使用 PositiveSSL 证书,并且我通过 SSL 检查器在线进行的每项检查都给出了 A 级证书。

显然,PositiveSSL 不是 Android 中经过验证的 CA Root,所以当我尝试连接到我的服务器时,我会收到 javax.net.ssl.SSLPeerUnverifiedException: No peer certificate,这里在各种问题中进行了讨论。

其中一个给定的解决方案(对我来说是最好的)是创建一个自定义 SSLFactory 并允许它接受未经验证的证书,以防此证书来自我的服务器(设置允许的服务器名称)。

我找不到任何关于如何做到这一点的代码示例。如果有人愿意帮助我,那就太好了

【问题讨论】:

    标签: java android ssl https ssl-certificate


    【解决方案1】:

    Yopu 可以为您编写自定义 SSLSocketFactory,如下所示

     public class MySSLSocketFactory extends SSLSocketFactory {
     SSLContext sslContext = SSLContext.getInstance("TLS");
    
     public MySSLSocketFactory(KeyStore truststore)
            throws NoSuchAlgorithmException, KeyManagementException,
            KeyStoreException, UnrecoverableKeyException {
        super(truststore);
    
        TrustManager tm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }
    
            @Override
            public void checkServerTrusted(X509Certificate[] chain,
                                           String authType) throws CertificateException {
            }
    
            @Override
            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();
    }
     }
    

    现在使用 SocketFactory 创建一个自定义 HttpClient 以通过证书。您也可以使用您的 SockeFactory 接受一些特定的证书。

       public static DefaultHttpClient getNewHttpClient() {
        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.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 (UnrecoverableKeyException e) {
            return new DefaultHttpClient();
        } catch (NoSuchAlgorithmException e) {
            return new DefaultHttpClient();
        } catch (CertificateException e) {
            return new DefaultHttpClient();
        } catch (IOException e) {
            return new DefaultHttpClient();
        } catch (KeyStoreException e) {
            return new DefaultHttpClient();
        } catch (KeyManagementException e) {
            return new DefaultHttpClient();
        }
    }
    

    现在使用上面提到的实例化 httpclient:

    DefaultHttpClient client = getNewHttpClient();
    

    在这篇文章中查看 emmby 答案

    Trusting all certificates using HttpClient over HTTPS

    更新:

    如果您只想验证某个特定的服务器。使用 Https 在浏览器中打开 url 并从 url bar 下载证书,很容易谷歌它如何做到这一点。它会给你一个证书文件。

    将该证书复制到您的资产文件夹中。并执行以下操作。

      private X509Certificate getCertFromFile(String path) throws Exception {
         AssetManager assetManager = MyActivity.this.getResources().getAssets();
         InputStream inputStream = null;
         try {
             inputStream = assetManager.open(path);
         } catch (IOException e) {
             e.printStackTrace();
         }
         InputStream caInput = new BufferedInputStream(inputStream);
         X509Certificate cert = null;
         CertificateFactory cf = CertificateFactory.getInstance("X509");
         cert = (X509Certificate) cf.generateCertificate(caInput);
         cert.getSerialNumber();
         return cert;
      }
    

    现在在上面的套接字工厂代码中返回此证书而不是 null。

     @Override
        public X509Certificate[] getAcceptedIssuers() {
            X509Certificate[] certificates=new X509Certificate[]{getCertFromFile(PATH_OF_CERTIFICATE_FILE)};
    
           return certificates;
             //asset folder path is  taken in method.Only provide path respective to asset folder like /certificate/MyCertificate.crt inside assest.
    
        }
    

    【讨论】:

    • 因为这个问题给了我一种方法,它接受所有连接,而我的问题是只接受来自我的服务器的连接
    • 我已经更新了答案尝试一下,如果您遇到任何问题,请告诉我。
    • 我能知道为什么它被否决了吗?我已更新答案并更正了返回类型。
    • 确定@AsafNevo。由于此类服务器不可用,我无法在最后尝试。如果您遇到任何问题,请告诉我。
    【解决方案2】:

    您可以将自定义服务器证书放在自定义信任存储(BKS 文件)中,将其包含在您的应用中并使用它初始化您的 SSLSocketFactory:

    SSLSocketFactory sslSocketFactory = new SSLSocketFactory(trustStore);
    

    之后使用 SSLSocketFactory 创建连接。

    在这种情况下,仅连接到证书包含在密钥库中的服务器。

    可以在 Nikolay Elenkov 的主页(使用您自己的信任存储:HttpClient 部分)上找到如何将其与 HttpClient 一起使用的一个很好的示例:

    http://nelenkov.blogspot.de/2011/12/using-custom-certificate-trust-store-on.html

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-19
      • 1970-01-01
      • 2013-11-23
      • 1970-01-01
      • 2017-07-31
      • 2017-09-23
      相关资源
      最近更新 更多