restemplate/indy/Protocols/IdSSLDotNET.pas

588 lines
18 KiB
Plaintext
Raw Permalink Blame History

unit IdSSLDotNET;
interface
{$i IdCompilerDefines.inc}
{*******************************************************}
{ }
{ Indy SSL Support for Microsoft.NET 2.0 }
{ }
{ Copyright (C) 2007 Indy Pit Crew }
{ Original author J. Peter Mugaas }
{ 2007-Aug-22 }
{ }
{*******************************************************}
uses
Classes,
IdException,
IdGlobal,
IdIOHandler,
IdSocketHandle,
IdSSL,
IdThread,
IdYarn,
System.Collections,
System.IO,
System.Net.Sockets,
System.Net.Security,
System.Security.Authentication,
System.Security.Cryptography.X509Certificates;
const
DEF_clientCertificateRequired = False;
DEF_checkCertificateRevocation = True;
type
TOnValidatePeerCertificate = procedure (ASender : TObject;
ACertificate : X509Certificate; AChain : X509Chain;
AsslPolicyErrors : SslPolicyErrors; var VValid : Boolean) of object;
TOnLocalCertificateSelectionCallback = procedure (ASender : TObject;
AtargetHost : String;
AlocalCertificates : X509CertificateCollection;
AremoteCertificate : X509Certificate;
AacceptableIssuers : array of String;
VCert : X509Certificate) of object;
TIdSSLIOHandlerSocketNET = class(TIdSSLIOHandlerSocketBase)
protected
FenabledSslProtocols: System.Security.Authentication.SslProtocols;
FOnValidatePeerCertificate : TOnValidatePeerCertificate;
FOnLocalCertificateSelection : TOnLocalCertificateSelectionCallback;
FSSL : SslStream;
FServerCertificate : X509Certificate;
FClientCertificates : X509CertificateCollection;
FOnSSLHandshakeDone : TNotifyEvent;
FclientCertificateRequired : Boolean;
FcheckCertificateRevocation : Boolean;
procedure OpenEncodedConnection; virtual;
//Ssl certificate validation callback
function ValidatePeerCertificate(
sender : System.&Object;
certificate : X509Certificate;
chain : X509Chain;
sslPolicyErrors : SslPolicyErrors) : Boolean;
function LocalCertificateSelectionCallback (
sender : System.&Object;
targetHost : String;
localCertificates : X509CertificateCollection;
remoteCertificate : X509Certificate;
acceptableIssuers : array of String) : X509Certificate;
function RecvEnc(var VBuffer: TIdBytes): Integer; override;
function SendEnc(const ABuffer: TIdBytes; const AOffset, ALength: Integer): Integer; override;
procedure SetPassThrough(const Value: Boolean); override;
procedure InitComponent; override;
procedure ConnectClient; override;
//
function GetCipherAlgorithm: CipherAlgorithmType;
function GetCipherStrength: Integer;
function GetHashAlgorithm: HashAlgorithmType;
function GetHashStrength: Integer;
function GetIsEncrypted: Boolean;
function GetIsSigned: Boolean;
function GetKeyExchangeAlgorithm: ExchangeAlgorithmType;
function GetKeyExchangeStrength: Integer;
function GetRemoteCertificate: X509Certificate;
function GetSslProtocol: SslProtocols;
public
procedure Close; override;
procedure StartSSL; override;
function Clone : TIdSSLIOHandlerSocketBase; override;
property CipherAlgorithm : CipherAlgorithmType read GetCipherAlgorithm;
property CipherStrength : Integer read GetCipherStrength;
property HashAlgorithm : HashAlgorithmType read GetHashAlgorithm;
property HashStrength : Integer read GetHashStrength;
property IsEncrypted : Boolean read GetIsEncrypted;
property IsSigned : Boolean read GetIsSigned;
property KeyExchangeAlgorithm : ExchangeAlgorithmType read GetKeyExchangeAlgorithm;
property KeyExchangeStrength : Integer read GetKeyExchangeStrength;
property RemoteCertificate : X509Certificate read GetRemoteCertificate;
property SslProtocol : SslProtocols read GetSslProtocol;
published
property enabledSslProtocols : System.Security.Authentication.SslProtocols read FenabledSslProtocols write FenabledSslProtocols;
property ServerCertificate : X509Certificate read FServerCertificate write FServerCertificate;
property ClientCertificates : X509CertificateCollection read FClientCertificates write FClientCertificates;
property clientCertificateRequired : Boolean read FclientCertificateRequired write FclientCertificateRequired;
property checkCertificateRevocation : Boolean read FcheckCertificateRevocation write FcheckCertificateRevocation;
property OnSSLHandshakeDone : TNotifyEvent read FOnSSLHandshakeDone write FOnSSLHandshakeDone;
property OnLocalCertificateSelection : TOnLocalCertificateSelectionCallback
read FOnLocalCertificateSelection write FOnLocalCertificateSelection;
property OnValidatePeerCertificate : TOnValidatePeerCertificate
read FOnValidatePeerCertificate write FOnValidatePeerCertificate;
end;
TIdServerIOHandlerSSLNET = class(TIdServerIOHandlerSSLBase)
protected
FOnValidatePeerCertificate : TOnValidatePeerCertificate;
FOnLocalCertificateSelection : TOnLocalCertificateSelectionCallback;
FOnSSLHandshakeDone : TNotifyEvent;
FenabledSslProtocols : System.Security.Authentication.SslProtocols;
FServerCertificate : X509Certificate;
FclientCertificateRequired : Boolean;
FcheckCertificateRevocation : Boolean;
procedure InitComponent; override;
procedure SetIOHandlerValues(AIO : TIdSSLIOHandlerSocketNET);
published
public
destructor Destroy; override;
procedure Init; override;
procedure Shutdown; override;
function MakeClientIOHandler : TIdSSLIOHandlerSocketBase; override;
//
function MakeFTPSvrPort : TIdSSLIOHandlerSocketBase; override;
function MakeFTPSvrPasv : TIdSSLIOHandlerSocketBase; override;
function Accept(ASocket: TIdSocketHandle; AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler; override;
published
property enabledSslProtocols : System.Security.Authentication.SslProtocols read FenabledSslProtocols write FenabledSslProtocols;
property ServerCertificate : X509Certificate read FServerCertificate write FServerCertificate;
property clientCertificateRequired : Boolean read FclientCertificateRequired write FclientCertificateRequired;
property checkCertificateRevocation : Boolean read FcheckCertificateRevocation write FcheckCertificateRevocation;
property OnSSLHandshakeDone : TNotifyEvent read FOnSSLHandshakeDone write FOnSSLHandshakeDone;
property OnLocalCertificateSelection : TOnLocalCertificateSelectionCallback
read FOnLocalCertificateSelection write FOnLocalCertificateSelection;
property OnValidatePeerCertificate : TOnValidatePeerCertificate
read FOnValidatePeerCertificate write FOnValidatePeerCertificate;
end;
EIdSSLNetException = class(EIdException);
EIdSSLCertRequiredForSvr = class(EIdSSLNetException);
EIdSSLNotAuthenticated = class(EIdSSLNetException);
implementation
uses
IdResourceStringsSSLDotNet,
IdStack,
SysUtils;
{ TIdSSLIOHandlerSocketNET }
function TIdSSLIOHandlerSocketNET.Clone: TIdSSLIOHandlerSocketBase;
begin
Result := TIdSSLIOHandlerSocketNET.Create(nil);
TIdSSLIOHandlerSocketNET(Result).FenabledSslProtocols := FenabledSslProtocols;
TIdSSLIOHandlerSocketNET(Result).FOnValidatePeerCertificate := FOnValidatePeerCertificate;
TIdSSLIOHandlerSocketNET(Result).FOnLocalCertificateSelection := FOnLocalCertificateSelection;
TIdSSLIOHandlerSocketNET(Result).FServerCertificate := FServerCertificate;
TIdSSLIOHandlerSocketNET(Result).FClientCertificates := FClientCertificates;
TIdSSLIOHandlerSocketNET(Result).FOnSSLHandshakeDone := FOnSSLHandshakeDone;
end;
procedure TIdSSLIOHandlerSocketNET.Close;
begin
if Assigned(FSSL) then
begin
FSSL.Close;
FreeAndNil(FSSL);
end;
inherited;
end;
procedure TIdSSLIOHandlerSocketNET.ConnectClient;
var
LPassThrough: Boolean;
begin
// RLebeau 1/11/07: In case a proxy is being used, pass through
// any data from the base class unencrypted when setting up that
// connection. We should do this anyway since SSL hasn't been
// initialized yet!
LPassThrough := fPassThrough;
fPassThrough := True;
try
inherited ConnectClient;
finally
fPassThrough := LPassThrough;
end;
StartSSL;
end;
function TIdSSLIOHandlerSocketNET.GetCipherAlgorithm: CipherAlgorithmType;
begin
if Assigned(FSSL) then
begin
Result := FSSL.CipherAlgorithm;
end
else
begin
Result := System.Security.Authentication.CipherAlgorithmType.None;
end;
end;
function TIdSSLIOHandlerSocketNET.GetCipherStrength: Integer;
begin
if Assigned(FSSL) then
begin
Result := FSSL.CipherStrength;
end
else
begin
Result := 0;
end;
end;
function TIdSSLIOHandlerSocketNET.GetHashAlgorithm: HashAlgorithmType;
begin
if Assigned(FSSL) then
begin
Result := FSSL.HashAlgorithm;
end
else
begin
Result := HashAlgorithmType.None;
end;
end;
function TIdSSLIOHandlerSocketNET.GetHashStrength: Integer;
begin
if Assigned(FSSL) then
begin
Result := FSSL.HashStrength;
end
else
begin
Result := 0;
end;
end;
function TIdSSLIOHandlerSocketNET.GetIsEncrypted: Boolean;
begin
if Assigned(FSSL) then
begin
Result := FSSL.IsEncrypted;
end
else
begin
Result := False;
end;
end;
function TIdSSLIOHandlerSocketNET.GetIsSigned: Boolean;
begin
if Assigned(FSSL) then
begin
Result := FSSL.IsSigned;
end
else
begin
Result := False;
end;
end;
function TIdSSLIOHandlerSocketNET.GetKeyExchangeAlgorithm: ExchangeAlgorithmType;
begin
if Assigned(FSSL) then
begin
Result := FSSL.KeyExchangeAlgorithm;
end
else
begin
Result := ExchangeAlgorithmType.None;
end;
end;
function TIdSSLIOHandlerSocketNET.GetKeyExchangeStrength: Integer;
begin
if Assigned(FSSL) then
begin
Result := FSSL.KeyExchangeStrength;
end
else
begin
Result := 0;
end;
end;
function TIdSSLIOHandlerSocketNET.GetRemoteCertificate: X509Certificate;
begin
if Assigned(FSSL) then
begin
Result := FSSL.RemoteCertificate;
end
else
begin
Result := nil;
end;
end;
function TIdSSLIOHandlerSocketNET.GetSslProtocol: SslProtocols;
begin
if Assigned(FSSL) then
begin
Result := FSSL.SslProtocol;
end
else
begin
Result := SslProtocols.None;
end;
end;
procedure TIdSSLIOHandlerSocketNET.InitComponent;
begin
inherited;
FenabledSslProtocols := System.Security.Authentication.SslProtocols.Default;
FclientCertificateRequired := DEF_clientCertificateRequired;
FcheckCertificateRevocation := DEF_checkCertificateRevocation;
end;
function TIdSSLIOHandlerSocketNET.LocalCertificateSelectionCallback(
sender: TObject; targetHost: String;
localCertificates: X509CertificateCollection;
remoteCertificate: X509Certificate;
acceptableIssuers: array of String): X509Certificate;
var i : Integer;
LIssuer : String;
begin
Result := nil;
if Assigned(FOnLocalCertificateSelection) then
begin
FOnLocalCertificateSelection(Self,targetHost,localCertificates,remoteCertificate,Acceptableissuers,Result);
end
else
begin
if Assigned(acceptableIssuers) and
(Length(acceptableIssuers)>0) and
Assigned(localCertificates) and
(localCertificates.Count > 0) then
begin
// Use the first certificate that is from an acceptable issuer.
for I := 0 to LocalCertificates.Count -1 do
begin
LIssuer := LocalCertificates[i].Issuer;
if (System.Array.IndexOf(acceptableIssuers, Lissuer)>-1) then
begin
Result := LocalCertificates[i];
Exit;
end;
end;
end;
end;
if (localCertificates <> nil) and
(localCertificates.Count > 0) then
begin
Result := localCertificates[0];
end;
end;
procedure TIdSSLIOHandlerSocketNET.OpenEncodedConnection;
begin
FSSL := System.Net.Security.SslStream.Create(
System.Net.Sockets.NetworkStream.Create(FBinding.Handle,False),False,
ValidatePeerCertificate,LocalCertificateSelectionCallback);
if IsPeer then
begin
if Assigned(FServerCertificate) then
begin
FSSL.AuthenticateAsServer(FServerCertificate,FclientCertificateRequired,FenabledSslProtocols,FcheckCertificateRevocation);
end
else
begin
raise EIdSSLCertRequiredForSvr.Create(RSSSLNETCertificateRequired);
end;
end
else
begin
if Assigned(FClientCertificates) then
begin
FSSL.AuthenticateAsClient(FHost,FClientCertificates,FenabledSslProtocols,True);
if not FSSL.IsMutuallyAuthenticated then
begin
raise EIdSSLNotAuthenticated.Create(RSSSLNETNotAuthenticated);
end;
end
else
begin
FSSL.AuthenticateAsClient(FHost,nil,FenabledSslProtocols,True);
if not FSSL.IsAuthenticated then
begin
raise EIdSSLNotAuthenticated.Create(RSSSLNETNotAuthenticated);
end;
end;
end;
if Assigned(FOnSSLHandshakeDone) then
begin
FOnSSLHandshakeDone(Self);
end;
end;
function TIdSSLIOHandlerSocketNET.RecvEnc(var VBuffer: TIdBytes): Integer;
begin
Result := FSSL.Read(VBuffer,0,Length(VBuffer));
end;
function TIdSSLIOHandlerSocketNET.SendEnc(const ABuffer: TIdBytes;
const AOffset, ALength: Integer): Integer;
begin
FSSL.Write(ABuffer,AOffset,ALength);
Result := ALength;
end;
procedure TIdSSLIOHandlerSocketNET.SetPassThrough(const Value: Boolean);
begin
if fPassThrough <> Value then begin
if not Value then begin
if BindingAllocated then begin
OpenEncodedConnection;
end;
end;
fPassThrough := Value;
end;
end;
procedure TIdSSLIOHandlerSocketNET.StartSSL;
begin
if not PassThrough then begin
OpenEncodedConnection;
end;
end;
function TIdSSLIOHandlerSocketNET.ValidatePeerCertificate(sender: TObject;
certificate: X509Certificate; chain: X509Chain;
sslPolicyErrors: SslPolicyErrors): Boolean;
begin
if Assigned(FOnValidatePeerCertificate) then
begin
FOnValidatePeerCertificate(sender,certificate,chain,sslPolicyErrors, Result);
end
else
begin
{
This is a workaround for a quirk. If using this as a server, the validation routine
may be called even though there may not be a client certificate and
FclientCertificateRequired was set to false. It might be by design though.
}
case sslPolicyErrors of
System.Net.Security.SslPolicyErrors.None : Result := True;
System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable :
begin
if IsPeer and (not FclientCertificateRequired) then
begin
Result := True;
end
else
begin
Result := False;
end;
end;
else
Result := False;
end;
end;
end;
{ TIdServerIOHandlerSSLNET }
function TIdServerIOHandlerSSLNET.Accept(ASocket: TIdSocketHandle;
AListenerThread: TIdThread; AYarn: TIdYarn): TIdIOHandler;
var
LIO : TIdSSLIOHandlerSocketNET;
begin
LIO := TIdSSLIOHandlerSocketNET.Create(nil);
LIO.PassThrough := True;
LIO.IsPeer := True;
LIO.Open;
if LIO.Binding.Accept(ASocket.Handle) then
begin
SetIOHandlerValues(LIO);
Result := LIO;
end
else
begin
Result := nil;
FreeAndNil(LIO);
end;
end;
destructor TIdServerIOHandlerSSLNET.Destroy;
begin
inherited;
end;
procedure TIdServerIOHandlerSSLNET.Init;
begin
inherited;
end;
procedure TIdServerIOHandlerSSLNET.InitComponent;
begin
inherited InitComponent;
FenabledSslProtocols := System.Security.Authentication.SslProtocols.Default;
FclientCertificateRequired := DEF_clientCertificateRequired;
FcheckCertificateRevocation := DEF_checkCertificateRevocation;
end;
function TIdServerIOHandlerSSLNET.MakeClientIOHandler: TIdSSLIOHandlerSocketBase;
var
LIO : TIdSSLIOHandlerSocketNET;
begin
LIO := TIdSSLIOHandlerSocketNET.Create(nil);
LIO.PassThrough := True;
LIO.IsPeer := False;
SetIOHandlerValues(LIO);
Result := LIO;
end;
function TIdServerIOHandlerSSLNET.MakeFTPSvrPasv: TIdSSLIOHandlerSocketBase;
var
LIO : TIdSSLIOHandlerSocketNET;
begin
LIO := TIdSSLIOHandlerSocketNET.Create(nil);
LIO.PassThrough := True;
LIO.IsPeer := True;
SetIOHandlerValues(LIO);
Result := LIO;
end;
function TIdServerIOHandlerSSLNET.MakeFTPSvrPort: TIdSSLIOHandlerSocketBase;
var
LIO : TIdSSLIOHandlerSocketNET;
begin
LIO := TIdSSLIOHandlerSocketNET.Create(nil);
LIO.PassThrough := True;
LIO.IsPeer := True;
SetIOHandlerValues(LIO);
Result := LIO;
end;
procedure TIdServerIOHandlerSSLNET.SetIOHandlerValues(
AIO: TIdSSLIOHandlerSocketNET);
begin
AIO.FServerCertificate := FServerCertificate;
AIO.FclientCertificateRequired := FclientCertificateRequired;
// AIO.FClientCertificates := FClientCertificates;
AIO.FcheckCertificateRevocation := FcheckCertificateRevocation;
AIO.FOnSSLHandshakeDone := FOnSSLHandshakeDone;
AIO.FenabledSslProtocols := FenabledSslProtocols;
AIO.FOnLocalCertificateSelection := FOnLocalCertificateSelection;
AIO.FOnValidatePeerCertificate := FOnValidatePeerCertificate;
end;
procedure TIdServerIOHandlerSSLNET.Shutdown;
begin
inherited;
end;
initialization
RegisterSSL('Indy SSL Support for Microsoft.NET 2.0','Indy Pit Crew', {do not localize}
'Copyright <20> 1993 - 2007'#10#13 + {do not localize}
'Chad Z. Hower (Kudzu) and the Indy Pit Crew. All rights reserved.', {do not localize}
'Open SSL Support DLL Delphi and C++Builder interface', {do not localize}
'http://www.indyproject.org/'#10#13 + {do not localize}
'Original Author - J. Peter Mugaas', {do not localize}
TIdSSLIOHandlerSocketNET,
TIdServerIOHandlerSSLNET);
TIdSSLIOHandlerSocketNET.RegisterIOHandler;
end.