【问题标题】:Is there a way to enable TLS 1.2 in Picasso library on older devices?有没有办法在旧设备上的 Picasso 库中启用 TLS 1.2?
【发布时间】:2017-06-29 05:33:30
【问题描述】:

我的应用正在使用来自 HTTPS 源 Solar Dynamics Observatory 的图像,并且这些图像在使用 API 21 (5.0) 或更高版本的 Android 版本上加载得很好,但我可以' t 在从 API 16(我为我的应用程序设置的最低 API)到 API 19 的 Android 版本上加载这些图像。这是加载图像的代码,它非常基本 -

Picasso.with(this)
                .load("https://sdo.gsfc.nasa.gov/assets/img/latest/latest_2048_HMIIC.jpg")
                .placeholder(R.drawable.placeholdernew)
                .error(R.drawable.errornew)
                .into(mImageView, new com.squareup.picasso.Callback() {
                    @Override
                    public void onSuccess() {
                        mAttacher = new PhotoViewAttacher(mImageView); //this adds the zoom function after the picture is loaded successfully, so the user couldn't zoom the placeholder or error picture :)
                    }

                    @Override
                    public void onError() {
                        Log.d("Pic Error", "Loading Error");
                    }
                });

如您所见,我的代码中没有错误(我认为:P)。我使用 SSLLabs 站点检查了 SDO 站点,该站点说此 SDO 服务器不接受 API 20 之前的 Android 设备上的 TLS 握手。有没有办法在旧 Android 版本的 Picasso 中启用 TLS 1.2?帮助将不胜感激!在此先感谢:)

【问题讨论】:

  • 如果您添加 OkHttp,您可以尝试配置 OkHttp 以支持 TLS 1.2,如 some of these answers 中所述,然后让 Picasso 使用您的 OkHttpClient。下周初我将尝试为此编写一些代码。非常感谢您提供真实的 URL,因为它有助于测试。
  • 谢谢你。如果您想测试图像,只需单击任何太阳图像(如果重要的话,我在我的应用程序中使用 2048 个图像和 512 个图像)。我现在正在等待代码:)

标签: android picasso tls1.2


【解决方案1】:

好吧,这行得通,但它很丑。

在你项目的依赖中,添加:

compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.squareup.okhttp3:okhttp:3.6.0'
compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.0.2'

(您可能已经有了picasso 行——我只是确保您使用的是最新版本)

接下来,将这个类添加到您的项目中(基于this answer):

  public static class TLSSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory internalSSLSocketFactory;

    public TLSSocketFactory(SSLSocketFactory delegate) throws
      KeyManagementException, NoSuchAlgorithmException {
      internalSSLSocketFactory = delegate;
    }

    @Override
    public String[] getDefaultCipherSuites() {
      return internalSSLSocketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
      return internalSSLSocketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose)
      throws IOException {
      return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
      return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
      return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
      return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
      return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort));
    }

    /*
     * Utility methods
     */

    private static Socket enableTLSOnSocket(Socket socket) {
      if (socket != null && (socket instanceof SSLSocket)
        && isTLSServerEnabled((SSLSocket) socket)) { // skip the fix if server doesn't provide there TLS version
        ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1.1", "TLSv1.2"});
      }
      return socket;
    }

    private static boolean isTLSServerEnabled(SSLSocket sslSocket) {
      System.out.println("__prova__ :: " + sslSocket.getSupportedProtocols().toString());
      for (String protocol : sslSocket.getSupportedProtocols()) {
        if (protocol.equals("TLSv1.1") || protocol.equals("TLSv1.2")) {
          return true;
        }
      }
      return false;
    }
  }

(那个类是public static,因此被设计成一个嵌套在别的东西里面的类——如果你想让它成为一个独立的类,就去掉static

然后,在您使用 Picasso 的类中,添加此方法,基于 this issue comment

  public X509TrustManager provideX509TrustManager() {
    try {
      TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      factory.init((KeyStore) null);
      TrustManager[] trustManagers = factory.getTrustManagers();
      return (X509TrustManager) trustManagers[0];
    }
    catch (NoSuchAlgorithmException exception) {
      Log.e(getClass().getSimpleName(), "not trust manager available", exception);
    }
    catch (KeyStoreException exception) {
      Log.e(getClass().getSimpleName(), "not trust manager available", exception);
    }

    return null;
  }

最后,这段代码应该会成功下载你的图片:

    SSLContext sslContext=SSLContext.getInstance("TLS");
    sslContext.init(null, null, null);
    SSLSocketFactory noSSLv3Factory;

    if (Build.VERSION.SDK_INT<=Build.VERSION_CODES.KITKAT) {
      noSSLv3Factory=new TLSSocketFactory(sslContext.getSocketFactory());
    }
    else {
      noSSLv3Factory=sslContext.getSocketFactory();
    }

    OkHttpClient.Builder okb=new OkHttpClient.Builder()
      .sslSocketFactory(noSSLv3Factory, provideX509TrustManager());
    OkHttpClient ok=okb.build();

    Picasso p=new Picasso.Builder(getActivity())
      .downloader(new OkHttp3Downloader(ok))
      .build();

    p.load(
      "https://sdo.gsfc.nasa.gov/assets/img/latest/latest_2048_HMIIC.jpg")
      .fit().centerCrop()
      .placeholder(R.drawable.owner_placeholder)
      .error(R.drawable.owner_error).into(icon);

(您可以将我的fit() 和后续调用替换为适合您项目的调用)

如果您碰巧认识维护该 NASA 服务器的人员......他们真的应该升级他们的 SSL 支持。只是说说而已。

【讨论】:

  • 哇,非常感谢!明天我会测试它,让你知道它是否工作:)
  • 再次感谢您,图像加载实际上是在旧设备上工作的! :)
  • 我在OkHttpClient.Builder okb = new OkHttpClient.Builder() 上收到NoClassDefFoundError okio.Buffer。我已经包含了所有三个库:picasso 2.5.2okhttp3.10.0picasso2-okhttp3-downloader-1.0.1。不确定是否重要我在 eclipse 上运行我的项目并将库包含在 libs 文件夹中。
  • @Abbas:Google 不再支持 Eclipse 开发。您将需要手动跟踪这些库使用的每个依赖项,并添加这些库。一个是okio,就是你看到的NoClassDefError的来源。
  • 感谢这解决了崩溃,但图像仍然没有加载。但是我能够使用相同的代码使其工作。早些时候,我尝试将 Picasso 对象保存在一个静态类中并使用它,但是当它不起作用时,我将相同的代码放在一个单独的文件中,并带有它自己的静态 picasso 对象(一个单例类),令人惊讶的是它起作用了。任何想法为什么单身人士会工作,但不是。附言之前代码在主要活动的onCreate() 中会不会搞砸?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-22
  • 2020-11-08
  • 1970-01-01
  • 2018-10-31
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多