Https连接Android


101

我正在做一个https帖子,但我得到了ssl异常的例外,不是受信任的服务器证书。如果我做正常的HTTP,它工作正常。我是否必须以某种方式接受服务器证书?


我可以得到相同的一些样本实施
Sam97305421562

可能对这个线程有帮助,但是我无法确定它是否可以运行最新的API:groups.google.com/group/android-developers/browse_thread/thread/…–
RzR

Answers:


45

我正在猜测,但是如果您想进行实际的握手,则必须让android知道您的证书。如果您只想接受任何内容,请使用以下伪代码通过Apache HTTP Client获得所需的内容:

SchemeRegistry schemeRegistry = new SchemeRegistry ();

schemeRegistry.register (new Scheme ("http",
    PlainSocketFactory.getSocketFactory (), 80));
schemeRegistry.register (new Scheme ("https",
    new CustomSSLSocketFactory (), 443));

ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager (
    params, schemeRegistry);


return new DefaultHttpClient (cm, params);

CustomSSLSocketFactory:

public class CustomSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
private SSLSocketFactory FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory ();

public CustomSSLSocketFactory ()
    {
    super(null);
    try
        {
        SSLContext context = SSLContext.getInstance ("TLS");
        TrustManager[] tm = new TrustManager[] { new FullX509TrustManager () };
        context.init (null, tm, new SecureRandom ());

        FACTORY = context.getSocketFactory ();
        }
    catch (Exception e)
        {
        e.printStackTrace();
        }
    }

public Socket createSocket() throws IOException
{
    return FACTORY.createSocket();
}

 // TODO: add other methods like createSocket() and getDefaultCipherSuites().
 // Hint: they all just make a call to member FACTORY 
}

FullX509TrustManager是一个实现javax.net.ssl.X509TrustManager的类,但实际上没有任何方法可以执行任何工作,请在此处获取示例。

祝好运!


为什么会有FACTORY变量?
Codevalley

1
返回FACTORY.createSocket(); 有问题。创建的套接字在execute()上给出了空Pointer异常。另外,我注意到,有2个SocketFactory类。1. org.apache.http.conn.ssl.SSLSocketFactory和2. javax.net.ssl.SSLSocketFactory
Codevalley 2010年

2
Nate,您好,这对我的情况有所帮助。但是,我在制作CustomSSLSocketFactory时遇到了一些问题。它应该扩展什么类?还是应该实现什么接口?我一直在尝试:javax.net.SocketFactory,javax.net.ssl.SSLSocketFactory,org.apache.http.conn.scheme.SocketFactory,所有这些都强制实现其他方法……所以,总的来说,什么代码中使用的类的名称空间是什么?谢谢。:)
Cephron 2011年

2
对我不起作用,给我同样的“不可信服务器证书”错误。
奥马尔·雷曼

1
我怎么能上这堂课FullX509TrustManager
RajaReddy PolamReddy 2012年

89

这就是我在做什么。它只是不再检查证书了。

// always verify the host - dont check for certificate
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

/**
 * Trust every server - dont check for any certificate
 */
private static void trustAllHosts() {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[] {};
        }

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

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

    // Install the all-trusting trust manager
    try {
        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection
                .setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

    HttpURLConnection http = null;

    if (url.getProtocol().toLowerCase().equals("https")) {
        trustAllHosts();
        HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
        https.setHostnameVerifier(DO_NOT_VERIFY);
        http = https;
    } else {
        http = (HttpURLConnection) url.openConnection();
    }

看起来不错!但是,我使用的是WebView,并且只需连接到https服务器即可进行测试。(客户端无法向其提供匹配的FQDN,也无法在http上进行测试。)使用WebView时,有什么方法可以解决此问题?我是否只是将这段代码放在WebView所在的“活动”中,并且“它才有效”?(怀疑不是吗???)
Joe D'Andrea

我正在尝试逐步使用该解决方案,并且遇到这样的异常:07-25 12:35:25.941:WARN / System.err(24383):java.lang.ClassCastException:org.apache.harmony.luni .internal.net.www.protocol.http.HttpURLConnectionImpl在我尝试打开连接的地方...为什么知道?
罗伯特

1
嘿,我按照您的建议做了,但我却收到了例外javax.net.ssl.SSLException: Received fatal alert: bad_record_mac。我也尝试用替换TLSSSL但没有帮助。请帮帮我,谢谢
devsri

40
虽然这在开发阶段很不错,但您应该意识到以下事实:任何人都可以通过伪造随机SSL证书来建立MITM安全连接,这使您的连接不再安全。看看这个问题,看看它做了正确的方式,在这里来看看如何获得证书,最后这个学习如何将其添加到密钥存储。
cimnine

2
您不应使用推荐或发布此代码。这是完全不安全的。您应该解决实际的问题。
罗恩侯爵,



4

http://madurangasblogs.blogspot.in/2013/08/avoiding-javaxnetsslsslpeerunverifiedex.html

由马杜兰加(Maduranga)提供

开发使用https的应用程序时,您的测试服务器没有有效的SSL证书。有时网站使用自签名证书,或者网站使用免费的SSL证书。因此,如果您尝试使用Apache连接到服务器HttpClient,则会得到一个异常,告诉您“对等方未通过身份验证”。尽管信任生产软件中的所有证书不是一个好习惯,但是您可能必须根据情况这样做。该解决方案解决了“对等体未认证”引起的异常。

但是在我们寻求解决方案之前,我必须警告您,这对于生产应用程序不是一个好主意。这将违反使用安全证书的目的。因此,除非您有充分的理由或确定不会造成任何问题,否则请勿使用此解决方案。

通常,您会创建一个HttpClient这样的。

HttpClient httpclient = new DefaultHttpClient();

但是您必须更改创建HttpClient的方式。

首先,您必须创建一个扩展类org.apache.http.conn.ssl.SSLSocketFactory

import org.apache.http.conn.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

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() {
            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();
    }
}

然后创建这样的方法。

public HttpClient 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 (Exception e) {
         return new DefaultHttpClient();
     }
}

然后,您可以创建HttpClient

HttpClient httpclient = getNewHttpClient();

如果您尝试将登录请求发送到登录页面,则其余代码将如下所示。

private URI url = new URI("url of the action of the form");
HttpPost httppost =  new HttpPost(url);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();  
nameValuePairs.add(new BasicNameValuePair("username", "user"));  
nameValuePairs.add(new BasicNameValuePair("password", "password"));
try {
    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    HttpResponse response = httpclient.execute(httppost);
    HttpEntity entity = response.getEntity();
    InputStream is = entity.getContent();
} catch (UnsupportedEncodingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (ClientProtocolException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

您将html页面获取到InputStream。然后,您可以对返回的html页面执行任何操作。

但是这里您将面临一个问题。如果要使用Cookie管理会话,则无法使用此方法进行。如果要获取Cookie,则必须通过浏览器进行操作。然后只有您会收到Cookie。




2

这些答案中的任何一个都不适合我,因此这里是信任任何证书的代码。

import java.io.IOException;

    import java.net.Socket;
    import java.security.KeyManagementException;
    import java.security.KeyStoreException;
    import java.security.NoSuchAlgorithmException;
    import java.security.UnrecoverableKeyException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;

    import javax.net.ssl.SSLContext;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;

    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpPost;
    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.SSLSocketFactory;
    import org.apache.http.conn.ssl.X509HostnameVerifier;
    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.HttpConnectionParams;
import org.apache.http.params.HttpParams;

    public class HttpsClientBuilder {
        public static DefaultHttpClient getBelieverHttpsClient() {

            DefaultHttpClient client = null;

            SchemeRegistry Current_Scheme = new SchemeRegistry();
            Current_Scheme.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            try {
                Current_Scheme.register(new Scheme("https", new Naive_SSLSocketFactory(), 8443));
            } catch (KeyManagementException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (UnrecoverableKeyException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (KeyStoreException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            HttpParams Current_Params = new BasicHttpParams();
            int timeoutConnection = 8000;
            HttpConnectionParams.setConnectionTimeout(Current_Params, timeoutConnection);
            int timeoutSocket = 10000;
            HttpConnectionParams.setSoTimeout(Current_Params, timeoutSocket);
            ThreadSafeClientConnManager Current_Manager = new ThreadSafeClientConnManager(Current_Params, Current_Scheme);
            client = new DefaultHttpClient(Current_Manager, Current_Params);
            //HttpPost httpPost = new HttpPost(url);
            //client.execute(httpPost);

         return client;
         }

    public static class Naive_SSLSocketFactory extends SSLSocketFactory
    {
        protected SSLContext Cur_SSL_Context = SSLContext.getInstance("TLS");

        public Naive_SSLSocketFactory ()
                throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException
        {
            super(null, null, null, null, null, (X509HostnameVerifier)null);
            Cur_SSL_Context.init(null, new TrustManager[] { new X509_Trust_Manager() }, null);
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port,
                boolean autoClose) throws IOException
        {
            return Cur_SSL_Context.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

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

    private static class X509_Trust_Manager implements X509TrustManager
    {

        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            // TODO Auto-generated method stub

        }

        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            // TODO Auto-generated method stub

        }

        public X509Certificate[] getAcceptedIssuers() {
            // TODO Auto-generated method stub
            return null;
        }

    };
}

这段代码可以很好地处理URL模式-localhost:8443 / webProject / YourService
Speise 2012年

构造函数SSLSocketFactory(null,null,null,null,null,X509HostnameVerifier)未定义
Michal

1

我不知道ssl证书的Android细节,但是Android不会立即接受自签名的ssl证书是有道理的。我从android论坛找到了该帖子,该帖子似乎正在解决同一问题:http : //androidforums.com/android-applications/950-imap-self-signed-ssl-certificates.html


就像我说的,我不知道Android的细节,但是今天我必须告诉平台您要连接的服务器的ssl证书是什么。也就是说,如果您正在使用平台无法识别的自签名证书。SslCertificate类可能会有所帮助:developer.android.com/reference/android/net/http/… 今天晚些时候,当我有更多时间时,我将对此进行深入研究。
Matti Lyra,2009年


0

由于某种原因,上面针对httpClient提到的解决方案对我不起作用。最后,我能够通过在实现自定义SSLSocketFactory类时正确覆盖该方法来使其工作。

@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) 
                              throws IOException, UnknownHostException 
    {
    return sslFactory.createSocket(socket, host, port, autoClose);
}

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

这对我来说是完美的方式。您可以在以下线程上看到完整的自定义类并实现:http : //blog.syedgakbar.com/2012/07/21/android-https-and-not-trusted-server-certificate-error/


0

我上这堂课发现

package com.example.fakessl;

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;

import android.util.Log;

public class CertificadoAceptar {
    private static TrustManager[] trustManagers;

    public static class _FakeX509TrustManager implements
            javax.net.ssl.X509TrustManager {
        private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};

        public void checkClientTrusted(X509Certificate[] arg0, String arg1)
                throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] arg0, String arg1)
                throws CertificateException {
        }

        public boolean isClientTrusted(X509Certificate[] chain) {
            return (true);
        }

        public boolean isServerTrusted(X509Certificate[] chain) {
            return (true);
        }

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

    public static void allowAllSSL() {

        javax.net.ssl.HttpsURLConnection
                .setDefaultHostnameVerifier(new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });

        javax.net.ssl.SSLContext context = null;

        if (trustManagers == null) {
            trustManagers = new javax.net.ssl.TrustManager[] { new _FakeX509TrustManager() };
        }

        try {
            context = javax.net.ssl.SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            Log.e("allowAllSSL", e.toString());
        } catch (KeyManagementException e) {
            Log.e("allowAllSSL", e.toString());
        }
        javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context
                .getSocketFactory());
    }
}

在你的代码白色这

CertificadoAceptar ca = new CertificadoAceptar();
ca.allowAllSSL();
HttpsTransportSE Transport = new HttpsTransportSE("iphost or host name", 8080, "/WS/wsexample.asmx?WSDL", 30000);

0

帮助我在AWS Apache服务器上使用自签名证书并通过android设备与HttpsURLConnection进行连接的源:

aws实例上的SSL-
带有HTTPS和SSL的 ssl Android Security的 Amazon教程-在客户端上创建自己的信任管理器以接受您的证书
创建自签名证书 -用于创建证书的简单脚本

然后,我执行了以下操作:

  1. 确保服务器支持https(sudo yum install -y mod24_ssl)
  2. 将此脚本放在文件中create_my_certs.sh
#!/bin/bash
FQDN=$1

# make directories to work from
mkdir -p server/ client/ all/

# Create your very own Root Certificate Authority
openssl genrsa \
  -out all/my-private-root-ca.privkey.pem \
  2048

# Self-sign your Root Certificate Authority
# Since this is private, the details can be as bogus as you like
openssl req \
  -x509 \
  -new \
  -nodes \
  -key all/my-private-root-ca.privkey.pem \
  -days 1024 \
  -out all/my-private-root-ca.cert.pem \
  -subj "/C=US/ST=Utah/L=Provo/O=ACME Signing Authority Inc/CN=example.com"

# Create a Device Certificate for each domain,
# such as example.com, *.example.com, awesome.example.com
# NOTE: You MUST match CN to the domain name or ip address you want to use
openssl genrsa \
  -out all/privkey.pem \
  2048

# Create a request from your Device, which your Root CA will sign
openssl req -new \
  -key all/privkey.pem \
  -out all/csr.pem \
  -subj "/C=US/ST=Utah/L=Provo/O=ACME Tech Inc/CN=${FQDN}"

# Sign the request from Device with your Root CA
openssl x509 \
  -req -in all/csr.pem \
  -CA all/my-private-root-ca.cert.pem \
  -CAkey all/my-private-root-ca.privkey.pem \
  -CAcreateserial \
  -out all/cert.pem \
  -days 500

# Put things in their proper place
rsync -a all/{privkey,cert}.pem server/
cat all/cert.pem > server/fullchain.pem         # we have no intermediates in this case
rsync -a all/my-private-root-ca.cert.pem server/
rsync -a all/my-private-root-ca.cert.pem client/
  1. bash create_my_certs.sh yourdomain.com
  2. 将证书放置在服务器上的适当位置(您可以在/etc/httpd/conf.d/ssl.conf中找到配置)。所有这些都应该设置:
    SSLCertificateFile
    SSLCertificateKeyFile
    SSLCertificateChainFile
    SSLCACertificateFile

  3. 使用重新启动httpd sudo service httpd restart并确保已启动httpd :
    停止httpd:[确定]
    启动httpd:[确定]

  4. 复制my-private-root-ca.cert到您的android项目资产文件夹

  5. 创建您的信任管理器:

    SSLContext SSLContext;

    CertificateFactory cf = CertificateFactory.getInstance(“ X.509”); InputStream caInput = context.getAssets()。open(“ my-private-root-ca.cert.pem”); 证书ca; 尝试{ca = cf.generateCertificate(caInput); }最后{caInput.close(); }

      // 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 = SSLContext.getInstance("TLS");
      SSSLContext.init(null, tmf.getTrustManagers(), null);
  6. 并使用HttpsURLConnection建立连接:

    HttpsURLConnection连接=(HttpsURLConnection)url.openConnection(); connection.setSSLSocketFactory(SSLContext.getSocketFactory());

  7. 就是这样,尝试您的https连接。


0

可能您可以尝试这样的事情。这对我有帮助

    SslContextFactory sec = new SslContextFactory();
    sec.setValidateCerts(false);
    sec.setTrustAll(true);

    org.eclipse.jetty.websocket.client.WebSocketClient client = new WebSocketClient(sec);

-1

只需将此方法用作您的HTTPClient:

public static  HttpClient 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 (Exception e) {
        return new DefaultHttpClient();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.