IdSSLDotNET.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. unit IdSSLDotNET;
  2. interface
  3. {$i IdCompilerDefines.inc}
  4. {*******************************************************}
  5. { }
  6. { Indy SSL Support for Microsoft.NET 2.0 }
  7. { }
  8. { Copyright (C) 2007 Indy Pit Crew }
  9. { Original author J. Peter Mugaas }
  10. { 2007-Aug-22 }
  11. { }
  12. {*******************************************************}
  13. uses
  14. Classes,
  15. IdException,
  16. IdGlobal,
  17. IdIOHandler,
  18. IdSocketHandle,
  19. IdSSL,
  20. IdThread,
  21. IdYarn,
  22. System.Collections,
  23. System.IO,
  24. System.Net.Sockets,
  25. System.Net.Security,
  26. System.Security.Authentication,
  27. System.Security.Cryptography.X509Certificates;
  28. const
  29. DEF_clientCertificateRequired = False;
  30. DEF_checkCertificateRevocation = True;
  31. type
  32. TOnValidatePeerCertificate = procedure (ASender : TObject;
  33. ACertificate : X509Certificate; AChain : X509Chain;
  34. AsslPolicyErrors : SslPolicyErrors; var VValid : Boolean) of object;
  35. TOnLocalCertificateSelectionCallback = procedure (ASender : TObject;
  36. AtargetHost : String;
  37. AlocalCertificates : X509CertificateCollection;
  38. AremoteCertificate : X509Certificate;
  39. AacceptableIssuers : array of String;
  40. VCert : X509Certificate) of object;
  41. TIdSSLIOHandlerSocketNET = class(TIdSSLIOHandlerSocketBase)
  42. protected
  43. FenabledSslProtocols: System.Security.Authentication.SslProtocols;
  44. FOnValidatePeerCertificate : TOnValidatePeerCertificate;
  45. FOnLocalCertificateSelection : TOnLocalCertificateSelectionCallback;
  46. FSSL : SslStream;
  47. FServerCertificate : X509Certificate;
  48. FClientCertificates : X509CertificateCollection;
  49. FOnSSLHandshakeDone : TNotifyEvent;
  50. FclientCertificateRequired : Boolean;
  51. FcheckCertificateRevocation : Boolean;
  52. procedure OpenEncodedConnection; virtual;
  53. //Ssl certificate validation callback
  54. function ValidatePeerCertificate(
  55. sender : System.&Object;
  56. certificate : X509Certificate;
  57. chain : X509Chain;
  58. sslPolicyErrors : SslPolicyErrors) : Boolean;
  59. function LocalCertificateSelectionCallback (
  60. sender : System.&Object;
  61. targetHost : String;
  62. localCertificates : X509CertificateCollection;
  63. remoteCertificate : X509Certificate;
  64. acceptableIssuers : array of String) : X509Certificate;
  65. function RecvEnc(var VBuffer: TIdBytes): Integer; override;
  66. function SendEnc(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override;
  67. procedure SetPassThrough(const Value: Boolean); override;
  68. procedure InitComponent; override;
  69. procedure ConnectClient; override;
  70. //
  71. function GetCipherAlgorithm: CipherAlgorithmType;
  72. function GetCipherStrength: Integer;
  73. function GetHashAlgorithm: HashAlgorithmType;
  74. function GetHashStrength: Integer;
  75. function GetIsEncrypted: Boolean;
  76. function GetIsSigned: Boolean;
  77. function GetKeyExchangeAlgorithm: ExchangeAlgorithmType;
  78. function GetKeyExchangeStrength: Integer;
  79. function GetRemoteCertificate: X509Certificate;
  80. function GetSslProtocol: SslProtocols;
  81. public
  82. procedure Close; override;
  83. procedure StartSSL; override;
  84. function Clone : TIdSSLIOHandlerSocketBase; override;
  85. property CipherAlgorithm : CipherAlgorithmType read GetCipherAlgorithm;
  86. property CipherStrength : Integer read GetCipherStrength;
  87. property HashAlgorithm : HashAlgorithmType read GetHashAlgorithm;
  88. property HashStrength : Integer read GetHashStrength;
  89. property IsEncrypted : Boolean read GetIsEncrypted;
  90. property IsSigned : Boolean read GetIsSigned;
  91. property KeyExchangeAlgorithm : ExchangeAlgorithmType read GetKeyExchangeAlgorithm;
  92. property KeyExchangeStrength : Integer read GetKeyExchangeStrength;
  93. property RemoteCertificate : X509Certificate read GetRemoteCertificate;
  94. property SslProtocol : SslProtocols read GetSslProtocol;
  95. published
  96. property enabledSslProtocols : System.Security.Authentication.SslProtocols read FenabledSslProtocols write FenabledSslProtocols;
  97. property ServerCertificate : X509Certificate read FServerCertificate write FServerCertificate;
  98. property ClientCertificates : X509CertificateCollection read FClientCertificates write FClientCertificates;
  99. property clientCertificateRequired : Boolean read FclientCertificateRequired write FclientCertificateRequired;
  100. property checkCertificateRevocation : Boolean read FcheckCertificateRevocation write FcheckCertificateRevocation;
  101. property OnSSLHandshakeDone : TNotifyEvent read FOnSSLHandshakeDone write FOnSSLHandshakeDone;
  102. property OnLocalCertificateSelection : TOnLocalCertificateSelectionCallback
  103. read FOnLocalCertificateSelection write FOnLocalCertificateSelection;
  104. property OnValidatePeerCertificate : TOnValidatePeerCertificate
  105. read FOnValidatePeerCertificate write FOnValidatePeerCertificate;
  106. end;
  107. TIdServerIOHandlerSSLNET = class(TIdServerIOHandlerSSLBase)
  108. protected
  109. FOnValidatePeerCertificate : TOnValidatePeerCertificate;
  110. FOnLocalCertificateSelection : TOnLocalCertificateSelectionCallback;
  111. FOnSSLHandshakeDone : TNotifyEvent;
  112. FenabledSslProtocols : System.Security.Authentication.SslProtocols;
  113. FServerCertificate : X509Certificate;
  114. FclientCertificateRequired : Boolean;
  115. FcheckCertificateRevocation : Boolean;
  116. procedure InitComponent; override;
  117. procedure SetIOHandlerValues(AIO : TIdSSLIOHandlerSocketNET);
  118. published
  119. public
  120. destructor Destroy; override;
  121. procedure Init; override;
  122. procedure Shutdown; override;
  123. function MakeClientIOHandler : TIdSSLIOHandlerSocketBase; override;
  124. //
  125. function MakeFTPSvrPort : TIdSSLIOHandlerSocketBase; override;
  126. function MakeFTPSvrPasv : TIdSSLIOHandlerSocketBase; override;
  127. function Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; override;
  128. published
  129. property enabledSslProtocols : System.Security.Authentication.SslProtocols read FenabledSslProtocols write FenabledSslProtocols;
  130. property ServerCertificate : X509Certificate read FServerCertificate write FServerCertificate;
  131. property clientCertificateRequired : Boolean read FclientCertificateRequired write FclientCertificateRequired;
  132. property checkCertificateRevocation : Boolean read FcheckCertificateRevocation write FcheckCertificateRevocation;
  133. property OnSSLHandshakeDone : TNotifyEvent read FOnSSLHandshakeDone write FOnSSLHandshakeDone;
  134. property OnLocalCertificateSelection : TOnLocalCertificateSelectionCallback
  135. read FOnLocalCertificateSelection write FOnLocalCertificateSelection;
  136. property OnValidatePeerCertificate : TOnValidatePeerCertificate
  137. read FOnValidatePeerCertificate write FOnValidatePeerCertificate;
  138. end;
  139. EIdSSLNetException = class(EIdException);
  140. EIdSSLCertRequiredForSvr = class(EIdSSLNetException);
  141. EIdSSLNotAuthenticated = class(EIdSSLNetException);
  142. implementation
  143. uses
  144. IdResourceStringsSSLDotNet,
  145. IdStack,
  146. SysUtils;
  147. { TIdSSLIOHandlerSocketNET }
  148. function TIdSSLIOHandlerSocketNET.Clone: TIdSSLIOHandlerSocketBase;
  149. begin
  150. Result := TIdSSLIOHandlerSocketNET.Create(nil);
  151. TIdSSLIOHandlerSocketNET(Result).FenabledSslProtocols := FenabledSslProtocols;
  152. TIdSSLIOHandlerSocketNET(Result).FOnValidatePeerCertificate := FOnValidatePeerCertificate;
  153. TIdSSLIOHandlerSocketNET(Result).FOnLocalCertificateSelection := FOnLocalCertificateSelection;
  154. TIdSSLIOHandlerSocketNET(Result).FServerCertificate := FServerCertificate;
  155. TIdSSLIOHandlerSocketNET(Result).FClientCertificates := FClientCertificates;
  156. TIdSSLIOHandlerSocketNET(Result).FOnSSLHandshakeDone := FOnSSLHandshakeDone;
  157. end;
  158. procedure TIdSSLIOHandlerSocketNET.Close;
  159. begin
  160. if Assigned(FSSL) then
  161. begin
  162. FSSL.Close;
  163. FreeAndNil(FSSL);
  164. end;
  165. inherited;
  166. end;
  167. procedure TIdSSLIOHandlerSocketNET.ConnectClient;
  168. var
  169. LPassThrough: Boolean;
  170. begin
  171. // RLebeau 1/11/07: In case a proxy is being used, pass through
  172. // any data from the base class unencrypted when setting up that
  173. // connection. We should do this anyway since SSL hasn't been
  174. // initialized yet!
  175. LPassThrough := fPassThrough;
  176. fPassThrough := True;
  177. try
  178. inherited ConnectClient;
  179. finally
  180. fPassThrough := LPassThrough;
  181. end;
  182. StartSSL;
  183. end;
  184. function TIdSSLIOHandlerSocketNET.GetCipherAlgorithm: CipherAlgorithmType;
  185. begin
  186. if Assigned(FSSL) then
  187. begin
  188. Result := FSSL.CipherAlgorithm;
  189. end
  190. else
  191. begin
  192. Result := System.Security.Authentication.CipherAlgorithmType.None;
  193. end;
  194. end;
  195. function TIdSSLIOHandlerSocketNET.GetCipherStrength: Integer;
  196. begin
  197. if Assigned(FSSL) then
  198. begin
  199. Result := FSSL.CipherStrength;
  200. end
  201. else
  202. begin
  203. Result := 0;
  204. end;
  205. end;
  206. function TIdSSLIOHandlerSocketNET.GetHashAlgorithm: HashAlgorithmType;
  207. begin
  208. if Assigned(FSSL) then
  209. begin
  210. Result := FSSL.HashAlgorithm;
  211. end
  212. else
  213. begin
  214. Result := HashAlgorithmType.None;
  215. end;
  216. end;
  217. function TIdSSLIOHandlerSocketNET.GetHashStrength: Integer;
  218. begin
  219. if Assigned(FSSL) then
  220. begin
  221. Result := FSSL.HashStrength;
  222. end
  223. else
  224. begin
  225. Result := 0;
  226. end;
  227. end;
  228. function TIdSSLIOHandlerSocketNET.GetIsEncrypted: Boolean;
  229. begin
  230. if Assigned(FSSL) then
  231. begin
  232. Result := FSSL.IsEncrypted;
  233. end
  234. else
  235. begin
  236. Result := False;
  237. end;
  238. end;
  239. function TIdSSLIOHandlerSocketNET.GetIsSigned: Boolean;
  240. begin
  241. if Assigned(FSSL) then
  242. begin
  243. Result := FSSL.IsSigned;
  244. end
  245. else
  246. begin
  247. Result := False;
  248. end;
  249. end;
  250. function TIdSSLIOHandlerSocketNET.GetKeyExchangeAlgorithm: ExchangeAlgorithmType;
  251. begin
  252. if Assigned(FSSL) then
  253. begin
  254. Result := FSSL.KeyExchangeAlgorithm;
  255. end
  256. else
  257. begin
  258. Result := ExchangeAlgorithmType.None;
  259. end;
  260. end;
  261. function TIdSSLIOHandlerSocketNET.GetKeyExchangeStrength: Integer;
  262. begin
  263. if Assigned(FSSL) then
  264. begin
  265. Result := FSSL.KeyExchangeStrength;
  266. end
  267. else
  268. begin
  269. Result := 0;
  270. end;
  271. end;
  272. function TIdSSLIOHandlerSocketNET.GetRemoteCertificate: X509Certificate;
  273. begin
  274. if Assigned(FSSL) then
  275. begin
  276. Result := FSSL.RemoteCertificate;
  277. end
  278. else
  279. begin
  280. Result := nil;
  281. end;
  282. end;
  283. function TIdSSLIOHandlerSocketNET.GetSslProtocol: SslProtocols;
  284. begin
  285. if Assigned(FSSL) then
  286. begin
  287. Result := FSSL.SslProtocol;
  288. end
  289. else
  290. begin
  291. Result := SslProtocols.None;
  292. end;
  293. end;
  294. procedure TIdSSLIOHandlerSocketNET.InitComponent;
  295. begin
  296. inherited;
  297. FenabledSslProtocols := System.Security.Authentication.SslProtocols.Default;
  298. FclientCertificateRequired := DEF_clientCertificateRequired;
  299. FcheckCertificateRevocation := DEF_checkCertificateRevocation;
  300. end;
  301. function TIdSSLIOHandlerSocketNET.LocalCertificateSelectionCallback(
  302. sender: TObject; targetHost: String;
  303. localCertificates: X509CertificateCollection;
  304. remoteCertificate: X509Certificate;
  305. acceptableIssuers: array of String): X509Certificate;
  306. var i : Integer;
  307. LIssuer : String;
  308. begin
  309. Result := nil;
  310. if Assigned(FOnLocalCertificateSelection) then
  311. begin
  312. FOnLocalCertificateSelection(Self,targetHost,localCertificates,remoteCertificate,Acceptableissuers,Result);
  313. end
  314. else
  315. begin
  316. if Assigned(acceptableIssuers) and
  317. (Length(acceptableIssuers)>0) and
  318. Assigned(localCertificates) and
  319. (localCertificates.Count > 0) then
  320. begin
  321. // Use the first certificate that is from an acceptable issuer.
  322. for I := 0 to LocalCertificates.Count -1 do
  323. begin
  324. LIssuer := LocalCertificates[i].Issuer;
  325. if (System.Array.IndexOf(acceptableIssuers, Lissuer)>-1) then
  326. begin
  327. Result := LocalCertificates[i];
  328. Exit;
  329. end;
  330. end;
  331. end;
  332. end;
  333. if (localCertificates <> nil) and
  334. (localCertificates.Count > 0) then
  335. begin
  336. Result := localCertificates[0];
  337. end;
  338. end;
  339. procedure TIdSSLIOHandlerSocketNET.OpenEncodedConnection;
  340. begin
  341. FSSL := System.Net.Security.SslStream.Create(
  342. System.Net.Sockets.NetworkStream.Create(FBinding.Handle,False),True,
  343. ValidatePeerCertificate,LocalCertificateSelectionCallback);
  344. if IsPeer then
  345. begin
  346. if Assigned(FServerCertificate) then
  347. begin
  348. FSSL.AuthenticateAsServer(FServerCertificate,FclientCertificateRequired,FenabledSslProtocols,FcheckCertificateRevocation);
  349. end
  350. else
  351. begin
  352. raise EIdSSLCertRequiredForSvr.Create(RSSSLNETCertificateRequired);
  353. end;
  354. end
  355. else
  356. begin
  357. if Assigned(FClientCertificates) then
  358. begin
  359. FSSL.AuthenticateAsClient(FHost,FClientCertificates,FenabledSslProtocols,True);
  360. if not FSSL.IsMutuallyAuthenticated then
  361. begin
  362. raise EIdSSLNotAuthenticated.Create(RSSSLNETNotAuthenticated);
  363. end;
  364. end
  365. else
  366. begin
  367. FSSL.AuthenticateAsClient(FHost,nil,FenabledSslProtocols,True);
  368. if not FSSL.IsAuthenticated then
  369. begin
  370. raise EIdSSLNotAuthenticated.Create(RSSSLNETNotAuthenticated);
  371. end;
  372. end;
  373. end;
  374. if Assigned(FOnSSLHandshakeDone) then
  375. begin
  376. FOnSSLHandshakeDone(Self);
  377. end;
  378. end;
  379. function TIdSSLIOHandlerSocketNET.RecvEnc(var VBuffer: TIdBytes): Integer;
  380. begin
  381. Result := FSSL.Read(VBuffer,0,Length(VBuffer));
  382. end;
  383. function TIdSSLIOHandlerSocketNET.SendEnc(const ABuffer: TIdBytes;
  384. const AOffset, ALength: Integer): Integer;
  385. begin
  386. FSSL.Write(ABuffer,AOffset,ALength);
  387. Result := ALength;
  388. end;
  389. procedure TIdSSLIOHandlerSocketNET.SetPassThrough(const Value: Boolean);
  390. begin
  391. if fPassThrough <> Value then begin
  392. if not Value then begin
  393. if BindingAllocated then begin
  394. OpenEncodedConnection;
  395. end;
  396. end
  397. else if FSSL <> nil then begin
  398. FSSL.Close;
  399. FreeAndNil(FSSL);
  400. end;
  401. fPassThrough := Value;
  402. end;
  403. end;
  404. procedure TIdSSLIOHandlerSocketNET.StartSSL;
  405. begin
  406. if not PassThrough then begin
  407. OpenEncodedConnection;
  408. end;
  409. end;
  410. function TIdSSLIOHandlerSocketNET.ValidatePeerCertificate(sender: TObject;
  411. certificate: X509Certificate; chain: X509Chain;
  412. sslPolicyErrors: SslPolicyErrors): Boolean;
  413. begin
  414. if Assigned(FOnValidatePeerCertificate) then
  415. begin
  416. FOnValidatePeerCertificate(sender,certificate,chain,sslPolicyErrors, Result);
  417. end
  418. else
  419. begin
  420. {
  421. This is a workaround for a quirk. If using this as a server, the validation routine
  422. may be called even though there may not be a client certificate and
  423. FclientCertificateRequired was set to false. It might be by design though.
  424. }
  425. case sslPolicyErrors of
  426. System.Net.Security.SslPolicyErrors.None : Result := True;
  427. System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable :
  428. begin
  429. if IsPeer and (not FclientCertificateRequired) then
  430. begin
  431. Result := True;
  432. end
  433. else
  434. begin
  435. Result := False;
  436. end;
  437. end;
  438. else
  439. Result := False;
  440. end;
  441. end;
  442. end;
  443. { TIdServerIOHandlerSSLNET }
  444. function TIdServerIOHandlerSSLNET.Accept(ASocket: TIdSocketHandle;
  445. AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler;
  446. var
  447. LIO : TIdSSLIOHandlerSocketNET;
  448. begin
  449. LIO := TIdSSLIOHandlerSocketNET.Create(nil);
  450. LIO.PassThrough := True;
  451. LIO.IsPeer := True;
  452. LIO.Open;
  453. if LIO.Binding.Accept(ASocket.Handle) then begin
  454. SetIOHandlerValues(LIO);
  455. end else begin
  456. FreeAndNil(LIO);
  457. end;
  458. Result := LIO;
  459. end;
  460. destructor TIdServerIOHandlerSSLNET.Destroy;
  461. begin
  462. inherited;
  463. end;
  464. procedure TIdServerIOHandlerSSLNET.Init;
  465. begin
  466. inherited;
  467. end;
  468. procedure TIdServerIOHandlerSSLNET.InitComponent;
  469. begin
  470. inherited InitComponent;
  471. FenabledSslProtocols := System.Security.Authentication.SslProtocols.Default;
  472. FclientCertificateRequired := DEF_clientCertificateRequired;
  473. FcheckCertificateRevocation := DEF_checkCertificateRevocation;
  474. end;
  475. function TIdServerIOHandlerSSLNET.MakeClientIOHandler: TIdSSLIOHandlerSocketBase;
  476. var
  477. LIO : TIdSSLIOHandlerSocketNET;
  478. begin
  479. LIO := TIdSSLIOHandlerSocketNET.Create(nil);
  480. LIO.PassThrough := True;
  481. LIO.IsPeer := False;
  482. SetIOHandlerValues(LIO);
  483. Result := LIO;
  484. end;
  485. function TIdServerIOHandlerSSLNET.MakeFTPSvrPasv: TIdSSLIOHandlerSocketBase;
  486. var
  487. LIO : TIdSSLIOHandlerSocketNET;
  488. begin
  489. LIO := TIdSSLIOHandlerSocketNET.Create(nil);
  490. LIO.PassThrough := True;
  491. LIO.IsPeer := True;
  492. SetIOHandlerValues(LIO);
  493. Result := LIO;
  494. end;
  495. function TIdServerIOHandlerSSLNET.MakeFTPSvrPort: TIdSSLIOHandlerSocketBase;
  496. var
  497. LIO : TIdSSLIOHandlerSocketNET;
  498. begin
  499. LIO := TIdSSLIOHandlerSocketNET.Create(nil);
  500. LIO.PassThrough := True;
  501. LIO.IsPeer := True;
  502. SetIOHandlerValues(LIO);
  503. Result := LIO;
  504. end;
  505. procedure TIdServerIOHandlerSSLNET.SetIOHandlerValues(
  506. AIO: TIdSSLIOHandlerSocketNET);
  507. begin
  508. AIO.FServerCertificate := FServerCertificate;
  509. AIO.FclientCertificateRequired := FclientCertificateRequired;
  510. // AIO.FClientCertificates := FClientCertificates;
  511. AIO.FcheckCertificateRevocation := FcheckCertificateRevocation;
  512. AIO.FOnSSLHandshakeDone := FOnSSLHandshakeDone;
  513. AIO.FenabledSslProtocols := FenabledSslProtocols;
  514. AIO.FOnLocalCertificateSelection := FOnLocalCertificateSelection;
  515. AIO.FOnValidatePeerCertificate := FOnValidatePeerCertificate;
  516. end;
  517. procedure TIdServerIOHandlerSSLNET.Shutdown;
  518. begin
  519. inherited;
  520. end;
  521. initialization
  522. {$I IdSymbolDeprecatedOff.inc}
  523. RegisterSSL('Indy SSL Support for Microsoft.NET 2.0','Indy Pit Crew', {do not localize}
  524. 'Copyright © 1993 - 2023'#10#13 + {do not localize}
  525. 'Chad Z. Hower (Kudzu) and the Indy Pit Crew. All rights reserved.', {do not localize}
  526. 'Open SSL Support DLL Delphi and C++Builder interface', {do not localize}
  527. 'http://www.indyproject.org/'#10#13 + {do not localize}
  528. 'Original Author - J. Peter Mugaas', {do not localize}
  529. TIdSSLIOHandlerSocketNET,
  530. TIdServerIOHandlerSSLNET);
  531. {$I IdSymbolDeprecatedOn.inc}
  532. TIdSSLIOHandlerSocketNET.RegisterIOHandler;
  533. end.