我正在尝试使用 HTTPS 连接 HttpClient lib,但问题是,由于证书不是由公认的证书颁发机构(CA)签署的,例如 威瑞信,全局标志, 等,列在 Android 可信证书集中,我不断收到 javax.net.ssl.SSLException: Not trusted server certificate.

我见过一些解决方案,您只需接受所有证书,但如果我想询问用户怎么办?

我想要一个类似于浏览器的对话框,让用户决定是否继续。最好我想使用与浏览器相同的证书库。有任何想法吗?

有帮助吗?

解决方案

您需要做的第一件事是设置验证级别。这样的水平并不多:

  • ALLOW_ALL_HOSTNAME_VERIFIER
  • BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
  • STRICT_HOSTNAME_VERIFIER

虽然 setHostnameVerifier() 方法对于新库 apache 来说已经过时,但是对于 Android SDK 中的版本来说是正常的。所以我们采取 ALLOW_ALL_HOSTNAME_VERIFIER 并将其设置在方法工厂中 SSLSocketFactory.setHostnameVerifier().

接下来,您需要将我们的工厂协议设置为 https。为此,只需调用 SchemeRegistry.register() 方法。

然后你需要创建一个 DefaultHttpClientSingleClientConnManager。另外,在下面的代码中您可以看到默认情况下也会使用我们的标志(ALLOW_ALL_HOSTNAME_VERIFIER)通过方法 HttpsURLConnection.setDefaultHostnameVerifier()

下面的代码对我有用:

HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

DefaultHttpClient client = new DefaultHttpClient();

SchemeRegistry registry = new SchemeRegistry();
SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory();
socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
registry.register(new Scheme("https", socketFactory, 443));
SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry);
DefaultHttpClient httpClient = new DefaultHttpClient(mgr, client.getParams());

// Set verifier     
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);

// Example send http request
final String url = "https://encrypted.google.com/";
HttpPost httpPost = new HttpPost(url);
HttpResponse response = httpClient.execute(httpPost);

其他提示

要实现来自 Android 平台不信任的证书颁发机构的安全连接,需要执行以下主要步骤。

根据许多用户的要求,我已经镜像了我的最重要的部分 博客文章 这里:

  1. 获取所有必需的证书(根证书和任何中间 CA 证书)
  2. 使用 keytool 创建密钥库并 充气城堡 提供者并导入证书
  3. 在您的 Android 应用程序中加载密钥库并将其用于安全连接(我建议使用 Apache HttpClient 而不是标准 java.net.ssl.HttpsURLConnection (更容易理解,更高效)

获取证书

您必须获取构建从端点证书一直到根 CA 的链的所有证书。这意味着任何(如果存在)中间 CA 证书以及根 CA 证书。您不需要获取端点证书。

创建密钥库

下载 BouncyCastle 提供商 并将其存储到已知位置。还要确保您可以调用 keytool 命令(通常位于 JRE 安装的 bin 文件夹下)。

现在将获得的证书(不要导入端点证书)导入到 BouncyCastle 格式的密钥库中。

我没有测试过,但我认为导入证书的顺序很重要。这意味着,首先导入最低层的中间 CA 证书,然后一直导入根 CA 证书。

使用以下命令创建带有密码的新密钥库(如果尚不存在) 我的秘密 将创建并导入中间 CA 证书。我还定义了 BouncyCastle 提供程序,可以在我的文件系统和密钥库格式中找到它。对链中的每个证书执行此命令。

keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

验证证书是否已正确导入到密钥库中:

keytool -list -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

应该输出整个链:

RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43

现在您可以将密钥库复制为 Android 应用程序中的原始资源 res/raw/

在您的应用程序中使用密钥库

首先,我们必须创建一个自定义 Apache HttpClient,它使用我们的密钥库进行 HTTPS 连接:

public class MyHttpClient extends DefaultHttpClient {

  final Context context;

  public MyHttpClient(Context context) {
      this.context = context;
  }

  @Override
  protected ClientConnectionManager createClientConnectionManager() {
      SchemeRegistry registry = new SchemeRegistry();
      registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
      // Register for port 443 our SSLSocketFactory with our keystore
      // to the ConnectionManager
      registry.register(new Scheme("https", newSslSocketFactory(), 443));
      return new SingleClientConnManager(getParams(), registry);
  }

  private SSLSocketFactory newSslSocketFactory() {
      try {
          // Get an instance of the Bouncy Castle KeyStore format
          KeyStore trusted = KeyStore.getInstance("BKS");
          // Get the raw resource, which contains the keystore with
          // your trusted certificates (root and any intermediate certs)
          InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
          try {
              // Initialize the keystore with the provided trusted certificates
              // Also provide the password of the keystore
              trusted.load(in, "mysecret".toCharArray());
          } finally {
              in.close();
          }
          // Pass the keystore to the SSLSocketFactory. The factory is responsible
          // for the verification of the server certificate.
          SSLSocketFactory sf = new SSLSocketFactory(trusted);
          // Hostname verification from certificate
          // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
          sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
          return sf;
      } catch (Exception e) {
          throw new AssertionError(e);
      }
  }
}

我们已经创建了自定义 HttpClient,现在我们可以使用它来进行安全连接。例如,当我们对 REST 资源进行 GET 调用时。

// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();

就是这样 ;)

如果服务器上有自定义/自签名证书,但设备上没有,则可以使用以下类加载它并在 Android 客户端上使用它:

放置证书 *.crt 文件输入 /res/raw 以便可以从 R.raw.*

使用下面的类来获得 HTTPClient 或者 HttpsURLConnection 它将有一个使用该证书的套接字工厂:

package com.example.customssl;

import android.content.Context;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;

public class CustomCAHttpsProvider {

    /**
     * Creates a {@link org.apache.http.client.HttpClient} which is configured to work with a custom authority
     * certificate.
     *
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http Client.
     * @throws Exception If there is an error initializing the client.
     */
    public static HttpClient getHttpClient(Context context, int certRawResId, boolean allowAllHosts) throws Exception {


        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // init ssl socket factory with key store
        SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore);

        // skip hostname security check if specified
        if (allowAllHosts) {
            sslSocketFactory.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        // basic http params for client
        HttpParams params = new BasicHttpParams();

        // normal scheme registry with our ssl socket factory for "https"
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));

        // create connection manager
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

        // create http client
        return new DefaultHttpClient(cm, params);
    }

    /**
     * Creates a {@link javax.net.ssl.HttpsURLConnection} which is configured to work with a custom authority
     * certificate.
     *
     * @param urlString     remote url string.
     * @param context       Application Context
     * @param certRawResId  R.raw.id of certificate file (*.crt). Should be stored in /res/raw.
     * @param allowAllHosts If true then client will not check server against host names of certificate.
     * @return Http url connection.
     * @throws Exception If there is an error initializing the connection.
     */
    public static HttpsURLConnection getHttpsUrlConnection(String urlString, Context context, int certRawResId,
                                                           boolean allowAllHosts) throws Exception {

        // build key store with ca certificate
        KeyStore keyStore = buildKeyStore(context, certRawResId);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);

        // Create a connection from url
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());

        // skip hostname security check if specified
        if (allowAllHosts) {
            urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier());
        }

        return urlConnection;
    }

    private static KeyStore buildKeyStore(Context context, int certRawResId) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
        // init a default key store
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);

        // read and add certificate authority
        Certificate cert = readCert(context, certRawResId);
        keyStore.setCertificateEntry("ca", cert);

        return keyStore;
    }

    private static Certificate readCert(Context context, int certResourceId) throws CertificateException, IOException {

        // read certificate resource
        InputStream caInput = context.getResources().openRawResource(certResourceId);

        Certificate ca;
        try {
            // generate a certificate
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            ca = cf.generateCertificate(caInput);
        } finally {
            caInput.close();
        }

        return ca;
    }

}

关键点:

  1. Certificate 对象是从生成的 .crt 文件。
  2. 默认的 KeyStore 被建造。
  3. keyStore.setCertificateEntry("ca", cert) 正在将证书添加到别名“ca”下的密钥存储中。您修改代码以添加更多证书(中间 CA 等)。
  4. 主要目标是生成一个 SSLSocketFactory 然后可以使用 HTTPClient 或者 HttpsURLConnection.
  5. SSLSocketFactory 可以进一步配置,例如跳过主机名验证等。

更多信息请访问: http://developer.android.com/training/articles/security-ssl.html

我顶端回答didn't工作。经过一番调查,我发现在“Android开发者”所需资料: https://developer.android.com/training/articles/security-ssl.html#SelfSigned

创建空实现X509TrustManager的并达到目的:

private static class MyTrustManager implements 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;
    }

}

...

HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
try
{
    // Create an SSLContext that uses our TrustManager
    SSLContext context = SSLContext.getInstance("TLS");
    TrustManager[] tmlist = {new MyTrustManager()};
    context.init(null, tmlist, null);
    conn.setSSLSocketFactory(context.getSocketFactory());
}
catch (NoSuchAlgorithmException e)
{
    throw new IOException(e);
} catch (KeyManagementException e)
{
    throw new IOException(e);
}
conn.setRequestMethod("GET");
int rcode = conn.getResponseCode();

请注意,此空实现TustManager的仅仅是一个例子,并用它在生产环境会造成严重的安全威胁!

我在尝试使用 https 将 Android 应用程序连接到 RESTful 服务时感到沮丧。另外,我对所有建议完全禁用证书检查的答案感到有点恼火。如果这样做,https 有何意义?

在谷歌搜索这个主题一段时间后,我终于找到了 不需要外部 jar,只需要 Android API 的解决方案。感谢 Andrew Smith,他于 2014 年 7 月发布了此内容

 /**
 * Set up a connection to myservice.domain using HTTPS. An entire function
 * is needed to do this because myservice.domain has a self-signed certificate.
 * 
 * The caller of the function would do something like:
 * HttpsURLConnection urlConnection = setUpHttpsConnection("https://littlesvr.ca");
 * InputStream in = urlConnection.getInputStream();
 * And read from that "in" as usual in Java
 * 
 * Based on code from:
 * https://developer.android.com/training/articles/security-ssl.html#SelfSigned
 */
public static HttpsURLConnection setUpHttpsConnection(String urlString)
{
    try
    {
        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = CertificateFactory.getInstance("X.509");

        // My CRT file that I put in the assets folder
        // I got this file by following these steps:
        // * Go to https://littlesvr.ca using Firefox
        // * Click the padlock/More/Security/View Certificate/Details/Export
        // * Saved the file as littlesvr.crt (type X.509 Certificate (PEM))
        // The MainActivity.context is declared as:
        // public static Context context;
        // And initialized in MainActivity.onCreate() as:
        // MainActivity.context = getApplicationContext();
        InputStream caInput = new BufferedInputStream(MainActivity.context.getAssets().open("littlesvr.crt"));
        Certificate ca = cf.generateCertificate(caInput);
        System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        // Create an SSLContext that uses our TrustManager
        SSLContext context = SSLContext.getInstance("TLS");
        context.init(null, tmf.getTrustManagers(), null);

        // Tell the URLConnection to use a SocketFactory from our SSLContext
        URL url = new URL(urlString);
        HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
        urlConnection.setSSLSocketFactory(context.getSocketFactory());

        return urlConnection;
    }
    catch (Exception ex)
    {
        Log.e(TAG, "Failed to establish SSL connection to server: " + ex.toString());
        return null;
    }
}

它非常适合我的模型应用程序。

谷歌建议的Android排球的用于HTTP / HTTPS连接中的使用量,因为这HttpClient已被弃用。所以,你知道正确的选择。)

和也,的 NEVER NUKE SSL证书(NEVER !!!)。

要核弹SSL证书,是完全反对SSL的目的,这是促进安全即可。有没有使用SSL,如果你打算轰炸自带所有SSL证书的感觉。一个更好的解决办法是,没有使用SSL,或更好的解决方案,将是创建在应用程式+使用Android排球为HTTP / HTTPS连接的自定义TrustManager

这里的一个主旨我创建的,与碱性LoginApp,执行HTTPS连接时,使用在服务器侧上的自签名证书,在App接受。

下面也是另一个吉斯特,可以帮助,对于设置创建自签名SSL证书你的服务器上,也使用在你的应用程序的证书。 非常重要的:您必须从Android项目复制这是由脚本生成上面的.crt文件,“原始”目录

下面是你如何可以给您的密钥库添加额外的证书,以避免这个问题:的信任使用HttpClient的所有证书通过HTTPS

就像你问它不会提示用户,但会使其不太可能用户会碰到一个“不受信任的服务器证书”的错误。

创建 SSL 证书的最简单方法

打开 Firefox(我想用 Chrome 也可以,但对我来说用 FF 更容易)

使用自签名 SSL 证书访问您的开发站点。

单击证书(站点名称旁边)

点击“更多信息”

点击“查看证书”

点击“详细信息”

单击“导出...”

选择“X.509证书蒙山链(PEM)”,选择要保存的文件夹和名称,然后单击“保存”

转到命令行,到下载 pem 文件的目录并执行“openssl x509 -inform PEM -outform DM -in .pem -out .crt”

将.crt文件复制到Android设备内部的Android设备内部的 /sdcard文件夹的根,设置>安全性>从存储中安装。

它应该检测证书,并允许你将其添加到设备 浏览到您的开发站点。

第一次它应该要求您确认安全异常。就这样。

该证书应适用于 Android 上安装的任何浏览器(浏览器、Chrome、Opera、Dolphin...)

请记住,如果您从不同的域提供静态文件(我们都是页面速度的婊子),您还需要添加该域的证书。

我写了小型图书馆 SSL-utils的,Android的在Android上信任特定证书。

可以简单地通过给出从资产目录的文件名加载任何证书。

用法:

OkHttpClient client = new OkHttpClient();
SSLContext sslContext = SslUtils.getSslContextForCertificateFile(context, "BPClass2RootCA-sha2.cer");
client.setSslSocketFactory(sslContext.getSocketFactory());

这些修复程序都不工作了我的开发平台定位SDK 16,版本4.1.2,所以我找到了解决办法。

我的应用商店使用“ http://www.example.com服务器数据/page.php?data=somedata

最近page.php文件被转移到“ https://www.secure-example.com /page.php ”我不断收到‘javax.net.ssl.SSLException:不信任的服务器证书’

除了接受只对单个页面的所有证书,与此指南开始的我解决我的问题我自己写page.php文件的“ http://www.example.com/page公布.PHP

<?php

caronte ("https://www.secure-example.com/page.php");

function caronte($url) {
    // build curl request
    $ch = curl_init();
    foreach ($_POST as $a => $b) {
        $post[htmlentities($a)]=htmlentities($b);
    }
    curl_setopt($ch, CURLOPT_URL,$url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS,http_build_query($post));

    // receive server response ...
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $server_output = curl_exec ($ch);
    curl_close ($ch);

    echo $server_output;
}

?>

也许这将有所帮助...它适用于使用自签名证书的Java客户端(没有证书的检查)。要小心,仅使用它的发展情况,因为这是没有任何安全可言!

如何忽略的Apache HttpClient的SSL证书错误4.0

希望它会在Android上的作品只是增加HttpClient库...祝你好运!

这是从缺乏SNI(服务器名称标识)支持INA,ndroid 2.x的所得问题我被这个问题所困扰了一个星期,直到我碰到以下问题,它不仅赋予了问题的一个很好的背景来了,但也提供了没有任何安全漏洞的工作和有效的解决方案。

'没有对方的证书'误差在Android 2.3的但不是在4

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top