相关文章推荐
玉树临风的马克杯  ·  GitLab CI/CD | GitLab·  1 年前    · 
销魂的棒棒糖  ·  wstring_convert 类 | ...·  1 年前    · 

使用TidHTTP在安卓上出现 "无法加载SSL库 "的错误

9 人关注

我试图在Delphi Seattle中用TIdHTTP.Get下载一个文件。这是一个用于安卓的应用程序,我所有的尝试都失败了。我得到的都是同样的错误 "无法加载SSL库"。这就是程序。

procedure TfrmMain.DownloadPicture(const AURL: string);
  MeS: TMemoryStream;
  cidSSL: TIdSSLIOHandlerSocketOpenSSL;
  cidHTTP: TIdHTTP;
begin
  cidHTTP:= TIdHTTP.Create(nil);
  cidSSL:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  Mes := TMemoryStream.Create;
    cidHTTP.ReadTimeout := 30000;
    cidHTTP.IOHandler := IdSSL;
    cidSSL.SSLOptions.Method := sslvSSLv3;
    cidSSL.SSLOptions.Mode := sslmUnassigned;
    cidSSL.StartSSL;
    cidHTTP.Get(AURL, Mes);
  except
    on E : Exception do
      begin showmessage('Error: '+E.Message);
      end;
  end;
  Mes.Position := 0;
  frmImage.Image.Bitmap.LoadFromStream(Mes);
end;
    
android
delphi
ssl
indy
wordermorr
wordermorr
发布于 2016-05-09
5 个回答
Remy Lebeau
Remy Lebeau
发布于 2021-04-06
已采纳
0 人赞同

如果你 没有 使用Android 6 Marshmallow,OpenSSL在Android上应该是工作正常的。 当你遇到 "无法加载 "的错误时,你可以在 IdSSLOpenSSLHeaders 单元中调用Indy的 WhichFailedToLoad() 函数,以找出OpenSSL无法加载的原因。 如果你的设备没有预装OpenSSL,你可以将OpenSSL二进制文件与你的应用程序一起部署,并使用Indy的 IdOpenSSLSetLibPath() 函数来告诉Indy从哪里加载它们。

也就是说,从Android 6 Marshmallow开始, 谷歌不再支持 Android上的 OpenSSL 。它已经被一个名为 BoringSSL 的自定义分叉所取代, Indy还不完全支持 它(尽管在西雅图发布后,Indy已经做了 一些 与BoringSSL相关的修改)。 因此,如果你在Android 6上使用Indy SSL/TLS时遇到问题,你可以尝试升级到最新的 Github快照 ,看看是否有帮助。

BoringSSL对OpenSSL的API接口做了一些 重大 改变(放弃了函数,改变了数据类型,等等),所以它与现有的OpenSSL代码不具备向后兼容性。 但更糟糕的是,BoringSSL使用与OpenSSL相同的库文件名,并在设备启动时预装,所以不可能在你的Android应用中部署自定义的OpenSSL库二进制文件。当应用程序试图在运行时加载OpenSSL库文件名时,Android将简单地使用预加载的BoringSSL二进制文件(无论你是否调用Indy的 IdOpenSSLSetLibPath() 函数)。

Indy在Android的NDK层面上操作,而不是在Java层面上,所以要让Indy避免使用BoringSSL,就要求用户要么。

  • 用新的文件名重新编译OpenSSL库,使其不与BoringSSL冲突(AFAIK没有这个版本),然后更新Indy以使用这些文件名。

  • 将OpenSSL的源代码直接编译到他们的Android应用中。Indy目前的设置并不支持在iOS以外的任何平台上静态链接OpenSSL,但如果有人能够为Android上的OpenSSL制作可行的 .a 文件,这应该是一个很小的改变,用相关的定义更新Indy的 IdSSLOpenSSLHeaders_static 单元。我确实知道至少有一个用户在尝试这条路线,但该用户还没有成功(让源代码完全正确链接的错误)。

  • 切换到安卓的更高级别的基于Java的加密API。 这是谷歌的首选解决方案。 但印地安人目前并不支持它。 这样做需要为Android套接字I/O和SSL/TLS编写一套全新的 TIdStack TIdIOHandler 类,使用JNI调用来访问Java APIs。 但这有性能和线程问题需要处理。

    所以,目前还没有已知的可行的解决方法来使Indy SSL/TLS在Android 6+上工作。

    更新 :Embarcadero论坛上的一位用户能够找到与Indy兼容的OpenSSL .so 文件,并能在Android 6上工作。

    https://forums.embarcadero.com/thread.jspa?threadID=211089

    在许多关于我的应用程序安装在安卓6设备上会崩溃的报告之后,我在网上搜索了一些提示和几个需要的.so编译文件,1.02版本,将这两个文件添加到我的应用程序的play store部署中(assets\internal),并改变了Indy的路径,调用

    IdOpenSSLSetLibPath(TPath.GetDocumentsPath)

    在我的数据模块的OnCreate中。

    在对我的代码进行这些修改后,我的应用程序在我的全新的S7上完美运行,采用的是Android 6.0.1,在所有其他最近的Android设备上也是如此(使用Play Store部署测试)。

    而现在,谷歌警告弹出,告诉我与我的应用程序一起部署的OpsnSSL文件不是最低要求的1.02f版本(或1.01r)....,所以我在这个论坛的帖子。

    总之,你可以在这里下载最新的.so文件,也就是我现在部署的文件。 https://drive.google.com/file/d/0B7AxqW32K0oXWW9nUk9qaFpHT0k/view?usp=sharing

    同一讨论中的另一个用户似乎也有一些成功。

    我已经在安卓6上运行了几个月,加载.so库很正常。新的问题是我们需要编译新版本的openssl库。Cygwin对我来说不能正常编译,所以我改用linux安装来创建它们(如果可能的话) https://wiki.openssl.org/index.php/Android#Build_the_OpenSSL_Library_2

    这里有一个资源库,你可以抓取一些当前的预建库 https://github.com/emileb/OpenSSL-for-Android-Prebuilt.git

    更新 :以下(德国)论坛的讨论为注册用户提供了Android(和iOS模拟器)的OpenSSL 1.0.2g二进制文件。它们在Google Play商店中不显示安全警告。

    http://www.delphipraxis.net/188736-kompilierte-openssl-bibliotheken-fuer-android.html

    OpenSSL 1.0.2g Android.zip

    OpenSSL 1.0.2g iOS Simulator.zip

    更新 :OpenSSL 1.0.2g的Android二进制文件现在可以在Embarcadero Attachments论坛上找到。

    https://forums.embarcadero.com/thread.jspa?threadID=211147

    然后,为了加载OpenSSL而不是BoringSSL,请遵循以下步骤。

  • 将2个 .so 文件添加到你的项目部署中,并将它们设置为部署到 .\assets\internal\ 文件夹中

  • 在你的DPR的 uses 条款中添加 System.StartupCopy 单元作为第一个单元。

  • 在应用程序启动时调用 IdOpenSSLSetLibPath(TPath.GetDocumentsPath)

    更新 。OpenSSL 1.0.1t和1.0.2h二进制文件现在在Embarcadero附件论坛上。

    https://forums.embarcadero.com/thread.jspa?threadID=211147

    更新 :二进制文件现在已经发布在Indy的Fulgan镜像上。

    http://indy.fulgan.com/SSL/

    更新 :Indy不再使用Fulgan镜像来托管OpenSSL二进制文件。它们现在在他们自己的GitHub repo中。

    https://www.indyproject.org/2020/06/16/openssl-binaries-moved-to-github/

    https://github.com/IndySockets/OpenSSL-Binaries

  • 谢谢你,雷米!我使用的是Android 6 Marshmallow。到现在为止,我不得不找出另一个解决我的问题的方法,并且成功了,但我肯定会在未来再次面临这个问题,你的回答帮助很大。
    我很难过,我很生气,我很绝望。我讨厌谷歌这样做。我恨Embarcadero没有选择在Android 6中使用SSL/TLS(这在今天是一个死刑判决......没有网络加密的应用程序? 喂?)我一直在努力使用Delphi的原因之一。总是有这样的大问题。另一方面,我理解谷歌决定让一切通过他们的操作系统背后的意义。Delphi总是很特别(不是本地的,等等)。我更高兴的是有像Remy和那些发现这个文件的人这样的有帮助的人。至少谷歌对他们的分叉称呼是正确的。BoringSSL。
    几年后来到这里。正如Remy所说,你需要下载so文件。下载后,进入Project -->Deployment并添加这些so文件。编辑远程路径。你就完成了!
    IdOpenSSLSetLibPath没有找到,我可以做什么?
    @BDuarte IdOpenSSLSetLibPath() 是在 IdSSLOpenSSLHeaders 单位中。请确保该单位在你的 uses 条款中。
    user5467230
    user5467230
    发布于 2021-04-06
    0 人赞同

    只要把TidHTTP组件换成TNetHTTPClient就可以了。

    他已经接受了非常详细的答案,并且已经获得了大量的支持票,这对发帖人有什么帮助?
    你甚至不需要很聪明就能理解它的帮助。但我会试着解释,就像我在和一个孩子说话一样。你删除一个有问题的组件,在它的位置上,保留所有的属性,你插入一个新的组件,不需要下载,不需要外部文件。如果你有一个新的玻璃杯,你为什么要把破碎的玻璃杯的碎片粘在一起?
    Alexandre M
    Alexandre M
    发布于 2021-04-06
    0 人赞同

    可能你有一个新的OpenSSL库和一个旧的Indy版本。看看这个。 Indy 10 - IdSMTP.Connect提高 "无法加载SSL库"。

    Alexandre,我使用Delphi Seattle,Indy版本是10.6.2.5298。不过,这些步骤似乎是针对Windows的。
    只有一个Indy的源代码。旧版本的Indy不支持新的OpenSSL,与平台无关。
    Germán Estévez -Neftalí-
    Germán Estévez -Neftalí-
    发布于 2021-04-06
    0 人赞同

    有32/64位的版本。 请确保你有正确版本的文件。

    正确的版本见 这个FTP

    其他下载二进制文件的镜像 在这里

    Edimar Pinheiro
    Edimar Pinheiro
    发布于 2021-04-06
    0 人赞同

    我使用这个代码,这很有效。

      procedure TfPrecos.GetImageByUrl(URL: string);
        Strm: TMemoryStream;
        vIdHTTP: TIdHTTP;
        cidSSL: TIdSSLIOHandlerSocketOpenSSL;
      begin
      IdOpenSSLSetLibPath(TPath.GetDocumentsPath);  //libcrypto.so and libssl.so in ./assets/internal
        vIdHTTP.IOHandler := cidSSL;
        cidSSL.SSLOptions.Method := sslvSSLv23; // version 2.3
        cidSSL.SSLOptions.Mode := sslmUnassigned;
        cidSSL.StartSSL;
          vIdHTTP.Get(URL, Strm);
          if (Strm.Size > 0) then
          begin
            Strm.Position := 0;
              Image1.Bitmap.LoadFromStream(Strm);
            finally
            end;
          end;
        finally
          Strm.DisposeOf;