使用 TidHTTP 在 Android 上"Could not load SSL library"错误



我正在尝试用TIdHTTP下载一个文件。请访问Delphi Seattle。这是一个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;

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

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

BoringSL对OpenSSL API接口进行了一些重大更改(删除函数、更改数据类型等),因此它与现有的OpenSSL代码不向后兼容。但更糟糕的是,BoringSL使用与OpenSSL相同的库文件名,并且在设备启动时预加载,因此无法使用Android应用程序部署定制的OpenSSL库二进制文件。当应用程序试图在运行时加载OpenSSL库文件名时,Android只需使用预加载的BoringSL二进制文件(无论您是否调用Indy的IdOpenSSLSetLibPath()函数)。

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

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

  2. 将OpenSSL源代码直接编译到他们的Android应用程序中。Indy目前还没有设置为在iOS以外的任何平台上支持OpenSSL的静态链接,但如果有人能在Android上为OpenSSL生成可行的.a文件,这应该是一个小小的改变,用相关定义更新Indy的IdSSLOpenSSLHeaders_static单元。据我所知,至少有一个用户尝试了这条路线,但该用户还没有成功(在正确链接源代码时出错)。

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

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


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

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

在许多关于我的应用程序在安卓6设备中安装后崩溃的报告之后,我在网上搜索了一些提示和一些所需的提示。因此编译的文件,1.02版本,将这两个文件添加到我应用程序的play store部署中(资产\内部),并更改了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模拟器.zip


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

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

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

  1. 将2个.so文件添加到项目部署中,并将它们设置为部署到.assetsinternal文件夹

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

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


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

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


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

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


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

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

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

只需将TidHTTP组件交换为TNetHTTPClient

您可能有一个新的OpenSSL库和一个旧的Indy版本。检查此项:Indy 10-IdSMTP。连接升高";无法加载SSL库">

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

请参阅此FTP以获取正确的版本。

其他镜像可在此处下载二进制文件。

我使用这个代码,它可以工作:

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;

最新更新