前言
使用 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
上面那篇官方文档里面提到的下载地址是:
OpenSSL 3.x 的库
如果使用 OpenSSL 3.0 的动态链接库,则应该是给程序以下两个文件:
-
libcrypto-3.dll
-
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 的源文件里面:
Delphi
requires
rtl,
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 的接口。给接口搞一个函数。我这里是:
Delphi
{ Invokable interface IIndySSLTaurus }
unit IndySSLTaurusIntf;
interface
uses 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;
implementation
initialization
{ Invokable interfaces must be registered }
InvRegistry.RegisterInterface(TypeInfo(IIndySSLTaurus));
end.
上述接口的实现部分的代码:
Delphi
{ Invokable implementation File for TIndySSLTaurus which implements IIndySSLTaurus }
unit IndySSLTaurusImpl;
interface
uses Soap.InvokeRegistry, System.Types, Soap.XSBuiltIns, IndySSLTaurusIntf;
type
{ TIndySSLTaurus }
TIndySSLTaurus = class(TInvokableClass, IIndySSLTaurus)
public
function Hello(const S: string): string; stdcall;
end;
implementation
{ TIndySSLTaurus }
function TIndySSLTaurus.Hello(const S: string): string;
begin
Result := '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 就可以了。
给客户端写测试代码:
Delphi
procedure TForm2.Button1Click(Sender: TObject);
var
Intf: IIndySSLTaurus;
begin
Intf := HTTPRIO1 as IIndySSLTaurus;
try
Memo1.Lines.Add(Intf.Hello(Edit1.Text));
finally
Intf := nil;
end;
end;
运行客户端,点击按钮,能够在 Memo1 里面看到从服务器端正确返回的值。测试通过。
为服务器端增加 OpenSSL 3.x 的支持
从控件面板,拖一个 TaurusTLSServerIOHandler1 到主界面上。然后为服务器端增加以下代码:
Delphi
procedure TForm1.FormCreate(Sender: TObject);
begin
FServer := 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);
begin
APort := 8080; //如果不加上这个,就只能走默认的 443
AUseSSL := True;
end;
剩下的代码就是 Delphi IDE 在创建这个 Soap Server 的时候自动创建的代码了,如下:
Delphi
procedure TForm1.StartServer;
begin
if not FServer.Active then
begin
FServer.Bindings.Clear;
FServer.DefaultPort := StrToInt(EditPort.Text); //这里默认是 8080
FServer.OnQuerySSLPort := OnQuerySSLPort;
FServer.Active := True;
end;
end;
另外,还有个事件方法,是关于证书的密码的:
Delphi
procedure TForm1.TaurusTLSServerIOHandler1GetPassword(ASender: TObject;
var VPassword: string; const AIsWrite: Boolean; var VOk: Boolean);
begin
VPassword := '';
end;
我的证书是自签发证书,没有密码。因此这个事件方法没有也能运行。
到这里,这个支持OpenSSL 3.x 的 https 的 Soap Server 就可以使用了。编译运行它。
把前面的客户端的 URL 改为:https://localhost:8080/soap 然后运行客户端,测试通过。
需要注意的问题
我在前面的代码里面,为这个控件,赋值了证书文件名:
Delphi
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 库。