Explorar o código

add additional x509 classes

Ugochukwu Mmaduekwe hai 1 semana
pai
achega
061b763537

+ 1 - 0
CryptoLib/src/GeneralUtilities/ClpCryptoLibTypes.pas

@@ -76,6 +76,7 @@ type
   EBadBlockCryptoLibException = class(ECryptoLibException);
   EPemGenerationCryptoLibException = class(ECryptoLibException);
   ECertificateCryptoLibException = class(ECryptoLibException);
+  ECrlCryptoLibException = class(ECryptoLibException);
 
   /// <summary>
   /// Represents a dynamic array of Byte.

+ 4 - 0
CryptoLib/src/Interfaces/Asn1/X509/ClpIX509Asn1Objects.pas

@@ -167,6 +167,8 @@ type
     function GetCount: Int32;
     function GetNames: TCryptoLibGenericArray<IGeneralName>;
 
+    function ToString: String;
+
     property Count: Int32 read GetCount;
   end;
 
@@ -481,6 +483,8 @@ type
   ICrlDistPoint = interface(IAsn1Encodable)
     ['{B8C9D0E1-F2A3-4567-1234-567890123456}']
     function GetDistributionPoints: TCryptoLibGenericArray<IDistributionPoint>;
+
+    function ToString: String;
   end;
 
   /// <summary>

+ 78 - 0
CryptoLib/src/Interfaces/X509/ClpIX509Crl.pas

@@ -0,0 +1,78 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIX509Crl;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIX509CrlEntry,
+  ClpIX509Certificate,
+  ClpIAsymmetricKeyParameter,
+  ClpIVerifierFactoryProvider,
+  ClpNullable,
+  ClpCryptoLibTypes,
+  ClpBigInteger;
+
+type
+  IX509Crl = interface(IInterface)
+    ['{D3E4F5A6-B7C8-9012-DEF0-345678901234}']
+
+    function GetCertificateList: ICertificateList;
+    function GetVersion: Int32;
+    function GetIssuerDN: IX509Name;
+    function GetThisUpdate: TDateTime;
+    function GetNextUpdate: TNullable<TDateTime>;
+    function GetRevokedCertificate(const ASerialNumber: TBigInteger): IX509CrlEntry;
+    function GetRevokedCertificates: TCryptoLibGenericArray<IX509CrlEntry>;
+    function GetTbsCertList: TCryptoLibByteArray;
+    function GetSignature: TCryptoLibByteArray;
+    function GetSigAlgName: String;
+    function GetSigAlgOid: String;
+    function GetSigAlgParams: TCryptoLibByteArray;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetEncoded: TCryptoLibByteArray;
+
+    function IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+    function IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+    procedure Verify(const AKey: IAsymmetricKeyParameter); overload;
+    procedure Verify(const AVerifierProvider: IVerifierFactoryProvider); overload;
+    procedure VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+
+    function IsRevoked(const ACert: IX509Certificate): Boolean;
+    function Equals(const AOther: TObject): Boolean;
+    function GetHashCode: Int32;
+    function ToString: String;
+
+    property CertificateList: ICertificateList read GetCertificateList;
+    property Version: Int32 read GetVersion;
+    property IssuerDN: IX509Name read GetIssuerDN;
+    property ThisUpdate: TDateTime read GetThisUpdate;
+    property NextUpdate: TNullable<TDateTime> read GetNextUpdate;
+    property SigAlgName: String read GetSigAlgName;
+    property SigAlgOid: String read GetSigAlgOid;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+  end;
+
+implementation
+
+end.

+ 55 - 0
CryptoLib/src/Interfaces/X509/ClpIX509CrlEntry.pas

@@ -0,0 +1,55 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIX509CrlEntry;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpCryptoLibTypes,
+  ClpBigInteger;
+
+type
+  /// <summary>
+  /// Interface for X.509 CRL entry (revoked certificate entry).
+  /// </summary>
+  IX509CrlEntry = interface(IInterface)
+    ['{C2D3E4F5-A6B7-8901-CDEF-234567890123}']
+
+    function GetCrlEntry: ICrlEntry;
+    function GetCertificateIssuer: IX509Name;
+    function GetEncoded: TCryptoLibByteArray;
+    function GetSerialNumber: TBigInteger;
+    function GetRevocationDate: TDateTime;
+    function GetHasExtensions: Boolean;
+    function Equals(const AOther: TObject): Boolean;
+    function GetHashCode: Int32;
+    function ToString: String;
+
+    property CrlEntry: ICrlEntry read GetCrlEntry;
+    property SerialNumber: TBigInteger read GetSerialNumber;
+    property RevocationDate: TDateTime read GetRevocationDate;
+    property HasExtensions: Boolean read GetHasExtensions;
+  end;
+
+implementation
+
+end.

+ 45 - 0
CryptoLib/src/Interfaces/X509/ClpIX509CrlParser.pas

@@ -0,0 +1,45 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIX509CrlParser;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpCryptoLibTypes,
+  ClpIX509Crl;
+
+type
+  /// <summary>
+  /// Interface for X.509 CRL parser.
+  /// </summary>
+  IX509CrlParser = interface(IInterface)
+    ['{E4F5A6B7-C8D9-0123-EF01-456789012345}']
+
+    function ReadCrl(const AInput: TCryptoLibByteArray): IX509Crl; overload;
+    function ReadCrls(const AInput: TCryptoLibByteArray): TCryptoLibGenericArray<IX509Crl>; overload;
+    function ReadCrl(const AInStream: TStream): IX509Crl; overload;
+    function ReadCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>; overload;
+    function ParseCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>;
+  end;
+
+implementation
+
+end.

+ 187 - 0
CryptoLib/src/Misc/ClpNullable.pas

@@ -0,0 +1,187 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpNullable;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Defaults,
+  TypInfo;
+
+type
+  /// <summary>
+  /// Generic nullable wrapper restricted at runtime to value types (non-class).
+  /// Accepted kinds: Integer/Int64, Float, Enumeration, Set, Char/WChar, Record.
+  /// Rejected kinds: Class, Interface, String types, Dynamic array, Variant, Method, etc.
+  /// </summary>
+  TNullable<T> = record
+  private
+    FHasValue: Boolean;
+    FValue: T;
+
+    class constructor Create; // runs once per closed generic (e.g., TNullable<Int64>)
+    class procedure AssertSupported; static;
+
+  public
+    class function Some(const V: T): TNullable<T>; static;
+    class function None: TNullable<T>; static;
+
+    function HasValue: Boolean; inline;
+    function Value: T;
+    function TryGetValue(out V: T): Boolean; inline;
+    function ValueOrDefault(const DefaultValue: T): T; inline;
+
+    procedure Clear; inline;
+
+    class operator Implicit(const V: T): TNullable<T>;
+    class operator Explicit(const N: TNullable<T>): T;
+
+    class operator Equal(const A, B: TNullable<T>): Boolean;
+    class operator NotEqual(const A, B: TNullable<T>): Boolean;
+
+    class operator Equal(const A: TNullable<T>; const B: T): Boolean;
+    class operator NotEqual(const A: TNullable<T>; const B: T): Boolean;
+    class operator Equal(const A: T; const B: TNullable<T>): Boolean;
+    class operator NotEqual(const A: T; const B: TNullable<T>): Boolean;
+  end;
+
+implementation
+
+class procedure TNullable<T>.AssertSupported;
+var
+  K: TTypeKind;
+begin
+  K := PTypeInfo(TypeInfo(T)).Kind;
+
+  case K of
+    tkInteger, tkInt64, tkEnumeration, tkFloat, tkSet, tkChar, tkWChar, tkRecord:
+      Exit; // OK
+  else
+    raise EInvalidOp.CreateFmt(
+      'TNullable<%s> only supports value types (got %s). ' +
+      'Disallowed: class/interface/string/dyn array/variant/etc.',
+      [GetTypeName(TypeInfo(T)), GetEnumName(TypeInfo(TTypeKind), Ord(K))]
+    );
+  end;
+end;
+
+class constructor TNullable<T>.Create;
+begin
+  AssertSupported; // fires once per T
+end;
+
+class function TNullable<T>.Some(const V: T): TNullable<T>;
+begin
+  Result.FHasValue := True;
+  Result.FValue := V;
+end;
+
+class function TNullable<T>.None: TNullable<T>;
+begin
+  Result.FHasValue := False;
+  Result.FValue := Default(T);
+end;
+
+function TNullable<T>.HasValue: Boolean;
+begin
+  Result := FHasValue;
+end;
+
+function TNullable<T>.Value: T;
+begin
+  if not FHasValue then
+    raise EInvalidOp.Create('TNullable: value is null');
+  Result := FValue;
+end;
+
+function TNullable<T>.TryGetValue(out V: T): Boolean;
+begin
+  Result := FHasValue;
+  if Result then
+    V := FValue
+  else
+    V := Default(T);
+end;
+
+function TNullable<T>.ValueOrDefault(const DefaultValue: T): T;
+begin
+  if FHasValue then
+    Result := FValue
+  else
+    Result := DefaultValue;
+end;
+
+procedure TNullable<T>.Clear;
+begin
+  FHasValue := False;
+  FValue := Default(T);
+end;
+
+class operator TNullable<T>.Implicit(const V: T): TNullable<T>;
+begin
+  Result := Some(V);
+end;
+
+class operator TNullable<T>.Explicit(const N: TNullable<T>): T;
+begin
+  Result := N.Value; // will raise if FHasValue = false
+end;
+
+class operator TNullable<T>.Equal(const A, B: TNullable<T>): Boolean;
+var
+  Cmp: IEqualityComparer<T>;
+begin
+  if A.FHasValue <> B.FHasValue then
+    Exit(False);
+  if not A.FHasValue then
+    Exit(True); // both null
+  Cmp := TEqualityComparer<T>.Default;
+  Result := Cmp.Equals(A.FValue, B.FValue);
+end;
+
+class operator TNullable<T>.NotEqual(const A, B: TNullable<T>): Boolean;
+begin
+  Result := not (A = B);
+end;
+
+class operator TNullable<T>.Equal(const A: TNullable<T>; const B: T): Boolean;
+begin
+  Result := A.FHasValue and TEqualityComparer<T>.Default.Equals(A.FValue, B);
+end;
+
+class operator TNullable<T>.NotEqual(const A: TNullable<T>; const B: T): Boolean;
+begin
+  Result := not (A = B);
+end;
+
+class operator TNullable<T>.Equal(const A: T; const B: TNullable<T>): Boolean;
+begin
+  Result := B = A;
+end;
+
+class operator TNullable<T>.NotEqual(const A: T; const B: TNullable<T>): Boolean;
+begin
+  Result := not (A = B);
+end;
+
+end.
+
+

+ 5 - 5
CryptoLib/src/X509/ClpX509CertificateParser.pas

@@ -60,8 +60,8 @@ type
     FSDataObjectCount: Int32;
     FCurrentStream: TStream;
 
-    class constructor Boot();
-    class procedure InitializePemCertParser();
+    class constructor Create();
+    class procedure Boot();
 
     function ReadDerCertificate(const ADIn: TAsn1InputStream): IX509Certificate;
     function ReadPemCertificate(const AInStream: TStream): IX509Certificate;
@@ -82,12 +82,12 @@ implementation
 
 { TX509CertificateParser }
 
-class constructor TX509CertificateParser.Boot();
+class constructor TX509CertificateParser.Create();
 begin
-  InitializePemCertParser();
+  Boot();
 end;
 
-class procedure TX509CertificateParser.InitializePemCertParser();
+class procedure TX509CertificateParser.Boot();
 begin
   FPemCertParser := TPemParser.Create('CERTIFICATE');
 end;

+ 628 - 0
CryptoLib/src/X509/ClpX509Crl.pas

@@ -0,0 +1,628 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpX509Crl;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Math,
+  Generics.Collections,
+  ClpIAsn1Core,
+  ClpIAsn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpIVerifierFactory,
+  ClpIVerifierFactoryProvider,
+  ClpIX509Crl,
+  ClpIX509CrlEntry,
+  ClpIX509Certificate,
+  ClpIX509Extension,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpX509CrlEntry,
+  ClpX509ExtensionBase,
+  ClpX509ExtensionUtilities,
+  ClpX509SignatureUtilities,
+  ClpX509Utilities,
+  ClpAsn1VerifierFactory,
+  ClpAsn1VerifierFactoryProvider,
+  ClpAsn1Dumper,
+  ClpAsn1Core,
+  ClpAsn1Objects,
+  ClpBigInteger,
+  ClpNullable,
+  ClpEncoders,
+  ClpArrayUtilities,
+  ClpCryptoLibTypes;
+
+type
+  TX509Crl = class(TX509ExtensionBase, IX509Crl)
+  strict private
+  type
+    ICachedEncoding = interface(IInterface)
+      ['{A2B3C4D5-E6F7-8901-BCDE-F23456789012}']
+      function GetEncoding: TCryptoLibByteArray;
+      function GetEncoded: TCryptoLibByteArray;
+    end;
+
+    TCachedEncoding = class(TInterfacedObject, ICachedEncoding)
+    strict private
+      var
+        FEncoding: TCryptoLibByteArray;
+        FException: Exception;
+    public
+      constructor Create(const AEncoding: TCryptoLibByteArray; const AException: Exception);
+      function GetEncoding: TCryptoLibByteArray;
+      function GetEncoded: TCryptoLibByteArray;
+    end;
+
+  strict private
+    var
+      FCertificateList: ICertificateList;
+      FSigAlgParams: TCryptoLibByteArray;
+      FIsIndirect: Boolean;
+      FSigAlgName: String;
+      FCachedEncoding: ICachedEncoding;
+      FHashValueSet: Boolean;
+      FHashValue: Int32;
+
+    function GetCachedEncoding: ICachedEncoding;
+    function CreateCachedEncoding(const ACertList: ICertificateList): ICachedEncoding;
+    function GetIsIndirectCrl: Boolean;
+    function LoadCrlEntries: TCryptoLibGenericArray<IX509CrlEntry>;
+    procedure CheckSignature(const AVerifier: IVerifierFactory);
+    function CheckSignatureValid(const AVerifier: IVerifierFactory): Boolean;
+
+  strict protected
+    function GetX509Extensions: IX509Extensions; override;
+
+  public
+    constructor Create(const AEncoding: TCryptoLibByteArray); overload;
+    constructor Create(const ACertificateList: ICertificateList); overload;
+
+    function GetCertificateList: ICertificateList;
+    function GetVersion: Int32;
+    function GetIssuerDN: IX509Name;
+    function GetThisUpdate: TDateTime;
+    function GetNextUpdate: TNullable<TDateTime>;
+    function GetRevokedCertificate(const ASerialNumber: TBigInteger): IX509CrlEntry;
+    function GetRevokedCertificates: TCryptoLibGenericArray<IX509CrlEntry>;
+    function GetTbsCertList: TCryptoLibByteArray;
+    function GetSignature: TCryptoLibByteArray;
+    function GetSigAlgName: String;
+    function GetSigAlgOid: String;
+    function GetSigAlgParams: TCryptoLibByteArray;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetEncoded: TCryptoLibByteArray;
+
+    function IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+    function IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+    procedure Verify(const AKey: IAsymmetricKeyParameter); overload;
+    procedure Verify(const AVerifierProvider: IVerifierFactoryProvider); overload;
+    procedure VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+
+    function IsRevoked(const ACert: IX509Certificate): Boolean;
+    function Equals(const AOther: TObject): Boolean; reintroduce;
+    function GetHashCode: Int32; reintroduce;
+    function ToString: String; override;
+  end;
+
+implementation
+
+{ TX509Crl.TCachedEncoding }
+
+constructor TX509Crl.TCachedEncoding.Create(const AEncoding: TCryptoLibByteArray; const AException: Exception);
+begin
+  inherited Create();
+  FEncoding := AEncoding;
+  FException := AException;
+end;
+
+function TX509Crl.TCachedEncoding.GetEncoding: TCryptoLibByteArray;
+begin
+  Result := FEncoding;
+end;
+
+function TX509Crl.TCachedEncoding.GetEncoded: TCryptoLibByteArray;
+begin
+  if FException <> nil then
+    raise FException;
+  if FEncoding = nil then
+    raise ECrlCryptoLibException.Create('CRL encoding is null');
+  Result := FEncoding;
+end;
+
+{ TX509Crl }
+
+constructor TX509Crl.Create(const AEncoding: TCryptoLibByteArray);
+begin
+  Create(TCertificateList.GetInstance(AEncoding));
+end;
+
+constructor TX509Crl.Create(const ACertificateList: ICertificateList);
+var
+  LParameters: IAsn1Encodable;
+begin
+  inherited Create();
+  if ACertificateList = nil then
+    raise EArgumentNilCryptoLibException.Create('certificateList');
+
+  FCertificateList := ACertificateList;
+
+  try
+    LParameters := ACertificateList.SignatureAlgorithm.Parameters;
+    if LParameters <> nil then
+      FSigAlgParams := LParameters.GetEncoded(TAsn1Encodable.Der)
+    else
+      FSigAlgParams := nil;
+    FIsIndirect := GetIsIndirectCrl;
+  except
+    on E: Exception do
+      raise ECrlCryptoLibException.Create('CRL contents invalid: ' + E.ToString);
+  end;
+end;
+
+function TX509Crl.GetX509Extensions: IX509Extensions;
+begin
+  if FCertificateList.Version >= 2 then
+    Result := FCertificateList.TbsCertList.Extensions
+  else
+    Result := nil;
+end;
+
+function TX509Crl.GetIsIndirectCrl: Boolean;
+var
+  LObj: IAsn1Object;
+  LIdp: IIssuingDistributionPoint;
+begin
+  try
+    LObj := GetExtensionParsedValue(TX509Extensions.IssuingDistributionPoint);
+    if LObj = nil then
+    begin
+      Result := False;
+      Exit;
+    end;
+    LIdp := TIssuingDistributionPoint.GetInstance(LObj as IAsn1Convertible);
+    Result := (LIdp <> nil) and LIdp.IsIndirectCrl;
+  except
+    on E: Exception do
+      raise ECrlCryptoLibException.Create('Exception reading IssuingDistributionPoint' + E.ToString);
+  end;
+end;
+
+function TX509Crl.GetCachedEncoding: ICachedEncoding;
+begin
+  if FCachedEncoding = nil then
+    FCachedEncoding := CreateCachedEncoding(FCertificateList);
+  Result := FCachedEncoding;
+end;
+
+function TX509Crl.CreateCachedEncoding(const ACertList: ICertificateList): ICachedEncoding;
+var
+  LEncoding: TCryptoLibByteArray;
+  LException: Exception;
+begin
+  LEncoding := nil;
+  LException := nil;
+  try
+    LEncoding := ACertList.GetDerEncoded();
+  except
+    on E: EIOCryptoLibException do
+      LException := ECrlCryptoLibException.Create('Failed to DER-encode CRL: ' + E.Message);
+  end;
+  Result := TCachedEncoding.Create(LEncoding, LException);
+end;
+
+function TX509Crl.GetCertificateList: ICertificateList;
+begin
+  Result := FCertificateList;
+end;
+
+function TX509Crl.GetVersion: Int32;
+begin
+  Result := FCertificateList.Version;
+end;
+
+function TX509Crl.GetIssuerDN: IX509Name;
+begin
+  Result := FCertificateList.Issuer;
+end;
+
+function TX509Crl.GetThisUpdate: TDateTime;
+begin
+  Result := FCertificateList.ThisUpdate.ToDateTime();
+end;
+
+function TX509Crl.GetNextUpdate: TNullable<TDateTime>;
+var
+  LNext: ITime;
+begin
+  LNext := FCertificateList.TbsCertList.NextUpdate;
+  if LNext = nil then
+    Result := TNullable<TDateTime>.None
+  else
+    Result := TNullable<TDateTime>.Some(LNext.ToDateTime());
+end;
+
+function TX509Crl.LoadCrlEntries: TCryptoLibGenericArray<IX509CrlEntry>;
+var
+  LRevoked: TCryptoLibGenericArray<ICrlEntry>;
+  LList: TList<IX509CrlEntry>;
+  I: Int32;
+  LPreviousIssuer: IX509Name;
+  LEntry: IX509CrlEntry;
+begin
+  LRevoked := FCertificateList.GetRevokedCertificates();
+  if (LRevoked = nil) or (System.Length(LRevoked) = 0) then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LList := TList<IX509CrlEntry>.Create();
+  try
+    LPreviousIssuer := GetIssuerDN();
+    for I := 0 to System.High(LRevoked) do
+    begin
+      LEntry := TX509CrlEntry.Create(LRevoked[I], FIsIndirect, LPreviousIssuer);
+      LList.Add(LEntry);
+      LPreviousIssuer := LEntry.GetCertificateIssuer();
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
+function TX509Crl.GetRevokedCertificate(const ASerialNumber: TBigInteger): IX509CrlEntry;
+var
+  LRevoked: TCryptoLibGenericArray<ICrlEntry>;
+  LPreviousIssuer: IX509Name;
+  I: Int32;
+  LEntry: IX509CrlEntry;
+begin
+  LRevoked := FCertificateList.GetRevokedCertificates();
+  if LRevoked = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LPreviousIssuer := GetIssuerDN();
+  for I := 0 to System.High(LRevoked) do
+  begin
+    LEntry := TX509CrlEntry.Create(LRevoked[I], FIsIndirect, LPreviousIssuer);
+    if LRevoked[I].UserCertificate.Value.Equals(ASerialNumber) then
+    begin
+      Result := LEntry;
+      Exit;
+    end;
+    LPreviousIssuer := LEntry.GetCertificateIssuer();
+  end;
+  Result := nil;
+end;
+
+function TX509Crl.GetRevokedCertificates: TCryptoLibGenericArray<IX509CrlEntry>;
+var
+  LEntries: TCryptoLibGenericArray<IX509CrlEntry>;
+begin
+  LEntries := LoadCrlEntries();
+  if (LEntries = nil) or (System.Length(LEntries) = 0) then
+    Result := nil
+  else
+    Result := LEntries;
+end;
+
+function TX509Crl.GetTbsCertList: TCryptoLibByteArray;
+begin
+  try
+    Result := FCertificateList.TbsCertList.GetDerEncoded();
+  except
+    on E: Exception do
+      raise ECrlCryptoLibException.Create(E.ToString);
+  end;
+end;
+
+function TX509Crl.GetSignature: TCryptoLibByteArray;
+begin
+  Result := FCertificateList.GetSignatureOctets();
+end;
+
+function TX509Crl.GetSigAlgName: String;
+begin
+  if FSigAlgName = '' then
+    FSigAlgName := TX509SignatureUtilities.GetSignatureName(GetSignatureAlgorithm());
+  Result := FSigAlgName;
+end;
+
+function TX509Crl.GetSigAlgOid: String;
+begin
+  Result := FCertificateList.SignatureAlgorithm.Algorithm.Id;
+end;
+
+function TX509Crl.GetSigAlgParams: TCryptoLibByteArray;
+begin
+  if FSigAlgParams = nil then
+    Result := nil
+  else
+    Result := System.Copy(FSigAlgParams);
+end;
+
+function TX509Crl.GetSignatureAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FCertificateList.SignatureAlgorithm;
+end;
+
+function TX509Crl.GetEncoded: TCryptoLibByteArray;
+begin
+  Result := System.Copy(GetCachedEncoding().GetEncoded());
+end;
+
+function TX509Crl.IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean;
+begin
+  Result := CheckSignatureValid(TAsn1VerifierFactory.Create(FCertificateList.SignatureAlgorithm, AKey) as IVerifierFactory);
+end;
+
+function TX509Crl.IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+begin
+  Result := CheckSignatureValid(AVerifierProvider.CreateVerifierFactory(FCertificateList.SignatureAlgorithm));
+end;
+
+function TX509Crl.IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+var
+  LTbsCertList: ITbsCertificateList;
+  LExtensions: IX509Extensions;
+  LAltSigAlg: IAltSignatureAlgorithm;
+  LAltSigValue: IAltSignatureValue;
+  LVerifier: IVerifierFactory;
+  LTbsSeq: IAsn1Sequence;
+  LV: IAsn1EncodableVector;
+  LStart, I: Int32;
+  LVersion: IDerInteger;
+  LTagged: IDerTaggedObject;
+begin
+  LTbsCertList := FCertificateList.TbsCertList;
+  LExtensions := LTbsCertList.Extensions;
+  LAltSigAlg := TAltSignatureAlgorithm.FromExtensions(LExtensions);
+  LAltSigValue := TAltSignatureValue.FromExtensions(LExtensions);
+  LVerifier := AVerifierProvider.CreateVerifierFactory(LAltSigAlg.Algorithm);
+  LTbsSeq := TAsn1Sequence.GetInstance(LTbsCertList.ToAsn1Object());
+  LV := TAsn1EncodableVector.Create();
+  LStart := 1;
+  if (LTbsSeq.Count > 0) and Supports(LTbsSeq[0], IDerInteger, LVersion) then
+  begin
+    LV.Add(LTbsSeq[0] as IAsn1Encodable);
+    LStart := 2;
+  end;
+  for I := LStart to LTbsSeq.Count - 2 do
+    LV.Add(LTbsSeq[I] as IAsn1Encodable);
+  LTagged := TDerTaggedObject.Create(True, 0, LExtensions.ToAsn1ObjectTrimmed() as IAsn1Encodable);
+  LV.Add(LTagged as IAsn1Encodable);
+  Result := TX509Utilities.VerifySignature(LVerifier, TDerSequence.Create(LV) as IAsn1Encodable, LAltSigValue.Signature);
+end;
+
+procedure TX509Crl.Verify(const AKey: IAsymmetricKeyParameter);
+begin
+  CheckSignature(TAsn1VerifierFactory.Create(FCertificateList.SignatureAlgorithm, AKey) as IVerifierFactory);
+end;
+
+procedure TX509Crl.Verify(const AVerifierProvider: IVerifierFactoryProvider);
+begin
+  CheckSignature(AVerifierProvider.CreateVerifierFactory(FCertificateList.SignatureAlgorithm));
+end;
+
+procedure TX509Crl.VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+begin
+  if not IsAlternativeSignatureValid(AVerifierProvider) then
+    raise EInvalidKeyCryptoLibException.Create('CRL alternative signature does not verify with supplied public key.');
+end;
+
+procedure TX509Crl.CheckSignature(const AVerifier: IVerifierFactory);
+begin
+  if not CheckSignatureValid(AVerifier) then
+    raise EInvalidKeyCryptoLibException.Create('CRL does not verify with supplied public key.');
+end;
+
+function TX509Crl.CheckSignatureValid(const AVerifier: IVerifierFactory): Boolean;
+var
+  LTbsCertList: ITbsCertificateList;
+begin
+  LTbsCertList := FCertificateList.TbsCertList;
+  if not TX509Utilities.AreEquivalentAlgorithms(FCertificateList.SignatureAlgorithm, LTbsCertList.Signature) then
+    raise ECrlCryptoLibException.Create('Signature algorithm on CertificateList does not match TbsCertList.');
+  Result := TX509Utilities.VerifySignature(AVerifier, LTbsCertList as IAsn1Encodable, FCertificateList.Signature);
+end;
+
+function TX509Crl.IsRevoked(const ACert: IX509Certificate): Boolean;
+var
+  LRevoked: TCryptoLibGenericArray<ICrlEntry>;
+  LSerial: TBigInteger;
+  I: Int32;
+begin
+  LRevoked := FCertificateList.GetRevokedCertificates();
+  if LRevoked <> nil then
+  begin
+    LSerial := ACert.GetSerialNumber();
+    for I := 0 to System.High(LRevoked) do
+      if LRevoked[I].UserCertificate.Value.Equals(LSerial) then
+      begin
+        Result := True;
+        Exit;
+      end;
+  end;
+  Result := False;
+end;
+
+function TX509Crl.Equals(const AOther: TObject): Boolean;
+var
+  LThat: IX509Crl;
+  LThisEncoding, LThatEncoding: TCryptoLibByteArray;
+  LThatObj: TX509Crl;
+begin
+  if Self = AOther then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  if not Supports(AOther, IX509Crl, LThat) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LThatObj := AOther as TX509Crl;
+  if FHashValueSet and LThatObj.FHashValueSet then
+  begin
+    if FHashValue <> LThatObj.FHashValue then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end
+  else if (FCachedEncoding = nil) or (LThatObj.FCachedEncoding = nil) then
+  begin
+    if (FCertificateList.Signature <> nil) and (not FCertificateList.Signature.Equals(LThat.CertificateList.Signature)) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  LThisEncoding := GetCachedEncoding().GetEncoding();
+  LThatEncoding := LThatObj.GetCachedEncoding().GetEncoding();
+  Result := (LThisEncoding <> nil) and (LThatEncoding <> nil) and TArrayUtilities.AreEqual<Byte>(LThisEncoding, LThatEncoding);
+end;
+
+function TX509Crl.GetHashCode: Int32;
+var
+  LEncoding: TCryptoLibByteArray;
+begin
+  if not FHashValueSet then
+  begin
+    LEncoding := GetCachedEncoding().GetEncoding();
+    FHashValue := TArrayUtilities.GetArrayHashCode(LEncoding);
+    FHashValueSet := True;
+  end;
+  Result := FHashValue;
+end;
+
+function TX509Crl.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LSig: TCryptoLibByteArray;
+  I, LCount: Int32;
+  LNext: TNullable<TDateTime>;
+  LExtensions: IX509Extensions;
+  LOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+  LObj: IAsn1Object;
+  LDerInt: IDerInteger;
+  LEntries: TCryptoLibGenericArray<IX509CrlEntry>;
+  LEntry: IX509CrlEntry;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.Append('              Version: ').Append(GetVersion()).AppendLine();
+    LBuf.Append('             IssuerDN: ').Append(GetIssuerDN().ToString()).AppendLine();
+    LBuf.Append('          This update: ').Append(DateTimeToStr(GetThisUpdate())).AppendLine();
+    LNext := GetNextUpdate();
+    if LNext.HasValue then
+      LBuf.Append('          Next update: ').Append(DateTimeToStr(LNext.Value)).AppendLine()
+    else
+      LBuf.Append('          Next update: null').AppendLine();
+    LBuf.Append('  Signature Algorithm: ').Append(GetSigAlgName()).AppendLine();
+
+    LSig := GetSignature();
+    LBuf.Append('            Signature: ');
+    LCount := Math.Min(20, System.Length(LSig));
+    LBuf.AppendLine(THex.Encode(TArrayUtilities.CopyOfRange<Byte>(LSig, 0, LCount)));
+    I := 20;
+    while I < System.Length(LSig) do
+    begin
+      LCount := Math.Min(20, System.Length(LSig) - I);
+      LBuf.Append('                       ').AppendLine(THex.Encode(TArrayUtilities.CopyOfRange<Byte>(LSig, I, I + LCount)));
+      System.Inc(I, 20);
+    end;
+
+    LExtensions := FCertificateList.TbsCertList.Extensions;
+    if LExtensions <> nil then
+    begin
+      LOids := LExtensions.GetExtensionOids();
+      if System.Length(LOids) > 0 then
+      begin
+        LBuf.AppendLine('           Extensions:');
+        for LOid in LOids do
+        begin
+          LExt := LExtensions.GetExtension(LOid);
+          if (LExt <> nil) and (LExt.Value <> nil) then
+          begin
+            LObj := TX509ExtensionUtilities.FromExtensionValue(LExt.Value);
+            LBuf.Append('                       critical(').Append(BoolToStr(LExt.IsCritical, True)).Append(') ');
+            try
+              if LOid.Equals(TX509Extensions.CrlNumber) then
+              begin
+                LDerInt := TDerInteger.GetInstance(LObj);
+                LBuf.Append(LDerInt.PositiveValue.ToString()).AppendLine();
+              end
+              else if LOid.Equals(TX509Extensions.DeltaCrlIndicator) then
+              begin
+                LDerInt := TDerInteger.GetInstance(LObj);
+                LBuf.Append('Base CRL: ').Append(LDerInt.PositiveValue.ToString()).AppendLine();
+              end
+              else if LOid.Equals(TX509Extensions.IssuingDistributionPoint) then
+                LBuf.Append(TIssuingDistributionPoint.GetInstance(LObj as IAsn1Sequence).ToString()).AppendLine()
+              else if LOid.Equals(TX509Extensions.CrlDistributionPoints) then
+                LBuf.Append(TCrlDistPoint.GetInstance(LObj as IAsn1Sequence).ToString()).AppendLine()
+              else if LOid.Equals(TX509Extensions.FreshestCrl) then
+                LBuf.Append(TCrlDistPoint.GetInstance(LObj as IAsn1Sequence).ToString()).AppendLine()
+              else
+              begin
+                LBuf.Append(LOid.Id);
+                LBuf.Append(' value = ').Append(TAsn1Dumper.DumpAsString(LObj)).AppendLine();
+              end;
+            except
+              LBuf.Append(LOid.Id);
+              LBuf.Append(' value = *****').AppendLine();
+            end;
+          end
+          else
+            LBuf.AppendLine();
+        end;
+      end;
+    end;
+
+    LEntries := GetRevokedCertificates();
+    if LEntries <> nil then
+      for LEntry in LEntries do
+      begin
+        LBuf.Append(LEntry.ToString());
+        LBuf.AppendLine();
+      end;
+
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+end.

+ 278 - 0
CryptoLib/src/X509/ClpX509CrlEntry.pas

@@ -0,0 +1,278 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpX509CrlEntry;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpIAsn1Core,
+  ClpIAsn1Objects,
+  ClpAsn1Objects,
+  ClpIX509Extension,
+  ClpIX509CrlEntry,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpX509ExtensionBase,
+  ClpX509ExtensionUtilities,
+  ClpAsn1Dumper,
+  ClpCryptoLibTypes;
+
+type
+  TX509CrlEntry = class(TX509ExtensionBase, IX509CrlEntry)
+  strict private
+    var
+      FCrlEntry: ICrlEntry;
+      FIsIndirect: Boolean;
+      FPreviousCertificateIssuer: IX509Name;
+      FCertificateIssuer: IX509Name;
+      FHashValueSet: Boolean;
+      FHashValue: Int32;
+
+    function LoadCertificateIssuer: IX509Name;
+  strict protected
+    function GetX509Extensions: IX509Extensions; override;
+  public
+    constructor Create(const ACrlEntry: ICrlEntry); overload;
+    constructor Create(const ACrlEntry: ICrlEntry; AIsIndirect: Boolean;
+      const APreviousCertificateIssuer: IX509Name); overload;
+
+    function GetCrlEntry: ICrlEntry;
+    function GetCertificateIssuer: IX509Name;
+    function GetEncoded: TCryptoLibByteArray;
+    function GetSerialNumber: TBigInteger;
+    function GetRevocationDate: TDateTime;
+    function GetHasExtensions: Boolean;
+    function Equals(const AOther: TObject): Boolean; reintroduce;
+    function GetHashCode: Int32; reintroduce;
+    function ToString: String; override;
+
+    property CrlEntry: ICrlEntry read GetCrlEntry;
+    property SerialNumber: TBigInteger read GetSerialNumber;
+    property RevocationDate: TDateTime read GetRevocationDate;
+    property HasExtensions: Boolean read GetHasExtensions;
+  end;
+
+implementation
+
+{ TX509CrlEntry }
+
+constructor TX509CrlEntry.Create(const ACrlEntry: ICrlEntry);
+begin
+  inherited Create();
+  FCrlEntry := ACrlEntry;
+  FIsIndirect := False;
+  FPreviousCertificateIssuer := nil;
+  FCertificateIssuer := LoadCertificateIssuer();
+end;
+
+constructor TX509CrlEntry.Create(const ACrlEntry: ICrlEntry; AIsIndirect: Boolean;
+  const APreviousCertificateIssuer: IX509Name);
+begin
+  inherited Create();
+  FCrlEntry := ACrlEntry;
+  FIsIndirect := AIsIndirect;
+  FPreviousCertificateIssuer := APreviousCertificateIssuer;
+  FCertificateIssuer := LoadCertificateIssuer();
+end;
+
+function TX509CrlEntry.GetX509Extensions: IX509Extensions;
+begin
+  Result := FCrlEntry.Extensions;
+end;
+
+function TX509CrlEntry.LoadCertificateIssuer: IX509Name;
+var
+  LCertificateIssuer: IGeneralNames;
+  LNames: TCryptoLibGenericArray<IGeneralName>;
+  I: Int32;
+  LObj: IAsn1Object;
+begin
+  if not FIsIndirect then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LObj := GetExtensionParsedValue(TX509Extensions.CertificateIssuer);
+  if LObj = nil then
+  begin
+    Result := FPreviousCertificateIssuer;
+    Exit;
+  end;
+
+  try
+    LCertificateIssuer := TGeneralNames.GetInstance(LObj as IAsn1Convertible);
+    if LCertificateIssuer <> nil then
+    begin
+      LNames := LCertificateIssuer.GetNames;
+      for I := 0 to System.High(LNames) do
+        if LNames[I].TagNo = TGeneralName.DirectoryName then
+        begin
+          Result := TX509Name.GetInstance(LNames[I].Name);
+          Exit;
+        end;
+    end;
+  except
+    // ignore
+  end;
+
+  Result := nil;
+end;
+
+function TX509CrlEntry.GetCrlEntry: ICrlEntry;
+begin
+  Result := FCrlEntry;
+end;
+
+function TX509CrlEntry.GetCertificateIssuer: IX509Name;
+begin
+  Result := FCertificateIssuer;
+end;
+
+function TX509CrlEntry.GetEncoded: TCryptoLibByteArray;
+begin
+  try
+    Result := FCrlEntry.GetDerEncoded();
+  except
+    on E: Exception do
+      raise ECrlCryptoLibException.Create(E.ToString);
+  end;
+end;
+
+function TX509CrlEntry.GetSerialNumber: TBigInteger;
+begin
+  Result := FCrlEntry.UserCertificate.Value;
+end;
+
+function TX509CrlEntry.GetRevocationDate: TDateTime;
+begin
+  Result := FCrlEntry.RevocationDate.ToDateTime;
+end;
+
+function TX509CrlEntry.GetHasExtensions: Boolean;
+begin
+  Result := FCrlEntry.Extensions <> nil;
+end;
+
+function TX509CrlEntry.Equals(const AOther: TObject): Boolean;
+var
+  LThat: IX509CrlEntry;
+begin
+  if Self = AOther then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  if not Supports(AOther, IX509CrlEntry, LThat) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  if FHashValueSet and (LThat as TX509CrlEntry).FHashValueSet then
+  begin
+    if FHashValue <> (LThat as TX509CrlEntry).FHashValue then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  Result := FCrlEntry.Equals(LThat.CrlEntry as IAsn1Convertible);
+end;
+
+function TX509CrlEntry.GetHashCode: Int32;
+begin
+  if not FHashValueSet then
+  begin
+    FHashValue := FCrlEntry.GetHashCode();
+    FHashValueSet := True;
+  end;
+  Result := FHashValue;
+end;
+
+function TX509CrlEntry.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LExtensions: IX509Extensions;
+  LOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  I: Int32;
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+  LObj: IAsn1Object;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.Append('        userCertificate: ').Append(SerialNumber.ToString).AppendLine;
+    LBuf.Append('         revocationDate: ').Append(DateTimeToStr(RevocationDate)).AppendLine;
+    LBuf.Append('      certificateIssuer: ');
+    if FCertificateIssuer <> nil then
+      LBuf.Append(FCertificateIssuer.ToString)
+    else
+      LBuf.Append('null');
+    LBuf.AppendLine;
+
+    LExtensions := FCrlEntry.Extensions;
+    if LExtensions <> nil then
+    begin
+      LOids := LExtensions.ExtensionOids;
+      if System.Length(LOids) > 0 then
+      begin
+        LBuf.AppendLine('   crlEntryExtensions:');
+        for I := 0 to System.High(LOids) do
+        begin
+          LOid := LOids[I];
+          LExt := LExtensions.GetExtension(LOid);
+          if (LExt <> nil) and (LExt.Value <> nil) then
+          begin
+            LObj := TX509ExtensionUtilities.FromExtensionValue(LExt.Value);
+            LBuf.Append('                       critical(').Append(LExt.IsCritical).Append(') ');
+            try
+              if LOid.Equals(TX509Extensions.ReasonCode) then
+                LBuf.Append(TCrlReason.Create(TDerEnumerated.GetInstance(LObj)).ToString())
+              else if LOid.Equals(TX509Extensions.CertificateIssuer) then
+                LBuf.Append('Certificate issuer: ').Append(TGeneralNames.GetInstance(LObj as IAsn1Sequence).ToString())
+              else
+              begin
+                LBuf.Append(LOid.Id);
+                LBuf.Append(' value = ').Append(TAsn1Dumper.DumpAsString(LObj));
+              end;
+              LBuf.AppendLine;
+            except
+              LBuf.Append(LOid.Id);
+              LBuf.Append(' value = *****').AppendLine;
+            end;
+          end
+          else
+            LBuf.AppendLine;
+        end;
+      end;
+    end;
+
+    Result := LBuf.ToString;
+  finally
+    LBuf.Free;
+  end;
+end;
+
+end.

+ 319 - 0
CryptoLib/src/X509/ClpX509CrlParser.pas

@@ -0,0 +1,319 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpX509CrlParser;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Generics.Collections,
+  ClpIAsn1Core,
+  ClpIAsn1Objects,
+  ClpIX509CrlParser,
+  ClpIX509Crl,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpX509Crl,
+  ClpPemObjects,
+  ClpIPemObjects,
+  ClpAsn1Streams,
+  ClpAsn1Utilities,
+  ClpPkcsAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpPkcsObjectIdentifiers,
+  ClpStreams,
+  ClpStreamUtilities,
+  ClpCryptoLibTypes;
+
+type
+  TX509CrlParser = class sealed(TInterfacedObject, IX509CrlParser)
+  strict private
+  class var
+    FPemCrlParser: IPemParser;
+
+  var
+    FSCrlData: IAsn1Set;
+    FSCrlDataObjectCount: Int32;
+    FCurrentCrlStream: TStream;
+
+  class constructor Create();
+  class procedure Boot();
+
+  function ReadDerCrl(const ADIn: TAsn1InputStream): IX509Crl;
+  function ReadPemCrl(const AInStream: TStream): IX509Crl;
+  function GetCrl(): IX509Crl;
+
+  public
+    constructor Create();
+    destructor Destroy(); override;
+
+    function ReadCrl(const AInput: TCryptoLibByteArray): IX509Crl; overload;
+    function ReadCrls(const AInput: TCryptoLibByteArray): TCryptoLibGenericArray<IX509Crl>; overload;
+    function ReadCrl(const AInStream: TStream): IX509Crl; overload;
+    function ReadCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>; overload;
+    function ParseCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>;
+  end;
+
+implementation
+
+{ TX509CrlParser }
+
+class constructor TX509CrlParser.Create();
+begin
+  Boot();
+end;
+
+class procedure TX509CrlParser.Boot();
+begin
+  FPemCrlParser := TPemParser.Create('CRL');
+end;
+
+constructor TX509CrlParser.Create();
+begin
+  inherited Create();
+  FSCrlData := nil;
+  FSCrlDataObjectCount := 0;
+  FCurrentCrlStream := nil;
+end;
+
+destructor TX509CrlParser.Destroy();
+begin
+  inherited Destroy();
+end;
+
+function TX509CrlParser.ReadDerCrl(const ADIn: TAsn1InputStream): IX509Crl;
+var
+  LSeq: IAsn1Sequence;
+  LContentType: IDerObjectIdentifier;
+  LSignedData: ISignedData;
+begin
+  LSeq := ADIn.ReadObject() as IAsn1Sequence;
+
+  if (LSeq.Count > 1) and Supports(LSeq[0], IDerObjectIdentifier, LContentType) then
+  begin
+    if LContentType.Equals(TPkcsObjectIdentifiers.SignedData) then
+    begin
+      if TAsn1Utilities.TryGetOptionalContextTagged<Boolean, ISignedData>(
+        LSeq[1], 0, True, LSignedData,
+        function(ATagged: IAsn1TaggedObject; AState: Boolean): ISignedData
+        begin
+          Result := TSignedData.GetTagged(ATagged, AState);
+        end) then
+      begin
+        FSCrlData := LSignedData.Crls;
+        if FSCrlData <> nil then
+        begin
+          FSCrlDataObjectCount := 0;
+          Result := GetCrl();
+          Exit;
+        end;
+      end;
+    end;
+  end;
+
+  Result := TX509Crl.Create(TCertificateList.GetInstance(LSeq));
+end;
+
+function TX509CrlParser.ReadPemCrl(const AInStream: TStream): IX509Crl;
+var
+  LSeq: IAsn1Sequence;
+begin
+  LSeq := FPemCrlParser.ReadPemObject(AInStream);
+
+  if LSeq = nil then
+    Result := nil
+  else
+    Result := TX509Crl.Create(TCertificateList.GetInstance(LSeq));
+end;
+
+function TX509CrlParser.GetCrl(): IX509Crl;
+var
+  LCertList: ICertificateList;
+begin
+  if (FSCrlData = nil) or (FSCrlDataObjectCount >= FSCrlData.Count) then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LCertList := TCertificateList.GetInstance(FSCrlData[FSCrlDataObjectCount]);
+  System.Inc(FSCrlDataObjectCount);
+  Result := TX509Crl.Create(LCertList);
+end;
+
+function TX509CrlParser.ReadCrl(const AInput: TCryptoLibByteArray): IX509Crl;
+var
+  LInStream: TMemoryStream;
+begin
+  LInStream := TMemoryStream.Create();
+  try
+    if System.Length(AInput) > 0 then
+      LInStream.Write(AInput[0], System.Length(AInput));
+    LInStream.Position := 0;
+    Result := ReadCrl(LInStream);
+  finally
+    LInStream.Free();
+  end;
+end;
+
+function TX509CrlParser.ReadCrls(const AInput: TCryptoLibByteArray): TCryptoLibGenericArray<IX509Crl>;
+var
+  LInStream: TMemoryStream;
+begin
+  LInStream := TMemoryStream.Create();
+  try
+    if System.Length(AInput) > 0 then
+      LInStream.Write(AInput[0], System.Length(AInput));
+    LInStream.Position := 0;
+    Result := ReadCrls(LInStream);
+  finally
+    LInStream.Free();
+  end;
+end;
+
+function TX509CrlParser.ReadCrl(const AInStream: TStream): IX509Crl;
+
+  function ReadDerCrlFromStream(const AStream: TStream): IX509Crl;
+  var
+    LAsn1In: TAsn1InputStream;
+  begin
+    LAsn1In := TAsn1InputStream.Create(AStream, Int32.MaxValue, True);
+    try
+      Result := ReadDerCrl(LAsn1In);
+    finally
+      LAsn1In.Free();
+    end;
+  end;
+
+var
+  LTag: Int32;
+  LByte: Byte;
+  LPushbackStream: TPushbackStream;
+  LStreamToUse: TStream;
+begin
+  LPushbackStream := nil;
+  if AInStream = nil then
+    raise EArgumentNilCryptoLibException.Create('inStream');
+
+  if not AInStream.CanRead then
+    raise EArgumentCryptoLibException.Create('Stream must be read-able');
+
+  if FCurrentCrlStream = nil then
+  begin
+    FCurrentCrlStream := AInStream;
+    FSCrlData := nil;
+    FSCrlDataObjectCount := 0;
+  end
+  else if FCurrentCrlStream <> AInStream then
+  begin
+    FCurrentCrlStream := AInStream;
+    FSCrlData := nil;
+    FSCrlDataObjectCount := 0;
+  end;
+
+  try
+    if FSCrlData <> nil then
+    begin
+      if FSCrlDataObjectCount <> FSCrlData.Count then
+      begin
+        Result := GetCrl();
+        Exit;
+      end;
+
+      FSCrlData := nil;
+      FSCrlDataObjectCount := 0;
+      Result := nil;
+      Exit;
+    end;
+
+    LTag := AInStream.ReadByte();
+    if LTag < 0 then
+    begin
+      Result := nil;
+      Exit;
+    end;
+
+    if AInStream.CanSeek then
+    begin
+      AInStream.Seek(-1, TSeekOrigin.soCurrent);
+      LStreamToUse := AInStream;
+    end
+    else
+    begin
+      LPushbackStream := TPushbackStream.Create(AInStream);
+      try
+        LByte := Byte(LTag);
+        LPushbackStream.UnRead(Int32(LByte));
+        LStreamToUse := LPushbackStream;
+
+        if LTag <> $30 then
+        begin
+          Result := ReadPemCrl(LStreamToUse);
+          Exit;
+        end;
+
+        Result := ReadDerCrlFromStream(LStreamToUse);
+      finally
+        LPushbackStream.Free();
+      end;
+      Exit;
+    end;
+
+    if LTag <> $30 then
+    begin
+      Result := ReadPemCrl(LStreamToUse);
+      Exit;
+    end;
+
+    Result := ReadDerCrlFromStream(LStreamToUse);
+  except
+    on E: ECrlCryptoLibException do
+      raise;
+    on E: Exception do
+      raise ECrlCryptoLibException.Create('Failed to read CRL: ' + E.Message);
+  end;
+end;
+
+function TX509CrlParser.ReadCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>;
+begin
+  Result := ParseCrls(AInStream);
+end;
+
+function TX509CrlParser.ParseCrls(const AInStream: TStream): TCryptoLibGenericArray<IX509Crl>;
+var
+  LCrls: TList<IX509Crl>;
+  LCrl: IX509Crl;
+begin
+  LCrls := TList<IX509Crl>.Create();
+  try
+    LCrl := ReadCrl(AInStream);
+    while LCrl <> nil do
+    begin
+      LCrls.Add(LCrl);
+      LCrl := ReadCrl(AInStream);
+    end;
+    Result := LCrls.ToArray();
+  finally
+    LCrls.Free();
+  end;
+end;
+
+end.