我正在尝试使用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;
答案 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需要用户:
使用不与BoringSSL冲突的新文件名重新编译OpenSSL库(AFAIK没有可用的已知版本),然后更新Indy以使用这些文件名。
将OpenSSL源代码直接编译到他们的Android应用程序中。 Indy目前尚未设置为支持OpenSSL在除iOS之外的任何平台上的静态链接,但如果有人能够生成可行的IdSSLOpenSSLHeaders_static
单元,那么这应该是一个小的更改,以更新Indy的.a
单元。适用于Android的OpenSSL的1}}文件。我知道至少有一个用户正在尝试这条路线,但该用户尚未成功(错误导致源代码完全正确链接)。
切换到Android的更高级别的基于Java的加密API。这是Google的首选解决方案。但是Indy目前不支持它。这样做需要为Android套接字I / O和SSL / TLS编写一组全新的TIdStack
和TIdIOHandler
类,使用JNI调用来访问Java API。但这有性能和线程问题需要处理。
因此,目前还没有可行的解决方法可用于使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 iOS Simulator.zip
更新:OpenSSL 1.0.2g的Android二进制文件现已在Embarcadero附件论坛中提供:
https://forums.embarcadero.com/thread.jspa?threadID=211147
然后,要加载OpenSSL而不是BoringSSL,请执行以下步骤:
将2个.so
文件添加到项目部署中并将其设置为部署到.\assets\internal\
文件夹
将System.StartupCopy
单元添加为DPR uses
子句中的第一个单元。
在应用启动时致电IdOpenSSLSetLibPath(TPath.GetDocumentsPath)
。
更新:OpenSSL 1.0.1t和1.0.2h二进制文件现在位于Embarcadero附件论坛中:
https://forums.embarcadero.com/thread.jspa?threadID=211147
更新:二进制文件现已发布在Indy的Fulgan镜像上:
答案 1 :(得分:0)
可能你有一个新的OpenSSL库和一个较旧的Indy版本。检查一下: Indy 10 - IdSMTP.Connect raising "Could not load SSL library."
答案 2 :(得分:0)
答案 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;