使用TidHTTP在Android上出现“无法加载SSL库”错误

时间:2016-05-08 22:43:01

标签: android delphi ssl indy

我正在尝试使用Delphi Seattle中的TIdHTTP.Get下载文件。这是一个Android的应用程序,我的所有尝试都失败了。所有我得到它是相同的错误“无法加载SSL库”。这是程序:

procedure TfrmMain.DownloadPicture(const AURL: string);
var
  MeS: TMemoryStream;
  cidSSL: TIdSSLIOHandlerSocketOpenSSL;
  cidHTTP: TIdHTTP;
begin
  cidHTTP:= TIdHTTP.Create(nil);
  cidSSL:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  Mes := TMemoryStream.Create;

  try
    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;

5 个答案:

答案 0 :(得分:13)

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

话虽如此,从Android 6 Marshmallow开始,Google no longer supports OpenSSL在Android上。它已被替换为名为BoringSSL的自定义分支,Indy does not fully support yet(尽管在西雅图发布之后已经对Indy进行了一些 BoringSSL相关更改)。因此,如果您在Android 6上使用Indy SSL / TLS时遇到问题,可以尝试升级到最新的SVN snapshot以查看它是否有帮助(在撰写本文时,当前的SVN修订版为5359)。

BoringSSL对OpenSSL API接口进行了一些主要更改(删除功能,更改数据类型等),因此它不会向后兼容现有的OpenSSL代码。但更糟糕的是,BoringSSL使用与OpenSSL相同的库文件名,并在设备启动时预先加载,因此无法使用Android应用程序部署自定义构建的OpenSSL库二进制文件。当应用程序尝试在运行时加载OpenSSL库文件名时,Android将简单地使用预加载的BoringSSL二进制文件(无论您是否调用Indy' IdOpenSSLSetLibPath()函数)。

Indy在Android的NDK级别运行,而不是Java级别,因此要让Indy避免BoringSSL需要用户:

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

  2. 将OpenSSL源代码直接编译到他们的Android应用程序中。 Indy目前尚未设置为支持OpenSSL在除iOS之外的任何平台上的静态链接,但如果有人能够生成可行的IdSSLOpenSSLHeaders_static单元,那么这应该是一个小的更改,以更新Indy的.a单元。适用于Android的OpenSSL的1}}文件。我知道至少有一个用户正在尝试这条路线,但该用户尚未成功(错误导致源代码完全正确链接)。

  3. 切换到Android的更高级别的基于Java的加密API。这是Google的首选解决方案。但是Indy目前不支持它。这样做需要为Android套接字I / O和SSL / TLS编写一组全新的TIdStackTIdIOHandler类,使用JNI调用来访问Java API。但这有性能和线程问题需要处理。

  4. 因此,目前还没有可行的解决方法可用于使Indy SSL / TLS在Android 6 +上运行。

    更新:Embarcadero论坛中的用户能够找到与Indy兼容并在Android 6上运行的OpenSSL .so文件:

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

      

    在我的应用程序崩溃之后,如果安装在Android 6设备中,我已经搜索了一些提示和一些所需的.so编译文件,1.02版本,将这两个文件添加到Play商店部署我的应用程序(assets \ internal)并更改了Indy的路径

         

    IdOpenSSLSetLibPath(TPath.GetDocumentsPath)

         

    在我的数据模块的OnCreate中。

         

    在对我的代码进行修改后,我的应用程序在我的全新S7与Android 6.0.1以及所有其他最近的Android设备(使用Play商店部署测试)上完美运行。

         

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

         

    ...

         

    无论如何,你可以在这里下载最新的.so文件,我现在部署的文件:   https://drive.google.com/file/d/0B7AxqW32K0oXWW9nUk9qaFpHT0k/view?usp=sharing

    同一讨论中的另一位用户似乎也取得了一些成功:

      

    我一直在运行android 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附件论坛中提供:

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

    然后,要加载OpenSSL而不是BoringSSL,请执行以下步骤:

    1. 将2个.so文件添加到项目部署中并将其设置为部署到.\assets\internal\文件夹

    2. System.StartupCopy单元添加为DPR uses子句中的第一个单元。

    3. 在应用启动时致电IdOpenSSLSetLibPath(TPath.GetDocumentsPath)

    4. 更新:OpenSSL 1.0.1t和1.0.2h二进制文件现在位于Embarcadero附件论坛中:

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

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

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

答案 1 :(得分:0)

可能你有一个新的OpenSSL库和一个较旧的Indy版本。检查一下: Indy 10 - IdSMTP.Connect raising "Could not load SSL library."

答案 2 :(得分:0)

有32/64位的版本。 确保您拥有正确版本的文件。

请参阅this FTP了解正确的版本。

下载二进制文件here的其他镜像。

答案 3 :(得分:0)

只需将TidHTTP组件替换为TNetHTTPClient

答案 4 :(得分:0)

我使用这个代码并且这有效:

  procedure TfPrecos.GetImageByUrl(URL: string);
  var
    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;
    try
      vIdHTTP.Get(URL, Strm);
      if (Strm.Size > 0) then
      begin
        Strm.Position := 0;
        try
          Image1.Bitmap.LoadFromStream(Strm);
        finally
       
        end;
      end;
    finally
      Strm.DisposeOf;
      vIdHTTP.DisposeOf;
    end;

  end;