unit sslbase;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

Type
  TSSLType = (stAny,stSSLv2,stSSLv3,stTLSv1,stTLSv1_1,stTLSv1_2);

  { TSSLData }

  TSSLData = Class(TPersistent)
  private
    FFileName: String;
    FValue: TBytes;
  Public
    Function Empty : Boolean;
    Procedure Assign(Source : TPersistent);override;
    Property FileName : String Read FFileName Write FFileName;
    Property Value: TBytes Read FValue Write FValue;
  end;

Const
  SSLDataCount = 4; // 0 based.
  StrDataCount = 2; // 0 based.

Type
  { TSSLSocketHandler }

  { TCertificateData }

  TCertificateData = Class(TPersistent)
  Private
    FStrData : Array[0..StrDataCount] of string;
    FCertData : Array[0..SSLDataCount] of TSSLData;
    function GetSSLData(AIndex: Integer): TSSLData;
    procedure SetSSLData(AIndex: Integer; AValue: TSSLData);
    function GetString(AIndex: Integer): String;
    procedure SetString(AIndex: Integer; AValue: String);
  Public
    constructor Create;
    Destructor Destroy; override;
    Procedure Assign(Source : TPersistent); override;
    Function NeedCertificateData : Boolean;
  Published
    property KeyPassword: string Index 0 read GetString write SetString;
    property CipherList: string Index 1 read GetString write SetString;
    Property HostName : String Index 2 read GetString write SetString;
    property Certificate : TSSLData Index 0 Read GetSSLData Write SetSSLData;
    property TrustedCertificate : TSSLData Index 1 Read GetSSLData Write SetSSLData;
    property PrivateKey : TSSLData Index 2 Read GetSSLData Write SetSSLData;
    property PFX: TSSLData Index 3 Read GetSSLData Write SetSSLData;
    property CertCA: TSSLData Index 4 Read GetSSLData Write SetSSLData;
  end;

    { TX509Certificate }
  TCertAndKey = Record
    Certificate : TBytes;
    PrivateKey : TBytes;
  end;

  TX509Certificate = Class (TObject)
  private
    FCommonName: string;
    FCountry: String;
    FHostName: string;
    FKeySize: Integer;
    FOrganization: String;
    FSerial: Integer;
    FValidFrom: TDateTime;
    FValidTo: TDateTime;
    FVersion: Integer;
    function GetKeySize: Integer;
    function GetValidFrom: TDateTime;
    function GetValidTo: TDateTime;
    function GetVersion: Integer;
  Protected
    Function GetRealSerial : Integer;
  Public
    Function CreateCertificateAndKey : TCertAndKey; virtual; abstract;
    Procedure CreateCertificateAndKey(Var aCertificate,aKey : TBytes);
    Property Country : String Read FCountry Write FCountry;
    Property HostName : string Read FHostName Write FHostName;
    Property CommonName : string Read FCommonName Write FCommonName;
    Property Organization : String Read FOrganization Write FOrganization;
    Property KeySize : Integer Read GetKeySize Write FKeySize;
    // Valid from. Default today -1;
    Property ValidFrom : TDateTime Read GetValidFrom Write FValidFrom;
    // Valid To. Default today + 31;
    Property ValidTo : TDateTime Read GetValidTo Write FValidTo;
    // Version Default 1.
    Property Version : Integer Read GetVersion Write FVersion;
    // Serial. If zero, then a serial is generated.
    Property Serial : Integer Read FSerial Write FSerial;

  end;

implementation

{ TSSLData }

Function TSSLData.Empty: Boolean;
begin
  Result:=(Length(Value)=0) and (FileName='');
end;

Procedure TSSLData.Assign(Source: TPersistent);

begin
  if Source is TSSLData then
   With TSSLData(Source) do
    begin
    Self.FValue:=FValue;
    Self.FFileName:=FFileName;
    end
  else
    inherited Assign(Source);
end;

{ TCertificateData }

function TCertificateData.GetSSLData(AIndex: Integer): TSSLData;
begin
  Result:=FCertData[AIndex];
end;

procedure TCertificateData.SetSSLData(AIndex: Integer; AValue: TSSLData);
begin
  FCertData[AIndex].Assign(AValue);
end;

function TCertificateData.GetString(AIndex: Integer): String;
begin
  Result:=FStrData[AIndex];
  if (AIndex=2) and (result='') then
    Result:='localhost';
end;

procedure TCertificateData.SetString(AIndex: Integer; AValue: String);
begin
  FStrData[AIndex]:=aValue;
end;

constructor TCertificateData.Create;

Var
  I : Integer;

begin
  CipherList:='DEFAULT';
  HostName:='localhost';
  For I:=0 to SSLDataCount do
    FCertData[i]:=TSSLData.Create;
end;

destructor TCertificateData.Destroy;

Var
  I : Integer;

begin
  For I:=0 to SSLDataCount do
    FreeAndNil(FCertData[i]);
  inherited Destroy;
end;

procedure TCertificateData.Assign(Source: TPersistent);

Var
  CD : TCertificateData;
  I : Integer;

begin
  if Source is TCertificateData then
    begin
    CD:=Source as TCertificateData;
    For I:=0 to StrDataCount do
      FStrData[i]:=CD.FStrData[i];
    For I:=0 to SSLDataCount do
      FCertData[i].Assign(CD.FCertData[i])
    end
  else
    inherited Assign(Source);
end;

function TCertificateData.NeedCertificateData: Boolean;
begin
  Result:=Certificate.Empty and PFX.Empty;
end;

function TX509Certificate.GetKeySize: Integer;
begin
  Result:=FKeySize;
  if Result=0 then
    Result:=1024;
end;

function TX509Certificate.GetValidFrom: TDateTime;
begin
  Result:=FValidFrom;
  If Result=0 then
    Result:=Date-1;
end;

function TX509Certificate.GetValidTo: TDateTime;
begin
  Result:=FValidTo;
  If Result=0 then
    Result:=Date+31;
end;


function TX509Certificate.GetVersion: Integer;
begin
  Result:=FVersion;
  if FVersion=0 then
    FVersion:=1;
end;

function TX509Certificate.GetRealSerial: Integer;
begin
  Result:=FSerial;
  if Result=0 then
    Result:=10; // MinutesBetween(Now,EncodeDate(2019,1,1));
end;

procedure TX509Certificate.CreateCertificateAndKey(var aCertificate, aKey: TBytes);

Var
  CK : TCertAndKey;

begin
  CK:=CreateCertificateAndKey;
  aCertificate:=CK.Certificate;
  aKey:=CK.PrivateKey;
end;

end.

