Indy HTTP Server 使用 OpenSSL 3.0
前言
使用 Indy HTTP Server 做一个 Web Server 或者一个 WebService Server,如果使用 Delphi 默认的 TIdServerIOHandlerSSLOpenSSL,只能加载 OpenSSL 1.0 的 DLL 库。
现如今 OpenSSL 已经是 3.0 了。
还好网上有开源的 TTaurusTLSServerIOHandler 可以用来替换 TIdServerIOHandlerSSLOpenSSL 加载 OpenSSL 3.0 的 DLL 库。
以下描述,基于 Delphi 12 社区版。
如何使用
OpenSSL 1.x 的库
首先,如果使用 TIdServerIOHandlerSSLOpenSSL 加载 OpenSSL 1.0 的动态链接库,应该给程序准备:
1. libeay32.dll
2. ssleay32.dll
上述两个文件的下载地址,请参考:
Securing Indy Network Connections - RAD Studio
上面那篇官方文档里面提到的下载地址是:
Index of /SSL
OpenSSL 3.x 的库
如果使用 OpenSSL 3.0 的动态链接库,则应该是给程序以下两个文件:
1. libcrypto-3.dll
2. libssl-3.dll
上述两个 OpenSSL 3.x 的文件的下载地址,在:
https://github.com/JPeterMugaas/TaurusTLS/tree/main
这个地址的首页里面有提供。
关于 TaurusTLS 的使用
首先去 https://github.com/JPeterMugaas/TaurusTLS/tree/main 下载这个控件的源代码。
然后在 Delphi 里面安装它。
问题:
它的官方页面里面提到:
- Set the INDY_PATH environment variable for your user account to the location where Indy is located.
- Open TaurusForIndy290All.groupproj in the TaurusTLS\Packages\d12 folder.
- Compile TaurusTLS_RTForIndy290.
- Compile TaurusTLS_DTForIndy290 and install it in the IDE.
但实际上,按照上述说法,没法安装。
问题的详情
TaurusTLS_RTForIndy290 的源文件里面:
requiresrtl,IndyCore290,IndySystem290,IndyProtocols290;
实际上 Delphi 自带的 Indy 里面,没有 IndyCore290.dcp 等上述三个带 290 的文件。
把上述代码的 290 删除掉,仅仅留下 IndyCore 以及另外两个同样去掉 290 的引用,编译能够通过,但整个硬盘到处找,也找不到编译后的 bpl 文件。
问题的解决
仔细观察这个控件所在的文件夹:
Delphi\Controls\TaurusTLS-main\Packages\d12
发现里面实际上有两个包文件,名字是:
TaurusTLS_RT.dpk
TaurusTLS_DT.dpk
打开这两个包文件来编译,安装控件成功!
因此,不应该按照它的说明,编译 TaurusTLS_DTForIndy290.dpk 和 TaurusTLS_RTForIndy290.dpk 这两个项目。
使用注意
在 Delphi 中创建一个 Soap Server 项目,选择 Stand alone 模式,因此它就自带了 Indy HTTP Server 作为内置的 Web Server。创建项目时,那个 https 的检查框不要勾选。
程序有了以后,顺便创建一个 WebService 的接口。给接口搞一个函数。我这里是:
{ Invokable interface IIndySSLTaurus }unit IndySSLTaurusIntf;interfaceuses Soap.InvokeRegistry, System.Types, Soap.XSBuiltIns;type{ Invokable interfaces must derive from IInvokable }IIndySSLTaurus = interface(IInvokable)['{CA6E9719-4ABF-4E22-8E81-C74231025E99}']{ Methods of Invokable interface must not use the default }{ calling convention; stdcall is recommended }function Hello(const S: string): string; stdcall;end;implementationinitialization{ Invokable interfaces must be registered }InvRegistry.RegisterInterface(TypeInfo(IIndySSLTaurus));end.
上述接口的实现部分的代码:
{ Invokable implementation File for TIndySSLTaurus which implements IIndySSLTaurus }unit IndySSLTaurusImpl;interfaceuses Soap.InvokeRegistry, System.Types, Soap.XSBuiltIns, IndySSLTaurusIntf;type{ TIndySSLTaurus }TIndySSLTaurus = class(TInvokableClass, IIndySSLTaurus)publicfunction Hello(const S: string): string; stdcall;end;implementation{ TIndySSLTaurus }function TIndySSLTaurus.Hello(const S: string): string;
beginResult := 'Hello, ' + S;
end;initialization
{ Invokable classes must be registered }InvRegistry.RegisterInvokableClass(TIndySSLTaurus);
end.
这个项目,Delphi IDE 默认给出的是 8080 端口。运行它。
然后,做一个 WebService 客户端程序,拖一个 HTTPRIO1 这个控件到 Form 上面。
这个时候,还没有实现 https,因此,给这个 HTTPRIO1.URL 赋值:
http://localhost:8080/soap 就可以了。
给客户端写测试代码:
procedure TForm2.Button1Click(Sender: TObject);
varIntf: IIndySSLTaurus;
beginIntf := HTTPRIO1 as IIndySSLTaurus;tryMemo1.Lines.Add(Intf.Hello(Edit1.Text));finallyIntf := nil;end;
end;
运行客户端,点击按钮,能够在 Memo1 里面看到从服务器端正确返回的值。测试通过。
为服务器端增加 OpenSSL 3.x 的支持
从控件面板,拖一个 TaurusTLSServerIOHandler1 到主界面上。然后为服务器端增加以下代码:
procedure TForm1.FormCreate(Sender: TObject);
beginFServer := TIdHTTPWebBrokerBridge.Create(Self);//必须用代码指定。设计期在属性面板里面指定的证书文件,没有效果。TaurusTLSServerIOHandler1.DefaultCert.PublicKey := 'mysite.net.cert.pem';TaurusTLSServerIOHandler1.DefaultCert.PrivateKey := 'mysite.net.key.pem';FServer.OnQuerySSLPort := OnQuerySSLPort;//写绝对路径也没问题
// TaurusTLSServerIOHandler1.DefaultCert.PublicKey := 'D:\TestD12\IndySSL_Taurus\证书备份\mysite.net.cert.pem';
// TaurusTLSServerIOHandler1.DefaultCert.PrivateKey := 'D:\TestD12\IndySSL_Taurus\证书备份\mysite.net.key.pem';FServer.IOHandler := TaurusTLSServerIOHandler1; TaurusTLS.LoadOpenSSLLibrary;
end;procedure TForm1.OnQuerySSLPort(APort: TIdPort; var AUseSSL: Boolean);
beginAPort := 8080; //如果不加上这个,就只能走默认的 443AUseSSL := True;
end;
剩下的代码就是 Delphi IDE 在创建这个 Soap Server 的时候自动创建的代码了,如下:
procedure TForm1.StartServer;
beginif not FServer.Active thenbeginFServer.Bindings.Clear;FServer.DefaultPort := StrToInt(EditPort.Text); //这里默认是 8080FServer.OnQuerySSLPort := OnQuerySSLPort;FServer.Active := True;end;
end;
另外,还有个事件方法,是关于证书的密码的:
procedure TForm1.TaurusTLSServerIOHandler1GetPassword(ASender: TObject;var VPassword: string; const AIsWrite: Boolean; var VOk: Boolean);
beginVPassword := '';
end;
我的证书是自签发证书,没有密码。因此这个事件方法没有也能运行。
到这里,这个支持OpenSSL 3.x 的 https 的 Soap Server 就可以使用了。编译运行它。
把前面的客户端的 URL 改为:https://localhost:8080/soap 然后运行客户端,测试通过。
需要注意的问题
我在前面的代码里面,为这个控件,赋值了证书文件名:
TaurusTLSServerIOHandler1.DefaultCert.PublicKey := 'mysite.net.cert.pem';TaurusTLSServerIOHandler1.DefaultCert.PrivateKey := 'mysite.net.key.pem';
实际上,它在设计期的属性面板里面,有这两个属性。
但是,如果我们在设计期的属性面板里面填写上面的两个文件名,不管是填文件名,还是填写带绝对路径的文件名,只要没有用代码为它赋值,都不能正常工作。
对比起来,如果采用 Indy 自己的 IdServerIOHandlerSSLOpenSSL1(只支持 OpenSSL 1.x)的话,设计期填写的上述属性,也是能够正常工作的。
还有一个小问题
因为我使用的是自己签发的证书,所以在测试中,在 Debug 状态下,用浏览器访问本程序,Delphi IDE 会弹出不少异常提示,然后浏览器还是能够显示这个 Soap Server 的首页。但此时如果点击首页上的其它链接,比如查看接口,就会出错到页面完全无法显示。
但是,如果此时是使用 Delphi 自己的 Web Service 客户端去访问这个服务器,调用接口函数,没有任何问题,也没有异常错误提示。
结束
使用 Taurus 这个控件,确实能够让 Indy 调用 OpenSSL 3.x 的 DLL 库。