Просмотр исходного кода

add some x509, pkcs and x9 asn1objects

Ugochukwu Mmaduekwe 2 недель назад
Родитель
Сommit
3d3991758b
84 измененных файлов с 19096 добавлено и 53 удалено
  1. 71 12
      CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
  2. 6 6
      CryptoLib.Tests/src/Asn1/BitStringTests.pas
  3. 129 0
      CryptoLib.Tests/src/Asn1/PKCS/Pkcs10CertRequestTests.pas
  4. 111 0
      CryptoLib.Tests/src/Asn1/PKCS/PrivateKeyInfoTests.pas
  5. 136 0
      CryptoLib.Tests/src/Asn1/X509/GeneralNameTests.pas
  6. 130 0
      CryptoLib.Tests/src/Asn1/X509/KeyUsageTests.pas
  7. 109 0
      CryptoLib.Tests/src/Asn1/X509/SubjectKeyIdentifierTests.pas
  8. 119 0
      CryptoLib.Tests/src/Asn1/X509/X509AltTests.pas
  9. 254 0
      CryptoLib.Tests/src/Asn1/X509/X509ExtensionsTests.pas
  10. 202 0
      CryptoLib.Tests/src/Asn1/X509/X509NameTests.pas
  11. 2 3
      CryptoLib.Tests/src/Crypto/RSADigestSignerTests.pas
  12. 133 0
      CryptoLib.Tests/src/Utils/Net/IPAddressUtilitiesTests.pas
  13. 278 0
      CryptoLib.Tests/src/Utils/Pem/PemReaderTests.pas
  14. 406 0
      CryptoLib/src/Asn1/ClpAsn1Dumper.pas
  15. 17 4
      CryptoLib/src/Asn1/ClpAsn1Objects.pas
  16. 1444 0
      CryptoLib/src/Asn1/Pkcs/ClpPkcsAsn1Objects.pas
  17. 55 1
      CryptoLib/src/Asn1/Pkcs/ClpPkcsObjectIdentifiers.pas
  18. 227 0
      CryptoLib/src/Asn1/Sec/ClpSecAsn1Objects.pas
  19. 362 0
      CryptoLib/src/Asn1/X500/ClpIetfUtilities.pas
  20. 60 0
      CryptoLib/src/Asn1/X509/ClpRfc5280Asn1Utilities.pas
  21. 7037 0
      CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas
  22. 936 0
      CryptoLib/src/Asn1/X509/ClpX509Certificate.pas
  23. 103 0
      CryptoLib/src/Asn1/X509/ClpX509DefaultEntryConverter.pas
  24. 137 0
      CryptoLib/src/Asn1/X509/ClpX509Extension.pas
  25. 165 0
      CryptoLib/src/Asn1/X509/ClpX509ExtensionBase.pas
  26. 245 0
      CryptoLib/src/Asn1/X509/ClpX509ExtensionUtilities.pas
  27. 269 0
      CryptoLib/src/Asn1/X509/ClpX509ExtensionsGenerator.pas
  28. 79 0
      CryptoLib/src/Asn1/X509/ClpX509NameEntryConverter.pas
  29. 135 0
      CryptoLib/src/Asn1/X509/ClpX509NameTokenizer.pas
  30. 324 0
      CryptoLib/src/Asn1/X509/ClpX509SignatureUtilities.pas
  31. 387 0
      CryptoLib/src/Asn1/X509/ClpX509Utilities.pas
  32. 223 0
      CryptoLib/src/Asn1/X9/ClpX9Asn1Objects.pas
  33. 19 0
      CryptoLib/src/Asn1/X9/ClpX9ECParameters.pas
  34. 119 0
      CryptoLib/src/Crypto/IO/ClpDigestSink.pas
  35. 119 0
      CryptoLib/src/Crypto/IO/ClpSignerSink.pas
  36. 100 0
      CryptoLib/src/Crypto/Operators/ClpAsn1DigestFactory.pas
  37. 144 0
      CryptoLib/src/Crypto/Operators/ClpAsn1SignatureFactory.pas
  38. 112 0
      CryptoLib/src/Crypto/Operators/ClpAsn1VerifierFactory.pas
  39. 86 0
      CryptoLib/src/Crypto/Operators/ClpAsn1VerifierFactoryProvider.pas
  40. 78 0
      CryptoLib/src/Crypto/Operators/ClpDefaultDigestCalculator.pas
  41. 73 0
      CryptoLib/src/Crypto/Operators/ClpDefaultDigestResult.pas
  42. 73 0
      CryptoLib/src/Crypto/Operators/ClpDefaultSignatureCalculator.pas
  43. 76 0
      CryptoLib/src/Crypto/Operators/ClpDefaultSignatureResult.pas
  44. 78 0
      CryptoLib/src/Crypto/Operators/ClpDefaultVerifierCalculator.pas
  45. 70 0
      CryptoLib/src/Crypto/Operators/ClpDefaultVerifierResult.pas
  46. 15 2
      CryptoLib/src/Crypto/Signers/ClpDsaDigestSigner.pas
  47. 6 0
      CryptoLib/src/Crypto/Signers/ClpEd25519CtxSigner.pas
  48. 6 0
      CryptoLib/src/Crypto/Signers/ClpEd25519PhSigner.pas
  49. 6 0
      CryptoLib/src/Crypto/Signers/ClpEd25519Signer.pas
  50. 6 0
      CryptoLib/src/Crypto/Signers/ClpGenericSigner.pas
  51. 7 2
      CryptoLib/src/Crypto/Signers/ClpPssSigner.pas
  52. 6 12
      CryptoLib/src/Crypto/Signers/ClpRsaDigestSigner.pas
  53. 24 0
      CryptoLib/src/Crypto/Signers/ClpSchnorrDigestSigner.pas
  54. 14 0
      CryptoLib/src/Crypto/Signers/SignersEncodings/ClpSignersEncodings.pas
  55. 10 11
      CryptoLib/src/Interfaces/ClpIAsn1Objects.pas
  56. 50 0
      CryptoLib/src/Interfaces/ClpIBlockResult.pas
  57. 57 0
      CryptoLib/src/Interfaces/ClpIDigestFactory.pas
  58. 52 0
      CryptoLib/src/Interfaces/ClpIMacFactory.pas
  59. 38 0
      CryptoLib/src/Interfaces/ClpIMiscPemGenerator.pas
  60. 151 0
      CryptoLib/src/Interfaces/ClpIPemObjects.pas
  61. 149 0
      CryptoLib/src/Interfaces/ClpIPkcsAsn1Objects.pas
  62. 50 0
      CryptoLib/src/Interfaces/ClpISecAsn1Objects.pas
  63. 50 0
      CryptoLib/src/Interfaces/ClpISignatureFactory.pas
  64. 5 0
      CryptoLib/src/Interfaces/ClpISigner.pas
  65. 5 0
      CryptoLib/src/Interfaces/ClpISignersEncodings.pas
  66. 50 0
      CryptoLib/src/Interfaces/ClpIStreamCalculator.pas
  67. 46 0
      CryptoLib/src/Interfaces/ClpIVerifier.pas
  68. 50 0
      CryptoLib/src/Interfaces/ClpIVerifierFactory.pas
  69. 42 0
      CryptoLib/src/Interfaces/ClpIVerifierFactoryProvider.pas
  70. 563 0
      CryptoLib/src/Interfaces/ClpIX509Asn1Objects.pas
  71. 98 0
      CryptoLib/src/Interfaces/ClpIX509Certificate.pas
  72. 44 0
      CryptoLib/src/Interfaces/ClpIX509Extension.pas
  73. 64 0
      CryptoLib/src/Interfaces/ClpIX509ExtensionsGenerator.pas
  74. 40 0
      CryptoLib/src/Interfaces/ClpIX509NameEntryConverter.pas
  75. 40 0
      CryptoLib/src/Interfaces/ClpIX509NameTokenizer.pas
  76. 46 0
      CryptoLib/src/Interfaces/ClpIX9Asn1Objects.pas
  77. 170 0
      CryptoLib/src/Security/ClpPrivateKeyFactory.pas
  78. 152 0
      CryptoLib/src/Security/ClpPublicKeyFactory.pas
  79. 64 0
      CryptoLib/src/Utils/ClpArrayUtils.pas
  80. 22 0
      CryptoLib/src/Utils/ClpCollectionUtilities.pas
  81. 210 0
      CryptoLib/src/Utils/ClpCryptoLibComparers.pas
  82. 378 0
      CryptoLib/src/Utils/ClpIPAddressUtilities.pas
  83. 192 0
      CryptoLib/src/Utils/ClpPlatform.pas
  84. 680 0
      CryptoLib/src/Utils/Pem/ClpPemObjects.pas

+ 71 - 12
CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr

@@ -19,14 +19,14 @@ program CryptoLib.Tests;
 {$ENDIF}
 
 uses
-{$IFDEF TESTINSIGHT}
-   TestInsight.DUnit,
-{$ELSE}
-   Forms,
-   TestFramework,
-   GUITestRunner,
-   TextTestRunner,
-{$ENDIF}
+  {$IFDEF TESTINSIGHT}
+  TestInsight.DUnit,
+  {$ELSE}
+  Forms,
+  TestFramework,
+  GUITestRunner,
+  TextTestRunner,
+  {$ENDIF }
   ClpECGost3410NamedCurves in '..\..\CryptoLib\src\Asn1\CryptoPro\ClpECGost3410NamedCurves.pas',
   ClpCryptoProObjectIdentifiers in '..\..\CryptoLib\src\Asn1\CryptoPro\ClpCryptoProObjectIdentifiers.pas',
   ClpNistObjectIdentifiers in '..\..\CryptoLib\src\Asn1\Nist\ClpNistObjectIdentifiers.pas',
@@ -419,10 +419,6 @@ uses
   ClpRsaBlindingParameters in '..\..\CryptoLib\src\Crypto\Parameters\ClpRsaBlindingParameters.pas',
   ClpIRsaBlindingFactorGenerator in '..\..\CryptoLib\src\Interfaces\ClpIRsaBlindingFactorGenerator.pas',
   ClpRsaBlindingFactorGenerator in '..\..\CryptoLib\src\Crypto\Generators\ClpRsaBlindingFactorGenerator.pas',
-  ClpIAlgorithmIdentifier in '..\..\CryptoLib\src\Interfaces\ClpIAlgorithmIdentifier.pas',
-  ClpAlgorithmIdentifier in '..\..\CryptoLib\src\Asn1\X509\ClpAlgorithmIdentifier.pas',
-  ClpIDigestInfo in '..\..\CryptoLib\src\Interfaces\ClpIDigestInfo.pas',
-  ClpDigestInfo in '..\..\CryptoLib\src\Asn1\X509\ClpDigestInfo.pas',
   ClpX509ObjectIdentifiers in '..\..\CryptoLib\src\Asn1\X509\ClpX509ObjectIdentifiers.pas',
   ClpIRsaBlindingEngine in '..\..\CryptoLib\src\Interfaces\ClpIRsaBlindingEngine.pas',
   ClpRsaBlindingEngine in '..\..\CryptoLib\src\Crypto\Engines\ClpRsaBlindingEngine.pas',
@@ -434,6 +430,59 @@ uses
   ClpGenericSigner in '..\..\CryptoLib\src\Crypto\Signers\ClpGenericSigner.pas',
   ClpIPrehash in '..\..\CryptoLib\src\Interfaces\ClpIPrehash.pas',
   ClpPrehash in '..\..\CryptoLib\src\Crypto\Digests\ClpPrehash.pas',
+  ClpX509Asn1Objects in '..\..\CryptoLib\src\Asn1\X509\ClpX509Asn1Objects.pas',
+  ClpPkcsAsn1Objects in '..\..\CryptoLib\src\Asn1\Pkcs\ClpPkcsAsn1Objects.pas',
+  ClpX9Asn1Objects in '..\..\CryptoLib\src\Asn1\X9\ClpX9Asn1Objects.pas',
+  ClpX509Extension in '..\..\CryptoLib\src\Asn1\X509\ClpX509Extension.pas',
+  ClpX509DefaultEntryConverter in '..\..\CryptoLib\src\Asn1\X509\ClpX509DefaultEntryConverter.pas',
+  ClpX509ExtensionsGenerator in '..\..\CryptoLib\src\Asn1\X509\ClpX509ExtensionsGenerator.pas',
+  ClpX509NameEntryConverter in '..\..\CryptoLib\src\Asn1\X509\ClpX509NameEntryConverter.pas',
+  ClpX509NameTokenizer in '..\..\CryptoLib\src\Asn1\X509\ClpX509NameTokenizer.pas',
+  ClpSignerSink in '..\..\CryptoLib\src\Crypto\IO\ClpSignerSink.pas',
+  ClpDigestSink in '..\..\CryptoLib\src\Crypto\IO\ClpDigestSink.pas',
+  ClpDefaultSignatureResult in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultSignatureResult.pas',
+  ClpDefaultVerifierResult in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultVerifierResult.pas',
+  ClpDefaultSignatureCalculator in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultSignatureCalculator.pas',
+  ClpDefaultVerifierCalculator in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultVerifierCalculator.pas',
+  ClpDefaultDigestCalculator in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultDigestCalculator.pas',
+  ClpDefaultDigestResult in '..\..\CryptoLib\src\Crypto\Operators\ClpDefaultDigestResult.pas',
+  ClpAsn1SignatureFactory in '..\..\CryptoLib\src\Crypto\Operators\ClpAsn1SignatureFactory.pas',
+  ClpAsn1VerifierFactory in '..\..\CryptoLib\src\Crypto\Operators\ClpAsn1VerifierFactory.pas',
+  ClpAsn1VerifierFactoryProvider in '..\..\CryptoLib\src\Crypto\Operators\ClpAsn1VerifierFactoryProvider.pas',
+  ClpAsn1DigestFactory in '..\..\CryptoLib\src\Crypto\Operators\ClpAsn1DigestFactory.pas',
+  ClpPublicKeyFactory in '..\..\CryptoLib\src\Security\ClpPublicKeyFactory.pas',
+  ClpPrivateKeyFactory in '..\..\CryptoLib\src\Security\ClpPrivateKeyFactory.pas',
+  ClpIX509Asn1Objects in '..\..\CryptoLib\src\Interfaces\ClpIX509Asn1Objects.pas',
+  ClpIX509Extension in '..\..\CryptoLib\src\Interfaces\ClpIX509Extension.pas',
+  ClpIX509NameEntryConverter in '..\..\CryptoLib\src\Interfaces\ClpIX509NameEntryConverter.pas',
+  ClpIX509Certificate in '..\..\CryptoLib\src\Interfaces\ClpIX509Certificate.pas',
+  ClpIPkcsAsn1Objects in '..\..\CryptoLib\src\Interfaces\ClpIPkcsAsn1Objects.pas',
+  ClpIX9Asn1Objects in '..\..\CryptoLib\src\Interfaces\ClpIX9Asn1Objects.pas',
+  ClpIX509ExtensionsGenerator in '..\..\CryptoLib\src\Interfaces\ClpIX509ExtensionsGenerator.pas',
+  ClpIBlockResult in '..\..\CryptoLib\src\Interfaces\ClpIBlockResult.pas',
+  ClpIVerifier in '..\..\CryptoLib\src\Interfaces\ClpIVerifier.pas',
+  ClpIStreamCalculator in '..\..\CryptoLib\src\Interfaces\ClpIStreamCalculator.pas',
+  ClpISignatureFactory in '..\..\CryptoLib\src\Interfaces\ClpISignatureFactory.pas',
+  ClpIVerifierFactoryProvider in '..\..\CryptoLib\src\Interfaces\ClpIVerifierFactoryProvider.pas',
+  ClpIVerifierFactory in '..\..\CryptoLib\src\Interfaces\ClpIVerifierFactory.pas',
+  ClpIDigestFactory in '..\..\CryptoLib\src\Interfaces\ClpIDigestFactory.pas',
+  ClpIMacFactory in '..\..\CryptoLib\src\Interfaces\ClpIMacFactory.pas',
+  ClpISecAsn1Objects in '..\..\CryptoLib\src\Interfaces\ClpISecAsn1Objects.pas',
+  ClpSecAsn1Objects in '..\..\CryptoLib\src\Asn1\Sec\ClpSecAsn1Objects.pas',
+  ClpIPAddressUtilities in '..\..\CryptoLib\src\Utils\ClpIPAddressUtilities.pas',
+  ClpRfc5280Asn1Utilities in '..\..\CryptoLib\src\Asn1\X509\ClpRfc5280Asn1Utilities.pas',
+  ClpIetfUtilities in '..\..\CryptoLib\src\Asn1\X500\ClpIetfUtilities.pas',
+  ClpIMiscPemGenerator in '..\..\CryptoLib\src\Interfaces\ClpIMiscPemGenerator.pas',
+  ClpIPemObjects in '..\..\CryptoLib\src\Interfaces\ClpIPemObjects.pas',
+  ClpPemObjects in '..\..\CryptoLib\src\Utils\Pem\ClpPemObjects.pas',
+  ClpIX509NameTokenizer in '..\..\CryptoLib\src\Interfaces\ClpIX509NameTokenizer.pas',
+  ClpCryptoLibComparers in '..\..\CryptoLib\src\Utils\ClpCryptoLibComparers.pas',
+  ClpX509ExtensionUtilities in '..\..\CryptoLib\src\Asn1\X509\ClpX509ExtensionUtilities.pas',
+  ClpX509Certificate in '..\..\CryptoLib\src\Asn1\X509\ClpX509Certificate.pas',
+  ClpX509ExtensionBase in '..\..\CryptoLib\src\Asn1\X509\ClpX509ExtensionBase.pas',
+  ClpX509SignatureUtilities in '..\..\CryptoLib\src\Asn1\X509\ClpX509SignatureUtilities.pas',
+  ClpX509Utilities in '..\..\CryptoLib\src\Asn1\X509\ClpX509Utilities.pas',
+  ClpAsn1Dumper in '..\..\CryptoLib\src\Asn1\ClpAsn1Dumper.pas',
   ClpFixedSecureRandom in '..\src\Utils\ClpFixedSecureRandom.pas',
   ClpIFixedSecureRandom in '..\src\Utils\ClpIFixedSecureRandom.pas',
   ClpIShortenedDigest in '..\src\Utils\ClpIShortenedDigest.pas',
@@ -518,6 +567,16 @@ uses
   RSADigestSignerTests in '..\src\Crypto\RSADigestSignerTests.pas',
   ISO9796Tests in '..\src\Crypto\ISO9796Tests.pas',
   PssTests in '..\src\Crypto\PssTests.pas',
+  GeneralNameTests in '..\src\Asn1\X509\GeneralNameTests.pas',
+  KeyUsageTests in '..\src\Asn1\X509\KeyUsageTests.pas',
+  SubjectKeyIdentifierTests in '..\src\Asn1\X509\SubjectKeyIdentifierTests.pas',
+  X509AltTests in '..\src\Asn1\X509\X509AltTests.pas',
+  X509ExtensionsTests in '..\src\Asn1\X509\X509ExtensionsTests.pas',
+  X509NameTests in '..\src\Asn1\X509\X509NameTests.pas',
+  Pkcs10CertRequestTests in '..\src\Asn1\PKCS\Pkcs10CertRequestTests.pas',
+  PrivateKeyInfoTests in '..\src\Asn1\PKCS\PrivateKeyInfoTests.pas',
+  IPAddressUtilitiesTests in '..\src\Utils\Net\IPAddressUtilitiesTests.pas',
+  PemReaderTests in '..\src\Utils\Pem\PemReaderTests.pas',
   CryptoLibTestBase in '..\src\CryptoLibTestBase.pas';
 
 begin

+ 6 - 6
CryptoLib.Tests/src/Asn1/BitStringTests.pas

@@ -33,6 +33,8 @@ uses
 {$ENDIF FPC}
   ClpAsn1Objects,
   ClpIAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
   ClpCryptoLibTypes,
   CryptoLibTestBase;
 
@@ -43,7 +45,7 @@ type
     procedure EncodingCheck(const ADerData, ADlData: TCryptoLibByteArray);
 
   published
-    //procedure TestBitString;
+    procedure TestBitString;
     procedure TestZeroLengthStrings;
     procedure TestRandomPadBits;
 
@@ -144,11 +146,10 @@ begin
   CheckTrue(Supports(LDer, IDerBitString), 'DER test failed');
 end;
 
-(*
+
 procedure TBitStringTest.TestBitString;
 var
   LK: IKeyUsage;
-  LEmptyBytes: TCryptoLibByteArray;
 begin
   LK := TKeyUsage.Create(TKeyUsage.DigitalSignature);
   if (LK.GetBytes()[0] <> Byte(TKeyUsage.DigitalSignature)) or
@@ -185,9 +186,8 @@ begin
     Fail('failed decipherOnly');
   end;
 
-  System.SetLength(LEmptyBytes, 0);
   try
-    TAsn1Object.FromByteArray((TDerBitString.Create(LEmptyBytes, 0) as IDerBitString).GetEncoded());
+    TAsn1Object.FromByteArray((TDerBitString.CreateEmpty as IDerBitString).GetEncoded());
   except
     on E: Exception do
     begin
@@ -195,7 +195,7 @@ begin
     end;
   end;
 
-end; *)
+end;
 
 initialization
 

+ 129 - 0
CryptoLib.Tests/src/Asn1/PKCS/Pkcs10CertRequestTests.pas

@@ -0,0 +1,129 @@
+{ *********************************************************************************** }
+{ *                              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 Pkcs10CertRequestTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpPkcsAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TPkcs10CertRequestTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FReq1: TCryptoLibByteArray;
+      FReq2: TCryptoLibByteArray;
+
+    procedure SetUpTestData;
+    procedure BasicPkcs10Test(const ATestName: String; const AReq: TCryptoLibByteArray);
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestBasicCR;
+    procedure TestUniversalCR;
+
+  end;
+
+implementation
+
+{ TPkcs10CertRequestTest }
+
+procedure TPkcs10CertRequestTest.SetUpTestData;
+begin
+  FReq1 := DecodeBase64('MIHoMIGTAgEAMC4xDjAMBgNVBAMTBVRlc3QyMQ8wDQYDVQQKEwZBbmFUb20xCzAJBgNVBAYTAlNF' +
+    'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALlEt31Tzt2MlcOljvacJgzQVhmlMoqAOgqJ9Pgd3Gux' +
+    'Z7/WcIlgW4QCB7WZT21O1YoghwBhPDMcNGrHei9kHQkCAwEAAaAAMA0GCSqGSIb3DQEBBQUAA0EA' +
+    'NDEI4ecNtJ3uHwGGlitNFq9WxcoZ0djbQJ5hABMotav6gtqlrwKXY2evaIrsNwkJtNdwwH18aQDU' +
+    'KCjOuBL38Q==');
+
+  FReq2 := DecodeBase64('MIIB6TCCAVICAQAwgagxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRQwEgYDVQQH' +
+    'EwtTYW50YSBDbGFyYTEMMAoGA1UEChMDQUJCMVEwTwYDVQQLHEhQAAAAAAAAAG8AAAAAAAAAdwAA' +
+    'AAAAAABlAAAAAAAAAHIAAAAAAAAAIAAAAAAAAABUAAAAAAAAABxIAAAAAAAARAAAAAAAAAAxDTAL' +
+    'BgNVBAMTBGJsdWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANETRZ+6occCOrFxNhfKIp4C' +
+    'mMkxwhBNb7TnnahpbM9O0r4hrBPcfYuL7u9YX/jN0YNUP+/CiT39HhSe/bikaBPDEyNsl988I8vX' +
+    'piEdgxYq/+LTgGHbjRsRYCkPtmzwBbuBldNF8bV7pu0v4UScSsExmGqqDlX1TbPU8KkPU1iTAgMB' +
+    'AAGgADANBgkqhkiG9w0BAQQFAAOBgQAFbrs9qUwh93CtETk7DeUD5HcdCnxauo1bck44snSV6MZV' +
+    'OCIGaYu1501kmhEvAtVVRr6SEHwimfQDDIjnrWwYsEr/DT6tkTZAbfRd3qUu3iKjT0H0vlUZp0hJ' +
+    '66mINtBM84uZFBfoXiWY8M3FuAnGmvy6ah/dYtJorTxLKiGkew==');
+end;
+
+procedure TPkcs10CertRequestTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TPkcs10CertRequestTest.BasicPkcs10Test(const ATestName: String; const AReq: TCryptoLibByteArray);
+var
+  LCertReq: ICertificationRequest;
+  LBytes: TCryptoLibByteArray;
+begin
+  try
+    LCertReq := TCertificationRequest.GetInstance(AReq);
+
+    LBytes := LCertReq.GetDerEncoded();
+
+    if not AreEqual(LBytes, AReq) then
+    begin
+      Fail(Format('Pkcs10: %s failed comparison test', [ATestName]));
+    end;
+  except
+    on E: Exception do
+    begin
+      Fail(Format('Pkcs10: Exception - %s %s', [ATestName, E.Message]));
+    end;
+  end;
+end;
+
+procedure TPkcs10CertRequestTest.TestBasicCR;
+begin
+  BasicPkcs10Test('Basic CR', FReq1);
+end;
+
+procedure TPkcs10CertRequestTest.TestUniversalCR;
+begin
+  BasicPkcs10Test('Universal CR', FReq2);
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TPkcs10CertRequestTest);
+{$ELSE}
+RegisterTest(TPkcs10CertRequestTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 111 - 0
CryptoLib.Tests/src/Asn1/PKCS/PrivateKeyInfoTests.pas

@@ -0,0 +1,111 @@
+{ *********************************************************************************** }
+{ *                              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 PrivateKeyInfoTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpIAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TPrivateKeyInfoTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FPriv: TCryptoLibByteArray;
+      FPrivWithPub: TCryptoLibByteArray;
+
+    procedure SetUpTestData;
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestFunction;
+
+  end;
+
+implementation
+
+{ TPrivateKeyInfoTest }
+
+procedure TPrivateKeyInfoTest.SetUpTestData;
+begin
+  FPriv := DecodeBase64('MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC');
+
+  FPrivWithPub := DecodeBase64('MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC' +
+    'oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB' +
+    'Z9w7lshQhqowtrbLDFw4rXAxZuE=');
+end;
+
+procedure TPrivateKeyInfoTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TPrivateKeyInfoTest.TestFunction;
+var
+  LPrivInfo1, LPrivInfo2: IPrivateKeyInfo;
+  LPublicKey: IDerBitString;
+begin
+  LPrivInfo1 := TPrivateKeyInfo.GetInstance(FPriv);
+
+  CheckFalse(LPrivInfo1.HasPublicKey, 'PrivateKeyInfo should not have public key');
+
+  LPrivInfo2 := TPrivateKeyInfo.Create(LPrivInfo1.PrivateKeyAlgorithm, LPrivInfo1.ParsePrivateKey());
+
+  CheckTrue(AreEqual(FPriv, LPrivInfo2.GetEncoded()), 'enc 1 failed');
+
+  LPrivInfo1 := TPrivateKeyInfo.GetInstance(FPrivWithPub);
+
+  CheckTrue(LPrivInfo1.HasPublicKey, 'PrivateKeyInfo should have public key');
+
+  LPublicKey := LPrivInfo1.PublicKey;
+
+  LPrivInfo2 := TPrivateKeyInfo.Create(LPrivInfo1.PrivateKeyAlgorithm, LPrivInfo1.ParsePrivateKey(),
+    LPrivInfo1.Attributes, LPublicKey.GetOctets());
+
+  CheckTrue(AreEqual(FPrivWithPub, LPrivInfo2.GetEncoded()), 'enc 2 failed');
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TPrivateKeyInfoTest);
+{$ELSE}
+RegisterTest(TPrivateKeyInfoTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 136 - 0
CryptoLib.Tests/src/Asn1/X509/GeneralNameTests.pas

@@ -0,0 +1,136 @@
+{ *********************************************************************************** }
+{ *                              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 GeneralNameTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TGeneralNameTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FIpv4: TCryptoLibByteArray;
+      FIpv4WithMask24: TCryptoLibByteArray;
+      FIpv4WithMask14: TCryptoLibByteArray;
+      FIpv6a: TCryptoLibByteArray;
+      FIpv6b: TCryptoLibByteArray;
+      FIpv6c: TCryptoLibByteArray;
+      FIpv6d: TCryptoLibByteArray;
+      FIpv6e: TCryptoLibByteArray;
+      FIpv6f: TCryptoLibByteArray;
+      FIpv6g: TCryptoLibByteArray;
+      FIpv6h: TCryptoLibByteArray;
+
+    procedure SetUpTestData;
+    procedure CheckIPAddressEncoding(const AInputIP: String;
+      const AExpectedEncoding: TCryptoLibByteArray; const AMessage: String);
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestIPv4;
+    procedure TestIPv6;
+
+  end;
+
+implementation
+
+{ TGeneralNameTest }
+
+procedure TGeneralNameTest.SetUpTestData;
+begin
+  FIpv4 := DecodeHex('87040a090800');
+  FIpv4WithMask24 := DecodeHex('87080a090800ffffff00');
+  FIpv4WithMask14 := DecodeHex('87080a090800fffc0000');
+
+  FIpv6a := DecodeHex('871020010db885a308d313198a2e03707334');
+  FIpv6b := DecodeHex('871020010db885a3000013198a2e03707334');
+  FIpv6c := DecodeHex('871000000000000000000000000000000001');
+  FIpv6d := DecodeHex('871020010db885a3000000008a2e03707334');
+  FIpv6e := DecodeHex('871020010db885a3000000008a2e0a090800');
+  FIpv6f := DecodeHex('872020010db885a3000000008a2e0a090800ffffffffffff00000000000000000000');
+  FIpv6g := DecodeHex('872020010db885a3000000008a2e0a090800ffffffffffffffffffffffffffffffff');
+  FIpv6h := DecodeHex('872020010db885a300000000000000000000ffffffffffff00000000000000000000');
+end;
+
+procedure TGeneralNameTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TGeneralNameTest.CheckIPAddressEncoding(const AInputIP: String;
+  const AExpectedEncoding: TCryptoLibByteArray; const AMessage: String);
+var
+  LGeneralName: IGeneralName;
+  LEncoded: TCryptoLibByteArray;
+begin
+  LGeneralName := TGeneralName.Create(TGeneralName.IPAddress, AInputIP);
+  LEncoded := LGeneralName.GetEncoded();
+  CheckTrue(AreEqual(AExpectedEncoding, LEncoded), AMessage);
+end;
+
+procedure TGeneralNameTest.TestIPv4;
+begin
+  CheckIPAddressEncoding('10.9.8.0', FIpv4, 'ipv4 encoding failed');
+  CheckIPAddressEncoding('10.9.8.0/255.255.255.0', FIpv4WithMask24, 'ipv4 with netmask 1 encoding (24bit) failed');
+  CheckIPAddressEncoding('10.9.8.0/24', FIpv4WithMask24, 'ipv4 with netmask 2 encoding (24bit) failed');
+  CheckIPAddressEncoding('10.9.8.0/255.252.0.0', FIpv4WithMask14, 'ipv4 with netmask 1 encoding (14bit) failed');
+  CheckIPAddressEncoding('10.9.8.0/14', FIpv4WithMask14, 'ipv4 with netmask 2 encoding (14bit) failed');
+end;
+
+procedure TGeneralNameTest.TestIPv6;
+begin
+  CheckIPAddressEncoding('2001:0db8:85a3:08d3:1319:8a2e:0370:7334', FIpv6a, 'ipv6a failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::1319:8a2e:0370:7334', FIpv6b, 'ipv6b failed');
+  CheckIPAddressEncoding('::1', FIpv6c, 'ipv6c failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::8a2e:0370:7334', FIpv6d, 'ipv6d failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::8a2e:10.9.8.0', FIpv6e, 'ipv6e failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::8a2e:10.9.8.0/ffff:ffff:ffff::0000', FIpv6f, 'ipv6f failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::8a2e:10.9.8.0/128', FIpv6g, 'ipv6g failed');
+  CheckIPAddressEncoding('2001:0db8:85a3::/48', FIpv6h, 'ipv6h failed');
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TGeneralNameTest);
+{$ELSE}
+RegisterTest(TGeneralNameTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 130 - 0
CryptoLib.Tests/src/Asn1/X509/KeyUsageTests.pas

@@ -0,0 +1,130 @@
+{ *********************************************************************************** }
+{ *                              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 KeyUsageTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  CryptoLibTestBase;
+
+type
+
+  TKeyUsageTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    procedure TestFlagValueCorrect(ABitNo: Int32; AValue: Int32);
+
+  published
+    procedure TestDigitalSignature;
+    procedure TestNonRepudiation;
+    procedure TestKeyEncipherment;
+    procedure TestDataEncipherment;
+    procedure TestKeyAgreement;
+    procedure TestKeyCertSign;
+    procedure TestCrlSign;
+    procedure TestEncipherOnly;
+    procedure TestDecipherOnly;
+
+  end;
+
+implementation
+
+{ TKeyUsageTest }
+
+procedure TKeyUsageTest.TestFlagValueCorrect(ABitNo: Int32; AValue: Int32);
+const
+  // bits array with bit values
+  Bits: array[0..31] of Int32 = (
+    (1 shl 7), (1 shl 6), (1 shl 5), (1 shl 4), (1 shl 3), (1 shl 2), (1 shl 1), (1 shl 0),
+    (1 shl 15), (1 shl 14), (1 shl 13), (1 shl 12), (1 shl 11), (1 shl 10), (1 shl 9), (1 shl 8),
+    (1 shl 23), (1 shl 22), (1 shl 21), (1 shl 20), (1 shl 19), (1 shl 18), (1 shl 17), (1 shl 16),
+    (1 shl 31), (1 shl 30), (1 shl 29), (1 shl 28), (1 shl 27), (1 shl 26), (1 shl 25), (1 shl 24)
+  );
+begin
+  if Bits[ABitNo] <> AValue then
+  begin
+    Fail(Format('bit value %d wrong', [ABitNo]));
+  end;
+end;
+
+procedure TKeyUsageTest.TestDigitalSignature;
+begin
+  TestFlagValueCorrect(0, TKeyUsage.DigitalSignature);
+end;
+
+procedure TKeyUsageTest.TestNonRepudiation;
+begin
+  TestFlagValueCorrect(1, TKeyUsage.NonRepudiation);
+end;
+
+procedure TKeyUsageTest.TestKeyEncipherment;
+begin
+  TestFlagValueCorrect(2, TKeyUsage.KeyEncipherment);
+end;
+
+procedure TKeyUsageTest.TestDataEncipherment;
+begin
+  TestFlagValueCorrect(3, TKeyUsage.DataEncipherment);
+end;
+
+procedure TKeyUsageTest.TestKeyAgreement;
+begin
+  TestFlagValueCorrect(4, TKeyUsage.KeyAgreement);
+end;
+
+procedure TKeyUsageTest.TestKeyCertSign;
+begin
+  TestFlagValueCorrect(5, TKeyUsage.KeyCertSign);
+end;
+
+procedure TKeyUsageTest.TestCrlSign;
+begin
+  TestFlagValueCorrect(6, TKeyUsage.CrlSign);
+end;
+
+procedure TKeyUsageTest.TestEncipherOnly;
+begin
+  TestFlagValueCorrect(7, TKeyUsage.EncipherOnly);
+end;
+
+procedure TKeyUsageTest.TestDecipherOnly;
+begin
+  TestFlagValueCorrect(8, TKeyUsage.DecipherOnly);
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TKeyUsageTest);
+{$ELSE}
+RegisterTest(TKeyUsageTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 109 - 0
CryptoLib.Tests/src/Asn1/X509/SubjectKeyIdentifierTests.pas

@@ -0,0 +1,109 @@
+{ *********************************************************************************** }
+{ *                              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 SubjectKeyIdentifierTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509ExtensionUtilities,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TSubjectKeyIdentifierTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FPubKeyInfo: TCryptoLibByteArray;
+      FShaID: TCryptoLibByteArray;
+      FShaTruncID: TCryptoLibByteArray;
+
+    procedure SetUpTestData;
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestCreateSubjectKeyIdentifier;
+
+  end;
+
+implementation
+
+{ TSubjectKeyIdentifierTest }
+
+procedure TSubjectKeyIdentifierTest.SetUpTestData;
+begin
+  FPubKeyInfo := DecodeBase64('MFgwCwYJKoZIhvcNAQEBA0kAMEYCQQC6wMMmHYMZszT/7bNFMn+gaZoiWJLVP8ODRuu1C2jeAe' +
+    'QpxM+5Oe7PaN2GNy3nBE4EOYkB5pMJWA0y9n04FX8NAgED');
+
+  FShaID := DecodeHex('d8128a06d6c2feb0865994a2936e7b75b836a021');
+
+  FShaTruncID := DecodeHex('436e7b75b836a021');
+end;
+
+procedure TSubjectKeyIdentifierTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TSubjectKeyIdentifierTest.TestCreateSubjectKeyIdentifier;
+var
+  LPubInfo: ISubjectPublicKeyInfo;
+  LSki: ISubjectKeyIdentifier;
+begin
+  LPubInfo := TSubjectPublicKeyInfo.GetInstance(FPubKeyInfo);
+  LSki := TX509ExtensionUtilities.CreateSubjectKeyIdentifier(LPubInfo);
+
+  if not AreEqual(FShaID, LSki.GetKeyIdentifier()) then
+  begin
+    Fail('SHA-1 ID does not match');
+  end;
+
+  LSki := TX509ExtensionUtilities.CreateTruncatedSubjectKeyIdentifier(LPubInfo);
+
+  if not AreEqual(FShaTruncID, LSki.GetKeyIdentifier()) then
+  begin
+    Fail('truncated SHA-1 ID does not match');
+  end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TSubjectKeyIdentifierTest);
+{$ELSE}
+RegisterTest(TSubjectKeyIdentifierTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 119 - 0
CryptoLib.Tests/src/Asn1/X509/X509AltTests.pas

@@ -0,0 +1,119 @@
+{ *********************************************************************************** }
+{ *                              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 X509AltTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509ExtensionsGenerator,
+  ClpIX509ExtensionsGenerator,
+  ClpPkcsObjectIdentifiers,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TX509AltTest = class(TCryptoLibAlgorithmTestCase)
+  published
+    procedure TestX509AltTypes;
+
+  end;
+
+implementation
+
+{ TX509AltTest }
+
+procedure TX509AltTest.TestX509AltTypes;
+var
+  LSubAlt: ISubjectAltPublicKeyInfo;
+  LSigValAlt: IAltSignatureValue;
+  LSigAlgAlt, LSigAlgAlt2: IAltSignatureAlgorithm;
+  LExtGen: IX509ExtensionsGenerator;
+  LExts: IX509Extensions;
+  LBytes: TCryptoLibByteArray;
+begin
+  LBytes := DecodeHex('0102030405060708090807060504030201');
+  LSubAlt := TSubjectAltPublicKeyInfo.Create(
+    TAlgorithmIdentifier.Create(TPkcsObjectIdentifiers.RsaEncryption, TDerNull.Instance),
+    TDerBitString.Create(LBytes));
+
+  LSigValAlt := TAltSignatureValue.Create(LBytes);
+
+  LSigAlgAlt := TAltSignatureAlgorithm.Create(
+    TAlgorithmIdentifier.Create(TPkcsObjectIdentifiers.MD5WithRsaEncryption, TDerNull.Instance));
+
+  LSigAlgAlt2 := TAltSignatureAlgorithm.Create(
+    TPkcsObjectIdentifiers.MD5WithRsaEncryption, TDerNull.Instance);
+
+  CheckTrue(LSigAlgAlt.Equals(LSigAlgAlt2), 'AltSignatureAlgorithm instances should be equal');
+
+  LExtGen := TX509ExtensionsGenerator.Create();
+
+  LExtGen.AddExtension(TX509Extensions.SubjectAltPublicKeyInfo, False, LSubAlt);
+
+  LExtGen.AddExtension(TX509Extensions.AltSignatureAlgorithm, False, LSigAlgAlt);
+
+  LExtGen.AddExtension(TX509Extensions.AltSignatureValue, False, LSigValAlt);
+
+
+  LExts := LExtGen.Generate();
+
+  CheckTrue(LSubAlt.Equals(TSubjectAltPublicKeyInfo.FromExtensions(LExts)), 'SubjectAltPublicKeyInfo from extensions should match');
+
+  CheckTrue(LSigAlgAlt.Equals(TAltSignatureAlgorithm.FromExtensions(LExts)), 'AltSignatureAlgorithm from extensions should match');
+
+  CheckTrue(LSigValAlt.Equals(TAltSignatureValue.FromExtensions(LExts)), 'AltSignatureValue from extensions should match');
+
+  CheckTrue(LSubAlt.Equals(TSubjectAltPublicKeyInfo.GetInstance(LSubAlt.GetEncoded())), 'SubjectAltPublicKeyInfo round-trip should match');
+
+  CheckTrue(LSigAlgAlt.Equals(TAltSignatureAlgorithm.GetInstance(LSigAlgAlt.GetEncoded())), 'AltSignatureAlgorithm round-trip should match');
+
+  CheckTrue(LSigValAlt.Equals(TAltSignatureValue.GetInstance(LSigValAlt.GetEncoded())), 'AltSignatureValue round-trip should match');
+
+  CheckTrue(LSubAlt.Equals(TSubjectAltPublicKeyInfo.GetInstance(TDerTaggedObject.Create(1, LSubAlt), True)), 'SubjectAltPublicKeyInfo from tagged should match');
+
+  CheckTrue(LSigAlgAlt.Equals(TAltSignatureAlgorithm.GetInstance(TDerTaggedObject.Create(1, LSigAlgAlt), True)), 'AltSignatureAlgorithm from tagged should match');
+
+  CheckTrue(LSigValAlt.Equals(TAltSignatureValue.GetInstance(TDerTaggedObject.Create(1, LSigValAlt), True)), 'AltSignatureValue from tagged should match');
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TX509AltTest);
+{$ELSE}
+RegisterTest(TX509AltTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 254 - 0
CryptoLib.Tests/src/Asn1/X509/X509ExtensionsTests.pas

@@ -0,0 +1,254 @@
+{ *********************************************************************************** }
+{ *                              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 X509ExtensionsTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509ExtensionsGenerator,
+  ClpIX509ExtensionsGenerator,
+  ClpIX509Extension,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TX509ExtensionsTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FOid1, FOid2, FOid3: IDerObjectIdentifier;
+
+    procedure SetUpTestData;
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestDuplicateExtensions;
+    procedure TestAllowedDuplicateExtensions;
+    procedure TestFunction;
+
+  end;
+
+implementation
+
+{ TX509ExtensionsTest }
+
+procedure TX509ExtensionsTest.SetUpTestData;
+begin
+  FOid1 := TDerObjectIdentifier.Create('1.2.1');
+  FOid2 := TDerObjectIdentifier.Create('1.2.2');
+  FOid3 := TDerObjectIdentifier.Create('1.2.3');
+end;
+
+procedure TX509ExtensionsTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TX509ExtensionsTest.TestDuplicateExtensions;
+var
+  LName1, LName2: IGeneralName;
+  LExtGen, LGenX: IX509ExtensionsGenerator;
+  LExts: IX509Extensions;
+  LReturnedExtension: IX509Extension;
+  LSeq: IAsn1Sequence;
+  LBytes: TCryptoLibByteArray;
+begin
+  LName1 := TGeneralName.Create(TGeneralName.DnsName, 'bc1.local');
+
+  LName2 := TGeneralName.Create(TGeneralName.DnsName, 'bc2.local');
+
+  LExtGen := TX509ExtensionsGenerator.Create();
+
+  LExtGen.AddExtension(TX509Extensions.SubjectAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.SubjectAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+
+  LBytes := LExtGen.Generate().GetEncoded();
+  LExts := TX509Extensions.GetInstance(TAsn1Sequence.GetInstance(LBytes) as TObject);
+
+  LReturnedExtension := LExts.GetExtension(TX509Extensions.SubjectAlternativeName);
+
+  LSeq := TAsn1Sequence.GetInstance(LReturnedExtension.GetParsedValue());
+
+  CheckTrue(TGeneralName.GetInstance(LSeq[0] as TObject).Equals(LName1), 'expected name 1');
+
+  CheckTrue(TGeneralName.GetInstance(LSeq[1] as TObject).Equals(LName2), 'expected name 2');
+
+  LGenX := TX509ExtensionsGenerator.Create();
+
+  LGenX.AddExtensions(LExts);
+
+  LBytes := LGenX.Generate().GetEncoded();
+  LExts := TX509Extensions.GetInstance(TAsn1Sequence.GetInstance(LBytes) as TObject);
+
+  LReturnedExtension := LExts.GetExtension(TX509Extensions.SubjectAlternativeName);
+
+  LSeq := TAsn1Sequence.GetInstance(LReturnedExtension.GetParsedValue());
+
+  CheckTrue(TGeneralName.GetInstance(LSeq[0] as TObject).Equals(LName1), 'expected name 1');
+
+  CheckTrue(TGeneralName.GetInstance(LSeq[1] as TObject).Equals(LName2), 'expected name 2');
+end;
+
+procedure TX509ExtensionsTest.TestAllowedDuplicateExtensions;
+var
+  LName1, LName2: IGeneralName;
+  LExtGen: IX509ExtensionsGenerator;
+begin
+  LName1 := TGeneralName.Create(TGeneralName.DnsName, 'bc1.local');
+
+  LName2 := TGeneralName.Create(TGeneralName.DnsName, 'bc2.local');
+
+  LExtGen := TX509ExtensionsGenerator.Create();
+
+  LExtGen.AddExtension(TX509Extensions.SubjectAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.SubjectAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.IssuerAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.IssuerAlternativeName, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.SubjectDirectoryAttributes, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.SubjectDirectoryAttributes, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.CertificateIssuer, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.CertificateIssuer, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+
+  LExtGen.AddExtension(TX509Extensions.AuditIdentity, False,
+    TDerSequence.Create(TAsn1EncodableVector.Create([LName1]) as IAsn1EncodableVector) as IDerSequence);
+
+  try
+    LExtGen.AddExtension(TX509Extensions.AuditIdentity, False,
+      TDerSequence.Create(TAsn1EncodableVector.Create([LName2]) as IAsn1EncodableVector) as IDerSequence);
+    Fail('Expected exception, not a white listed duplicate.');
+  except
+    // ok - expected exception
+  end;
+end;
+
+procedure TX509ExtensionsTest.TestFunction;
+var
+  LGen: IX509ExtensionsGenerator;
+  LExt1, LExt2: IX509Extensions;
+  LBytes20, LBytes22: TCryptoLibByteArray;
+begin
+  LGen := TX509ExtensionsGenerator.Create();
+
+  System.SetLength(LBytes20, 20);
+
+  LGen.AddExtension(FOid1, True, LBytes20);
+
+  LGen.AddExtension(FOid2, True, LBytes20);
+
+  LExt1 := LGen.Generate();
+
+  LExt2 := LGen.Generate();
+
+  CheckTrue(LExt1.Equals(LExt2), 'Equals test failed');
+
+  LGen.Reset();
+
+  LGen.AddExtension(FOid2, True, LBytes20);
+
+  LGen.AddExtension(FOid1, True, LBytes20);
+
+  LExt2 := LGen.Generate();
+
+  CheckFalse(LExt1.Equals(LExt2), 'inequality test failed');
+
+  CheckTrue(LExt1.Equivalent(LExt2), 'equivalence true failed');
+
+  LGen.Reset();
+
+  System.SetLength(LBytes22, 22);
+
+  LGen.AddExtension(FOid1, True, LBytes22);
+
+  LGen.AddExtension(FOid2, True, LBytes20);
+
+  LExt2 := LGen.Generate();
+
+  CheckFalse(LExt1.Equals(LExt2), 'inequality 1 failed');
+
+  CheckFalse(LExt1.Equivalent(LExt2), 'non-equivalence 1 failed');
+
+  LGen.Reset();
+
+  LGen.AddExtension(FOid3, True, LBytes20);
+
+  LGen.AddExtension(FOid2, True, LBytes20);
+
+  LExt2 := LGen.Generate();
+
+  CheckFalse(LExt1.Equals(LExt2), 'inequality 2 failed');
+
+  CheckFalse(LExt1.Equivalent(LExt2), 'non-equivalence 2 failed');
+
+  try
+    LGen.AddExtension(FOid2, True, LBytes20);
+    Fail('repeated oid');
+  except
+    on E: EArgumentCryptoLibException do
+    begin
+      if E.Message <> 'extension 1.2.2 already added' then
+        Fail(Format('wrong exception on repeated oid: %s', [E.Message]));
+    end;
+  end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TX509ExtensionsTest);
+{$ELSE}
+RegisterTest(TX509ExtensionsTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 202 - 0
CryptoLib.Tests/src/Asn1/X509/X509NameTests.pas

@@ -0,0 +1,202 @@
+{ *********************************************************************************** }
+{ *                              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 X509NameTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+  Generics.Collections,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpArrayUtils,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TX509NameTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FSubjects: TCryptoLibStringArray;
+      FHexSubjects: TCryptoLibStringArray;
+
+    procedure SetUpTestData;
+    function FromBytes(const ABytes: TCryptoLibByteArray): IX509Name;
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestBasicEncoding;
+    procedure TestRegeneration;
+    procedure TestHexRegeneration;
+    procedure TestEquality;
+
+  end;
+
+implementation
+
+{ TX509NameTest }
+
+procedure TX509NameTest.SetUpTestData;
+begin
+  System.SetLength(FSubjects, 12);
+  FSubjects[0] := 'C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Webserver Team,CN=www2.connect4.com.au,[email protected]';
+  FSubjects[1] := 'C=AU,ST=Victoria,L=South Melbourne,O=Connect 4 Pty Ltd,OU=Certificate Authority,CN=Connect 4 CA,[email protected]';
+  FSubjects[2] := 'C=AU,ST=QLD,CN=SSLeay/rsa test cert';
+  FSubjects[3] := 'C=US,O=National Aeronautics and Space Administration,SERIALNUMBER=16+CN=Steve Schoch';
+  FSubjects[4] := '[email protected],C=US,OU=Hewlett Packard Company (ISSL),CN=Paul A. Cooke';
+  FSubjects[5] := 'O=Sun Microsystems Inc,CN=store.sun.com';
+  FSubjects[6] := 'unstructuredAddress=192.168.1.33,unstructuredName=pixfirewall.ciscopix.com,CN=pixfirewall.ciscopix.com';
+  FSubjects[7] := 'CN=*.canal-plus.com,OU=Provided by TBS INTERNET https://www.tbs-certificats.com/,OU=\ CANAL \+,O=CANAL\+DISTRIBUTION,L=issy les moulineaux,ST=Hauts de Seine,C=FR';
+  FSubjects[8] := 'O=Bouncy Castle,CN=www.bouncycastle.org\ ';
+  FSubjects[9] := 'O=Bouncy Castle,CN=c:\\fred\\bob';
+  FSubjects[10] := 'C=0,O=1,OU=2,T=3,CN=4,SERIALNUMBER=5,STREET=6,SERIALNUMBER=7,L=8,ST=9,SURNAME=10,GIVENNAME=11,INITIALS=12,' +
+    'GENERATION=13,UniqueIdentifier=14,BusinessCategory=15,PostalCode=16,DN=17,Pseudonym=18,PlaceOfBirth=19,' +
+    'Gender=20,CountryOfCitizenship=21,CountryOfResidence=22,NameAtBirth=23,PostalAddress=24,2.5.4.54=25,' +
+    'TelephoneNumber=26,Name=27,E=28,unstructuredName=29,unstructuredAddress=30,E=31,DC=32,UID=33';
+  FSubjects[11] := 'C=DE,L=Berlin,O=Wohnungsbaugenossenschaft \"Humboldt-Universität\" eG,CN=transfer.wbg-hub.de';
+
+  System.SetLength(FHexSubjects, 4);
+  FHexSubjects[0] := 'CN=\20Test\20X,O=\20Test,C=GB';         // input
+  FHexSubjects[1] := 'CN=\ Test X,O=\ Test,C=GB';              // expected
+  FHexSubjects[2] := 'CN=\20Test\20X\20,O=\20Test,C=GB';     // input
+  FHexSubjects[3] := 'CN=\ Test X\ ,O=\ Test,C=GB';           // expected
+end;
+
+procedure TX509NameTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+function TX509NameTest.FromBytes(const ABytes: TCryptoLibByteArray): IX509Name;
+begin
+  Result := TX509Name.GetInstance(ABytes);
+end;
+
+procedure TX509NameTest.TestBasicEncoding;
+var
+  LName1, LName2: IX509Name;
+begin
+  LName1 := TX509Name.Create('CN=Test');
+  LName2 := FromBytes(LName1.GetEncoded());
+  CheckTrue(LName1.Equivalent(LName2), 'Basic encoding test failed');
+end;
+
+procedure TX509NameTest.TestRegeneration;
+var
+  I: Int32;
+  LSubject: String;
+  LName: IX509Name;
+  LDecodedName: IX509Name;
+  LDecodedSubject: String;
+begin
+  for I := 0 to System.Length(FSubjects) - 1 do
+  begin
+    LSubject := FSubjects[I];
+
+    LName := TX509Name.Create(LSubject);
+
+    LDecodedName := FromBytes(LName.GetEncoded());
+
+    LDecodedSubject := LDecodedName.ToString();
+
+    if not LSubject.Equals(LDecodedSubject) then
+    begin
+      Fail(Format('Failed regeneration test %d got: %s expected %s', [I, LDecodedSubject, LSubject]));
+    end;
+  end;
+end;
+
+procedure TX509NameTest.TestHexRegeneration;
+var
+  I: Int32;
+  LSubject, LExpected: String;
+  LName: IX509Name;
+  LDecodedName: IX509Name;
+  LDecodedSubject: String;
+begin
+  I := 0;
+  while I < System.Length(FHexSubjects) do
+  begin
+    LSubject := FHexSubjects[I];
+    LExpected := FHexSubjects[I + 1];
+
+    LName := TX509Name.Create(LSubject);
+
+    LDecodedName := FromBytes(LName.GetEncoded());
+
+    LDecodedSubject := LDecodedName.ToString();
+
+    if not LExpected.Equals(LDecodedSubject) then
+    begin
+      Fail(Format('Failed hex regeneration test %d got: %s expected %s', [I, LDecodedSubject, LExpected]));
+    end;
+
+    System.Inc(I, 2);
+  end;
+end;
+
+procedure TX509NameTest.TestEquality;
+var
+  LName1, LName2: IX509Name;
+begin
+  LName1 := TX509Name.Create('CN=The     Legion');
+  LName2 := TX509Name.Create('CN=The Legion');
+  CheckTrue(LName1.Equivalent(LName2), 'Equality test 1 failed');
+
+  LName1 := TX509Name.Create('CN=   The Legion');
+  LName2 := TX509Name.Create('CN=The Legion');
+  CheckTrue(LName1.Equivalent(LName2), 'Equality test 2 failed');
+
+  LName1 := TX509Name.Create('CN=The Legion   ');
+  LName2 := TX509Name.Create('CN=The Legion');
+  CheckTrue(LName1.Equivalent(LName2), 'Equality test 3 failed');
+
+  LName1 := TX509Name.Create('CN=  The     Legion ');
+  LName2 := TX509Name.Create('CN=The Legion');
+  CheckTrue(LName1.Equivalent(LName2), 'Equality test 4 failed');
+
+  LName1 := TX509Name.Create('CN=  the     legion ');
+  LName2 := TX509Name.Create('CN=The Legion');
+  CheckTrue(LName1.Equivalent(LName2), 'Equality test 5 failed');
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TX509NameTest);
+{$ELSE}
+RegisterTest(TX509NameTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 2 - 3
CryptoLib.Tests/src/Crypto/RSADigestSignerTests.pas

@@ -42,10 +42,9 @@ uses
   ClpRsaPrivateCrtKeyParameters,
   ClpIRsaPrivateCrtKeyParameters,
   ClpIAsn1Objects,
-  ClpAlgorithmIdentifier,
-  ClpIDigestInfo,
-  ClpDigestInfo,
   ClpAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
   ClpDigestUtilities,
   ClpX509ObjectIdentifiers,
   ClpNistObjectIdentifiers,

+ 133 - 0
CryptoLib.Tests/src/Utils/Net/IPAddressUtilitiesTests.pas

@@ -0,0 +1,133 @@
+{ *********************************************************************************** }
+{ *                              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 IPAddressUtilitiesTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpIPAddressUtilities,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TIPAddressUtilitiesTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    var
+      FValidIPv4: TCryptoLibStringArray;
+      FInvalidIPv4: TCryptoLibStringArray;
+      FValidIPv6: TCryptoLibStringArray;
+      FInvalidIPv6: TCryptoLibStringArray;
+
+    procedure SetUpTestData;
+    procedure DoTestIP(const AValid, AInvalid: TCryptoLibStringArray);
+
+  protected
+    procedure SetUp; override;
+
+  published
+    procedure TestIPv4;
+    procedure TestIPv6;
+
+  end;
+
+implementation
+
+{ TIPAddressUtilitiesTest }
+
+procedure TIPAddressUtilitiesTest.SetUpTestData;
+begin
+  System.SetLength(FValidIPv4, 3);
+  FValidIPv4[0] := '0.0.0.0';
+  FValidIPv4[1] := '255.255.255.255';
+  FValidIPv4[2] := '192.168.0.0';
+
+  System.SetLength(FInvalidIPv4, 5);
+  FInvalidIPv4[0] := '0.0.0.0.1';
+  FInvalidIPv4[1] := '256.255.255.255';
+  FInvalidIPv4[2] := '1';
+  FInvalidIPv4[3] := 'A.B.C';
+  FInvalidIPv4[4] := '1:.4.6.5';
+
+  System.SetLength(FValidIPv6, 3);
+  FValidIPv6[0] := '0:0:0:0:0:0:0:0';
+  FValidIPv6[1] := 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF';
+  FValidIPv6[2] := '0:1:2:3:FFFF:5:FFFF:1';
+
+  System.SetLength(FInvalidIPv6, 2);
+  FInvalidIPv6[0] := '0.0.0.0:1';
+  FInvalidIPv6[1] := 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFFF';
+end;
+
+procedure TIPAddressUtilitiesTest.SetUp;
+begin
+  inherited SetUp;
+  SetUpTestData;
+end;
+
+procedure TIPAddressUtilitiesTest.DoTestIP(const AValid, AInvalid: TCryptoLibStringArray);
+var
+  I: Int32;
+begin
+  for I := 0 to System.Length(AValid) - 1 do
+  begin
+    if not TIPAddressUtilities.IsValid(AValid[I]) then
+    begin
+      Fail(Format('Valid input string not accepted: %s.', [AValid[I]]));
+    end;
+  end;
+
+  for I := 0 to System.Length(AInvalid) - 1 do
+  begin
+    if TIPAddressUtilities.IsValid(AInvalid[I]) then
+    begin
+      Fail(Format('Invalid input string accepted: %s.', [AInvalid[I]]));
+    end;
+  end;
+end;
+
+procedure TIPAddressUtilitiesTest.TestIPv4;
+begin
+  DoTestIP(FValidIPv4, FInvalidIPv4);
+end;
+
+procedure TIPAddressUtilitiesTest.TestIPv6;
+begin
+  DoTestIP(FValidIPv6, FInvalidIPv6);
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TIPAddressUtilitiesTest);
+{$ELSE}
+RegisterTest(TIPAddressUtilitiesTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 278 - 0
CryptoLib.Tests/src/Utils/Pem/PemReaderTests.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 PemReaderTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+  Classes,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpPemObjects,
+  ClpIPemObjects,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpConverters,
+  ClpEncoders,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TPemReaderTest = class(TCryptoLibAlgorithmTestCase)
+  published
+    procedure TestMalformedInput;
+    procedure TestSaneInput;
+    procedure TestWithHeaders;
+    procedure TestNoWhiteSpace;
+
+  end;
+
+implementation
+
+{ TPemReaderTest }
+
+procedure TPemReaderTest.TestMalformedInput;
+var
+  LRaw: String;
+  LStream: TStringStream;
+  LPemReader: IPemReader;
+  LPemObject: IPemObject;
+  LPkcs10: ICertificationRequest;
+  LSubject: String;
+begin
+  LRaw := '-----BEGIN CERTIFICATE REQUEST----- MIIBkTCB+wIBADAUMRIwEAYDVQQDDAlUZXN0MlNBTnMwgZ8wDQYJKoZIhvcNAQEB' +
+    ' BQADgY0AMIGJAoGBAPPPH7W8LqBMCwSu/MsmCeSCfBzMEp4k+aZmeKw8EQD1R3FK' +
+    ' WtPy/LcaUyQhyIeNPFAH8JEz0dJRJjleFL8G5pv7c2YXjBmIfbF/W2eETBIohMDP' +
+    ' pWOqKYiT1mqzw25rP1VuXGXaSfN22RReomUd9O2GuEkaqz5x5iTRD6aLmDoJAgMB' +
+    ' AAGgPjA8BgkqhkiG9w0BCQ4xLzAtMCsGA1UdEQQkMCKCD3NhbjEudGVzdC5sb2Nh' +
+    ' bIIPc2FuMi50ZXN0LmxvY2FsMA0GCSqGSIb3DQEBCwUAA4GBAOacp+9s7/jpmSTA' +
+    ' ORvx4nsDwBsY4VLeuPUc2gYmHqfVgrCCSHKPQtQge0P5atudbo+q8Fn+/5JnJR6/' +
+    ' JaooICY3M+/QVrvzvV30i5W8aEIERfXsEIcFyVxv24p6SbrGAcSjwpqvgAf0z82F' +
+    ' D3f1qdFATb9HAFsuD/J0HexTFDvB -----END CERTIFICATE REQUEST-----';
+
+  LStream := TStringStream.Create(LRaw, TEncoding.ASCII);
+  try
+    LPemReader := TPemReader.Create(LStream);
+    LPemObject := LPemReader.ReadPemObject();
+
+    LPkcs10 := TCertificationRequest.GetInstance(
+      TAsn1Sequence.GetInstance(LPemObject.Content) as TObject);
+    LSubject := LPkcs10.GetCertificationRequestInfo().Subject.ToString();
+
+    CheckEquals('CERTIFICATE REQUEST', LPemObject.&Type, 'PEM type should be CERTIFICATE REQUEST');
+    CheckEquals('CN=Test2SANs', LSubject, 'Subject should match');
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TPemReaderTest.TestSaneInput;
+var
+  LTest: String;
+  LStream: TStringStream;
+  LPemReader: IPemReader;
+  LPemObject: IPemObject;
+  LCert: IX509CertificateStructure;
+  LIssuer: String;
+begin
+  LTest := 'Certificate:' + sLineBreak +
+    '    Data:' + sLineBreak +
+    '        Version: 3 (0x2)' + sLineBreak +
+    '        Serial Number: 865 (0x361)' + sLineBreak +
+    '    Signature Algorithm: ecdsa-with-SHA1' + sLineBreak +
+    '        Issuer: CN=estExampleCA' + sLineBreak +
+    '        Validity' + sLineBreak +
+    '            Not Before: Sep 29 12:41:31 2014 GMT' + sLineBreak +
+    '            Not After : Dec 16 12:41:31 2022 GMT' + sLineBreak +
+    '        Subject: CN=*.cisco.com' + sLineBreak +
+    '        Subject Public Key Info:' + sLineBreak +
+    '            Public Key Algorithm: rsaEncryption' + sLineBreak +
+    '                Public-Key: (1024 bit)' + sLineBreak +
+    '                Modulus:' + sLineBreak +
+    '                    00:b7:08:e6:18:f2:32:d7:07:44:4b:f3:b1:83:01:' + sLineBreak +
+    '                    59:f8:bc:ec:26:71:92:9a:53:70:f2:c0:be:2a:d6:' + sLineBreak +
+    '                    26:6f:45:11:86:d7:ee:37:9d:d3:2f:22:b2:8b:9b:' + sLineBreak +
+    '                    c5:96:00:36:73:97:c3:4c:f2:7a:0b:2c:e0:cc:d9:' + sLineBreak +
+    '                    f0:ec:ba:1b:75:8c:66:b1:86:10:fd:be:df:6b:67:' + sLineBreak +
+    '                    9c:0e:6b:2a:0e:d0:80:a8:dc:7a:d4:df:6e:79:28:' + sLineBreak +
+    '                    a7:60:1a:11:b7:ae:40:94:bb:b4:11:ed:1b:6f:a7:' + sLineBreak +
+    '                    91:ae:33:ec:bf:9c:30:f3:dc:91:2c:b4:3e:8c:c9:' + sLineBreak +
+    '                    bd:f1:d1:aa:f6:c2:1d:6a:cd' + sLineBreak +
+    '                Exponent: 65537 (0x10001)' + sLineBreak +
+    '        X509v3 extensions:' + sLineBreak +
+    '            X509v3 Basic Constraints: ' + sLineBreak +
+    '                CA:FALSE' + sLineBreak +
+    '            X509v3 Key Usage: ' + sLineBreak +
+    '                Digital Signature, Non Repudiation, Key Encipherment' + sLineBreak +
+    '    Signature Algorithm: ecdsa-with-SHA1' + sLineBreak +
+    '         30:44:02:20:76:4f:3a:6c:b4:99:cb:1e:37:f4:0d:6e:e1:74:' + sLineBreak +
+    '         4b:99:bb:f5:c4:b6:3d:c1:61:df:8c:d7:1f:9f:e7:d3:64:d6:' + sLineBreak +
+    '         02:20:64:38:8f:6f:32:37:2b:7d:cf:28:93:e5:e6:e7:70:c5:' + sLineBreak +
+    '         a9:12:04:b0:4b:a5:29:7b:23:df:85:f2:18:44:8b:d2' + sLineBreak +
+    '-----BEGIN CERTIFICATE-----' + sLineBreak +
+    'MIIBezCCASOgAwIBAgICA2EwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt' + sLineBreak +
+    'cGxlQ0EwHhcNMTQwOTI5MTI0MTMxWhcNMjIxMjE2MTI0MTMxWjAWMRQwEgYDVQQD' + sLineBreak +
+    'DAsqLmNpc2NvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtwjmGPIy' + sLineBreak +
+    '1wdES/OxgwFZ+LzsJnGSmlNw8sC+KtYmb0URhtfuN53TLyKyi5vFlgA2c5fDTPJ6' + sLineBreak +
+    'CyzgzNnw7LobdYxmsYYQ/b7fa2ecDmsqDtCAqNx61N9ueSinYBoRt65AlLu0Ee0b' + sLineBreak +
+    'b6eRrjPsv5ww89yRLLQ+jMm98dGq9sIdas0CAwEAAaMaMBgwCQYDVR0TBAIwADAL' + sLineBreak +
+    'BgNVHQ8EBAMCBeAwCQYHKoZIzj0EAQNHADBEAiB2TzpstJnLHjf0DW7hdEuZu/XE' + sLineBreak +
+    'tj3BYd+M1x+f59Nk1gIgZDiPbzI3K33PKJPl5udwxakSBLBLpSl7I9+F8hhEi9I=' + sLineBreak +
+    '-----END CERTIFICATE-----' + sLineBreak;
+
+  LStream := TStringStream.Create(LTest, TEncoding.ASCII);
+  try
+    LPemReader := TPemReader.Create(LStream);
+    LPemObject := LPemReader.ReadPemObject();
+    
+    LCert := TX509CertificateStructure.GetInstance(
+      TAsn1Sequence.GetInstance(LPemObject.Content) as TObject);
+    LIssuer := LCert.Issuer.ToString();
+
+    CheckEquals('CERTIFICATE', LPemObject.&Type, 'PEM type should be CERTIFICATE');
+    CheckEquals('CN=estExampleCA', LIssuer, 'Issuer should match');
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TPemReaderTest.TestWithHeaders;
+var
+  LHdr, LHdr2, LHdr3, LHdr4, LHdr5, LTest: String;
+  LStream: TStringStream;
+  LPemReader: IPemReader;
+  LPemObject: IPemObject;
+  LCert: IX509CertificateStructure;
+  LIssuer: String;
+  LHeaders: TCryptoLibGenericArray<IPemHeader>;
+  LExpectedHeaders: array[0..4] of array[0..1] of String;
+  I: Int32;
+begin
+  LHdr := 'Proc-Type: 4,CRL' + sLineBreak;
+  LHdr2 := 'CRL: CRL Header' + sLineBreak;
+  LHdr3 := 'Originator-Certificate: originator certificate' + sLineBreak;
+  LHdr4 := 'CRL: crl header' + sLineBreak;
+  LHdr5 := 'Originator-Certificate: next originator certificate' + sLineBreak;
+
+  LTest := '-----BEGIN CERTIFICATE-----' + sLineBreak + LHdr + LHdr2 + '    ' + #9 + #13 + #0 + LHdr3 + LHdr4 + LHdr5 +
+    'MIIBezCCASOgAwIBAgICA2EwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt' + sLineBreak +
+    'cGxlQ0EwHhcNMTQwOTI5MTI0MTMxWhcNMjIxMjE2MTI0MTMxWjAWMRQwEgYDVQQD' + sLineBreak +
+    'DAsqLmNpc2NvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtwjmGPIy' + sLineBreak +
+    '1wdES/OxgwFZ+LzsJnGSmlNw8sC+KtYmb0URhtfuN53TLyKyi5vFlgA2c5fDTPJ6' + sLineBreak +
+    'CyzgzNnw7LobdYxmsYYQ/b7fa2ecDmsqDtCAqNx61N9ueSinYBoRt65AlLu0Ee0b' + sLineBreak +
+    'b6eRrjPsv5ww89yRLLQ+jMm98dGq9sIdas0CAwEAAaMaMBgwCQYDVR0TBAIwADAL' + sLineBreak +
+    'BgNVHQ8EBAMCBeAwCQYHKoZIzj0EAQNHADBEAiB2TzpstJnLHjf0DW7hdEuZu/XE' + sLineBreak +
+    'tj3BYd+M1x+f59Nk1gIgZDiPbzI3K33PKJPl5udwxakSBLBLpSl7I9+F8hhEi9I=' + sLineBreak +
+    '-----END CERTIFICATE-----' + sLineBreak;
+
+  LStream := TStringStream.Create(LTest, TEncoding.ASCII);
+  try
+    LPemReader := TPemReader.Create(LStream);
+    LPemObject := LPemReader.ReadPemObject();
+
+    LCert := TX509CertificateStructure.GetInstance(
+      TAsn1Sequence.GetInstance(LPemObject.Content) as TObject);
+    LIssuer := LCert.Issuer.ToString();
+
+    CheckEquals('CERTIFICATE', LPemObject.&Type, 'PEM type should be CERTIFICATE');
+    CheckEquals('CN=estExampleCA', LIssuer, 'Issuer should match');
+
+    LHeaders := LPemObject.Headers;
+    LExpectedHeaders[0][0] := 'Proc-Type';
+    LExpectedHeaders[0][1] := '4,CRL';
+    LExpectedHeaders[1][0] := 'CRL';
+    LExpectedHeaders[1][1] := 'CRL Header';
+    LExpectedHeaders[2][0] := 'Originator-Certificate';
+    LExpectedHeaders[2][1] := 'originator certificate';
+    LExpectedHeaders[3][0] := 'CRL';
+    LExpectedHeaders[3][1] := 'crl header';
+    LExpectedHeaders[4][0] := 'Originator-Certificate';
+    LExpectedHeaders[4][1] := 'next originator certificate';
+
+    CheckEquals(5, System.Length(LHeaders), 'Should have 5 headers');
+    for I := 0 to System.Length(LExpectedHeaders) - 1 do
+    begin
+      CheckEquals(LExpectedHeaders[I][0], LHeaders[I].Name, 
+        Format('Header %d name should match', [I]));
+      CheckEquals(LExpectedHeaders[I][1], LHeaders[I].Value, 
+        Format('Header %d value should match', [I]));
+    end;
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TPemReaderTest.TestNoWhiteSpace;
+var
+  LTest: String;
+  LStream: TStringStream;
+  LPemReader: IPemReader;
+  LPemObject: IPemObject;
+  LCert: IX509CertificateStructure;
+  LIssuer: String;
+begin
+  LTest := '-----BEGIN CERTIFICATE-----' +
+    'MIIBezCCASOgAwIBAgICA2EwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt' +
+    'cGxlQ0EwHhcNMTQwOTI5MTI0MTMxWhcNMjIxMjE2MTI0MTMxWjAWMRQwEgYDVQQD' +
+    'DAsqLmNpc2NvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtwjmGPIy' +
+    '1wdES/OxgwFZ+LzsJnGSmlNw8sC+KtYmb0URhtfuN53TLyKyi5vFlgA2c5fDTPJ6' +
+    'CyzgzNnw7LobdYxmsYYQ/b7fa2ecDmsqDtCAqNx61N9ueSinYBoRt65AlLu0Ee0b' +
+    'b6eRrjPsv5ww89yRLLQ+jMm98dGq9sIdas0CAwEAAaMaMBgwCQYDVR0TBAIwADAL' +
+    'BgNVHQ8EBAMCBeAwCQYHKoZIzj0EAQNHADBEAiB2TzpstJnLHjf0DW7hdEuZu/XE' +
+    'tj3BYd+M1x+f59Nk1gIgZDiPbzI3K33PKJPl5udwxakSBLBLpSl7I9+F8hhEi9I=' +
+    '-----END CERTIFICATE-----';
+
+  LStream := TStringStream.Create(LTest, TEncoding.ASCII);
+  try
+    LPemReader := TPemReader.Create(LStream);
+    LPemObject := LPemReader.ReadPemObject();
+    
+    LCert := TX509CertificateStructure.GetInstance(
+      TAsn1Sequence.GetInstance(LPemObject.Content) as TObject);
+    LIssuer := LCert.Issuer.ToString();
+
+    CheckEquals('CERTIFICATE', LPemObject.&Type, 'PEM type should be CERTIFICATE');
+    CheckEquals('CN=estExampleCA', LIssuer, 'Issuer should match');
+  finally
+    LStream.Free;
+  end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TPemReaderTest);
+{$ELSE}
+RegisterTest(TPemReaderTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 406 - 0
CryptoLib/src/Asn1/ClpAsn1Dumper.pas

@@ -0,0 +1,406 @@
+{ *********************************************************************************** }
+{ *                              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 ClpAsn1Dumper;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Math,
+  SysUtils,
+  Classes,
+  ClpIAsn1Objects,
+  ClpAsn1Objects,
+  ClpAsn1Utilities,
+  ClpAsn1Streams,
+  ClpEncoders,
+  ClpCryptoLibTypes,
+  ClpArrayUtils;
+
+type
+  /// <summary>
+  /// Utility class for dumping ASN.1 objects as formatted strings.
+  /// </summary>
+  TAsn1Dumper = class sealed(TObject)
+  strict private
+    const
+      Tab = '    ';
+      SampleSize = 32;
+
+    class procedure AsString(const AIndent: String; AVerbose: Boolean;
+      const AObj: IAsn1Object; const ABuf: TStringBuilder); static;
+    class procedure DumpBinaryDataAsString(const ABuf: TStringBuilder;
+      const AIndent: String; const ABytes: TCryptoLibByteArray); static;
+    class procedure AppendAscString(const ABuf: TStringBuilder;
+      const ABytes: TCryptoLibByteArray; AOff, ALen: Int32); static;
+
+  public
+    /// <summary>
+    /// Dump out a DER object as a formatted string, in non-verbose mode.
+    /// </summary>
+    /// <param name="AObj">the Asn1Encodable to be dumped out.</param>
+    /// <returns>the resulting string.</returns>
+    class function DumpAsString(const AObj: IAsn1Encodable): String; overload; static;
+    /// <summary>
+    /// Dump out the object as a string.
+    /// </summary>
+    /// <param name="AObj">the Asn1Encodable to be dumped out.</param>
+    /// <param name="AVerbose">if true, dump out the contents of octet and bit strings.</param>
+    /// <returns>the resulting string.</returns>
+    class function DumpAsString(const AObj: IAsn1Encodable; AVerbose: Boolean): String; overload; static;
+    /// <summary>
+    /// Parse ASN.1 objects from input stream, and write them to the output.
+    /// </summary>
+    class procedure Dump(const AInput: TStream; const AOutput: TStringBuilder); static;
+  end;
+
+implementation
+
+{ TAsn1Dumper }
+
+class procedure TAsn1Dumper.AppendAscString(const ABuf: TStringBuilder;
+  const ABytes: TCryptoLibByteArray; AOff, ALen: Int32);
+var
+  I: Int32;
+  C: Char;
+begin
+  I := AOff;
+  while I <> AOff + ALen do
+  begin
+    C := Char(ABytes[I]);
+    if (C >= ' ') and (C <= '~') then
+    begin
+      ABuf.Append(C);
+    end;
+    System.Inc(I);
+  end;
+end;
+
+class procedure TAsn1Dumper.AsString(const AIndent: String; AVerbose: Boolean;
+  const AObj: IAsn1Object; const ABuf: TStringBuilder);
+var
+  LSequence: IAsn1Sequence;
+  LSet: IAsn1Set;
+  LTaggedObject: IAsn1TaggedObject;
+  LOid: IDerObjectIdentifier;
+  LRelativeOid: IAsn1RelativeOid;
+  LBoolean: IDerBoolean;
+  LInteger: IDerInteger;
+  LOctetString: IAsn1OctetString;
+  LBitString: IDerBitString;
+  LIA5String: IDerIA5String;
+  LUtf8String: IDerUtf8String;
+  LPrintableString: IDerPrintableString;
+  LVisibleString: IDerVisibleString;
+  LBmpString: IDerBmpString;
+  LT61String: IDerT61String;
+  LGraphicString: IDerGraphicString;
+  LVideotexString: IDerVideotexString;
+  LUtcTime: IAsn1UtcTime;
+  LGeneralizedTime: IAsn1GeneralizedTime;
+  LEnumerated: IDerEnumerated;
+  LExternal: IDerExternal;
+  LBerSequence: IBerSequence;
+  LBerSet: IBerSet;
+  LBerTaggedObject: IBerTaggedObject;
+  LBerOctetString: IBerOctetString;
+  LBerBitString: IBerBitString;
+  LDLSequence: IDLSequence;
+  LDLSet: IDLSet;
+  LDLTaggedObject: IDLTaggedObject;
+  LDLBitString: IDLBitString;
+  I, LCount: Int32;
+  LElementsIndent, LBaseIndent, LTab: String;
+begin
+  ABuf.Append(AIndent);
+
+  if Supports(AObj, IAsn1Null) then
+  begin
+    ABuf.AppendLine('NULL');
+  end
+  else if Supports(AObj, IAsn1Sequence, LSequence) then
+  begin
+    if Supports(LSequence, IBerSequence, LBerSequence) then
+    begin
+      ABuf.AppendLine('BER Sequence');
+    end
+    else if not Supports(LSequence, IDLSequence, LDLSequence) then
+    begin
+      ABuf.AppendLine('DER Sequence');
+    end
+    else
+    begin
+      ABuf.AppendLine('Sequence');
+    end;
+
+    LElementsIndent := AIndent + Tab;
+    LCount := LSequence.Count;
+    for I := 0 to LCount - 1 do
+    begin
+      AsString(LElementsIndent, AVerbose, LSequence[I].ToAsn1Object(), ABuf);
+    end;
+  end
+  else if Supports(AObj, IAsn1Set, LSet) then
+  begin
+    if Supports(LSet, IBerSet, LBerSet) then
+    begin
+      ABuf.AppendLine('BER Set');
+    end
+    else if not Supports(LSet, IDLSet, LDLSet) then
+    begin
+      ABuf.AppendLine('DER Set');
+    end
+    else
+    begin
+      ABuf.AppendLine('Set');
+    end;
+
+    LElementsIndent := AIndent + Tab;
+    LCount := LSet.Count;
+    for I := 0 to LCount - 1 do
+    begin
+      AsString(LElementsIndent, AVerbose, LSet[I].ToAsn1Object(), ABuf);
+    end;
+  end
+  else if Supports(AObj, IAsn1TaggedObject, LTaggedObject) then
+  begin
+    if Supports(LTaggedObject, IBerTaggedObject, LBerTaggedObject) then
+    begin
+      ABuf.Append('BER Tagged ');
+    end
+    else if not Supports(LTaggedObject, IDLTaggedObject, LDLTaggedObject) then
+    begin
+      ABuf.Append('DER Tagged ');
+    end
+    else
+    begin
+      ABuf.Append('Tagged ');
+    end;
+
+    ABuf.Append(TAsn1Utilities.GetTagText(LTaggedObject));
+
+    if not LTaggedObject.IsExplicit() then
+    begin
+      ABuf.Append(' IMPLICIT');
+    end;
+
+    ABuf.AppendLine();
+
+    LBaseIndent := AIndent + Tab;
+    AsString(LBaseIndent, AVerbose, LTaggedObject.GetBaseObject().ToAsn1Object(), ABuf);
+  end
+  else if Supports(AObj, IDerObjectIdentifier, LOid) then
+  begin
+    ABuf.AppendLine('ObjectIdentifier(' + LOid.GetID() + ')');
+  end
+  else if Supports(AObj, IAsn1RelativeOid, LRelativeOid) then
+  begin
+    ABuf.AppendLine('RelativeOID(' + LRelativeOid.GetID() + ')');
+  end
+  else if Supports(AObj, IDerBoolean, LBoolean) then
+  begin
+    ABuf.AppendLine('Boolean(' + BoolToStr(LBoolean.IsTrue, True) + ')');
+  end
+  else if Supports(AObj, IDerInteger, LInteger) then
+  begin
+    ABuf.AppendLine('Integer(' + LInteger.Value.ToString() + ')');
+  end
+  else if Supports(AObj, IAsn1OctetString, LOctetString) then
+  begin
+    if Supports(LOctetString, IBerOctetString, LBerOctetString) then
+    begin
+      ABuf.Append('BER Octet String[');
+    end
+    else
+    begin
+      ABuf.Append('DER Octet String[');
+    end;
+
+    ABuf.AppendLine(IntToStr(LOctetString.GetOctetsLength()) + ']');
+
+    if AVerbose then
+    begin
+      DumpBinaryDataAsString(ABuf, AIndent, LOctetString.GetOctets());
+    end;
+  end
+  else if Supports(AObj, IDerBitString, LBitString) then
+  begin
+    if Supports(LBitString, IBerBitString, LBerBitString) then
+    begin
+      ABuf.Append('BER Bit String[');
+    end
+    else if Supports(LBitString, IDLBitString, LDLBitString) then
+    begin
+      ABuf.Append('DL Bit String[');
+    end
+    else
+    begin
+      ABuf.Append('DER Bit String[');
+    end;
+
+    ABuf.AppendLine(IntToStr(System.Length(LBitString.GetBytes())) + ', ' + IntToStr(LBitString.GetPadBits()) + ']');
+
+    if AVerbose then
+    begin
+      DumpBinaryDataAsString(ABuf, AIndent, LBitString.GetBytes());
+    end;
+  end
+  else if Supports(AObj, IDerIA5String, LIA5String) then
+  begin
+    ABuf.AppendLine('IA5String(' + LIA5String.GetString() + ')');
+  end
+  else if Supports(AObj, IDerUtf8String, LUtf8String) then
+  begin
+    ABuf.AppendLine('UTF8String(' + LUtf8String.GetString() + ')');
+  end
+  else if Supports(AObj, IDerPrintableString, LPrintableString) then
+  begin
+    ABuf.AppendLine('PrintableString(' + LPrintableString.GetString() + ')');
+  end
+  else if Supports(AObj, IDerVisibleString, LVisibleString) then
+  begin
+    ABuf.AppendLine('VisibleString(' + LVisibleString.GetString() + ')');
+  end
+  else if Supports(AObj, IDerBmpString, LBmpString) then
+  begin
+    ABuf.AppendLine('BMPString(' + LBmpString.GetString() + ')');
+  end
+  else if Supports(AObj, IDerT61String, LT61String) then
+  begin
+    ABuf.AppendLine('T61String(' + LT61String.GetString() + ')');
+  end
+  else if Supports(AObj, IDerGraphicString, LGraphicString) then
+  begin
+    ABuf.AppendLine('GraphicString(' + LGraphicString.GetString() + ')');
+  end
+  else if Supports(AObj, IDerVideotexString, LVideotexString) then
+  begin
+    ABuf.AppendLine('VideotexString(' + LVideotexString.GetString() + ')');
+  end
+  else if Supports(AObj, IAsn1UtcTime, LUtcTime) then
+  begin
+    ABuf.AppendLine('UTCTime(' + LUtcTime.TimeString + ')');
+  end
+  else if Supports(AObj, IAsn1GeneralizedTime, LGeneralizedTime) then
+  begin
+    ABuf.AppendLine('GeneralizedTime(' + LGeneralizedTime.TimeString + ')');
+  end
+  else if Supports(AObj, IDerEnumerated, LEnumerated) then
+  begin
+    ABuf.AppendLine('DER Enumerated(' + LEnumerated.Value.ToString() + ')');
+  end
+  else if Supports(AObj, IDerExternal, LExternal) then
+  begin
+    ABuf.AppendLine('External ');
+    LTab := AIndent + Tab;
+
+    if LExternal.GetDirectReference() <> nil then
+    begin
+      ABuf.Append(LTab);
+      ABuf.AppendLine('Direct Reference: ' + LExternal.GetDirectReference().GetID());
+    end;
+    if LExternal.GetIndirectReference() <> nil then
+    begin
+      ABuf.Append(LTab);
+      ABuf.AppendLine('Indirect Reference: ' + LExternal.GetIndirectReference().ToString());
+    end;
+    if LExternal.GetDataValueDescriptor() <> nil then
+    begin
+      AsString(LTab, AVerbose, LExternal.GetDataValueDescriptor(), ABuf);
+    end;
+    ABuf.Append(LTab);
+    ABuf.AppendLine('Encoding: ' + IntToStr(LExternal.GetEncoding()));
+    AsString(LTab, AVerbose, LExternal.GetExternalContent(), ABuf);
+  end
+  else
+  begin
+    ABuf.Append(AObj.ToString());
+    ABuf.AppendLine();
+  end;
+end;
+
+class procedure TAsn1Dumper.DumpBinaryDataAsString(const ABuf: TStringBuilder;
+  const AIndent: String; const ABytes: TCryptoLibByteArray);
+var
+  I, LRemaining, LChunk, J: Int32;
+  LIndent: String;
+begin
+  if System.Length(ABytes) < 1 then
+    Exit;
+
+  LIndent := AIndent + Tab;
+
+  I := 0;
+  while I < System.Length(ABytes) do
+  begin
+    LRemaining := System.Length(ABytes) - I;
+    LChunk := Math.Min(LRemaining, SampleSize);
+
+    ABuf.Append(LIndent);
+    ABuf.Append(THex.Encode(TArrayUtils.CopyOfRange(ABytes, I, I + LChunk)));
+    J := LChunk;
+    while J < SampleSize do
+    begin
+      ABuf.Append('  ');
+      System.Inc(J);
+    end;
+    ABuf.Append(Tab);
+    AppendAscString(ABuf, ABytes, I, LChunk);
+    ABuf.AppendLine();
+    System.Inc(I, SampleSize);
+  end;
+end;
+
+class function TAsn1Dumper.DumpAsString(const AObj: IAsn1Encodable): String;
+begin
+  Result := DumpAsString(AObj, False);
+end;
+
+class function TAsn1Dumper.DumpAsString(const AObj: IAsn1Encodable; AVerbose: Boolean): String;
+var
+  LBuf: TStringBuilder;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    AsString('', AVerbose, AObj.ToAsn1Object(), LBuf);
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+class procedure TAsn1Dumper.Dump(const AInput: TStream; const AOutput: TStringBuilder);
+var
+  LAsn1In: TAsn1InputStream;
+  LAsn1Object: IAsn1Object;
+begin
+  LAsn1In := TAsn1InputStream.Create(AInput, MaxInt, True);
+  try
+    LAsn1Object := LAsn1In.ReadObject();
+    while LAsn1Object <> nil do
+    begin
+      AOutput.Append(DumpAsString(LAsn1Object));
+      LAsn1Object := LAsn1In.ReadObject();
+    end;
+  finally
+    LAsn1In.Free;
+  end;
+end;
+
+end.

+ 17 - 4
CryptoLib/src/Asn1/ClpAsn1Objects.pas

@@ -26,7 +26,6 @@ uses
   SysUtils,
   Math,
   DateUtils,
-  StrUtils,
   ClpBits,
   ClpCryptoLibTypes,
   ClpBigInteger,
@@ -495,6 +494,10 @@ type
     /// </summary>
     class function GetInstance(const AObj: TObject): IAsn1OctetString; overload; static;
     /// <summary>
+    /// Get instance from byte array.
+    /// </summary>
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAsn1OctetString; overload; static;
+    /// <summary>
     /// Get instance from ASN.1 object.
     /// </summary>
     class function GetInstance(const AObj: IAsn1Object): IAsn1OctetString; overload; static;
@@ -8181,6 +8184,16 @@ begin
   raise EArgumentCryptoLibException.CreateFmt('illegal object in GetInstance: %s', [TPlatform.GetTypeName(AObj)]);
 end;
 
+class function TAsn1OctetString.GetInstance(const AEncoded: TCryptoLibByteArray): IAsn1OctetString;
+begin
+  try
+    Result := TAsn1OctetString.Meta.Instance.FromByteArray(AEncoded) as IAsn1OctetString;
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.CreateFmt('failed to construct OCTET STRING from byte[]: %s', [E.Message]);
+  end;
+end;
+
 class function TAsn1OctetString.GetInstance(const AObj: IAsn1Object): IAsn1OctetString;
 begin
   if AObj = nil then
@@ -11549,9 +11562,9 @@ class function TAsn1GeneralizedTime.IndexOfSign(const AStr: String; AStartIndex:
 var
   LIndex: Int32;
 begin
-  LIndex := PosEx('+', AStr, AStartIndex);
+  LIndex := TPlatform.IndexOf(AStr, '+', AStartIndex);
   if LIndex = 0 then
-    LIndex := PosEx('-', AStr, AStartIndex);
+    LIndex := TPlatform.IndexOf(AStr, '-', AStartIndex);
     Result := LIndex;
 end;
 
@@ -13929,7 +13942,7 @@ end;
 
 class function TDerInteger.AllowUnsafe(): Boolean;
 begin
-  Result := FAllowUnsafeInteger;
+  Result := AllowUnsafeInteger;
 end;
 
 class function TDerInteger.IsMalformed(const ABytes: TCryptoLibByteArray): Boolean;

+ 1444 - 0
CryptoLib/src/Asn1/Pkcs/ClpPkcsAsn1Objects.pas

@@ -0,0 +1,1444 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPkcsAsn1Objects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  ClpBigInteger,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
+  ClpPkcsObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpCryptoLibTypes,
+  ClpAsn1Utilities;
+
+resourcestring
+  SBadSequenceSize = 'Bad sequence size: %d';
+  SAttrTypeNil = 'attrType';
+  SAttrValuesNil = 'attrValues';
+  SWrongNumberOfElements = 'Wrong number of elements in sequence';
+  SRequestInfoNil = 'requestInfo';
+  SAlgorithmNil = 'algorithm';
+  SSignatureNil = 'signature';
+  SSubjectNil = 'subject';
+  SSubjectPKInfoNil = 'subjectPKInfo';
+  SUnexpectedElementsInSequence = 'Unexpected elements in sequence';
+  SChallengePasswordMustHaveSingleValue = 'challengePassword attribute must have exactly one value';
+  SUnstructuredNameMustHaveSingleValue = 'unstructuredName attribute must have exactly one value';
+  SPrivateKeyAlgorithmNil = 'privateKeyAlgorithm';
+  SPrivateKeyNil = 'privateKey';
+  SVersionNil = 'version';
+
+type
+  /// <summary>
+  /// The AttributePkcs object.
+  /// </summary>
+  TAttributePkcs = class(TAsn1Encodable, IAttributePkcs)
+
+  strict private
+  var
+    FAttrType: IDerObjectIdentifier;
+    FAttrValues: IAsn1Set;
+
+  strict protected
+    function GetAttrType: IDerObjectIdentifier;
+    function GetAttrValues: IAsn1Set;
+
+  public
+    class function GetInstance(AObj: TObject): IAttributePkcs; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): IAttributePkcs; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttributePkcs; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAttributePkcs; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributePkcs; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAttrType: IDerObjectIdentifier;
+      const AAttrValues: IAsn1Set); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property AttrType: IDerObjectIdentifier read GetAttrType;
+    property AttrValues: IAsn1Set read GetAttrValues;
+
+  end;
+
+  /// <summary>
+  /// The CertificationRequestInfo object.
+  /// </summary>
+  TCertificationRequestInfo = class(TAsn1Encodable, ICertificationRequestInfo)
+
+  strict private
+  var
+    FVersion: IDerInteger;
+    FSubject: IX509Name;
+    FSubjectPKInfo: ISubjectPublicKeyInfo;
+    FAttributes: IAsn1Set;
+
+  strict protected
+    function GetVersion: IDerInteger;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetAttributes: IAsn1Set;
+
+  public
+    class function ValidateAttributes(const AAttributes: IAsn1Set): IAsn1Set; static;
+
+    class function GetInstance(AObj: TObject): ICertificationRequestInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): ICertificationRequestInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ICertificationRequestInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ICertificationRequestInfo; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ICertificationRequestInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ASubject: IX509Name;
+      const APkInfo: ISubjectPublicKeyInfo; const AAttributes: IAsn1Set); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: IDerInteger read GetVersion;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property Attributes: IAsn1Set read GetAttributes;
+
+  end;
+
+  /// <summary>
+  /// The CertificationRequest object.
+  /// </summary>
+  TCertificationRequest = class(TAsn1Encodable, ICertificationRequest)
+
+  strict private
+  var
+    FReqInfo: ICertificationRequestInfo;
+    FSigAlgId: IAlgorithmIdentifier;
+    FSigBits: IDerBitString;
+
+  strict protected
+    function GetCertificationRequestInfo: ICertificationRequestInfo;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignature: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+
+  public
+    class function GetInstance(AObj: TObject): ICertificationRequest; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): ICertificationRequest; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ICertificationRequest; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ICertificationRequest; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ICertificationRequest; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ARequestInfo: ICertificationRequestInfo;
+      const AAlgorithm: IAlgorithmIdentifier; const ASignature: IDerBitString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property Signature: IDerBitString read GetSignature;
+
+  end;
+
+  /// <summary>
+  /// The PrivateKeyInfo object.
+  /// </summary>
+  TPrivateKeyInfo = class(TAsn1Encodable, IPrivateKeyInfo)
+
+  strict private
+  var
+    FVersion: IDerInteger;
+    FPrivateKeyAlgorithm: IAlgorithmIdentifier;
+    FPrivateKey: IAsn1OctetString;
+    FAttributes: IAsn1Set;
+    FPublicKey: IDerBitString;
+
+  strict protected
+    function GetVersion: IDerInteger;
+    function GetPrivateKeyAlgorithm: IAlgorithmIdentifier;
+    function GetPrivateKey: IAsn1OctetString;
+    function GetAttributes: IAsn1Set;
+    function GetPublicKey: IDerBitString;
+    function HasPublicKey: Boolean;
+    function ParsePrivateKey: IAsn1Object;
+    function ParsePublicKey: IAsn1Object;
+
+  public
+    class function GetInstance(AObj: TObject): IPrivateKeyInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): IPrivateKeyInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IPrivateKeyInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IPrivateKeyInfo; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IPrivateKeyInfo; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IPrivateKeyInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+      const APrivateKey: IAsn1Encodable); overload;
+    constructor Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+      const APrivateKey: IAsn1Encodable; const AAttributes: IAsn1Set); overload;
+    constructor Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+      const APrivateKey: IAsn1Encodable; const AAttributes: IAsn1Set;
+      const APublicKey: TCryptoLibByteArray); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: IDerInteger read GetVersion;
+    property PrivateKeyAlgorithm: IAlgorithmIdentifier read GetPrivateKeyAlgorithm;
+    property PrivateKey: IAsn1OctetString read GetPrivateKey;
+    property Attributes: IAsn1Set read GetAttributes;
+    property PublicKey: IDerBitString read GetPublicKey;
+
+  end;
+
+  /// <summary>
+  /// The RsaPrivateKeyStructure object.
+  /// </summary>
+  TRsaPrivateKeyStructure = class(TAsn1Encodable, IRsaPrivateKeyStructure)
+
+  strict private
+  var
+    FVersion: IDerInteger;
+    FModulus: TBigInteger;
+    FPublicExponent: TBigInteger;
+    FPrivateExponent: TBigInteger;
+    FPrime1: TBigInteger;
+    FPrime2: TBigInteger;
+    FExponent1: TBigInteger;
+    FExponent2: TBigInteger;
+    FCoefficient: TBigInteger;
+
+  strict protected
+    function GetModulus: TBigInteger;
+    function GetPublicExponent: TBigInteger;
+    function GetPrivateExponent: TBigInteger;
+    function GetPrime1: TBigInteger;
+    function GetPrime2: TBigInteger;
+    function GetExponent1: TBigInteger;
+    function GetExponent2: TBigInteger;
+    function GetCoefficient: TBigInteger;
+
+  public
+    class function GetInstance(AObj: TObject): IRsaPrivateKeyStructure; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): IRsaPrivateKeyStructure; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IRsaPrivateKeyStructure; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IRsaPrivateKeyStructure; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IRsaPrivateKeyStructure; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AModulus, APublicExponent, APrivateExponent,
+      APrime1, APrime2, AExponent1, AExponent2, ACoefficient: TBigInteger); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Modulus: TBigInteger read GetModulus;
+    property PublicExponent: TBigInteger read GetPublicExponent;
+    property PrivateExponent: TBigInteger read GetPrivateExponent;
+    property Prime1: TBigInteger read GetPrime1;
+    property Prime2: TBigInteger read GetPrime2;
+    property Exponent1: TBigInteger read GetExponent1;
+    property Exponent2: TBigInteger read GetExponent2;
+    property Coefficient: TBigInteger read GetCoefficient;
+
+  end;
+
+  /// <summary>
+  /// The RsassaPssParameters object.
+  /// <pre>
+  /// RSASSA-PSS-params ::= SEQUENCE {
+  ///   hashAlgorithm      [0] OAEP-PSSDigestAlgorithms  DEFAULT sha1,
+  ///    maskGenAlgorithm   [1] PKCS1MGFAlgorithms  DEFAULT mgf1SHA1,
+  ///    saltLength         [2] INTEGER  DEFAULT 20,
+  ///    trailerField       [3] TrailerField  DEFAULT trailerFieldBC
+  ///  }
+  /// </pre>
+  /// </summary>
+  TRsassaPssParameters = class(TAsn1Encodable, IRsassaPssParameters)
+
+  public
+    class var
+      DefaultHashAlgorithm: IAlgorithmIdentifier;
+      DefaultMaskGenAlgorithm: IAlgorithmIdentifier;
+      DefaultMaskGenFunction: IAlgorithmIdentifier; // Obsolete, use DefaultMaskGenAlgorithm
+      DefaultSaltLength: IDerInteger;
+      DefaultTrailerField: IDerInteger;
+
+    class procedure Boot; static;
+    class constructor Create;
+
+  strict private
+  var
+    FHashAlgorithm: IAlgorithmIdentifier;
+    FMaskGenAlgorithm: IAlgorithmIdentifier;
+    FSaltLength: IDerInteger;
+    FTrailerField: IDerInteger;
+
+  strict protected
+    function GetHashAlgorithm: IAlgorithmIdentifier;
+    function GetMaskGenAlgorithm: IAlgorithmIdentifier;
+    function GetSaltLength: IDerInteger;
+    function GetTrailerField: IDerInteger;
+
+  public
+    class function GetInstance(AObj: TObject): IRsassaPssParameters; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): IRsassaPssParameters; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IRsassaPssParameters; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IRsassaPssParameters; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IRsassaPssParameters; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create; overload;
+    constructor Create(const AHashAlgorithm, AMaskGenAlgorithm: IAlgorithmIdentifier;
+      const ASaltLength, ATrailerField: IDerInteger); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property HashAlgorithm: IAlgorithmIdentifier read GetHashAlgorithm;
+    property MaskGenAlgorithm: IAlgorithmIdentifier read GetMaskGenAlgorithm;
+    property SaltLength: IDerInteger read GetSaltLength;
+    property TrailerField: IDerInteger read GetTrailerField;
+
+  end;
+
+implementation
+
+{ TAttributePkcs }
+
+class function TAttributePkcs.GetInstance(AObj: TObject): IAttributePkcs;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAttributePkcs, Result) then
+    Exit;
+
+  Result := TAttributePkcs.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAttributePkcs.GetInstance(const AObj: IAsn1Object): IAttributePkcs;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAttributePkcs, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetInstance(AObj);
+  Result := TAttributePkcs.Create(LSequence);
+end;
+
+class function TAttributePkcs.GetInstance(const AEncoded: TCryptoLibByteArray): IAttributePkcs;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct AttributePkcs from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TAttributePkcs.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAttributePkcs;
+begin
+  Result := TAttributePkcs.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAttributePkcs.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributePkcs;
+begin
+  Result := TAttributePkcs.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAttributePkcs.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FAttrType := TDerObjectIdentifier.GetInstance(ASeq[0] as TAsn1Encodable);
+  FAttrValues := TAsn1Set.GetInstance(ASeq[1] as TAsn1Encodable);
+end;
+
+constructor TAttributePkcs.Create(const AAttrType: IDerObjectIdentifier;
+  const AAttrValues: IAsn1Set);
+begin
+  inherited Create();
+
+  if AAttrType = nil then
+    raise EArgumentNilCryptoLibException.Create(SAttrTypeNil);
+  if AAttrValues = nil then
+    raise EArgumentNilCryptoLibException.Create(SAttrValuesNil);
+
+  FAttrType := AAttrType;
+  FAttrValues := AAttrValues;
+end;
+
+function TAttributePkcs.GetAttrType: IDerObjectIdentifier;
+begin
+  Result := FAttrType;
+end;
+
+function TAttributePkcs.GetAttrValues: IAsn1Set;
+begin
+  Result := FAttrValues;
+end;
+
+function TAttributePkcs.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FAttrType, FAttrValues]);
+end;
+
+{ TCertificationRequestInfo }
+
+class function TCertificationRequestInfo.GetInstance(AObj: TObject): ICertificationRequestInfo;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, ICertificationRequestInfo, Result) then
+    Exit;
+
+  Result := TCertificationRequestInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TCertificationRequestInfo.GetInstance(const AObj: IAsn1Object): ICertificationRequestInfo;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ICertificationRequestInfo, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetInstance(AObj);
+  Result := TCertificationRequestInfo.Create(LSequence);
+end;
+
+class function TCertificationRequestInfo.GetInstance(const AEncoded: TCryptoLibByteArray): ICertificationRequestInfo;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct CertificationRequestInfo from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TCertificationRequestInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ICertificationRequestInfo;
+begin
+  Result := TCertificationRequestInfo.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TCertificationRequestInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ICertificationRequestInfo;
+begin
+  Result := TCertificationRequestInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TCertificationRequestInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 3) or (LCount > 4) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FVersion := TDerInteger.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FSubject := TX509Name.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FSubjectPKInfo := TSubjectPublicKeyInfo.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+
+  // NOTE: some CertificationRequestInfo objects seem to treat this field as optional.
+  FAttributes := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAsn1Set>(ASeq, LPos, 0, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAsn1Set
+    begin
+      Result := TAsn1Set.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+  begin
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+  end;
+
+  ValidateAttributes(FAttributes);
+end;
+
+constructor TCertificationRequestInfo.Create(const ASubject: IX509Name;
+  const APkInfo: ISubjectPublicKeyInfo; const AAttributes: IAsn1Set);
+begin
+  inherited Create();
+
+  FVersion := TDerInteger.Zero;
+  if ASubject = nil then
+    raise EArgumentNilCryptoLibException.Create(SSubjectNil);
+  if APkInfo = nil then
+    raise EArgumentNilCryptoLibException.Create(SSubjectPKInfoNil);
+
+  FSubject := ASubject;
+  FSubjectPKInfo := APkInfo;
+  FAttributes := ValidateAttributes(AAttributes);
+end;
+
+class function TCertificationRequestInfo.ValidateAttributes(const AAttributes: IAsn1Set): IAsn1Set;
+var
+  I: Int32;
+  LAttr: IAttributePkcs;
+begin
+  if AAttributes <> nil then
+  begin
+    for I := 0 to AAttributes.Count - 1 do
+    begin
+      LAttr := TAttributePkcs.GetInstance(AAttributes[I] as TAsn1Encodable);
+      if TPkcsObjectIdentifiers.Pkcs9AtChallengePassword.Equals(LAttr.AttrType) then
+      begin
+        if LAttr.AttrValues.Count <> 1 then
+        begin
+          raise EArgumentCryptoLibException.Create(SChallengePasswordMustHaveSingleValue);
+        end;
+      end
+      else if TPkcsObjectIdentifiers.Pkcs9AtUnstructuredName.Equals(LAttr.AttrType) then
+      begin
+        if LAttr.AttrValues.Count <> 1 then
+        begin
+          raise EArgumentCryptoLibException.Create(SUnstructuredNameMustHaveSingleValue);
+        end;
+      end;
+    end;
+  end;
+  Result := AAttributes;
+end;
+
+function TCertificationRequestInfo.GetVersion: IDerInteger;
+begin
+  Result := FVersion;
+end;
+
+function TCertificationRequestInfo.GetSubject: IX509Name;
+begin
+  Result := FSubject;
+end;
+
+function TCertificationRequestInfo.GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+begin
+  Result := FSubjectPKInfo;
+end;
+
+function TCertificationRequestInfo.GetAttributes: IAsn1Set;
+begin
+  Result := FAttributes;
+end;
+
+function TCertificationRequestInfo.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(4);
+  LV.Add([FVersion, FSubject, FSubjectPKInfo]);
+  LV.AddOptionalTagged(False, 0, FAttributes);
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TCertificationRequest }
+
+class function TCertificationRequest.GetInstance(AObj: TObject): ICertificationRequest;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, ICertificationRequest, Result) then
+    Exit;
+
+  Result := TCertificationRequest.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TCertificationRequest.GetInstance(const AObj: IAsn1Object): ICertificationRequest;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ICertificationRequest, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetInstance(AObj);
+  Result := TCertificationRequest.Create(LSequence);
+end;
+
+class function TCertificationRequest.GetInstance(const AEncoded: TCryptoLibByteArray): ICertificationRequest;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct CertificationRequest from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TCertificationRequest.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ICertificationRequest;
+begin
+  Result := TCertificationRequest.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TCertificationRequest.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ICertificationRequest;
+begin
+  Result := TCertificationRequest.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TCertificationRequest.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 3 then
+  begin
+    raise EArgumentCryptoLibException.Create(SWrongNumberOfElements);
+  end;
+
+  FReqInfo := TCertificationRequestInfo.GetInstance(ASeq[0] as TObject);
+  FSigAlgId := TAlgorithmIdentifier.GetInstance(ASeq[1] as TObject);
+  FSigBits := TDerBitString.GetInstance(ASeq[2] as TObject);
+end;
+
+constructor TCertificationRequest.Create(const ARequestInfo: ICertificationRequestInfo;
+  const AAlgorithm: IAlgorithmIdentifier; const ASignature: IDerBitString);
+begin
+  inherited Create();
+
+  if ARequestInfo = nil then
+    raise EArgumentNilCryptoLibException.Create(SRequestInfoNil);
+  if AAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create(SAlgorithmNil);
+  if ASignature = nil then
+    raise EArgumentNilCryptoLibException.Create(SSignatureNil);
+
+  FReqInfo := ARequestInfo;
+  FSigAlgId := AAlgorithm;
+  FSigBits := ASignature;
+end;
+
+function TCertificationRequest.GetCertificationRequestInfo: ICertificationRequestInfo;
+begin
+  Result := FReqInfo;
+end;
+
+function TCertificationRequest.GetSignatureAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FSigAlgId;
+end;
+
+function TCertificationRequest.GetSignature: IDerBitString;
+begin
+  Result := FSigBits;
+end;
+
+function TCertificationRequest.GetSignatureOctets: TCryptoLibByteArray;
+begin
+  Result := FSigBits.GetOctets();
+end;
+
+function TCertificationRequest.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FReqInfo, FSigAlgId, FSigBits]);
+end;
+
+{ TPrivateKeyInfo }
+
+class function TPrivateKeyInfo.GetInstance(AObj: TObject): IPrivateKeyInfo;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IPrivateKeyInfo, Result) then
+    Exit;
+
+  Result := TPrivateKeyInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TPrivateKeyInfo.GetInstance(const AObj: IAsn1Object): IPrivateKeyInfo;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IPrivateKeyInfo, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetInstance(AObj);
+  Result := TPrivateKeyInfo.Create(LSequence);
+end;
+
+class function TPrivateKeyInfo.GetInstance(const AEncoded: TCryptoLibByteArray): IPrivateKeyInfo;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct PrivateKeyInfo from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TPrivateKeyInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IPrivateKeyInfo;
+begin
+  Result := TPrivateKeyInfo.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TPrivateKeyInfo.GetOptional(const AElement: IAsn1Encodable): IPrivateKeyInfo;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IPrivateKeyInfo, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TPrivateKeyInfo.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TPrivateKeyInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IPrivateKeyInfo;
+begin
+  Result := TPrivateKeyInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TPrivateKeyInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos, LVersionValue: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 3) or (LCount > 5) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FVersion := TDerInteger.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FPrivateKeyAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FPrivateKey := TAsn1OctetString.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+
+  FAttributes := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAsn1Set>(ASeq, LPos, 0, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAsn1Set
+    begin
+      Result := TAsn1Set.GetTagged(ATagged, AState);
+    end);
+
+  FPublicKey := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerBitString>(ASeq, LPos, 1, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerBitString
+    begin
+      Result := TDerBitString.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+  begin
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+  end;
+
+  LVersionValue := FVersion.IntValueExact;
+  if (FPublicKey <> nil) and (LVersionValue < 1) then
+  begin
+    raise EArgumentCryptoLibException.Create('''publicKey'' requires version v2(1) or later');
+  end;
+end;
+
+constructor TPrivateKeyInfo.Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+  const APrivateKey: IAsn1Encodable);
+begin
+  Create(APrivateKeyAlgorithm, APrivateKey, nil, nil);
+end;
+
+constructor TPrivateKeyInfo.Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+  const APrivateKey: IAsn1Encodable; const AAttributes: IAsn1Set);
+begin
+  Create(APrivateKeyAlgorithm, APrivateKey, AAttributes, nil);
+end;
+
+constructor TPrivateKeyInfo.Create(const APrivateKeyAlgorithm: IAlgorithmIdentifier;
+  const APrivateKey: IAsn1Encodable; const AAttributes: IAsn1Set;
+  const APublicKey: TCryptoLibByteArray);
+begin
+  inherited Create();
+
+  if APublicKey <> nil then
+    FVersion := TDerInteger.ValueOf(1)
+  else
+    FVersion := TDerInteger.Zero;
+
+  if APrivateKeyAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create('privateKeyAlgorithm');
+  if APrivateKey = nil then
+    raise EArgumentNilCryptoLibException.Create('privateKey');
+
+  FPrivateKeyAlgorithm := APrivateKeyAlgorithm;
+  FPrivateKey := TDerOctetString.Create(APrivateKey);
+  FAttributes := AAttributes;
+  if APublicKey <> nil then
+    FPublicKey := TDerBitString.FromContentsOptional(APublicKey)
+  else
+    FPublicKey := nil;
+end;
+
+function TPrivateKeyInfo.GetVersion: IDerInteger;
+begin
+  Result := FVersion;
+end;
+
+function TPrivateKeyInfo.GetPrivateKeyAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FPrivateKeyAlgorithm;
+end;
+
+function TPrivateKeyInfo.GetPrivateKey: IAsn1OctetString;
+begin
+  Result := FPrivateKey;
+end;
+
+function TPrivateKeyInfo.GetAttributes: IAsn1Set;
+begin
+  Result := FAttributes;
+end;
+
+function TPrivateKeyInfo.GetPublicKey: IDerBitString;
+begin
+  Result := FPublicKey;
+end;
+
+function TPrivateKeyInfo.HasPublicKey: Boolean;
+begin
+  Result := FPublicKey <> nil;
+end;
+
+function TPrivateKeyInfo.ParsePrivateKey: IAsn1Object;
+begin
+  Result := TAsn1Object.FromByteArray(FPrivateKey.GetOctets());
+end;
+
+function TPrivateKeyInfo.ParsePublicKey: IAsn1Object;
+var
+  LStream: TStream;
+  LBitStringParser: IAsn1BitStringParser;
+begin
+  if FPublicKey = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(FPublicKey, IAsn1BitStringParser, LBitStringParser) then
+  begin
+    LStream := LBitStringParser.GetOctetStream();
+    try
+      Result := TAsn1Object.FromStream(LStream);
+    finally
+      LStream.Free;
+    end;
+  end
+  else
+  begin
+    // Fallback: parse from bytes (assuming octet-aligned)
+    Result := TAsn1Object.FromByteArray(FPublicKey.GetOctets());
+  end;
+end;
+
+function TPrivateKeyInfo.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(5);
+  LV.Add([FVersion, FPrivateKeyAlgorithm, FPrivateKey]);
+  LV.AddOptionalTagged(False, 0, FAttributes);
+  LV.AddOptionalTagged(False, 1, FPublicKey);
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TRsaPrivateKeyStructure }
+
+class function TRsaPrivateKeyStructure.GetInstance(AObj: TObject): IRsaPrivateKeyStructure;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IRsaPrivateKeyStructure, Result) then
+    Exit;
+
+  Result := TRsaPrivateKeyStructure.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TRsaPrivateKeyStructure.GetInstance(const AObj: IAsn1Object): IRsaPrivateKeyStructure;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IRsaPrivateKeyStructure, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetInstance(AObj);
+  Result := TRsaPrivateKeyStructure.Create(LSequence);
+end;
+
+class function TRsaPrivateKeyStructure.GetInstance(const AEncoded: TCryptoLibByteArray): IRsaPrivateKeyStructure;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct RsaPrivateKeyStructure from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TRsaPrivateKeyStructure.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IRsaPrivateKeyStructure;
+begin
+  Result := TRsaPrivateKeyStructure.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TRsaPrivateKeyStructure.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IRsaPrivateKeyStructure;
+begin
+  Result := TRsaPrivateKeyStructure.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TRsaPrivateKeyStructure.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+  LVersion: IDerInteger;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 9 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  LVersion := TDerInteger.GetInstance(ASeq[0] as TAsn1Encodable);
+  FModulus := TDerInteger.GetInstance(ASeq[1] as TAsn1Encodable).Value;
+  FPublicExponent := TDerInteger.GetInstance(ASeq[2] as TAsn1Encodable).Value;
+  FPrivateExponent := TDerInteger.GetInstance(ASeq[3] as TAsn1Encodable).Value;
+  FPrime1 := TDerInteger.GetInstance(ASeq[4] as TAsn1Encodable).Value;
+  FPrime2 := TDerInteger.GetInstance(ASeq[5] as TAsn1Encodable).Value;
+  FExponent1 := TDerInteger.GetInstance(ASeq[6] as TAsn1Encodable).Value;
+  FExponent2 := TDerInteger.GetInstance(ASeq[7] as TAsn1Encodable).Value;
+  FCoefficient := TDerInteger.GetInstance(ASeq[8] as TAsn1Encodable).Value;
+
+  if not LVersion.HasValue(0) then
+    raise EArgumentCryptoLibException.Create('wrong version for RSA private key');
+
+  FVersion := LVersion;
+end;
+
+constructor TRsaPrivateKeyStructure.Create(const AModulus, APublicExponent, APrivateExponent,
+  APrime1, APrime2, AExponent1, AExponent2, ACoefficient: TBigInteger);
+begin
+  inherited Create();
+
+  if not AModulus.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('modulus');
+  if not APublicExponent.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('publicExponent');
+  if not APrivateExponent.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('privateExponent');
+  if not APrime1.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('prime1');
+  if not APrime2.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('prime2');
+  if not AExponent1.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('exponent1');
+  if not AExponent2.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('exponent2');
+  if not ACoefficient.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create('coefficient');
+
+  FVersion := TDerInteger.Zero;
+  FModulus := AModulus;
+  FPublicExponent := APublicExponent;
+  FPrivateExponent := APrivateExponent;
+  FPrime1 := APrime1;
+  FPrime2 := APrime2;
+  FExponent1 := AExponent1;
+  FExponent2 := AExponent2;
+  FCoefficient := ACoefficient;
+end;
+
+function TRsaPrivateKeyStructure.GetModulus: TBigInteger;
+begin
+  Result := FModulus;
+end;
+
+function TRsaPrivateKeyStructure.GetPublicExponent: TBigInteger;
+begin
+  Result := FPublicExponent;
+end;
+
+function TRsaPrivateKeyStructure.GetPrivateExponent: TBigInteger;
+begin
+  Result := FPrivateExponent;
+end;
+
+function TRsaPrivateKeyStructure.GetPrime1: TBigInteger;
+begin
+  Result := FPrime1;
+end;
+
+function TRsaPrivateKeyStructure.GetPrime2: TBigInteger;
+begin
+  Result := FPrime2;
+end;
+
+function TRsaPrivateKeyStructure.GetExponent1: TBigInteger;
+begin
+  Result := FExponent1;
+end;
+
+function TRsaPrivateKeyStructure.GetExponent2: TBigInteger;
+begin
+  Result := FExponent2;
+end;
+
+function TRsaPrivateKeyStructure.GetCoefficient: TBigInteger;
+begin
+  Result := FCoefficient;
+end;
+
+function TRsaPrivateKeyStructure.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([
+    FVersion,
+    TDerInteger.Create(FModulus),
+    TDerInteger.Create(FPublicExponent),
+    TDerInteger.Create(FPrivateExponent),
+    TDerInteger.Create(FPrime1),
+    TDerInteger.Create(FPrime2),
+    TDerInteger.Create(FExponent1),
+    TDerInteger.Create(FExponent2),
+    TDerInteger.Create(FCoefficient)
+  ]);
+end;
+
+{ TRsassaPssParameters }
+
+class constructor TRsassaPssParameters.Create;
+begin
+  Boot;
+end;
+
+class procedure TRsassaPssParameters.Boot;
+begin
+  DefaultHashAlgorithm := TAlgorithmIdentifier.Create(TOiwObjectIdentifiers.IdSha1, TDerNull.Instance);
+  DefaultMaskGenAlgorithm := TAlgorithmIdentifier.Create(TPkcsObjectIdentifiers.IdMgf1, DefaultHashAlgorithm);
+  DefaultMaskGenFunction := DefaultMaskGenAlgorithm; // Obsolete, use DefaultMaskGenAlgorithm
+  DefaultSaltLength := TDerInteger.ValueOf(20);
+  DefaultTrailerField := TDerInteger.One;
+end;
+
+class function TRsassaPssParameters.GetInstance(AObj: TObject): IRsassaPssParameters;
+var
+  LAsn1Obj: IAsn1Object;
+  LConvertible: IAsn1Convertible;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IRsassaPssParameters, Result) then
+    Exit;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Convertible, LConvertible) then
+  begin
+    LAsn1Obj := LConvertible.ToAsn1Object();
+    Result := GetInstance(LAsn1Obj);
+    Exit;
+  end;
+
+  Result := TRsassaPssParameters.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TRsassaPssParameters.GetInstance(const AObj: IAsn1Object): IRsassaPssParameters;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IRsassaPssParameters, Result) then
+    Exit;
+
+  Result := TRsassaPssParameters.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TRsassaPssParameters.GetInstance(const AEncoded: TCryptoLibByteArray): IRsassaPssParameters;
+begin
+  Result := GetInstance(TAsn1Object.FromByteArray(AEncoded));
+end;
+
+class function TRsassaPssParameters.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IRsassaPssParameters;
+begin
+  Result := TRsassaPssParameters.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TRsassaPssParameters.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IRsassaPssParameters;
+begin
+  Result := TRsassaPssParameters.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TRsassaPssParameters.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 4) then
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+
+  FHashAlgorithm := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAlgorithmIdentifier>(ASeq, LPos, 0, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAlgorithmIdentifier
+    begin
+      Result := TAlgorithmIdentifier.GetTagged(ATagged, AState);
+    end);
+  if FHashAlgorithm = nil then
+    FHashAlgorithm := DefaultHashAlgorithm;
+
+  FMaskGenAlgorithm := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAlgorithmIdentifier>(ASeq, LPos, 1, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAlgorithmIdentifier
+    begin
+      Result := TAlgorithmIdentifier.GetTagged(ATagged, AState);
+    end);
+  if FMaskGenAlgorithm = nil then
+    FMaskGenAlgorithm := DefaultMaskGenAlgorithm;
+
+  FSaltLength := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerInteger>(ASeq, LPos, 2, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerInteger
+    begin
+      Result := TDerInteger.GetTagged(ATagged, AState);
+    end);
+  if FSaltLength = nil then
+    FSaltLength := DefaultSaltLength;
+
+  FTrailerField := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerInteger>(ASeq, LPos, 3, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerInteger
+    begin
+      Result := TDerInteger.GetTagged(ATagged, AState);
+    end);
+  if FTrailerField = nil then
+    FTrailerField := DefaultTrailerField;
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.CreateRes(@SUnexpectedElementsInSequence);
+end;
+
+constructor TRsassaPssParameters.Create;
+begin
+  inherited Create();
+  FHashAlgorithm := DefaultHashAlgorithm;
+  FMaskGenAlgorithm := DefaultMaskGenAlgorithm;
+  FSaltLength := DefaultSaltLength;
+  FTrailerField := DefaultTrailerField;
+end;
+
+constructor TRsassaPssParameters.Create(const AHashAlgorithm, AMaskGenAlgorithm: IAlgorithmIdentifier;
+  const ASaltLength, ATrailerField: IDerInteger);
+begin
+  inherited Create();
+  if AHashAlgorithm = nil then
+    FHashAlgorithm := DefaultHashAlgorithm
+  else
+    FHashAlgorithm := AHashAlgorithm;
+
+  if AMaskGenAlgorithm = nil then
+    FMaskGenAlgorithm := DefaultMaskGenAlgorithm
+  else
+    FMaskGenAlgorithm := AMaskGenAlgorithm;
+
+  if ASaltLength = nil then
+    FSaltLength := DefaultSaltLength
+  else
+    FSaltLength := ASaltLength;
+
+  if ATrailerField = nil then
+    FTrailerField := DefaultTrailerField
+  else
+    FTrailerField := ATrailerField;
+end;
+
+function TRsassaPssParameters.GetHashAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FHashAlgorithm;
+end;
+
+function TRsassaPssParameters.GetMaskGenAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FMaskGenAlgorithm;
+end;
+
+function TRsassaPssParameters.GetSaltLength: IDerInteger;
+begin
+  Result := FSaltLength;
+end;
+
+function TRsassaPssParameters.GetTrailerField: IDerInteger;
+begin
+  Result := FTrailerField;
+end;
+
+function TRsassaPssParameters.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(4);
+
+  if not DefaultHashAlgorithm.Equals(FHashAlgorithm) then
+  begin
+    LV.Add(TDerTaggedObject.Create(True, 0, FHashAlgorithm));
+  end;
+
+  if not DefaultMaskGenAlgorithm.Equals(FMaskGenAlgorithm) then
+  begin
+    LV.Add(TDerTaggedObject.Create(True, 1, FMaskGenAlgorithm));
+  end;
+
+  if not DefaultSaltLength.Equals(FSaltLength) then
+  begin
+    LV.Add(TDerTaggedObject.Create(True, 2, FSaltLength));
+  end;
+
+  if not DefaultTrailerField.Equals(FTrailerField) then
+  begin
+    LV.Add(TDerTaggedObject.Create(True, 3, FTrailerField));
+  end;
+
+  Result := TDerSequence.Create(LV);
+end;
+
+end.

+ 55 - 1
CryptoLib/src/Asn1/Pkcs/ClpPkcsObjectIdentifiers.pas

@@ -54,6 +54,12 @@ type
     //
     DigestAlgorithm: String = '1.2.840.113549.2';
 
+    //
+    // pkcs-9 OBJECT IDENTIFIER ::= {
+    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+    //
+    Pkcs9: String = '1.2.840.113549.1.9';
+
   class var
 
     FIsBooted: Boolean;
@@ -86,7 +92,13 @@ type
     FMD2, FMD4, FMD5,
     FIdHmacWithSha1, FIdHmacWithSha224, FIdHmacWithSha256,
     FIdHmacWithSha384, FIdHmacWithSha512,
-    FIdHmacWithSha512_224, FIdHmacWithSha512_256
+    FIdHmacWithSha512_224, FIdHmacWithSha512_256,
+
+    // PKCS#9
+    FPkcs9AtEmailAddress,
+    FPkcs9AtChallengePassword,
+    FPkcs9AtUnstructuredName,
+    FPkcs9AtUnstructuredAddress
       : IDerObjectIdentifier;
 
     // PKCS#1 RSA getters
@@ -125,6 +137,12 @@ type
     class function GetIdHmacWithSha512_224: IDerObjectIdentifier; static; inline;
     class function GetIdHmacWithSha512_256: IDerObjectIdentifier; static; inline;
 
+    // PKCS#9 getters
+    class function GetPkcs9AtEmailAddress: IDerObjectIdentifier; static; inline;
+    class function GetPkcs9AtChallengePassword: IDerObjectIdentifier; static; inline;
+    class function GetPkcs9AtUnstructuredName: IDerObjectIdentifier; static; inline;
+    class function GetPkcs9AtUnstructuredAddress: IDerObjectIdentifier; static; inline;
+
     class constructor PkcsObjectIdentifiers();
 
   public
@@ -174,6 +192,14 @@ type
     class property IdHmacWithSha512_224: IDerObjectIdentifier read GetIdHmacWithSha512_224;
     class property IdHmacWithSha512_256: IDerObjectIdentifier read GetIdHmacWithSha512_256;
 
+    //
+    // PKCS#9
+    //
+    class property Pkcs9AtEmailAddress: IDerObjectIdentifier read GetPkcs9AtEmailAddress;
+    class property Pkcs9AtChallengePassword: IDerObjectIdentifier read GetPkcs9AtChallengePassword;
+    class property Pkcs9AtUnstructuredName: IDerObjectIdentifier read GetPkcs9AtUnstructuredName;
+    class property Pkcs9AtUnstructuredAddress: IDerObjectIdentifier read GetPkcs9AtUnstructuredAddress;
+
     class procedure Boot(); static;
 
   end;
@@ -222,6 +248,12 @@ begin
     FIdHmacWithSha512_224 := TDerObjectIdentifier.Create(DigestAlgorithm + '.12');
     FIdHmacWithSha512_256 := TDerObjectIdentifier.Create(DigestAlgorithm + '.13');
 
+    // PKCS#9
+    FPkcs9AtEmailAddress := TDerObjectIdentifier.Create(Pkcs9 + '.1');
+    FPkcs9AtUnstructuredName := TDerObjectIdentifier.Create(Pkcs9 + '.2');
+    FPkcs9AtChallengePassword := TDerObjectIdentifier.Create(Pkcs9 + '.7');
+    FPkcs9AtUnstructuredAddress := TDerObjectIdentifier.Create(Pkcs9 + '.8');
+
     FIsBooted := True;
   end;
 end;
@@ -374,6 +406,28 @@ begin
   Result := FIdHmacWithSha512_256;
 end;
 
+// PKCS#9 getters
+
+class function TPkcsObjectIdentifiers.GetPkcs9AtEmailAddress: IDerObjectIdentifier;
+begin
+  Result := FPkcs9AtEmailAddress;
+end;
+
+class function TPkcsObjectIdentifiers.GetPkcs9AtChallengePassword: IDerObjectIdentifier;
+begin
+  Result := FPkcs9AtChallengePassword;
+end;
+
+class function TPkcsObjectIdentifiers.GetPkcs9AtUnstructuredName: IDerObjectIdentifier;
+begin
+  Result := FPkcs9AtUnstructuredName;
+end;
+
+class function TPkcsObjectIdentifiers.GetPkcs9AtUnstructuredAddress: IDerObjectIdentifier;
+begin
+  Result := FPkcs9AtUnstructuredAddress;
+end;
+
 class constructor TPkcsObjectIdentifiers.PkcsObjectIdentifiers;
 begin
   TPkcsObjectIdentifiers.Boot;

+ 227 - 0
CryptoLib/src/Asn1/Sec/ClpSecAsn1Objects.pas

@@ -0,0 +1,227 @@
+{ *********************************************************************************** }
+{ *                              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 ClpSecAsn1Objects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpISecAsn1Objects,
+  ClpCryptoLibTypes,
+  ClpAsn1Utilities;
+
+resourcestring
+  SBadSequenceSize = 'Bad sequence size: %d';
+  SKeyNil = 'key';
+  SOrderBitLengthTooSmall = 'must be >= key bitlength';
+  SUnexpectedElementsInSequence = 'Unexpected elements in sequence';
+
+type
+  /// <summary>
+  /// the elliptic curve private key object from SEC 1
+  /// </summary>
+  TECPrivateKeyStructure = class(TAsn1Encodable, IECPrivateKeyStructure)
+
+  strict private
+  var
+    FVersion: IDerInteger;
+    FPrivateKey: IAsn1OctetString;
+    FParameters: IAsn1Encodable;
+    FPublicKey: IDerBitString;
+
+  strict protected
+    function GetVersion: IDerInteger;
+    function GetPrivateKey: IAsn1OctetString;
+    function GetParameters: IAsn1Encodable;
+    function GetPublicKey: IDerBitString;
+
+  public
+    class function GetInstance(AObj: TObject): IECPrivateKeyStructure; overload; static;
+    class function GetInstance(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IECPrivateKeyStructure; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IECPrivateKeyStructure; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(AOrderBitLength: Int32; const AKey: TBigInteger); overload;
+    constructor Create(AOrderBitLength: Int32; const AKey: TBigInteger;
+      const AParameters: IAsn1Encodable); overload;
+    constructor Create(AOrderBitLength: Int32; const AKey: TBigInteger;
+      const APublicKey: IDerBitString; const AParameters: IAsn1Encodable); overload;
+
+    function GetKey: TBigInteger;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: IDerInteger read GetVersion;
+    property PrivateKey: IAsn1OctetString read GetPrivateKey;
+    property Parameters: IAsn1Encodable read GetParameters;
+    property PublicKey: IDerBitString read GetPublicKey;
+
+  end;
+
+implementation
+
+{ TECPrivateKeyStructure }
+
+class function TECPrivateKeyStructure.GetInstance(AObj: TObject): IECPrivateKeyStructure;
+var
+  LInstance: IECPrivateKeyStructure;
+  LAsn1Obj: IAsn1Object;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IECPrivateKeyStructure, LInstance) then
+  begin
+    Result := LInstance;
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1Object, LAsn1Obj) then
+  begin
+    Result := TECPrivateKeyStructure.Create(TAsn1Sequence.GetInstance(LAsn1Obj));
+    Exit;
+  end;
+
+  Result := TECPrivateKeyStructure.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TECPrivateKeyStructure.GetInstance(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IECPrivateKeyStructure;
+begin
+  Result := TECPrivateKeyStructure.Create(TAsn1Sequence.GetInstance(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TECPrivateKeyStructure.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IECPrivateKeyStructure;
+begin
+  Result := TECPrivateKeyStructure.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TECPrivateKeyStructure.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  
+  if (LCount < 2) or (LCount > 4) then
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+
+  FVersion := TDerInteger.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  
+  FPrivateKey := TAsn1OctetString.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  
+  FParameters := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAsn1Encodable>(ASeq, LPos, 0, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAsn1Encodable
+    begin
+      Result := ATagged.GetExplicitBaseObject();
+    end);
+  
+  FPublicKey := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerBitString>(ASeq, LPos, 1, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerBitString
+    begin
+      Result := TDerBitString.GetTagged(ATagged, AState);
+    end);
+  
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+constructor TECPrivateKeyStructure.Create(AOrderBitLength: Int32; const AKey: TBigInteger);
+begin
+  Create(AOrderBitLength, AKey, nil);
+end;
+
+constructor TECPrivateKeyStructure.Create(AOrderBitLength: Int32; const AKey: TBigInteger;
+  const AParameters: IAsn1Encodable);
+begin
+  Create(AOrderBitLength, AKey, nil, AParameters);
+end;
+
+constructor TECPrivateKeyStructure.Create(AOrderBitLength: Int32; const AKey: TBigInteger;
+  const APublicKey: IDerBitString; const AParameters: IAsn1Encodable);
+var
+  LPrivateKeyContents: TCryptoLibByteArray;
+begin
+  inherited Create();
+  
+  if not AKey.IsInitialized then
+    raise EArgumentNilCryptoLibException.Create(SKeyNil);
+  if AOrderBitLength < AKey.BitLength then
+    raise EArgumentCryptoLibException.Create(SOrderBitLengthTooSmall);
+
+  LPrivateKeyContents := TBigIntegers.AsUnsignedByteArray((AOrderBitLength + 7) div 8, AKey);
+
+  FVersion := TDerInteger.One;
+  FPrivateKey := TDerOctetString.Create(LPrivateKeyContents);
+  FParameters := AParameters;
+  FPublicKey := APublicKey;
+end;
+
+function TECPrivateKeyStructure.GetVersion: IDerInteger;
+begin
+  Result := FVersion;
+end;
+
+function TECPrivateKeyStructure.GetPrivateKey: IAsn1OctetString;
+begin
+  Result := FPrivateKey;
+end;
+
+function TECPrivateKeyStructure.GetParameters: IAsn1Encodable;
+begin
+  Result := FParameters;
+end;
+
+function TECPrivateKeyStructure.GetPublicKey: IDerBitString;
+begin
+  Result := FPublicKey;
+end;
+
+function TECPrivateKeyStructure.GetKey: TBigInteger;
+begin
+  Result := TBigInteger.Create(1, FPrivateKey.GetOctets());
+end;
+
+function TECPrivateKeyStructure.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(4);
+  LV.Add(FVersion);
+  LV.Add(FPrivateKey);
+  LV.AddOptionalTagged(True, 0, FParameters);
+  LV.AddOptionalTagged(True, 1, FPublicKey);
+  Result := TDerSequence.Create(LV);
+end;
+
+end.

+ 362 - 0
CryptoLib/src/Asn1/X500/ClpIetfUtilities.pas

@@ -0,0 +1,362 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIetfUtilities;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes,
+  ClpPlatform,
+  ClpEncoders;
+
+type
+  /// <summary>
+  /// IETF utilities for X.500 name handling.
+  /// </summary>
+  TIetfUtilities = class sealed(TObject)
+  strict private
+    class function IsHexDigit(AC: Char): Boolean; static;
+    /// <summary>
+    /// Convert a hexadecimal character to its integer value.
+    /// </summary>
+    class function ConvertHex(AC: Char): Int32; static;
+    class function DecodeObject(const AOValue: String): IAsn1Object; static;
+
+  public
+    /// <summary>
+    /// Unescape a string value, handling quotes, escapes, and hex encoding.
+    /// </summary>
+    class function Unescape(const AElt: String): String; static;
+    /// <summary>
+    /// Convert an ASN.1 encodable value to a string representation.
+    /// </summary>
+    class function ValueToString(const AValue: IAsn1Encodable): String; static;
+    /// <summary>
+    /// Canonicalize a string value.
+    /// </summary>
+    class function Canonicalize(const AStr: String): String; static;
+    /// <summary>
+    /// Get canonical string representation of an ASN.1 encodable value.
+    /// </summary>
+    class function CanonicalString(const AValue: IAsn1Encodable): String; static;
+    /// <summary>
+    /// Strip internal spaces from a string (collapse multiple spaces to single).
+    /// </summary>
+    class function StripInternalSpaces(const AStr: String): String; static;
+  end;
+
+implementation
+
+{ TIetfUtilities }
+
+class function TIetfUtilities.IsHexDigit(AC: Char): Boolean;
+begin
+  Result := (('0' <= AC) and (AC <= '9')) or (('a' <= AC) and (AC <= 'f')) or (('A' <= AC) and (AC <= 'F'));
+end;
+
+class function TIetfUtilities.ConvertHex(AC: Char): Int32;
+begin
+  if ('0' <= AC) and (AC <= '9') then
+    Result := Ord(AC) - Ord('0')
+  else if ('a' <= AC) and (AC <= 'f') then
+    Result := Ord(AC) - Ord('a') + 10
+  else
+    Result := Ord(AC) - Ord('A') + 10;
+end;
+
+class function TIetfUtilities.DecodeObject(const AOValue: String): IAsn1Object;
+var
+  LHexStr: String;
+  LBytes: TCryptoLibByteArray;
+begin
+  try
+    LHexStr := System.Copy(AOValue, 2, System.Length(AOValue) - 1);
+
+    LBytes := THex.Decode(LHexStr);
+    Result := TAsn1Object.FromByteArray(LBytes);
+  except
+    on E: Exception do
+      raise EInvalidOperationCryptoLibException.Create('unknown encoding in name: ' + E.Message);
+  end;
+end;
+
+class function TIetfUtilities.Unescape(const AElt: String): String;
+var
+  LSb: TStringBuilder;
+  LStart, I, LLastEscaped: Int32;
+  LEscaped, LQuoted, LNonWhiteSpaceEncountered: Boolean;
+  LC: Char;
+  LHex1: Int32;
+begin
+  if System.Length(AElt) < 1 then
+  begin
+    Result := AElt;
+    Exit;
+  end;
+
+  if (TPlatform.IndexOf(AElt, '\') = 0) and (TPlatform.IndexOf(AElt, '"') = 0) then
+  begin
+    Result := TPlatform.Trim(AElt);
+    Exit;
+  end;
+
+  LEscaped := False;
+  LQuoted := False;
+  LSb := TStringBuilder.Create(System.Length(AElt));
+  try
+
+    LStart := 1;
+    if (System.Length(AElt) > 0) and (AElt[1] = '\') then
+    begin
+      if (System.Length(AElt) > 1) and (AElt[2] = '#') then
+      begin
+        LStart := 3; // Skip '\#' (positions 1 and 2)
+        LSb.Append('\#');
+      end;
+    end;
+
+    LNonWhiteSpaceEncountered := False;
+    LLastEscaped := 0;
+    LHex1 := 0; // Store as Int32 (0 = no hex digit waiting)
+
+    for I := LStart to System.Length(AElt) do
+    begin
+      LC := AElt[I];
+
+      // nonWhiteSpaceEncountered = true;
+      if LC <> ' ' then
+        LNonWhiteSpaceEncountered := True;
+
+      if LC = '"' then
+      begin
+        if not LEscaped then
+        begin
+          LQuoted := not LQuoted;
+        end
+        else
+        begin
+          LSb.Append(LC);
+          LEscaped := False;
+        end;
+      end
+      else if (LC = '\') and (not LEscaped) and (not LQuoted) then
+      begin
+        LEscaped := True;
+        LLastEscaped := LSb.Length;
+      end
+      else
+      begin
+        if (LC = ' ') and (not LEscaped) and (not LNonWhiteSpaceEncountered) then
+        begin
+          Continue;
+        end;
+        if LEscaped and IsHexDigit(LC) then
+        begin
+          if LHex1 <> 0 then
+          begin
+            LSb.Append(Chr(ConvertHex(Chr(LHex1)) * 16 + ConvertHex(LC)));
+            LEscaped := False;
+            LHex1 := 0;
+            Continue;
+          end;
+          LHex1 := Ord(LC);
+          Continue;
+        end;
+        LSb.Append(LC);
+        LEscaped := False;
+      end;
+    end;
+
+    if LSb.Length > 0 then
+    begin
+      while (LSb.Length > 0) and 
+            (LSb.Chars[LSb.Length - 1] = ' ') and 
+            (LLastEscaped <> LSb.Length - 1) do
+      begin
+        LSb.Length := LSb.Length - 1;
+      end;
+      Result := LSb.ToString();
+    end
+    else
+      Result := '';
+  finally
+    LSb.Free;
+  end;
+end;
+
+class function TIetfUtilities.ValueToString(const AValue: IAsn1Encodable): String;
+var
+  LVBuf: TStringBuilder;
+  LV, LResult: String;
+  LStr: IAsn1String;
+  LEnd, LIndex, LStart, LEndBuf: Int32;
+begin
+  LVBuf := TStringBuilder.Create();
+  try
+    if Supports(AValue, IAsn1String, LStr) and (not Supports(AValue, IDerUniversalString)) then
+    begin
+      LV := LStr.GetString();
+      if (System.Length(LV) > 0) and (LV[1] = '#') then
+      begin
+        LVBuf.Append('\');
+      end;
+      LVBuf.Append(LV);
+    end
+    else
+    begin
+      try
+        LVBuf.Append('#');
+        LVBuf.Append(THex.Encode(AValue.ToAsn1Object().GetEncoded(TAsn1Encodable.Der), False));
+      except
+        on E: Exception do
+          raise EArgumentCryptoLibException.Create('Other value has no encoded form');
+      end;
+    end;
+
+    LResult := LVBuf.ToString();
+    LEnd := System.Length(LResult);
+    LIndex := 1; // Pascal strings are 1-based
+
+    if TPlatform.StartsWith(LResult, '\#') then
+    begin
+      System.Inc(LIndex, 2);
+    end;
+
+    while LIndex <= LEnd do
+    begin
+      case LResult[LIndex] of
+        ',', '"', '\', '+', '=', '<', '>', ';':
+          begin
+            System.Insert('\', LResult, LIndex);
+            System.Inc(LIndex, 2);
+            System.Inc(LEnd);
+          end;
+      else
+        System.Inc(LIndex);
+      end;
+    end;
+
+    LStart := 1;
+    if System.Length(LResult) > 0 then
+    begin
+      while (System.Length(LResult) >= LStart) and (LResult[LStart] = ' ') do
+      begin
+        System.Insert('\', LResult, LStart);
+        System.Inc(LStart, 2);
+      end;
+    end;
+
+    LEndBuf := System.Length(LResult);
+    while (LEndBuf >= 1) and (LResult[LEndBuf] = ' ') do
+    begin
+      System.Insert('\', LResult, LEndBuf);
+      System.Dec(LEndBuf);
+    end;
+
+    Result := LResult;
+  finally
+    LVBuf.Free;
+  end;
+end;
+
+class function TIetfUtilities.Canonicalize(const AStr: String): String;
+var
+  LV: String;
+  LObj: IAsn1Object;
+  LStr: IAsn1String;
+  LStart, LEnd: Int32;
+begin
+  LV := TPlatform.Trim(TPlatform.ToLowerInvariant(AStr));
+
+  if (System.Length(LV) > 0) and (LV[1] = '#') then
+  begin
+    LObj := DecodeObject(LV);
+    if Supports(LObj, IAsn1String, LStr) then
+    begin
+      LV := TPlatform.Trim(TPlatform.ToLowerInvariant(LStr.GetString()));
+    end;
+  end;
+
+  if System.Length(LV) > 1 then
+  begin
+    LStart := 1;
+    while (LStart + 1 <= System.Length(LV)) and (LV[LStart] = '\') and (LV[LStart + 1] = ' ') do
+    begin
+      System.Inc(LStart, 2);
+    end;
+
+    LEnd := System.Length(LV);
+    while (LEnd - 1 >= 1) and (LV[LEnd - 1] = '\') and (LV[LEnd] = ' ') do
+    begin
+      System.Dec(LEnd, 2);
+    end;
+
+    if (LStart > 1) or (LEnd < System.Length(LV)) then
+    begin
+      LV := System.Copy(LV, LStart, LEnd - LStart + 1);
+    end;
+  end;
+
+  Result := StripInternalSpaces(LV);
+end;
+
+class function TIetfUtilities.CanonicalString(const AValue: IAsn1Encodable): String;
+begin
+  Result := Canonicalize(ValueToString(AValue));
+end;
+
+class function TIetfUtilities.StripInternalSpaces(const AStr: String): String;
+var
+  LSb: TStringBuilder;
+  I: Int32;
+  LC1, LC2: Char;
+begin
+  if System.Length(AStr) = 0 then
+  begin
+    Result := '';
+    Exit;
+  end;
+
+  LSb := TStringBuilder.Create();
+  try
+    LC1 := AStr[1];
+    LSb.Append(LC1);
+
+    for I := 2 to System.Length(AStr) do
+    begin
+      LC2 := AStr[I];
+      if not ((LC1 = ' ') and (LC2 = ' ')) then
+      begin
+        LSb.Append(LC2);
+      end;
+      LC1 := LC2;
+    end;
+
+    Result := LSb.ToString();
+  finally
+    LSb.Free;
+  end;
+end;
+
+end.

+ 60 - 0
CryptoLib/src/Asn1/X509/ClpRfc5280Asn1Utilities.pas

@@ -0,0 +1,60 @@
+{ *********************************************************************************** }
+{ *                              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 ClpRfc5280Asn1Utilities;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpDateTimeUtilities,
+  ClpAsn1Objects,
+  ClpIAsn1Objects;
+
+type
+  /// <summary>
+  /// RFC 5280 ASN.1 utilities for creating time objects.
+  /// </summary>
+  TRfc5280Asn1Utilities = class sealed(TObject)
+  public
+    /// <summary>
+    /// Create a GeneralizedTime from a DateTime with second precision.
+    /// </summary>
+    class function CreateGeneralizedTime(const ADateTime: TDateTime): IAsn1GeneralizedTime; static;
+    /// <summary>
+    /// Create a UtcTime from a DateTime with 2049 as the two-digit year maximum.
+    /// </summary>
+    class function CreateUtcTime(const ADateTime: TDateTime): IAsn1UtcTime; static;
+  end;
+
+implementation
+
+{ TRfc5280Asn1Utilities }
+
+class function TRfc5280Asn1Utilities.CreateGeneralizedTime(const ADateTime: TDateTime): IAsn1GeneralizedTime;
+begin
+  Result := TDerGeneralizedTime.Create(TDateTimeUtilities.WithPrecisionSecond(ADateTime));
+end;
+
+class function TRfc5280Asn1Utilities.CreateUtcTime(const ADateTime: TDateTime): IAsn1UtcTime;
+begin
+  Result := TDerUtcTime.Create(ADateTime, 2049);
+end;
+
+end.

+ 7037 - 0
CryptoLib/src/Asn1/X509/ClpX509Asn1Objects.pas

@@ -0,0 +1,7037 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509Asn1Objects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  DateUtils,
+  Classes,
+  Generics.Collections,
+  SyncObjs,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIX509Extension,
+  ClpX509Extension,
+  ClpIX509NameEntryConverter,
+  ClpX509NameEntryConverter,
+  ClpIX509NameTokenizer,
+  ClpX509NameTokenizer,
+  ClpX509ObjectIdentifiers,
+  ClpPkcsObjectIdentifiers,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpCryptoLibTypes,
+  ClpArrayUtils,
+  ClpAsn1Utilities,
+  ClpCollectionUtilities,
+  ClpPlatform,
+  ClpIPAddressUtilities,
+  ClpRfc5280Asn1Utilities,
+  ClpDateTimeUtilities,
+  ClpIetfUtilities,
+  ClpEncoders,
+  ClpCryptoLibComparers;
+
+resourcestring
+  SBadSequenceSize = 'Bad sequence size: %d';
+  SAlgorithmNil = 'algorithm';
+  SDigestAlgorithmNil = 'digestAlgorithm';
+  SDigestNil = 'digest';
+  SAlgorithmNilAlt = 'algorithm';
+  SSignatureNil = 'signature';
+  SNotCA = 'Not a valid RSA modulus';
+  SNotValidPublicExponent = 'Not a valid RSA public exponent';
+  SSubjectPublicKeyInfoNil = 'subjectPublicKeyInfo';
+  SNotBeforeNil = 'notBefore';
+  SNotAfterNil = 'notAfter';
+  STbsCertNil = 'tbsCert';
+  SSigAlgIDNil = 'sigAlgID';
+  SSigNil = 'sig';
+  SVersionNumberNotRecognised = 'version number not recognised';
+  SUnexpectedElementsInSequence = 'Unexpected elements in sequence';
+  SInvalidKeyIdentifier = 'keyID';
+  SInvalidKeyID = 'keyID';
+
+type
+  /// <summary>
+  /// The AlgorithmIdentifier object.
+  /// <code>
+  /// AlgorithmIdentifier ::= SEQUENCE {
+  ///   algorithm OBJECT IDENTIFIER,
+  ///   parameters ANY DEFINED BY algorithm OPTIONAL
+  /// }
+  /// </code>
+  /// </summary>
+  TAlgorithmIdentifier = class(TAsn1Encodable, IAlgorithmIdentifier)
+
+  strict private
+  var
+    FAlgorithm: IDerObjectIdentifier;
+    FParameters: IAsn1Encodable;
+
+  strict protected
+    function GetAlgorithm: IDerObjectIdentifier;
+    function GetParameters: IAsn1Encodable;
+
+  public
+    /// <summary>
+    /// Parse an AlgorithmIdentifier from an object.
+    /// </summary>
+    class function GetInstance(AObj: TObject): IAlgorithmIdentifier; overload; static;
+    /// <summary>
+    /// Parse an AlgorithmIdentifier from a byte array.
+    /// </summary>
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAlgorithmIdentifier; overload; static;
+    /// <summary>
+    /// Parse an AlgorithmIdentifier from a tagged object.
+    /// </summary>
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAlgorithmIdentifier; overload; static;
+    /// <summary>
+    /// Get optional AlgorithmIdentifier.
+    /// </summary>
+    class function GetOptional(const AElement: IAsn1Encodable): IAlgorithmIdentifier; static;
+    /// <summary>
+    /// Get tagged AlgorithmIdentifier.
+    /// </summary>
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAlgorithmIdentifier; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAlgorithm: IDerObjectIdentifier); overload;
+    constructor Create(const AAlgorithm: IDerObjectIdentifier;
+      const AParameters: IAsn1Encodable); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Algorithm: IDerObjectIdentifier read GetAlgorithm;
+    property Parameters: IAsn1Encodable read GetParameters;
+
+  end;
+
+  /// <summary>
+  /// The DigestInfo object.
+  /// DigestInfo ::= SEQUENCE {
+  ///   digestAlgorithm AlgorithmIdentifier,
+  ///   digest OCTET STRING
+  /// }
+  /// </summary>
+  TDigestInfo = class(TAsn1Encodable, IDigestInfo)
+
+  strict private
+  var
+    FDigestAlgorithm: IAlgorithmIdentifier;
+    FDigest: IAsn1OctetString;
+
+  strict protected
+    function GetDigestAlgorithm: IAlgorithmIdentifier;
+    function GetDigest: IAsn1OctetString;
+    function GetDigestBytes: TCryptoLibByteArray;
+
+  public
+    /// <summary>
+    /// Parse a DigestInfo from an object.
+    /// </summary>
+    class function GetInstance(AObj: TObject): IDigestInfo; overload; static;
+    /// <summary>
+    /// Parse a DigestInfo from DER-encoded bytes.
+    /// </summary>
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IDigestInfo; overload; static;
+    /// <summary>
+    /// Parse a DigestInfo from a tagged object.
+    /// </summary>
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IDigestInfo; overload; static;
+    /// <summary>
+    /// Get tagged DigestInfo.
+    /// </summary>
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IDigestInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAlgId: IAlgorithmIdentifier;
+      const ADigest: TCryptoLibByteArray); overload;
+    constructor Create(const ADigestAlgorithm: IAlgorithmIdentifier;
+      const ADigest: IAsn1OctetString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property DigestAlgorithm: IAlgorithmIdentifier read GetDigestAlgorithm;
+    property Digest: IAsn1OctetString read GetDigest;
+
+  end;
+
+  /// <summary>
+  /// The GeneralName object.
+  /// </summary>
+  TGeneralName = class(TAsn1Encodable, IGeneralName, IAsn1Choice)
+
+  strict private
+  var
+    FTag: Int32;
+    FName: IAsn1Encodable;
+
+  strict private
+    class function ToGeneralNameEncoding(const AIp: String): TCryptoLibByteArray; static;
+    class procedure CopyInts(const AParsedIp: TCryptoLibInt32Array; var AAddr: TCryptoLibByteArray; AOffset: Int32); static;
+    class procedure ParseIPv4(const AIp: String; var AAddr: TCryptoLibByteArray; AOffset: Int32); static;
+    class procedure ParseIPv4Mask(const AMask: String; var AAddr: TCryptoLibByteArray; AOffset: Int32); static;
+    class function ParseIPv6(const AIp: String): TCryptoLibInt32Array; static;
+    class function ParseIPv6Mask(const AMask: String): TCryptoLibInt32Array; static;
+
+  strict protected
+    function GetTagNo: Int32;
+    function GetName: IAsn1Encodable;
+
+  public
+    const
+      OtherName = 0;
+      Rfc822Name = 1;
+      DnsName = 2;
+      X400Address = 3;
+      DirectoryName = 4;
+      EdiPartyName = 5;
+      UniformResourceIdentifier = 6;
+      IPAddress = 7;
+      RegisteredID = 8;
+
+    class function GetInstance(AObj: TObject): IGeneralName; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IGeneralName; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IGeneralName; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IGeneralName; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IGeneralName; static;
+    class function GetOptionalBaseObject(const ATaggedObject: IAsn1TaggedObject): IAsn1Encodable; static;
+
+    constructor Create(const ADirectoryName: IX509Name); overload;
+    constructor Create(const AName: IAsn1Object; ATag: Int32); overload;
+    constructor Create(ATag: Int32; const AName: IAsn1Encodable); overload;
+    constructor Create(ATag: Int32; const AName: String); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+    property TagNo: Int32 read GetTagNo;
+    property Name: IAsn1Encodable read GetName;
+
+  end;
+
+  /// <summary>
+  /// The GeneralNames object.
+  /// </summary>
+  TGeneralNames = class(TAsn1Encodable, IGeneralNames)
+
+  strict private
+  var
+    FNames: TCryptoLibGenericArray<IGeneralName>;
+
+  strict protected
+    function GetCount: Int32;
+    function GetNames: TCryptoLibGenericArray<IGeneralName>;
+
+  public
+    class function GetInstance(AObj: TObject): IGeneralNames; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IGeneralNames; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IGeneralNames; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IGeneralNames; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IGeneralNames; static;
+
+    constructor Create(const AName: IGeneralName); overload;
+    constructor Create(const ANames: TCryptoLibGenericArray<IGeneralName>); overload;
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+    property Count: Int32 read GetCount;
+
+  end;
+
+  /// <summary>
+  /// The KeyUsage object (extends TDerBitString).
+  /// </summary>
+  TKeyUsage = class(TDerBitString, IKeyUsage)
+
+  public
+    const
+      DigitalSignature = (1 shl 7);
+      NonRepudiation = (1 shl 6);
+      KeyEncipherment = (1 shl 5);
+      DataEncipherment = (1 shl 4);
+      KeyAgreement = (1 shl 3);
+      KeyCertSign = (1 shl 2);
+      CrlSign = (1 shl 1);
+      EncipherOnly = (1 shl 0);
+      DecipherOnly = (1 shl 15);
+
+    class function GetInstance(AObj: TObject): IKeyUsage; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IKeyUsage; overload; static;
+
+    constructor Create(AUsage: Int32); overload;
+    constructor Create(const AUsage: IDerBitString); overload;
+
+    function ToString: String; override;
+
+  end;
+
+  /// <summary>
+  /// The AuthorityKeyIdentifier object.
+  /// </summary>
+  TAuthorityKeyIdentifier = class(TAsn1Encodable, IAuthorityKeyIdentifier)
+
+  strict private
+  var
+    FKeyIdentifier: IAsn1OctetString;
+    FAuthorityCertIssuer: IGeneralNames;
+    FAuthorityCertSerialNumber: IDerInteger;
+
+  strict protected
+    function GetKeyIdentifier: IAsn1OctetString;
+    function GetAuthorityCertIssuer: IGeneralNames;
+    function GetAuthorityCertSerialNumber: IDerInteger;
+
+  public
+    class function GetInstance(AObj: TObject): IAuthorityKeyIdentifier; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAuthorityKeyIdentifier; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAuthorityKeyIdentifier; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAuthorityKeyIdentifier; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): IAuthorityKeyIdentifier; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AKeyIdentifier: TCryptoLibByteArray); overload;
+    constructor Create(const AKeyIdentifier: TCryptoLibByteArray;
+      const AAuthorityCertIssuer: IGeneralNames;
+      const AAuthorityCertSerialNumber: TBigInteger); overload;
+    constructor Create(const AKeyIdentifier: IAsn1OctetString); overload;
+    constructor Create(const AKeyIdentifier: IAsn1OctetString;
+      const AAuthorityCertIssuer: IGeneralNames;
+      const AAuthorityCertSerialNumber: IDerInteger); overload;
+    constructor Create(const AAuthorityCertIssuer: IGeneralNames;
+      const AAuthorityCertSerialNumber: TBigInteger); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property KeyIdentifier: IAsn1OctetString read GetKeyIdentifier;
+    property AuthorityCertIssuer: IGeneralNames read GetAuthorityCertIssuer;
+    property AuthorityCertSerialNumber: IDerInteger read GetAuthorityCertSerialNumber;
+
+  end;
+
+  /// <summary>
+  /// The ExtendedKeyUsage object.
+  /// </summary>
+  TExtendedKeyUsage = class(TAsn1Encodable, IExtendedKeyUsage)
+
+  strict private
+  var
+    FUsageTable: TDictionary<IDerObjectIdentifier, Boolean>;
+    FSeq: IAsn1Sequence;
+
+  strict protected
+    function HasKeyPurposeId(const AKeyPurposeId: IDerObjectIdentifier): Boolean;
+    function GetAllUsages: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetCount: Int32;
+
+  public
+    class function GetInstance(AObj: TObject): IExtendedKeyUsage; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IExtendedKeyUsage; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IExtendedKeyUsage; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IExtendedKeyUsage; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): IExtendedKeyUsage; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AUsages: TCryptoLibGenericArray<IDerObjectIdentifier>); overload;
+
+    destructor Destroy; override;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Count: Int32 read GetCount;
+
+  end;
+
+  /// <summary>
+  /// The X509Extensions object.
+  /// </summary>
+  TX509Extensions = class(TAsn1Encodable, IX509Extensions)
+
+  strict private
+  var
+    FExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>;
+    FOrdering: TList<IDerObjectIdentifier>;
+
+  strict private
+    class var
+      FSubjectDirectoryAttributes: IDerObjectIdentifier;
+      FSubjectKeyIdentifier: IDerObjectIdentifier;
+      FKeyUsage: IDerObjectIdentifier;
+      FPrivateKeyUsagePeriod: IDerObjectIdentifier;
+      FSubjectAlternativeName: IDerObjectIdentifier;
+      FIssuerAlternativeName: IDerObjectIdentifier;
+      FBasicConstraints: IDerObjectIdentifier;
+      FCrlNumber: IDerObjectIdentifier;
+      FReasonCode: IDerObjectIdentifier;
+      FInstructionCode: IDerObjectIdentifier;
+      FInvalidityDate: IDerObjectIdentifier;
+      FDeltaCrlIndicator: IDerObjectIdentifier;
+      FIssuingDistributionPoint: IDerObjectIdentifier;
+      FCertificateIssuer: IDerObjectIdentifier;
+      FNameConstraints: IDerObjectIdentifier;
+      FCrlDistributionPoints: IDerObjectIdentifier;
+      FCertificatePolicies: IDerObjectIdentifier;
+      FPolicyMappings: IDerObjectIdentifier;
+      FAuthorityKeyIdentifier: IDerObjectIdentifier;
+      FPolicyConstraints: IDerObjectIdentifier;
+      FExtendedKeyUsage: IDerObjectIdentifier;
+      FFreshestCrl: IDerObjectIdentifier;
+      FInhibitAnyPolicy: IDerObjectIdentifier;
+      FAuthorityInfoAccess: IDerObjectIdentifier;
+      FBiometricInfo: IDerObjectIdentifier;
+      FQCStatements: IDerObjectIdentifier;
+      FAuditIdentity: IDerObjectIdentifier;
+      FSubjectInfoAccess: IDerObjectIdentifier;
+      FLogoType: IDerObjectIdentifier;
+      FNoRevAvail: IDerObjectIdentifier;
+      FTargetInformation: IDerObjectIdentifier;
+      FExpiredCertsOnCrl: IDerObjectIdentifier;
+      FSubjectAltPublicKeyInfo: IDerObjectIdentifier;
+      FAltSignatureAlgorithm: IDerObjectIdentifier;
+      FAltSignatureValue: IDerObjectIdentifier;
+      FDraftDeltaCertificateDescriptor: IDerObjectIdentifier;
+
+    class procedure Boot; static;
+    class constructor Create;
+    class destructor Destroy;
+
+  strict protected
+    function GetCount: Int32;
+    function GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+    function GetExtensionParsedValue(const AOid: IDerObjectIdentifier): IAsn1Object; overload;
+    function GetExtensionValue(const AOid: IDerObjectIdentifier): IAsn1OctetString;
+    function GetExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetNonCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetExtensionOidsInternal(AIsCritical: Boolean): TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function HasAnyCriticalExtensions: Boolean;
+    function Equivalent(const AOther: IX509Extensions): Boolean;
+
+  public
+    // Extension OID constants
+    class property SubjectDirectoryAttributes: IDerObjectIdentifier read FSubjectDirectoryAttributes;
+    class property SubjectKeyIdentifier: IDerObjectIdentifier read FSubjectKeyIdentifier;
+    class property KeyUsage: IDerObjectIdentifier read FKeyUsage;
+    class property PrivateKeyUsagePeriod: IDerObjectIdentifier read FPrivateKeyUsagePeriod;
+    class property SubjectAlternativeName: IDerObjectIdentifier read FSubjectAlternativeName;
+    class property IssuerAlternativeName: IDerObjectIdentifier read FIssuerAlternativeName;
+    class property BasicConstraints: IDerObjectIdentifier read FBasicConstraints;
+    class property CrlNumber: IDerObjectIdentifier read FCrlNumber;
+    class property ReasonCode: IDerObjectIdentifier read FReasonCode;
+    class property InstructionCode: IDerObjectIdentifier read FInstructionCode;
+    class property InvalidityDate: IDerObjectIdentifier read FInvalidityDate;
+    class property DeltaCrlIndicator: IDerObjectIdentifier read FDeltaCrlIndicator;
+    class property IssuingDistributionPoint: IDerObjectIdentifier read FIssuingDistributionPoint;
+    class property CertificateIssuer: IDerObjectIdentifier read FCertificateIssuer;
+    class property NameConstraints: IDerObjectIdentifier read FNameConstraints;
+    class property CrlDistributionPoints: IDerObjectIdentifier read FCrlDistributionPoints;
+    class property CertificatePolicies: IDerObjectIdentifier read FCertificatePolicies;
+    class property PolicyMappings: IDerObjectIdentifier read FPolicyMappings;
+    class property AuthorityKeyIdentifier: IDerObjectIdentifier read FAuthorityKeyIdentifier;
+    class property PolicyConstraints: IDerObjectIdentifier read FPolicyConstraints;
+    class property ExtendedKeyUsage: IDerObjectIdentifier read FExtendedKeyUsage;
+    class property FreshestCrl: IDerObjectIdentifier read FFreshestCrl;
+    class property InhibitAnyPolicy: IDerObjectIdentifier read FInhibitAnyPolicy;
+    class property AuthorityInfoAccess: IDerObjectIdentifier read FAuthorityInfoAccess;
+    class property BiometricInfo: IDerObjectIdentifier read FBiometricInfo;
+    class property QCStatements: IDerObjectIdentifier read FQCStatements;
+    class property AuditIdentity: IDerObjectIdentifier read FAuditIdentity;
+    class property SubjectInfoAccess: IDerObjectIdentifier read FSubjectInfoAccess;
+    class property LogoType: IDerObjectIdentifier read FLogoType;
+    class property NoRevAvail: IDerObjectIdentifier read FNoRevAvail;
+    class property TargetInformation: IDerObjectIdentifier read FTargetInformation;
+    class property ExpiredCertsOnCrl: IDerObjectIdentifier read FExpiredCertsOnCrl;
+    class property SubjectAltPublicKeyInfo: IDerObjectIdentifier read FSubjectAltPublicKeyInfo;
+    class property AltSignatureAlgorithm: IDerObjectIdentifier read FAltSignatureAlgorithm;
+    class property AltSignatureValue: IDerObjectIdentifier read FAltSignatureValue;
+    class property DraftDeltaCertificateDescriptor: IDerObjectIdentifier read FDraftDeltaCertificateDescriptor;
+
+    class function GetInstance(AObj: TObject): IX509Extensions; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IX509Extensions; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IX509Extensions; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IX509Extensions; static;
+    class function GetExtensionParsedValue(const AExtensions: IX509Extensions;
+      const AOid: IDerObjectIdentifier): IAsn1Object; overload; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>); overload;
+    constructor Create(const AOrdering: TList<IDerObjectIdentifier>;
+      const AExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>); overload;
+    constructor Create(const AOids: TList<IDerObjectIdentifier>;
+      const AValues: TList<IX509Extension>); overload;
+
+    destructor Destroy; override;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToAsn1ObjectTrimmed: IAsn1Sequence;
+
+    property Count: Int32 read GetCount;
+
+  end;
+
+  /// <summary>
+  /// The X509Name object.
+  /// </summary>
+  TX509Name = class(TAsn1Encodable, IX509Name, IAsn1Choice)
+
+  strict private
+  var
+    FOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    FValues: TCryptoLibStringArray;
+    FValueList: TCryptoLibStringArray;
+    FAdded: TCryptoLibBooleanArray; // Track which attributes are added to current RDN
+    FSeq: IAsn1Sequence; // Cached sequence
+    FConverter: IX509NameEntryConverter; // Converter for value encoding
+
+  strict private
+    class var
+      FDefaultReverse: Boolean;
+      FDefaultReverseLock: TCriticalSection;
+      FDefaultSymbols: TDictionary<IDerObjectIdentifier, String>;
+      FRFC2253Symbols: TDictionary<IDerObjectIdentifier, String>;
+      FRFC1779Symbols: TDictionary<IDerObjectIdentifier, String>;
+      FDefaultLookup: TDictionary<String, IDerObjectIdentifier>;
+      FC: IDerObjectIdentifier;
+      FO: IDerObjectIdentifier;
+      FOU: IDerObjectIdentifier;
+      FT: IDerObjectIdentifier;
+      FCN: IDerObjectIdentifier;
+      FStreet: IDerObjectIdentifier;
+      FSerialNumber: IDerObjectIdentifier;
+      FL: IDerObjectIdentifier;
+      FST: IDerObjectIdentifier;
+      FSurname: IDerObjectIdentifier;
+      FGivenName: IDerObjectIdentifier;
+      FInitials: IDerObjectIdentifier;
+      FGeneration: IDerObjectIdentifier;
+      FUniqueIdentifier: IDerObjectIdentifier;
+      FDescription: IDerObjectIdentifier;
+      FBusinessCategory: IDerObjectIdentifier;
+      FPostalCode: IDerObjectIdentifier;
+      FDnQualifier: IDerObjectIdentifier;
+      FPseudonym: IDerObjectIdentifier;
+      FRole: IDerObjectIdentifier;
+      FDateOfBirth: IDerObjectIdentifier;
+      FPlaceOfBirth: IDerObjectIdentifier;
+      FGender: IDerObjectIdentifier;
+      FCountryOfCitizenship: IDerObjectIdentifier;
+      FCountryOfResidence: IDerObjectIdentifier;
+      FNameAtBirth: IDerObjectIdentifier;
+      FPostalAddress: IDerObjectIdentifier;
+      FDmdName: IDerObjectIdentifier;
+      FTelephoneNumber: IDerObjectIdentifier;
+      FOrganizationIdentifier: IDerObjectIdentifier;
+      FName: IDerObjectIdentifier;
+      FEmailAddress: IDerObjectIdentifier;
+      FUnstructuredName: IDerObjectIdentifier;
+      FUnstructuredAddress: IDerObjectIdentifier;
+      FE: IDerObjectIdentifier;
+      FDC: IDerObjectIdentifier;
+      FUID: IDerObjectIdentifier;
+      FJurisdictionC: IDerObjectIdentifier;
+      FJurisdictionST: IDerObjectIdentifier;
+      FJurisdictionL: IDerObjectIdentifier;
+
+    class procedure Boot; static;
+    class function GetDefaultReverse: Boolean; static;
+    class procedure SetDefaultReverse(const AValue: Boolean); static;
+    class constructor Create;
+    class destructor Destroy;
+
+  strict protected
+    function GetOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetValues: TCryptoLibStringArray; overload;
+    function GetValueList: TCryptoLibStringArray; overload;
+    function GetValue(const AOid: IDerObjectIdentifier): String;
+    function GetValues(const AOid: IDerObjectIdentifier): TCryptoLibStringArray; overload;
+
+  public
+    // OID constants
+    class property C: IDerObjectIdentifier read FC;
+    class property O: IDerObjectIdentifier read FO;
+    class property OU: IDerObjectIdentifier read FOU;
+    class property T: IDerObjectIdentifier read FT;
+    class property CN: IDerObjectIdentifier read FCN;
+    class property Street: IDerObjectIdentifier read FStreet;
+    class property SerialNumber: IDerObjectIdentifier read FSerialNumber;
+    class property L: IDerObjectIdentifier read FL;
+    class property ST: IDerObjectIdentifier read FST;
+    class property Surname: IDerObjectIdentifier read FSurname;
+    class property GivenName: IDerObjectIdentifier read FGivenName;
+    class property Initials: IDerObjectIdentifier read FInitials;
+    class property Generation: IDerObjectIdentifier read FGeneration;
+    class property UniqueIdentifier: IDerObjectIdentifier read FUniqueIdentifier;
+    class property Description: IDerObjectIdentifier read FDescription;
+    class property BusinessCategory: IDerObjectIdentifier read FBusinessCategory;
+    class property PostalCode: IDerObjectIdentifier read FPostalCode;
+    class property DnQualifier: IDerObjectIdentifier read FDnQualifier;
+    class property Pseudonym: IDerObjectIdentifier read FPseudonym;
+    class property Role: IDerObjectIdentifier read FRole;
+    class property DateOfBirth: IDerObjectIdentifier read FDateOfBirth;
+    class property PlaceOfBirth: IDerObjectIdentifier read FPlaceOfBirth;
+    class property Gender: IDerObjectIdentifier read FGender;
+    class property CountryOfCitizenship: IDerObjectIdentifier read FCountryOfCitizenship;
+    class property CountryOfResidence: IDerObjectIdentifier read FCountryOfResidence;
+    class property NameAtBirth: IDerObjectIdentifier read FNameAtBirth;
+    class property PostalAddress: IDerObjectIdentifier read FPostalAddress;
+    class property DmdName: IDerObjectIdentifier read FDmdName;
+    class property TelephoneNumber: IDerObjectIdentifier read FTelephoneNumber;
+    class property OrganizationIdentifier: IDerObjectIdentifier read FOrganizationIdentifier;
+    class property Name: IDerObjectIdentifier read FName;
+    class property EmailAddress: IDerObjectIdentifier read FEmailAddress;
+    class property UnstructuredName: IDerObjectIdentifier read FUnstructuredName;
+    class property UnstructuredAddress: IDerObjectIdentifier read FUnstructuredAddress;
+    class property E: IDerObjectIdentifier read FE;
+    class property DC: IDerObjectIdentifier read FDC;
+    class property UID: IDerObjectIdentifier read FUID;
+    class property JurisdictionC: IDerObjectIdentifier read FJurisdictionC;
+    class property JurisdictionST: IDerObjectIdentifier read FJurisdictionST;
+    class property JurisdictionL: IDerObjectIdentifier read FJurisdictionL;
+
+    class property DefaultReverse: Boolean read GetDefaultReverse write SetDefaultReverse;
+    class property DefaultSymbols: TDictionary<IDerObjectIdentifier, String> read FDefaultSymbols;
+    class property RFC2253Symbols: TDictionary<IDerObjectIdentifier, String> read FRFC2253Symbols;
+    class property RFC1779Symbols: TDictionary<IDerObjectIdentifier, String> read FRFC1779Symbols;
+    class property DefaultLookup: TDictionary<String, IDerObjectIdentifier> read FDefaultLookup;
+
+    class function GetInstance(AObj: TObject): IX509Name; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IX509Name; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IX509Name; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IX509Name; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IX509Name; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AName: String); overload;
+    constructor Create(const AReverse: Boolean; const AName: String); overload;
+    constructor Create(const AReverse: Boolean; const ATable: TDictionary<String, String>;
+      const AName: String); overload;
+    constructor Create(const AOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+      const AValues: TCryptoLibStringArray); overload;
+    constructor Create(const AOrdering: TList<IDerObjectIdentifier>;
+      const AAttributes: TDictionary<IDerObjectIdentifier, String>); overload;
+    constructor Create(const AOrdering: TList<IDerObjectIdentifier>;
+      const AAttributes: TDictionary<IDerObjectIdentifier, String>;
+      const AConverter: IX509NameEntryConverter); overload;
+    constructor Create(const AOids: TList<IDerObjectIdentifier>;
+      const AValues: TList<String>); overload;
+    constructor Create(const AOids: TList<IDerObjectIdentifier>;
+      const AValues: TList<String>; const AConverter: IX509NameEntryConverter); overload;
+    constructor Create(const ADirName: String; const AConverter: IX509NameEntryConverter); overload;
+    constructor Create(const AReverse: Boolean; const ADirName: String;
+      const AConverter: IX509NameEntryConverter); overload;
+    constructor Create(const AReverse: Boolean; const ALookup: TDictionary<String, IDerObjectIdentifier>;
+      const ADirName: String); overload;
+    constructor Create(const AReverse: Boolean; const ALookup: TDictionary<String, IDerObjectIdentifier>;
+      const ADirName: String; const AConverter: IX509NameEntryConverter); overload;
+
+    function GetOidList: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetValueList(const AOid: IDerObjectIdentifier = nil): TCryptoLibStringArray; overload;
+    function Equivalent(const AOther: IX509Name; AInOrder: Boolean = False): Boolean;
+    function ToString(AReverse: Boolean; const AOidSymbols: TDictionary<IDerObjectIdentifier, String>): String; overload;
+    function ToString: String; overload; override;
+    function ToString(const AOid: IDerObjectIdentifier): String; overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Oids: TCryptoLibGenericArray<IDerObjectIdentifier> read GetOids;
+    property Values: TCryptoLibStringArray read GetValues;
+
+  private
+    class function CreateDefaultConverter: IX509NameEntryConverter; static;
+    class function DecodeOid(const AName: String;
+      const ALookup: TDictionary<String, IDerObjectIdentifier>): IDerObjectIdentifier; static;
+    class procedure AppendValue(const ABuf: TStringBuilder;
+      const AOidSymbols: TDictionary<IDerObjectIdentifier, String>;
+      const AOid: IDerObjectIdentifier; const AVal: String); static;
+    class function EquivalentStrings(const AS1, AS2: String): Boolean; static;
+    class function NextToken(const ATokenizer: IX509NameTokenizer): String; overload; static;
+    class function NextToken(const ATokenizer: IX509NameTokenizer; AExpectMoreTokens: Boolean): String; overload; static;
+  strict private
+    procedure AddAttribute(const ALookup: TDictionary<String, IDerObjectIdentifier>;
+      const AToken: String; AAdded: Boolean; const AOidList: TList<IDerObjectIdentifier>;
+      const AValueList: TList<String>; const AAddedList: TList<Boolean>);
+
+  end;
+
+  /// <summary>
+  /// The Time object (CHOICE type: Asn1UtcTime or Asn1GeneralizedTime).
+  /// <pre>
+  /// Time ::= CHOICE {
+  ///             utcTime        UTCTime,
+  ///             generalTime    GeneralizedTime }
+  /// </pre>
+  /// </summary>
+  TTime = class(TAsn1Encodable, ITime, IAsn1Choice)
+
+  strict private
+  var
+    FTimeObject: IAsn1Object;
+
+  strict protected
+    function GetTimeObject: IAsn1Object;
+
+  public
+    class function GetInstance(AObj: TObject): ITime; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ITime; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ITime; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): ITime; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ITime; static;
+
+    constructor Create(const AGeneralizedTime: IAsn1GeneralizedTime); overload;
+    constructor Create(const AUtcTime: IAsn1UtcTime); overload;
+    constructor Create(const ADateTime: TDateTime); overload;
+
+    function ToDateTime: TDateTime;
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+    property TimeObject: IAsn1Object read GetTimeObject;
+
+  end;
+
+  /// <summary>
+  /// The Validity object.
+  /// </summary>
+  TValidity = class(TAsn1Encodable, IValidity)
+
+  strict private
+  var
+    FNotBefore: ITime;
+    FNotAfter: ITime;
+
+  strict protected
+    function GetNotBefore: ITime;
+    function GetNotAfter: ITime;
+
+  public
+    class function GetInstance(AObj: TObject): IValidity; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IValidity; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IValidity; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IValidity; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ANotBefore, ANotAfter: ITime); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property NotBefore: ITime read GetNotBefore;
+    property NotAfter: ITime read GetNotAfter;
+
+  end;
+
+  /// <summary>
+  /// The AltSignatureAlgorithm object.
+  /// </summary>
+  TAltSignatureAlgorithm = class(TAsn1Encodable, IAltSignatureAlgorithm)
+
+  strict private
+  var
+    FAlgorithm: IAlgorithmIdentifier;
+
+  strict protected
+    function GetAlgorithm: IAlgorithmIdentifier;
+
+  public
+    class function GetInstance(AObj: TObject): IAltSignatureAlgorithm; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAltSignatureAlgorithm; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAltSignatureAlgorithm; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAltSignatureAlgorithm; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): IAltSignatureAlgorithm; static;
+
+    constructor Create(const AAlgorithm: IAlgorithmIdentifier); overload;
+    constructor Create(const AAlgorithm: IDerObjectIdentifier); overload;
+    constructor Create(const AAlgorithm: IDerObjectIdentifier;
+      const AParameters: IAsn1Encodable); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+
+  end;
+
+  /// <summary>
+  /// The AltSignatureValue object.
+  /// </summary>
+  TAltSignatureValue = class(TAsn1Encodable, IAltSignatureValue)
+
+  strict private
+  var
+    FSignature: IDerBitString;
+
+  strict protected
+    function GetSignature: IDerBitString;
+
+  public
+    class function GetInstance(AObj: TObject): IAltSignatureValue; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAltSignatureValue; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAltSignatureValue; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAltSignatureValue; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): IAltSignatureValue; static;
+
+    constructor Create(const ASignature: IDerBitString); overload;
+    constructor Create(const ASignature: TCryptoLibByteArray); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Signature: IDerBitString read GetSignature;
+
+  end;
+
+  /// <summary>
+  /// The SubjectKeyIdentifier object.
+  /// </summary>
+  TSubjectKeyIdentifier = class(TAsn1Encodable, ISubjectKeyIdentifier)
+
+  strict private
+  var
+    FKeyIdentifier: TCryptoLibByteArray;
+
+  strict protected
+    function GetKeyIdentifier: TCryptoLibByteArray;
+
+  public
+    class function GetInstance(AObj: TObject): ISubjectKeyIdentifier; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectKeyIdentifier; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ISubjectKeyIdentifier; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ISubjectKeyIdentifier; static;
+
+    constructor Create(const AKeyID: TCryptoLibByteArray); overload;
+    constructor Create(const AKeyID: IAsn1OctetString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+  end;
+
+  /// <summary>
+  /// The BasicConstraints object.
+  /// </summary>
+  TBasicConstraints = class(TAsn1Encodable, IBasicConstraints)
+
+  strict private
+  var
+    FCA: IDerBoolean;
+    FPathLenConstraint: IDerInteger;
+
+  strict protected
+    function IsCA: Boolean;
+    function GetPathLenConstraint: TBigInteger;
+
+  public
+    class function GetInstance(AObj: TObject): IBasicConstraints; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IBasicConstraints; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IBasicConstraints; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IBasicConstraints; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(ACA: Boolean); overload;
+    constructor Create(APathLenConstraint: Int32); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+    property PathLenConstraint: TBigInteger read GetPathLenConstraint;
+
+  end;
+
+  /// <summary>
+  /// The SubjectPublicKeyInfo object.
+  /// </summary>
+  TSubjectPublicKeyInfo = class(TAsn1Encodable, ISubjectPublicKeyInfo)
+
+  strict private
+  var
+    FAlgorithm: IAlgorithmIdentifier;
+    FPublicKey: IDerBitString;
+
+  strict protected
+    function GetAlgorithm: IAlgorithmIdentifier;
+    function GetPublicKey: IDerBitString;
+
+  public
+    function ParsePublicKey: IAsn1Object;
+    class function GetInstance(AObj: TObject): ISubjectPublicKeyInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectPublicKeyInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ISubjectPublicKeyInfo; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): ISubjectPublicKeyInfo; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ISubjectPublicKeyInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAlgID: IAlgorithmIdentifier;
+      const APublicKey: IDerBitString); overload;
+    constructor Create(const AAlgID: IAlgorithmIdentifier;
+      const APublicKey: IAsn1Encodable); overload;
+    constructor Create(const AAlgID: IAlgorithmIdentifier;
+      const APublicKey: TCryptoLibByteArray); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+    property PublicKey: IDerBitString read GetPublicKey;
+
+  end;
+
+  /// <summary>
+  /// The RsaPublicKeyStructure object.
+  /// </summary>
+  TRsaPublicKeyStructure = class(TAsn1Encodable, IRsaPublicKeyStructure)
+
+  strict private
+  var
+    FModulus: TBigInteger;
+    FPublicExponent: TBigInteger;
+
+  strict protected
+    function GetModulus: TBigInteger;
+    function GetPublicExponent: TBigInteger;
+
+  public
+    class function GetInstance(AObj: TObject): IRsaPublicKeyStructure; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IRsaPublicKeyStructure; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IRsaPublicKeyStructure; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IRsaPublicKeyStructure; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AModulus, APublicExponent: TBigInteger); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Modulus: TBigInteger read GetModulus;
+    property PublicExponent: TBigInteger read GetPublicExponent;
+
+  end;
+
+  /// <summary>
+  /// The SubjectAltPublicKeyInfo object.
+  /// </summary>
+  TSubjectAltPublicKeyInfo = class(TAsn1Encodable, ISubjectAltPublicKeyInfo)
+
+  strict private
+  var
+    FAlgorithm: IAlgorithmIdentifier;
+    FSubjectAltPublicKey: IDerBitString;
+
+  strict protected
+    function GetAlgorithm: IAlgorithmIdentifier;
+    function GetSubjectAltPublicKey: IDerBitString;
+
+  public
+    class function GetInstance(AObj: TObject): ISubjectAltPublicKeyInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectAltPublicKeyInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ISubjectAltPublicKeyInfo; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ISubjectAltPublicKeyInfo; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): ISubjectAltPublicKeyInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAlgorithm: IAlgorithmIdentifier;
+      const ASubjectAltPublicKey: IDerBitString); overload;
+    constructor Create(const ASubjectPublicKeyInfo: ISubjectPublicKeyInfo); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+    property SubjectAltPublicKey: IDerBitString read GetSubjectAltPublicKey;
+
+  end;
+
+  /// <summary>
+  /// The TbsCertificateStructure object.
+  /// </summary>
+  TTbsCertificateStructure = class(TAsn1Encodable, ITbsCertificateStructure)
+
+  strict private
+  class var
+    FAllowNonDERTbsCertificate: Boolean;
+  var
+    FVersion: IDerInteger;
+    FSerialNumber: IDerInteger;
+    FSignature: IAlgorithmIdentifier;
+    FIssuer: IX509Name;
+    FValidity: IValidity;
+    FSubject: IX509Name;
+    FSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    FIssuerUniqueID: IDerBitString;
+    FSubjectUniqueID: IDerBitString;
+    FExtensions: IX509Extensions;
+    FSeq: IAsn1Sequence;
+
+  strict protected
+    function GetVersion: Int32;
+    function GetVersionNumber: IDerInteger;
+    function GetSerialNumber: IDerInteger;
+    function GetSignature: IAlgorithmIdentifier;
+    function GetIssuer: IX509Name;
+    function GetValidity: IValidity;
+    function GetStartDate: ITime;
+    function GetEndDate: ITime;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+
+  public
+    class constructor Create;
+    
+    class function GetInstance(AObj: TObject): ITbsCertificateStructure; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ITbsCertificateStructure; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ITbsCertificateStructure; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ITbsCertificateStructure; static;
+    
+    class function GetAllowNonDERTbsCertificate: Boolean; static;
+    class procedure SetAllowNonDERTbsCertificate(AValue: Boolean); static;
+    
+    class property AllowNonDERTbsCertificate: Boolean read GetAllowNonDERTbsCertificate write SetAllowNonDERTbsCertificate;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AVersion: IDerInteger; const ASerialNumber: IDerInteger;
+      const ASignature: IAlgorithmIdentifier; const AIssuer: IX509Name;
+      const AValidity: IValidity; const ASubject: IX509Name;
+      const ASubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+      const AIssuerUniqueID: IDerBitString; const ASubjectUniqueID: IDerBitString;
+      const AExtensions: IX509Extensions); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: Int32 read GetVersion;
+    property VersionNumber: IDerInteger read GetVersionNumber;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property Signature: IAlgorithmIdentifier read GetSignature;
+    property Issuer: IX509Name read GetIssuer;
+    property Validity: IValidity read GetValidity;
+    property StartDate: ITime read GetStartDate;
+    property EndDate: ITime read GetEndDate;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property SubjectUniqueID: IDerBitString read GetSubjectUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+
+  end;
+
+  /// <summary>
+  /// The X509CertificateStructure object.
+  /// </summary>
+  TX509CertificateStructure = class(TAsn1Encodable, IX509CertificateStructure)
+
+  strict private
+  var
+    FTbsCertificate: ITbsCertificateStructure;
+    FSignatureAlgorithm: IAlgorithmIdentifier;
+    FSignature: IDerBitString;
+
+  strict protected
+    function GetTbsCertificate: ITbsCertificateStructure;
+    function GetVersion: Int32;
+    function GetSerialNumber: IDerInteger;
+    function GetIssuer: IX509Name;
+    function GetValidity: IValidity;
+    function GetStartDate: ITime;
+    function GetEndDate: ITime;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignature: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+
+  public
+    class function GetInstance(AObj: TObject): IX509CertificateStructure; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IX509CertificateStructure; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IX509CertificateStructure; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IX509CertificateStructure; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IX509CertificateStructure; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ATbsCert: ITbsCertificateStructure;
+      const ASigAlgID: IAlgorithmIdentifier; const ASig: IDerBitString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property TbsCertificate: ITbsCertificateStructure read GetTbsCertificate;
+    property Version: Int32 read GetVersion;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property Issuer: IX509Name read GetIssuer;
+    property Validity: IValidity read GetValidity;
+    property StartDate: ITime read GetStartDate;
+    property EndDate: ITime read GetEndDate;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property SubjectUniqueID: IDerBitString read GetSubjectUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property Signature: IDerBitString read GetSignature;
+
+  end;
+
+  /// <summary>
+  /// The AttributeX509 object.
+  /// </summary>
+  TAttributeX509 = class(TAsn1Encodable, IAttributeX509)
+
+  strict private
+  var
+    FAttrType: IDerObjectIdentifier;
+    FAttrValues: IAsn1Set;
+
+  strict protected
+    function GetAttrType: IDerObjectIdentifier;
+    function GetAttrValues: IAsn1Set;
+    function GetAttributeValues: TCryptoLibGenericArray<IAsn1Encodable>;
+
+  public
+    class function GetInstance(AObj: TObject): IAttributeX509; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeX509; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributeX509; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributeX509; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AAttrType: IDerObjectIdentifier; const AAttrValues: IAsn1Set); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property AttrType: IDerObjectIdentifier read GetAttrType;
+    property AttrValues: IAsn1Set read GetAttrValues;
+
+  end;
+
+  /// <summary>
+  /// The AttCertValidityPeriod object.
+  /// </summary>
+  TAttCertValidityPeriod = class(TAsn1Encodable, IAttCertValidityPeriod)
+
+  strict private
+  var
+    FNotBeforeTime: IAsn1GeneralizedTime;
+    FNotAfterTime: IAsn1GeneralizedTime;
+
+  strict protected
+    function GetNotBeforeTime: IAsn1GeneralizedTime;
+    function GetNotAfterTime: IAsn1GeneralizedTime;
+
+  public
+    class function GetInstance(AObj: TObject): IAttCertValidityPeriod; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttCertValidityPeriod; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IAttCertValidityPeriod; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttCertValidityPeriod; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ANotBeforeTime, ANotAfterTime: IAsn1GeneralizedTime); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property NotBeforeTime: IAsn1GeneralizedTime read GetNotBeforeTime;
+    property NotAfterTime: IAsn1GeneralizedTime read GetNotAfterTime;
+
+  end;
+
+  /// <summary>
+  /// The PolicyInformation object.
+  /// </summary>
+  TPolicyInformation = class(TAsn1Encodable, IPolicyInformation)
+
+  strict private
+  var
+    FPolicyIdentifier: IDerObjectIdentifier;
+    FPolicyQualifiers: IAsn1Sequence;
+
+  strict protected
+    function GetPolicyIdentifier: IDerObjectIdentifier;
+    function GetPolicyQualifiers: IAsn1Sequence;
+
+  public
+    class function GetInstance(AObj: TObject): IPolicyInformation; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IPolicyInformation; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IPolicyInformation; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IPolicyInformation; static;
+
+    constructor Create(const APolicyIdentifier: IDerObjectIdentifier); overload;
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const APolicyIdentifier: IDerObjectIdentifier;
+      const APolicyQualifiers: IAsn1Sequence); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property PolicyIdentifier: IDerObjectIdentifier read GetPolicyIdentifier;
+    property PolicyQualifiers: IAsn1Sequence read GetPolicyQualifiers;
+
+  end;
+
+  /// <summary>
+  /// The IssuerSerial object.
+  /// </summary>
+  TIssuerSerial = class(TAsn1Encodable, IIssuerSerial)
+
+  strict private
+  var
+    FIssuer: IGeneralNames;
+    FSerial: IDerInteger;
+    FIssuerUid: IDerBitString;
+
+  strict protected
+    function GetIssuer: IGeneralNames;
+    function GetSerial: IDerInteger;
+    function GetIssuerUid: IDerBitString;
+
+  public
+    class function GetInstance(AObj: TObject): IIssuerSerial; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IIssuerSerial; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IIssuerSerial; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IIssuerSerial; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IIssuerSerial; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AIssuer: IX509Name; const ASerial: IDerInteger); overload;
+    constructor Create(const AIssuer: IGeneralNames; const ASerial: IDerInteger); overload;
+    constructor Create(const AIssuer: IGeneralNames; const ASerial: IDerInteger;
+      const AIssuerUid: IDerBitString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Issuer: IGeneralNames read GetIssuer;
+    property Serial: IDerInteger read GetSerial;
+    property IssuerUid: IDerBitString read GetIssuerUid;
+
+  end;
+
+  /// <summary>
+  /// The V2Form object.
+  /// </summary>
+  TV2Form = class(TAsn1Encodable, IV2Form)
+
+  strict private
+  var
+    FIssuerName: IGeneralNames;
+    FBaseCertificateID: IIssuerSerial;
+    FObjectDigestInfo: IObjectDigestInfo;
+
+  strict protected
+    function GetIssuerName: IGeneralNames;
+    function GetBaseCertificateID: IIssuerSerial;
+    function GetObjectDigestInfo: IObjectDigestInfo;
+
+  public
+    class function GetInstance(AObj: TObject): IV2Form; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IV2Form; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IV2Form; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IV2Form; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IV2Form; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AIssuerName: IGeneralNames); overload;
+    constructor Create(const AIssuerName: IGeneralNames; const ABaseCertificateID: IIssuerSerial); overload;
+    constructor Create(const AIssuerName: IGeneralNames; const AObjectDigestInfo: IObjectDigestInfo); overload;
+    constructor Create(const AIssuerName: IGeneralNames; const ABaseCertificateID: IIssuerSerial;
+      const AObjectDigestInfo: IObjectDigestInfo); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property IssuerName: IGeneralNames read GetIssuerName;
+    property BaseCertificateID: IIssuerSerial read GetBaseCertificateID;
+    property ObjectDigestInfo: IObjectDigestInfo read GetObjectDigestInfo;
+
+  end;
+
+  /// <summary>
+  /// The ObjectDigestInfo object.
+  /// </summary>
+  TObjectDigestInfo = class(TAsn1Encodable, IObjectDigestInfo)
+
+  strict private
+  var
+    FDigestedObjectType: IDerEnumerated;
+    FOtherObjectTypeID: IDerObjectIdentifier;
+    FDigestAlgorithm: IAlgorithmIdentifier;
+    FObjectDigest: IDerBitString;
+
+  public
+    const
+      PublicKey = 0;
+      PublicKeyCert = 1;
+      OtherObjectDigest = 2;
+
+  strict protected
+    function GetDigestedObjectType: IDerEnumerated;
+    function GetOtherObjectTypeID: IDerObjectIdentifier;
+    function GetDigestAlgorithm: IAlgorithmIdentifier;
+    function GetObjectDigest: IDerBitString;
+
+  public
+    class function GetInstance(AObj: TObject): IObjectDigestInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IObjectDigestInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AIsExplicit: Boolean): IObjectDigestInfo; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IObjectDigestInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(ADigestedObjectType: Int32; const AOtherObjectTypeID: String;
+      const ADigestAlgorithm: IAlgorithmIdentifier; const AObjectDigest: TCryptoLibByteArray); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property DigestedObjectType: IDerEnumerated read GetDigestedObjectType;
+    property OtherObjectTypeID: IDerObjectIdentifier read GetOtherObjectTypeID;
+    property DigestAlgorithm: IAlgorithmIdentifier read GetDigestAlgorithm;
+    property ObjectDigest: IDerBitString read GetObjectDigest;
+
+  end;
+
+  /// <summary>
+  /// The DistributionPointName object.
+  /// </summary>
+  TDistributionPointName = class(TAsn1Encodable, IDistributionPointName, IAsn1Choice)
+
+  strict private
+  var
+    FType: Int32;
+    FName: IAsn1Encodable;
+
+  public
+    const
+      FullName = 0;
+      NameRelativeToCrlIssuer = 1;
+
+  strict protected
+    class function GetOptionalBaseObject(const ATaggedObject: IAsn1TaggedObject): IAsn1Encodable; static;
+
+  public
+    class function GetInstance(AObj: TObject): IDistributionPointName; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IDistributionPointName; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IDistributionPointName; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IDistributionPointName; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IDistributionPointName; static;
+
+    constructor Create(const AName: IGeneralNames); overload;
+    constructor Create(AType: Int32; const AName: IAsn1Encodable); overload;
+
+    function GetType: Int32;
+    function GetName: IAsn1Encodable;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+  end;
+
+  /// <summary>
+  /// The ReasonFlags object.
+  /// </summary>
+  TReasonFlags = class(TDerBitString, IReasonFlags)
+
+  public
+    const
+      Unused = (1 shl 7);
+      KeyCompromise = (1 shl 6);
+      CACompromise = (1 shl 5);
+      AffiliationChanged = (1 shl 4);
+      Superseded = (1 shl 3);
+      CessationOfOperation = (1 shl 2);
+      CertificateHold = (1 shl 1);
+      PrivilegeWithdrawn = (1 shl 0);
+      AACompromise = (1 shl 15);
+
+  public
+    constructor Create(AReasons: Int32); overload;
+    constructor Create(const AReasons: IDerBitString); overload;
+
+  end;
+
+  /// <summary>
+  /// The DistributionPoint object.
+  /// </summary>
+  TDistributionPoint = class(TAsn1Encodable, IDistributionPoint)
+
+  strict private
+  var
+    FDistributionPointName: IDistributionPointName;
+    FReasons: IReasonFlags;
+    FCrlIssuer: IGeneralNames;
+
+  strict protected
+    function GetDistributionPointName: IDistributionPointName;
+    function GetReasons: IReasonFlags;
+    function GetCrlIssuer: IGeneralNames;
+
+  public
+    class function GetInstance(AObj: TObject): IDistributionPoint; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IDistributionPoint; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IDistributionPoint; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IDistributionPoint; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ADistributionPointName: IDistributionPointName;
+      const AReasons: IReasonFlags; const ACrlIssuer: IGeneralNames); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+    property DistributionPointName: IDistributionPointName read GetDistributionPointName;
+    property Reasons: IReasonFlags read GetReasons;
+    property CrlIssuer: IGeneralNames read GetCrlIssuer;
+
+  end;
+
+  /// <summary>
+  /// The AttCertIssuer object.
+  /// </summary>
+  TAttCertIssuer = class(TAsn1Encodable, IAttCertIssuer, IAsn1Choice)
+
+  strict private
+  var
+    FObj: IAsn1Encodable;
+    FChoiceObj: IAsn1Object;
+
+  strict protected
+    function GetIssuer: IAsn1Encodable;
+
+  public
+    class function GetInstance(AObj: TObject): IAttCertIssuer; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttCertIssuer; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AIsExplicit: Boolean): IAttCertIssuer; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IAttCertIssuer; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttCertIssuer; static;
+
+    constructor Create(const ANames: IGeneralNames); overload;
+    constructor Create(const AV2Form: IV2Form); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Issuer: IAsn1Encodable read GetIssuer;
+
+  end;
+
+  /// <summary>
+  /// The Holder object.
+  /// </summary>
+  THolder = class(TAsn1Encodable, IHolder)
+
+  strict private
+  var
+    FBaseCertificateID: IIssuerSerial;
+    FEntityName: IGeneralNames;
+    FObjectDigestInfo: IObjectDigestInfo;
+    FVersion: Int32;
+
+  strict protected
+    function GetVersion: Int32;
+    function GetBaseCertificateID: IIssuerSerial;
+    function GetEntityName: IGeneralNames;
+    function GetObjectDigestInfo: IObjectDigestInfo;
+
+  public
+    class function GetInstance(AObj: TObject): IHolder; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IHolder; overload; static;
+    class function GetInstance(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IHolder; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IHolder; static;
+
+    constructor Create(const ATagObj: IAsn1TaggedObject); overload;
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const ABaseCertificateID: IIssuerSerial); overload;
+    constructor Create(const ABaseCertificateID: IIssuerSerial; AVersion: Int32); overload;
+    constructor Create(const AEntityName: IGeneralNames); overload;
+    constructor Create(const AEntityName: IGeneralNames; AVersion: Int32); overload;
+    constructor Create(const AObjectDigestInfo: IObjectDigestInfo); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: Int32 read GetVersion;
+    property BaseCertificateID: IIssuerSerial read GetBaseCertificateID;
+    property EntityName: IGeneralNames read GetEntityName;
+    property ObjectDigestInfo: IObjectDigestInfo read GetObjectDigestInfo;
+
+  end;
+
+  /// <summary>
+  /// The AttributeCertificate object.
+  /// </summary>
+  TAttributeCertificate = class(TAsn1Encodable, IAttributeCertificate)
+
+  strict private
+  var
+    FACInfo: IAttributeCertificateInfo;
+    FSignatureAlgorithm: IAlgorithmIdentifier;
+    FSignatureValue: IDerBitString;
+
+  strict protected
+    function GetACInfo: IAttributeCertificateInfo;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignatureValue: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+
+  public
+    class function GetInstance(AObj: TObject): IAttributeCertificate; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeCertificate; overload; static;
+    class function GetInstance(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributeCertificate; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributeCertificate; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const AACInfo: IAttributeCertificateInfo;
+      const ASignatureAlgorithm: IAlgorithmIdentifier; const ASignatureValue: IDerBitString); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property ACInfo: IAttributeCertificateInfo read GetACInfo;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property SignatureValue: IDerBitString read GetSignatureValue;
+
+  end;
+
+  /// <summary>
+  /// The AttributeCertificateInfo object.
+  /// </summary>
+  TAttributeCertificateInfo = class(TAsn1Encodable, IAttributeCertificateInfo)
+
+  strict private
+  var
+    FVersion: IDerInteger;
+    FHolder: IHolder;
+    FIssuer: IAttCertIssuer;
+    FSignature: IAlgorithmIdentifier;
+    FSerialNumber: IDerInteger;
+    FAttrCertValidityPeriod: IAttCertValidityPeriod;
+    FAttributes: IAsn1Sequence;
+    FIssuerUniqueID: IDerBitString;
+    FExtensions: IX509Extensions;
+
+  strict protected
+    function GetVersion: IDerInteger;
+    function GetHolder: IHolder;
+    function GetIssuer: IAttCertIssuer;
+    function GetSignature: IAlgorithmIdentifier;
+    function GetSerialNumber: IDerInteger;
+    function GetAttrCertValidityPeriod: IAttCertValidityPeriod;
+    function GetAttributes: IAsn1Sequence;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+
+  public
+    class function GetInstance(AObj: TObject): IAttributeCertificateInfo; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeCertificateInfo; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AIsExplicit: Boolean): IAttributeCertificateInfo; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IAttributeCertificateInfo; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Version: IDerInteger read GetVersion;
+    property Holder: IHolder read GetHolder;
+    property Issuer: IAttCertIssuer read GetIssuer;
+    property Signature: IAlgorithmIdentifier read GetSignature;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property AttrCertValidityPeriod: IAttCertValidityPeriod read GetAttrCertValidityPeriod;
+    property Attributes: IAsn1Sequence read GetAttributes;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+
+  end;
+
+  /// <summary>
+  /// The CrlDistPoint object.
+  /// </summary>
+  TCrlDistPoint = class(TAsn1Encodable, ICrlDistPoint)
+
+  strict private
+  var
+    FSeq: IAsn1Sequence;
+
+  strict protected
+    function GetDistributionPoints: TCryptoLibGenericArray<IDistributionPoint>;
+
+  public
+    class function GetInstance(AObj: TObject): ICrlDistPoint; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): ICrlDistPoint; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): ICrlDistPoint; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): ICrlDistPoint; static;
+    class function FromExtensions(const AExtensions: IX509Extensions): ICrlDistPoint; static;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+    constructor Create(const APoints: TCryptoLibGenericArray<IDistributionPoint>); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+    function ToString: String; override;
+
+  end;
+
+implementation
+
+uses
+  ClpX509DefaultEntryConverter;
+
+{ TAlgorithmIdentifier }
+
+class function TAlgorithmIdentifier.GetInstance(AObj: TObject): IAlgorithmIdentifier;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAlgorithmIdentifier, Result) then
+    Exit;
+
+  Result := TAlgorithmIdentifier.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAlgorithmIdentifier.GetInstance(const AEncoded: TCryptoLibByteArray): IAlgorithmIdentifier;
+begin
+  Result := TAlgorithmIdentifier.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAlgorithmIdentifier.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAlgorithmIdentifier;
+begin
+  Result := TAlgorithmIdentifier.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAlgorithmIdentifier.GetOptional(const AElement: IAsn1Encodable): IAlgorithmIdentifier;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IAlgorithmIdentifier, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TAlgorithmIdentifier.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TAlgorithmIdentifier.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAlgorithmIdentifier;
+begin
+  Result := TAlgorithmIdentifier.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAlgorithmIdentifier.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if (LCount < 1) or (LCount > 2) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FAlgorithm := TDerObjectIdentifier.GetInstance(ASeq[0] as TAsn1Encodable);
+  if LCount < 2 then
+    FParameters := nil
+  else
+    FParameters := ASeq[1] as IAsn1Encodable;
+end;
+
+constructor TAlgorithmIdentifier.Create(const AAlgorithm: IDerObjectIdentifier);
+begin
+  Create(AAlgorithm, nil);
+end;
+
+constructor TAlgorithmIdentifier.Create(const AAlgorithm: IDerObjectIdentifier;
+  const AParameters: IAsn1Encodable);
+begin
+  inherited Create();
+
+  if AAlgorithm = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SAlgorithmNil);
+  end;
+
+  FAlgorithm := AAlgorithm;
+  FParameters := AParameters;
+end;
+
+function TAlgorithmIdentifier.GetAlgorithm: IDerObjectIdentifier;
+begin
+  Result := FAlgorithm;
+end;
+
+function TAlgorithmIdentifier.GetParameters: IAsn1Encodable;
+begin
+  Result := FParameters;
+end;
+
+function TAlgorithmIdentifier.ToAsn1Object: IAsn1Object;
+begin
+  if FParameters = nil then
+    Result := TDerSequence.Create([FAlgorithm as IAsn1Encodable])
+  else
+    Result := TDerSequence.Create([FAlgorithm as IAsn1Encodable, FParameters]);
+end;
+
+{ TDigestInfo }
+
+class function TDigestInfo.GetInstance(AObj: TObject): IDigestInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IDigestInfo, Result) then
+    Exit;
+
+  Result := TDigestInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TDigestInfo.GetInstance(const AEncoded: TCryptoLibByteArray): IDigestInfo;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+  Result := TDigestInfo.Create(TAsn1Sequence.GetInstance(LAsn1Obj));
+end;
+
+class function TDigestInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IDigestInfo;
+begin
+  Result := TDigestInfo.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TDigestInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IDigestInfo;
+begin
+  Result := TDigestInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TDigestInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FDigestAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[0] as TAsn1Encodable);
+  FDigest := TAsn1OctetString.GetInstance(ASeq[1] as TAsn1Encodable);
+end;
+
+constructor TDigestInfo.Create(const AAlgId: IAlgorithmIdentifier;
+  const ADigest: TCryptoLibByteArray);
+begin
+  inherited Create();
+
+  if AAlgId = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SDigestAlgorithmNil);
+  end;
+
+  FDigestAlgorithm := AAlgId;
+  FDigest := TDerOctetString.Create(ADigest);
+end;
+
+constructor TDigestInfo.Create(const ADigestAlgorithm: IAlgorithmIdentifier;
+  const ADigest: IAsn1OctetString);
+begin
+  inherited Create();
+
+  if ADigestAlgorithm = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SDigestAlgorithmNil);
+  end;
+
+  if ADigest = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SDigestNil);
+  end;
+
+  FDigestAlgorithm := ADigestAlgorithm;
+  FDigest := ADigest;
+end;
+
+function TDigestInfo.GetDigestAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FDigestAlgorithm;
+end;
+
+function TDigestInfo.GetDigest: IAsn1OctetString;
+begin
+  Result := FDigest;
+end;
+
+function TDigestInfo.GetDigestBytes: TCryptoLibByteArray;
+begin
+  Result := TArrayUtils.Clone(FDigest.GetOctets());
+end;
+
+function TDigestInfo.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FDigestAlgorithm, FDigest]);
+end;
+
+{ TTime }
+
+class function TTime.GetInstance(AObj: TObject): ITime;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<ITime>(AObj,
+    function(AElement: IAsn1Encodable): ITime
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TTime.GetInstance(const AEncoded: TCryptoLibByteArray): ITime;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<ITime>(AEncoded,
+    function(AElement: IAsn1Encodable): ITime
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TTime.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ITime;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<ITime>(AObj, AExplicitly,
+    function(AElement: IAsn1Encodable): ITime
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TTime.GetOptional(const AElement: IAsn1Encodable): ITime;
+var
+  LUtcTime: IAsn1UtcTime;
+  LGeneralizedTime: IAsn1GeneralizedTime;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, ITime, Result) then
+    Exit;
+
+  LUtcTime := TAsn1UtcTime.GetOptional(AElement);
+  if LUtcTime <> nil then
+  begin
+    Result := TTime.Create(LUtcTime);
+    Exit;
+  end;
+
+  LGeneralizedTime := TAsn1GeneralizedTime.GetOptional(AElement);
+  if LGeneralizedTime <> nil then
+  begin
+    Result := TTime.Create(LGeneralizedTime);
+    Exit;
+  end;
+
+  Result := nil;
+end;
+
+class function TTime.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ITime;
+begin
+  Result := TAsn1Utilities.GetTaggedChoice<ITime>(ATaggedObject, ADeclaredExplicit,
+    function(AElement: IAsn1Encodable): ITime
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+constructor TTime.Create(const AGeneralizedTime: IAsn1GeneralizedTime);
+begin
+  inherited Create();
+  if AGeneralizedTime = nil then
+    raise EArgumentNilCryptoLibException.Create('generalizedTime');
+  FTimeObject := AGeneralizedTime;
+end;
+
+constructor TTime.Create(const AUtcTime: IAsn1UtcTime);
+begin
+  inherited Create();
+  if AUtcTime = nil then
+    raise EArgumentNilCryptoLibException.Create('utcTime');
+
+  // Validate utcTime is in the appropriate year range
+  AUtcTime.ToDateTime(2049);
+
+  FTimeObject := AUtcTime;
+end;
+
+constructor TTime.Create(const ADateTime: TDateTime);
+var
+  LUtc: TDateTime;
+  LYear, LMonth, LDay, LHour, LMinute, LSecond, LMillisecond: Word;
+begin
+  inherited Create();
+
+  // creates a time object from a given date - if the date is between 1950
+  // and 2049 a UTCTime object is Generated, otherwise a GeneralizedTime
+  // is used.
+  LUtc := TTimeZone.Local.ToUniversalTime(ADateTime);
+  DecodeDateTime(LUtc, LYear, LMonth, LDay, LHour, LMinute, LSecond, LMillisecond);
+
+  if (LYear < 1950) or (LYear > 2049) then
+  begin
+    FTimeObject := TRfc5280Asn1Utilities.CreateGeneralizedTime(LUtc);
+  end
+  else
+  begin
+    FTimeObject := TRfc5280Asn1Utilities.CreateUtcTime(LUtc);
+  end;
+end;
+
+function TTime.GetTimeObject: IAsn1Object;
+begin
+  Result := FTimeObject;
+end;
+
+function TTime.ToDateTime: TDateTime;
+var
+  LUtcTime: IAsn1UtcTime;
+  LGeneralizedTime: IAsn1GeneralizedTime;
+begin
+  try
+    if Supports(FTimeObject, IAsn1UtcTime, LUtcTime) then
+    begin
+      Result := LUtcTime.ToDateTime(2049);
+      Exit;
+    end;
+
+    if Supports(FTimeObject, IAsn1GeneralizedTime, LGeneralizedTime) then
+    begin
+      Result := LGeneralizedTime.ToDateTime();
+      Exit;
+    end;
+
+    raise EInvalidOperationCryptoLibException.Create('invalid time object');
+  except
+    on E: Exception do
+    begin
+      // this should never happen
+      raise EInvalidOperationCryptoLibException.CreateFmt('invalid date string: %s', [E.Message]);
+    end;
+  end;
+end;
+
+function TTime.ToAsn1Object: IAsn1Object;
+begin
+  Result := FTimeObject;
+end;
+
+function TTime.ToString: String;
+var
+  LUtcTime: IAsn1UtcTime;
+  LGeneralizedTime: IAsn1GeneralizedTime;
+  LDateTime: TDateTime;
+begin
+  if Supports(FTimeObject, IAsn1UtcTime, LUtcTime) then
+  begin
+    LDateTime := LUtcTime.ToDateTime(2049);
+    Result := TDateTimeUtilities.FormatCanonical(
+      LDateTime,
+      'yyyyMMddHHmmssK',
+      TFormatSettings.Invariant,
+      False
+    );
+    Exit;
+  end;
+
+  if Supports(FTimeObject, IAsn1GeneralizedTime, LGeneralizedTime) then
+  begin
+    LDateTime := LGeneralizedTime.ToDateTime();
+    Result := TDateTimeUtilities.FormatCanonical(
+      LDateTime,
+      'yyyyMMddHHmmss.FFFFFFFK',
+      TFormatSettings.Invariant,
+      False
+    );
+    Exit;
+  end;
+
+  raise EInvalidOperationCryptoLibException.Create('invalid time object');
+end;
+
+{ TValidity }
+
+class function TValidity.GetInstance(AObj: TObject): IValidity;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IValidity, Result) then
+    Exit;
+
+  Result := TValidity.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TValidity.GetInstance(const AEncoded: TCryptoLibByteArray): IValidity;
+begin
+  Result := TValidity.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+
+class function TValidity.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IValidity;
+begin
+  Result := TValidity.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TValidity.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IValidity;
+begin
+  Result := TValidity.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TValidity.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FNotBefore := TTime.GetInstance(ASeq[0] as TObject);
+  FNotAfter := TTime.GetInstance(ASeq[1] as TObject);
+end;
+
+constructor TValidity.Create(const ANotBefore, ANotAfter: ITime);
+begin
+  inherited Create();
+
+  if ANotBefore = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SNotBeforeNil);
+  end;
+
+  if ANotAfter = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SNotAfterNil);
+  end;
+
+  FNotBefore := ANotBefore;
+  FNotAfter := ANotAfter;
+end;
+
+function TValidity.GetNotBefore: ITime;
+begin
+  Result := FNotBefore;
+end;
+
+function TValidity.GetNotAfter: ITime;
+begin
+  Result := FNotAfter;
+end;
+
+function TValidity.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FNotBefore as IAsn1Encodable, FNotAfter as IAsn1Encodable]);
+end;
+
+{ TAltSignatureAlgorithm }
+
+class function TAltSignatureAlgorithm.GetInstance(AObj: TObject): IAltSignatureAlgorithm;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAltSignatureAlgorithm, Result) then
+    Exit;
+
+  Result := TAltSignatureAlgorithm.Create(TAlgorithmIdentifier.GetInstance(AObj));
+end;
+
+class function TAltSignatureAlgorithm.GetInstance(const AEncoded: TCryptoLibByteArray): IAltSignatureAlgorithm;
+begin
+  Result := TAltSignatureAlgorithm.Create(TAlgorithmIdentifier.GetInstance(AEncoded));
+end;
+
+class function TAltSignatureAlgorithm.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAltSignatureAlgorithm;
+begin
+  Result := TAltSignatureAlgorithm.Create(TAlgorithmIdentifier.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAltSignatureAlgorithm.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAltSignatureAlgorithm;
+begin
+  Result := TAltSignatureAlgorithm.Create(TAlgorithmIdentifier.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TAltSignatureAlgorithm.FromExtensions(const AExtensions: IX509Extensions): IAltSignatureAlgorithm;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.AltSignatureAlgorithm) as TObject);
+end;
+
+constructor TAltSignatureAlgorithm.Create(const AAlgorithm: IAlgorithmIdentifier);
+begin
+  inherited Create();
+
+  if AAlgorithm = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SAlgorithmNilAlt);
+  end;
+
+  FAlgorithm := AAlgorithm;
+end;
+
+constructor TAltSignatureAlgorithm.Create(const AAlgorithm: IDerObjectIdentifier);
+begin
+  Create(AAlgorithm, nil);
+end;
+
+constructor TAltSignatureAlgorithm.Create(const AAlgorithm: IDerObjectIdentifier;
+  const AParameters: IAsn1Encodable);
+begin
+  inherited Create();
+
+  FAlgorithm := TAlgorithmIdentifier.Create(AAlgorithm, AParameters);
+end;
+
+function TAltSignatureAlgorithm.GetAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FAlgorithm;
+end;
+
+function TAltSignatureAlgorithm.ToAsn1Object: IAsn1Object;
+begin
+  Result := FAlgorithm.ToAsn1Object();
+end;
+
+{ TAltSignatureValue }
+
+class function TAltSignatureValue.GetInstance(AObj: TObject): IAltSignatureValue;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAltSignatureValue, Result) then
+    Exit;
+
+  Result := TAltSignatureValue.Create(TDerBitString.GetInstance(AObj));
+end;
+
+class function TAltSignatureValue.GetInstance(const AEncoded: TCryptoLibByteArray): IAltSignatureValue;
+begin
+  Result := TAltSignatureValue.Create(TDerBitString.GetInstance(AEncoded));
+end;
+
+class function TAltSignatureValue.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAltSignatureValue;
+begin
+  Result := TAltSignatureValue.Create(TDerBitString.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAltSignatureValue.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAltSignatureValue;
+begin
+  Result := TAltSignatureValue.Create(TDerBitString.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TAltSignatureValue.FromExtensions(const AExtensions: IX509Extensions): IAltSignatureValue;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.AltSignatureValue) as TObject);
+end;
+
+constructor TAltSignatureValue.Create(const ASignature: IDerBitString);
+begin
+  inherited Create();
+
+  if ASignature = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SSignatureNil);
+  end;
+
+  FSignature := ASignature;
+end;
+
+constructor TAltSignatureValue.Create(const ASignature: TCryptoLibByteArray);
+begin
+  inherited Create();
+
+  FSignature := TDerBitString.Create(ASignature);
+end;
+
+function TAltSignatureValue.GetSignature: IDerBitString;
+begin
+  Result := FSignature;
+end;
+
+function TAltSignatureValue.ToAsn1Object: IAsn1Object;
+begin
+  Result := FSignature;
+end;
+
+{ TSubjectKeyIdentifier }
+
+class function TSubjectKeyIdentifier.GetInstance(AObj: TObject): ISubjectKeyIdentifier;
+var
+  LExtension: IX509Extension;
+  LPublicKeyInfo: ISubjectPublicKeyInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ISubjectKeyIdentifier, Result) then
+    Exit;
+
+  if Supports(AObj, ISubjectPublicKeyInfo, LPublicKeyInfo) then
+  begin
+    // TODO: This constructor is obsolete - use X509ExtensionUtilities instead
+    raise ENotImplementedCryptoLibException.Create('SubjectKeyIdentifier from SubjectPublicKeyInfo not yet implemented');
+  end;
+
+  if Supports(AObj, IX509Extension, LExtension) then
+  begin
+    Result := GetInstance(TX509Extension.ConvertValueToObject(LExtension) as TObject);
+    Exit;
+  end;
+
+  Result := TSubjectKeyIdentifier.Create(TAsn1OctetString.GetInstance(AObj));
+end;
+
+class function TSubjectKeyIdentifier.GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectKeyIdentifier;
+begin
+  Result := TSubjectKeyIdentifier.Create(TAsn1OctetString.GetInstance(AEncoded));
+end;
+
+class function TSubjectKeyIdentifier.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ISubjectKeyIdentifier;
+begin
+  Result := TSubjectKeyIdentifier.Create(TAsn1OctetString.GetInstance(AObj, AExplicitly));
+end;
+
+class function TSubjectKeyIdentifier.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ISubjectKeyIdentifier;
+begin
+  Result := TSubjectKeyIdentifier.Create(TAsn1OctetString.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TSubjectKeyIdentifier.Create(const AKeyID: TCryptoLibByteArray);
+begin
+  inherited Create();
+
+  if AKeyID = nil then
+  begin
+    raise EArgumentNilCryptoLibException.Create(SInvalidKeyIdentifier);
+  end;
+
+  FKeyIdentifier := TArrayUtils.Clone(AKeyID);
+end;
+
+constructor TSubjectKeyIdentifier.Create(const AKeyID: IAsn1OctetString);
+begin
+  Create(AKeyID.GetOctets());
+end;
+
+function TSubjectKeyIdentifier.GetKeyIdentifier: TCryptoLibByteArray;
+begin
+  Result := TArrayUtils.Clone(FKeyIdentifier);
+end;
+
+function TSubjectKeyIdentifier.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerOctetString.FromContents(FKeyIdentifier);
+end;
+
+{ TBasicConstraints }
+
+class function TBasicConstraints.GetInstance(AObj: TObject): IBasicConstraints;
+var
+  LExtension: IX509Extension;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IBasicConstraints, Result) then
+    Exit;
+
+  if Supports(AObj, IX509Extension, LExtension) then
+  begin
+    Result := GetInstance(TX509Extension.ConvertValueToObject(LExtension) as TObject);
+    Exit;
+  end;
+
+  Result := TBasicConstraints.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TBasicConstraints.GetInstance(const AEncoded: TCryptoLibByteArray): IBasicConstraints;
+begin
+  Result := TBasicConstraints.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TBasicConstraints.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IBasicConstraints;
+begin
+  Result := TBasicConstraints.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TBasicConstraints.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IBasicConstraints;
+begin
+  Result := TBasicConstraints.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TBasicConstraints.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 2) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FCA := TAsn1Utilities.ReadOptional<IDerBoolean>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerBoolean
+    begin
+      Result := TDerBoolean.GetOptional(AElement);
+    end);
+  if FCA = nil then
+    FCA := TDerBoolean.False;
+
+  FPathLenConstraint := TAsn1Utilities.ReadOptional<IDerInteger>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerInteger
+    begin
+      Result := TDerInteger.GetOptional(AElement);
+    end);
+
+  if LPos <> LCount then
+  begin
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+  end;
+end;
+
+constructor TBasicConstraints.Create(ACA: Boolean);
+begin
+  inherited Create();
+
+  if ACA then
+    FCA := TDerBoolean.True
+  else
+    FCA := TDerBoolean.False;
+  FPathLenConstraint := nil;
+end;
+
+constructor TBasicConstraints.Create(APathLenConstraint: Int32);
+begin
+  inherited Create();
+
+  FCA := TDerBoolean.True;
+  FPathLenConstraint := TDerInteger.ValueOf(APathLenConstraint);
+end;
+
+function TBasicConstraints.IsCA: Boolean;
+begin
+  Result := FCA.IsTrue;
+end;
+
+function TBasicConstraints.GetPathLenConstraint: TBigInteger;
+begin
+  if FPathLenConstraint = nil then
+    Result := Default(TBigInteger)
+  else
+    Result := FPathLenConstraint.Value;
+end;
+
+function TBasicConstraints.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(2);
+  if FCA.IsTrue then
+  begin
+    LV.Add(FCA);
+  end;
+  if FPathLenConstraint <> nil then
+  begin
+    LV.Add(FPathLenConstraint);
+  end;
+  Result := TDerSequence.Create(LV);
+end;
+
+function TBasicConstraints.ToString: String;
+begin
+  if FPathLenConstraint = nil then
+    Result := Format('BasicConstraints: isCa(%s)', [BoolToStr(IsCA, True)])
+  else
+    Result := Format('BasicConstraints: isCa(%s), pathLenConstraint = %s',
+      [BoolToStr(IsCA, True), FPathLenConstraint.Value.ToString]);
+end;
+
+{ TSubjectPublicKeyInfo }
+
+class function TSubjectPublicKeyInfo.GetInstance(AObj: TObject): ISubjectPublicKeyInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ISubjectPublicKeyInfo, Result) then
+    Exit;
+
+  Result := TSubjectPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TSubjectPublicKeyInfo.GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectPublicKeyInfo;
+begin
+  Result := TSubjectPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TSubjectPublicKeyInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ISubjectPublicKeyInfo;
+begin
+  Result := TSubjectPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TSubjectPublicKeyInfo.GetOptional(const AElement: IAsn1Encodable): ISubjectPublicKeyInfo;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, ISubjectPublicKeyInfo, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TSubjectPublicKeyInfo.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TSubjectPublicKeyInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ISubjectPublicKeyInfo;
+begin
+  Result := TSubjectPublicKeyInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TSubjectPublicKeyInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[0] as TAsn1Encodable);
+  FPublicKey := TDerBitString.GetInstance(ASeq[1] as TAsn1Encodable);
+end;
+
+constructor TSubjectPublicKeyInfo.Create(const AAlgID: IAlgorithmIdentifier;
+  const APublicKey: IDerBitString);
+begin
+  inherited Create();
+
+  FAlgorithm := AAlgID;
+  FPublicKey := APublicKey;
+end;
+
+constructor TSubjectPublicKeyInfo.Create(const AAlgID: IAlgorithmIdentifier;
+  const APublicKey: IAsn1Encodable);
+begin
+  inherited Create();
+
+  FAlgorithm := AAlgID;
+  FPublicKey := TDerBitString.Create(APublicKey);
+end;
+
+constructor TSubjectPublicKeyInfo.Create(const AAlgID: IAlgorithmIdentifier;
+  const APublicKey: TCryptoLibByteArray);
+begin
+  inherited Create();
+
+  FAlgorithm := AAlgID;
+  FPublicKey := TDerBitString.Create(APublicKey);
+end;
+
+function TSubjectPublicKeyInfo.GetAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FAlgorithm;
+end;
+
+function TSubjectPublicKeyInfo.GetPublicKey: IDerBitString;
+begin
+  Result := FPublicKey;
+end;
+
+function TSubjectPublicKeyInfo.ParsePublicKey: IAsn1Object;
+var
+  LStream: TStream;
+begin
+  LStream := FPublicKey.GetOctetStream();
+  try
+    Result := TAsn1Object.FromStream(LStream);
+  finally
+    LStream.Free;
+  end;
+end;
+
+function TSubjectPublicKeyInfo.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FAlgorithm, FPublicKey]);
+end;
+
+{ TRsaPublicKeyStructure }
+
+class function TRsaPublicKeyStructure.GetInstance(AObj: TObject): IRsaPublicKeyStructure;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IRsaPublicKeyStructure, Result) then
+    Exit;
+
+  Result := TRsaPublicKeyStructure.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TRsaPublicKeyStructure.GetInstance(const AEncoded: TCryptoLibByteArray): IRsaPublicKeyStructure;
+begin
+  Result := TRsaPublicKeyStructure.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+
+class function TRsaPublicKeyStructure.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IRsaPublicKeyStructure;
+begin
+  Result := TRsaPublicKeyStructure.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TRsaPublicKeyStructure.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IRsaPublicKeyStructure;
+begin
+  Result := TRsaPublicKeyStructure.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TRsaPublicKeyStructure.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  // Note: we are accepting technically incorrect (i.e. negative) values here
+  FModulus := TDerInteger.GetInstance(ASeq[0] as TAsn1Encodable).PositiveValue;
+  FPublicExponent := TDerInteger.GetInstance(ASeq[1] as TAsn1Encodable).PositiveValue;
+end;
+
+constructor TRsaPublicKeyStructure.Create(const AModulus, APublicExponent: TBigInteger);
+begin
+  inherited Create();
+
+  if AModulus.IsInitialized = False then
+    raise EArgumentNilCryptoLibException.Create('modulus');
+  if APublicExponent.IsInitialized = False then
+    raise EArgumentNilCryptoLibException.Create('publicExponent');
+  if AModulus.SignValue <= 0 then
+    raise EArgumentCryptoLibException.Create(SNotCA);
+  if APublicExponent.SignValue <= 0 then
+    raise EArgumentCryptoLibException.Create(SNotValidPublicExponent);
+
+  FModulus := AModulus;
+  FPublicExponent := APublicExponent;
+end;
+
+function TRsaPublicKeyStructure.GetModulus: TBigInteger;
+begin
+  Result := FModulus;
+end;
+
+function TRsaPublicKeyStructure.GetPublicExponent: TBigInteger;
+begin
+  Result := FPublicExponent;
+end;
+
+function TRsaPublicKeyStructure.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([TDerInteger.Create(FModulus) as IAsn1Encodable,
+    TDerInteger.Create(FPublicExponent) as IAsn1Encodable]);
+end;
+
+{ TSubjectAltPublicKeyInfo }
+
+class function TSubjectAltPublicKeyInfo.GetInstance(AObj: TObject): ISubjectAltPublicKeyInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ISubjectAltPublicKeyInfo, Result) then
+    Exit;
+
+  Result := TSubjectAltPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TSubjectAltPublicKeyInfo.GetInstance(const AEncoded: TCryptoLibByteArray): ISubjectAltPublicKeyInfo;
+begin
+  Result := TSubjectAltPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TSubjectAltPublicKeyInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ISubjectAltPublicKeyInfo;
+begin
+  Result := TSubjectAltPublicKeyInfo.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TSubjectAltPublicKeyInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ISubjectAltPublicKeyInfo;
+begin
+  Result := TSubjectAltPublicKeyInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TSubjectAltPublicKeyInfo.FromExtensions(const AExtensions: IX509Extensions): ISubjectAltPublicKeyInfo;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.SubjectAltPublicKeyInfo) as TObject);
+end;
+
+constructor TSubjectAltPublicKeyInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[0] as TAsn1Encodable);
+  FSubjectAltPublicKey := TDerBitString.GetInstance(ASeq[1] as TAsn1Encodable);
+end;
+
+constructor TSubjectAltPublicKeyInfo.Create(const AAlgorithm: IAlgorithmIdentifier;
+  const ASubjectAltPublicKey: IDerBitString);
+begin
+  inherited Create();
+
+  if AAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create(SAlgorithmNilAlt);
+  if ASubjectAltPublicKey = nil then
+    raise EArgumentNilCryptoLibException.Create('subjectAltPublicKey');
+
+  FAlgorithm := AAlgorithm;
+  FSubjectAltPublicKey := ASubjectAltPublicKey;
+end;
+
+constructor TSubjectAltPublicKeyInfo.Create(const ASubjectPublicKeyInfo: ISubjectPublicKeyInfo);
+begin
+  inherited Create();
+
+  if ASubjectPublicKeyInfo = nil then
+    raise EArgumentNilCryptoLibException.Create(SSubjectPublicKeyInfoNil);
+
+  FAlgorithm := ASubjectPublicKeyInfo.Algorithm;
+  FSubjectAltPublicKey := ASubjectPublicKeyInfo.PublicKey;
+end;
+
+function TSubjectAltPublicKeyInfo.GetAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FAlgorithm;
+end;
+
+function TSubjectAltPublicKeyInfo.GetSubjectAltPublicKey: IDerBitString;
+begin
+  Result := FSubjectAltPublicKey;
+end;
+
+function TSubjectAltPublicKeyInfo.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FAlgorithm, FSubjectAltPublicKey]);
+end;
+
+{ TTbsCertificateStructure }
+
+class function TTbsCertificateStructure.GetInstance(AObj: TObject): ITbsCertificateStructure;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ITbsCertificateStructure, Result) then
+    Exit;
+
+  Result := TTbsCertificateStructure.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TTbsCertificateStructure.GetInstance(const AEncoded: TCryptoLibByteArray): ITbsCertificateStructure;
+begin
+  Result := TTbsCertificateStructure.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+
+class function TTbsCertificateStructure.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ITbsCertificateStructure;
+begin
+  Result := TTbsCertificateStructure.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TTbsCertificateStructure.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ITbsCertificateStructure;
+begin
+  Result := TTbsCertificateStructure.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TTbsCertificateStructure.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+  LIsV1, LIsV2: Boolean;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 6) or (LCount > 10) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  FVersion := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerInteger>(ASeq, LPos, 0, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerInteger
+    begin
+      Result := TDerInteger.GetTagged(ATagged, AState);
+    end);
+  if FVersion = nil then
+    FVersion := TDerInteger.Zero;
+
+  LIsV1 := False;
+  LIsV2 := False;
+  if FVersion.HasValue(0) then
+  begin
+    LIsV1 := True;
+  end
+  else if FVersion.HasValue(1) then
+  begin
+    LIsV2 := True;
+  end
+  else if not FVersion.HasValue(2) then
+  begin
+    raise EArgumentCryptoLibException.Create(SVersionNumberNotRecognised);
+  end;
+
+  FSerialNumber := TDerInteger.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FSignature := TAlgorithmIdentifier.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FIssuer := TX509Name.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FValidity := TValidity.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FSubject := TX509Name.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+  FSubjectPublicKeyInfo := TSubjectPublicKeyInfo.GetInstance(ASeq[LPos] as TAsn1Encodable);
+  System.Inc(LPos);
+
+  if not LIsV1 then
+  begin
+    FIssuerUniqueID := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerBitString>(ASeq, LPos, 1, False,
+      function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerBitString
+      begin
+        Result := TDerBitString.GetTagged(ATagged, AState);
+      end);
+
+    FSubjectUniqueID := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerBitString>(ASeq, LPos, 2, False,
+      function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerBitString
+      begin
+        Result := TDerBitString.GetTagged(ATagged, AState);
+      end);
+
+    if not LIsV2 then
+    begin
+      FExtensions := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IX509Extensions>(ASeq, LPos, 3, True,
+        function(ATagged: IAsn1TaggedObject; AState: Boolean): IX509Extensions
+        begin
+          Result := TX509Extensions.GetTagged(ATagged, AState);
+        end);
+    end;
+  end;
+
+  if LPos <> LCount then
+  begin
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+  end;
+
+  FSeq := ASeq;
+end;
+
+constructor TTbsCertificateStructure.Create(const AVersion: IDerInteger; const ASerialNumber: IDerInteger;
+  const ASignature: IAlgorithmIdentifier; const AIssuer: IX509Name;
+  const AValidity: IValidity; const ASubject: IX509Name;
+  const ASubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+  const AIssuerUniqueID: IDerBitString; const ASubjectUniqueID: IDerBitString;
+  const AExtensions: IX509Extensions);
+begin
+  inherited Create();
+
+  if AVersion = nil then
+    FVersion := TDerInteger.Zero
+  else
+    FVersion := AVersion;
+  if ASerialNumber = nil then
+    raise EArgumentNilCryptoLibException.Create('serialNumber');
+  if ASignature = nil then
+    raise EArgumentNilCryptoLibException.Create('signature');
+  if AIssuer = nil then
+    raise EArgumentNilCryptoLibException.Create('issuer');
+  if AValidity = nil then
+    raise EArgumentNilCryptoLibException.Create('validity');
+  if ASubject = nil then
+    raise EArgumentNilCryptoLibException.Create('subject');
+  if ASubjectPublicKeyInfo = nil then
+    raise EArgumentNilCryptoLibException.Create('subjectPublicKeyInfo');
+
+  FSerialNumber := ASerialNumber;
+  FSignature := ASignature;
+  FIssuer := AIssuer;
+  FValidity := AValidity;
+  FSubject := ASubject;
+  FSubjectPublicKeyInfo := ASubjectPublicKeyInfo;
+  FIssuerUniqueID := AIssuerUniqueID;
+  FSubjectUniqueID := ASubjectUniqueID;
+  FExtensions := AExtensions;
+  FSeq := nil;
+end;
+
+function TTbsCertificateStructure.GetVersion: Int32;
+begin
+  Result := FVersion.IntValueExact + 1;
+end;
+
+function TTbsCertificateStructure.GetVersionNumber: IDerInteger;
+begin
+  Result := FVersion;
+end;
+
+function TTbsCertificateStructure.GetSerialNumber: IDerInteger;
+begin
+  Result := FSerialNumber;
+end;
+
+function TTbsCertificateStructure.GetSignature: IAlgorithmIdentifier;
+begin
+  Result := FSignature;
+end;
+
+function TTbsCertificateStructure.GetIssuer: IX509Name;
+begin
+  Result := FIssuer;
+end;
+
+function TTbsCertificateStructure.GetValidity: IValidity;
+begin
+  Result := FValidity;
+end;
+
+function TTbsCertificateStructure.GetStartDate: ITime;
+begin
+  Result := FValidity.NotBefore;
+end;
+
+function TTbsCertificateStructure.GetEndDate: ITime;
+begin
+  Result := FValidity.NotAfter;
+end;
+
+function TTbsCertificateStructure.GetSubject: IX509Name;
+begin
+  Result := FSubject;
+end;
+
+function TTbsCertificateStructure.GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+begin
+  Result := FSubjectPublicKeyInfo;
+end;
+
+function TTbsCertificateStructure.GetIssuerUniqueID: IDerBitString;
+begin
+  Result := FIssuerUniqueID;
+end;
+
+function TTbsCertificateStructure.GetSubjectUniqueID: IDerBitString;
+begin
+  Result := FSubjectUniqueID;
+end;
+
+function TTbsCertificateStructure.GetExtensions: IX509Extensions;
+begin
+  Result := FExtensions;
+end;
+
+class constructor TTbsCertificateStructure.Create;
+begin
+  FAllowNonDERTbsCertificate := False;
+end;
+
+class function TTbsCertificateStructure.GetAllowNonDERTbsCertificate: Boolean;
+begin
+  Result := FAllowNonDERTbsCertificate;
+end;
+
+class procedure TTbsCertificateStructure.SetAllowNonDERTbsCertificate(AValue: Boolean);
+begin
+  FAllowNonDERTbsCertificate := AValue;
+end;
+
+function TTbsCertificateStructure.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  if FSeq <> nil then
+  begin
+    if AllowNonDERTbsCertificate then
+    begin
+      Result := FSeq;
+      Exit;
+    end;
+  end;
+
+  LV := TAsn1EncodableVector.Create(10);
+
+  // DEFAULT Zero
+  if not FVersion.HasValue(0) then
+  begin
+    LV.Add(TDerTaggedObject.Create(True, 0, FVersion));
+  end;
+
+  LV.Add([FSerialNumber, FSignature, FIssuer, FValidity, FSubject, FSubjectPublicKeyInfo]);
+
+  // Note: implicit tag
+  LV.AddOptionalTagged(False, 1, FIssuerUniqueID);
+
+  // Note: implicit tag
+  LV.AddOptionalTagged(False, 2, FSubjectUniqueID);
+
+  LV.AddOptionalTagged(True, 3, FExtensions);
+
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TX509CertificateStructure }
+
+class function TX509CertificateStructure.GetInstance(AObj: TObject): IX509CertificateStructure;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IX509CertificateStructure, Result) then
+    Exit;
+
+  Result := TX509CertificateStructure.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TX509CertificateStructure.GetInstance(const AEncoded: TCryptoLibByteArray): IX509CertificateStructure;
+begin
+  Result := TX509CertificateStructure.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TX509CertificateStructure.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IX509CertificateStructure;
+begin
+  Result := TX509CertificateStructure.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TX509CertificateStructure.GetOptional(const AElement: IAsn1Encodable): IX509CertificateStructure;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IX509CertificateStructure, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TX509CertificateStructure.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TX509CertificateStructure.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IX509CertificateStructure;
+begin
+  Result := TX509CertificateStructure.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TX509CertificateStructure.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+
+  LCount := ASeq.Count;
+  if LCount <> 3 then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  end;
+
+  // correct x509 certificate
+  FTbsCertificate := TTbsCertificateStructure.GetInstance(ASeq[0] as TAsn1Encodable);
+  FSignatureAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[1] as TAsn1Encodable);
+  FSignature := TDerBitString.GetInstance(ASeq[2] as TAsn1Encodable);
+end;
+
+constructor TX509CertificateStructure.Create(const ATbsCert: ITbsCertificateStructure;
+  const ASigAlgID: IAlgorithmIdentifier; const ASig: IDerBitString);
+begin
+  inherited Create();
+
+  if ATbsCert = nil then
+    raise EArgumentNilCryptoLibException.Create(STbsCertNil);
+  if ASigAlgID = nil then
+    raise EArgumentNilCryptoLibException.Create(SSigAlgIDNil);
+  if ASig = nil then
+    raise EArgumentNilCryptoLibException.Create(SSigNil);
+
+  FTbsCertificate := ATbsCert;
+  FSignatureAlgorithm := ASigAlgID;
+  FSignature := ASig;
+end;
+
+function TX509CertificateStructure.GetTbsCertificate: ITbsCertificateStructure;
+begin
+  Result := FTbsCertificate;
+end;
+
+function TX509CertificateStructure.GetVersion: Int32;
+begin
+  Result := FTbsCertificate.Version;
+end;
+
+function TX509CertificateStructure.GetSerialNumber: IDerInteger;
+begin
+  Result := FTbsCertificate.SerialNumber;
+end;
+
+function TX509CertificateStructure.GetIssuer: IX509Name;
+begin
+  Result := FTbsCertificate.Issuer;
+end;
+
+function TX509CertificateStructure.GetValidity: IValidity;
+begin
+  Result := FTbsCertificate.Validity;
+end;
+
+function TX509CertificateStructure.GetStartDate: ITime;
+begin
+  Result := FTbsCertificate.StartDate;
+end;
+
+function TX509CertificateStructure.GetEndDate: ITime;
+begin
+  Result := FTbsCertificate.EndDate;
+end;
+
+function TX509CertificateStructure.GetSubject: IX509Name;
+begin
+  Result := FTbsCertificate.Subject;
+end;
+
+function TX509CertificateStructure.GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+begin
+  Result := FTbsCertificate.SubjectPublicKeyInfo;
+end;
+
+function TX509CertificateStructure.GetIssuerUniqueID: IDerBitString;
+begin
+  Result := FTbsCertificate.IssuerUniqueID;
+end;
+
+function TX509CertificateStructure.GetSubjectUniqueID: IDerBitString;
+begin
+  Result := FTbsCertificate.SubjectUniqueID;
+end;
+
+function TX509CertificateStructure.GetExtensions: IX509Extensions;
+begin
+  Result := FTbsCertificate.Extensions;
+end;
+
+function TX509CertificateStructure.GetSignatureAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FSignatureAlgorithm;
+end;
+
+function TX509CertificateStructure.GetSignature: IDerBitString;
+begin
+  Result := FSignature;
+end;
+
+function TX509CertificateStructure.GetSignatureOctets: TCryptoLibByteArray;
+begin
+  Result := FSignature.GetOctets();
+end;
+
+function TX509CertificateStructure.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FTbsCertificate, FSignatureAlgorithm, FSignature]);
+end;
+
+{ TGeneralName }
+
+class function TGeneralName.GetInstance(AObj: TObject): IGeneralName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IGeneralName>(AObj,
+    function(AElement: IAsn1Encodable): IGeneralName
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TGeneralName.GetInstance(const AEncoded: TCryptoLibByteArray): IGeneralName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IGeneralName>(AEncoded,
+    function(AElement: IAsn1Encodable): IGeneralName
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TGeneralName.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IGeneralName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IGeneralName>(AObj, AExplicitly,
+    function(AElement: IAsn1Encodable): IGeneralName
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TGeneralName.GetOptional(const AElement: IAsn1Encodable): IGeneralName;
+var
+  LTagged: IAsn1TaggedObject;
+  LBaseObject: IAsn1Encodable;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IGeneralName, Result) then
+    Exit;
+
+  LTagged := TAsn1TaggedObject.GetOptional(AElement);
+  if LTagged <> nil then
+  begin
+    LBaseObject := GetOptionalBaseObject(LTagged);
+    if LBaseObject <> nil then
+    begin
+      Result := TGeneralName.Create(LTagged.TagNo, LBaseObject);
+      Exit;
+    end;
+  end;
+
+  Result := nil;
+end;
+
+class function TGeneralName.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IGeneralName;
+begin
+  Result := TAsn1Utilities.GetTaggedChoice<IGeneralName>(ATaggedObject, ADeclaredExplicit,
+    function(AElement: IAsn1Encodable): IGeneralName
+    begin
+      Result := GetInstance(AElement as TAsn1Object);
+    end);
+end;
+
+class function TGeneralName.GetOptionalBaseObject(const ATaggedObject: IAsn1TaggedObject): IAsn1Encodable;
+var
+  LSeq: IAsn1Sequence;
+begin
+  if not ATaggedObject.HasContextTag() then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  case ATaggedObject.TagNo of
+    EdiPartyName:
+      begin
+        LSeq := TAsn1Sequence.GetTagged(ATaggedObject, False);
+        // TODO: Validate EdiPartyName
+        Result := LSeq;
+      end;
+    OtherName, X400Address:
+      Result := TAsn1Sequence.GetTagged(ATaggedObject, False);
+    DnsName, Rfc822Name, UniformResourceIdentifier:
+      Result := TDerIA5String.GetTagged(ATaggedObject, False);
+    DirectoryName:
+      Result := TX509Name.GetTagged(ATaggedObject, True);
+    IPAddress:
+      Result := TAsn1OctetString.GetTagged(ATaggedObject, False);
+    RegisteredID:
+      Result := TDerObjectIdentifier.GetTagged(ATaggedObject, False);
+  else
+    Result := nil;
+  end;
+end;
+
+constructor TGeneralName.Create(const ADirectoryName: IX509Name);
+begin
+  inherited Create();
+  FTag := DirectoryName;
+  FName := ADirectoryName;
+end;
+
+constructor TGeneralName.Create(const AName: IAsn1Object; ATag: Int32);
+begin
+  inherited Create();
+  FTag := ATag;
+  FName := AName;
+end;
+
+constructor TGeneralName.Create(ATag: Int32; const AName: IAsn1Encodable);
+begin
+  inherited Create();
+  FTag := ATag;
+  FName := AName;
+end;
+
+constructor TGeneralName.Create(ATag: Int32; const AName: String);
+var
+  LEncoding: TCryptoLibByteArray;
+begin
+  inherited Create();
+  FTag := ATag;
+
+  case ATag of
+    DnsName, Rfc822Name, UniformResourceIdentifier:
+      FName := TDerIA5String.Create(AName);
+    DirectoryName:
+      FName := TX509Name.Create(AName);
+    IPAddress:
+      begin
+        // Parse IP address (IPv4 or IPv6)
+        LEncoding := TGeneralName.ToGeneralNameEncoding(AName);
+        if LEncoding = nil then
+          raise EArgumentCryptoLibException.Create('IP Address is invalid');
+        FName := TDerOctetString.Create(LEncoding);
+      end;
+    RegisteredID:
+      FName := TDerObjectIdentifier.Create(AName);
+  else
+    raise EArgumentCryptoLibException.CreateFmt('can''t process string for tag: %s',
+      [TAsn1Utilities.GetTagText(TAsn1Tags.ContextSpecific, ATag)]);
+  end;
+end;
+
+function TGeneralName.GetTagNo: Int32;
+begin
+  Result := FTag;
+end;
+
+function TGeneralName.GetName: IAsn1Encodable;
+begin
+  Result := FName;
+end;
+
+function TGeneralName.ToAsn1Object: IAsn1Object;
+var
+  LIsExplicit: Boolean;
+begin
+  // directoryName is explicitly tagged as it is a CHOICE
+  LIsExplicit := (FTag = DirectoryName);
+  Result := TDerTaggedObject.Create(LIsExplicit, FTag, FName);
+end;
+
+function TGeneralName.ToString: String;
+var
+  LIA5: IDerIA5String;
+  LName: IX509Name;
+  LObj: IAsn1Object;
+begin
+  Result := IntToStr(FTag) + ': ';
+
+  case FTag of
+    Rfc822Name, DnsName, UniformResourceIdentifier:
+      begin
+        if Supports(FName, IDerIA5String, LIA5) then
+          Result := Result + LIA5.GetString();
+      end;
+    DirectoryName:
+      begin
+        if Supports(FName, IX509Name, LName) then
+          Result := Result + LName.ToString();
+      end;
+  else
+  begin
+    if Supports(FName, IAsn1String, LIA5) then
+      Result := Result + LIA5.GetString()
+    else if Supports(FName, IAsn1Object, LObj) then
+    begin
+      Result := Result + LObj.ToString();
+    end;
+  end;
+  end;
+end;
+
+class function TGeneralName.ToGeneralNameEncoding(const AIp: String): TCryptoLibByteArray;
+var
+  LSlashIndex: Int32;
+  LAddr: TCryptoLibByteArray;
+  LParsedIp: TCryptoLibInt32Array;
+  LMask: String;
+begin
+  if TIPAddressUtilities.IsValidIPv6WithNetmask(AIp) or TIPAddressUtilities.IsValidIPv6(AIp) then
+  begin
+    LSlashIndex := TPlatform.IndexOf(AIp, '/');
+
+    if LSlashIndex = 0 then
+    begin
+      System.SetLength(LAddr, 16);
+      LParsedIp := ParseIPv6(AIp);
+      CopyInts(LParsedIp, LAddr, 0);
+      Result := LAddr;
+    end
+    else
+    begin
+      System.SetLength(LAddr, 32);
+      // LSlashIndex is 1-based position of '/'
+      LParsedIp := ParseIPv6(System.Copy(AIp, 1, LSlashIndex - 1));
+      CopyInts(LParsedIp, LAddr, 0);
+      LMask := System.Copy(AIp, LSlashIndex + 1, System.Length(AIp) - LSlashIndex);
+      if TPlatform.IndexOf(LMask, ':') > 0 then
+      begin
+        LParsedIp := ParseIPv6(LMask);
+      end
+      else
+      begin
+        LParsedIp := ParseIPv6Mask(LMask);
+      end;
+      CopyInts(LParsedIp, LAddr, 16);
+      Result := LAddr;
+    end;
+  end
+  else if TIPAddressUtilities.IsValidIPv4WithNetmask(AIp) or TIPAddressUtilities.IsValidIPv4(AIp) then
+  begin
+    LSlashIndex := TPlatform.IndexOf(AIp, '/');
+
+    if LSlashIndex = 0 then
+    begin
+      System.SetLength(LAddr, 4);
+      ParseIPv4(AIp, LAddr, 0);
+      Result := LAddr;
+    end
+    else
+    begin
+      System.SetLength(LAddr, 8);
+      // LSlashIndex is 1-based position of '/', copy from start to slash (exclusive)
+      ParseIPv4(System.Copy(AIp, 1, LSlashIndex - 1), LAddr, 0);
+      LMask := System.Copy(AIp, LSlashIndex + 1, System.Length(AIp) - LSlashIndex);
+      if TPlatform.IndexOf(LMask, '.') > 0 then
+      begin
+        ParseIPv4(LMask, LAddr, 4);
+      end
+      else
+      begin
+        ParseIPv4Mask(LMask, LAddr, 4);
+      end;
+      Result := LAddr;
+    end;
+  end
+  else
+  begin
+    Result := nil;
+  end;
+end;
+
+class procedure TGeneralName.CopyInts(const AParsedIp: TCryptoLibInt32Array; var AAddr: TCryptoLibByteArray; AOffset: Int32);
+var
+  I: Int32;
+begin
+  for I := 0 to System.Length(AParsedIp) - 1 do
+  begin
+    AAddr[(I * 2) + AOffset] := Byte((AParsedIp[I] shr 8) and $FF);
+    AAddr[(I * 2 + 1) + AOffset] := Byte(AParsedIp[I] and $FF);
+  end;
+end;
+
+class procedure TGeneralName.ParseIPv4(const AIp: String; var AAddr: TCryptoLibByteArray; AOffset: Int32);
+var
+  LTokens: TCryptoLibStringArray;
+  I: Int32;
+  LToken: String;
+begin
+  // Split by '.' and '/'
+  System.SetLength(LTokens, 0);
+  LToken := '';
+  for I := 1 to System.Length(AIp) do
+  begin
+    if (AIp[I] = '.') or (AIp[I] = '/') then
+    begin
+      if LToken <> '' then
+      begin
+        System.SetLength(LTokens, System.Length(LTokens) + 1);
+        LTokens[System.Length(LTokens) - 1] := LToken;
+        LToken := '';
+      end;
+    end
+    else
+    begin
+      LToken := LToken + AIp[I];
+    end;
+  end;
+  if LToken <> '' then
+  begin
+    System.SetLength(LTokens, System.Length(LTokens) + 1);
+    LTokens[System.Length(LTokens) - 1] := LToken;
+  end;
+
+  // Parse each token as byte
+  for I := 0 to System.Length(LTokens) - 1 do
+  begin
+    AAddr[AOffset] := Byte(StrToInt(LTokens[I]));
+    System.Inc(AOffset);
+  end;
+end;
+
+class procedure TGeneralName.ParseIPv4Mask(const AMask: String; var AAddr: TCryptoLibByteArray; AOffset: Int32);
+var
+  LBits: Int32;
+begin
+  LBits := StrToInt(AMask);
+  while LBits >= 8 do
+  begin
+    AAddr[AOffset] := $FF;
+    System.Inc(AOffset);
+    LBits := LBits - 8;
+  end;
+  if LBits > 0 then
+  begin
+    AAddr[AOffset] := Byte(($FF00 shr LBits) and $FF);
+  end;
+end;
+
+class function TGeneralName.ParseIPv6(const AIp: String): TCryptoLibInt32Array;
+var
+  LProcessedIp: String;
+  LIndex: Int32;
+  LVal: TCryptoLibInt32Array;
+  LDoubleColon: Int32;
+  LSegments: TCryptoLibStringArray;
+  I, J: Int32;
+  LSegment: String;
+  LTokens: TCryptoLibStringArray;
+begin
+  LProcessedIp := AIp;
+  if TPlatform.StartsWith(LProcessedIp, '::') then
+  begin
+    LProcessedIp := System.Copy(LProcessedIp, 2, System.Length(LProcessedIp) - 1);
+  end
+  else if TPlatform.EndsWith(LProcessedIp, '::') then
+  begin
+    LProcessedIp := System.Copy(LProcessedIp, 1, System.Length(LProcessedIp) - 1);
+  end;
+
+  LIndex := 0;
+  System.SetLength(LVal, 8);
+  LDoubleColon := -1;
+
+  // Split by ':'
+  System.SetLength(LSegments, 0);
+  LSegment := '';
+  for I := 1 to System.Length(LProcessedIp) do
+  begin
+    if LProcessedIp[I] = ':' then
+    begin
+      System.SetLength(LSegments, System.Length(LSegments) + 1);
+      LSegments[System.Length(LSegments) - 1] := LSegment;
+      LSegment := '';
+    end
+    else
+    begin
+      LSegment := LSegment + LProcessedIp[I];
+    end;
+  end;
+  if LSegment <> '' then
+  begin
+    System.SetLength(LSegments, System.Length(LSegments) + 1);
+    LSegments[System.Length(LSegments) - 1] := LSegment;
+  end;
+
+  for I := 0 to System.Length(LSegments) - 1 do
+  begin
+    LSegment := LSegments[I];
+    if System.Length(LSegment) = 0 then
+    begin
+      LDoubleColon := LIndex;
+      LVal[LIndex] := 0;
+      System.Inc(LIndex);
+    end
+    else
+    begin
+      if TPlatform.IndexOf(LSegment, '.') = 0 then
+      begin
+        // Parse as hex
+        LVal[LIndex] := StrToInt('$' + LSegment);
+        System.Inc(LIndex);
+      end
+      else
+      begin
+        // IPv4 embedded in IPv6 - split by '.'
+        System.SetLength(LTokens, 0);
+        for J := 1 to System.Length(LSegment) do
+        begin
+          if LSegment[J] = '.' then
+          begin
+            System.SetLength(LTokens, System.Length(LTokens) + 1);
+            LTokens[System.Length(LTokens) - 1] := '';
+          end
+          else
+          begin
+            if System.Length(LTokens) = 0 then
+            begin
+              System.SetLength(LTokens, 1);
+              LTokens[0] := '';
+            end;
+            LTokens[System.Length(LTokens) - 1] := LTokens[System.Length(LTokens) - 1] + LSegment[J];
+          end;
+        end;
+        if System.Length(LTokens) < 4 then
+        begin
+          System.SetLength(LTokens, 4);
+          for J := System.Length(LTokens) to 3 do
+            LTokens[J] := '';
+        end;
+
+        LVal[LIndex] := (StrToInt(LTokens[0]) shl 8) or StrToInt(LTokens[1]);
+        System.Inc(LIndex);
+        LVal[LIndex] := (StrToInt(LTokens[2]) shl 8) or StrToInt(LTokens[3]);
+        System.Inc(LIndex);
+      end;
+    end;
+  end;
+
+  if LIndex <> System.Length(LVal) then
+  begin
+    // Expand double colon
+    // This copies elements from doubleColon to the end, shifting them right
+    // Then zeros out the middle section
+    if LDoubleColon >= 0 then
+    begin
+      // Copy elements from doubleColon to destination position
+      System.Move(LVal[LDoubleColon], LVal[System.Length(LVal) - (LIndex - LDoubleColon)],
+        (LIndex - LDoubleColon) * SizeOf(Int32));
+      // Zero out the middle section
+      for I := LDoubleColon to System.Length(LVal) - (LIndex - LDoubleColon) - 1 do
+      begin
+        LVal[I] := 0;
+      end;
+    end;
+  end;
+
+  Result := LVal;
+end;
+
+class function TGeneralName.ParseIPv6Mask(const AMask: String): TCryptoLibInt32Array;
+var
+  LRes: TCryptoLibInt32Array;
+  LBits, LResPos: Int32;
+begin
+  System.SetLength(LRes, 8);
+  LBits := StrToInt(AMask);
+  LResPos := 0;
+  while LBits >= 16 do
+  begin
+    LRes[LResPos] := $FFFF;
+    System.Inc(LResPos);
+    LBits := LBits - 16;
+  end;
+  if LBits > 0 then
+  begin
+    LRes[LResPos] := $FFFF shr (16 - LBits);
+  end;
+  Result := LRes;
+end;
+
+{ TGeneralNames }
+
+class function TGeneralNames.GetInstance(AObj: TObject): IGeneralNames;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IGeneralNames, Result) then
+    Exit;
+
+  Result := TGeneralNames.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TGeneralNames.GetInstance(const AEncoded: TCryptoLibByteArray): IGeneralNames;
+begin
+  Result := TGeneralNames.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+
+class function TGeneralNames.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IGeneralNames;
+begin
+  Result := TGeneralNames.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TGeneralNames.GetOptional(const AElement: IAsn1Encodable): IGeneralNames;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IGeneralNames, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TGeneralNames.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TGeneralNames.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IGeneralNames;
+begin
+  Result := TGeneralNames.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TGeneralNames.Create(const AName: IGeneralName);
+begin
+  inherited Create();
+  if AName = nil then
+    raise EArgumentNilCryptoLibException.Create('name');
+  System.SetLength(FNames, 1);
+  FNames[0] := AName;
+end;
+
+constructor TGeneralNames.Create(const ANames: TCryptoLibGenericArray<IGeneralName>);
+var
+  I: Int32;
+begin
+  inherited Create();
+  if (ANames = nil) or (System.Length(ANames) = 0) then
+    raise EArgumentNilCryptoLibException.Create('names cannot be null or empty');
+  for I := 0 to System.Length(ANames) - 1 do
+  begin
+    if ANames[I] = nil then
+      raise EArgumentNilCryptoLibException.Create('names cannot contain null');
+  end;
+  FNames := TArrayUtils.Clone<IGeneralName>(ANames);
+end;
+
+constructor TGeneralNames.Create(const ASeq: IAsn1Sequence);
+begin
+  inherited Create();
+  FNames := TCollectionUtilities.Map<IAsn1Encodable, IGeneralName>(ASeq.Elements,
+    function(AElement: IAsn1Encodable): IGeneralName
+    begin
+      Result := TGeneralName.GetInstance(AElement as TObject);
+    end);
+end;
+
+function TGeneralNames.GetCount: Int32;
+begin
+  Result := System.Length(FNames);
+end;
+
+function TGeneralNames.GetNames: TCryptoLibGenericArray<IGeneralName>;
+begin
+  Result := TArrayUtils.Clone<IGeneralName>(FNames);
+end;
+
+function TGeneralNames.ToAsn1Object: IAsn1Object;
+var
+  LElements: TCryptoLibGenericArray<IAsn1Encodable>;
+  I: Int32;
+begin
+  System.SetLength(LElements, System.Length(FNames));
+  for I := 0 to System.Length(FNames) - 1 do
+  begin
+    LElements[I] := FNames[I];
+  end;
+  Result := TDerSequence.FromElements(LElements);
+end;
+
+function TGeneralNames.ToString: String;
+var
+  SB: TStringBuilder;
+  I: Int32;
+begin
+  SB := TStringBuilder.Create;
+  try
+    SB.AppendLine('GeneralNames:');
+    for I := 0 to System.Length(FNames) - 1 do
+    begin
+      SB.Append('    ');
+      SB.AppendLine(FNames[I].ToString);
+    end;
+    Result := SB.ToString();
+  finally
+    SB.Free;
+  end;
+end;
+
+{ TKeyUsage }
+
+class function TKeyUsage.GetInstance(AObj: TObject): IKeyUsage;
+var
+  LExtension: IX509Extension;
+  LBitString: IDerBitString;
+begin
+  if Supports(AObj, IKeyUsage, Result) then
+    Exit;
+
+  if Supports(AObj, IX509Extension, LExtension) then
+  begin
+    //Result := GetInstance(TX509Extension.ConvertValueToObject(LExtension));
+    //Exit;
+    LBitString := TDerBitString.GetInstance(TX509Extension.ConvertValueToObject(LExtension));
+    Result := TKeyUsage.Create(LBitString);
+    Exit;
+  end;
+
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LBitString := TDerBitString.GetInstance(AObj);
+  Result := TKeyUsage.Create(LBitString);
+end;
+
+class function TKeyUsage.GetInstance(const AEncoded: TCryptoLibByteArray): IKeyUsage;
+begin
+  Result := TKeyUsage.Create(TDerBitString.GetInstance(AEncoded));
+end;
+
+constructor TKeyUsage.Create(AUsage: Int32);
+begin
+  inherited Create(AUsage);
+end;
+
+constructor TKeyUsage.Create(const AUsage: IDerBitString);
+begin
+  inherited Create(AUsage.GetBytes(), AUsage.PadBits);
+end;
+
+function TKeyUsage.ToString: String;
+var
+  LData: TCryptoLibByteArray;
+  LValue: Int32;
+begin
+  LData := GetBytes();
+  if System.Length(LData) = 1 then
+  begin
+    LValue := LData[0] and $FF;
+    Result := Format('KeyUsage: 0x%s', [IntToHex(LValue, 2)]);
+  end
+  else
+  begin
+    LValue := ((LData[1] and $FF) shl 8) or (LData[0] and $FF);
+    Result := Format('KeyUsage: 0x%s', [IntToHex(LValue, 4)]);
+  end;
+end;
+
+{ TAuthorityKeyIdentifier }
+
+class function TAuthorityKeyIdentifier.GetInstance(AObj: TObject): IAuthorityKeyIdentifier;
+var
+  LExtension: IX509Extension;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAuthorityKeyIdentifier, Result) then
+    Exit;
+
+  if Supports(AObj, IX509Extension, LExtension) then
+  begin
+    Result := GetInstance(TX509Extension.ConvertValueToObject(LExtension) as TObject);
+    Exit;
+  end;
+
+  Result := TAuthorityKeyIdentifier.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAuthorityKeyIdentifier.GetInstance(const AEncoded: TCryptoLibByteArray): IAuthorityKeyIdentifier;
+begin
+  Result := TAuthorityKeyIdentifier.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAuthorityKeyIdentifier.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAuthorityKeyIdentifier;
+begin
+  Result := TAuthorityKeyIdentifier.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAuthorityKeyIdentifier.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAuthorityKeyIdentifier;
+begin
+  Result := TAuthorityKeyIdentifier.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TAuthorityKeyIdentifier.FromExtensions(const AExtensions: IX509Extensions): IAuthorityKeyIdentifier;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.AuthorityKeyIdentifier) as TObject);
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+
+  FKeyIdentifier := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IAsn1OctetString>(ASeq, LPos, 0, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IAsn1OctetString
+    begin
+      Result := TAsn1OctetString.GetTagged(ATagged, AState);
+    end);
+
+  FAuthorityCertIssuer := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IGeneralNames>(ASeq, LPos, 1, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IGeneralNames
+    begin
+      Result := TGeneralNames.GetTagged(ATagged, AState);
+    end);
+
+  FAuthorityCertSerialNumber := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDerInteger>(ASeq, LPos, 2, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDerInteger
+    begin
+      Result := TDerInteger.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create('Unexpected elements in sequence');
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const AKeyIdentifier: TCryptoLibByteArray);
+begin
+  Create(AKeyIdentifier, nil, Default(TBigInteger));
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const AKeyIdentifier: TCryptoLibByteArray;
+  const AAuthorityCertIssuer: IGeneralNames;
+  const AAuthorityCertSerialNumber: TBigInteger);
+var
+  LSerialNumber: IDerInteger;
+begin
+  inherited Create();
+  if AKeyIdentifier <> nil then
+    FKeyIdentifier := TDerOctetString.FromContentsOptional(AKeyIdentifier)
+  else
+    FKeyIdentifier := nil;
+  FAuthorityCertIssuer := AAuthorityCertIssuer;
+  if AAuthorityCertSerialNumber.IsInitialized then
+    LSerialNumber := TDerInteger.Create(AAuthorityCertSerialNumber)
+  else
+    LSerialNumber := nil;
+  FAuthorityCertSerialNumber := LSerialNumber;
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const AKeyIdentifier: IAsn1OctetString);
+begin
+  Create(AKeyIdentifier, nil, nil);
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const AKeyIdentifier: IAsn1OctetString;
+  const AAuthorityCertIssuer: IGeneralNames;
+  const AAuthorityCertSerialNumber: IDerInteger);
+begin
+  inherited Create();
+  FKeyIdentifier := AKeyIdentifier;
+  FAuthorityCertIssuer := AAuthorityCertIssuer;
+  FAuthorityCertSerialNumber := AAuthorityCertSerialNumber;
+end;
+
+constructor TAuthorityKeyIdentifier.Create(const AAuthorityCertIssuer: IGeneralNames;
+  const AAuthorityCertSerialNumber: TBigInteger);
+begin
+  Create(nil, AAuthorityCertIssuer, AAuthorityCertSerialNumber);
+end;
+
+function TAuthorityKeyIdentifier.GetKeyIdentifier: IAsn1OctetString;
+begin
+  Result := FKeyIdentifier;
+end;
+
+function TAuthorityKeyIdentifier.GetAuthorityCertIssuer: IGeneralNames;
+begin
+  Result := FAuthorityCertIssuer;
+end;
+
+function TAuthorityKeyIdentifier.GetAuthorityCertSerialNumber: IDerInteger;
+begin
+  Result := FAuthorityCertSerialNumber;
+end;
+
+function TAuthorityKeyIdentifier.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create();
+  LV.AddOptionalTagged(False, 0, FKeyIdentifier);
+  LV.AddOptionalTagged(False, 1, FAuthorityCertIssuer);
+  LV.AddOptionalTagged(False, 2, FAuthorityCertSerialNumber);
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TExtendedKeyUsage }
+
+class function TExtendedKeyUsage.GetInstance(AObj: TObject): IExtendedKeyUsage;
+var
+  LExtension: IX509Extension;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IExtendedKeyUsage, Result) then
+    Exit;
+
+  if Supports(AObj, IX509Extension, LExtension) then
+  begin
+    Result := GetInstance(TX509Extension.ConvertValueToObject(LExtension) as TObject);
+    Exit;
+  end;
+
+  Result := TExtendedKeyUsage.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TExtendedKeyUsage.GetInstance(const AEncoded: TCryptoLibByteArray): IExtendedKeyUsage;
+begin
+  Result := TExtendedKeyUsage.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TExtendedKeyUsage.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IExtendedKeyUsage;
+begin
+  Result := TExtendedKeyUsage.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TExtendedKeyUsage.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IExtendedKeyUsage;
+begin
+  Result := TExtendedKeyUsage.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TExtendedKeyUsage.FromExtensions(const AExtensions: IX509Extensions): IExtendedKeyUsage;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.ExtendedKeyUsage) as TObject);
+end;
+
+constructor TExtendedKeyUsage.Create(const ASeq: IAsn1Sequence);
+var
+  I: Int32;
+  LOid: IDerObjectIdentifier;
+begin
+  inherited Create();
+  FSeq := ASeq;
+  FUsageTable := TDictionary<IDerObjectIdentifier, Boolean>.Create(TCryptoLibComparers.OidEqualityComparer);
+
+  for I := 0 to ASeq.Count - 1 do
+  begin
+    LOid := TDerObjectIdentifier.GetInstance(ASeq[I] as TAsn1Encodable);
+    FUsageTable.Add(LOid, True);
+  end;
+end;
+
+constructor TExtendedKeyUsage.Create(const AUsages: TCryptoLibGenericArray<IDerObjectIdentifier>);
+var
+  I: Int32;
+  LV: IAsn1EncodableVector;
+  LOid: IDerObjectIdentifier;
+begin
+  inherited Create();
+  FUsageTable := TDictionary<IDerObjectIdentifier, Boolean>.Create(TCryptoLibComparers.OidEqualityComparer);
+  LV := TAsn1EncodableVector.Create();
+
+  for I := 0 to System.Length(AUsages) - 1 do
+  begin
+    LOid := AUsages[I];
+    LV.Add(LOid);
+    FUsageTable.Add(LOid, True);
+  end;
+
+  FSeq := TDerSequence.Create(LV);
+end;
+
+destructor TExtendedKeyUsage.Destroy;
+begin
+  FUsageTable.Free;
+  inherited Destroy;
+end;
+
+function TExtendedKeyUsage.HasKeyPurposeId(const AKeyPurposeId: IDerObjectIdentifier): Boolean;
+begin
+  Result := FUsageTable.ContainsKey(AKeyPurposeId);
+end;
+
+function TExtendedKeyUsage.GetAllUsages: TCryptoLibGenericArray<IDerObjectIdentifier>;
+var
+  LList: TList<IDerObjectIdentifier>;
+  LOid: IDerObjectIdentifier;
+begin
+  LList := TList<IDerObjectIdentifier>.Create();
+  try
+    for LOid in FUsageTable.Keys do
+    begin
+      LList.Add(LOid);
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
+function TExtendedKeyUsage.GetCount: Int32;
+begin
+  Result := FUsageTable.Count;
+end;
+
+function TExtendedKeyUsage.ToAsn1Object: IAsn1Object;
+begin
+  Result := FSeq;
+end;
+
+{ TX509Extensions }
+
+class constructor TX509Extensions.Create;
+begin
+  Boot;
+end;
+
+class destructor TX509Extensions.Destroy;
+begin
+  // Class vars are interface references, no cleanup needed
+end;
+
+class procedure TX509Extensions.Boot;
+begin
+  FSubjectDirectoryAttributes := TDerObjectIdentifier.Create('2.5.29.9');
+  FSubjectKeyIdentifier := TDerObjectIdentifier.Create('2.5.29.14');
+  FKeyUsage := TDerObjectIdentifier.Create('2.5.29.15');
+  FPrivateKeyUsagePeriod := TDerObjectIdentifier.Create('2.5.29.16');
+  FSubjectAlternativeName := TDerObjectIdentifier.Create('2.5.29.17');
+  FIssuerAlternativeName := TDerObjectIdentifier.Create('2.5.29.18');
+  FBasicConstraints := TDerObjectIdentifier.Create('2.5.29.19');
+  FCrlNumber := TDerObjectIdentifier.Create('2.5.29.20');
+  FReasonCode := TDerObjectIdentifier.Create('2.5.29.21');
+  FInstructionCode := TDerObjectIdentifier.Create('2.5.29.23');
+  FInvalidityDate := TDerObjectIdentifier.Create('2.5.29.24');
+  FDeltaCrlIndicator := TDerObjectIdentifier.Create('2.5.29.27');
+  FIssuingDistributionPoint := TDerObjectIdentifier.Create('2.5.29.28');
+  FCertificateIssuer := TDerObjectIdentifier.Create('2.5.29.29');
+  FNameConstraints := TDerObjectIdentifier.Create('2.5.29.30');
+  FCrlDistributionPoints := TDerObjectIdentifier.Create('2.5.29.31');
+  FCertificatePolicies := TDerObjectIdentifier.Create('2.5.29.32');
+  FPolicyMappings := TDerObjectIdentifier.Create('2.5.29.33');
+  FAuthorityKeyIdentifier := TDerObjectIdentifier.Create('2.5.29.35');
+  FPolicyConstraints := TDerObjectIdentifier.Create('2.5.29.36');
+  FExtendedKeyUsage := TDerObjectIdentifier.Create('2.5.29.37');
+  FFreshestCrl := TDerObjectIdentifier.Create('2.5.29.46');
+  FInhibitAnyPolicy := TDerObjectIdentifier.Create('2.5.29.54');
+  FAuthorityInfoAccess := TX509ObjectIdentifiers.IdPE.Branch('1');
+  FBiometricInfo := TX509ObjectIdentifiers.IdPE.Branch('2');
+  FQCStatements := TX509ObjectIdentifiers.IdPE.Branch('3');
+  FAuditIdentity := TX509ObjectIdentifiers.IdPE.Branch('4');
+  FSubjectInfoAccess := TX509ObjectIdentifiers.IdPE.Branch('11');
+  FLogoType := TX509ObjectIdentifiers.IdPE.Branch('12');
+  FNoRevAvail := TDerObjectIdentifier.Create('2.5.29.56');
+  FTargetInformation := TDerObjectIdentifier.Create('2.5.29.55');
+  FExpiredCertsOnCrl := TDerObjectIdentifier.Create('2.5.29.60');
+  FSubjectAltPublicKeyInfo := TDerObjectIdentifier.Create('2.5.29.72');
+  FAltSignatureAlgorithm := TDerObjectIdentifier.Create('2.5.29.73');
+  FAltSignatureValue := TDerObjectIdentifier.Create('2.5.29.74');
+  FDraftDeltaCertificateDescriptor := TDerObjectIdentifier.Create('2.16.840.1.114027.80.6.1');
+end;
+
+class function TX509Extensions.GetInstance(AObj: TObject): IX509Extensions;
+var
+  LSequence: IAsn1Sequence;
+  LTagged: IAsn1TaggedObject;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IX509Extensions, Result) then
+    Exit;
+
+  if Supports(AObj, IAsn1Sequence, LSequence) then
+  begin
+    Result := TX509Extensions.Create(LSequence);
+    Exit;
+  end;
+
+  if Supports(AObj, IAsn1TaggedObject, LTagged) then
+  begin
+    Result := GetInstance(TAsn1Utilities.CheckContextTagClass(LTagged).GetBaseObject().ToAsn1Object() as TObject);
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.Create('unknown object in factory: ' + TPlatform.GetTypeName(AObj));
+end;
+
+class function TX509Extensions.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IX509Extensions;
+begin
+  Result := TX509Extensions.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TX509Extensions.GetOptional(const AElement: IAsn1Encodable): IX509Extensions;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IX509Extensions, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TX509Extensions.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TX509Extensions.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IX509Extensions;
+begin
+  Result := TX509Extensions.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TX509Extensions.GetExtensionParsedValue(const AExtensions: IX509Extensions;
+  const AOid: IDerObjectIdentifier): IAsn1Object;
+var
+  LExt: IX509Extension;
+begin
+  if AExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LExt := AExtensions.GetExtension(AOid);
+  if LExt = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  Result := LExt.GetParsedValue();
+end;
+
+constructor TX509Extensions.Create(const ASeq: IAsn1Sequence);
+var
+  I: Int32;
+  LS: IAsn1Sequence;
+  LOid: IDerObjectIdentifier;
+  LIsCritical: Boolean;
+  LOctets: IAsn1OctetString;
+  LExt: IX509Extension;
+begin
+  inherited Create();
+
+  FOrdering := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+  FExtensions := TDictionary<IDerObjectIdentifier, IX509Extension>.Create(TCryptoLibComparers.OidEqualityComparer);
+
+  // Don't require non-empty sequence; we see empty extension blocks in the wild
+  for I := 0 to ASeq.Count - 1 do
+  begin
+    LS := TAsn1Sequence.GetInstance(ASeq[I] as TAsn1Encodable);
+
+    if (LS.Count < 2) or (LS.Count > 3) then
+      raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LS.Count]);
+
+    LOid := TDerObjectIdentifier.GetInstance(LS[0] as TAsn1Encodable);
+
+    LIsCritical := (LS.Count = 3) and TDerBoolean.GetInstance(LS[1] as TAsn1Encodable).IsTrue;
+
+    LOctets := TAsn1OctetString.GetInstance(LS[LS.Count - 1] as TAsn1Encodable);
+
+    if FExtensions.ContainsKey(LOid) then
+      raise EArgumentCryptoLibException.CreateFmt('repeated extension found: %s', [LOid.Id]);
+
+    LExt := TX509Extension.Create(LIsCritical, LOctets);
+    FExtensions.Add(LOid, LExt);
+    FOrdering.Add(LOid);
+  end;
+end;
+
+constructor TX509Extensions.Create(const AExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>);
+begin
+  Create(nil, AExtensions);
+end;
+
+constructor TX509Extensions.Create(const AOrdering: TList<IDerObjectIdentifier>;
+  const AExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>);
+var
+  LOid: IDerObjectIdentifier;
+begin
+  inherited Create();
+
+  if AOrdering = nil then
+  begin
+    FOrdering := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+    for LOid in AExtensions.Keys do
+    begin
+      FOrdering.Add(LOid);
+    end;
+  end
+  else
+  begin
+    FOrdering := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+    FOrdering.AddRange(AOrdering);
+  end;
+
+  FExtensions := TDictionary<IDerObjectIdentifier, IX509Extension>.Create(TCryptoLibComparers.OidEqualityComparer);
+  for LOid in FOrdering do
+  begin
+    FExtensions.Add(LOid, AExtensions[LOid]);
+  end;
+end;
+
+constructor TX509Extensions.Create(const AOids: TList<IDerObjectIdentifier>;
+  const AValues: TList<IX509Extension>);
+var
+  I: Int32;
+begin
+  inherited Create();
+
+  FOrdering := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+  FOrdering.AddRange(AOids);
+  FExtensions := TDictionary<IDerObjectIdentifier, IX509Extension>.Create(TCryptoLibComparers.OidEqualityComparer);
+
+  I := 0;
+  for I := 0 to FOrdering.Count - 1 do
+  begin
+    FExtensions.Add(FOrdering[I], AValues[I]);
+  end;
+end;
+
+destructor TX509Extensions.Destroy;
+begin
+  FExtensions.Free;
+  FOrdering.Free;
+  inherited Destroy;
+end;
+
+function TX509Extensions.GetCount: Int32;
+begin
+  Result := FOrdering.Count;
+end;
+
+function TX509Extensions.GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+begin
+  if not FExtensions.TryGetValue(AOid, Result) then
+    Result := nil;
+end;
+
+function TX509Extensions.GetExtensionParsedValue(const AOid: IDerObjectIdentifier): IAsn1Object;
+var
+  LExt: IX509Extension;
+begin
+  LExt := GetExtension(AOid);
+  if LExt = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := LExt.GetParsedValue();
+end;
+
+function TX509Extensions.GetExtensionValue(const AOid: IDerObjectIdentifier): IAsn1OctetString;
+var
+  LExt: IX509Extension;
+begin
+  LExt := GetExtension(AOid);
+  if LExt = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := LExt.Value;
+end;
+
+function TX509Extensions.GetExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+var
+  I: Int32;
+begin
+  System.SetLength(Result, FOrdering.Count);
+  for I := 0 to FOrdering.Count - 1 do
+  begin
+    Result[I] := FOrdering[I];
+  end;
+end;
+
+function TX509Extensions.GetNonCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+begin
+  Result := GetExtensionOidsInternal(False);
+end;
+
+function TX509Extensions.GetCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+begin
+  Result := GetExtensionOidsInternal(True);
+end;
+
+function TX509Extensions.GetExtensionOidsInternal(AIsCritical: Boolean): TCryptoLibGenericArray<IDerObjectIdentifier>;
+var
+  LOids: TList<IDerObjectIdentifier>;
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+begin
+  LOids := TList<IDerObjectIdentifier>.Create();
+  try
+    for LOid in FOrdering do
+    begin
+      LExt := FExtensions[LOid];
+      if LExt.IsCritical = AIsCritical then
+      begin
+        LOids.Add(LOid);
+      end;
+    end;
+    Result := LOids.ToArray();
+  finally
+    LOids.Free;
+  end;
+end;
+
+function TX509Extensions.HasAnyCriticalExtensions: Boolean;
+var
+  LOid: IDerObjectIdentifier;
+begin
+  for LOid in FOrdering do
+  begin
+    if FExtensions[LOid].IsCritical then
+    begin
+      Result := True;
+      Exit;
+    end;
+  end;
+  Result := False;
+end;
+
+function TX509Extensions.Equivalent(const AOther: IX509Extensions): Boolean;
+var
+  LOid: IDerObjectIdentifier;
+  LOtherExt: IX509Extension;
+begin
+  if AOther.Count <> FExtensions.Count then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  for LOid in FOrdering do
+  begin
+    LOtherExt := AOther.GetExtension(LOid);
+    if (LOtherExt = nil) or (not FExtensions[LOid].Value.Equals(LOtherExt.Value)) or
+      (FExtensions[LOid].IsCritical <> LOtherExt.IsCritical) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  Result := True;
+end;
+
+function TX509Extensions.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+begin
+  LV := TAsn1EncodableVector.Create(FOrdering.Count);
+
+  for LOid in FOrdering do
+  begin
+    LExt := FExtensions[LOid];
+    if LExt.IsCritical then
+    begin
+      LV.Add(TDerSequence.Create([LOid, TDerBoolean.True, LExt.Value]));
+    end
+    else
+    begin
+      LV.Add(TDerSequence.Create([LOid, LExt.Value]));
+    end;
+  end;
+
+  Result := TDerSequence.Create(LV);
+end;
+
+function TX509Extensions.ToAsn1ObjectTrimmed: IAsn1Sequence;
+var
+  LCount: Int32;
+  LV: IAsn1EncodableVector;
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+begin
+  // Count excludes AltSignatureValue if present
+  LCount := FOrdering.Count;
+  if FExtensions.ContainsKey(TX509Extensions.AltSignatureValue) then
+    System.Dec(LCount);
+
+  LV := TAsn1EncodableVector.Create(LCount);
+
+  for LOid in FOrdering do
+  begin
+    if TX509Extensions.AltSignatureValue.Equals(LOid) then
+      Continue;
+
+    LExt := FExtensions[LOid];
+    if LExt.IsCritical then
+    begin
+      LV.Add(TDerSequence.Create([LOid, TDerBoolean.True, LExt.Value]));
+    end
+    else
+    begin
+      LV.Add(TDerSequence.Create([LOid, LExt.Value]));
+    end;
+  end;
+
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TX509Name }
+
+class function TX509Name.GetInstance(AObj: TObject): IX509Name;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IX509Name>(AObj,
+    function(AElement: IAsn1Encodable): IX509Name
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TX509Name.GetInstance(const AEncoded: TCryptoLibByteArray): IX509Name;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IX509Name>(AEncoded,
+    function(AElement: IAsn1Encodable): IX509Name
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TX509Name.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IX509Name;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IX509Name>(AObj, AExplicitly,
+    function(AElement: IAsn1Encodable): IX509Name
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TX509Name.GetOptional(const AElement: IAsn1Encodable): IX509Name;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IX509Name, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TX509Name.Create(LSequence)
+  else
+    Result := nil;
+end;
+
+class function TX509Name.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IX509Name;
+begin
+  Result := TAsn1Utilities.GetTaggedChoice<IX509Name>(ATaggedObject, ADeclaredExplicit,
+    function(AElement: IAsn1Encodable): IX509Name
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+constructor TX509Name.Create(const ASeq: IAsn1Sequence);
+var
+  I, J: Int32;
+  LRdn: IAsn1Set;
+  LAttrTypeAndValue: IAsn1Sequence;
+  LType, LValue: IAsn1Object;
+  LAsn1String: IAsn1String;
+  LValueStr: String;
+  LOidList: TList<IDerObjectIdentifier>;
+  LValueList: TList<String>;
+  LAddedList: TList<Boolean>;
+begin
+  inherited Create();
+  FSeq := ASeq;
+  FConverter := nil;
+
+  LOidList := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+  LValueList := TList<String>.Create();
+  LAddedList := TList<Boolean>.Create();
+  try
+    // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+    for I := 0 to ASeq.Count - 1 do
+    begin
+      // RelativeDistinguishedName ::= SET SIZE(1..MAX) OF AttributeTypeAndValue
+      LRdn := TAsn1Set.GetInstance(ASeq[I] as TObject);
+
+      for J := 0 to LRdn.Count - 1 do
+      begin
+        // AttributeTypeAndValue ::= SEQUENCE { type OID, value ANY }
+        LAttrTypeAndValue := TAsn1Sequence.GetInstance(LRdn[J] as TObject);
+        if LAttrTypeAndValue.Count <> 2 then
+          raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LAttrTypeAndValue.Count]);
+
+        LType := LAttrTypeAndValue[0].ToAsn1Object();
+        LValue := LAttrTypeAndValue[1].ToAsn1Object();
+
+        LOidList.Add(TDerObjectIdentifier.GetInstance(LType));
+
+        // Handle string values vs hex-encoded values
+        if Supports(LValue, IAsn1String, LAsn1String) then
+        begin
+          // Check if it's DerUniversalString (which we don't treat as string)
+          if not Supports(LValue, IDerUniversalString) then
+          begin
+            LValueStr := LAsn1String.GetString();
+            if (System.Length(LValueStr) > 0) and (LValueStr[1] = '#') then
+            begin
+              LValueStr := '\' + LValueStr;
+            end;
+            LValueList.Add(LValueStr);
+          end
+          else
+          begin
+            // DerUniversalString - hex encode
+            LValueStr := '#' + THex.Encode(LValue.GetEncoded(TAsn1Encodable.Der), False);
+            LValueList.Add(LValueStr);
+          end;
+        end
+        else
+        begin
+          // Hex-encode non-string values
+          LValueStr := '#' + THex.Encode(LValue.GetEncoded(TAsn1Encodable.Der), False);
+          LValueList.Add(LValueStr);
+        end;
+
+        // true if not first attribute in RDN
+        LAddedList.Add(J <> 0);
+      end;
+    end;
+
+    // Convert lists to arrays
+    System.SetLength(FOids, LOidList.Count);
+    System.SetLength(FValues, LValueList.Count);
+    System.SetLength(FAdded, LAddedList.Count);
+    System.SetLength(FValueList, LValueList.Count);
+
+    for I := 0 to LOidList.Count - 1 do
+    begin
+      FOids[I] := LOidList[I];
+      FValues[I] := LValueList[I];
+      FValueList[I] := LValueList[I];
+      FAdded[I] := LAddedList[I];
+    end;
+  finally
+    LOidList.Free;
+    LValueList.Free;
+    LAddedList.Free;
+  end;
+end;
+
+constructor TX509Name.Create(const AName: String);
+begin
+  Create(False, AName);
+end;
+
+constructor TX509Name.Create(const AReverse: Boolean; const AName: String);
+begin
+  Create(AReverse, FDefaultLookup, AName);
+end;
+
+constructor TX509Name.Create(const AReverse: Boolean; const ATable: TDictionary<String, String>;
+  const AName: String);
+var
+  LLookup: TDictionary<String, IDerObjectIdentifier>;
+  LKey: String;
+  LOid: IDerObjectIdentifier;
+  LPair: TPair<String, IDerObjectIdentifier>;
+begin
+  inherited Create();
+  FConverter := CreateDefaultConverter();
+
+  if ATable <> nil then
+  begin
+    // This constructor takes TDictionary<String, String> which needs conversion
+    // Convert string keys to OIDs using DefaultLookup, then merge with DefaultLookup
+    LLookup := TDictionary<String, IDerObjectIdentifier>.Create(TCryptoLibComparers.OrdinalIgnoreCaseEqualityComparer);
+    try
+      // First, add entries from ATable (convert string keys to OIDs via DefaultLookup)
+      for LKey in ATable.Keys do
+      begin
+        if FDefaultLookup.TryGetValue(LKey, LOid) then
+        begin
+          LLookup.Add(LKey, LOid);
+        end;
+      end;
+      // Then add all entries from DefaultLookup
+      for LPair in FDefaultLookup do
+      begin
+        if not LLookup.ContainsKey(LPair.Key) then
+        begin
+          LLookup.Add(LPair.Key, LPair.Value);
+        end;
+      end;
+      Create(AReverse, LLookup, AName, FConverter);
+    finally
+      LLookup.Free;
+    end;
+  end
+  else
+  begin
+    Create(AReverse, FDefaultLookup, AName, FConverter);
+  end;
+end;
+
+constructor TX509Name.Create(const AOrdering: TList<IDerObjectIdentifier>;
+  const AAttributes: TDictionary<IDerObjectIdentifier, String>);
+begin
+  Create(AOrdering, AAttributes, CreateDefaultConverter());
+end;
+
+constructor TX509Name.Create(const AOrdering: TList<IDerObjectIdentifier>;
+  const AAttributes: TDictionary<IDerObjectIdentifier, String>;
+  const AConverter: IX509NameEntryConverter);
+var
+  LOid: IDerObjectIdentifier;
+  LAttribute: String;
+  I: Int32;
+begin
+  inherited Create();
+  FConverter := AConverter;
+
+  System.SetLength(FOids, AOrdering.Count);
+  System.SetLength(FValues, AOrdering.Count);
+  System.SetLength(FAdded, AOrdering.Count);
+
+  for I := 0 to AOrdering.Count - 1 do
+  begin
+    LOid := AOrdering[I];
+    if not AAttributes.TryGetValue(LOid, LAttribute) then
+      raise EArgumentCryptoLibException.CreateFmt('No attribute for object id - %s - passed to distinguished name', [LOid.Id]);
+
+    FOids[I] := LOid;
+    FValues[I] := LAttribute;
+    FAdded[I] := False;
+  end;
+end;
+
+constructor TX509Name.Create(const AOids: TList<IDerObjectIdentifier>;
+  const AValues: TList<String>);
+begin
+  Create(AOids, AValues, CreateDefaultConverter());
+end;
+
+constructor TX509Name.Create(const AOids: TList<IDerObjectIdentifier>;
+  const AValues: TList<String>; const AConverter: IX509NameEntryConverter);
+var
+  I: Int32;
+begin
+  inherited Create();
+  FConverter := AConverter;
+
+  if AOids.Count <> AValues.Count then
+    raise EArgumentCryptoLibException.Create('''oids'' must be same length as ''values''.');
+
+  System.SetLength(FOids, AOids.Count);
+  System.SetLength(FValues, AValues.Count);
+  System.SetLength(FAdded, AOids.Count);
+
+  for I := 0 to AOids.Count - 1 do
+  begin
+    FOids[I] := AOids[I];
+    FValues[I] := AValues[I];
+    FAdded[I] := False;
+  end;
+end;
+
+constructor TX509Name.Create(const ADirName: String; const AConverter: IX509NameEntryConverter);
+begin
+  Create(FDefaultReverse, ADirName, AConverter);
+end;
+
+constructor TX509Name.Create(const AReverse: Boolean; const ADirName: String;
+  const AConverter: IX509NameEntryConverter);
+begin
+  Create(AReverse, FDefaultLookup, ADirName, AConverter);
+end;
+
+constructor TX509Name.Create(const AReverse: Boolean; const ALookup: TDictionary<String, IDerObjectIdentifier>;
+  const ADirName: String);
+begin
+  Create(AReverse, ALookup, ADirName, CreateDefaultConverter());
+end;
+
+constructor TX509Name.Create(const AReverse: Boolean; const ALookup: TDictionary<String, IDerObjectIdentifier>;
+  const ADirName: String; const AConverter: IX509NameEntryConverter);
+var
+  LNameTokenizer, LRdnTokenizer: IX509NameTokenizer;
+  LRdn, LTypeToken, LValueToken: String;
+  LOidList: TList<IDerObjectIdentifier>;
+  LValueList: TList<String>;
+  LAddedList: TList<Boolean>;
+  I, LCount: Int32;
+  LOid: IDerObjectIdentifier;
+  LUnescapedValue: String;
+  LO: TList<IDerObjectIdentifier>;
+  LV: TList<String>;
+  LA: TList<Boolean>;
+begin
+  inherited Create();
+  FConverter := AConverter;
+
+  LOidList := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+  LValueList := TList<String>.Create();
+  LAddedList := TList<Boolean>.Create();
+  try
+    LNameTokenizer := TX509NameTokenizer.Create(ADirName);
+    while LNameTokenizer.HasMoreTokens() do
+    begin
+      LRdn := NextToken(LNameTokenizer);
+
+      LRdnTokenizer := TX509NameTokenizer.Create(LRdn, '+');
+      AddAttribute(ALookup, NextToken(LRdnTokenizer), False, LOidList, LValueList, LAddedList);
+
+      while LRdnTokenizer.HasMoreTokens() do
+      begin
+        AddAttribute(ALookup, NextToken(LRdnTokenizer), True, LOidList, LValueList, LAddedList);
+      end;
+    end;
+
+    if AReverse then
+    begin
+      // Reverse the order
+      LO := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+      LV := TList<String>.Create();
+      LA := TList<Boolean>.Create();
+      try
+        LCount := 1;
+        for I := 0 to LOidList.Count - 1 do
+        begin
+          if LAddedList[I] then
+            LCount := LCount and Int32(-1)
+          else
+            LCount := LCount and 0;  // Set to 0 (all bits clear)
+          LO.Insert(LCount, LOidList[I]);
+          LV.Insert(LCount, LValueList[I]);
+          LA.Insert(LCount, LAddedList[I]);
+          System.Inc(LCount);
+        end;
+        LOidList.Clear;
+        LValueList.Clear;
+        LAddedList.Clear;
+        LOidList.AddRange(LO);
+        LValueList.AddRange(LV);
+        LAddedList.AddRange(LA);
+      finally
+        LO.Free;
+        LV.Free;
+        LA.Free;
+      end;
+    end;
+
+    System.SetLength(FOids, LOidList.Count);
+    System.SetLength(FValues, LValueList.Count);
+    System.SetLength(FAdded, LAddedList.Count);
+    System.SetLength(FValueList, LValueList.Count);
+
+    for I := 0 to LOidList.Count - 1 do
+    begin
+      FOids[I] := LOidList[I];
+      FValues[I] := LValueList[I];
+      FAdded[I] := LAddedList[I];
+      FValueList[I] := LValueList[I];
+    end;
+  finally
+    LOidList.Free;
+    LValueList.Free;
+    LAddedList.Free;
+  end;
+end;
+
+constructor TX509Name.Create(const AOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  const AValues: TCryptoLibStringArray);
+var
+  I: Int32;
+begin
+  inherited Create();
+  FConverter := CreateDefaultConverter();
+  FOids := TArrayUtils.Clone<IDerObjectIdentifier>(AOids);
+  FValues := TArrayUtils.Clone(AValues);
+  FValueList := TArrayUtils.Clone(AValues);
+  // Initialize FAdded array - all false for direct constructor
+  System.SetLength(FAdded, System.Length(FOids));
+  for I := 0 to System.Length(FAdded) - 1 do
+  begin
+    FAdded[I] := False;
+  end;
+end;
+
+function TX509Name.GetOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+begin
+  Result := TArrayUtils.Clone<IDerObjectIdentifier>(FOids);
+end;
+
+function TX509Name.GetValues: TCryptoLibStringArray;
+begin
+  Result := TArrayUtils.Clone(FValues);
+end;
+
+function TX509Name.GetValueList: TCryptoLibStringArray;
+begin
+  Result := GetValueList(nil);
+end;
+
+function TX509Name.GetValue(const AOid: IDerObjectIdentifier): String;
+var
+  I: Int32;
+begin
+  for I := 0 to System.Length(FOids) - 1 do
+  begin
+    if FOids[I].Equals(AOid) then
+    begin
+      Result := FValues[I];
+      Exit;
+    end;
+  end;
+  Result := '';
+end;
+
+function TX509Name.GetValues(const AOid: IDerObjectIdentifier): TCryptoLibStringArray;
+var
+  LValues: TList<String>;
+  I: Int32;
+begin
+  LValues := TList<String>.Create();
+  try
+    for I := 0 to System.Length(FOids) - 1 do
+    begin
+      if FOids[I].Equals(AOid) then
+      begin
+        LValues.Add(FValues[I]);
+      end;
+    end;
+    Result := LValues.ToArray();
+  finally
+    LValues.Free;
+  end;
+end;
+
+function TX509Name.ToString: String;
+begin
+  Result := ToString(FDefaultReverse, FDefaultSymbols);
+end;
+
+function TX509Name.ToString(AReverse: Boolean; const AOidSymbols: TDictionary<IDerObjectIdentifier, String>): String;
+var
+  LComponents: TList<TStringBuilder>;
+  LAva: TStringBuilder;
+  I: Int32;
+  LBuf: TStringBuilder;
+begin
+  LComponents := TList<TStringBuilder>.Create();
+  try
+    LAva := nil;
+
+    for I := 0 to System.Length(FOids) - 1 do
+    begin
+      if FAdded[I] then
+      begin
+        LAva.Append('+');
+        AppendValue(LAva, AOidSymbols, FOids[I], FValues[I]);
+      end
+      else
+      begin
+        LAva := TStringBuilder.Create();
+        AppendValue(LAva, AOidSymbols, FOids[I], FValues[I]);
+        LComponents.Add(LAva);
+      end;
+    end;
+
+    if AReverse then
+    begin
+      // Reverse components list
+      for I := 0 to (LComponents.Count div 2) - 1 do
+      begin
+        LComponents.Exchange(I, LComponents.Count - 1 - I);
+      end;
+    end;
+
+    LBuf := TStringBuilder.Create();
+    try
+      if LComponents.Count > 0 then
+      begin
+        LBuf.Append(LComponents[0].ToString());
+
+        for I := 1 to LComponents.Count - 1 do
+        begin
+          LBuf.Append(',');
+          LBuf.Append(LComponents[I].ToString());
+        end;
+      end;
+
+      Result := LBuf.ToString();
+    finally
+      LBuf.Free;
+    end;
+  finally
+    for I := 0 to LComponents.Count - 1 do
+      LComponents[I].Free;
+    LComponents.Free;
+  end;
+end;
+
+function TX509Name.ToString(const AOid: IDerObjectIdentifier): String;
+begin
+  Result := GetValue(AOid);
+end;
+
+function TX509Name.ToAsn1Object: IAsn1Object;
+var
+  LVec, LSVec: IAsn1EncodableVector;
+  LOid: IDerObjectIdentifier;
+  I: Int32;
+  LConvertedValue: IAsn1Object;
+begin
+  if FSeq <> nil then
+  begin
+    Result := FSeq;
+    Exit;
+  end;
+
+  // Initialize converter if not already set
+  if FConverter = nil then
+  begin
+    FConverter := CreateDefaultConverter();
+  end;
+
+  LVec := TAsn1EncodableVector.Create();
+  LSVec := TAsn1EncodableVector.Create();
+  LOid := nil;
+
+  for I := 0 to System.Length(FOids) - 1 do
+  begin
+    // If previous OID exists and current is not added to current RDN, finalize RDN
+    if (LOid <> nil) and (not FAdded[I]) then
+    begin
+      LVec.Add(TDerSet.FromVector(LSVec));
+      LSVec := TAsn1EncodableVector.Create();
+    end;
+
+    LOid := FOids[I];
+    LConvertedValue := FConverter.GetConvertedValue(LOid, FValues[I]);
+    LSVec.Add(TDerSequence.Create([LOid, LConvertedValue]));
+  end;
+
+  // Add final RDN
+  LVec.Add(TDerSet.FromVector(LSVec));
+
+  FSeq := TDerSequence.Create(LVec);
+  Result := FSeq;
+end;
+
+class constructor TX509Name.Create;
+begin
+  FDefaultReverseLock := TCriticalSection.Create();
+  Boot;
+end;
+
+class destructor TX509Name.Destroy;
+begin
+  FDefaultSymbols.Free;
+  FRFC2253Symbols.Free;
+  FRFC1779Symbols.Free;
+  FDefaultLookup.Free;
+  FDefaultReverseLock.Free;
+end;
+
+class function TX509Name.GetDefaultReverse: Boolean;
+begin
+  FDefaultReverseLock.Acquire;
+  try
+    Result := FDefaultReverse;
+  finally
+    FDefaultReverseLock.Release;
+  end;
+end;
+
+class procedure TX509Name.SetDefaultReverse(const AValue: Boolean);
+begin
+  FDefaultReverseLock.Acquire;
+  try
+    FDefaultReverse := AValue;
+  finally
+    FDefaultReverseLock.Release;
+  end;
+end;
+
+class procedure TX509Name.Boot;
+begin
+  FDefaultReverse := False;
+  FDefaultSymbols := TDictionary<IDerObjectIdentifier, String>.Create(TCryptoLibComparers.OidEqualityComparer);
+  FRFC2253Symbols := TDictionary<IDerObjectIdentifier, String>.Create(TCryptoLibComparers.OidEqualityComparer);
+  FRFC1779Symbols := TDictionary<IDerObjectIdentifier, String>.Create(TCryptoLibComparers.OidEqualityComparer);
+  FDefaultLookup := TDictionary<String, IDerObjectIdentifier>.Create(TCryptoLibComparers.OrdinalIgnoreCaseEqualityComparer);
+
+  // OID constants
+  FC := TDerObjectIdentifier.Create('2.5.4.6');
+  FO := TDerObjectIdentifier.Create('2.5.4.10');
+  FOU := TDerObjectIdentifier.Create('2.5.4.11');
+  FT := TDerObjectIdentifier.Create('2.5.4.12');
+  FCN := TDerObjectIdentifier.Create('2.5.4.3');
+  FStreet := TDerObjectIdentifier.Create('2.5.4.9');
+  FSerialNumber := TDerObjectIdentifier.Create('2.5.4.5');
+  FL := TDerObjectIdentifier.Create('2.5.4.7');
+  FST := TDerObjectIdentifier.Create('2.5.4.8');
+  FSurname := TDerObjectIdentifier.Create('2.5.4.4');
+  FGivenName := TDerObjectIdentifier.Create('2.5.4.42');
+  FInitials := TDerObjectIdentifier.Create('2.5.4.43');
+  FGeneration := TDerObjectIdentifier.Create('2.5.4.44');
+  FUniqueIdentifier := TDerObjectIdentifier.Create('2.5.4.45');
+  FDescription := TDerObjectIdentifier.Create('2.5.4.13');
+  FBusinessCategory := TDerObjectIdentifier.Create('2.5.4.15');
+  FPostalCode := TDerObjectIdentifier.Create('2.5.4.17');
+  FDnQualifier := TDerObjectIdentifier.Create('2.5.4.46');
+  FPseudonym := TDerObjectIdentifier.Create('2.5.4.65');
+  FRole := TDerObjectIdentifier.Create('2.5.4.72');
+  FDateOfBirth := TX509ObjectIdentifiers.IdPda.Branch('1');
+  FPlaceOfBirth := TX509ObjectIdentifiers.IdPda.Branch('2');
+  FGender := TX509ObjectIdentifiers.IdPda.Branch('3');
+  FCountryOfCitizenship := TX509ObjectIdentifiers.IdPda.Branch('4');
+  FCountryOfResidence := TX509ObjectIdentifiers.IdPda.Branch('5');
+  FNameAtBirth := TDerObjectIdentifier.Create('1.3.36.8.3.14');
+  FPostalAddress := TDerObjectIdentifier.Create('2.5.4.16');
+  FDmdName := TDerObjectIdentifier.Create('2.5.4.54');
+  FTelephoneNumber := TX509ObjectIdentifiers.IdAtTelephoneNumber;
+  FOrganizationIdentifier := TX509ObjectIdentifiers.IdAtOrganizationIdentifier;
+  FName := TX509ObjectIdentifiers.IdAtName;
+  FEmailAddress := TPkcsObjectIdentifiers.Pkcs9AtEmailAddress;
+  FUnstructuredName := TPkcsObjectIdentifiers.Pkcs9AtUnstructuredName;
+  FUnstructuredAddress := TPkcsObjectIdentifiers.Pkcs9AtUnstructuredAddress;
+  FE := FEmailAddress;
+  FDC := TDerObjectIdentifier.Create('0.9.2342.19200300.100.1.25');
+  FUID := TDerObjectIdentifier.Create('0.9.2342.19200300.100.1.1');
+  FJurisdictionC := TDerObjectIdentifier.Create('1.3.6.1.4.1.311.60.2.1.3');
+  FJurisdictionST := TDerObjectIdentifier.Create('1.3.6.1.4.1.311.60.2.1.2');
+  FJurisdictionL := TDerObjectIdentifier.Create('1.3.6.1.4.1.311.60.2.1.1');
+
+  // DefaultSymbols
+  FDefaultSymbols.Add(FC, 'C');
+  FDefaultSymbols.Add(FO, 'O');
+  FDefaultSymbols.Add(FT, 'T');
+  FDefaultSymbols.Add(FOU, 'OU');
+  FDefaultSymbols.Add(FCN, 'CN');
+  FDefaultSymbols.Add(FL, 'L');
+  FDefaultSymbols.Add(FST, 'ST');
+  FDefaultSymbols.Add(FSerialNumber, 'SERIALNUMBER');
+  FDefaultSymbols.Add(FEmailAddress, 'E');
+  FDefaultSymbols.Add(FDC, 'DC');
+  FDefaultSymbols.Add(FUID, 'UID');
+  FDefaultSymbols.Add(FStreet, 'STREET');
+  FDefaultSymbols.Add(FSurname, 'SURNAME');
+  FDefaultSymbols.Add(FGivenName, 'GIVENNAME');
+  FDefaultSymbols.Add(FInitials, 'INITIALS');
+  FDefaultSymbols.Add(FGeneration, 'GENERATION');
+  FDefaultSymbols.Add(FDescription, 'DESCRIPTION');
+  FDefaultSymbols.Add(FRole, 'ROLE');
+  FDefaultSymbols.Add(FUnstructuredAddress, 'unstructuredAddress');
+  FDefaultSymbols.Add(FUnstructuredName, 'unstructuredName');
+  FDefaultSymbols.Add(FUniqueIdentifier, 'UniqueIdentifier');
+  FDefaultSymbols.Add(FDnQualifier, 'DN');
+  FDefaultSymbols.Add(FPseudonym, 'Pseudonym');
+  FDefaultSymbols.Add(FPostalAddress, 'PostalAddress');
+  FDefaultSymbols.Add(FNameAtBirth, 'NameAtBirth');
+  FDefaultSymbols.Add(FCountryOfCitizenship, 'CountryOfCitizenship');
+  FDefaultSymbols.Add(FCountryOfResidence, 'CountryOfResidence');
+  FDefaultSymbols.Add(FGender, 'Gender');
+  FDefaultSymbols.Add(FPlaceOfBirth, 'PlaceOfBirth');
+  FDefaultSymbols.Add(FDateOfBirth, 'DateOfBirth');
+  FDefaultSymbols.Add(FPostalCode, 'PostalCode');
+  FDefaultSymbols.Add(FBusinessCategory, 'BusinessCategory');
+  FDefaultSymbols.Add(FTelephoneNumber, 'TelephoneNumber');
+  FDefaultSymbols.Add(FName, 'Name');
+  FDefaultSymbols.Add(FOrganizationIdentifier, 'organizationIdentifier');
+  FDefaultSymbols.Add(FJurisdictionC, 'jurisdictionCountry');
+  FDefaultSymbols.Add(FJurisdictionST, 'jurisdictionState');
+  FDefaultSymbols.Add(FJurisdictionL, 'jurisdictionLocality');
+
+  // RFC2253Symbols
+  FRFC2253Symbols.Add(FC, 'C');
+  FRFC2253Symbols.Add(FO, 'O');
+  FRFC2253Symbols.Add(FOU, 'OU');
+  FRFC2253Symbols.Add(FCN, 'CN');
+  FRFC2253Symbols.Add(FL, 'L');
+  FRFC2253Symbols.Add(FST, 'ST');
+  FRFC2253Symbols.Add(FStreet, 'STREET');
+  FRFC2253Symbols.Add(FDC, 'DC');
+  FRFC2253Symbols.Add(FUID, 'UID');
+
+  // RFC1779Symbols
+  FRFC1779Symbols.Add(FC, 'C');
+  FRFC1779Symbols.Add(FO, 'O');
+  FRFC1779Symbols.Add(FOU, 'OU');
+  FRFC1779Symbols.Add(FCN, 'CN');
+  FRFC1779Symbols.Add(FL, 'L');
+  FRFC1779Symbols.Add(FST, 'ST');
+  FRFC1779Symbols.Add(FStreet, 'STREET');
+
+  // DefaultLookup
+  FDefaultLookup.Add('c', FC);
+  FDefaultLookup.Add('o', FO);
+  FDefaultLookup.Add('t', FT);
+  FDefaultLookup.Add('ou', FOU);
+  FDefaultLookup.Add('cn', FCN);
+  FDefaultLookup.Add('l', FL);
+  FDefaultLookup.Add('st', FST);
+  FDefaultLookup.Add('sn', FSurname);
+  FDefaultLookup.Add('serialnumber', FSerialNumber);
+  FDefaultLookup.Add('street', FStreet);
+  FDefaultLookup.Add('emailaddress', FE);
+  FDefaultLookup.Add('dc', FDC);
+  FDefaultLookup.Add('e', FE);
+  FDefaultLookup.Add('uid', FUID);
+  FDefaultLookup.Add('surname', FSurname);
+  FDefaultLookup.Add('givenname', FGivenName);
+  FDefaultLookup.Add('initials', FInitials);
+  FDefaultLookup.Add('generation', FGeneration);
+  FDefaultLookup.Add('description', FDescription);
+  FDefaultLookup.Add('role', FRole);
+  FDefaultLookup.Add('unstructuredaddress', FUnstructuredAddress);
+  FDefaultLookup.Add('unstructuredname', FUnstructuredName);
+  FDefaultLookup.Add('uniqueidentifier', FUniqueIdentifier);
+  FDefaultLookup.Add('dn', FDnQualifier);
+  FDefaultLookup.Add('pseudonym', FPseudonym);
+  FDefaultLookup.Add('postaladdress', FPostalAddress);
+  FDefaultLookup.Add('nameatbirth', FNameAtBirth);
+  FDefaultLookup.Add('countryofcitizenship', FCountryOfCitizenship);
+  FDefaultLookup.Add('countryofresidence', FCountryOfResidence);
+  FDefaultLookup.Add('gender', FGender);
+  FDefaultLookup.Add('placeofbirth', FPlaceOfBirth);
+  FDefaultLookup.Add('dateofbirth', FDateOfBirth);
+  FDefaultLookup.Add('postalcode', FPostalCode);
+  FDefaultLookup.Add('businesscategory', FBusinessCategory);
+  FDefaultLookup.Add('telephonenumber', FTelephoneNumber);
+  FDefaultLookup.Add('name', FName);
+  FDefaultLookup.Add('organizationidentifier', FOrganizationIdentifier);
+  FDefaultLookup.Add('jurisdictioncountry', FJurisdictionC);
+  FDefaultLookup.Add('jurisdictionstate', FJurisdictionST);
+  FDefaultLookup.Add('jurisdictionlocality', FJurisdictionL);
+end;
+
+class function TX509Name.CreateDefaultConverter: IX509NameEntryConverter;
+begin
+  Result := TX509DefaultEntryConverter.Create();
+end;
+
+class function TX509Name.DecodeOid(const AName: String;
+  const ALookup: TDictionary<String, IDerObjectIdentifier>): IDerObjectIdentifier;
+var
+  LOid: IDerObjectIdentifier;
+begin
+  if (System.Length(AName) >= 4) and TPlatform.StartsWith(AName, 'OID.', True) then
+  begin
+    // Skip "OID." (4 characters), copy rest of string
+    Result := TDerObjectIdentifier.Create(System.Copy(AName, 5, System.Length(AName) - 4));
+    Exit;
+  end;
+
+  if TDerObjectIdentifier.TryFromID(AName, LOid) then
+  begin
+    Result := LOid;
+    Exit;
+  end;
+
+  if ALookup.TryGetValue(AName, LOid) then
+  begin
+    Result := LOid;
+    Exit;
+  end;
+
+  raise EArgumentCryptoLibException.CreateFmt('Unknown object id - %s - passed to distinguished name', [AName]);
+end;
+
+class function TX509Name.NextToken(const ATokenizer: IX509NameTokenizer): String;
+var
+  LToken: String;
+begin
+  LToken := ATokenizer.NextToken();
+  if LToken = '' then
+    raise EArgumentCryptoLibException.Create('badly formatted directory string');
+  Result := LToken;
+end;
+
+class function TX509Name.NextToken(const ATokenizer: IX509NameTokenizer; AExpectMoreTokens: Boolean): String;
+var
+  LToken: String;
+begin
+  LToken := ATokenizer.NextToken();
+  if (LToken = '') or (ATokenizer.HasMoreTokens() <> AExpectMoreTokens) then
+    raise EArgumentCryptoLibException.Create('badly formatted directory string');
+  Result := LToken;
+end;
+
+
+class function TX509Name.EquivalentStrings(const AS1, AS2: String): Boolean;
+var
+  LV1, LV2: String;
+begin
+  if AS1 = AS2 then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  LV1 := TIetfUtilities.Canonicalize(AS1);
+  LV2 := TIetfUtilities.Canonicalize(AS2);
+
+  if LV1 <> LV2 then
+  begin
+    LV1 := TIetfUtilities.StripInternalSpaces(LV1);
+    LV2 := TIetfUtilities.StripInternalSpaces(LV2);
+
+    if LV1 <> LV2 then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  Result := True;
+end;
+
+procedure TX509Name.AddAttribute(const ALookup: TDictionary<String, IDerObjectIdentifier>;
+  const AToken: String; AAdded: Boolean; const AOidList: TList<IDerObjectIdentifier>;
+  const AValueList: TList<String>; const AAddedList: TList<Boolean>);
+var
+  LTokenizer: IX509NameTokenizer;
+  LTypeToken, LValueToken: String;
+  LOid: IDerObjectIdentifier;
+  LUnescapedValue: String;
+begin
+  LTokenizer := TX509NameTokenizer.Create(AToken, '=');
+  LTypeToken := NextToken(LTokenizer, True);
+  LValueToken := NextToken(LTokenizer, False);
+
+  LOid := DecodeOid(Trim(LTypeToken), ALookup);
+  LUnescapedValue := TIetfUtilities.Unescape(LValueToken);
+
+  AOidList.Add(LOid);
+  AValueList.Add(LUnescapedValue);
+  AAddedList.Add(AAdded);
+end;
+
+class procedure TX509Name.AppendValue(const ABuf: TStringBuilder;
+  const AOidSymbols: TDictionary<IDerObjectIdentifier, String>;
+  const AOid: IDerObjectIdentifier; const AVal: String);
+var
+  LSym: String;
+  LStart, LEnd, LIndex: Int32;
+  LC: Char;
+begin
+  if AOidSymbols.TryGetValue(AOid, LSym) then
+    ABuf.Append(LSym)
+  else
+    ABuf.Append(AOid.Id);
+
+  ABuf.Append('=');
+  LStart := ABuf.Length;
+
+  ABuf.Append(AVal);
+  LEnd := ABuf.Length;
+
+  // Skip escaped hash prefix if present
+  LIndex := LStart;
+  if (LIndex + 1 < LEnd) and (ABuf.Chars[LIndex] = '\') and (ABuf.Chars[LIndex + 1] = '#') then
+    System.Inc(LIndex, 2);
+
+  // Escape special characters
+  while LIndex <> LEnd do
+  begin
+    LC := ABuf.Chars[LIndex];
+    case LC of
+      ',', '"', '\', '+', '=', '<', '>', ';':
+      begin
+        ABuf.Insert(LIndex, '\');
+        System.Inc(LIndex, 2);
+        System.Inc(LEnd);
+      end;
+    else
+      begin
+        System.Inc(LIndex);
+      end;
+    end;
+  end;
+
+  while (LStart < LEnd) and (ABuf.Chars[LStart] = ' ') do
+  begin
+    ABuf.Insert(LStart, '\');
+    System.Inc(LStart, 2);
+    System.Inc(LEnd);
+  end;
+
+  // Escape trailing spaces
+  // Pre-decrement end at start of each iteration, then check
+  // First decrement before loop
+  System.Dec(LEnd);
+  while (LEnd > LStart) and (ABuf.Chars[LEnd] = ' ') do
+  begin
+    ABuf.Insert(LEnd, '\');
+    // Decrement for next iteration (matches --end in while condition)
+    System.Dec(LEnd);
+  end;
+end;
+
+function TX509Name.GetOidList: TCryptoLibGenericArray<IDerObjectIdentifier>;
+begin
+  Result := TArrayUtils.Clone<IDerObjectIdentifier>(FOids);
+end;
+
+function TX509Name.GetValueList(const AOid: IDerObjectIdentifier): TCryptoLibStringArray;
+var
+  LV: TList<String>;
+  I: Int32;
+  LValue: String;
+begin
+  LV := TList<String>.Create();
+  try
+    for I := 0 to System.Length(FValues) - 1 do
+    begin
+      if (AOid = nil) or AOid.Equals(FOids[I]) then
+      begin
+        LValue := FValues[I];
+        if TPlatform.StartsWith(LValue, '\#') then
+        begin
+          // Skip '\' at position 1, copy rest of string
+          LValue := System.Copy(LValue, 2, System.Length(LValue) - 1);
+        end;
+        LV.Add(LValue);
+      end;
+    end;
+    Result := LV.ToArray();
+  finally
+    LV.Free;
+  end;
+end;
+
+function TX509Name.Equivalent(const AOther: IX509Name; AInOrder: Boolean): Boolean;
+var
+  LOtherOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LOtherValues: TCryptoLibStringArray;
+  I, LOrderingSize, LStart, LEnd, LDelta, J: Int32;
+  LIndexes: TCryptoLibBooleanArray;
+  LOid: IDerObjectIdentifier;
+  LValue: String;
+  LFound: Boolean;
+begin
+  if AOther = nil then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  if AOther = Self as IX509Name then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  LOtherOids := AOther.Oids;
+  LOtherValues := AOther.Values;
+  LOrderingSize := System.Length(FOids);
+
+  if LOrderingSize <> System.Length(LOtherOids) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  if LOrderingSize = 0 then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  if AInOrder then
+  begin
+    // In-order comparison
+    for I := 0 to LOrderingSize - 1 do
+    begin
+      if not FOids[I].Equals(LOtherOids[I]) then
+      begin
+        Result := False;
+        Exit;
+      end;
+
+      if not EquivalentStrings(FValues[I], LOtherValues[I]) then
+      begin
+        Result := False;
+        Exit;
+      end;
+    end;
+    Result := True;
+  end
+  else
+  begin
+    // Out-of-order comparison
+    System.SetLength(LIndexes, LOrderingSize);
+
+    if FOids[0].Equals(LOtherOids[0]) then
+    begin
+      // Guess forward
+      LStart := 0;
+      LEnd := LOrderingSize;
+      LDelta := 1;
+    end
+    else
+    begin
+      // Guess reversed
+      LStart := LOrderingSize - 1;
+      LEnd := -1;
+      LDelta := -1;
+    end;
+
+    I := LStart;
+    while I <> LEnd do
+    begin
+      LOid := FOids[I];
+      LValue := FValues[I];
+      LFound := False;
+
+      for J := 0 to LOrderingSize - 1 do
+      begin
+        if LIndexes[J] then
+          Continue;
+
+        if LOid.Equals(LOtherOids[J]) then
+        begin
+          if EquivalentStrings(LValue, LOtherValues[J]) then
+          begin
+            LIndexes[J] := True;
+            LFound := True;
+            Break;
+          end;
+        end;
+      end;
+
+      if not LFound then
+      begin
+        Result := False;
+        Exit;
+      end;
+
+      System.Inc(I, LDelta);
+    end;
+
+    Result := True;
+  end;
+end;
+
+{ TAttributeX509 }
+
+class function TAttributeX509.GetInstance(AObj: TObject): IAttributeX509;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAttributeX509, Result) then
+    Exit;
+
+  Result := TAttributeX509.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAttributeX509.GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeX509;
+begin
+  Result := TAttributeX509.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAttributeX509.GetInstance(const AObj: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributeX509;
+begin
+  Result := TAttributeX509.Create(TAsn1Sequence.GetInstance(AObj, ADeclaredExplicit));
+end;
+
+class function TAttributeX509.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributeX509;
+begin
+  Result := TAttributeX509.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAttributeX509.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FAttrType := TDerObjectIdentifier.GetInstance(ASeq[0] as TObject);
+  FAttrValues := TAsn1Set.GetInstance(ASeq[1] as TObject);
+end;
+
+constructor TAttributeX509.Create(const AAttrType: IDerObjectIdentifier; const AAttrValues: IAsn1Set);
+begin
+  inherited Create();
+  if AAttrType = nil then
+    raise EArgumentNilCryptoLibException.Create('attrType');
+  if AAttrValues = nil then
+    raise EArgumentNilCryptoLibException.Create('attrValues');
+  FAttrType := AAttrType;
+  FAttrValues := AAttrValues;
+end;
+
+function TAttributeX509.GetAttrType: IDerObjectIdentifier;
+begin
+  Result := FAttrType;
+end;
+
+function TAttributeX509.GetAttrValues: IAsn1Set;
+begin
+  Result := FAttrValues;
+end;
+
+function TAttributeX509.GetAttributeValues: TCryptoLibGenericArray<IAsn1Encodable>;
+begin
+  Result := FAttrValues.GetElements();
+end;
+
+function TAttributeX509.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create(FAttrType, FAttrValues);
+end;
+
+{ TAttCertValidityPeriod }
+
+class function TAttCertValidityPeriod.GetInstance(AObj: TObject): IAttCertValidityPeriod;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAttCertValidityPeriod, Result) then
+    Exit;
+
+  Result := TAttCertValidityPeriod.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAttCertValidityPeriod.GetInstance(const AEncoded: TCryptoLibByteArray): IAttCertValidityPeriod;
+begin
+  Result := TAttCertValidityPeriod.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAttCertValidityPeriod.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IAttCertValidityPeriod;
+begin
+  Result := TAttCertValidityPeriod.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TAttCertValidityPeriod.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttCertValidityPeriod;
+begin
+  Result := TAttCertValidityPeriod.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAttCertValidityPeriod.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  if LCount <> 2 then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FNotBeforeTime := TAsn1GeneralizedTime.GetInstance(ASeq[0] as TObject);
+  FNotAfterTime := TAsn1GeneralizedTime.GetInstance(ASeq[1] as TObject);
+end;
+
+constructor TAttCertValidityPeriod.Create(const ANotBeforeTime, ANotAfterTime: IAsn1GeneralizedTime);
+begin
+  inherited Create();
+  if ANotBeforeTime = nil then
+    raise EArgumentNilCryptoLibException.Create('notBeforeTime');
+  if ANotAfterTime = nil then
+    raise EArgumentNilCryptoLibException.Create('notAfterTime');
+  FNotBeforeTime := ANotBeforeTime;
+  FNotAfterTime := ANotAfterTime;
+end;
+
+function TAttCertValidityPeriod.GetNotBeforeTime: IAsn1GeneralizedTime;
+begin
+  Result := FNotBeforeTime;
+end;
+
+function TAttCertValidityPeriod.GetNotAfterTime: IAsn1GeneralizedTime;
+begin
+  Result := FNotAfterTime;
+end;
+
+function TAttCertValidityPeriod.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create(FNotBeforeTime, FNotAfterTime);
+end;
+
+{ TPolicyInformation }
+
+class function TPolicyInformation.GetInstance(AObj: TObject): IPolicyInformation;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IPolicyInformation, Result) then
+    Exit;
+
+  Result := TPolicyInformation.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TPolicyInformation.GetInstance(const AEncoded: TCryptoLibByteArray): IPolicyInformation;
+begin
+  Result := TPolicyInformation.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TPolicyInformation.GetInstance(const AObj: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IPolicyInformation;
+begin
+  Result := TPolicyInformation.Create(TAsn1Sequence.GetInstance(AObj, ADeclaredExplicit));
+end;
+
+class function TPolicyInformation.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IPolicyInformation;
+begin
+  Result := TPolicyInformation.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TPolicyInformation.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  if (LCount < 1) or (LCount > 2) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FPolicyIdentifier := TDerObjectIdentifier.GetInstance(ASeq[0] as TObject);
+  if LCount < 2 then
+    FPolicyQualifiers := nil
+  else
+    FPolicyQualifiers := TAsn1Sequence.GetInstance(ASeq[1] as TObject);
+end;
+
+constructor TPolicyInformation.Create(const APolicyIdentifier: IDerObjectIdentifier);
+begin
+  Create(APolicyIdentifier, nil);
+end;
+
+constructor TPolicyInformation.Create(const APolicyIdentifier: IDerObjectIdentifier;
+  const APolicyQualifiers: IAsn1Sequence);
+begin
+  inherited Create();
+  if APolicyIdentifier = nil then
+    raise EArgumentNilCryptoLibException.Create('policyIdentifier');
+  FPolicyIdentifier := APolicyIdentifier;
+  FPolicyQualifiers := APolicyQualifiers;
+end;
+
+function TPolicyInformation.GetPolicyIdentifier: IDerObjectIdentifier;
+begin
+  Result := FPolicyIdentifier;
+end;
+
+function TPolicyInformation.GetPolicyQualifiers: IAsn1Sequence;
+begin
+  Result := FPolicyQualifiers;
+end;
+
+function TPolicyInformation.ToAsn1Object: IAsn1Object;
+begin
+  if FPolicyQualifiers = nil then
+    Result := TDerSequence.Create(FPolicyIdentifier)
+  else
+    Result := TDerSequence.Create(FPolicyIdentifier, FPolicyQualifiers);
+end;
+
+{ TIssuerSerial }
+
+class function TIssuerSerial.GetInstance(AObj: TObject): IIssuerSerial;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IIssuerSerial, Result) then
+    Exit;
+
+  Result := TIssuerSerial.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TIssuerSerial.GetInstance(const AEncoded: TCryptoLibByteArray): IIssuerSerial;
+begin
+  Result := TIssuerSerial.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TIssuerSerial.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IIssuerSerial;
+begin
+  Result := TIssuerSerial.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TIssuerSerial.GetOptional(const AElement: IAsn1Encodable): IIssuerSerial;
+var
+  LSeq: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IIssuerSerial, Result) then
+    Exit;
+
+  LSeq := TAsn1Sequence.GetOptional(AElement);
+  if LSeq <> nil then
+  begin
+    Result := TIssuerSerial.Create(LSeq);
+    Exit;
+  end;
+
+  Result := nil;
+end;
+
+class function TIssuerSerial.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IIssuerSerial;
+begin
+  Result := TIssuerSerial.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TIssuerSerial.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 2) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FIssuer := TGeneralNames.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FSerial := TDerInteger.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FIssuerUid := TAsn1Utilities.ReadOptional<IDerBitString>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerBitString
+    begin
+      Result := TDerBitString.GetOptional(AElement);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+constructor TIssuerSerial.Create(const AIssuer: IX509Name; const ASerial: IDerInteger);
+begin
+  Create(TGeneralNames.Create(TGeneralName.Create(AIssuer)), ASerial);
+end;
+
+constructor TIssuerSerial.Create(const AIssuer: IGeneralNames; const ASerial: IDerInteger);
+begin
+  Create(AIssuer, ASerial, nil);
+end;
+
+constructor TIssuerSerial.Create(const AIssuer: IGeneralNames; const ASerial: IDerInteger;
+  const AIssuerUid: IDerBitString);
+begin
+  inherited Create();
+  if AIssuer = nil then
+    raise EArgumentNilCryptoLibException.Create('issuer');
+  if ASerial = nil then
+    raise EArgumentNilCryptoLibException.Create('serial');
+  FIssuer := AIssuer;
+  FSerial := ASerial;
+  FIssuerUid := AIssuerUid;
+end;
+
+function TIssuerSerial.GetIssuer: IGeneralNames;
+begin
+  Result := FIssuer;
+end;
+
+function TIssuerSerial.GetSerial: IDerInteger;
+begin
+  Result := FSerial;
+end;
+
+function TIssuerSerial.GetIssuerUid: IDerBitString;
+begin
+  Result := FIssuerUid;
+end;
+
+function TIssuerSerial.ToAsn1Object: IAsn1Object;
+begin
+  if FIssuerUid = nil then
+    Result := TDerSequence.Create([FIssuer, FSerial])
+  else
+    Result := TDerSequence.Create([FIssuer, FSerial, FIssuerUid]);
+end;
+
+{ TV2Form }
+
+class function TV2Form.GetInstance(AObj: TObject): IV2Form;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IV2Form, Result) then
+    Exit;
+
+  Result := TV2Form.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TV2Form.GetInstance(const AEncoded: TCryptoLibByteArray): IV2Form;
+begin
+  Result := TV2Form.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TV2Form.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IV2Form;
+begin
+  Result := TV2Form.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TV2Form.GetOptional(const AElement: IAsn1Encodable): IV2Form;
+var
+  LSeq: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IV2Form, Result) then
+    Exit;
+
+  LSeq := TAsn1Sequence.GetOptional(AElement);
+  if LSeq <> nil then
+  begin
+    Result := TV2Form.Create(LSeq);
+    Exit;
+  end;
+
+  Result := nil;
+end;
+
+class function TV2Form.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IV2Form;
+begin
+  Result := TV2Form.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TV2Form.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FIssuerName := TAsn1Utilities.ReadOptional<IGeneralNames>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IGeneralNames
+    begin
+      Result := TGeneralNames.GetOptional(AElement);
+    end);
+  FBaseCertificateID := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IIssuerSerial>(ASeq, LPos, 0, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IIssuerSerial
+    begin
+      Result := TIssuerSerial.GetTagged(ATagged, AState);
+    end);
+  FObjectDigestInfo := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IObjectDigestInfo>(ASeq, LPos, 1, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IObjectDigestInfo
+    begin
+      Result := TObjectDigestInfo.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+constructor TV2Form.Create(const AIssuerName: IGeneralNames);
+begin
+  Create(AIssuerName, nil, nil);
+end;
+
+constructor TV2Form.Create(const AIssuerName: IGeneralNames; const ABaseCertificateID: IIssuerSerial);
+begin
+  Create(AIssuerName, ABaseCertificateID, nil);
+end;
+
+constructor TV2Form.Create(const AIssuerName: IGeneralNames; const AObjectDigestInfo: IObjectDigestInfo);
+begin
+  Create(AIssuerName, nil, AObjectDigestInfo);
+end;
+
+constructor TV2Form.Create(const AIssuerName: IGeneralNames; const ABaseCertificateID: IIssuerSerial;
+  const AObjectDigestInfo: IObjectDigestInfo);
+begin
+  inherited Create();
+  FIssuerName := AIssuerName;
+  FBaseCertificateID := ABaseCertificateID;
+  FObjectDigestInfo := AObjectDigestInfo;
+end;
+
+function TV2Form.GetIssuerName: IGeneralNames;
+begin
+  Result := FIssuerName;
+end;
+
+function TV2Form.GetBaseCertificateID: IIssuerSerial;
+begin
+  Result := FBaseCertificateID;
+end;
+
+function TV2Form.GetObjectDigestInfo: IObjectDigestInfo;
+begin
+  Result := FObjectDigestInfo;
+end;
+
+function TV2Form.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(3);
+  LV.AddOptional(FIssuerName);
+  LV.AddOptionalTagged(False, 0, FBaseCertificateID);
+  LV.AddOptionalTagged(False, 1, FObjectDigestInfo);
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TObjectDigestInfo }
+
+class function TObjectDigestInfo.GetInstance(AObj: TObject): IObjectDigestInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IObjectDigestInfo, Result) then
+    Exit;
+
+  Result := TObjectDigestInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TObjectDigestInfo.GetInstance(const AEncoded: TCryptoLibByteArray): IObjectDigestInfo;
+begin
+  Result := TObjectDigestInfo.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TObjectDigestInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AIsExplicit: Boolean): IObjectDigestInfo;
+begin
+  Result := TObjectDigestInfo.Create(TAsn1Sequence.GetInstance(AObj, AIsExplicit));
+end;
+
+class function TObjectDigestInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IObjectDigestInfo;
+begin
+  Result := TObjectDigestInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TObjectDigestInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 3) or (LCount > 4) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FDigestedObjectType := TDerEnumerated.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FOtherObjectTypeID := TAsn1Utilities.ReadOptional<IDerObjectIdentifier>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerObjectIdentifier
+    begin
+      Result := TDerObjectIdentifier.GetOptional(AElement);
+    end);
+  FDigestAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FObjectDigest := TDerBitString.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+constructor TObjectDigestInfo.Create(ADigestedObjectType: Int32; const AOtherObjectTypeID: String;
+  const ADigestAlgorithm: IAlgorithmIdentifier; const AObjectDigest: TCryptoLibByteArray);
+begin
+  inherited Create();
+  FDigestedObjectType := TDerEnumerated.Create(ADigestedObjectType);
+
+  if ADigestedObjectType = OtherObjectDigest then
+    FOtherObjectTypeID := TDerObjectIdentifier.Create(AOtherObjectTypeID);
+
+  if ADigestAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create('digestAlgorithm');
+  FDigestAlgorithm := ADigestAlgorithm;
+  FObjectDigest := TDerBitString.Create(AObjectDigest);
+end;
+
+function TObjectDigestInfo.GetDigestedObjectType: IDerEnumerated;
+begin
+  Result := FDigestedObjectType;
+end;
+
+function TObjectDigestInfo.GetOtherObjectTypeID: IDerObjectIdentifier;
+begin
+  Result := FOtherObjectTypeID;
+end;
+
+function TObjectDigestInfo.GetDigestAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FDigestAlgorithm;
+end;
+
+function TObjectDigestInfo.GetObjectDigest: IDerBitString;
+begin
+  Result := FObjectDigest;
+end;
+
+function TObjectDigestInfo.ToAsn1Object: IAsn1Object;
+begin
+  if FOtherObjectTypeID = nil then
+    Result := TDerSequence.Create([FDigestedObjectType, FDigestAlgorithm, FObjectDigest])
+  else
+    Result := TDerSequence.Create([FDigestedObjectType, FOtherObjectTypeID, FDigestAlgorithm, FObjectDigest]);
+end;
+
+{ TDistributionPoint }
+
+class function TDistributionPoint.GetInstance(AObj: TObject): IDistributionPoint;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IDistributionPoint, Result) then
+    Exit;
+
+  Result := TDistributionPoint.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TDistributionPoint.GetInstance(const AEncoded: TCryptoLibByteArray): IDistributionPoint;
+begin
+  Result := TDistributionPoint.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TDistributionPoint.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IDistributionPoint;
+begin
+  Result := TDistributionPoint.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TDistributionPoint.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IDistributionPoint;
+begin
+  Result := TDistributionPoint.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TDistributionPoint.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FDistributionPointName := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IDistributionPointName>(ASeq, LPos, 0, True,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IDistributionPointName
+    begin
+      Result := TDistributionPointName.GetTagged(ATagged, AState);
+    end);
+  FReasons := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IReasonFlags>(ASeq, LPos, 1, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IReasonFlags
+    begin
+      Result := TReasonFlags.Create(TDerBitString.GetTagged(ATagged, AState));
+    end);
+  FCrlIssuer := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IGeneralNames>(ASeq, LPos, 2, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IGeneralNames
+    begin
+      Result := TGeneralNames.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+constructor TDistributionPoint.Create(const ADistributionPointName: IDistributionPointName;
+  const AReasons: IReasonFlags; const ACrlIssuer: IGeneralNames);
+begin
+  inherited Create();
+  FDistributionPointName := ADistributionPointName;
+  FReasons := AReasons;
+  FCrlIssuer := ACrlIssuer;
+end;
+
+function TDistributionPoint.GetDistributionPointName: IDistributionPointName;
+begin
+  Result := FDistributionPointName;
+end;
+
+function TDistributionPoint.GetReasons: IReasonFlags;
+begin
+  Result := FReasons;
+end;
+
+function TDistributionPoint.GetCrlIssuer: IGeneralNames;
+begin
+  Result := FCrlIssuer;
+end;
+
+function TDistributionPoint.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(3);
+  if FDistributionPointName <> nil then
+    LV.AddOptionalTagged(True, 0, FDistributionPointName as IAsn1Encodable);
+  if FReasons <> nil then
+    LV.AddOptionalTagged(False, 1, FReasons as IAsn1Encodable);
+  if FCrlIssuer <> nil then
+    LV.AddOptionalTagged(False, 2, FCrlIssuer as IAsn1Encodable);
+  Result := TDerSequence.Create(LV);
+end;
+
+function TDistributionPoint.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LIndent: String;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.AppendLine('DistributionPoint: [');
+    LIndent := '    ';
+    if FDistributionPointName <> nil then
+    begin
+      LBuf.Append(LIndent).Append('distributionPoint:').AppendLine();
+      LBuf.Append(LIndent).Append(LIndent).Append((FDistributionPointName as TDistributionPointName).ToString()).AppendLine();
+    end;
+    if FReasons <> nil then
+    begin
+      LBuf.Append(LIndent).Append('reasons:').AppendLine();
+      LBuf.Append(LIndent).Append(LIndent).Append(FReasons.ToAsn1Object().ToString()).AppendLine();
+    end;
+    if FCrlIssuer <> nil then
+    begin
+      LBuf.Append(LIndent).Append('cRLIssuer:').AppendLine();
+      LBuf.Append(LIndent).Append(LIndent).Append((FCrlIssuer as TGeneralNames).ToString()).AppendLine();
+    end;
+    LBuf.AppendLine(']');
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+{ TDistributionPointName }
+
+class function TDistributionPointName.GetInstance(AObj: TObject): IDistributionPointName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IDistributionPointName>(AObj,
+    function(AElement: IAsn1Encodable): IDistributionPointName
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TDistributionPointName.GetInstance(const AEncoded: TCryptoLibByteArray): IDistributionPointName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IDistributionPointName>(AEncoded,
+    function(AElement: IAsn1Encodable): IDistributionPointName
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TDistributionPointName.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IDistributionPointName;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IDistributionPointName>(AObj, AExplicitly,
+    function(AElement: IAsn1Encodable): IDistributionPointName
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TDistributionPointName.GetOptional(const AElement: IAsn1Encodable): IDistributionPointName;
+var
+  LTaggedObject: IAsn1TaggedObject;
+  LBaseObject: IAsn1Encodable;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IDistributionPointName, Result) then
+    Exit;
+
+  LTaggedObject := TAsn1TaggedObject.GetOptional(AElement);
+  if LTaggedObject <> nil then
+  begin
+    LBaseObject := GetOptionalBaseObject(LTaggedObject);
+    if LBaseObject <> nil then
+    begin
+      Result := TDistributionPointName.Create(LTaggedObject.TagNo, LBaseObject);
+      Exit;
+    end;
+  end;
+
+  Result := nil;
+end;
+
+class function TDistributionPointName.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IDistributionPointName;
+begin
+  Result := TAsn1Utilities.GetTaggedChoice<IDistributionPointName>(ATaggedObject, ADeclaredExplicit,
+    function(AElement: IAsn1Encodable): IDistributionPointName
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TDistributionPointName.GetOptionalBaseObject(const ATaggedObject: IAsn1TaggedObject): IAsn1Encodable;
+begin
+  if ATaggedObject.HasContextTag() then
+  begin
+    case ATaggedObject.TagNo of
+      FullName:
+        Result := TGeneralNames.GetTagged(ATaggedObject, False);
+      NameRelativeToCrlIssuer:
+        Result := TAsn1Set.GetTagged(ATaggedObject, False);
+    else
+      Result := nil;
+    end;
+  end
+  else
+    Result := nil;
+end;
+
+constructor TDistributionPointName.Create(const AName: IGeneralNames);
+begin
+  Create(FullName, AName);
+end;
+
+constructor TDistributionPointName.Create(AType: Int32; const AName: IAsn1Encodable);
+begin
+  inherited Create();
+  FType := AType;
+  FName := AName;
+end;
+
+function TDistributionPointName.GetType: Int32;
+begin
+  Result := FType;
+end;
+
+function TDistributionPointName.GetName: IAsn1Encodable;
+begin
+  Result := FName;
+end;
+
+function TDistributionPointName.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerTaggedObject.Create(False, FType, FName);
+end;
+
+function TDistributionPointName.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LIndent: String;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.AppendLine('DistributionPointName: [');
+    LIndent := '    ';
+    if FType = FullName then
+    begin
+      LBuf.Append(LIndent).Append('fullName:').AppendLine();
+      LBuf.Append(LIndent).Append(LIndent).Append(FName.ToAsn1Object().ToString()).AppendLine();
+    end
+    else
+    begin
+      LBuf.Append(LIndent).Append('nameRelativeToCRLIssuer:').AppendLine();
+      LBuf.Append(LIndent).Append(LIndent).Append(FName.ToAsn1Object().ToString()).AppendLine();
+    end;
+    LBuf.AppendLine(']');
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+{ TReasonFlags }
+
+constructor TReasonFlags.Create(AReasons: Int32);
+begin
+  inherited Create(AReasons);
+end;
+
+constructor TReasonFlags.Create(const AReasons: IDerBitString);
+begin
+  inherited Create(AReasons.GetBytes(), AReasons.PadBits);
+end;
+
+{ TAttCertIssuer }
+
+class function TAttCertIssuer.GetInstance(AObj: TObject): IAttCertIssuer;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IAttCertIssuer>(AObj,
+    function(AElement: IAsn1Encodable): IAttCertIssuer
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TAttCertIssuer.GetInstance(const AEncoded: TCryptoLibByteArray): IAttCertIssuer;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IAttCertIssuer>(AEncoded,
+    function(AElement: IAsn1Encodable): IAttCertIssuer
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TAttCertIssuer.GetInstance(const AObj: IAsn1TaggedObject;
+  AIsExplicit: Boolean): IAttCertIssuer;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IAttCertIssuer>(AObj, AIsExplicit,
+    function(AElement: IAsn1Encodable): IAttCertIssuer
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+class function TAttCertIssuer.GetOptional(const AElement: IAsn1Encodable): IAttCertIssuer;
+var
+  LV1Form: IGeneralNames;
+  LTaggedObject: IAsn1TaggedObject;
+  LV2Form: IV2Form;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IAttCertIssuer, Result) then
+    Exit;
+
+  LV1Form := TGeneralNames.GetOptional(AElement);
+  if LV1Form <> nil then
+  begin
+    Result := TAttCertIssuer.Create(LV1Form);
+    Exit;
+  end;
+
+  LTaggedObject := TAsn1TaggedObject.GetOptional(AElement);
+  if (LTaggedObject <> nil) and LTaggedObject.HasContextTag(0) then
+  begin
+    Result := TAttCertIssuer.Create(TV2Form.GetTagged(LTaggedObject, False));
+    Exit;
+  end;
+
+  LV2Form := TV2Form.GetOptional(AElement);
+  if LV2Form <> nil then
+  begin
+    Result := TAttCertIssuer.Create(LV2Form);
+    Exit;
+  end;
+
+  Result := nil;
+end;
+
+class function TAttCertIssuer.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttCertIssuer;
+begin
+  Result := TAsn1Utilities.GetTaggedChoice<IAttCertIssuer>(ATaggedObject, ADeclaredExplicit,
+    function(AElement: IAsn1Encodable): IAttCertIssuer
+    begin
+      Result := GetInstance(AElement as TObject);
+    end);
+end;
+
+constructor TAttCertIssuer.Create(const ANames: IGeneralNames);
+begin
+  inherited Create();
+  FObj := ANames;
+  FChoiceObj := FObj.ToAsn1Object();
+end;
+
+constructor TAttCertIssuer.Create(const AV2Form: IV2Form);
+begin
+  inherited Create();
+  FObj := AV2Form;
+  FChoiceObj := TDerTaggedObject.Create(False, 0, FObj);
+end;
+
+function TAttCertIssuer.GetIssuer: IAsn1Encodable;
+begin
+  Result := FObj;
+end;
+
+function TAttCertIssuer.ToAsn1Object: IAsn1Object;
+begin
+  Result := FChoiceObj;
+end;
+
+{ THolder }
+
+class function THolder.GetInstance(AObj: TObject): IHolder;
+var
+  LTaggedObject: IAsn1TaggedObject;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IHolder, Result) then
+    Exit;
+
+  if Supports(AObj, IAsn1TaggedObject, LTaggedObject) then
+  begin
+    Result := THolder.Create(LTaggedObject);
+    Exit;
+  end;
+
+  Result := THolder.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function THolder.GetInstance(const AEncoded: TCryptoLibByteArray): IHolder;
+begin
+  Result := THolder.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function THolder.GetInstance(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IHolder;
+begin
+  Result := THolder.Create(TAsn1Sequence.GetInstance(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function THolder.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IHolder;
+begin
+  Result := THolder.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor THolder.Create(const ATagObj: IAsn1TaggedObject);
+begin
+  inherited Create();
+  if ATagObj.HasContextTag(0) then
+    FBaseCertificateID := TIssuerSerial.GetTagged(ATagObj, True)
+  else if ATagObj.HasContextTag(1) then
+    FEntityName := TGeneralNames.GetTagged(ATagObj, True)
+  else
+    raise EArgumentCryptoLibException.Create('unknown tag in Holder');
+  FVersion := 0;
+end;
+
+constructor THolder.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 0) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FBaseCertificateID := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IIssuerSerial>(ASeq, LPos, 0, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IIssuerSerial
+    begin
+      Result := TIssuerSerial.GetTagged(ATagged, AState);
+    end);
+  FEntityName := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IGeneralNames>(ASeq, LPos, 1, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IGeneralNames
+    begin
+      Result := TGeneralNames.GetTagged(ATagged, AState);
+    end);
+  FObjectDigestInfo := TAsn1Utilities.ReadOptionalContextTagged<Boolean, IObjectDigestInfo>(ASeq, LPos, 2, False,
+    function(ATagged: IAsn1TaggedObject; AState: Boolean): IObjectDigestInfo
+    begin
+      Result := TObjectDigestInfo.GetTagged(ATagged, AState);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+
+  FVersion := 1;
+end;
+
+constructor THolder.Create(const ABaseCertificateID: IIssuerSerial);
+begin
+  Create(ABaseCertificateID, 1);
+end;
+
+constructor THolder.Create(const ABaseCertificateID: IIssuerSerial; AVersion: Int32);
+begin
+  inherited Create();
+  FBaseCertificateID := ABaseCertificateID;
+  FVersion := AVersion;
+end;
+
+constructor THolder.Create(const AEntityName: IGeneralNames);
+begin
+  Create(AEntityName, 1);
+end;
+
+constructor THolder.Create(const AEntityName: IGeneralNames; AVersion: Int32);
+begin
+  inherited Create();
+  FEntityName := AEntityName;
+  FVersion := AVersion;
+end;
+
+constructor THolder.Create(const AObjectDigestInfo: IObjectDigestInfo);
+begin
+  inherited Create();
+  FObjectDigestInfo := AObjectDigestInfo;
+  FVersion := 1;
+end;
+
+function THolder.GetVersion: Int32;
+begin
+  Result := FVersion;
+end;
+
+function THolder.GetBaseCertificateID: IIssuerSerial;
+begin
+  Result := FBaseCertificateID;
+end;
+
+function THolder.GetEntityName: IGeneralNames;
+begin
+  Result := FEntityName;
+end;
+
+function THolder.GetObjectDigestInfo: IObjectDigestInfo;
+begin
+  Result := FObjectDigestInfo;
+end;
+
+function THolder.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  if FVersion = 1 then
+  begin
+    LV := TAsn1EncodableVector.Create(3);
+    LV.AddOptionalTagged(False, 0, FBaseCertificateID);
+    LV.AddOptionalTagged(False, 1, FEntityName);
+    LV.AddOptionalTagged(False, 2, FObjectDigestInfo);
+    Result := TDerSequence.Create(LV);
+  end
+  else
+  begin
+    if FEntityName <> nil then
+      Result := TDerTaggedObject.Create(True, 1, FEntityName)
+    else
+      Result := TDerTaggedObject.Create(True, 0, FBaseCertificateID);
+  end;
+end;
+
+{ TAttributeCertificate }
+
+class function TAttributeCertificate.GetInstance(AObj: TObject): IAttributeCertificate;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAttributeCertificate, Result) then
+    Exit;
+
+  Result := TAttributeCertificate.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAttributeCertificate.GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeCertificate;
+begin
+  Result := TAttributeCertificate.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAttributeCertificate.GetInstance(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributeCertificate;
+begin
+  Result := TAttributeCertificate.Create(TAsn1Sequence.GetInstance(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TAttributeCertificate.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributeCertificate;
+begin
+  Result := TAttributeCertificate.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAttributeCertificate.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  if LCount <> 3 then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FACInfo := TAttributeCertificateInfo.GetInstance(ASeq[0] as TObject);
+  FSignatureAlgorithm := TAlgorithmIdentifier.GetInstance(ASeq[1] as TObject);
+  FSignatureValue := TDerBitString.GetInstance(ASeq[2] as TObject);
+end;
+
+constructor TAttributeCertificate.Create(const AACInfo: IAttributeCertificateInfo;
+  const ASignatureAlgorithm: IAlgorithmIdentifier; const ASignatureValue: IDerBitString);
+begin
+  inherited Create();
+  if AACInfo = nil then
+    raise EArgumentNilCryptoLibException.Create('acinfo');
+  if ASignatureAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create('signatureAlgorithm');
+  if ASignatureValue = nil then
+    raise EArgumentNilCryptoLibException.Create('signatureValue');
+  FACInfo := AACInfo;
+  FSignatureAlgorithm := ASignatureAlgorithm;
+  FSignatureValue := ASignatureValue;
+end;
+
+function TAttributeCertificate.GetACInfo: IAttributeCertificateInfo;
+begin
+  Result := FACInfo;
+end;
+
+function TAttributeCertificate.GetSignatureAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FSignatureAlgorithm;
+end;
+
+function TAttributeCertificate.GetSignatureValue: IDerBitString;
+begin
+  Result := FSignatureValue;
+end;
+
+function TAttributeCertificate.GetSignatureOctets: TCryptoLibByteArray;
+begin
+  Result := FSignatureValue.GetOctets();
+end;
+
+function TAttributeCertificate.ToAsn1Object: IAsn1Object;
+begin
+  Result := TDerSequence.Create([FACInfo, FSignatureAlgorithm, FSignatureValue]);
+end;
+
+{ TAttributeCertificateInfo }
+
+class function TAttributeCertificateInfo.GetInstance(AObj: TObject): IAttributeCertificateInfo;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IAttributeCertificateInfo, Result) then
+    Exit;
+
+  Result := TAttributeCertificateInfo.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TAttributeCertificateInfo.GetInstance(const AEncoded: TCryptoLibByteArray): IAttributeCertificateInfo;
+begin
+  Result := TAttributeCertificateInfo.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TAttributeCertificateInfo.GetInstance(const AObj: IAsn1TaggedObject;
+  AIsExplicit: Boolean): IAttributeCertificateInfo;
+begin
+  Result := TAttributeCertificateInfo.Create(TAsn1Sequence.GetInstance(AObj, AIsExplicit));
+end;
+
+class function TAttributeCertificateInfo.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IAttributeCertificateInfo;
+begin
+  Result := TAttributeCertificateInfo.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+constructor TAttributeCertificateInfo.Create(const ASeq: IAsn1Sequence);
+var
+  LCount, LPos: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  LPos := 0;
+  if (LCount < 6) or (LCount > 9) then
+    raise EArgumentCryptoLibException.CreateFmt(SBadSequenceSize, [LCount]);
+
+  FVersion := TAsn1Utilities.ReadOptional<IDerInteger>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerInteger
+    begin
+      Result := TDerInteger.GetOptional(AElement);
+    end);
+  if FVersion = nil then
+    FVersion := TDerInteger.Zero;
+  FHolder := THolder.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FIssuer := TAttCertIssuer.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FSignature := TAlgorithmIdentifier.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FSerialNumber := TDerInteger.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FAttrCertValidityPeriod := TAttCertValidityPeriod.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FAttributes := TAsn1Sequence.GetInstance(ASeq[LPos] as TObject);
+  System.Inc(LPos);
+  FIssuerUniqueID := TAsn1Utilities.ReadOptional<IDerBitString>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IDerBitString
+    begin
+      Result := TDerBitString.GetOptional(AElement);
+    end);
+  FExtensions := TAsn1Utilities.ReadOptional<IX509Extensions>(ASeq, LPos,
+    function(AElement: IAsn1Encodable): IX509Extensions
+    begin
+      Result := TX509Extensions.GetOptional(AElement);
+    end);
+
+  if LPos <> LCount then
+    raise EArgumentCryptoLibException.Create(SUnexpectedElementsInSequence);
+end;
+
+function TAttributeCertificateInfo.GetVersion: IDerInteger;
+begin
+  Result := FVersion;
+end;
+
+function TAttributeCertificateInfo.GetHolder: IHolder;
+begin
+  Result := FHolder;
+end;
+
+function TAttributeCertificateInfo.GetIssuer: IAttCertIssuer;
+begin
+  Result := FIssuer;
+end;
+
+function TAttributeCertificateInfo.GetSignature: IAlgorithmIdentifier;
+begin
+  Result := FSignature;
+end;
+
+function TAttributeCertificateInfo.GetSerialNumber: IDerInteger;
+begin
+  Result := FSerialNumber;
+end;
+
+function TAttributeCertificateInfo.GetAttrCertValidityPeriod: IAttCertValidityPeriod;
+begin
+  Result := FAttrCertValidityPeriod;
+end;
+
+function TAttributeCertificateInfo.GetAttributes: IAsn1Sequence;
+begin
+  Result := FAttributes;
+end;
+
+function TAttributeCertificateInfo.GetIssuerUniqueID: IDerBitString;
+begin
+  Result := FIssuerUniqueID;
+end;
+
+function TAttributeCertificateInfo.GetExtensions: IX509Extensions;
+begin
+  Result := FExtensions;
+end;
+
+function TAttributeCertificateInfo.ToAsn1Object: IAsn1Object;
+var
+  LV: IAsn1EncodableVector;
+begin
+  LV := TAsn1EncodableVector.Create(9);
+  if not FVersion.HasValue(0) then
+  begin
+    LV.Add(FVersion);
+  end;
+  LV.Add([FHolder, FIssuer, FSignature, FSerialNumber, FAttrCertValidityPeriod, FAttributes]);
+  LV.AddOptional(FIssuerUniqueID, FExtensions);
+  Result := TDerSequence.Create(LV);
+end;
+
+{ TCrlDistPoint }
+
+class function TCrlDistPoint.GetInstance(AObj: TObject): ICrlDistPoint;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, ICrlDistPoint, Result) then
+    Exit;
+
+  Result := TCrlDistPoint.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TCrlDistPoint.GetInstance(const AEncoded: TCryptoLibByteArray): ICrlDistPoint;
+begin
+  Result := TCrlDistPoint.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TCrlDistPoint.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): ICrlDistPoint;
+begin
+  Result := TCrlDistPoint.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TCrlDistPoint.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): ICrlDistPoint;
+begin
+  Result := TCrlDistPoint.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+class function TCrlDistPoint.FromExtensions(const AExtensions: IX509Extensions): ICrlDistPoint;
+begin
+  Result := GetInstance(TX509Extensions.GetExtensionParsedValue(AExtensions, TX509Extensions.CrlDistributionPoints) as TObject);
+end;
+
+constructor TCrlDistPoint.Create(const ASeq: IAsn1Sequence);
+begin
+  inherited Create();
+  FSeq := ASeq;
+end;
+
+constructor TCrlDistPoint.Create(const APoints: TCryptoLibGenericArray<IDistributionPoint>);
+var
+  LV: IAsn1EncodableVector;
+  I: Int32;
+begin
+  inherited Create();
+  LV := TAsn1EncodableVector.Create();
+  for I := 0 to System.Length(APoints) - 1 do
+  begin
+    LV.Add(APoints[I]);
+  end;
+  FSeq := TDerSequence.Create(LV);
+end;
+
+function TCrlDistPoint.GetDistributionPoints: TCryptoLibGenericArray<IDistributionPoint>;
+var
+  LElements: TCryptoLibGenericArray<IAsn1Encodable>;
+  LResult: TList<IDistributionPoint>;
+  I: Int32;
+begin
+  LElements := FSeq.GetElements();
+  LResult := TList<IDistributionPoint>.Create();
+  try
+    for I := 0 to System.Length(LElements) - 1 do
+    begin
+      LResult.Add(TDistributionPoint.GetInstance(LElements[I] as TObject));
+    end;
+    Result := LResult.ToArray();
+  finally
+    LResult.Free;
+  end;
+end;
+
+function TCrlDistPoint.ToAsn1Object: IAsn1Object;
+begin
+  Result := FSeq;
+end;
+
+function TCrlDistPoint.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LDps: TCryptoLibGenericArray<IDistributionPoint>;
+  I: Int32;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.AppendLine('CRLDistPoint:');
+    LDps := GetDistributionPoints();
+    for I := 0 to System.Length(LDps) - 1 do
+    begin
+      LBuf.Append('    ').Append((LDps[I] as TDistributionPoint).ToString()).AppendLine();
+    end;
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+end.

+ 936 - 0
CryptoLib/src/Asn1/X509/ClpX509Certificate.pas

@@ -0,0 +1,936 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509Certificate;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Rtti,
+  Math,
+  Generics.Collections,
+  DateUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpX509ExtensionBase,
+  ClpIX509Certificate,
+  ClpAsn1Dumper,
+  ClpX509ExtensionUtilities,
+  ClpX509SignatureUtilities,
+  ClpX509Utilities,
+  ClpIAsymmetricKeyParameter,
+  ClpIVerifierFactory,
+  ClpIVerifier,
+  ClpIStreamCalculator,
+  ClpIVerifierFactoryProvider,
+  ClpAsn1VerifierFactory,
+  ClpAsn1VerifierFactoryProvider,
+  ClpPublicKeyFactory,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpArrayUtils,
+  ClpEncoders,
+  ClpIPAddressUtilities;
+
+type
+  /// <summary>
+  /// An Object representing an X509 Certificate.
+  /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects.
+  /// </summary>
+  TX509Certificate = class(TX509ExtensionBase, IX509Certificate)
+
+  strict private
+  type
+    ICachedEncoding = interface(IInterface)
+      ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
+      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
+      FCertificateStructure: IX509CertificateStructure;
+      FSigAlgParams: TCryptoLibByteArray;
+      FBasicConstraints: IBasicConstraints;
+      FKeyUsage: TCryptoLibBooleanArray;
+      FSigAlgName: String;
+      FPublicKeyValue: IAsymmetricKeyParameter;
+      FCachedEncoding: ICachedEncoding;
+      FHashValueSet: Boolean;
+      FHashValue: Int32;
+
+    function GetCachedEncoding: ICachedEncoding;
+    function CreateCachedEncoding(const ACert: IX509CertificateStructure): ICachedEncoding;
+    function CreatePublicKey(const ACert: IX509CertificateStructure): IAsymmetricKeyParameter;
+    function IPAddressToString(const AAddrBytes: TCryptoLibByteArray): String;
+
+  strict protected
+    function GetX509Extensions: IX509Extensions; override;
+    function GetAlternativeNameExtension(const AOid: IDerObjectIdentifier): IGeneralNames; virtual;
+    function GetAlternativeNames(const AOid: IDerObjectIdentifier): TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>; virtual;
+    procedure CheckSignature(const AVerifier: IVerifierFactory); virtual;
+    function CheckSignatureValid(const AVerifier: IVerifierFactory): Boolean; virtual;
+
+  public
+    constructor Create(const ACertData: TCryptoLibByteArray); overload;
+    constructor Create(const ACertificate: IX509CertificateStructure); overload;
+
+    function GetCertificateStructure: IX509CertificateStructure;
+    function IsValidNow: Boolean;
+    function IsValid(const ATime: TDateTime): Boolean;
+    procedure CheckValidity(); overload;
+    procedure CheckValidity(const ATime: TDateTime); overload;
+
+    function GetVersion: Int32;
+    function GetSerialNumber: TBigInteger;
+    function GetIssuerDN: IX509Name;
+    function GetSubjectDN: IX509Name;
+    function GetNotBefore: TDateTime;
+    function GetNotAfter: TDateTime;
+    function GetTbsCertificate: ITbsCertificateStructure;
+    function GetTbsCertificateEncoded: TCryptoLibByteArray;
+    function GetSignature: TCryptoLibByteArray;
+    function GetSigAlgName: String;
+    function GetSigAlgOid: String;
+    function GetSigAlgParams: TCryptoLibByteArray;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetIsCritical: Boolean;
+    function GetValue: IAsn1OctetString;
+    function GetParsedValue: IAsn1Object;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetKeyUsage: TCryptoLibBooleanArray;
+    function GetExtendedKeyUsage: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetBasicConstraints: Int32;
+    function GetIssuerAlternativeNameExtension: IGeneralNames;
+    function GetSubjectAlternativeNameExtension: IGeneralNames;
+    function GetIssuerAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+    function GetSubjectAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetPublicKey: IAsymmetricKeyParameter;
+    function GetEncoded: TCryptoLibByteArray;
+
+    function Equals(AObj: TObject): Boolean; reintroduce;
+    function GetHashCode: Int32; override;
+    function ToString: String; override;
+
+    function IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+    function IsAlternativeSignatureValid(const APublicKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+
+    procedure Verify(const AKey: IAsymmetricKeyParameter); overload;
+    procedure Verify(const AVerifierProvider: IVerifierFactoryProvider); overload;
+    procedure VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+
+    class function AreEquivalentAlgorithms(const AId1, AId2: IAlgorithmIdentifier): Boolean; static;
+    class function VerifySignature(const AVerifierFactory: IVerifierFactory;
+      const AAsn1Encodable: IAsn1Encodable; const ASignature: IDerBitString): Boolean; static;
+
+    property CertificateStructure: IX509CertificateStructure read GetCertificateStructure;
+    property Version: Int32 read GetVersion;
+    property SerialNumber: TBigInteger read GetSerialNumber;
+    property IssuerDN: IX509Name read GetIssuerDN;
+    property SubjectDN: IX509Name read GetSubjectDN;
+    property NotBefore: TDateTime read GetNotBefore;
+    property NotAfter: TDateTime read GetNotAfter;
+    property TbsCertificate: ITbsCertificateStructure read GetTbsCertificate;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property SigAlgName: String read GetSigAlgName;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+
+  end;
+
+implementation
+
+uses
+  ClpRfc5280Asn1Utilities,
+  ClpIX509Extension;
+
+{ TX509Certificate.TCachedEncoding }
+
+constructor TX509Certificate.TCachedEncoding.Create(const AEncoding: TCryptoLibByteArray; const AException: Exception);
+begin
+  inherited Create();
+  FEncoding := AEncoding;
+  FException := AException;
+end;
+
+function TX509Certificate.TCachedEncoding.GetEncoding: TCryptoLibByteArray;
+begin
+  Result := FEncoding;
+end;
+
+function TX509Certificate.TCachedEncoding.GetEncoded: TCryptoLibByteArray;
+begin
+  if FException <> nil then
+    raise FException;
+  if FEncoding = nil then
+    raise EIOCryptoLibException.Create('Failed to encode certificate');
+  Result := FEncoding;
+end;
+
+{ TX509Certificate }
+
+constructor TX509Certificate.Create(const ACertData: TCryptoLibByteArray);
+begin
+  Create(TX509CertificateStructure.GetInstance(ACertData));
+end;
+
+constructor TX509Certificate.Create(const ACertificate: IX509CertificateStructure);
+var
+  LParameters: IAsn1Encodable;
+  LKeyUsageBits: IDerBitString;
+  LKeyUsageBytes: TCryptoLibByteArray;
+  LLength, I: Int32;
+begin
+  inherited Create();
+  if ACertificate = nil then
+    raise EArgumentNilCryptoLibException.Create('certificate');
+
+  FCertificateStructure := ACertificate;
+
+  try
+    LParameters := ACertificate.SignatureAlgorithm.Parameters;
+    if LParameters <> nil then
+      FSigAlgParams := LParameters.GetEncoded(TAsn1Encodable.Der)
+    else
+      FSigAlgParams := nil;
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.Create('Certificate contents invalid: ' + E.Message);
+  end;
+
+  try
+    FBasicConstraints := TX509ExtensionUtilities.GetExtension<IBasicConstraints>(ACertificate.Extensions,
+      TX509Extensions.BasicConstraints,
+      function(AOctets: TCryptoLibByteArray): IBasicConstraints
+      begin
+        Result := TBasicConstraints.GetInstance(AOctets);
+      end
+    );
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.Create('cannot construct BasicConstraints: ' + E.Message);
+  end;
+
+  try
+    LKeyUsageBits := TX509ExtensionUtilities.GetExtension<IDerBitString>(ACertificate.Extensions,
+      TX509Extensions.KeyUsage,
+      function(AOctets: TCryptoLibByteArray): IDerBitString
+      begin
+        Result := TDerBitString.GetInstance(AOctets);
+      end);
+    if LKeyUsageBits <> nil then
+    begin
+      LKeyUsageBytes := LKeyUsageBits.GetBytes();
+      LLength := (System.Length(LKeyUsageBytes) * 8) - LKeyUsageBits.PadBits;
+      if LLength < 9 then
+        LLength := 9;
+      System.SetLength(FKeyUsage, LLength);
+      for I := 0 to LLength - 1 do
+      begin
+        FKeyUsage[I] := (LKeyUsageBytes[I div 8] and ($80 shr (I mod 8))) <> 0;
+      end;
+    end
+    else
+    begin
+      FKeyUsage := nil;
+    end;
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.Create('cannot construct KeyUsage: ' + E.Message);
+  end;
+end;
+
+function TX509Certificate.GetX509Extensions: IX509Extensions;
+begin
+  if FCertificateStructure.Version >= 3 then
+    Result := FCertificateStructure.Extensions
+  else
+    Result := nil;
+end;
+
+function TX509Certificate.GetCertificateStructure: IX509CertificateStructure;
+begin
+  Result := FCertificateStructure;
+end;
+
+function TX509Certificate.IsValidNow: Boolean;
+begin
+  Result := IsValid(Now);
+end;
+
+function TX509Certificate.IsValid(const ATime: TDateTime): Boolean;
+begin
+  Result := (CompareDateTime(ATime, NotBefore) >= 0) and (CompareDateTime(ATime, NotAfter) <= 0);
+end;
+
+procedure TX509Certificate.CheckValidity();
+begin
+  CheckValidity(Now);
+end;
+
+procedure TX509Certificate.CheckValidity(const ATime: TDateTime);
+begin
+  if CompareDateTime(ATime, NotAfter) > 0 then
+    raise EArgumentCryptoLibException.CreateFmt('certificate expired on %s', [DateTimeToStr(FCertificateStructure.EndDate.ToDateTime())]);
+  if CompareDateTime(ATime, NotBefore) < 0 then
+    raise EArgumentCryptoLibException.CreateFmt('certificate not valid until %s', [DateTimeToStr(FCertificateStructure.StartDate.ToDateTime())]);
+end;
+
+function TX509Certificate.GetVersion: Int32;
+begin
+  Result := FCertificateStructure.Version;
+end;
+
+function TX509Certificate.GetSerialNumber: TBigInteger;
+begin
+  Result := FCertificateStructure.SerialNumber.Value;
+end;
+
+function TX509Certificate.GetIssuerDN: IX509Name;
+begin
+  Result := FCertificateStructure.Issuer;
+end;
+
+function TX509Certificate.GetSubjectDN: IX509Name;
+begin
+  Result := FCertificateStructure.Subject;
+end;
+
+function TX509Certificate.GetNotBefore: TDateTime;
+begin
+  Result := FCertificateStructure.StartDate.ToDateTime();
+end;
+
+function TX509Certificate.GetNotAfter: TDateTime;
+begin
+  Result := FCertificateStructure.EndDate.ToDateTime();
+end;
+
+function TX509Certificate.GetTbsCertificate: ITbsCertificateStructure;
+begin
+  Result := FCertificateStructure.TbsCertificate;
+end;
+
+function TX509Certificate.GetTbsCertificateEncoded: TCryptoLibByteArray;
+begin
+  Result := FCertificateStructure.TbsCertificate.GetDerEncoded();
+end;
+
+function TX509Certificate.GetSignature: TCryptoLibByteArray;
+begin
+  Result := FCertificateStructure.GetSignatureOctets();
+end;
+
+function TX509Certificate.GetSigAlgName: String;
+begin
+  if FSigAlgName = '' then
+  begin
+    FSigAlgName := TX509SignatureUtilities.GetSignatureName(SignatureAlgorithm);
+  end;
+  Result := FSigAlgName;
+end;
+
+function TX509Certificate.GetIsCritical: Boolean;
+begin
+  raise ENotSupportedCryptoLibException.Create('GetIsCritical not applicable to X509Certificate');
+end;
+
+function TX509Certificate.GetValue: IAsn1OctetString;
+begin
+  raise ENotSupportedCryptoLibException.Create('GetValue not applicable to X509Certificate');
+end;
+
+function TX509Certificate.GetParsedValue: IAsn1Object;
+begin
+  raise ENotSupportedCryptoLibException.Create('GetParsedValue not applicable to X509Certificate');
+end;
+
+function TX509Certificate.GetSigAlgOid: String;
+begin
+  Result := FCertificateStructure.SignatureAlgorithm.Algorithm.Id;
+end;
+
+function TX509Certificate.GetSigAlgParams: TCryptoLibByteArray;
+begin
+  Result := TArrayUtils.Clone(FSigAlgParams);
+end;
+
+function TX509Certificate.GetSignatureAlgorithm: IAlgorithmIdentifier;
+begin
+  Result := FCertificateStructure.SignatureAlgorithm;
+end;
+
+function TX509Certificate.GetIssuerUniqueID: IDerBitString;
+begin
+  Result := FCertificateStructure.IssuerUniqueID;
+end;
+
+function TX509Certificate.GetSubjectUniqueID: IDerBitString;
+begin
+  Result := FCertificateStructure.SubjectUniqueID;
+end;
+
+function TX509Certificate.GetKeyUsage: TCryptoLibBooleanArray;
+begin
+  Result := TArrayUtils.Clone<Boolean>(FKeyUsage);
+end;
+
+function TX509Certificate.GetExtendedKeyUsage: TCryptoLibGenericArray<IDerObjectIdentifier>;
+var
+  LSeq: IAsn1Sequence;
+  LResult: TList<IDerObjectIdentifier>;
+  I: Int32;
+begin
+  try
+    LSeq := TX509ExtensionUtilities.GetExtension<IAsn1Sequence>(GetX509Extensions(),
+      TX509Extensions.ExtendedKeyUsage,
+      function(AOctets: TCryptoLibByteArray): IAsn1Sequence
+      begin
+        Result := TAsn1Sequence.GetInstance(AOctets);
+      end);
+    if LSeq = nil then
+    begin
+      Result := nil;
+      Exit;
+    end;
+
+    LResult := TList<IDerObjectIdentifier>.Create();
+    try
+      for I := 0 to LSeq.Count - 1 do
+      begin
+        LResult.Add(TDerObjectIdentifier.GetInstance(LSeq[I] as TAsn1Encodable));
+      end;
+      Result := LResult.ToArray();
+    finally
+      LResult.Free;
+    end;
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.Create('error processing extended key usage extension: ' + E.Message);
+  end;
+end;
+
+function TX509Certificate.GetBasicConstraints: Int32;
+begin
+  if (FBasicConstraints = nil) or (not FBasicConstraints.IsCA()) then
+  begin
+    Result := -1;
+    Exit;
+  end;
+
+  if FBasicConstraints.PathLenConstraint.IsInitialized then
+    Result := FBasicConstraints.PathLenConstraint.Int32ValueExact
+  else
+    Result := MaxInt;
+end;
+
+function TX509Certificate.GetIssuerAlternativeNameExtension: IGeneralNames;
+begin
+  Result := GetAlternativeNameExtension(TX509Extensions.IssuerAlternativeName);
+end;
+
+function TX509Certificate.GetSubjectAlternativeNameExtension: IGeneralNames;
+begin
+  Result := GetAlternativeNameExtension(TX509Extensions.SubjectAlternativeName);
+end;
+
+function TX509Certificate.GetIssuerAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+begin
+  Result := GetAlternativeNames(TX509Extensions.IssuerAlternativeName);
+end;
+
+function TX509Certificate.GetSubjectAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+begin
+  Result := GetAlternativeNames(TX509Extensions.SubjectAlternativeName);
+end;
+
+function TX509Certificate.GetAlternativeNameExtension(const AOid: IDerObjectIdentifier): IGeneralNames;
+begin
+  Result := TX509ExtensionUtilities.GetExtension<IGeneralNames>(GetX509Extensions(), AOid,
+    function(AOctets: TCryptoLibByteArray): IGeneralNames
+    begin
+      Result := TGeneralNames.GetInstance(TAsn1Object.FromByteArray(AOctets) as TObject);
+    end);
+end;
+
+function TX509Certificate.GetAlternativeNames(const AOid: IDerObjectIdentifier): TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+var
+  LGeneralNames: IGeneralNames;
+  LGns: TCryptoLibGenericArray<IGeneralName>;
+  LResult: TList<TCryptoLibGenericArray<TValue>>;
+  LEntry: TList<TValue>;
+  LGn: IGeneralName;
+  I: Int32;
+  LName: IAsn1Encodable;
+  LAsn1String: IAsn1String;
+  LNameObj: IX509Name;
+  LOid: IDerObjectIdentifier;
+  LOctetString: IAsn1OctetString;
+  LIPAddr: String;
+begin
+  LGeneralNames := GetAlternativeNameExtension(AOid);
+  if LGeneralNames = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LGns := LGeneralNames.GetNames();
+  LResult := TList<TCryptoLibGenericArray<TValue>>.Create();
+  try
+    for I := 0 to System.Length(LGns) - 1 do
+    begin
+      LGn := LGns[I];
+      LEntry := TList<TValue>.Create();
+      try
+        LEntry.Add(TValue.From<Int32>(LGn.TagNo));
+
+        case LGn.TagNo of
+          TGeneralName.EdiPartyName,
+          TGeneralName.X400Address,
+          TGeneralName.OtherName:
+            begin
+              LEntry.Add(TValue.From<TCryptoLibByteArray>(LGn.GetEncoded()));
+            end;
+          TGeneralName.DirectoryName:
+            begin
+              LName := LGn.Name;
+              if Supports(LName, IX509Name, LNameObj) then
+                LEntry.Add(TValue.From<String>(LNameObj.ToString()))
+              else
+                LEntry.Add(TValue.From<String>(TX509Name.GetInstance(LName as TAsn1Object).ToString()));
+            end;
+          TGeneralName.DnsName,
+          TGeneralName.Rfc822Name,
+          TGeneralName.UniformResourceIdentifier:
+            begin
+              if Supports(LGn.Name, IAsn1String, LAsn1String) then
+                LEntry.Add(TValue.From<String>(LAsn1String.GetString()));
+            end;
+          TGeneralName.RegisteredID:
+            begin
+              LOid := TDerObjectIdentifier.GetInstance(LGn.Name as TObject);
+              LEntry.Add(TValue.From<String>(LOid.Id));
+            end;
+          TGeneralName.IPAddress:
+            begin
+              LOctetString := TAsn1OctetString.GetInstance(LGn.Name as TObject);
+              LIPAddr := IPAddressToString(LOctetString.GetOctets());
+              LEntry.Add(TValue.From<String>(LIPAddr));
+            end;
+        else
+          raise EIOCryptoLibException.CreateFmt('Bad tag number: %d', [LGn.TagNo]);
+        end;
+
+        LResult.Add(LEntry.ToArray());
+      finally
+        LEntry.Free;
+      end;
+    end;
+    Result := LResult.ToArray();
+  finally
+    LResult.Free;
+  end;
+end;
+
+function TX509Certificate.IPAddressToString(const AAddrBytes: TCryptoLibByteArray): String;
+var
+  I: Int32;
+  LAddr: String;
+  LWord: Word;
+begin
+  if System.Length(AAddrBytes) = 4 then
+  begin
+    // IPv4
+    LAddr := IntToStr(AAddrBytes[0] and $FF);
+    for I := 1 to 3 do
+    begin
+      LAddr := LAddr + '.' + IntToStr(AAddrBytes[I] and $FF);
+    end;
+    Result := LAddr;
+  end
+  else if System.Length(AAddrBytes) = 16 then
+  begin
+    // IPv6 - format as 8 groups of 4 hex digits
+    LWord := (AAddrBytes[0] shl 8) or AAddrBytes[1];
+    LAddr := IntToHex(LWord, 4);
+    for I := 1 to 7 do
+    begin
+      LWord := (AAddrBytes[I * 2] shl 8) or AAddrBytes[I * 2 + 1];
+      LAddr := LAddr + ':' + IntToHex(LWord, 4);
+    end;
+    Result := LAddr;
+  end
+  else
+  begin
+    // Unknown format, return hex
+    Result := THex.Encode(AAddrBytes);
+  end;
+end;
+
+function TX509Certificate.GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+begin
+  Result := FCertificateStructure.SubjectPublicKeyInfo;
+end;
+
+function TX509Certificate.GetPublicKey: IAsymmetricKeyParameter;
+begin
+  // Cache the public key to support repeated-use optimizations
+  if FPublicKeyValue = nil then
+  begin
+    FPublicKeyValue := CreatePublicKey(FCertificateStructure);
+  end;
+  Result := FPublicKeyValue;
+end;
+
+function TX509Certificate.CreatePublicKey(const ACert: IX509CertificateStructure): IAsymmetricKeyParameter;
+begin
+  Result := TPublicKeyFactory.CreateKey(ACert.SubjectPublicKeyInfo);
+end;
+
+function TX509Certificate.GetEncoded: TCryptoLibByteArray;
+begin
+  Result := TArrayUtils.Clone(GetCachedEncoding().GetEncoded());
+end;
+
+function TX509Certificate.GetCachedEncoding: ICachedEncoding;
+begin
+  if FCachedEncoding = nil then
+  begin
+    FCachedEncoding := CreateCachedEncoding(FCertificateStructure);
+  end;
+  Result := FCachedEncoding;
+end;
+
+function TX509Certificate.CreateCachedEncoding(const ACert: IX509CertificateStructure): ICachedEncoding;
+var
+  LEncoding: TCryptoLibByteArray;
+  LException: Exception;
+begin
+  LEncoding := nil;
+  LException := nil;
+  try
+    LEncoding := ACert.GetEncoded(TAsn1Encodable.Der);
+  except
+    on E: Exception do
+    begin
+      LException := EIOCryptoLibException.Create('Failed to DER-encode certificate: ' + E.Message);
+    end;
+  end;
+  Result := TCachedEncoding.Create(LEncoding, LException);
+end;
+
+function TX509Certificate.Equals(AObj: TObject): Boolean;
+var
+  LThatCert: IX509Certificate;
+  LThat: TX509Certificate;
+  LThisEncoding, LThatEncoding: TCryptoLibByteArray;
+  LSignature: IDerBitString;
+begin
+  if Self = AObj then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  if not Supports(AObj, IX509Certificate, LThatCert) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LThat := AObj as TX509Certificate;
+
+  if FHashValueSet and LThat.FHashValueSet then
+  begin
+    if FHashValue <> LThat.FHashValue then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end
+  else if (FCachedEncoding = nil) or (LThat.FCachedEncoding = nil) then
+  begin
+    LSignature := FCertificateStructure.Signature;
+    if (LSignature <> nil) and (not LSignature.Equals(LThat.FCertificateStructure.Signature)) then
+    begin
+      Result := False;
+      Exit;
+    end;
+  end;
+
+  LThisEncoding := GetCachedEncoding().GetEncoding();
+  LThatEncoding := LThat.GetCachedEncoding().GetEncoding();
+
+  if (LThisEncoding <> nil) and (LThatEncoding <> nil) then
+    Result := TArrayUtils.AreEqual(LThisEncoding, LThatEncoding)
+  else
+    Result := False;
+end;
+
+function TX509Certificate.GetHashCode: Int32;
+var
+  LEncoding: TCryptoLibByteArray;
+begin
+  if not FHashValueSet then
+  begin
+    LEncoding := GetCachedEncoding().GetEncoding();
+    FHashValue := TArrayUtils.GetArrayHashCode(LEncoding);
+    FHashValueSet := True;
+  end;
+  Result := FHashValue;
+end;
+
+function TX509Certificate.ToString: String;
+var
+  LBuf: TStringBuilder;
+  LSig: TCryptoLibByteArray;
+  I, LLen: Int32;
+  LExtensions: IX509Extensions;
+  LOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LExt: IX509Extension;
+  LOid: IDerObjectIdentifier;
+  LObj: IAsn1Object;
+begin
+  LBuf := TStringBuilder.Create();
+  try
+    LBuf.Append('  [0]         Version: ').Append(Version).AppendLine();
+    LBuf.Append('         SerialNumber: ').Append(SerialNumber.ToString()).AppendLine();
+    LBuf.Append('             IssuerDN: ').Append(IssuerDN.ToString()).AppendLine();
+    LBuf.Append('           Start Date: ').Append(DateTimeToStr(NotBefore)).AppendLine();
+    LBuf.Append('           Final Date: ').Append(DateTimeToStr(NotAfter)).AppendLine();
+    LBuf.Append('            SubjectDN: ').Append(SubjectDN.ToString()).AppendLine();
+    LBuf.Append('           Public Key: ').Append('[Public Key]').AppendLine();
+    LBuf.Append('  Signature Algorithm: ').Append(GetSigAlgName()).AppendLine();
+
+    LSig := GetSignature();
+    LLen := System.Math.Min(20, System.Length(LSig));
+    LBuf.Append('            Signature: ').AppendLine(THex.Encode(TArrayUtils.CopyOfRange(LSig, 0, LLen)));
+
+    I := 20;
+    while I < System.Length(LSig) do
+    begin
+      LLen := Math.Min(20, System.Length(LSig) - I);
+      LBuf.Append('                       ').AppendLine(THex.Encode(TArrayUtils.CopyOfRange(LSig, I, I + LLen)));
+      System.Inc(I, 20);
+    end;
+
+    LExtensions := FCertificateStructure.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
+            try
+              LObj := TX509ExtensionUtilities.FromExtensionValue(LExt.Value);
+              LBuf.Append('                       critical(').Append(BoolToStr(LExt.IsCritical, True)).Append(') ');
+              try
+                if LOid.Equals(TX509Extensions.BasicConstraints) then
+                begin
+                  LBuf.Append(TBasicConstraints.GetInstance(LObj as TObject).ToString());
+                end
+                else if LOid.Equals(TX509Extensions.KeyUsage) then
+                begin
+                  LBuf.Append(TKeyUsage.GetInstance(LObj).ToString());
+                end
+                else
+                begin
+                  LBuf.Append(LOid.Id);
+                  LBuf.Append(' value = ').Append(TAsn1Dumper.DumpAsString(LObj));
+                end;
+              except
+                on E: Exception do
+                begin
+                  LBuf.Append(LOid.Id);
+                  LBuf.Append(' value = *****');
+                end;
+              end;
+            except
+              // Ignore parsing errors
+            end;
+            LBuf.AppendLine();
+          end;
+        end;
+      end;
+    end;
+
+    Result := LBuf.ToString();
+  finally
+    LBuf.Free;
+  end;
+end;
+
+function TX509Certificate.IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean;
+begin
+  Result := CheckSignatureValid(TAsn1VerifierFactory.Create(FCertificateStructure.SignatureAlgorithm, AKey));
+end;
+
+function TX509Certificate.IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+begin
+  Result := CheckSignatureValid(AVerifierProvider.CreateVerifierFactory(FCertificateStructure.SignatureAlgorithm as TObject));
+end;
+
+function TX509Certificate.IsAlternativeSignatureValid(const APublicKey: IAsymmetricKeyParameter): Boolean;
+begin
+  Result := IsAlternativeSignatureValid(TAsn1VerifierFactoryProvider.Create(APublicKey));
+end;
+
+function TX509Certificate.IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean;
+var
+  LTbsCertificate: ITbsCertificateStructure;
+  LExtensions: IX509Extensions;
+  LAltSigAlg: IAltSignatureAlgorithm;
+  LAltSigValue: IAltSignatureValue;
+  LVerifier: IVerifierFactory;
+  LTbsSeq: IAsn1Sequence;
+  LV: IAsn1EncodableVector;
+  I: Int32;
+  LTagged: IDerTaggedObject;
+begin
+  LTbsCertificate := FCertificateStructure.TbsCertificate;
+  LExtensions := LTbsCertificate.Extensions;
+
+  LAltSigAlg := TAltSignatureAlgorithm.FromExtensions(LExtensions);
+  LAltSigValue := TAltSignatureValue.FromExtensions(LExtensions);
+
+  LVerifier := AVerifierProvider.CreateVerifierFactory(LAltSigAlg.Algorithm as TObject);
+
+  LTbsSeq := TAsn1Sequence.GetInstance(LTbsCertificate.ToAsn1Object());
+  LV := TAsn1EncodableVector.Create();
+
+  for I := 0 to LTbsSeq.Count - 2 do
+  begin
+    if I <> 2 then // signature field - must be ver 3 so version always present
+    begin
+      LV.Add(LTbsSeq[I]);
+    end;
+  end;
+
+    LTagged := TDerTaggedObject.Create(True, 3, LExtensions.ToAsn1ObjectTrimmed());
+  LV.Add(LTagged);
+
+  Result := TX509Utilities.VerifySignature(LVerifier, TDerSequence.Create(LV), LAltSigValue.Signature);
+end;
+
+procedure TX509Certificate.Verify(const AKey: IAsymmetricKeyParameter);
+begin
+  CheckSignature(TAsn1VerifierFactory.Create(FCertificateStructure.SignatureAlgorithm, AKey));
+end;
+
+procedure TX509Certificate.Verify(const AVerifierProvider: IVerifierFactoryProvider);
+begin
+  CheckSignature(AVerifierProvider.CreateVerifierFactory(FCertificateStructure.SignatureAlgorithm as TObject));
+end;
+
+procedure TX509Certificate.VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+begin
+  if not IsAlternativeSignatureValid(AVerifierProvider) then
+    raise EInvalidKeyCryptoLibException.Create('Public key presented not for certificate alternative signature');
+end;
+
+procedure TX509Certificate.CheckSignature(const AVerifier: IVerifierFactory);
+begin
+  if not CheckSignatureValid(AVerifier) then
+    raise EInvalidKeyCryptoLibException.Create('Public key presented not for certificate signature');
+end;
+
+function TX509Certificate.CheckSignatureValid(const AVerifier: IVerifierFactory): Boolean;
+var
+  LTbsCertificate: ITbsCertificateStructure;
+begin
+  LTbsCertificate := FCertificateStructure.TbsCertificate;
+
+  if not TX509Certificate.AreEquivalentAlgorithms(FCertificateStructure.SignatureAlgorithm, LTbsCertificate.Signature) then
+    raise EArgumentCryptoLibException.Create('signature algorithm in TBS cert not same as outer cert');
+
+  Result := TX509Certificate.VerifySignature(AVerifier, LTbsCertificate, FCertificateStructure.Signature);
+end;
+
+class function TX509Certificate.AreEquivalentAlgorithms(const AId1, AId2: IAlgorithmIdentifier): Boolean;
+var
+  LParams1, LParams2: IAsn1Encodable;
+begin
+  if not AId1.Algorithm.Equals(AId2.Algorithm) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // Check if both have absent parameters (null or DerNull)
+  LParams1 := AId1.Parameters;
+  LParams2 := AId2.Parameters;
+  if ((LParams1 = nil) or TDerNull.Instance.Equals(LParams1)) and
+     ((LParams2 = nil) or TDerNull.Instance.Equals(LParams2)) then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  // Compare parameters
+  if LParams1 = nil then
+    Result := (LParams2 = nil) or TDerNull.Instance.Equals(LParams2)
+  else if LParams2 = nil then
+    Result := TDerNull.Instance.Equals(LParams1)
+  else
+    Result := LParams1.Equals(LParams2);
+end;
+
+class function TX509Certificate.VerifySignature(const AVerifierFactory: IVerifierFactory;
+  const AAsn1Encodable: IAsn1Encodable; const ASignature: IDerBitString): Boolean;
+var
+  LCalculator: IStreamCalculator<IVerifier>;
+  LStream: TStream;
+  LResult: IVerifier;
+begin
+  LCalculator := AVerifierFactory.CreateCalculator();
+  LStream := LCalculator.Stream;
+  try
+    AAsn1Encodable.EncodeTo(LStream, TAsn1Encodable.Der);
+  finally
+    LStream.Free;
+  end;
+  LResult := LCalculator.GetResult();
+  Result := LResult.IsVerified(ASignature.GetOctets());
+end;
+
+end.

+ 103 - 0
CryptoLib/src/Asn1/X509/ClpX509DefaultEntryConverter.pas

@@ -0,0 +1,103 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509DefaultEntryConverter;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509NameEntryConverter,
+  ClpX509NameEntryConverter,
+  ClpX509Asn1Objects,
+  ClpX509ObjectIdentifiers,
+  ClpPkcsObjectIdentifiers,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// The default converter for X509 DN entries when going from their string value to ASN.1 strings.
+  /// </summary>
+  TX509DefaultEntryConverter = class(TX509NameEntryConverter, IX509NameEntryConverter)
+
+  public
+    /// <summary>
+    /// Apply default conversion for the given value depending on the oid and the character range of the value.
+    /// </summary>
+    function GetConvertedValue(const AOid: IDerObjectIdentifier;
+      const AValue: String): IAsn1Object; override;
+
+  end;
+
+implementation
+
+{ TX509DefaultEntryConverter }
+
+function TX509DefaultEntryConverter.GetConvertedValue(const AOid: IDerObjectIdentifier;
+  const AValue: String): IAsn1Object;
+var
+  LValue: String;
+begin
+  if (System.Length(AValue) <> 0) and (AValue[1] = '#') then
+  begin
+    try
+      Result := ConvertHexEncoded(AValue, 1);
+      Exit;
+    except
+      on E: Exception do
+        raise ECryptoLibException.CreateFmt('can''t recode value for oid %s', [AOid.Id]);
+    end;
+  end;
+
+  LValue := AValue;
+  if (System.Length(LValue) <> 0) and (LValue[1] = '\') then
+  begin
+    LValue := System.Copy(LValue, 2, System.Length(LValue) - 1);
+  end;
+
+  // EmailAddress and DC
+  if AOid.Equals(TX509Name.EmailAddress) or AOid.Equals(TX509Name.DC) then
+  begin
+    Result := TDerIA5String.Create(LValue);
+    Exit;
+  end;
+
+  // DateOfBirth: accept time string as well as # (for compatibility)
+  if AOid.Equals(TX509Name.DateOfBirth) then
+  begin
+    Result := TAsn1GeneralizedTime.Create(LValue);
+    Exit;
+  end;
+
+  // C, SerialNumber, DnQualifier, TelephoneNumber
+  if AOid.Equals(TX509Name.C) or
+    AOid.Equals(TX509Name.SerialNumber) or
+    AOid.Equals(TX509Name.DnQualifier) or
+    AOid.Equals(TX509Name.TelephoneNumber) then
+  begin
+    Result := TDerPrintableString.Create(LValue);
+    Exit;
+  end;
+
+  Result := TDerUtf8String.Create(LValue);
+end;
+
+end.

+ 137 - 0
CryptoLib/src/Asn1/X509/ClpX509Extension.pas

@@ -0,0 +1,137 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509Extension;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Extension,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// An object for the elements in the X.509 V3 extension block.
+  /// Note: This is NOT an ASN1Encodable, it's a helper class.
+  /// </summary>
+  TX509Extension = class(TInterfacedObject, IX509Extension)
+
+  strict private
+  var
+    FCritical: Boolean;
+    FValue: IAsn1OctetString;
+
+  strict protected
+    function GetIsCritical: Boolean;
+    function GetValue: IAsn1OctetString;
+    function GetParsedValue: IAsn1Object;
+
+  public
+    /// <summary>
+    /// Convert the value of the passed in extension to an object.
+    /// </summary>
+    class function ConvertValueToObject(const AExt: IX509Extension): IAsn1Object; static;
+
+    constructor Create(const ACritical: IDerBoolean; const AValue: IAsn1OctetString); overload;
+    constructor Create(ACritical: Boolean; const AValue: IAsn1OctetString); overload;
+
+    function GetHashCode: Int32;
+    function Equals(AObj: TObject): Boolean; reintroduce;
+
+    property IsCritical: Boolean read GetIsCritical;
+    property Value: IAsn1OctetString read GetValue;
+
+  end;
+
+implementation
+
+{ TX509Extension }
+
+class function TX509Extension.ConvertValueToObject(const AExt: IX509Extension): IAsn1Object;
+begin
+  try
+    Result := TAsn1Object.FromByteArray(AExt.Value.GetOctets());
+  except
+    on E: Exception do
+      raise EArgumentCryptoLibException.Create('can''t convert extension: ' + E.Message);
+  end;
+end;
+
+constructor TX509Extension.Create(const ACritical: IDerBoolean; const AValue: IAsn1OctetString);
+begin
+  inherited Create();
+  if ACritical = nil then
+    raise EArgumentNilCryptoLibException.Create('critical');
+  if AValue = nil then
+    raise EArgumentNilCryptoLibException.Create('value');
+  FCritical := ACritical.IsTrue;
+  FValue := AValue;
+end;
+
+constructor TX509Extension.Create(ACritical: Boolean; const AValue: IAsn1OctetString);
+begin
+  inherited Create();
+  if AValue = nil then
+    raise EArgumentNilCryptoLibException.Create('value');
+  FCritical := ACritical;
+  FValue := AValue;
+end;
+
+function TX509Extension.GetIsCritical: Boolean;
+begin
+  Result := FCritical;
+end;
+
+function TX509Extension.GetValue: IAsn1OctetString;
+begin
+  Result := FValue;
+end;
+
+function TX509Extension.GetParsedValue: IAsn1Object;
+begin
+  Result := ConvertValueToObject(Self);
+end;
+
+function TX509Extension.GetHashCode: Int32;
+var
+  LVH: Int32;
+begin
+  LVH := FValue.GetHashCode();
+  if FCritical then
+    Result := LVH
+  else
+    Result := not LVH;
+end;
+
+function TX509Extension.Equals(AObj: TObject): Boolean;
+var
+  LThat: IX509Extension;
+begin
+  if not Supports(AObj, IX509Extension, LThat) then
+  begin
+    Result := False;
+    Exit;
+  end;
+  Result := FValue.Equals(LThat.Value) and (FCritical = LThat.IsCritical);
+end;
+
+end.

+ 165 - 0
CryptoLib/src/Asn1/X509/ClpX509ExtensionBase.pas

@@ -0,0 +1,165 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509ExtensionBase;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIX509Extension,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Base class for X509 objects that support extensions.
+  /// </summary>
+  TX509ExtensionBase = class abstract(TInterfacedObject)
+
+  strict protected
+    /// <summary>
+    /// Get the X509Extensions object for this instance.
+    /// </summary>
+    function GetX509Extensions: IX509Extensions; virtual; abstract;
+
+    /// <summary>
+    /// Get extension OIDs filtered by critical flag.
+    /// </summary>
+    function GetExtensionOids(ACritical: Boolean): TCryptoLibStringArray; virtual;
+
+  public
+    /// <summary>
+    /// Get non critical extensions.
+    /// </summary>
+    /// <returns>An array of non critical extension OID strings.</returns>
+    function GetNonCriticalExtensionOids: TCryptoLibStringArray; virtual;
+
+    /// <summary>
+    /// Get any critical extensions.
+    /// </summary>
+    /// <returns>An array of critical extension OID strings.</returns>
+    function GetCriticalExtensionOids: TCryptoLibStringArray; virtual;
+
+    /// <summary>
+    /// Get extension value by OID.
+    /// </summary>
+    function GetExtensionValue(const AOid: IDerObjectIdentifier): IAsn1OctetString; virtual;
+
+    /// <summary>
+    /// Get extension by OID.
+    /// </summary>
+    function GetExtension(const AOid: IDerObjectIdentifier): IX509Extension; virtual;
+
+    /// <summary>
+    /// Get parsed extension value by OID.
+    /// </summary>
+    function GetExtensionParsedValue(const AOid: IDerObjectIdentifier): IAsn1Object; virtual;
+
+  end;
+
+implementation
+
+{ TX509ExtensionBase }
+
+function TX509ExtensionBase.GetExtensionOids(ACritical: Boolean): TCryptoLibStringArray;
+var
+  LExtensions: IX509Extensions;
+  LOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LExt: IX509Extension;
+  LList: TList<String>;
+  I: Int32;
+begin
+  LExtensions := GetX509Extensions();
+  if LExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LList := TList<String>.Create();
+  try
+    LOids := LExtensions.GetExtensionOids;
+    for I := 0 to System.Length(LOids) - 1 do
+    begin
+      LExt := LExtensions.GetExtension(LOids[I]);
+      if (LExt <> nil) and (LExt.IsCritical = ACritical) then
+      begin
+        LList.Add(LOids[I].Id);
+      end;
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
+function TX509ExtensionBase.GetNonCriticalExtensionOids: TCryptoLibStringArray;
+begin
+  Result := GetExtensionOids(False);
+end;
+
+function TX509ExtensionBase.GetCriticalExtensionOids: TCryptoLibStringArray;
+begin
+  Result := GetExtensionOids(True);
+end;
+
+function TX509ExtensionBase.GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+var
+  LExtensions: IX509Extensions;
+begin
+  LExtensions := GetX509Extensions();
+  if LExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := LExtensions.GetExtension(AOid);
+end;
+
+function TX509ExtensionBase.GetExtensionParsedValue(const AOid: IDerObjectIdentifier): IAsn1Object;
+var
+  LExtensions: IX509Extensions;
+begin
+  LExtensions := GetX509Extensions();
+  if LExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := LExtensions.GetExtensionParsedValue(AOid);
+end;
+
+function TX509ExtensionBase.GetExtensionValue(const AOid: IDerObjectIdentifier): IAsn1OctetString;
+var
+  LExtensions: IX509Extensions;
+begin
+  LExtensions := GetX509Extensions();
+  if LExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := LExtensions.GetExtensionValue(AOid);
+end;
+
+end.

+ 245 - 0
CryptoLib/src/Asn1/X509/ClpX509ExtensionUtilities.pas

@@ -0,0 +1,245 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509ExtensionUtilities;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpOiwObjectIdentifiers,
+  ClpDigestUtilities,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpArrayUtils;
+
+type
+  /// <summary>
+  /// Utility class for X509 extension operations.
+  /// </summary>
+  TX509ExtensionUtilities = class sealed(TObject)
+
+  strict private
+    class function CalculateSha1(const AData: TCryptoLibByteArray): TCryptoLibByteArray; overload; static;
+    class function CalculateSha1(const ASpki: ISubjectPublicKeyInfo): TCryptoLibByteArray; overload; static;
+    class function CalculateKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): IAsn1OctetString; overload; static;
+    class function CalculateKeyIdentifier(const ACertificate: IX509CertificateStructure): IAsn1OctetString; overload; static;
+    class function DeriveAuthCertKeyID(const AAuthorityCert: IX509CertificateStructure): IAsn1OctetString; static;
+
+  public
+    /// <summary>
+    /// Create AuthorityKeyIdentifier from SubjectPublicKeyInfo.
+    /// </summary>
+    class function CreateAuthorityKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): IAuthorityKeyIdentifier; overload; static;
+    /// <summary>
+    /// Create AuthorityKeyIdentifier from SubjectPublicKeyInfo with issuer and serial number.
+    /// </summary>
+    class function CreateAuthorityKeyIdentifier(const ASpki: ISubjectPublicKeyInfo;
+      const AIssuer: IGeneralNames; const ASerialNumber: IDerInteger): IAuthorityKeyIdentifier; overload; static;
+    /// <summary>
+    /// Create AuthorityKeyIdentifier from certificate.
+    /// </summary>
+    class function CreateAuthorityKeyIdentifier(const ACertificate: IX509CertificateStructure): IAuthorityKeyIdentifier; overload; static;
+    /// <summary>
+    /// Create SubjectKeyIdentifier from SubjectPublicKeyInfo.
+    /// </summary>
+    class function CreateSubjectKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): ISubjectKeyIdentifier; static;
+    /// <summary>
+    /// Create truncated SubjectKeyIdentifier from SubjectPublicKeyInfo.
+    /// </summary>
+    class function CreateTruncatedSubjectKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): ISubjectKeyIdentifier; static;
+    /// <summary>
+    /// Get AuthorityKeyIdentifier from extensions.
+    /// </summary>
+    class function GetAuthorityKeyIdentifier(const AExtensions: IX509Extensions): IAuthorityKeyIdentifier; static;
+    /// <summary>
+    /// Get SubjectKeyIdentifier from extensions.
+    /// </summary>
+    class function GetSubjectKeyIdentifier(const AExtensions: IX509Extensions): ISubjectKeyIdentifier; static;
+    /// <summary>
+    /// Parse extension value to ASN.1 object.
+    /// </summary>
+    class function FromExtensionValue(const AExtensionValue: IAsn1OctetString): IAsn1Object; overload; static;
+    /// <summary>
+    /// Extract the value of the given extension, if it exists.
+    /// </summary>
+    class function FromExtensionValue(const AExtensions: IX509Extensions;
+      const AOid: IDerObjectIdentifier): IAsn1Object; overload; static;
+    /// <summary>
+    /// Get extension value and parse it using the provided constructor function.
+    /// </summary>
+    class function GetExtension<TExtension>(const AExtensions: IX509Extensions;
+      const AOid: IDerObjectIdentifier;
+      const AConstructor: TFunc<TCryptoLibByteArray, TExtension>): TExtension; static;
+
+  end;
+
+implementation
+
+{ TX509ExtensionUtilities }
+
+class function TX509ExtensionUtilities.CalculateSha1(const AData: TCryptoLibByteArray): TCryptoLibByteArray;
+begin
+  Result := TDigestUtilities.CalculateDigest(TOiwObjectIdentifiers.IdSha1.id, AData);
+end;
+
+class function TX509ExtensionUtilities.CalculateSha1(const ASpki: ISubjectPublicKeyInfo): TCryptoLibByteArray;
+var
+  LPublicKey: IDerBitString;
+  LBytes: TCryptoLibByteArray;
+begin
+  LPublicKey := ASpki.PublicKey;
+  if LPublicKey.IsOctetAligned() then
+  begin
+    LBytes := LPublicKey.GetOctets();
+  end
+  else
+  begin
+    LBytes := LPublicKey.GetBytes();
+  end;
+  Result := CalculateSha1(LBytes);
+end;
+
+class function TX509ExtensionUtilities.CalculateKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): IAsn1OctetString;
+begin
+  Result := TDerOctetString.Create(CalculateSha1(ASpki));
+end;
+
+class function TX509ExtensionUtilities.CalculateKeyIdentifier(const ACertificate: IX509CertificateStructure): IAsn1OctetString;
+begin
+  Result := CalculateKeyIdentifier(ACertificate.SubjectPublicKeyInfo);
+end;
+
+class function TX509ExtensionUtilities.DeriveAuthCertKeyID(const AAuthorityCert: IX509CertificateStructure): IAsn1OctetString;
+var
+  LSubjectKeyIdentifier: ISubjectKeyIdentifier;
+  LExtensions: IX509Extensions;
+begin
+  LExtensions := AAuthorityCert.Extensions;
+  if LExtensions <> nil then
+  begin
+    LSubjectKeyIdentifier := GetSubjectKeyIdentifier(LExtensions);
+    if LSubjectKeyIdentifier <> nil then
+    begin
+      Result := TDerOctetString.WithContents(LSubjectKeyIdentifier.GetKeyIdentifier());
+      Exit;
+    end;
+  end;
+  Result := CalculateKeyIdentifier(AAuthorityCert);
+end;
+
+class function TX509ExtensionUtilities.CreateAuthorityKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): IAuthorityKeyIdentifier;
+begin
+  Result := TAuthorityKeyIdentifier.Create(CalculateKeyIdentifier(ASpki));
+end;
+
+class function TX509ExtensionUtilities.CreateAuthorityKeyIdentifier(const ASpki: ISubjectPublicKeyInfo;
+  const AIssuer: IGeneralNames; const ASerialNumber: IDerInteger): IAuthorityKeyIdentifier;
+begin
+  Result := TAuthorityKeyIdentifier.Create(CalculateKeyIdentifier(ASpki), AIssuer, ASerialNumber);
+end;
+
+class function TX509ExtensionUtilities.CreateAuthorityKeyIdentifier(const ACertificate: IX509CertificateStructure): IAuthorityKeyIdentifier;
+var
+  LKeyIdentifier: IAsn1OctetString;
+  LAuthorityCertIssuer: IGeneralNames;
+  LAuthorityCertSerialNumber: IDerInteger;
+begin
+  LKeyIdentifier := DeriveAuthCertKeyID(ACertificate);
+  LAuthorityCertIssuer := TGeneralNames.Create(TGeneralName.Create(ACertificate.Issuer));
+  LAuthorityCertSerialNumber := ACertificate.SerialNumber;
+  Result := TAuthorityKeyIdentifier.Create(LKeyIdentifier, LAuthorityCertIssuer, LAuthorityCertSerialNumber);
+end;
+
+class function TX509ExtensionUtilities.CreateSubjectKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): ISubjectKeyIdentifier;
+begin
+  Result := TSubjectKeyIdentifier.Create(CalculateKeyIdentifier(ASpki).GetOctets());
+end;
+
+class function TX509ExtensionUtilities.CreateTruncatedSubjectKeyIdentifier(const ASpki: ISubjectPublicKeyInfo): ISubjectKeyIdentifier;
+var
+  LSha1: TCryptoLibByteArray;
+  LId: TCryptoLibByteArray;
+begin
+  LSha1 := CalculateSha1(ASpki);
+  LId := TArrayUtils.CopyOfRange(LSha1, System.Length(LSha1) - 8, System.Length(LSha1));
+  LId[0] := LId[0] and $0F;
+  LId[0] := LId[0] or $40;
+  Result := TSubjectKeyIdentifier.Create(LId);
+end;
+
+class function TX509ExtensionUtilities.GetAuthorityKeyIdentifier(const AExtensions: IX509Extensions): IAuthorityKeyIdentifier;
+begin
+  Result := GetExtension<IAuthorityKeyIdentifier>(AExtensions, TX509Extensions.AuthorityKeyIdentifier,
+    function(AOctets: TCryptoLibByteArray): IAuthorityKeyIdentifier
+    begin
+      Result := TAuthorityKeyIdentifier.GetInstance(AOctets);
+    end);
+end;
+
+class function TX509ExtensionUtilities.GetSubjectKeyIdentifier(const AExtensions: IX509Extensions): ISubjectKeyIdentifier;
+begin
+  Result := GetExtension<ISubjectKeyIdentifier>(AExtensions, TX509Extensions.SubjectKeyIdentifier,
+    function(AOctets: TCryptoLibByteArray): ISubjectKeyIdentifier
+    begin
+      Result := TSubjectKeyIdentifier.GetInstance(AOctets);
+    end);
+end;
+
+class function TX509ExtensionUtilities.FromExtensionValue(const AExtensionValue: IAsn1OctetString): IAsn1Object;
+begin
+  Result := TAsn1Object.FromByteArray(AExtensionValue.GetOctets());
+end;
+
+class function TX509ExtensionUtilities.FromExtensionValue(const AExtensions: IX509Extensions;
+  const AOid: IDerObjectIdentifier): IAsn1Object;
+begin
+  if AExtensions = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := AExtensions.GetExtensionParsedValue(AOid);
+end;
+
+class function TX509ExtensionUtilities.GetExtension<TExtension>(const AExtensions: IX509Extensions;
+  const AOid: IDerObjectIdentifier;
+  const AConstructor: TFunc<TCryptoLibByteArray, TExtension>): TExtension;
+var
+  LExtensionValue: IAsn1OctetString;
+begin
+  if AExtensions = nil then
+  begin
+    Result := Default(TExtension);
+    Exit;
+  end;
+  LExtensionValue := AExtensions.GetExtensionValue(AOid);
+  if LExtensionValue = nil then
+  begin
+    Result := Default(TExtension);
+    Exit;
+  end;
+  Result := AConstructor(LExtensionValue.GetOctets());
+end;
+
+end.

+ 269 - 0
CryptoLib/src/Asn1/X509/ClpX509ExtensionsGenerator.pas

@@ -0,0 +1,269 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509ExtensionsGenerator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509ExtensionsGenerator,
+  ClpIX509Asn1Objects,
+  ClpIX509Extension,
+  ClpX509Extension,
+  ClpX509Asn1Objects,
+  ClpCryptoLibTypes,
+  ClpCryptoLibComparers;
+
+type
+  /// <remarks>Generator for X.509 extensions</remarks>
+  TX509ExtensionsGenerator = class(TInterfacedObject, IX509ExtensionsGenerator)
+
+  strict private
+  var
+    FExtensions: TDictionary<IDerObjectIdentifier, IX509Extension>;
+    FOrdering: TList<IDerObjectIdentifier>;
+
+  strict private
+    class var
+      FDupsAllowed: TDictionary<IDerObjectIdentifier, Boolean>;
+
+    class procedure Boot; static;
+    class constructor Create;
+    class destructor Destroy;
+
+  strict private
+    procedure ImplAddExtension(const AOid: IDerObjectIdentifier;
+      const AX509Extension: IX509Extension);
+    procedure ImplAddExtensionDup(const AExistingExtension: IX509Extension;
+      const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: TCryptoLibByteArray);
+
+  public
+    constructor Create;
+    destructor Destroy; override;
+
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Convertible); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Encodable); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: TCryptoLibByteArray); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier;
+      const AX509Extension: IX509Extension); overload;
+    procedure AddExtensions(const AExtensions: IX509Extensions);
+    function Generate: IX509Extensions;
+    function GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+    function HasExtension(const AOid: IDerObjectIdentifier): Boolean;
+    function IsEmpty: Boolean;
+    procedure RemoveExtension(const AOid: IDerObjectIdentifier);
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Convertible); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Encodable); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: TCryptoLibByteArray); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier;
+      const AX509Extension: IX509Extension); overload;
+    procedure Reset;
+
+  end;
+
+implementation
+
+{ TX509ExtensionsGenerator }
+
+class constructor TX509ExtensionsGenerator.Create;
+begin
+  Boot;
+end;
+
+class destructor TX509ExtensionsGenerator.Destroy;
+begin
+  FDupsAllowed.Free;
+end;
+
+class procedure TX509ExtensionsGenerator.Boot;
+begin
+  FDupsAllowed := TDictionary<IDerObjectIdentifier, Boolean>.Create(TCryptoLibComparers.OidEqualityComparer);
+  // OIDs that allow duplicate extensions
+  FDupsAllowed.Add(TX509Extensions.SubjectAlternativeName, True);
+  FDupsAllowed.Add(TX509Extensions.IssuerAlternativeName, True);
+  FDupsAllowed.Add(TX509Extensions.SubjectDirectoryAttributes, True);
+  FDupsAllowed.Add(TX509Extensions.CertificateIssuer, True);
+end;
+
+constructor TX509ExtensionsGenerator.Create;
+begin
+  inherited Create();
+  FExtensions := TDictionary<IDerObjectIdentifier, IX509Extension>.Create(TCryptoLibComparers.OidEqualityComparer);
+  FOrdering := TList<IDerObjectIdentifier>.Create(TCryptoLibComparers.OidComparer);
+end;
+
+destructor TX509ExtensionsGenerator.Destroy;
+begin
+  FExtensions.Free;
+  FOrdering.Free;
+  inherited Destroy;
+end;
+
+procedure TX509ExtensionsGenerator.AddExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: IAsn1Convertible);
+begin
+  AddExtension(AOid, ACritical, AExtValue.ToAsn1Object());
+end;
+
+procedure TX509ExtensionsGenerator.AddExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: IAsn1Encodable);
+var
+  LExisting: IX509Extension;
+begin
+  if FExtensions.TryGetValue(AOid, LExisting) then
+  begin
+    ImplAddExtensionDup(LExisting, AOid, ACritical, AExtValue.GetEncoded(TAsn1Encodable.Der));
+  end
+  else
+  begin
+    ImplAddExtension(AOid, TX509Extension.Create(ACritical, TDerOctetString.Create(AExtValue)));
+  end;
+end;
+
+procedure TX509ExtensionsGenerator.AddExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: TCryptoLibByteArray);
+var
+  LExisting: IX509Extension;
+begin
+  if FExtensions.TryGetValue(AOid, LExisting) then
+  begin
+    ImplAddExtensionDup(LExisting, AOid, ACritical, AExtValue);
+  end
+  else
+  begin
+    ImplAddExtension(AOid, TX509Extension.Create(ACritical, TDerOctetString.FromContents(AExtValue)));
+  end;
+end;
+
+procedure TX509ExtensionsGenerator.AddExtension(const AOid: IDerObjectIdentifier;
+  const AX509Extension: IX509Extension);
+begin
+  if HasExtension(AOid) then
+    raise EArgumentCryptoLibException.CreateFmt('extension %s already added', [AOid.Id]);
+  ImplAddExtension(AOid, AX509Extension);
+end;
+
+procedure TX509ExtensionsGenerator.AddExtensions(const AExtensions: IX509Extensions);
+var
+  LOid: IDerObjectIdentifier;
+  LExt: IX509Extension;
+begin
+  for LOid in AExtensions.GetExtensionOids() do
+  begin
+    LExt := AExtensions.GetExtension(LOid);
+    AddExtension(LOid, LExt.IsCritical, LExt.Value.GetOctets());
+  end;
+end;
+
+function TX509ExtensionsGenerator.Generate: IX509Extensions;
+begin
+  Result := TX509Extensions.Create(FOrdering, FExtensions);
+end;
+
+function TX509ExtensionsGenerator.GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+begin
+  if not FExtensions.TryGetValue(AOid, Result) then
+    Result := nil;
+end;
+
+function TX509ExtensionsGenerator.HasExtension(const AOid: IDerObjectIdentifier): Boolean;
+begin
+  Result := FExtensions.ContainsKey(AOid);
+end;
+
+function TX509ExtensionsGenerator.IsEmpty: Boolean;
+begin
+  Result := FOrdering.Count < 1;
+end;
+
+procedure TX509ExtensionsGenerator.RemoveExtension(const AOid: IDerObjectIdentifier);
+begin
+  if not HasExtension(AOid) then
+    raise EInvalidOperationCryptoLibException.CreateFmt('extension %s not present', [AOid.Id]);
+  FOrdering.Remove(AOid);
+  FExtensions.Remove(AOid);
+end;
+
+procedure TX509ExtensionsGenerator.ReplaceExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: IAsn1Convertible);
+begin
+  ReplaceExtension(AOid, ACritical, AExtValue.ToAsn1Object());
+end;
+
+procedure TX509ExtensionsGenerator.ReplaceExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: IAsn1Encodable);
+begin
+  ReplaceExtension(AOid, TX509Extension.Create(ACritical, TDerOctetString.Create(AExtValue)));
+end;
+
+procedure TX509ExtensionsGenerator.ReplaceExtension(const AOid: IDerObjectIdentifier;
+  ACritical: Boolean; const AExtValue: TCryptoLibByteArray);
+begin
+  ReplaceExtension(AOid, TX509Extension.Create(ACritical, TDerOctetString.FromContents(AExtValue)));
+end;
+
+procedure TX509ExtensionsGenerator.ReplaceExtension(const AOid: IDerObjectIdentifier;
+  const AX509Extension: IX509Extension);
+begin
+  if not HasExtension(AOid) then
+    raise EInvalidOperationCryptoLibException.CreateFmt('extension %s not present', [AOid.Id]);
+  FExtensions[AOid] := AX509Extension;
+end;
+
+procedure TX509ExtensionsGenerator.Reset;
+begin
+  FExtensions.Clear;
+  FOrdering.Clear;
+end;
+
+procedure TX509ExtensionsGenerator.ImplAddExtension(const AOid: IDerObjectIdentifier;
+  const AX509Extension: IX509Extension);
+begin
+  FOrdering.Add(AOid);
+  FExtensions.Add(AOid, AX509Extension);
+end;
+
+procedure TX509ExtensionsGenerator.ImplAddExtensionDup(const AExistingExtension: IX509Extension;
+  const AOid: IDerObjectIdentifier; ACritical: Boolean; const AExtValue: TCryptoLibByteArray);
+var
+  LSeq1, LSeq2, LConcat: IAsn1Sequence;
+begin
+  if not FDupsAllowed.ContainsKey(AOid) then
+    raise EArgumentCryptoLibException.CreateFmt('extension %s already added', [AOid.Id]);
+
+  LSeq1 := TAsn1Sequence.GetInstance(AExistingExtension.Value.GetOctets());
+  LSeq2 := TAsn1Sequence.GetInstance(AExtValue);
+  LConcat := TDerSequence.Concatenate([LSeq1, LSeq2]);
+
+  FExtensions[AOid] := TX509Extension.Create(AExistingExtension.IsCritical or ACritical,
+    TDerOctetString.Create(LConcat));
+end;
+
+end.

+ 79 - 0
CryptoLib/src/Asn1/X509/ClpX509NameEntryConverter.pas

@@ -0,0 +1,79 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509NameEntryConverter;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509NameEntryConverter,
+  ClpEncoders,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Abstract base class for X509NameEntryConverter.
+  /// </summary>
+  TX509NameEntryConverter = class abstract(TInterfacedObject, IX509NameEntryConverter)
+
+  strict protected
+    /// <summary>
+    /// Convert an inline encoded hex string rendition of an ASN.1 object back into its corresponding ASN.1 object.
+    /// </summary>
+    function ConvertHexEncoded(const AHexString: String;
+      AOffset: Int32): IAsn1Object;
+
+    /// <summary>
+    /// Return true if the passed in string can be represented without loss as a PrintableString, false otherwise.
+    /// </summary>
+    function CanBePrintable(const AStr: String): Boolean;
+
+  public
+    /// <summary>
+    /// Convert the passed in string value into the appropriate ASN.1 encoded object.
+    /// </summary>
+    function GetConvertedValue(const AOid: IDerObjectIdentifier;
+      const AValue: String): IAsn1Object; virtual; abstract;
+
+  end;
+
+implementation
+
+{ TX509NameEntryConverter }
+
+function TX509NameEntryConverter.ConvertHexEncoded(const AHexString: String;
+  AOffset: Int32): IAsn1Object;
+var
+  LBytes: TCryptoLibByteArray;
+  LHexSubstring: String;
+begin
+  LHexSubstring := System.Copy(AHexString, AOffset + 1, System.Length(AHexString) - AOffset);
+  LBytes := THex.Decode(LHexSubstring);
+  Result := TAsn1Object.FromByteArray(LBytes);
+end;
+
+function TX509NameEntryConverter.CanBePrintable(const AStr: String): Boolean;
+begin
+  Result := TDerPrintableString.IsPrintableString(AStr);
+end;
+
+end.

+ 135 - 0
CryptoLib/src/Asn1/X509/ClpX509NameTokenizer.pas

@@ -0,0 +1,135 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509NameTokenizer;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpCryptoLibTypes,
+  ClpIX509NameTokenizer;
+
+type
+  /// <summary>
+  /// Class for breaking up an X500 Name into its component tokens, ala java.util.StringTokenizer.
+  /// </summary>
+  TX509NameTokenizer = class sealed(TInterfacedObject, IX509NameTokenizer)
+
+  strict private
+  var
+    FValue: String;
+    FSeparator: Char;
+    FIndex: Int32;
+
+  public
+    constructor Create(const AOid: String); overload;
+    constructor Create(const AOid: String; ASeparator: Char); overload;
+
+    function HasMoreTokens: Boolean;
+    function NextToken: String;
+
+  end;
+
+implementation
+
+{ TX509NameTokenizer }
+
+constructor TX509NameTokenizer.Create(const AOid: String);
+begin
+  Create(AOid, ',');
+end;
+
+constructor TX509NameTokenizer.Create(const AOid: String; ASeparator: Char);
+begin
+  inherited Create();
+  if AOid = '' then
+    raise EArgumentNilCryptoLibException.Create('oid');
+  if (ASeparator = '"') or (ASeparator = '\') then
+    raise EArgumentCryptoLibException.Create('reserved separator character');
+
+  FValue := AOid;
+  FSeparator := ASeparator;
+  if System.Length(AOid) < 1 then
+    FIndex := 0
+  else
+    FIndex := -1;
+end;
+
+function TX509NameTokenizer.HasMoreTokens: Boolean;
+begin
+  Result := FIndex < System.Length(FValue);
+end;
+
+function TX509NameTokenizer.NextToken: String;
+var
+  LQuoted, LEscaped: Boolean;
+  LBeginIndex: Int32;
+  LC: Char;
+begin
+  if FIndex >= System.Length(FValue) then
+  begin
+    Result := '';
+    Exit;
+  end;
+
+  LQuoted := False;
+  LEscaped := False;
+  LBeginIndex := FIndex + 2;
+
+  // increments first, then checks
+  // This means: increment m_index, then check if less than Length, then enter loop
+  // Equivalent: increment first, then check in while condition
+  System.Inc(FIndex); // Increment first
+  while FIndex < System.Length(FValue) do
+  begin
+    LC := FValue[FIndex + 1];
+
+    if LEscaped then
+    begin
+      LEscaped := False;
+    end
+    else if LC = '"' then
+    begin
+      LQuoted := not LQuoted;
+    end
+    else if LQuoted then
+    begin
+      // Continue in quoted mode
+    end
+    else if LC = '\' then
+    begin
+      LEscaped := True;
+    end
+    else if LC = FSeparator then
+    begin
+      Result := System.Copy(FValue, LBeginIndex, FIndex - (LBeginIndex - 1));
+      Exit;
+    end;
+
+    System.Inc(FIndex); // Increment for next iteration
+  end;
+
+  if LEscaped or LQuoted then
+    raise EArgumentCryptoLibException.Create('badly formatted directory string');
+
+  Result := System.Copy(FValue, LBeginIndex, FIndex - (LBeginIndex - 1));
+end;
+
+end.

+ 324 - 0
CryptoLib/src/Asn1/X509/ClpX509SignatureUtilities.pas

@@ -0,0 +1,324 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509SignatureUtilities;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpPkcsObjectIdentifiers,
+  ClpIPkcsAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpX9ObjectIdentifiers,
+  ClpNistObjectIdentifiers,
+  ClpTeleTrusTObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpCryptoProObjectIdentifiers,
+  ClpRosstandartObjectIdentifiers,
+  ClpSignerUtilities,
+  ClpX509Utilities,
+  ClpCryptoLibComparers,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Internal utilities for X509 signature operations.
+  /// </summary>
+  TX509SignatureUtilities = class sealed(TObject)
+
+  strict private
+    class var
+      FAlgorithms: TDictionary<String, IDerObjectIdentifier>;
+      FExParams: TDictionary<String, IAsn1Encodable>;
+      FNoParams: TDictionary<IDerObjectIdentifier, IAlgorithmIdentifier>;
+
+    class function GetDigestName(const ADigestAlgOid: IDerObjectIdentifier): String; static;
+    class procedure Boot; static;
+    class constructor Create;
+
+  public
+    class function GetSignatureName(const ASigAlgID: IAlgorithmIdentifier): String; static;
+    class function GetSigOid(const ASigName: String): IDerObjectIdentifier; static;
+    class function GetSigAlgID(const AAlgorithmName: String): IAlgorithmIdentifier; static;
+    class function GetSigNames: TCryptoLibStringArray; static;
+
+  end;
+
+implementation
+
+{ TX509SignatureUtilities }
+
+class constructor TX509SignatureUtilities.Create;
+begin
+  Boot;
+end;
+
+class procedure TX509SignatureUtilities.Boot;
+begin
+  FAlgorithms := TDictionary<String, IDerObjectIdentifier>.Create(TCryptoLibComparers.OrdinalIgnoreCaseEqualityComparer);
+  FExParams := TDictionary<String, IAsn1Encodable>.Create();
+  FNoParams := TDictionary<IDerObjectIdentifier, IAlgorithmIdentifier>.Create(TCryptoLibComparers.OidEqualityComparer);
+
+  // Initialize algorithms - same as TX509Utilities
+  // MD2 algorithms
+  FAlgorithms.Add('MD2WITHRSAENCRYPTION', TPkcsObjectIdentifiers.MD2WithRsaEncryption);
+  FAlgorithms.Add('MD2WITHRSA', TPkcsObjectIdentifiers.MD2WithRsaEncryption);
+
+  // MD5 algorithms
+  FAlgorithms.Add('MD5WITHRSAENCRYPTION', TPkcsObjectIdentifiers.MD5WithRsaEncryption);
+  FAlgorithms.Add('MD5WITHRSA', TPkcsObjectIdentifiers.MD5WithRsaEncryption);
+
+  // SHA1 algorithms
+  FAlgorithms.Add('SHA1WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA-1WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA1WITHRSA', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA-1WITHRSA', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+
+  // SHA224 algorithms
+  FAlgorithms.Add('SHA224WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA-224WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA224WITHRSA', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA-224WITHRSA', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+
+  // SHA256 algorithms
+  FAlgorithms.Add('SHA256WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA-256WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA256WITHRSA', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA-256WITHRSA', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+
+  // SHA384 algorithms
+  FAlgorithms.Add('SHA384WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA-384WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA384WITHRSA', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA-384WITHRSA', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+
+  // SHA512 algorithms
+  FAlgorithms.Add('SHA512WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA-512WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA512WITHRSA', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA-512WITHRSA', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+
+  // SHA512(224) algorithms
+  FAlgorithms.Add('SHA512(224)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(224)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA512(224)WITHRSA', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(224)WITHRSA', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+
+  // SHA512(256) algorithms
+  FAlgorithms.Add('SHA512(256)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(256)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA512(256)WITHRSA', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(256)WITHRSA', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+
+  // RSA-PSS algorithms
+  FAlgorithms.Add('SHA1WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA224WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA256WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA384WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA512WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+
+  // RIPEMD algorithms
+  FAlgorithms.Add('RIPEMD160WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+  FAlgorithms.Add('RIPEMD160WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+  FAlgorithms.Add('RIPEMD128WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+  FAlgorithms.Add('RIPEMD128WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+  FAlgorithms.Add('RIPEMD256WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+  FAlgorithms.Add('RIPEMD256WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+
+  // DSA algorithms
+  FAlgorithms.Add('SHA1WITHDSA', TX9ObjectIdentifiers.IdDsaWithSha1);
+  FAlgorithms.Add('DSAWITHSHA1', TX9ObjectIdentifiers.IdDsaWithSha1);
+  FAlgorithms.Add('SHA224WITHDSA', TNistObjectIdentifiers.DsaWithSha224);
+  FAlgorithms.Add('SHA256WITHDSA', TNistObjectIdentifiers.DsaWithSha256);
+  FAlgorithms.Add('SHA384WITHDSA', TNistObjectIdentifiers.DsaWithSha384);
+  FAlgorithms.Add('SHA512WITHDSA', TNistObjectIdentifiers.DsaWithSha512);
+
+  // ECDSA algorithms
+  FAlgorithms.Add('SHA1WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha1);
+  FAlgorithms.Add('ECDSAWITHSHA1', TX9ObjectIdentifiers.ECDsaWithSha1);
+  FAlgorithms.Add('SHA224WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha224);
+  FAlgorithms.Add('SHA256WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha256);
+  FAlgorithms.Add('SHA384WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha384);
+  FAlgorithms.Add('SHA512WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha512);
+
+  // GOST algorithms
+  FAlgorithms.Add('GOST3411WITHGOST3410', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+  FAlgorithms.Add('GOST3411WITHGOST3410-94', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+  FAlgorithms.Add('GOST3411WITHECGOST3410', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+  FAlgorithms.Add('GOST3411WITHECGOST3410-2001', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+  FAlgorithms.Add('GOST3411WITHGOST3410-2001', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+
+  // Add no-params entries for algorithms that don't require parameters
+  FNoParams.Add(TX9ObjectIdentifiers.IdDsaWithSha1, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.IdDsaWithSha1));
+  FNoParams.Add(TX9ObjectIdentifiers.ECDsaWithSha1, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.ECDsaWithSha1));
+  FNoParams.Add(TX9ObjectIdentifiers.ECDsaWithSha224, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.ECDsaWithSha224));
+  FNoParams.Add(TX9ObjectIdentifiers.ECDsaWithSha256, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.ECDsaWithSha256));
+  FNoParams.Add(TX9ObjectIdentifiers.ECDsaWithSha384, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.ECDsaWithSha384));
+  FNoParams.Add(TX9ObjectIdentifiers.ECDsaWithSha512, TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.ECDsaWithSha512));
+end;
+
+class function TX509SignatureUtilities.GetDigestName(const ADigestAlgOid: IDerObjectIdentifier): String;
+begin
+  if TPkcsObjectIdentifiers.MD5.Equals(ADigestAlgOid) then
+    Result := 'MD5'
+  else if TOiwObjectIdentifiers.IdSha1.Equals(ADigestAlgOid) then
+    Result := 'SHA1'
+  else if TNistObjectIdentifiers.IdSha224.Equals(ADigestAlgOid) then
+    Result := 'SHA224'
+  else if TNistObjectIdentifiers.IdSha256.Equals(ADigestAlgOid) then
+    Result := 'SHA256'
+  else if TNistObjectIdentifiers.IdSha384.Equals(ADigestAlgOid) then
+    Result := 'SHA384'
+  else if TNistObjectIdentifiers.IdSha512.Equals(ADigestAlgOid) then
+    Result := 'SHA512'
+  else if TNistObjectIdentifiers.IdSha512_224.Equals(ADigestAlgOid) then
+    Result := 'SHA512(224)'
+  else if TNistObjectIdentifiers.IdSha512_256.Equals(ADigestAlgOid) then
+    Result := 'SHA512(256)'
+  else if TTeleTrusTObjectIdentifiers.RipeMD128.Equals(ADigestAlgOid) then
+    Result := 'RIPEMD128'
+  else if TTeleTrusTObjectIdentifiers.RipeMD160.Equals(ADigestAlgOid) then
+    Result := 'RIPEMD160'
+  else if TTeleTrusTObjectIdentifiers.RipeMD256.Equals(ADigestAlgOid) then
+    Result := 'RIPEMD256'
+  else if TCryptoProObjectIdentifiers.GostR3411.Equals(ADigestAlgOid) then
+    Result := 'GOST3411'
+  else if TRosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.Equals(ADigestAlgOid) then
+    Result := 'GOST3411-2012-256'
+  else if TRosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.Equals(ADigestAlgOid) then
+    Result := 'GOST3411-2012-512'
+  else
+    Result := ADigestAlgOid.Id;
+end;
+
+class function TX509SignatureUtilities.GetSignatureName(const ASigAlgID: IAlgorithmIdentifier): String;
+var
+  LSigAlgOid: IDerObjectIdentifier;
+  LSigAlgParams: IAsn1Encodable;
+  LRsassaPssParams: IRsassaPssParameters;
+  LECdsaParams: IAlgorithmIdentifier;
+begin
+  if ASigAlgID = nil then
+  begin
+    Result := '';
+    Exit;
+  end;
+
+  LSigAlgOid := ASigAlgID.Algorithm;
+  LSigAlgParams := ASigAlgID.Parameters;
+
+  if not TX509Utilities.IsAbsentParameters(LSigAlgParams) then
+  begin
+    if TPkcsObjectIdentifiers.IdRsassaPss.Equals(LSigAlgOid) then
+    begin
+      LRsassaPssParams := TRsassaPssParameters.GetInstance(LSigAlgParams as TObject);
+      Result := GetDigestName(LRsassaPssParams.HashAlgorithm.Algorithm) + 'withRSAandMGF1';
+      Exit;
+    end;
+    if TX9ObjectIdentifiers.ECDsaWithSha2.Equals(LSigAlgOid) then
+    begin
+      LECdsaParams := TAlgorithmIdentifier.GetInstance(LSigAlgParams as TObject);
+      Result := GetDigestName(LECdsaParams.Algorithm) + 'withECDSA';
+      Exit;
+    end;
+  end;
+
+  Result := TSignerUtilities.GetEncodingName(LSigAlgOid);
+  if Result = '' then
+    Result := LSigAlgOid.Id;
+end;
+
+class function TX509SignatureUtilities.GetSigOid(const ASigName: String): IDerObjectIdentifier;
+var
+  LUpperName: String;
+begin
+  if ASigName = '' then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LUpperName := UpperCase(ASigName);
+  if not FAlgorithms.TryGetValue(LUpperName, Result) then
+  begin
+    // Try to parse as OID string
+    try
+      Result := TDerObjectIdentifier.Create(ASigName);
+    except
+      Result := nil;
+    end;
+  end;
+end;
+
+class function TX509SignatureUtilities.GetSigAlgID(const AAlgorithmName: String): IAlgorithmIdentifier;
+var
+  LSigOid: IDerObjectIdentifier;
+  LNoParamsAlgID: IAlgorithmIdentifier;
+  LExplicitParams: IAsn1Encodable;
+begin
+  LSigOid := GetSigOid(AAlgorithmName);
+
+  if LSigOid = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  // Check for no-params algorithms
+  if FNoParams.TryGetValue(LSigOid, LNoParamsAlgID) then
+  begin
+    Result := LNoParamsAlgID;
+    Exit;
+  end;
+
+  // Check for explicit parameters
+  if FExParams.TryGetValue(AAlgorithmName, LExplicitParams) then
+  begin
+    Result := TAlgorithmIdentifier.Create(LSigOid, LExplicitParams);
+    Exit;
+  end;
+
+  // Default: OID with NULL parameters
+  Result := TAlgorithmIdentifier.Create(LSigOid, TDerNull.Instance);
+end;
+
+class function TX509SignatureUtilities.GetSigNames: TCryptoLibStringArray;
+var
+  LList: TList<String>;
+  LName: String;
+begin
+  LList := TList<String>.Create();
+  try
+    for LName in FAlgorithms.Keys do
+    begin
+      LList.Add(LName);
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
+end.

+ 387 - 0
CryptoLib/src/Asn1/X509/ClpX509Utilities.pas

@@ -0,0 +1,387 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509Utilities;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Generics.Collections,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX509Certificate,
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpIDigestFactory,
+  ClpIMacFactory,
+  ClpIVerifierFactory,
+  ClpISignatureFactory,
+  ClpPkcsObjectIdentifiers,
+  ClpX9ObjectIdentifiers,
+  ClpNistObjectIdentifiers,
+  ClpTeleTrusTObjectIdentifiers,
+  ClpCryptoProObjectIdentifiers,
+  ClpCryptoLibTypes,
+  ClpArrayUtils,
+  ClpCollectionUtilities;
+
+type
+  /// <summary>
+  /// Internal utilities for X509 operations.
+  /// </summary>
+  TX509Utilities = class sealed(TObject)
+
+  strict private
+    class var
+      FAlgorithms: TDictionary<String, IDerObjectIdentifier>;
+
+    class procedure Boot; static;
+    class constructor Create;
+
+  public
+    class function AreEquivalentAlgorithms(const AId1, AId2: IAlgorithmIdentifier): Boolean; static;
+    class function CalculateDigest(const ADigestAlgorithm: IAlgorithmIdentifier;
+      const AAsn1Encodable: IAsn1Encodable): TCryptoLibByteArray; overload; static;
+    class function CalculateDigest(const ADigestFactory: IDigestFactory;
+      const ABuf: TCryptoLibByteArray): TCryptoLibByteArray; overload; static;
+    class function CalculateDigest(const ADigestFactory: IDigestFactory;
+      const ABuf: TCryptoLibByteArray; AOff, ALen: Int32): TCryptoLibByteArray; overload; static;
+    class function CalculateDigest(const ADigestFactory: IDigestFactory;
+      const AAsn1Encodable: IAsn1Encodable): TCryptoLibByteArray; overload; static;
+    class function CalculateResult<TResult>(const AStreamCalculator: IStreamCalculator<TResult>;
+      const ABuf: TCryptoLibByteArray; AOff, ALen: Int32): TResult; overload; static;
+    class function CalculateResult<TResult>(const AStreamCalculator: IStreamCalculator<TResult>;
+      const AAsn1Encodable: IAsn1Encodable): TResult; overload; static;
+    class function CollectDerBitString(const AResult: IBlockResult): IDerBitString; static;
+    // TODO: CreateIssuerSerial methods require IssuerSerial class which doesn't exist yet
+    // class function CreateIssuerSerial(const ACertificate: IX509Certificate): IIssuerSerial; overload; static;
+    // class function CreateIssuerSerial(const ACertificate: IX509CertificateStructure): IIssuerSerial; overload; static;
+    class function GenerateBitString(const AStreamCalculator: IStreamCalculator<IBlockResult>;
+      const AAsn1Encodable: IAsn1Encodable): IDerBitString; static;
+    class function GenerateDigest(const ADigestFactory: IDigestFactory;
+      const AAsn1Encodable: IAsn1Encodable): IDerBitString; static;
+    class function GenerateMac(const AMacFactory: IMacFactory;
+      const AAsn1Encodable: IAsn1Encodable): IDerBitString; static;
+    class function GenerateSignature(const ASignatureFactory: ISignatureFactory;
+      const AAsn1Encodable: IAsn1Encodable): IDerBitString; static;
+    class function GetAlgNames: TCryptoLibStringArray; static;
+    class function HasAbsentParameters(const AAlgID: IAlgorithmIdentifier): Boolean; static;
+    class function IsAbsentParameters(const AParameters: IAsn1Encodable): Boolean; static;
+    class function VerifyMac(const AMacFactory: IMacFactory;
+      const AAsn1Encodable: IAsn1Encodable; const AExpected: IDerBitString): Boolean; static;
+    class function VerifySignature(const AVerifierFactory: IVerifierFactory;
+      const AAsn1Encodable: IAsn1Encodable; const ASignature: IDerBitString): Boolean; static;
+
+  end;
+
+implementation
+
+uses
+  ClpDigestUtilities,
+  ClpDefaultDigestCalculator,
+  ClpDefaultDigestResult,
+  ClpIDigest,
+  ClpIVerifier,
+  ClpCryptoLibComparers;
+
+{ TX509Utilities }
+
+class constructor TX509Utilities.Create;
+begin
+  Boot;
+end;
+
+class procedure TX509Utilities.Boot;
+begin
+  FAlgorithms := TDictionary<String, IDerObjectIdentifier>.Create(TCryptoLibComparers.OrdinalIgnoreCaseEqualityComparer);
+
+  // MD2 algorithms
+  FAlgorithms.Add('MD2WITHRSAENCRYPTION', TPkcsObjectIdentifiers.MD2WithRsaEncryption);
+  FAlgorithms.Add('MD2WITHRSA', TPkcsObjectIdentifiers.MD2WithRsaEncryption);
+
+  // MD5 algorithms
+  FAlgorithms.Add('MD5WITHRSAENCRYPTION', TPkcsObjectIdentifiers.MD5WithRsaEncryption);
+  FAlgorithms.Add('MD5WITHRSA', TPkcsObjectIdentifiers.MD5WithRsaEncryption);
+
+  // SHA1 algorithms
+  FAlgorithms.Add('SHA1WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA-1WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA1WITHRSA', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+  FAlgorithms.Add('SHA-1WITHRSA', TPkcsObjectIdentifiers.Sha1WithRsaEncryption);
+
+  // SHA224 algorithms
+  FAlgorithms.Add('SHA224WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA-224WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA224WITHRSA', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+  FAlgorithms.Add('SHA-224WITHRSA', TPkcsObjectIdentifiers.Sha224WithRsaEncryption);
+
+  // SHA256 algorithms
+  FAlgorithms.Add('SHA256WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA-256WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA256WITHRSA', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+  FAlgorithms.Add('SHA-256WITHRSA', TPkcsObjectIdentifiers.Sha256WithRsaEncryption);
+
+  // SHA384 algorithms
+  FAlgorithms.Add('SHA384WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA-384WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA384WITHRSA', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+  FAlgorithms.Add('SHA-384WITHRSA', TPkcsObjectIdentifiers.Sha384WithRsaEncryption);
+
+  // SHA512 algorithms
+  FAlgorithms.Add('SHA512WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA-512WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA512WITHRSA', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+  FAlgorithms.Add('SHA-512WITHRSA', TPkcsObjectIdentifiers.Sha512WithRsaEncryption);
+
+  // SHA512(224) algorithms
+  FAlgorithms.Add('SHA512(224)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(224)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA512(224)WITHRSA', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(224)WITHRSA', TPkcsObjectIdentifiers.Sha512_224WithRSAEncryption);
+
+  // SHA512(256) algorithms
+  FAlgorithms.Add('SHA512(256)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(256)WITHRSAENCRYPTION', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA512(256)WITHRSA', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+  FAlgorithms.Add('SHA-512(256)WITHRSA', TPkcsObjectIdentifiers.Sha512_256WithRSAEncryption);
+
+  // RSA-PSS algorithms
+  FAlgorithms.Add('SHA1WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA224WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA256WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA384WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+  FAlgorithms.Add('SHA512WITHRSAANDMGF1', TPkcsObjectIdentifiers.IdRsassaPss);
+
+  // RIPEMD algorithms
+  FAlgorithms.Add('RIPEMD160WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+  FAlgorithms.Add('RIPEMD160WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160);
+  FAlgorithms.Add('RIPEMD128WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+  FAlgorithms.Add('RIPEMD128WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128);
+  FAlgorithms.Add('RIPEMD256WITHRSAENCRYPTION', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+  FAlgorithms.Add('RIPEMD256WITHRSA', TTeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256);
+
+  // DSA algorithms
+  FAlgorithms.Add('SHA1WITHDSA', TX9ObjectIdentifiers.IdDsaWithSha1);
+  FAlgorithms.Add('DSAWITHSHA1', TX9ObjectIdentifiers.IdDsaWithSha1);
+  FAlgorithms.Add('SHA224WITHDSA', TNistObjectIdentifiers.DsaWithSha224);
+  FAlgorithms.Add('SHA256WITHDSA', TNistObjectIdentifiers.DsaWithSha256);
+  FAlgorithms.Add('SHA384WITHDSA', TNistObjectIdentifiers.DsaWithSha384);
+  FAlgorithms.Add('SHA512WITHDSA', TNistObjectIdentifiers.DsaWithSha512);
+
+  // ECDSA algorithms
+  FAlgorithms.Add('SHA1WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha1);
+  FAlgorithms.Add('ECDSAWITHSHA1', TX9ObjectIdentifiers.ECDsaWithSha1);
+  FAlgorithms.Add('SHA224WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha224);
+  FAlgorithms.Add('SHA256WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha256);
+  FAlgorithms.Add('SHA384WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha384);
+  FAlgorithms.Add('SHA512WITHECDSA', TX9ObjectIdentifiers.ECDsaWithSha512);
+
+  // GOST algorithms
+  FAlgorithms.Add('GOST3411WITHGOST3410', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+  FAlgorithms.Add('GOST3411WITHGOST3410-94', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94);
+  FAlgorithms.Add('GOST3411WITHECGOST3410', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+  FAlgorithms.Add('GOST3411WITHECGOST3410-2001', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+  FAlgorithms.Add('GOST3411WITHGOST3410-2001', TCryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001);
+end;
+
+class function TX509Utilities.AreEquivalentAlgorithms(const AId1, AId2: IAlgorithmIdentifier): Boolean;
+begin
+  if not AId1.Algorithm.Equals(AId2.Algorithm) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // TODO Java has a property to control whether absent parameters can match NULL parameters
+  if HasAbsentParameters(AId1) and HasAbsentParameters(AId2) then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  Result := AId1.Parameters.Equals(AId2.Parameters);
+end;
+
+class function TX509Utilities.CalculateDigest(const ADigestAlgorithm: IAlgorithmIdentifier;
+  const AAsn1Encodable: IAsn1Encodable): TCryptoLibByteArray;
+var
+  LDigest: IDigest;
+  LDigestCalculator: IStreamCalculator<IBlockResult>;
+  LDigestResult: IBlockResult;
+begin
+  LDigest := TDigestUtilities.GetDigest(ADigestAlgorithm.Algorithm);
+  LDigestCalculator := TDefaultDigestCalculator.Create(LDigest);
+  LDigestResult := CalculateResult<IBlockResult>(LDigestCalculator, AAsn1Encodable);
+  Result := LDigestResult.Collect();
+end;
+
+class function TX509Utilities.CalculateDigest(const ADigestFactory: IDigestFactory;
+  const ABuf: TCryptoLibByteArray): TCryptoLibByteArray;
+begin
+  Result := CalculateDigest(ADigestFactory, ABuf, 0, System.Length(ABuf));
+end;
+
+class function TX509Utilities.CalculateDigest(const ADigestFactory: IDigestFactory;
+  const ABuf: TCryptoLibByteArray; AOff, ALen: Int32): TCryptoLibByteArray;
+var
+  LDigestCalculator: IStreamCalculator<IBlockResult>;
+  LDigestResult: IBlockResult;
+begin
+  LDigestCalculator := ADigestFactory.CreateCalculator();
+  LDigestResult := CalculateResult<IBlockResult>(LDigestCalculator, ABuf, AOff, ALen);
+  Result := LDigestResult.Collect();
+end;
+
+class function TX509Utilities.CalculateDigest(const ADigestFactory: IDigestFactory;
+  const AAsn1Encodable: IAsn1Encodable): TCryptoLibByteArray;
+var
+  LDigestCalculator: IStreamCalculator<IBlockResult>;
+  LDigestResult: IBlockResult;
+begin
+  LDigestCalculator := ADigestFactory.CreateCalculator();
+  LDigestResult := CalculateResult<IBlockResult>(LDigestCalculator, AAsn1Encodable);
+  Result := LDigestResult.Collect();
+end;
+
+class function TX509Utilities.CalculateResult<TResult>(const AStreamCalculator: IStreamCalculator<TResult>;
+  const ABuf: TCryptoLibByteArray; AOff, ALen: Int32): TResult;
+var
+  LStream: TStream;
+begin
+  LStream := AStreamCalculator.Stream;
+  try
+    LStream.WriteBuffer(ABuf[AOff], ALen);
+  finally
+    LStream.Free;
+  end;
+  Result := AStreamCalculator.GetResult();
+end;
+
+class function TX509Utilities.CalculateResult<TResult>(const AStreamCalculator: IStreamCalculator<TResult>;
+  const AAsn1Encodable: IAsn1Encodable): TResult;
+var
+  LStream: TStream;
+begin
+  LStream := AStreamCalculator.Stream;
+  try
+    AAsn1Encodable.EncodeTo(LStream, TAsn1Encodable.Der);
+  finally
+    LStream.Free;
+  end;
+  Result := AStreamCalculator.GetResult();
+end;
+
+class function TX509Utilities.CollectDerBitString(const AResult: IBlockResult): IDerBitString;
+var
+  LData: TCryptoLibByteArray;
+begin
+  LData := AResult.Collect();
+  Result := TDerBitString.Create(LData);
+end;
+
+// TODO: Implement when IssuerSerial class is available
+// class function TX509Utilities.CreateIssuerSerial(const ACertificate: IX509Certificate): IIssuerSerial;
+// begin
+//   Result := CreateIssuerSerial(ACertificate.CertificateStructure);
+// end;
+//
+// class function TX509Utilities.CreateIssuerSerial(const ACertificate: IX509CertificateStructure): IIssuerSerial;
+// begin
+//   Result := TIssuerSerial.Create(ACertificate.Issuer, ACertificate.SerialNumber);
+// end;
+
+class function TX509Utilities.GenerateBitString(const AStreamCalculator: IStreamCalculator<IBlockResult>;
+  const AAsn1Encodable: IAsn1Encodable): IDerBitString;
+var
+  LResult: IBlockResult;
+begin
+  LResult := CalculateResult<IBlockResult>(AStreamCalculator, AAsn1Encodable);
+  Result := CollectDerBitString(LResult);
+end;
+
+class function TX509Utilities.GenerateDigest(const ADigestFactory: IDigestFactory;
+  const AAsn1Encodable: IAsn1Encodable): IDerBitString;
+begin
+  Result := GenerateBitString(ADigestFactory.CreateCalculator(), AAsn1Encodable);
+end;
+
+class function TX509Utilities.GenerateMac(const AMacFactory: IMacFactory;
+  const AAsn1Encodable: IAsn1Encodable): IDerBitString;
+begin
+  Result := GenerateBitString(AMacFactory.CreateCalculator(), AAsn1Encodable);
+end;
+
+class function TX509Utilities.GenerateSignature(const ASignatureFactory: ISignatureFactory;
+  const AAsn1Encodable: IAsn1Encodable): IDerBitString;
+begin
+  Result := GenerateBitString(ASignatureFactory.CreateCalculator(), AAsn1Encodable);
+end;
+
+class function TX509Utilities.GetAlgNames: TCryptoLibStringArray;
+var
+  LList: TList<String>;
+  LKey: String;
+begin
+  LList := TList<String>.Create();
+  try
+    for LKey in FAlgorithms.Keys do
+    begin
+      LList.Add(LKey);
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
+class function TX509Utilities.HasAbsentParameters(const AAlgID: IAlgorithmIdentifier): Boolean;
+begin
+  Result := IsAbsentParameters(AAlgID.Parameters);
+end;
+
+class function TX509Utilities.IsAbsentParameters(const AParameters: IAsn1Encodable): Boolean;
+begin
+  Result := (AParameters = nil) or TDerNull.Instance.Equals(AParameters);
+end;
+
+class function TX509Utilities.VerifyMac(const AMacFactory: IMacFactory;
+  const AAsn1Encodable: IAsn1Encodable; const AExpected: IDerBitString): Boolean;
+var
+  LResult: TCryptoLibByteArray;
+begin
+  LResult := CalculateResult<IBlockResult>(AMacFactory.CreateCalculator(), AAsn1Encodable).Collect();
+  Result := TArrayUtils.ConstantTimeAreEqual(LResult, AExpected.GetOctets());
+end;
+
+class function TX509Utilities.VerifySignature(const AVerifierFactory: IVerifierFactory;
+  const AAsn1Encodable: IAsn1Encodable; const ASignature: IDerBitString): Boolean;
+var
+  LCalculator: IStreamCalculator<IVerifier>;
+  LResult: IVerifier;
+begin
+  LCalculator := AVerifierFactory.CreateCalculator();
+  LResult := CalculateResult<IVerifier>(LCalculator, AAsn1Encodable);
+  Result := LResult.IsVerified(ASignature.GetOctets());
+end;
+
+end.

+ 223 - 0
CryptoLib/src/Asn1/X9/ClpX9Asn1Objects.pas

@@ -0,0 +1,223 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX9Asn1Objects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX9Asn1Objects,
+  ClpIX9ECParameters,
+  ClpX9ObjectIdentifiers,
+  ClpX9ECC,
+  ClpX9ECParameters,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpCryptoLibTypes,
+  ClpAsn1Utilities;
+
+resourcestring
+  SInvalidParameters = 'Invalid parameters';
+  SBadSequenceSize = 'Bad sequence size: %d';
+  SUnexpectedElementsInSequence = 'Unexpected elements in sequence';
+
+type
+  /// <summary>
+  /// The X962Parameters object.
+  /// </summary>
+  TX962Parameters = class(TAsn1Encodable, IX962Parameters)
+
+  strict private
+  var
+    FParameters: IAsn1Object;
+
+  strict protected
+    function GetParameters: IAsn1Object;
+    function GetNamedCurve: IDerObjectIdentifier;
+    function IsImplicitlyCA: Boolean;
+    function IsNamedCurve: Boolean;
+
+  public
+    class function GetInstance(AObj: TObject): IX962Parameters; overload; static;
+    class function GetInstance(const AObj: IAsn1Object): IX962Parameters; overload; static;
+    class function GetInstance(const AElement: IAsn1Encodable): IX962Parameters; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IX962Parameters; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IX962Parameters; overload; static;
+    class function GetOptional(const AElement: IAsn1Encodable): IX962Parameters; static;
+
+    constructor Create(const AParameters: IAsn1Object); overload;
+    constructor Create(const AParameters: IAsn1Encodable); overload;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property Parameters: IAsn1Object read GetParameters;
+
+  end;
+
+implementation
+
+{ TX962Parameters }
+
+class function TX962Parameters.GetInstance(AObj: TObject): IX962Parameters;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IX962Parameters>(AObj,
+    function(AElement: IAsn1Encodable): IX962Parameters
+    begin
+      Result := GetOptional(AElement);
+    end);
+end;
+
+class function TX962Parameters.GetOptional(const AElement: IAsn1Encodable): IX962Parameters;
+var
+  LECParams: IX9ECParameters;
+  LNamedCurve: IDerObjectIdentifier;
+  LNull: IAsn1Null;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IX962Parameters, Result) then
+    Exit;
+
+  LECParams := TX9ECParameters.GetOptional(AElement);
+  if LECParams <> nil then
+  begin
+    Result := TX962Parameters.Create(LECParams.ToAsn1Object());
+    Exit;
+  end;
+
+  LNamedCurve := TDerObjectIdentifier.GetOptional(AElement);
+  if LNamedCurve <> nil then
+  begin
+    Result := TX962Parameters.Create(LNamedCurve);
+    Exit;
+  end;
+
+  LNull := TAsn1Null.GetOptional(AElement);
+  if LNull <> nil then
+  begin
+    Result := TX962Parameters.Create(LNull);
+    Exit;
+  end;
+
+  Result := nil;
+end;
+
+class function TX962Parameters.GetInstance(const AObj: IAsn1Object): IX962Parameters;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  if Supports(AObj, IX962Parameters, Result) then
+    Exit;
+
+  Result := GetOptional(AObj as IAsn1Encodable);
+end;
+
+class function TX962Parameters.GetInstance(const AElement: IAsn1Encodable): IX962Parameters;
+begin
+  if AElement = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  Result := GetOptional(AElement);
+  if Result = nil then
+    raise EArgumentCryptoLibException.Create('unable to parse X962Parameters');
+end;
+
+class function TX962Parameters.GetInstance(const AEncoded: TCryptoLibByteArray): IX962Parameters;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  try
+    LAsn1Obj := TAsn1Object.FromByteArray(AEncoded);
+    Result := GetInstance(LAsn1Obj);
+  except
+    on E: EIOCryptoLibException do
+      raise EArgumentCryptoLibException.Create('failed to construct X962Parameters from byte[]: ' + E.Message);
+  end;
+end;
+
+class function TX962Parameters.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IX962Parameters;
+begin
+  Result := TAsn1Utilities.GetInstanceChoice<IX962Parameters>(AObj, AExplicitly,
+    function(AElement: IAsn1Encodable): IX962Parameters
+    begin
+      Result := GetInstance(AElement.ToAsn1Object());
+    end);
+end;
+
+constructor TX962Parameters.Create(const AParameters: IAsn1Object);
+begin
+  inherited Create();
+  FParameters := AParameters;
+end;
+
+constructor TX962Parameters.Create(const AParameters: IAsn1Encodable);
+begin
+  Create(AParameters.ToAsn1Object());
+end;
+
+function TX962Parameters.GetParameters: IAsn1Object;
+begin
+  Result := FParameters;
+end;
+
+function TX962Parameters.IsImplicitlyCA: Boolean;
+var
+  LNull: IAsn1Null;
+begin
+  // IsImplicitlyCA => m_params is Asn1Null
+  // Check if FParameters is an Asn1Null instance (not nil check)
+  Result := Supports(FParameters, IAsn1Null, LNull);
+end;
+
+function TX962Parameters.GetNamedCurve: IDerObjectIdentifier;
+begin
+  if not Supports(FParameters, IDerObjectIdentifier, Result) then
+    Result := nil;
+end;
+
+function TX962Parameters.IsNamedCurve: Boolean;
+begin
+  Result := GetNamedCurve <> nil;
+end;
+
+function TX962Parameters.ToAsn1Object: IAsn1Object;
+begin
+  Result := FParameters;
+end;
+
+end.

+ 19 - 0
CryptoLib/src/Asn1/X9/ClpX9ECParameters.pas

@@ -90,6 +90,8 @@ type
 
     class function GetInstance(obj: TObject): IX9ECParameters; static;
 
+    class function GetOptional(const AElement: IAsn1Encodable): IX9ECParameters; static;
+
   end;
 
 implementation
@@ -252,6 +254,23 @@ begin
   Result := Nil;
 end;
 
+class function TX9ECParameters.GetOptional(const AElement: IAsn1Encodable): IX9ECParameters;
+var
+  LSequence: IAsn1Sequence;
+begin
+  if AElement = nil then
+    raise EArgumentNilCryptoLibException.Create('element');
+
+  if Supports(AElement, IX9ECParameters, Result) then
+    Exit;
+
+  LSequence := TAsn1Sequence.GetOptional(AElement);
+  if LSequence <> nil then
+    Result := TX9ECParameters.Create(LSequence)
+  else
+    Result := nil;
+end;
+
 function TX9ECParameters.GetN: TBigInteger;
 begin
   Result := Fn;

+ 119 - 0
CryptoLib/src/Crypto/IO/ClpDigestSink.pas

@@ -0,0 +1,119 @@
+{ *********************************************************************************** }
+{ *                              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 ClpDigestSink;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIDigest,
+  ClpStreams,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// A stream that writes data to an IDigest for digest calculation.
+  /// </summary>
+  TDigestSink = class sealed(TBaseStream)
+
+  strict private
+  var
+    FDigest: IDigest;
+
+  protected
+    function GetSize: Int64; override;
+    function GetCanRead: Boolean; override;
+    function GetCanSeek: Boolean; override;
+    function GetCanWrite: Boolean; override;
+
+  public
+    constructor Create(const ADigest: IDigest);
+
+    function Read(var ABuffer; ACount: LongInt): LongInt; override;
+    function Write(const ABuffer; ACount: LongInt): LongInt; override;
+    function Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override;
+    procedure SetSize(const ANewSize: Int64); override;
+
+    property Digest: IDigest read FDigest;
+  end;
+
+implementation
+
+{ TDigestSink }
+
+constructor TDigestSink.Create(const ADigest: IDigest);
+begin
+  inherited Create();
+  if ADigest = nil then
+    raise EArgumentNilCryptoLibException.Create('digest');
+  FDigest := ADigest;
+end;
+
+function TDigestSink.GetSize: Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('GetSize not supported');
+end;
+
+function TDigestSink.GetCanRead: Boolean;
+begin
+  Result := False;
+end;
+
+function TDigestSink.GetCanSeek: Boolean;
+begin
+  Result := False;
+end;
+
+function TDigestSink.GetCanWrite: Boolean;
+begin
+  Result := True;
+end;
+
+function TDigestSink.Read(var ABuffer; ACount: LongInt): LongInt;
+begin
+  raise ENotSupportedCryptoLibException.Create('Read not supported');
+end;
+
+function TDigestSink.Write(const ABuffer; ACount: LongInt): LongInt;
+var
+  LBuffer: TCryptoLibByteArray;
+  LOffset: Int32;
+begin
+  if ACount > 0 then
+  begin
+    System.SetLength(LBuffer, ACount);
+    System.Move(ABuffer, LBuffer[0], ACount);
+    LOffset := 0;
+    FDigest.BlockUpdate(LBuffer, LOffset, ACount);
+  end;
+  Result := ACount;
+end;
+
+function TDigestSink.Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('Seek not supported');
+end;
+
+procedure TDigestSink.SetSize(const ANewSize: Int64);
+begin
+  raise ENotSupportedCryptoLibException.Create('SetSize not supported');
+end;
+
+end.

+ 119 - 0
CryptoLib/src/Crypto/IO/ClpSignerSink.pas

@@ -0,0 +1,119 @@
+{ *********************************************************************************** }
+{ *                              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 ClpSignerSink;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpISigner,
+  ClpStreams,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// A stream that writes data to an ISigner for signature calculation.
+  /// </summary>
+  TSignerSink = class sealed(TBaseStream)
+
+  strict private
+  var
+    FSigner: ISigner;
+
+  protected
+    function GetSize: Int64; override;
+    function GetCanRead: Boolean; override;
+    function GetCanSeek: Boolean; override;
+    function GetCanWrite: Boolean; override;
+
+  public
+    constructor Create(const ASigner: ISigner);
+
+    function Read(var ABuffer; ACount: LongInt): LongInt; override;
+    function Write(const ABuffer; ACount: LongInt): LongInt; override;
+    function Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64; override;
+    procedure SetSize(const ANewSize: Int64); override;
+
+    property Signer: ISigner read FSigner;
+  end;
+
+implementation
+
+{ TSignerSink }
+
+constructor TSignerSink.Create(const ASigner: ISigner);
+begin
+  inherited Create();
+  if ASigner = nil then
+    raise EArgumentNilCryptoLibException.Create('signer');
+  FSigner := ASigner;
+end;
+
+function TSignerSink.GetSize: Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('GetSize not supported');
+end;
+
+function TSignerSink.GetCanRead: Boolean;
+begin
+  Result := False;
+end;
+
+function TSignerSink.GetCanSeek: Boolean;
+begin
+  Result := False;
+end;
+
+function TSignerSink.GetCanWrite: Boolean;
+begin
+  Result := True;
+end;
+
+function TSignerSink.Read(var ABuffer; ACount: LongInt): LongInt;
+begin
+  raise ENotSupportedCryptoLibException.Create('Read not supported');
+end;
+
+function TSignerSink.Write(const ABuffer; ACount: LongInt): LongInt;
+var
+  LBuffer: TCryptoLibByteArray;
+  LOffset: Int32;
+begin
+  if ACount > 0 then
+  begin
+    System.SetLength(LBuffer, ACount);
+    System.Move(ABuffer, LBuffer[0], ACount);
+    LOffset := 0;
+    FSigner.BlockUpdate(LBuffer, LOffset, ACount);
+  end;
+  Result := ACount;
+end;
+
+function TSignerSink.Seek(const AOffset: Int64; AOrigin: TSeekOrigin): Int64;
+begin
+  raise ENotSupportedCryptoLibException.Create('Seek not supported');
+end;
+
+procedure TSignerSink.SetSize(const ANewSize: Int64);
+begin
+  raise ENotSupportedCryptoLibException.Create('SetSize not supported');
+end;
+
+end.

+ 100 - 0
CryptoLib/src/Crypto/Operators/ClpAsn1DigestFactory.pas

@@ -0,0 +1,100 @@
+{ *********************************************************************************** }
+{ *                              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 ClpAsn1DigestFactory;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIDigestFactory,
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpIDigest,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpIAsn1Objects,
+  ClpDigestUtilities,
+  ClpDefaultDigestCalculator,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Digest factory for ASN.1 based operations.
+  /// </summary>
+  TAsn1DigestFactory = class sealed(TInterfacedObject, IDigestFactory)
+
+  strict private
+  var
+    FDigest: IDigest;
+    FOid: IDerObjectIdentifier;
+
+  public
+    class function Get(const AOid: IDerObjectIdentifier): IDigestFactory; overload; static;
+    class function Get(const AMechanism: String): IDigestFactory; overload; static;
+
+    constructor Create(const ADigest: IDigest; const AOid: IDerObjectIdentifier);
+
+    function GetAlgorithmDetails: IAlgorithmIdentifier;
+    function GetDigestLength: Int32;
+    function CreateCalculator: IStreamCalculator<IBlockResult>;
+
+    property AlgorithmDetails: IAlgorithmIdentifier read GetAlgorithmDetails;
+    property DigestLength: Int32 read GetDigestLength;
+  end;
+
+implementation
+
+{ TAsn1DigestFactory }
+
+class function TAsn1DigestFactory.Get(const AOid: IDerObjectIdentifier): IDigestFactory;
+var
+  LDigest: IDigest;
+begin
+  LDigest := TDigestUtilities.GetDigest(AOid);
+  Result := TAsn1DigestFactory.Create(LDigest, AOid);
+end;
+
+class function TAsn1DigestFactory.Get(const AMechanism: String): IDigestFactory;
+begin
+  Result := Get(TDigestUtilities.GetObjectIdentifier(AMechanism));
+end;
+
+constructor TAsn1DigestFactory.Create(const ADigest: IDigest; const AOid: IDerObjectIdentifier);
+begin
+  inherited Create();
+  FDigest := ADigest;
+  FOid := AOid;
+end;
+
+function TAsn1DigestFactory.GetAlgorithmDetails: IAlgorithmIdentifier;
+begin
+  Result := TAlgorithmIdentifier.Create(FOid);
+end;
+
+function TAsn1DigestFactory.GetDigestLength: Int32;
+begin
+  Result := FDigest.GetDigestSize();
+end;
+
+function TAsn1DigestFactory.CreateCalculator: IStreamCalculator<IBlockResult>;
+begin
+  Result := TDefaultDigestCalculator.Create(FDigest);
+end;
+
+end.

+ 144 - 0
CryptoLib/src/Crypto/Operators/ClpAsn1SignatureFactory.pas

@@ -0,0 +1,144 @@
+{ *********************************************************************************** }
+{ *                              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 ClpAsn1SignatureFactory;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpISignatureFactory,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpISecureRandom,
+  ClpIBlockResult,
+  ClpIStreamCalculator,
+  ClpISigner,
+  ClpX509SignatureUtilities,
+  ClpSignerUtilities,
+  ClpDefaultSignatureCalculator,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Calculator factory class for signature generation in ASN.1 based profiles.
+  /// </summary>
+  TAsn1SignatureFactory = class sealed(TInterfacedObject, ISignatureFactory)
+
+  strict private
+  var
+    FAlgID: IAlgorithmIdentifier;
+    FAlgorithm: String;
+    FPrivateKey: IAsymmetricKeyParameter;
+    FRandom: ISecureRandom;
+
+  strict protected
+    function GetAlgorithmDetails: TObject;
+
+  public
+    constructor Create(const AAlgorithm: String;
+      const APrivateKey: IAsymmetricKeyParameter); overload;
+    constructor Create(const AAlgorithm: String;
+      const APrivateKey: IAsymmetricKeyParameter;
+      const ARandom: ISecureRandom); overload;
+    constructor Create(const AAlgorithm: IAlgorithmIdentifier;
+      const APrivateKey: IAsymmetricKeyParameter); overload;
+    constructor Create(const AAlgorithm: IAlgorithmIdentifier;
+      const APrivateKey: IAsymmetricKeyParameter;
+      const ARandom: ISecureRandom); overload;
+
+    function CreateCalculator: IStreamCalculator<IBlockResult>;
+
+    property AlgorithmDetails: TObject read GetAlgorithmDetails;
+
+    /// <summary>
+    /// Allows enumeration of the signature names supported.
+    /// </summary>
+    class function SignatureAlgNames: TCryptoLibStringArray; static;
+  end;
+
+implementation
+
+{ TAsn1SignatureFactory }
+
+constructor TAsn1SignatureFactory.Create(const AAlgorithm: String;
+  const APrivateKey: IAsymmetricKeyParameter);
+begin
+  Create(AAlgorithm, APrivateKey, nil);
+end;
+
+constructor TAsn1SignatureFactory.Create(const AAlgorithm: String;
+  const APrivateKey: IAsymmetricKeyParameter; const ARandom: ISecureRandom);
+begin
+  inherited Create();
+  if AAlgorithm = '' then
+    raise EArgumentNilCryptoLibException.Create('algorithm');
+  if APrivateKey = nil then
+    raise EArgumentNilCryptoLibException.Create('privateKey');
+  if not APrivateKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Key for signing must be private');
+
+  FAlgID := TX509SignatureUtilities.GetSigAlgID(AAlgorithm);
+  FAlgorithm := AAlgorithm;
+  FPrivateKey := APrivateKey;
+  FRandom := ARandom;
+end;
+
+constructor TAsn1SignatureFactory.Create(const AAlgorithm: IAlgorithmIdentifier;
+  const APrivateKey: IAsymmetricKeyParameter);
+begin
+  Create(AAlgorithm, APrivateKey, nil);
+end;
+
+constructor TAsn1SignatureFactory.Create(const AAlgorithm: IAlgorithmIdentifier;
+  const APrivateKey: IAsymmetricKeyParameter; const ARandom: ISecureRandom);
+begin
+  inherited Create();
+  if AAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create('algorithm');
+  if APrivateKey = nil then
+    raise EArgumentNilCryptoLibException.Create('privateKey');
+  if not APrivateKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Key for signing must be private');
+
+  FAlgID := AAlgorithm;
+  FAlgorithm := TX509SignatureUtilities.GetSignatureName(AAlgorithm);
+  FPrivateKey := APrivateKey;
+  FRandom := ARandom;
+end;
+
+function TAsn1SignatureFactory.GetAlgorithmDetails: TObject;
+begin
+  Result := FAlgID as TObject;
+end;
+
+function TAsn1SignatureFactory.CreateCalculator: IStreamCalculator<IBlockResult>;
+var
+  LSigner: ISigner;
+begin
+  LSigner := TSignerUtilities.InitSigner(FAlgorithm, True, FPrivateKey, FRandom);
+  Result := TDefaultSignatureCalculator.Create(LSigner);
+end;
+
+class function TAsn1SignatureFactory.SignatureAlgNames: TCryptoLibStringArray;
+begin
+  Result := TX509SignatureUtilities.GetSigNames();
+end;
+
+end.

+ 112 - 0
CryptoLib/src/Crypto/Operators/ClpAsn1VerifierFactory.pas

@@ -0,0 +1,112 @@
+{ *********************************************************************************** }
+{ *                              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 ClpAsn1VerifierFactory;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIVerifierFactory,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpIVerifier,
+  ClpIStreamCalculator,
+  ClpISigner,
+  ClpX509SignatureUtilities,
+  ClpSignerUtilities,
+  ClpDefaultVerifierCalculator,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Verifier class for signature verification in ASN.1 based profiles.
+  /// </summary>
+  TAsn1VerifierFactory = class sealed(TInterfacedObject, IVerifierFactory)
+
+  strict private
+  var
+    FAlgID: IAlgorithmIdentifier;
+    FAlgorithm: String;
+    FPublicKey: IAsymmetricKeyParameter;
+
+  strict protected
+    function GetAlgorithmDetails: TObject;
+
+  public
+    constructor Create(const AAlgorithm: String;
+      const APublicKey: IAsymmetricKeyParameter); overload;
+    constructor Create(const AAlgorithm: IAlgorithmIdentifier;
+      const APublicKey: IAsymmetricKeyParameter); overload;
+
+    function CreateCalculator: IStreamCalculator<IVerifier>;
+
+    property AlgorithmDetails: TObject read GetAlgorithmDetails;
+  end;
+
+implementation
+
+{ TAsn1VerifierFactory }
+
+constructor TAsn1VerifierFactory.Create(const AAlgorithm: String;
+  const APublicKey: IAsymmetricKeyParameter);
+begin
+  inherited Create();
+  if AAlgorithm = '' then
+    raise EArgumentNilCryptoLibException.Create('algorithm');
+  if APublicKey = nil then
+    raise EArgumentNilCryptoLibException.Create('publicKey');
+  if APublicKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Key for verifying must be public');
+
+  FAlgID := TX509SignatureUtilities.GetSigAlgID(AAlgorithm);
+  FAlgorithm := AAlgorithm;
+  FPublicKey := APublicKey;
+end;
+
+constructor TAsn1VerifierFactory.Create(const AAlgorithm: IAlgorithmIdentifier;
+  const APublicKey: IAsymmetricKeyParameter);
+begin
+  inherited Create();
+  if AAlgorithm = nil then
+    raise EArgumentNilCryptoLibException.Create('algorithm');
+  if APublicKey = nil then
+    raise EArgumentNilCryptoLibException.Create('publicKey');
+  if APublicKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Key for verifying must be public');
+
+  FAlgID := AAlgorithm;
+  FAlgorithm := TX509SignatureUtilities.GetSignatureName(AAlgorithm);
+  FPublicKey := APublicKey;
+end;
+
+function TAsn1VerifierFactory.GetAlgorithmDetails: TObject;
+begin
+  Result := FAlgID as TObject;
+end;
+
+function TAsn1VerifierFactory.CreateCalculator: IStreamCalculator<IVerifier>;
+var
+  LSigner: ISigner;
+begin
+  LSigner := TSignerUtilities.InitSigner(FAlgorithm, False, FPublicKey, nil);
+  Result := TDefaultVerifierCalculator.Create(LSigner);
+end;
+
+end.

+ 86 - 0
CryptoLib/src/Crypto/Operators/ClpAsn1VerifierFactoryProvider.pas

@@ -0,0 +1,86 @@
+{ *********************************************************************************** }
+{ *                              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 ClpAsn1VerifierFactoryProvider;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIVerifierFactoryProvider,
+  ClpIVerifierFactory,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpAsn1VerifierFactory,
+  ClpX509SignatureUtilities,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Provider class which supports dynamic creation of signature verifiers.
+  /// </summary>
+  TAsn1VerifierFactoryProvider = class sealed(TInterfacedObject, IVerifierFactoryProvider)
+
+  strict private
+  var
+    FPublicKey: IAsymmetricKeyParameter;
+
+  public
+    constructor Create(const APublicKey: IAsymmetricKeyParameter);
+
+    function CreateVerifierFactory(AAlgorithmDetails: TObject): IVerifierFactory;
+
+    /// <summary>
+    /// Allows enumeration of the signature names supported by the verifier provider.
+    /// </summary>
+    function SignatureAlgNames: TCryptoLibStringArray;
+  end;
+
+implementation
+
+{ TAsn1VerifierFactoryProvider }
+
+constructor TAsn1VerifierFactoryProvider.Create(const APublicKey: IAsymmetricKeyParameter);
+begin
+  inherited Create();
+  if APublicKey = nil then
+    raise EArgumentNilCryptoLibException.Create('publicKey');
+  if APublicKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Key for verifying must be public');
+
+  FPublicKey := APublicKey;
+end;
+
+function TAsn1VerifierFactoryProvider.CreateVerifierFactory(AAlgorithmDetails: TObject): IVerifierFactory;
+var
+  LAlgID: IAlgorithmIdentifier;
+begin
+  if not Supports(AAlgorithmDetails, IAlgorithmIdentifier, LAlgID) then
+    raise EInvalidCastCryptoLibException.Create('algorithmDetails must be IAlgorithmIdentifier');
+
+  Result := TAsn1VerifierFactory.Create(LAlgID, FPublicKey);
+end;
+
+function TAsn1VerifierFactoryProvider.SignatureAlgNames: TCryptoLibStringArray;
+begin
+  Result := TX509SignatureUtilities.GetSigNames();
+end;
+
+end.

+ 78 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultDigestCalculator.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 ClpDefaultDigestCalculator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpIDigest,
+  ClpDigestSink,
+  ClpDefaultDigestResult;
+
+type
+  /// <summary>
+  /// Default implementation of IStreamCalculator for digest operations.
+  /// </summary>
+  TDefaultDigestCalculator = class sealed(TInterfacedObject, IStreamCalculator<IBlockResult>)
+
+  strict private
+  var
+    FDigestSink: TDigestSink;
+
+  public
+    constructor Create(const ADigest: IDigest);
+    destructor Destroy; override;
+
+    function GetStream: TStream;
+    function GetResult: IBlockResult;
+
+    property Stream: TStream read GetStream;
+  end;
+
+implementation
+
+{ TDefaultDigestCalculator }
+
+constructor TDefaultDigestCalculator.Create(const ADigest: IDigest);
+begin
+  inherited Create();
+  FDigestSink := TDigestSink.Create(ADigest);
+end;
+
+destructor TDefaultDigestCalculator.Destroy;
+begin
+  FDigestSink.Free;
+  inherited Destroy;
+end;
+
+function TDefaultDigestCalculator.GetStream: TStream;
+begin
+  Result := FDigestSink;
+end;
+
+function TDefaultDigestCalculator.GetResult: IBlockResult;
+begin
+  Result := TDefaultDigestResult.Create(FDigestSink.Digest);
+end;
+
+end.

+ 73 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultDigestResult.pas

@@ -0,0 +1,73 @@
+{ *********************************************************************************** }
+{ *                              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 ClpDefaultDigestResult;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockResult,
+  ClpIDigest,
+  ClpDigestUtilities,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Default implementation of IBlockResult for digest operations.
+  /// </summary>
+  TDefaultDigestResult = class sealed(TInterfacedObject, IBlockResult)
+
+  strict private
+  var
+    FDigest: IDigest;
+
+  public
+    constructor Create(const ADigest: IDigest);
+
+    function Collect: TCryptoLibByteArray; overload;
+    function Collect(const ABuf: TCryptoLibByteArray; AOff: Int32): Int32; overload;
+    function GetMaxResultLength: Int32;
+  end;
+
+implementation
+
+{ TDefaultDigestResult }
+
+constructor TDefaultDigestResult.Create(const ADigest: IDigest);
+begin
+  inherited Create();
+  FDigest := ADigest;
+end;
+
+function TDefaultDigestResult.Collect: TCryptoLibByteArray;
+begin
+  Result := TDigestUtilities.DoFinal(FDigest);
+end;
+
+function TDefaultDigestResult.Collect(const ABuf: TCryptoLibByteArray; AOff: Int32): Int32;
+begin
+  Result := FDigest.DoFinal(ABuf, AOff);
+end;
+
+function TDefaultDigestResult.GetMaxResultLength: Int32;
+begin
+  Result := FDigest.GetDigestSize();
+end;
+
+end.

+ 73 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultSignatureCalculator.pas

@@ -0,0 +1,73 @@
+{ *********************************************************************************** }
+{ *                              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.           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpDefaultSignatureCalculator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpISigner,
+  ClpSignerSink,
+  ClpDefaultSignatureResult;
+
+type
+  /// <summary>
+  /// Default implementation of IStreamCalculator for signature operations.
+  /// </summary>
+  TDefaultSignatureCalculator = class sealed(TInterfacedObject, IStreamCalculator<IBlockResult>)
+
+  strict private
+  var
+    FSignerSink: TSignerSink;
+
+  public
+    constructor Create(const ASigner: ISigner);
+    destructor Destroy; override;
+
+    function GetStream: TStream;
+    function GetResult: IBlockResult;
+
+    property Stream: TStream read GetStream;
+  end;
+
+implementation
+
+{ TDefaultSignatureCalculator }
+
+constructor TDefaultSignatureCalculator.Create(const ASigner: ISigner);
+begin
+  inherited Create();
+  FSignerSink := TSignerSink.Create(ASigner);
+end;
+
+destructor TDefaultSignatureCalculator.Destroy;
+begin
+  FSignerSink.Free;
+  inherited Destroy;
+end;
+
+function TDefaultSignatureCalculator.GetStream: TStream;
+begin
+  Result := FSignerSink;
+end;
+
+function TDefaultSignatureCalculator.GetResult: IBlockResult;
+begin
+  Result := TDefaultSignatureResult.Create(FSignerSink.Signer);
+end;
+
+end.

+ 76 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultSignatureResult.pas

@@ -0,0 +1,76 @@
+{ *********************************************************************************** }
+{ *                              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 ClpDefaultSignatureResult;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockResult,
+  ClpISigner,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Default implementation of IBlockResult for signature operations.
+  /// </summary>
+  TDefaultSignatureResult = class sealed(TInterfacedObject, IBlockResult)
+
+  strict private
+  var
+    FSigner: ISigner;
+
+  public
+    constructor Create(const ASigner: ISigner);
+
+    function Collect: TCryptoLibByteArray; overload;
+    function Collect(const ABuf: TCryptoLibByteArray; AOff: Int32): Int32; overload;
+    function GetMaxResultLength: Int32;
+  end;
+
+implementation
+
+{ TDefaultSignatureResult }
+
+constructor TDefaultSignatureResult.Create(const ASigner: ISigner);
+begin
+  inherited Create();
+  FSigner := ASigner;
+end;
+
+function TDefaultSignatureResult.Collect: TCryptoLibByteArray;
+begin
+  Result := FSigner.GenerateSignature();
+end;
+
+function TDefaultSignatureResult.Collect(const ABuf: TCryptoLibByteArray; AOff: Int32): Int32;
+var
+  LSignature: TCryptoLibByteArray;
+begin
+  LSignature := Collect();
+  System.Move(LSignature[0], ABuf[AOff], System.Length(LSignature));
+  Result := System.Length(LSignature);
+end;
+
+function TDefaultSignatureResult.GetMaxResultLength: Int32;
+begin
+  Result := FSigner.GetMaxSignatureSize();
+end;
+
+end.

+ 78 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultVerifierCalculator.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 ClpDefaultVerifierCalculator;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIStreamCalculator,
+  ClpIVerifier,
+  ClpISigner,
+  ClpSignerSink,
+  ClpDefaultVerifierResult;
+
+type
+  /// <summary>
+  /// Default implementation of IStreamCalculator for verifier operations.
+  /// </summary>
+  TDefaultVerifierCalculator = class sealed(TInterfacedObject, IStreamCalculator<IVerifier>)
+
+  strict private
+  var
+    FSignerSink: TSignerSink;
+
+  public
+    constructor Create(const ASigner: ISigner);
+    destructor Destroy; override;
+
+    function GetStream: TStream;
+    function GetResult: IVerifier;
+
+    property Stream: TStream read GetStream;
+  end;
+
+implementation
+
+{ TDefaultVerifierCalculator }
+
+constructor TDefaultVerifierCalculator.Create(const ASigner: ISigner);
+begin
+  inherited Create();
+  FSignerSink := TSignerSink.Create(ASigner);
+end;
+
+destructor TDefaultVerifierCalculator.Destroy;
+begin
+  FSignerSink.Free;
+  inherited Destroy;
+end;
+
+function TDefaultVerifierCalculator.GetStream: TStream;
+begin
+  Result := FSignerSink;
+end;
+
+function TDefaultVerifierCalculator.GetResult: IVerifier;
+begin
+  Result := TDefaultVerifierResult.Create(FSignerSink.Signer);
+end;
+
+end.

+ 70 - 0
CryptoLib/src/Crypto/Operators/ClpDefaultVerifierResult.pas

@@ -0,0 +1,70 @@
+{ *********************************************************************************** }
+{ *                              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 ClpDefaultVerifierResult;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIVerifier,
+  ClpISigner,
+  ClpCryptoLibTypes,
+  ClpArrayUtils;
+
+type
+  /// <summary>
+  /// Default implementation of IVerifier for signature verification operations.
+  /// </summary>
+  TDefaultVerifierResult = class sealed(TInterfacedObject, IVerifier)
+
+  strict private
+  var
+    FSigner: ISigner;
+
+  public
+    constructor Create(const ASigner: ISigner);
+
+    function IsVerified(const AData: TCryptoLibByteArray): Boolean; overload;
+    function IsVerified(const ASource: TCryptoLibByteArray; AOff, ALength: Int32): Boolean; overload;
+  end;
+
+implementation
+
+{ TDefaultVerifierResult }
+
+constructor TDefaultVerifierResult.Create(const ASigner: ISigner);
+begin
+  inherited Create();
+  FSigner := ASigner;
+end;
+
+function TDefaultVerifierResult.IsVerified(const AData: TCryptoLibByteArray): Boolean;
+begin
+  Result := FSigner.VerifySignature(AData);
+end;
+
+function TDefaultVerifierResult.IsVerified(const ASource: TCryptoLibByteArray; AOff, ALength: Int32): Boolean;
+var
+  LSignature: TCryptoLibByteArray;
+begin
+  LSignature := TArrayUtils.CopyOfRange(ASource, AOff, AOff + ALength);
+  Result := FSigner.VerifySignature(LSignature);
+end;
+
+end.

+ 15 - 2
CryptoLib/src/Crypto/Signers/ClpDsaDigestSigner.pas

@@ -28,6 +28,7 @@ uses
   ClpIDsaExt,
   ClpIDigest,
   ClpBigInteger,
+  ClpBigIntegers,
   ClpCryptoLibTypes,
   ClpIParametersWithRandom,
   ClpSignersEncodings,
@@ -82,6 +83,11 @@ type
     procedure BlockUpdate(const input: TCryptoLibByteArray;
       inOff, length: Int32); virtual;
 
+    /// <summary>
+    /// Return the maximum size for a signature produced by this signer.
+    /// </summary>
+    function GetMaxSignatureSize: Int32; virtual;
+
     /// <summary>
     /// Generate a signature for the message we've been loaded with using the
     /// key we were initialised with.
@@ -140,7 +146,8 @@ begin
       (@SDsaDigestSignerNotInitializedForSignatureGeneration);
   end;
 
-  hash := Fdigest.GetUnderlyingIHash.TransformFinal().GetBytes();
+  SetLength(hash, Fdigest.GetDigestSize());
+  Fdigest.DoFinal(hash, 0);
 
   sig := Fdsa.GenerateSignature(hash);
 
@@ -168,6 +175,11 @@ begin
   end;
 end;
 
+function TDsaDigestSigner.GetMaxSignatureSize: Int32;
+begin
+  Result := Fencoding.GetMaxEncodingSize(GetOrder());
+end;
+
 procedure TDsaDigestSigner.Init(forSigning: Boolean;
   const parameters: ICipherParameters);
 var
@@ -222,7 +234,8 @@ begin
       (@SDsaDigestSignerNotInitializedForVerification);
   end;
 
-  hash := Fdigest.GetUnderlyingIHash.TransformFinal().GetBytes();
+  SetLength(hash, Fdigest.GetDigestSize());
+  Fdigest.DoFinal(hash, 0);
 
   try
     sig := Fencoding.Decode(GetOrder(), signature);

+ 6 - 0
CryptoLib/src/Crypto/Signers/ClpEd25519CtxSigner.pas

@@ -65,6 +65,7 @@ type
     procedure Update(b: Byte); virtual;
     procedure BlockUpdate(const buf: TCryptoLibByteArray;
       off, len: Int32); virtual;
+    function GetMaxSignatureSize: Int32; virtual;
     function GenerateSignature(): TCryptoLibByteArray; virtual;
     function VerifySignature(const signature: TCryptoLibByteArray)
       : Boolean; virtual;
@@ -150,6 +151,11 @@ begin
   FBuffer.Write(TCryptoLibByteArray.Create(b)[0], 1);
 end;
 
+function TEd25519CtxSigner.GetMaxSignatureSize: Int32;
+begin
+  Result := TEd25519.SignatureSize;
+end;
+
 function TEd25519CtxSigner.GenerateSignature: TCryptoLibByteArray;
 var
   signature, buf: TCryptoLibByteArray;

+ 6 - 0
CryptoLib/src/Crypto/Signers/ClpEd25519PhSigner.pas

@@ -64,6 +64,7 @@ type
     procedure Update(b: Byte); virtual;
     procedure BlockUpdate(const buf: TCryptoLibByteArray;
       off, len: Int32); virtual;
+    function GetMaxSignatureSize: Int32; virtual;
     function GenerateSignature(): TCryptoLibByteArray; virtual;
     function VerifySignature(const signature: TCryptoLibByteArray)
       : Boolean; virtual;
@@ -133,6 +134,11 @@ begin
   FPreHash.Update(b);
 end;
 
+function TEd25519PhSigner.GetMaxSignatureSize: Int32;
+begin
+  Result := TEd25519.SignatureSize;
+end;
+
 function TEd25519PhSigner.GenerateSignature: TCryptoLibByteArray;
 var
   signature, msg: TCryptoLibByteArray;

+ 6 - 0
CryptoLib/src/Crypto/Signers/ClpEd25519Signer.pas

@@ -63,6 +63,7 @@ type
     procedure Update(b: Byte); virtual;
     procedure BlockUpdate(const buf: TCryptoLibByteArray;
       off, len: Int32); virtual;
+    function GetMaxSignatureSize: Int32; virtual;
     function GenerateSignature(): TCryptoLibByteArray; virtual;
     function VerifySignature(const signature: TCryptoLibByteArray)
       : Boolean; virtual;
@@ -146,6 +147,11 @@ begin
   FBuffer.Write(TCryptoLibByteArray.Create(b)[0], 1);
 end;
 
+function TEd25519Signer.GetMaxSignatureSize: Int32;
+begin
+  Result := TEd25519.SignatureSize;
+end;
+
 function TEd25519Signer.GenerateSignature: TCryptoLibByteArray;
 var
   signature, buf: TCryptoLibByteArray;

+ 6 - 0
CryptoLib/src/Crypto/Signers/ClpGenericSigner.pas

@@ -63,6 +63,7 @@ type
     procedure Update(input: Byte);
     procedure BlockUpdate(const input: TCryptoLibByteArray;
       inOff, len: Int32);
+    function GetMaxSignatureSize: Int32;
     function GenerateSignature: TCryptoLibByteArray;
     function VerifySignature(const signature: TCryptoLibByteArray): Boolean;
     procedure Reset;
@@ -129,6 +130,11 @@ begin
   FDigest.BlockUpdate(input, inOff, len);
 end;
 
+function TGenericSigner.GetMaxSignatureSize: Int32;
+begin
+  Result := FEngine.OutputBlockSize;
+end;
+
 function TGenericSigner.GenerateSignature: TCryptoLibByteArray;
 var
   hash: TCryptoLibByteArray;

+ 7 - 2
CryptoLib/src/Crypto/Signers/ClpPssSigner.pas

@@ -132,6 +132,7 @@ type
     procedure Update(input: Byte);
     procedure BlockUpdate(const input: TCryptoLibByteArray;
       inOff, len: Int32);
+    function GetMaxSignatureSize: Int32;
     function GenerateSignature: TCryptoLibByteArray;
     function VerifySignature(const signature: TCryptoLibByteArray): Boolean;
     procedure Reset;
@@ -291,7 +292,6 @@ begin
     raise EArgumentCryptoLibException.CreateRes(@SKeyTooSmall);
   end;
 
-  // C# creates new array each time, so we must zero it to match behavior
   SetLength(FBlock, (FEmBits + 7) div 8);
   ClearBlock(FBlock);
 end;
@@ -324,7 +324,7 @@ begin
     raise EInvalidOperationCryptoLibException.Create('Digest size mismatch');
   end;
 
-  // Ensure block is zero-initialized before use (C# creates new array in Init)
+  // Ensure block is zero-initialized before use
   ClearBlock(FBlock);
 
   // PSS requires first 8 bytes of mDash to be zeros (padding1)
@@ -368,6 +368,11 @@ begin
   ClearBlock(FBlock);
 end;
 
+function TPssSigner.GetMaxSignatureSize: Int32;
+begin
+  Result := FCipher.OutputBlockSize;
+end;
+
 function TPssSigner.VerifySignature(
   const signature: TCryptoLibByteArray): Boolean;
 var

+ 6 - 12
CryptoLib/src/Crypto/Signers/ClpRsaDigestSigner.pas

@@ -39,10 +39,8 @@ uses
   ClpRsaBlindedEngine,
   ClpIPkcs1Encoding,
   ClpPkcs1Encoding,
-  ClpIAlgorithmIdentifier,
-  ClpAlgorithmIdentifier,
-  ClpIDigestInfo,
-  ClpDigestInfo,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
   ClpIAsn1Objects,
   ClpAsn1Objects,
   ClpX509ObjectIdentifiers,
@@ -253,9 +251,11 @@ end;
 
 class function TRsaDigestSigner.CheckDerEncoded(
   const hash: TCryptoLibByteArray): TCryptoLibByteArray;
+var
+  LDigestInfo: IDigestInfo;
 begin
   // Validate that hash is a valid DER-encoded DigestInfo
-  TDigestInfo.GetInstance(hash);
+  LDigestInfo := TDigestInfo.GetInstance(hash);
   Result := hash;
 end;
 
@@ -356,14 +356,8 @@ function TRsaDigestSigner.DerEncode(const digestAlgID: IAlgorithmIdentifier;
   const hash: TCryptoLibByteArray): TCryptoLibByteArray;
 var
   digestInfo: IDigestInfo;
-  digestOctetString: IDerOctetString;
 begin
-  if System.Length(hash) < 1 then
-    digestOctetString := TDerOctetString.Create(nil)
-  else
-    digestOctetString := TDerOctetString.Create(hash);
-
-  digestInfo := TDigestInfo.Create(digestAlgID, digestOctetString as IAsn1OctetString);
+  digestInfo := TDigestInfo.Create(digestAlgID, TDerOctetString.WithContents(hash) as IAsn1OctetString);
   Result := digestInfo.GetDerEncoded();
 end;
 

+ 24 - 0
CryptoLib/src/Crypto/Signers/ClpSchnorrDigestSigner.pas

@@ -30,6 +30,7 @@ uses
   ClpISchnorrDigestSigner,
   ClpIDigest,
   ClpBigInteger,
+  ClpBigIntegers,
   ClpCryptoLibTypes,
   ClpIParametersWithRandom,
   ClpIAsymmetricKeyParameter,
@@ -86,6 +87,11 @@ type
     procedure BlockUpdate(const input: TCryptoLibByteArray;
       inOff, length: Int32); virtual;
 
+    /// <summary>
+    /// Return the maximum size for a signature produced by this signer.
+    /// </summary>
+    function GetMaxSignatureSize: Int32; virtual;
+
     /// <summary>
     /// Generate a signature for the message we've been loaded with using the
     /// key we were initialised with.
@@ -179,6 +185,24 @@ begin
   Result := FDigest.AlgorithmName + 'with' + FSchnorr.AlgorithmName;
 end;
 
+function TSchnorrDigestSigner.GetMaxSignatureSize: Int32;
+var
+  LOrder: TBigInteger;
+begin
+  LOrder := GetOrder();
+  if LOrder.IsInitialized then
+  begin
+    // Schnorr signature is two big integers (r, s), each the size of the order
+    // For standard encoding, add some overhead for ASN.1 structure
+    Result := (TBigIntegers.GetByteLength(LOrder) * 2) + 20; // 20 bytes overhead for ASN.1
+  end
+  else
+  begin
+    // Fallback: assume 256-bit order (32 bytes per component)
+    Result := 84; // 2 * 32 + 20 overhead
+  end;
+end;
+
 function TSchnorrDigestSigner.GetOrder: TBigInteger;
 begin
   if Supports(FSchnorr, ISchnorrExt) then

+ 14 - 0
CryptoLib/src/Crypto/Signers/SignersEncodings/ClpSignersEncodings.pas

@@ -58,6 +58,8 @@ type
 
     function Encode(const n, r, s: TBigInteger): TCryptoLibByteArray; virtual;
 
+    function GetMaxEncodingSize(const n: TBigInteger): Int32; virtual;
+
     class property Instance: IStandardDsaEncoding read GetInstance;
 
   end;
@@ -84,6 +86,8 @@ type
 
     function Encode(const n, r, s: TBigInteger): TCryptoLibByteArray; virtual;
 
+    function GetMaxEncodingSize(const n: TBigInteger): Int32; virtual;
+
     class property Instance: IPlainDsaEncoding read GetInstance;
 
   end;
@@ -172,6 +176,11 @@ begin
   result := TDerInteger.Create(CheckValue(n, x));
 end;
 
+function TStandardDsaEncoding.GetMaxEncodingSize(const n: TBigInteger): Int32;
+begin
+  result := TDerSequence.GetEncodingLength(TDerInteger.GetEncodingLength(n) * 2);
+end;
+
 class function TStandardDsaEncoding.GetInstance: IStandardDsaEncoding;
 begin
   result := TStandardDsaEncoding.Create();
@@ -235,6 +244,11 @@ begin
   System.Move(bs[bsOff], buf[off + pos], bsLen * System.SizeOf(Byte));
 end;
 
+function TPlainDsaEncoding.GetMaxEncodingSize(const n: TBigInteger): Int32;
+begin
+  result := TBigIntegers.GetUnsignedByteLength(n) * 2;
+end;
+
 class function TPlainDsaEncoding.GetInstance: IPlainDsaEncoding;
 begin
   result := TPlainDsaEncoding.Create();

+ 10 - 11
CryptoLib/src/Interfaces/ClpIAsn1Objects.pas

@@ -681,25 +681,24 @@ type
     function GetObjectParser(ATag: Int32; AIsExplicit: Boolean): IAsn1Convertible;
     function ToString(): String;
 
-    // Additional public methods from TAsn1TaggedObject class (matching BC reference)
     /// <summary>
-    /// Get the base encodable object (public, matching BC line 274-277).
+    /// Get the base encodable object
     /// </summary>
     function GetBaseObject(): IAsn1Encodable;
     /// <summary>
-    /// Get the explicit base encodable object (public, matching BC line 285-291).
+    /// Get the explicit base encodable object
     /// </summary>
     function GetExplicitBaseObject(): IAsn1Encodable;
     /// <summary>
-    /// Get the explicit base tagged object (public, matching BC line 293-299).
+    /// Get the explicit base tagged object
     /// </summary>
     function GetExplicitBaseTagged(): IAsn1TaggedObject;
     /// <summary>
-    /// Get the implicit base tagged object (public, matching BC line 301-321).
+    /// Get the implicit base tagged object
     /// </summary>
     function GetImplicitBaseTagged(ABaseTagClass, ABaseTagNo: Int32): IAsn1TaggedObject;
     /// <summary>
-    /// Get base universal object (public, matching BC line 323-329).
+    /// Get base universal object
     /// </summary>
     function GetBaseUniversal(ADeclaredExplicit: Boolean; ATagNo: Int32): IAsn1Object; overload;
     /// <summary>
@@ -709,19 +708,19 @@ type
 
     // Methods from IAsn1TaggedObjectParser (since TAsn1TaggedObject implements both interfaces)
     /// <summary>
-    /// Parse a base universal object (public, matching BC line 361-378).
+    /// Parse a base universal object
     /// </summary>
     function ParseBaseUniversal(ADeclaredExplicit: Boolean; ABaseTagNo: Int32): IAsn1Convertible;
     /// <summary>
-    /// Parse an explicit base object (public, matching BC line 380-383).
+    /// Parse an explicit base object
     /// </summary>
     function ParseExplicitBaseObject(): IAsn1Convertible;
     /// <summary>
-    /// Parse an explicit base tagged object (public, matching BC line 385-388).
+    /// Parse an explicit base tagged object
     /// </summary>
     function ParseExplicitBaseTagged(): IAsn1TaggedObjectParser;
     /// <summary>
-    /// Parse an implicit base tagged object (public, matching BC line 390-393).
+    /// Parse an implicit base tagged object
     /// </summary>
     function ParseImplicitBaseTagged(ABaseTagClass, ABaseTagNo: Int32): IAsn1TaggedObjectParser;
   end;
@@ -808,7 +807,7 @@ type
     /// </summary>
     function LoadTaggedIL(ATagClass, ATagNo: Int32): IAsn1Object;
     /// <summary>
-    /// Read a vector of ASN.1 objects (internal method - public in Delphi as internal in C# is public in Delphi).
+    /// Read a vector of ASN.1 objects.
     /// </summary>
     function ReadVector(): IAsn1EncodableVector;
   end;

+ 50 - 0
CryptoLib/src/Interfaces/ClpIBlockResult.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIBlockResult;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for operators that reduce their input to a single block.
+  /// </summary>
+  IBlockResult = interface
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF0123456789}']
+
+    /// <summary>
+    /// Return the final result of the operation.
+    /// </summary>
+    function Collect: TCryptoLibByteArray; overload;
+    /// <summary>
+    /// Store the final result of the operation by copying it into the destination array.
+    /// </summary>
+    function Collect(const ABuf: TCryptoLibByteArray; AOff: Int32): Int32; overload;
+    /// <summary>
+    /// Return an upper limit for the size of the result.
+    /// </summary>
+    function GetMaxResultLength: Int32;
+  end;
+
+implementation
+
+end.

+ 57 - 0
CryptoLib/src/Interfaces/ClpIDigestFactory.pas

@@ -0,0 +1,57 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIDigestFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpIX509Asn1Objects;
+
+type
+  /// <summary>
+  /// Base interface for operator factories that create stream-based digest calculators.
+  /// </summary>
+  IDigestFactory = interface
+    ['{F1A2B3C4-D5E6-7890-ABCD-EF0123456789}']
+
+    /// <summary>The algorithm details object for calculators made by this factory.</summary>
+    function GetAlgorithmDetails: IAlgorithmIdentifier;
+
+    /// <summary>Return the size of the digest associated with this factory.</summary>
+    /// <returns>The length of the digest produced by this calculators from this factory in bytes.</returns>
+    function GetDigestLength: Int32;
+
+    /// <summary>
+    /// Create a stream calculator for the digest associated with this factory. The stream
+    /// calculator is used for the actual operation of entering the data to be digested
+    /// and producing the digest block.
+    /// </summary>
+    /// <returns>A calculator producing an IBlockResult with the final digest in it.</returns>
+    function CreateCalculator: IStreamCalculator<IBlockResult>;
+
+    property AlgorithmDetails: IAlgorithmIdentifier read GetAlgorithmDetails;
+    property DigestLength: Int32 read GetDigestLength;
+  end;
+
+implementation
+
+end.

+ 52 - 0
CryptoLib/src/Interfaces/ClpIMacFactory.pas

@@ -0,0 +1,52 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIMacFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIStreamCalculator,
+  ClpIBlockResult,
+  ClpIX509Asn1Objects;
+
+type
+  /// <summary>
+  /// Base interface for operator factories that create stream-based MAC calculators.
+  /// </summary>
+  IMacFactory = interface
+    ['{A2B3C4D5-E6F7-8901-BCDE-F0123456789A}']
+
+    /// <summary>The algorithm details object for this calculator.</summary>
+    function GetAlgorithmDetails: IAlgorithmIdentifier;
+
+    /// <summary>
+    /// Create a stream calculator for this MAC calculator. The stream
+    /// calculator is used for the actual operation of entering the data to be MACed
+    /// and producing the MAC block.
+    /// </summary>
+    /// <returns>A calculator producing an IBlockResult with a MAC in it.</returns>
+    function CreateCalculator: IStreamCalculator<IBlockResult>;
+
+    property AlgorithmDetails: IAlgorithmIdentifier read GetAlgorithmDetails;
+  end;
+
+implementation
+
+end.

+ 38 - 0
CryptoLib/src/Interfaces/ClpIMiscPemGenerator.pas

@@ -0,0 +1,38 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIMiscPemGenerator;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIPemObjects;
+
+type
+  /// <summary>
+  /// Interface for miscellaneous PEM generator.
+  /// </summary>
+  IMiscPemGenerator = interface(IPemObjectGenerator)
+    ['{F6A7B8C9-D0E1-2345-EF01-23456789ABCD}']
+
+  end;
+
+implementation
+
+end.

+ 151 - 0
CryptoLib/src/Interfaces/ClpIPemObjects.pas

@@ -0,0 +1,151 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPemObjects;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpCryptoLibTypes;
+
+type
+  IPemHeader = interface;
+  IPemObject = interface;
+  IPemObjectGenerator = interface;
+
+  /// <summary>
+  /// Interface for PEM header objects.
+  /// </summary>
+  IPemHeader = interface(IInterface)
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
+
+    function GetName: String;
+    function GetValue: String;
+
+    /// <summary>
+    /// Get the header name.
+    /// </summary>
+    property Name: String read GetName;
+    /// <summary>
+    /// Get the header value.
+    /// </summary>
+    property Value: String read GetValue;
+
+    /// <summary>
+    /// Get hash code for this header.
+    /// </summary>
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt; {$ENDIF DELPHI}
+    /// <summary>
+    /// Check if this header equals another object.
+    /// </summary>
+    function Equals(const AObj: IPemHeader): Boolean;
+    /// <summary>
+    /// Get string representation of this header.
+    /// </summary>
+    function ToString(): String;
+  end;
+
+  /// <summary>
+  /// Interface for PEM object generator.
+  /// </summary>
+  IPemObjectGenerator = interface(IInterface)
+    ['{B2C3D4E5-F6A7-8901-BCDE-F23456789012}']
+
+    /// <summary>
+    /// Generate a PEM object.
+    /// </summary>
+    /// <returns>A PEM object</returns>
+    function Generate(): IPemObject;
+  end;
+
+  /// <summary>
+  /// Interface for PEM objects.
+  /// </summary>
+  IPemObject = interface(IPemObjectGenerator)
+    ['{C3D4E5F6-A7B8-9012-CDEF-0123456789AB}']
+
+    function GetType: String;
+    function GetHeaders: TCryptoLibGenericArray<IPemHeader>;
+    function GetContent: TCryptoLibByteArray;
+
+    /// <summary>
+    /// Get the PEM object type.
+    /// </summary>
+    property &Type: String read GetType;
+    /// <summary>
+    /// Get the PEM headers.
+    /// </summary>
+    property Headers: TCryptoLibGenericArray<IPemHeader> read GetHeaders;
+    /// <summary>
+    /// Get the PEM content (decoded from base64).
+    /// </summary>
+    property Content: TCryptoLibByteArray read GetContent;
+  end;
+
+  /// <summary>
+  /// Interface for PEM reader.
+  /// </summary>
+  IPemReader = interface(IInterface)
+    ['{D4E5F6A7-B8C9-0123-DEF0-123456789ABC}']
+
+    function GetReader: TStream;
+
+    /// <summary>
+    /// Get the underlying stream reader.
+    /// </summary>
+    property Reader: TStream read GetReader;
+
+    /// <summary>
+    /// Read a PEM object from the stream.
+    /// </summary>
+    /// <returns>A PEM object, or nil if end of stream</returns>
+    function ReadPemObject(): IPemObject;
+  end;
+
+  /// <summary>
+  /// Interface for PEM writer.
+  /// </summary>
+  IPemWriter = interface(IInterface)
+    ['{E5F6A7B8-C9D0-1234-EF01-23456789ABCD}']
+
+    function GetWriter: TStream;
+
+    /// <summary>
+    /// Get the underlying stream writer.
+    /// </summary>
+    property Writer: TStream read GetWriter;
+
+    /// <summary>
+    /// Get the estimated output size for a PEM object.
+    /// </summary>
+    /// <param name="AObj">The PEM object</param>
+    /// <returns>Estimated size in bytes</returns>
+    function GetOutputSize(const AObj: IPemObject): Int32;
+
+    /// <summary>
+    /// Write a PEM object to the stream.
+    /// </summary>
+    /// <param name="AObjGen">The PEM object generator</param>
+    procedure WriteObject(const AObjGen: IPemObjectGenerator);
+  end;
+
+implementation
+
+end.

+ 149 - 0
CryptoLib/src/Interfaces/ClpIPkcsAsn1Objects.pas

@@ -0,0 +1,149 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPkcsAsn1Objects;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  // Forward declarations
+  IAttributePkcs = interface;
+  ICertificationRequest = interface;
+  ICertificationRequestInfo = interface;
+  IPrivateKeyInfo = interface;
+  IRsassaPssParameters = interface;
+
+  /// <summary>
+  /// Interface for AttributePkcs.
+  /// </summary>
+  IAttributePkcs = interface(IAsn1Encodable)
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF0123456789}']
+
+    function GetAttrType: IDerObjectIdentifier;
+    function GetAttrValues: IAsn1Set;
+
+    property AttrType: IDerObjectIdentifier read GetAttrType;
+    property AttrValues: IAsn1Set read GetAttrValues;
+  end;
+
+  /// <summary>
+  /// Interface for CertificationRequest.
+  /// </summary>
+  ICertificationRequest = interface(IAsn1Encodable)
+    ['{B2C3D4E5-F6A7-8901-BCDE-F0123456789A}']
+
+    function GetCertificationRequestInfo: ICertificationRequestInfo;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignature: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property Signature: IDerBitString read GetSignature;
+  end;
+
+  /// <summary>
+  /// Interface for CertificationRequestInfo.
+  /// </summary>
+  ICertificationRequestInfo = interface(IAsn1Encodable)
+    ['{C3D4E5F6-A7B8-9012-CDEF-0123456789AB}']
+
+    function GetVersion: IDerInteger;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetAttributes: IAsn1Set;
+
+    property Version: IDerInteger read GetVersion;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property Attributes: IAsn1Set read GetAttributes;
+  end;
+
+  /// <summary>
+  /// Interface for PrivateKeyInfo.
+  /// </summary>
+  IPrivateKeyInfo = interface(IAsn1Encodable)
+    ['{E6F7A8B9-C0D1-E2F3-A4B5-C6D7E8F9A0B1}']
+
+    function GetVersion: IDerInteger;
+    function GetPrivateKeyAlgorithm: IAlgorithmIdentifier;
+    function GetPrivateKey: IAsn1OctetString;
+    function GetAttributes: IAsn1Set;
+    function GetPublicKey: IDerBitString;
+    function HasPublicKey: Boolean;
+    function ParsePrivateKey: IAsn1Object;
+    function ParsePublicKey: IAsn1Object;
+
+    property Version: IDerInteger read GetVersion;
+    property PrivateKeyAlgorithm: IAlgorithmIdentifier read GetPrivateKeyAlgorithm;
+    property PrivateKey: IAsn1OctetString read GetPrivateKey;
+    property Attributes: IAsn1Set read GetAttributes;
+    property PublicKey: IDerBitString read GetPublicKey;
+  end;
+
+  /// <summary>
+  /// Interface for RsaPrivateKeyStructure.
+  /// </summary>
+  IRsaPrivateKeyStructure = interface(IAsn1Encodable)
+    ['{F7A8B9C0-D1E2-F345-A6B7-C8D9E0F1A2B3}']
+
+    function GetModulus: TBigInteger;
+    function GetPublicExponent: TBigInteger;
+    function GetPrivateExponent: TBigInteger;
+    function GetPrime1: TBigInteger;
+    function GetPrime2: TBigInteger;
+    function GetExponent1: TBigInteger;
+    function GetExponent2: TBigInteger;
+    function GetCoefficient: TBigInteger;
+
+    property Modulus: TBigInteger read GetModulus;
+    property PublicExponent: TBigInteger read GetPublicExponent;
+    property PrivateExponent: TBigInteger read GetPrivateExponent;
+    property Prime1: TBigInteger read GetPrime1;
+    property Prime2: TBigInteger read GetPrime2;
+    property Exponent1: TBigInteger read GetExponent1;
+    property Exponent2: TBigInteger read GetExponent2;
+    property Coefficient: TBigInteger read GetCoefficient;
+  end;
+
+  /// <summary>
+  /// Interface for RsassaPssParameters.
+  /// </summary>
+  IRsassaPssParameters = interface(IAsn1Encodable)
+    ['{A8B9C0D1-E2F3-4567-8901-23456789ABCD}']
+
+    function GetHashAlgorithm: IAlgorithmIdentifier;
+    function GetMaskGenAlgorithm: IAlgorithmIdentifier;
+    function GetSaltLength: IDerInteger;
+    function GetTrailerField: IDerInteger;
+
+    property HashAlgorithm: IAlgorithmIdentifier read GetHashAlgorithm;
+    property MaskGenAlgorithm: IAlgorithmIdentifier read GetMaskGenAlgorithm;
+    property SaltLength: IDerInteger read GetSaltLength;
+    property TrailerField: IDerInteger read GetTrailerField;
+  end;
+
+implementation
+
+end.

+ 50 - 0
CryptoLib/src/Interfaces/ClpISecAsn1Objects.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              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 ClpISecAsn1Objects;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpBigInteger,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for the elliptic curve private key object from SEC 1
+  /// </summary>
+  IECPrivateKeyStructure = interface(IAsn1Encodable)
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
+
+    function GetVersion: IDerInteger;
+    function GetPrivateKey: IAsn1OctetString;
+    function GetParameters: IAsn1Encodable;
+    function GetPublicKey: IDerBitString;
+    function GetKey: TBigInteger;
+
+    property Version: IDerInteger read GetVersion;
+    property PrivateKey: IAsn1OctetString read GetPrivateKey;
+    property Parameters: IAsn1Encodable read GetParameters;
+    property PublicKey: IDerBitString read GetPublicKey;
+  end;
+
+implementation
+
+end.

+ 50 - 0
CryptoLib/src/Interfaces/ClpISignatureFactory.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              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 ClpISignatureFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIStreamCalculator,
+  ClpIBlockResult;
+
+type
+  /// <summary>
+  /// Base interface for operators that serve as stream-based signature calculators.
+  /// </summary>
+  ISignatureFactory = interface
+    ['{D4E5F6A7-B8C9-0123-DEF0-123456789ABC}']
+
+    /// <summary>
+    /// The algorithm details object for this calculator.
+    /// </summary>
+    function GetAlgorithmDetails: TObject;
+
+    /// <summary>
+    /// Create a stream calculator for this signature calculator.
+    /// </summary>
+    function CreateCalculator: IStreamCalculator<IBlockResult>;
+
+    property AlgorithmDetails: TObject read GetAlgorithmDetails;
+  end;
+
+implementation
+
+end.

+ 5 - 0
CryptoLib/src/Interfaces/ClpISigner.pas

@@ -57,6 +57,11 @@ type
     procedure BlockUpdate(const input: TCryptoLibByteArray;
       inOff, length: Int32);
 
+    // /**
+    // * Return the maximum size for a signature produced by this signer.
+    // */
+    function GetMaxSignatureSize: Int32;
+
     // /**
     // * Generate a signature for the message we've been loaded with using
     // * the key we were initialised with.

+ 5 - 0
CryptoLib/src/Interfaces/ClpISignersEncodings.pas

@@ -46,6 +46,11 @@ type
     /// <returns>An encoding of the DSA signature given by the provided (r, s) pair.</returns>
     function Encode(const n, r, s: TBigInteger): TCryptoLibByteArray;
 
+    /// <summary>Get the maximum encoding size for a given order.</summary>
+    /// <param name="n">The order of the group.</param>
+    /// <returns>The maximum size in bytes for an encoding.</returns>
+    function GetMaxEncodingSize(const n: TBigInteger): Int32;
+
   end;
 
 type

+ 50 - 0
CryptoLib/src/Interfaces/ClpIStreamCalculator.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIStreamCalculator;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes;
+
+type
+  /// <summary>
+  /// Base interface for cryptographic operations such as Hashes, MACs, and Signatures
+  /// which reduce a stream of data to a single value.
+  /// </summary>
+  IStreamCalculator<TResult> = interface
+    ['{C3D4E5F6-A7B8-9012-CDEF-0123456789AB}']
+
+    /// <summary>
+    /// Return a "sink" stream which only exists to update the implementing object.
+    /// </summary>
+    function GetStream: TStream;
+    /// <summary>
+    /// Return the result of processing the stream. This value is only available once the stream
+    /// has been closed.
+    /// </summary>
+    function GetResult: TResult;
+
+    property Stream: TStream read GetStream;
+  end;
+
+implementation
+
+end.

+ 46 - 0
CryptoLib/src/Interfaces/ClpIVerifier.pas

@@ -0,0 +1,46 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIVerifier;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for operators that reduce their input to the validation of a signature.
+  /// </summary>
+  IVerifier = interface
+    ['{B2C3D4E5-F6A7-8901-BCDE-F0123456789A}']
+
+    /// <summary>
+    /// Return true if the passed in data matches what is expected by the verification result.
+    /// </summary>
+    function IsVerified(const AData: TCryptoLibByteArray): Boolean; overload;
+    /// <summary>
+    /// Return true if the length bytes from off in the source array match the signature expected.
+    /// </summary>
+    function IsVerified(const ASource: TCryptoLibByteArray; AOff, ALength: Int32): Boolean; overload;
+  end;
+
+implementation
+
+end.

+ 50 - 0
CryptoLib/src/Interfaces/ClpIVerifierFactory.pas

@@ -0,0 +1,50 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIVerifierFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIStreamCalculator,
+  ClpIVerifier;
+
+type
+  /// <summary>
+  /// Base interface for operators that serve as stream-based signature verifiers.
+  /// </summary>
+  IVerifierFactory = interface
+    ['{E5F6A7B8-C9D0-1234-EF01-23456789ABCD}']
+
+    /// <summary>
+    /// The algorithm details object for this verifier.
+    /// </summary>
+    function GetAlgorithmDetails: TObject;
+
+    /// <summary>
+    /// Create a stream calculator for this verifier.
+    /// </summary>
+    function CreateCalculator: IStreamCalculator<IVerifier>;
+
+    property AlgorithmDetails: TObject read GetAlgorithmDetails;
+  end;
+
+implementation
+
+end.

+ 42 - 0
CryptoLib/src/Interfaces/ClpIVerifierFactoryProvider.pas

@@ -0,0 +1,42 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIVerifierFactoryProvider;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIVerifierFactory;
+
+type
+  /// <summary>
+  /// Base interface for a provider to support the dynamic creation of signature verifiers.
+  /// </summary>
+  IVerifierFactoryProvider = interface
+    ['{F6A7B8C9-D0E1-2345-F012-3456789ABCDE}']
+
+    /// <summary>
+    /// Return a signature verifier for signature algorithm described in the passed in algorithm details object.
+    /// </summary>
+    function CreateVerifierFactory(AAlgorithmDetails: TObject): IVerifierFactory;
+  end;
+
+implementation
+
+end.

+ 563 - 0
CryptoLib/src/Interfaces/ClpIX509Asn1Objects.pas

@@ -0,0 +1,563 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509Asn1Objects;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Extension,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpArrayUtils;
+
+type
+  // Forward declarations
+  IAlgorithmIdentifier = interface;
+  IDigestInfo = interface;
+  IAltSignatureAlgorithm = interface;
+  IAltSignatureValue = interface;
+  IAuthorityKeyIdentifier = interface;
+  IBasicConstraints = interface;
+  IExtendedKeyUsage = interface;
+  IGeneralName = interface;
+  IGeneralNames = interface;
+  IKeyUsage = interface;
+  IRsaPublicKeyStructure = interface;
+  ISubjectAltPublicKeyInfo = interface;
+  ISubjectKeyIdentifier = interface;
+  ISubjectPublicKeyInfo = interface;
+  ITbsCertificateStructure = interface;
+  ITime = interface;
+  IValidity = interface;
+  IX509CertificateStructure = interface;
+  IX509Extensions = interface;
+  IX509Name = interface;
+  IIssuerSerial = interface;
+  IObjectDigestInfo = interface;
+  IDistributionPoint = interface;
+  IV2Form = interface;
+  IDistributionPointName = interface;
+  IReasonFlags = interface;
+  IAttributeX509 = interface;
+  IAttCertIssuer = interface;
+  IAttCertValidityPeriod = interface;
+  IHolder = interface;
+  IAttributeCertificate = interface;
+  IAttributeCertificateInfo = interface;
+  IPolicyInformation = interface;
+  ICrlDistPoint = interface;
+
+  /// <summary>
+  /// Interface for the AlgorithmIdentifier object.
+  /// <code>
+  /// AlgorithmIdentifier ::= SEQUENCE {
+  ///   algorithm OBJECT IDENTIFIER,
+  ///   parameters ANY DEFINED BY algorithm OPTIONAL
+  /// }
+  /// </code>
+  /// </summary>
+  IAlgorithmIdentifier = interface(IAsn1Encodable)
+    ['{E7F8A9B0-C1D2-E3F4-A5B6-C7D8E9F0A1B2}']
+
+    function GetAlgorithm: IDerObjectIdentifier;
+    function GetParameters: IAsn1Encodable;
+
+    property Algorithm: IDerObjectIdentifier read GetAlgorithm;
+    property Parameters: IAsn1Encodable read GetParameters;
+  end;
+
+  /// <summary>
+  /// Interface for the DigestInfo object.
+  /// DigestInfo ::= SEQUENCE {
+  ///   digestAlgorithm AlgorithmIdentifier,
+  ///   digest OCTET STRING
+  /// }
+  /// </summary>
+  IDigestInfo = interface(IAsn1Encodable)
+    ['{A1B2C3D4-E5F6-7890-ABCD-0123456789AB}']
+
+    function GetDigestAlgorithm: IAlgorithmIdentifier;
+    function GetDigest: IAsn1OctetString;
+    function GetDigestBytes: TCryptoLibByteArray;
+
+    property DigestAlgorithm: IAlgorithmIdentifier read GetDigestAlgorithm;
+    property Digest: IAsn1OctetString read GetDigest;
+  end;
+
+  /// <summary>
+  /// Interface for AltSignatureAlgorithm.
+  /// </summary>
+  IAltSignatureAlgorithm = interface(IAsn1Encodable)
+    ['{B2C3D4E5-F6A7-8901-BCDE-F0123456789A}']
+
+    function GetAlgorithm: IAlgorithmIdentifier;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+  end;
+
+  /// <summary>
+  /// Interface for AltSignatureValue.
+  /// </summary>
+  IAltSignatureValue = interface(IAsn1Encodable)
+    ['{C3D4E5F6-A7B8-9012-CDEF-0123456789AB}']
+
+    function GetSignature: IDerBitString;
+
+    property Signature: IDerBitString read GetSignature;
+  end;
+
+  /// <summary>
+  /// Interface for BasicConstraints.
+  /// </summary>
+  IBasicConstraints = interface(IAsn1Encodable)
+    ['{D4E5F6A7-B8C9-0123-DEF0-123456789ABC}']
+
+    function IsCA: Boolean;
+    function GetPathLenConstraint: TBigInteger;
+
+    function ToString: String;
+
+    property PathLenConstraint: TBigInteger read GetPathLenConstraint;
+  end;
+
+  /// <summary>
+  /// Interface for GeneralName.
+  /// </summary>
+  IGeneralName = interface(IAsn1Encodable)
+    ['{E5F6A7B8-C9D0-1234-EF01-23456789ABCD}']
+
+    function GetTagNo: Int32;
+    function GetName: IAsn1Encodable;
+
+    function ToString: String;
+
+    property TagNo: Int32 read GetTagNo;
+    property Name: IAsn1Encodable read GetName;
+  end;
+
+  /// <summary>
+  /// Interface for GeneralNames.
+  /// </summary>
+  IGeneralNames = interface(IAsn1Encodable)
+    ['{F6A7B8C9-D0E1-2345-F012-3456789ABCDE}']
+
+    function GetCount: Int32;
+    function GetNames: TCryptoLibGenericArray<IGeneralName>;
+
+    property Count: Int32 read GetCount;
+  end;
+
+  /// <summary>
+  /// Interface for AuthorityKeyIdentifier.
+  /// </summary>
+  IAuthorityKeyIdentifier = interface(IAsn1Encodable)
+    ['{F1A2B3C4-D5E6-789A-5678-9ABCDEF01234}']
+
+    function GetKeyIdentifier: IAsn1OctetString;
+    function GetAuthorityCertIssuer: IGeneralNames;
+    function GetAuthorityCertSerialNumber: IDerInteger;
+
+    property KeyIdentifier: IAsn1OctetString read GetKeyIdentifier;
+    property AuthorityCertIssuer: IGeneralNames read GetAuthorityCertIssuer;
+    property AuthorityCertSerialNumber: IDerInteger read GetAuthorityCertSerialNumber;
+  end;
+
+  /// <summary>
+  /// Interface for ExtendedKeyUsage.
+  /// </summary>
+  IExtendedKeyUsage = interface(IAsn1Encodable)
+    ['{A2B3C4D5-E6F7-890A-6789-ABCDEF012345}']
+
+    function HasKeyPurposeId(const AKeyPurposeId: IDerObjectIdentifier): Boolean;
+    function GetAllUsages: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetCount: Int32;
+
+    property Count: Int32 read GetCount;
+  end;
+
+  /// <summary>
+  /// Interface for KeyUsage.
+  /// </summary>
+  IKeyUsage = interface(IDerBitString)
+    ['{A7B8C9D0-E1F2-3456-0123-456789ABCDEF}']
+  end;
+
+  /// <summary>
+  /// Interface for RsaPublicKeyStructure.
+  /// </summary>
+  IRsaPublicKeyStructure = interface(IAsn1Encodable)
+    ['{B8C9D0E1-F2A3-4567-1234-56789ABCDEF0}']
+
+    function GetModulus: TBigInteger;
+    function GetPublicExponent: TBigInteger;
+
+    property Modulus: TBigInteger read GetModulus;
+    property PublicExponent: TBigInteger read GetPublicExponent;
+  end;
+
+  /// <summary>
+  /// Interface for SubjectAltPublicKeyInfo.
+  /// </summary>
+  ISubjectAltPublicKeyInfo = interface(IAsn1Encodable)
+    ['{C9D0E1F2-A3B4-5678-2345-6789ABCDEF01}']
+
+    function GetAlgorithm: IAlgorithmIdentifier;
+    function GetSubjectAltPublicKey: IDerBitString;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+    property SubjectAltPublicKey: IDerBitString read GetSubjectAltPublicKey;
+  end;
+
+  /// <summary>
+  /// Interface for SubjectKeyIdentifier.
+  /// </summary>
+  ISubjectKeyIdentifier = interface(IAsn1Encodable)
+    ['{D0E1F2A3-B4C5-6789-3456-789ABCDEF012}']
+
+    function GetKeyIdentifier: TCryptoLibByteArray;
+  end;
+
+  /// <summary>
+  /// Interface for SubjectPublicKeyInfo.
+  /// </summary>
+  ISubjectPublicKeyInfo = interface(IAsn1Encodable)
+    ['{E1F2A3B4-C5D6-789A-4567-89ABCDEF0123}']
+
+    function GetAlgorithm: IAlgorithmIdentifier;
+    function GetPublicKey: IDerBitString;
+    function ParsePublicKey: IAsn1Object;
+
+    property Algorithm: IAlgorithmIdentifier read GetAlgorithm;
+    property PublicKey: IDerBitString read GetPublicKey;
+  end;
+
+  /// <summary>
+  /// Interface for TbsCertificateStructure.
+  /// </summary>
+  ITbsCertificateStructure = interface(IAsn1Encodable)
+    ['{F2A3B4C5-D6E7-89AB-5678-9ABCDEF01234}']
+
+    function GetVersion: Int32;
+    function GetVersionNumber: IDerInteger;
+    function GetSerialNumber: IDerInteger;
+    function GetSignature: IAlgorithmIdentifier;
+    function GetIssuer: IX509Name;
+    function GetValidity: IValidity;
+    function GetStartDate: ITime;
+    function GetEndDate: ITime;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+
+    property Version: Int32 read GetVersion;
+    property VersionNumber: IDerInteger read GetVersionNumber;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property Signature: IAlgorithmIdentifier read GetSignature;
+    property Issuer: IX509Name read GetIssuer;
+    property Validity: IValidity read GetValidity;
+    property StartDate: ITime read GetStartDate;
+    property EndDate: ITime read GetEndDate;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property SubjectUniqueID: IDerBitString read GetSubjectUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+  end;
+
+  /// <summary>
+  /// Interface for Time (CHOICE type: Asn1UtcTime or Asn1GeneralizedTime).
+  /// </summary>
+  ITime = interface(IAsn1Encodable)
+    ['{B4C5D6E7-F8A9-0BCD-789A-BCDEF0123456}']
+
+    function ToDateTime: TDateTime;
+    function ToAsn1Object: IAsn1Object;
+  end;
+
+  /// <summary>
+  /// Interface for Validity.
+  /// </summary>
+  IValidity = interface(IAsn1Encodable)
+    ['{A3B4C5D6-E7F8-9ABC-6789-ABCDEF012345}']
+
+    function GetNotBefore: ITime;
+    function GetNotAfter: ITime;
+
+    property NotBefore: ITime read GetNotBefore;
+    property NotAfter: ITime read GetNotAfter;
+  end;
+
+  /// <summary>
+  /// Interface for X509CertificateStructure.
+  /// </summary>
+  IX509CertificateStructure = interface(IAsn1Encodable)
+    ['{B4C5D6E7-F8A9-BCDE-789A-BCDEF0123456}']
+
+    function GetTbsCertificate: ITbsCertificateStructure;
+    function GetVersion: Int32;
+    function GetSerialNumber: IDerInteger;
+    function GetIssuer: IX509Name;
+    function GetValidity: IValidity;
+    function GetStartDate: ITime;
+    function GetEndDate: ITime;
+    function GetSubject: IX509Name;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignature: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+
+    property TbsCertificate: ITbsCertificateStructure read GetTbsCertificate;
+    property Version: Int32 read GetVersion;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property Issuer: IX509Name read GetIssuer;
+    property Validity: IValidity read GetValidity;
+    property StartDate: ITime read GetStartDate;
+    property EndDate: ITime read GetEndDate;
+    property Subject: IX509Name read GetSubject;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property SubjectUniqueID: IDerBitString read GetSubjectUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property Signature: IDerBitString read GetSignature;
+  end;
+
+  /// <summary>
+  /// Interface for X509Extensions.
+  /// </summary>
+  IX509Extensions = interface(IAsn1Encodable)
+    ['{C5D6E7F8-A9B0-CDEF-89AB-CDEF01234567}']
+
+    function GetCount: Int32;
+    function GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+    function GetExtensionParsedValue(const AOid: IDerObjectIdentifier): IAsn1Object;
+    function GetExtensionValue(const AOid: IDerObjectIdentifier): IAsn1OctetString;
+    function GetExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetNonCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetCriticalExtensionOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function HasAnyCriticalExtensions: Boolean;
+    function Equivalent(const AOther: IX509Extensions): Boolean;
+    function ToAsn1ObjectTrimmed: IAsn1Sequence;
+
+    property Count: Int32 read GetCount;
+  end;
+
+  /// <summary>
+  /// Interface for X509Name.
+  /// </summary>
+  IX509Name = interface(IAsn1Encodable)
+    ['{D6E7F8A9-B0C1-DEF0-9ABC-DEF012345678}']
+
+    function GetOids: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetValues: TCryptoLibStringArray; overload;
+    function GetValueList: TCryptoLibStringArray; overload;
+    function ToString: String; overload;
+    function ToString(const AOid: IDerObjectIdentifier): String; overload;
+    function GetValue(const AOid: IDerObjectIdentifier): String; overload;
+    function GetValues(const AOid: IDerObjectIdentifier): TCryptoLibStringArray; overload;
+    function GetValueList(const AOid: IDerObjectIdentifier): TCryptoLibStringArray; overload;
+    function Equivalent(const AOther: IX509Name; AInOrder: Boolean = False): Boolean;
+
+    property Oids: TCryptoLibGenericArray<IDerObjectIdentifier> read GetOids;
+    property Values: TCryptoLibStringArray read GetValues;
+  end;
+
+  /// <summary>
+  /// Interface for AttributeX509.
+  /// </summary>
+  IAttributeX509 = interface(IAsn1Encodable)
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
+    function GetAttrType: IDerObjectIdentifier;
+    function GetAttrValues: IAsn1Set;
+    function GetAttributeValues: TCryptoLibGenericArray<IAsn1Encodable>;
+    property AttrType: IDerObjectIdentifier read GetAttrType;
+    property AttrValues: IAsn1Set read GetAttrValues;
+  end;
+
+  /// <summary>
+  /// Interface for AttCertIssuer.
+  /// </summary>
+  IAttCertIssuer = interface(IAsn1Encodable)
+    ['{B2C3D4E5-F6A7-8901-BCDE-F01234567890}']
+    function GetIssuer: IAsn1Encodable;
+    property Issuer: IAsn1Encodable read GetIssuer;
+  end;
+
+  /// <summary>
+  /// Interface for AttCertValidityPeriod.
+  /// </summary>
+  IAttCertValidityPeriod = interface(IAsn1Encodable)
+    ['{C3D4E5F6-A7B8-9012-CDEF-012345678901}']
+    function GetNotBeforeTime: IAsn1GeneralizedTime;
+    function GetNotAfterTime: IAsn1GeneralizedTime;
+    property NotBeforeTime: IAsn1GeneralizedTime read GetNotBeforeTime;
+    property NotAfterTime: IAsn1GeneralizedTime read GetNotAfterTime;
+  end;
+
+  /// <summary>
+  /// Interface for Holder.
+  /// </summary>
+  IHolder = interface(IAsn1Encodable)
+    ['{D4E5F6A7-B8C9-0123-DEF0-123456789012}']
+    function GetVersion: Int32;
+    function GetBaseCertificateID: IIssuerSerial;
+    function GetEntityName: IGeneralNames;
+    function GetObjectDigestInfo: IObjectDigestInfo;
+    property Version: Int32 read GetVersion;
+    property BaseCertificateID: IIssuerSerial read GetBaseCertificateID;
+    property EntityName: IGeneralNames read GetEntityName;
+    property ObjectDigestInfo: IObjectDigestInfo read GetObjectDigestInfo;
+  end;
+
+  /// <summary>
+  /// Interface for AttributeCertificate.
+  /// </summary>
+  IAttributeCertificate = interface(IAsn1Encodable)
+    ['{E5F6A7B8-C9D0-1234-EF01-234567890123}']
+    function GetACInfo: IAttributeCertificateInfo;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetSignatureValue: IDerBitString;
+    function GetSignatureOctets: TCryptoLibByteArray;
+    property ACInfo: IAttributeCertificateInfo read GetACInfo;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property SignatureValue: IDerBitString read GetSignatureValue;
+  end;
+
+  /// <summary>
+  /// Interface for AttributeCertificateInfo.
+  /// </summary>
+  IAttributeCertificateInfo = interface(IAsn1Encodable)
+    ['{F6A7B8C9-D0E1-2345-F012-345678901234}']
+    function GetVersion: IDerInteger;
+    function GetHolder: IHolder;
+    function GetIssuer: IAttCertIssuer;
+    function GetSignature: IAlgorithmIdentifier;
+    function GetSerialNumber: IDerInteger;
+    function GetAttrCertValidityPeriod: IAttCertValidityPeriod;
+    function GetAttributes: IAsn1Sequence;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetExtensions: IX509Extensions;
+    property Version: IDerInteger read GetVersion;
+    property Holder: IHolder read GetHolder;
+    property Issuer: IAttCertIssuer read GetIssuer;
+    property Signature: IAlgorithmIdentifier read GetSignature;
+    property SerialNumber: IDerInteger read GetSerialNumber;
+    property AttrCertValidityPeriod: IAttCertValidityPeriod read GetAttrCertValidityPeriod;
+    property Attributes: IAsn1Sequence read GetAttributes;
+    property IssuerUniqueID: IDerBitString read GetIssuerUniqueID;
+    property Extensions: IX509Extensions read GetExtensions;
+  end;
+
+  /// <summary>
+  /// Interface for PolicyInformation.
+  /// </summary>
+  IPolicyInformation = interface(IAsn1Encodable)
+    ['{A7B8C9D0-E1F2-3456-0123-456789012345}']
+    function GetPolicyIdentifier: IDerObjectIdentifier;
+    function GetPolicyQualifiers: IAsn1Sequence;
+    property PolicyIdentifier: IDerObjectIdentifier read GetPolicyIdentifier;
+    property PolicyQualifiers: IAsn1Sequence read GetPolicyQualifiers;
+  end;
+
+  /// <summary>
+  /// Interface for CrlDistPoint.
+  /// </summary>
+  ICrlDistPoint = interface(IAsn1Encodable)
+    ['{B8C9D0E1-F2A3-4567-1234-567890123456}']
+    function GetDistributionPoints: TCryptoLibGenericArray<IDistributionPoint>;
+  end;
+
+  /// <summary>
+  /// Interface for IssuerSerial.
+  /// </summary>
+  IIssuerSerial = interface(IAsn1Encodable)
+    ['{C9D0E1F2-A3B4-5678-0123-456789012345}']
+    function GetIssuer: IGeneralNames;
+    function GetSerial: IDerInteger;
+    function GetIssuerUid: IDerBitString;
+    property Issuer: IGeneralNames read GetIssuer;
+    property Serial: IDerInteger read GetSerial;
+    property IssuerUid: IDerBitString read GetIssuerUid;
+  end;
+
+  /// <summary>
+  /// Interface for V2Form.
+  /// </summary>
+  IV2Form = interface(IAsn1Encodable)
+    ['{D0E1F2A3-B4C5-6789-1234-567890123456}']
+    function GetIssuerName: IGeneralNames;
+    function GetBaseCertificateID: IIssuerSerial;
+    function GetObjectDigestInfo: IObjectDigestInfo;
+    property IssuerName: IGeneralNames read GetIssuerName;
+    property BaseCertificateID: IIssuerSerial read GetBaseCertificateID;
+    property ObjectDigestInfo: IObjectDigestInfo read GetObjectDigestInfo;
+  end;
+
+  /// <summary>
+  /// Interface for ObjectDigestInfo.
+  /// </summary>
+  IObjectDigestInfo = interface(IAsn1Encodable)
+    ['{E1F2A3B4-C5D6-7890-2345-678901234567}']
+    function GetDigestedObjectType: IDerEnumerated;
+    function GetOtherObjectTypeID: IDerObjectIdentifier;
+    function GetDigestAlgorithm: IAlgorithmIdentifier;
+    function GetObjectDigest: IDerBitString;
+    property DigestedObjectType: IDerEnumerated read GetDigestedObjectType;
+    property OtherObjectTypeID: IDerObjectIdentifier read GetOtherObjectTypeID;
+    property DigestAlgorithm: IAlgorithmIdentifier read GetDigestAlgorithm;
+    property ObjectDigest: IDerBitString read GetObjectDigest;
+  end;
+
+  /// <summary>
+  /// Interface for DistributionPoint.
+  /// </summary>
+  IDistributionPoint = interface(IAsn1Encodable)
+    ['{F2A3B4C5-D6E7-8901-3456-789012345678}']
+    function GetDistributionPointName: IDistributionPointName;
+    function GetReasons: IReasonFlags;
+    function GetCrlIssuer: IGeneralNames;
+    property DistributionPointName: IDistributionPointName read GetDistributionPointName;
+    property Reasons: IReasonFlags read GetReasons;
+    property CrlIssuer: IGeneralNames read GetCrlIssuer;
+  end;
+
+  /// <summary>
+  /// Interface for DistributionPointName.
+  /// </summary>
+  IDistributionPointName = interface(IAsn1Choice)
+    ['{A3B4C5D6-E7F8-9012-4567-890123456789}']
+  end;
+
+  /// <summary>
+  /// Interface for ReasonFlags.
+  /// </summary>
+  IReasonFlags = interface(IAsn1Encodable)
+    ['{B4C5D6E7-F8A9-0123-5678-901234567890}']
+  end;
+
+implementation
+
+end.

+ 98 - 0
CryptoLib/src/Interfaces/ClpIX509Certificate.pas

@@ -0,0 +1,98 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509Certificate;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Rtti,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIX509Extension,
+  ClpIAsymmetricKeyParameter,
+  ClpIVerifierFactoryProvider,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for X509Certificate.
+  /// </summary>
+  IX509Certificate = interface(IX509Extension)
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}']
+
+    function GetCertificateStructure: IX509CertificateStructure;
+    function IsValidNow: Boolean;
+    function IsValid(const ATime: TDateTime): Boolean;
+    procedure CheckValidity(); overload;
+    procedure CheckValidity(const ATime: TDateTime); overload;
+
+    function GetVersion: Int32;
+    function GetSerialNumber: TBigInteger;
+    function GetIssuerDN: IX509Name;
+    function GetSubjectDN: IX509Name;
+    function GetNotBefore: TDateTime;
+    function GetNotAfter: TDateTime;
+    function GetTbsCertificate: ITbsCertificateStructure;
+    function GetTbsCertificateEncoded: TCryptoLibByteArray;
+    function GetSignature: TCryptoLibByteArray;
+    function GetSigAlgName: String;
+    function GetSigAlgOid: String;
+    function GetSigAlgParams: TCryptoLibByteArray;
+    function GetSignatureAlgorithm: IAlgorithmIdentifier;
+    function GetIssuerUniqueID: IDerBitString;
+    function GetSubjectUniqueID: IDerBitString;
+    function GetKeyUsage: TCryptoLibBooleanArray;
+    function GetExtendedKeyUsage: TCryptoLibGenericArray<IDerObjectIdentifier>;
+    function GetBasicConstraints: Int32;
+    function GetIssuerAlternativeNameExtension: IGeneralNames;
+    function GetSubjectAlternativeNameExtension: IGeneralNames;
+    function GetIssuerAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+    function GetSubjectAlternativeNames: TCryptoLibGenericArray<TCryptoLibGenericArray<TValue>>;
+    function GetSubjectPublicKeyInfo: ISubjectPublicKeyInfo;
+    function GetPublicKey: IAsymmetricKeyParameter;
+    function GetEncoded: TCryptoLibByteArray;
+
+    function IsSignatureValid(const AKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+    function IsAlternativeSignatureValid(const APublicKey: IAsymmetricKeyParameter): Boolean; overload;
+    function IsAlternativeSignatureValid(const AVerifierProvider: IVerifierFactoryProvider): Boolean; overload;
+
+    procedure Verify(const AKey: IAsymmetricKeyParameter); overload;
+    procedure Verify(const AVerifierProvider: IVerifierFactoryProvider); overload;
+    procedure VerifyAltSignature(const AVerifierProvider: IVerifierFactoryProvider);
+
+    property CertificateStructure: IX509CertificateStructure read GetCertificateStructure;
+    property Version: Int32 read GetVersion;
+    property SerialNumber: TBigInteger read GetSerialNumber;
+    property IssuerDN: IX509Name read GetIssuerDN;
+    property SubjectDN: IX509Name read GetSubjectDN;
+    property NotBefore: TDateTime read GetNotBefore;
+    property NotAfter: TDateTime read GetNotAfter;
+    property TbsCertificate: ITbsCertificateStructure read GetTbsCertificate;
+    property SignatureAlgorithm: IAlgorithmIdentifier read GetSignatureAlgorithm;
+    property SigAlgName: String read GetSigAlgName;
+    property SubjectPublicKeyInfo: ISubjectPublicKeyInfo read GetSubjectPublicKeyInfo;
+  end;
+
+implementation
+
+end.

+ 44 - 0
CryptoLib/src/Interfaces/ClpIX509Extension.pas

@@ -0,0 +1,44 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509Extension;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects;
+
+type
+  /// <summary>
+  /// Interface for X509Extension (helper class, not ASN1Encodable).
+  /// </summary>
+  IX509Extension = interface
+    ['{E6F7A8B9-C0D1-E2F3-A4B5-C6D7E8F9A0B1}']
+
+    function GetIsCritical: Boolean;
+    function GetValue: IAsn1OctetString;
+    function GetParsedValue: IAsn1Object;
+
+    property IsCritical: Boolean read GetIsCritical;
+    property Value: IAsn1OctetString read GetValue;
+  end;
+
+implementation
+
+end.

+ 64 - 0
CryptoLib/src/Interfaces/ClpIX509ExtensionsGenerator.pas

@@ -0,0 +1,64 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509ExtensionsGenerator;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIX509Extension,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for X509ExtensionsGenerator.
+  /// </summary>
+  IX509ExtensionsGenerator = interface
+    ['{F2A3B4C5-D6E7-8901-FABC-0123456789DE}']
+
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Convertible); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Encodable); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: TCryptoLibByteArray); overload;
+    procedure AddExtension(const AOid: IDerObjectIdentifier;
+      const AX509Extension: IX509Extension); overload;
+    procedure AddExtensions(const AExtensions: IX509Extensions);
+    function Generate: IX509Extensions;
+    function GetExtension(const AOid: IDerObjectIdentifier): IX509Extension;
+    function HasExtension(const AOid: IDerObjectIdentifier): Boolean;
+    function IsEmpty: Boolean;
+    procedure RemoveExtension(const AOid: IDerObjectIdentifier);
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Convertible); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: IAsn1Encodable); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier; ACritical: Boolean;
+      const AExtValue: TCryptoLibByteArray); overload;
+    procedure ReplaceExtension(const AOid: IDerObjectIdentifier;
+      const AX509Extension: IX509Extension); overload;
+    procedure Reset;
+  end;
+
+implementation
+
+end.

+ 40 - 0
CryptoLib/src/Interfaces/ClpIX509NameEntryConverter.pas

@@ -0,0 +1,40 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509NameEntryConverter;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects;
+
+type
+  /// <summary>
+  /// Interface for X509NameEntryConverter (abstract base class).
+  /// </summary>
+  IX509NameEntryConverter = interface
+    ['{F1A2B3C4-D5E6-7890-EFAB-0123456789CD}']
+
+    function GetConvertedValue(const AOid: IDerObjectIdentifier;
+      const AValue: String): IAsn1Object;
+  end;
+
+implementation
+
+end.

+ 40 - 0
CryptoLib/src/Interfaces/ClpIX509NameTokenizer.pas

@@ -0,0 +1,40 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509NameTokenizer;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for breaking up an X500 Name into its component tokens, ala java.util.StringTokenizer.
+  /// </summary>
+  IX509NameTokenizer = interface
+    ['{A1B2C3D4-E5F6-7890-ABCD-EF0123456789}']
+
+    function HasMoreTokens: Boolean;
+    function NextToken: String;
+  end;
+
+implementation
+
+end.

+ 46 - 0
CryptoLib/src/Interfaces/ClpIX9Asn1Objects.pas

@@ -0,0 +1,46 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX9Asn1Objects;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpBigInteger;
+
+type
+  /// <summary>
+  /// Interface for X962Parameters.
+  /// </summary>
+  IX962Parameters = interface(IAsn1Encodable)
+    ['{D1E2F3A4-B5C6-7890-DEF1-23456789ABCD}']
+
+    function GetParameters: IAsn1Object;
+    function GetNamedCurve: IDerObjectIdentifier;
+    function IsImplicitlyCA: Boolean;
+    function IsNamedCurve: Boolean;
+
+    property Parameters: IAsn1Object read GetParameters;
+    property NamedCurve: IDerObjectIdentifier read GetNamedCurve;
+  end;
+
+implementation
+
+end.

+ 170 - 0
CryptoLib/src/Security/ClpPrivateKeyFactory.pas

@@ -0,0 +1,170 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPrivateKeyFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpIX9Asn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpIRsaPrivateCrtKeyParameters,
+  ClpIDsaPrivateKeyParameters,
+  ClpIECPrivateKeyParameters,
+  ClpRsaPrivateCrtKeyParameters,
+  ClpDsaPrivateKeyParameters,
+  ClpECPrivateKeyParameters,
+  ClpDsaParameters,
+  ClpDsaParameter,
+  ClpECDomainParameters,
+  ClpPkcsObjectIdentifiers,
+  ClpX509ObjectIdentifiers,
+  ClpX9ObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpX9Asn1Objects,
+  ClpX9ECC,
+  ClpX9ECParameters,
+  ClpIX9ECParameters,
+  ClpPkcsAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpECNamedCurveTable,
+  ClpIECDomainParameters,
+  ClpIDsaParameters,
+  ClpIDsaParameter,
+  ClpIX509Asn1Objects,
+  ClpSecAsn1Objects,
+  ClpISecAsn1Objects,
+  ClpBigInteger,
+  ClpBigIntegers,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Factory for creating private key parameters from PrivateKeyInfo.
+  /// </summary>
+  TPrivateKeyFactory = class sealed(TObject)
+
+  public
+    class function CreateKey(const APrivateKeyInfoData: TCryptoLibByteArray): IAsymmetricKeyParameter; overload; static;
+    class function CreateKey(const AInStr: TStream): IAsymmetricKeyParameter; overload; static;
+    class function CreateKey(const AKeyInfo: IPrivateKeyInfo): IAsymmetricKeyParameter; overload; static;
+
+  end;
+
+implementation
+
+{ TPrivateKeyFactory }
+
+class function TPrivateKeyFactory.CreateKey(const APrivateKeyInfoData: TCryptoLibByteArray): IAsymmetricKeyParameter;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  LAsn1Obj := TAsn1Object.FromByteArray(APrivateKeyInfoData);
+  Result := CreateKey(TPrivateKeyInfo.GetInstance(LAsn1Obj));
+end;
+
+class function TPrivateKeyFactory.CreateKey(const AInStr: TStream): IAsymmetricKeyParameter;
+var
+  LAsn1Obj: IAsn1Object;
+begin
+  LAsn1Obj := TAsn1Object.FromStream(AInStr);
+  Result := CreateKey(TPrivateKeyInfo.GetInstance(LAsn1Obj));
+end;
+
+class function TPrivateKeyFactory.CreateKey(const AKeyInfo: IPrivateKeyInfo): IAsymmetricKeyParameter;
+var
+  LAlgID: IAlgorithmIdentifier;
+  LAlgOid: IDerObjectIdentifier;
+  LRsaKeyStructure: IRsaPrivateKeyStructure;
+  LDsaX: IDerInteger;
+  LDsaParams: IDsaParameters;
+  LDsaPara: IDsaParameter;
+  LX962Params: IX962Parameters;
+  LX9ECParams: IX9ECParameters;
+  LECParams: IECDomainParameters;
+  LECPrivateKeyObj: IAsn1Object;
+  LECPrivateKeySeq: IECPrivateKeyStructure;
+begin
+  if AKeyInfo = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LAlgID := AKeyInfo.PrivateKeyAlgorithm;
+  LAlgOid := LAlgID.Algorithm;
+
+  // RSA keys
+  if LAlgOid.Equals(TPkcsObjectIdentifiers.RsaEncryption) or
+    LAlgOid.Equals(TX509ObjectIdentifiers.IdEARsa) or
+    LAlgOid.Equals(TPkcsObjectIdentifiers.IdRsassaPss) or
+    LAlgOid.Equals(TPkcsObjectIdentifiers.IdRsaesOaep) then
+  begin
+    LRsaKeyStructure := TRsaPrivateKeyStructure.GetInstance(AKeyInfo.ParsePrivateKey());
+    Result := TRsaPrivateCrtKeyParameters.Create(
+      LRsaKeyStructure.Modulus,
+      LRsaKeyStructure.PublicExponent,
+      LRsaKeyStructure.PrivateExponent,
+      LRsaKeyStructure.Prime1,
+      LRsaKeyStructure.Prime2,
+      LRsaKeyStructure.Exponent1,
+      LRsaKeyStructure.Exponent2,
+      LRsaKeyStructure.Coefficient);
+    Exit;
+  end;
+
+  // DSA keys
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdDsa) then
+  begin
+    LDsaX := TDerInteger.GetInstance(AKeyInfo.ParsePrivateKey());
+    if LAlgID.Parameters <> nil then
+    begin
+      LDsaPara := TDsaParameter.GetInstance(LAlgID.Parameters.ToAsn1Object() as TAsn1Object);
+      LDsaParams := TDsaParameters.Create(LDsaPara.P, LDsaPara.Q, LDsaPara.G);
+    end
+    else
+    begin
+      LDsaParams := nil;
+    end;
+    Result := TDsaPrivateKeyParameters.Create(LDsaX.Value, LDsaParams);
+    Exit;
+  end;
+
+ (* // TODO?
+  // EC keys
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdECPublicKey) then
+  begin
+    LECPrivateKeyObj := AKeyInfo.ParsePrivateKey();
+    LECPrivateKeySeq := TECPrivateKeyStructure.GetInstance(LECPrivateKeyObj);
+    LX962Params := TX962Parameters.GetInstance(LAlgID.Parameters);
+    LECParams := TECDomainParameters.FromX962Parameters(LX962Params);
+    Result := TECPrivateKeyParameters.Create('EC', LECPrivateKeySeq.GetKey(), LECParams);
+    Exit;
+  end; *)
+
+  // TODO: Add support for other key types (DH, ElGamal, GOST, EdDSA, etc.)
+  raise ENotSupportedCryptoLibException.CreateFmt('Key type with OID %s not yet supported', [LAlgOid.Id]);
+end;
+
+end.

+ 152 - 0
CryptoLib/src/Security/ClpPublicKeyFactory.pas

@@ -0,0 +1,152 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPublicKeyFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpIRsaKeyParameters,
+  ClpIDsaPublicKeyParameters,
+  ClpIECPublicKeyParameters,
+  ClpRsaKeyParameters,
+  ClpDsaPublicKeyParameters,
+  ClpECPublicKeyParameters,
+  ClpDsaParameters,
+  ClpECDomainParameters,
+  ClpPkcsObjectIdentifiers,
+  ClpX509ObjectIdentifiers,
+  ClpX9ObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpX9Asn1Objects,
+  ClpIX9Asn1Objects,
+  ClpX9ECC,
+  ClpX9ECParameters,
+  ClpIX9ECParameters,
+  ClpX509Asn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpAsn1Objects,
+  ClpECNamedCurveTable,
+  ClpIECC,
+  ClpIECDomainParameters,
+  ClpIDsaParameters,
+  ClpIDsaParameter,
+  ClpDsaParameter,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Factory for creating public key parameters from SubjectPublicKeyInfo.
+  /// </summary>
+  TPublicKeyFactory = class sealed(TObject)
+
+  public
+    class function CreateKey(const AKeyInfoData: TCryptoLibByteArray): IAsymmetricKeyParameter; overload; static;
+    class function CreateKey(const AInStr: TStream): IAsymmetricKeyParameter; overload; static;
+    class function CreateKey(const AKeyInfo: ISubjectPublicKeyInfo): IAsymmetricKeyParameter; overload; static;
+
+  end;
+
+implementation
+
+{ TPublicKeyFactory }
+
+class function TPublicKeyFactory.CreateKey(const AKeyInfoData: TCryptoLibByteArray): IAsymmetricKeyParameter;
+begin
+  Result := CreateKey(TSubjectPublicKeyInfo.GetInstance(TAsn1Sequence.GetInstance(AKeyInfoData) as TObject));
+end;
+
+class function TPublicKeyFactory.CreateKey(const AInStr: TStream): IAsymmetricKeyParameter;
+begin
+  Result := CreateKey(TSubjectPublicKeyInfo.GetInstance(TAsn1Object.FromStream(AInStr) as TAsn1Object));
+end;
+
+class function TPublicKeyFactory.CreateKey(const AKeyInfo: ISubjectPublicKeyInfo): IAsymmetricKeyParameter;
+var
+  LAlgID: IAlgorithmIdentifier;
+  LAlgOid: IDerObjectIdentifier;
+  LPubKey: IRsaPublicKeyStructure;
+  LDsaY: IDerInteger;
+  LDsaParams: IDsaParameters;
+  LDsaPara: IDsaParameter;
+  LX962Params: IX962Parameters;
+  LX9ECParams: IX9ECParameters;
+  LECParams: IECDomainParameters;
+  LQ: IECPoint;
+  LPublicKeyBytes: TCryptoLibByteArray;
+begin
+  if AKeyInfo = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+
+  LAlgID := AKeyInfo.Algorithm;
+  LAlgOid := LAlgID.Algorithm;
+
+  // RSA keys
+  if LAlgOid.Equals(TPkcsObjectIdentifiers.RsaEncryption) or
+    LAlgOid.Equals(TX509ObjectIdentifiers.IdEARsa) or
+    LAlgOid.Equals(TPkcsObjectIdentifiers.IdRsassaPss) or
+    LAlgOid.Equals(TPkcsObjectIdentifiers.IdRsaesOaep) then
+  begin
+    LPubKey := TRsaPublicKeyStructure.GetInstance(AKeyInfo.ParsePublicKey() as TObject);
+    Result := TRsaKeyParameters.Create(False, LPubKey.Modulus, LPubKey.PublicExponent);
+    Exit;
+  end;
+
+  // DSA keys
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdDsa) or
+    LAlgOid.Equals(TOiwObjectIdentifiers.DsaWithSha1) then
+  begin
+    LDsaY := TDerInteger.GetInstance(AKeyInfo.ParsePublicKey());
+    if LAlgID.Parameters <> nil then
+    begin
+      LDsaPara := TDsaParameter.GetInstance(LAlgID.Parameters.ToAsn1Object() as TAsn1Object);
+      LDsaParams := TDsaParameters.Create(LDsaPara.P, LDsaPara.Q, LDsaPara.G);
+    end
+    else
+    begin
+      LDsaParams := nil;
+    end;
+    Result := TDsaPublicKeyParameters.Create(LDsaY.Value, LDsaParams);
+    Exit;
+  end;
+
+ (* // TODO?
+ // EC keys
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdECPublicKey) then
+  begin
+    LX962Params := TX962Parameters.GetInstance(LAlgID.Parameters);
+    LECParams := TECDomainParameters.FromX962Parameters(LX962Params);
+    LPublicKeyBytes := AKeyInfo.PublicKey.GetBytes();
+    LQ := LECParams.Curve.DecodePoint(LPublicKeyBytes);
+    Result := TECPublicKeyParameters.Create('EC', LQ, LECParams);
+    Exit;
+  end; *)
+
+  // TODO: Add support for other key types (DH, ElGamal, GOST, EdDSA, etc.)
+  raise ENotSupportedCryptoLibException.CreateFmt('Key type with OID %s not yet supported', [LAlgOid.Id]);
+end;
+
+end.

+ 64 - 0
CryptoLib/src/Utils/ClpArrayUtils.pas

@@ -99,6 +99,11 @@ type
 
     class procedure ZeroFill(const buf: TCryptoLibByteArray); static;
 
+    class function Clone(const AData: TCryptoLibByteArray): TCryptoLibByteArray; overload; static;
+    class function Clone(const AData: TCryptoLibStringArray): TCryptoLibStringArray; overload; static;
+    class function Clone<T>(const AData: TCryptoLibGenericArray<T>): TCryptoLibGenericArray<T>; overload; static;
+    class function Clone<T>(const AData: TCryptoLibGenericArray<T>; const ACloneFunc: TFunc<T, T>): TCryptoLibGenericArray<T>; overload; static;
+
     class function NoZeroes(const data: TCryptoLibByteArray): Boolean; static;
 
   end;
@@ -499,4 +504,63 @@ begin
   Result[Length] := B;
 end;
 
+class function TArrayUtils.Clone(const AData: TCryptoLibByteArray): TCryptoLibByteArray;
+begin
+  if AData = nil then
+    Result := nil
+  else
+  begin
+    System.SetLength(Result, System.Length(AData));
+    System.Move(AData[0], Result[0], System.Length(AData) * System.SizeOf(Byte));
+  end;
+end;
+
+class function TArrayUtils.Clone(const AData: TCryptoLibStringArray): TCryptoLibStringArray;
+var
+  I: Int32;
+begin
+  if AData = nil then
+    Result := nil
+  else
+  begin
+    System.SetLength(Result, System.Length(AData));
+    for I := 0 to System.High(AData) do
+    begin
+      Result[I] := AData[I];
+    end;
+  end;
+end;
+
+class function TArrayUtils.Clone<T>(const AData: TCryptoLibGenericArray<T>): TCryptoLibGenericArray<T>;
+var
+  I: Int32;
+begin
+  if (AData = nil) or (System.Length(AData) = 0) then
+    Result := nil
+  else
+  begin
+    System.SetLength(Result, System.Length(AData));
+    for I := 0 to System.High(AData) do
+    begin
+      Result[I] := AData[I];
+    end;
+  end;
+end;
+
+class function TArrayUtils.Clone<T>(const AData: TCryptoLibGenericArray<T>; const ACloneFunc: TFunc<T, T>): TCryptoLibGenericArray<T>;
+var
+  I: Int32;
+begin
+  if (AData = nil) or (System.Length(AData) = 0) then
+    Result := nil
+  else
+  begin
+    System.SetLength(Result, System.Length(AData));
+    for I := 0 to System.High(AData) do
+    begin
+      Result[I] := ACloneFunc(AData[I]);
+    end;
+  end;
+end;
+
 end.

+ 22 - 0
CryptoLib/src/Utils/ClpCollectionUtilities.pas

@@ -23,6 +23,7 @@ interface
 
 uses
   SysUtils,
+  Generics.Collections,
   ClpCryptoLibTypes;
 
 type
@@ -45,6 +46,10 @@ type
     /// </summary>
     class function ToString<T>(const AC: TCryptoLibGenericArray<T>;
       const AConverter: TFunc<T, String>): String; reintroduce; overload; static;
+    /// <summary>
+    /// Create a proxy array from an enumerable collection (like TDictionary.Keys).
+    /// </summary>
+    class function Proxy<T>(const AEnumerable: IEnumerable<T>): TCryptoLibGenericArray<T>; static;
   end;
 
 implementation
@@ -105,4 +110,21 @@ begin
   end;
 end;
 
+class function TCollectionUtilities.Proxy<T>(const AEnumerable: IEnumerable<T>): TCryptoLibGenericArray<T>;
+var
+  LList: TList<T>;
+  LItem: T;
+begin
+  LList := TList<T>.Create();
+  try
+    for LItem in AEnumerable do
+    begin
+      LList.Add(LItem);
+    end;
+    Result := LList.ToArray();
+  finally
+    LList.Free;
+  end;
+end;
+
 end.

+ 210 - 0
CryptoLib/src/Utils/ClpCryptoLibComparers.pas

@@ -0,0 +1,210 @@
+{ *********************************************************************************** }
+{ *                              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 ClpCryptoLibComparers;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Generics.Defaults,
+  ClpIAsn1Objects,
+  ClpPlatform;
+
+type
+  /// <summary>
+  /// Equality comparer for IDerObjectIdentifier that uses value-based comparison
+  /// (Asn1Equals) instead of reference equality. Used with TDictionary.
+  /// </summary>
+  TOidEqualityComparer = class(TInterfacedObject, IEqualityComparer<IDerObjectIdentifier>)
+  strict private
+    function Equals(const ALeft, ARight: IDerObjectIdentifier): Boolean; reintroduce;
+    function GetHashCode(const AValue: IDerObjectIdentifier): Integer;
+  end;
+
+  /// <summary>
+  /// Comparer for IDerObjectIdentifier that uses value-based comparison
+  /// (Asn1Equals) for equality and ID string comparison for ordering.
+  /// Used with TList to ensure Contains, IndexOf, and Remove work correctly.
+  /// </summary>
+  TOidComparer = class(TInterfacedObject, IComparer<IDerObjectIdentifier>)
+  strict private
+    function Compare(const ALeft, ARight: IDerObjectIdentifier): Integer;
+  end;
+
+  /// <summary>
+  /// Equality comparer for String that uses ordinal case-insensitive comparison.
+  /// Uses invariant culture for case conversion (OrdinalIgnoreCase).
+  /// Used with TDictionary for case-insensitive string keys.
+  /// </summary>
+  TOrdinalIgnoreCaseEqualityComparer = class(TInterfacedObject, IEqualityComparer<String>)
+  strict private
+    function Equals(const ALeft, ARight: String): Boolean; reintroduce;
+    function GetHashCode(const AValue: String): Integer;
+  end;
+
+  /// <summary>
+  /// Static utility class providing access to custom comparers for CryptoLib types.
+  /// </summary>
+  TCryptoLibComparers = class sealed(TObject)
+  strict private
+    class var
+      FOidEqualityComparer: IEqualityComparer<IDerObjectIdentifier>;
+      FOidComparer: IComparer<IDerObjectIdentifier>;
+      FOrdinalIgnoreCaseEqualityComparer: IEqualityComparer<String>;
+    class constructor Create;
+  public
+    /// <summary>
+    /// Gets the OID equality comparer for use with TDictionary.
+    /// </summary>
+    class property OidEqualityComparer: IEqualityComparer<IDerObjectIdentifier> read FOidEqualityComparer;
+    
+    /// <summary>
+    /// Gets the OID comparer for use with TList.
+    /// </summary>
+    class property OidComparer: IComparer<IDerObjectIdentifier> read FOidComparer;
+    
+    /// <summary>
+    /// Gets the string ordinal ignore case equality comparer for use with TDictionary.
+    /// </summary>
+    class property OrdinalIgnoreCaseEqualityComparer: IEqualityComparer<String> read FOrdinalIgnoreCaseEqualityComparer;
+  end;
+
+implementation
+
+{ TOidEqualityComparer }
+
+function TOidEqualityComparer.Equals(const ALeft, ARight: IDerObjectIdentifier): Boolean;
+begin
+  // Use value-based comparison via Asn1Equals
+  if ALeft = ARight then
+  begin
+    Result := True;
+    Exit;
+  end;
+  
+  if (ALeft = nil) or (ARight = nil) then
+  begin
+    Result := False;
+    Exit;
+  end;
+  
+  // Use Asn1Equals which compares the contents byte arrays
+  Result := ALeft.Equals(ARight);
+end;
+
+function TOidEqualityComparer.GetHashCode(const AValue: IDerObjectIdentifier): Integer;
+begin
+  if AValue = nil then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Use Asn1GetHashCode which is based on contents byte array
+  Result := AValue.CallAsn1GetHashCode();
+end;
+
+{ TOidComparer }
+
+function TOidComparer.Compare(const ALeft, ARight: IDerObjectIdentifier): Integer;
+begin
+  // If both are nil, they're equal
+  if (ALeft = nil) and (ARight = nil) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // nil is less than non-nil
+  if ALeft = nil then
+  begin
+    Result := -1;
+    Exit;
+  end;
+  
+  if ARight = nil then
+  begin
+    Result := 1;
+    Exit;
+  end;
+  
+  // If they're the same reference, they're equal
+  if ALeft = ARight then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Use value-based equality check (Asn1Equals)
+  if ALeft.Equals(ARight) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // If not equal, compare by ID string for ordering
+  if ALeft.Id < ARight.Id then
+    Result := -1
+  else if ALeft.Id > ARight.Id then
+    Result := 1
+  else
+    Result := 0; // Should not happen if Equals returned False, but handle it
+end;
+
+{ TOrdinalIgnoreCaseEqualityComparer }
+
+function TOrdinalIgnoreCaseEqualityComparer.Equals(const ALeft, ARight: String): Boolean;
+begin
+  // Use ordinal case-insensitive comparison (invariant culture)
+  Result := TPlatform.EqualsIgnoreCase(ALeft, ARight);
+end;
+
+function TOrdinalIgnoreCaseEqualityComparer.GetHashCode(const AValue: String): Integer;
+var
+  LLowerValue: String;
+  I: Int32;
+begin
+  if System.Length(AValue) = 0 then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Convert to lowercase using invariant culture for consistent hashing
+  LLowerValue := TPlatform.ToLowerInvariant(AValue);
+  
+  // Compute hash code from lowercase string
+  // Using a simple hash algorithm (FNV-1a style)
+  Result := 2166136261; // FNV offset basis
+  for I := 1 to System.Length(LLowerValue) do
+  begin
+    Result := (Result xor Ord(LLowerValue[I])) * 16777619; // FNV prime
+  end;
+end;
+
+{ TCryptoLibComparers }
+
+class constructor TCryptoLibComparers.Create;
+begin
+  FOidEqualityComparer := TOidEqualityComparer.Create();
+  FOidComparer := TOidComparer.Create();
+  FOrdinalIgnoreCaseEqualityComparer := TOrdinalIgnoreCaseEqualityComparer.Create();
+end;
+
+end.

+ 378 - 0
CryptoLib/src/Utils/ClpIPAddressUtilities.pas

@@ -0,0 +1,378 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPAddressUtilities;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpPlatform;
+
+type
+  /// <summary>
+  /// IP Address utility class for validating IPv4 and IPv6 addresses.
+  /// </summary>
+  TIPAddressUtilities = class sealed(TObject)
+  strict private
+    class function IsParseableIPv4Octet(const AStr: String; APos, AEnd: Int32): Boolean; static;
+    class function IsParseableIPv4Mask(const AStr: String): Boolean; static;
+    class function IsParseableIPv6Segment(const AStr: String; APos, AEnd: Int32): Boolean; static;
+    class function IsParseableIPv6Mask(const AStr: String): Boolean; static;
+    class function IsParseableDecimal(const AStr: String; APos, AEnd, AMaxLength: Int32;
+      AAllowLeadingZero: Boolean; AMinValue, AMaxValue: Int32): Boolean; static;
+    class function IsParseableHexadecimal(const AStr: String; APos, AEnd, AMaxLength: Int32;
+      AAllowLeadingZero: Boolean; AMinValue, AMaxValue: Int32): Boolean; static;
+    class function GetDigitDecimal(const AStr: String; APos: Int32): Int32; static;
+    class function GetDigitHexadecimal(const AStr: String; APos: Int32): Int32; static;
+
+  public
+    /// <summary>
+    /// Validate the given IPv4 or IPv6 address.
+    /// </summary>
+    class function IsValid(const AAddress: String): Boolean; static;
+    /// <summary>
+    /// Validate the given IPv4 or IPv6 address and netmask.
+    /// </summary>
+    class function IsValidWithNetMask(const AAddress: String): Boolean; static;
+    /// <summary>
+    /// Validate the given IPv4 address.
+    /// </summary>
+    class function IsValidIPv4(const AAddress: String): Boolean; static;
+    /// <summary>
+    /// Validate the given IPv4 address with netmask.
+    /// </summary>
+    class function IsValidIPv4WithNetmask(const AAddress: String): Boolean; static;
+    /// <summary>
+    /// Validate the given IPv6 address.
+    /// </summary>
+    class function IsValidIPv6(const AAddress: String): Boolean; static;
+    /// <summary>
+    /// Validate the given IPv6 address with netmask.
+    /// </summary>
+    class function IsValidIPv6WithNetmask(const AAddress: String): Boolean; static;
+  end;
+
+implementation
+
+{ TIPAddressUtilities }
+
+class function TIPAddressUtilities.IsValid(const AAddress: String): Boolean;
+begin
+  Result := IsValidIPv4(AAddress) or IsValidIPv6(AAddress);
+end;
+
+class function TIPAddressUtilities.IsValidWithNetMask(const AAddress: String): Boolean;
+begin
+  Result := IsValidIPv4WithNetmask(AAddress) or IsValidIPv6WithNetmask(AAddress);
+end;
+
+class function TIPAddressUtilities.IsValidIPv4(const AAddress: String): Boolean;
+var
+  LLength, LPos, LEnd, LOctetIndex: Int32;
+begin
+  LLength := System.Length(AAddress);
+  if (LLength < 7) or (LLength > 15) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LPos := 1; // 1-based position
+  for LOctetIndex := 0 to 2 do
+  begin
+    // TPlatform.IndexOf returns 1-based index (0 if not found)
+    LEnd := TPlatform.IndexOf(AAddress, '.', LPos);
+    if LEnd = 0 then
+    begin
+      Result := False;
+      Exit;
+    end;
+
+    // IsParseableIPv4Octet expects 1-based positions
+    if not IsParseableIPv4Octet(AAddress, LPos, LEnd) then
+    begin
+      Result := False;
+      Exit;
+    end;
+
+    LPos := LEnd + 1; // Skip the '.' character
+  end;
+
+  // Check last octet
+  Result := IsParseableIPv4Octet(AAddress, LPos, LLength + 1);
+end;
+
+class function TIPAddressUtilities.IsValidIPv4WithNetmask(const AAddress: String): Boolean;
+var
+  LIndex: Int32;
+  LBefore, LAfter: String;
+begin
+  LIndex := TPlatform.IndexOf(AAddress, '/');
+  if LIndex = 0 then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // LIndex is 1-based position of '/'
+  LBefore := System.Copy(AAddress, 1, LIndex - 1);
+  LAfter := System.Copy(AAddress, LIndex + 1, System.Length(AAddress) - LIndex);
+
+  Result := IsValidIPv4(LBefore) and (IsValidIPv4(LAfter) or IsParseableIPv4Mask(LAfter));
+end;
+
+class function TIPAddressUtilities.IsValidIPv6(const AAddress: String): Boolean;
+var
+  LLength, LPos, LEnd, LSegmentCount: Int32;
+  LTemp, LValue: String;
+  LDoubleColonFound: Boolean;
+begin
+  LLength := System.Length(AAddress);
+  if LLength = 0 then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // Check first character
+  if (AAddress[1] <> ':') and (GetDigitHexadecimal(AAddress, 1) < 0) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LSegmentCount := 0;
+  LTemp := AAddress + ':';
+  LDoubleColonFound := False;
+
+  LPos := 1; // 1-based position
+  while LPos <= System.Length(LTemp) do
+  begin
+    LEnd := TPlatform.IndexOf(LTemp, ':', LPos);
+    if LEnd = 0 then
+      Break;
+
+    if LSegmentCount = 8 then
+    begin
+      Result := False;
+      Exit;
+    end;
+
+    if LPos <> LEnd then
+    begin
+      // Extract segment (1-based positions)
+      LValue := System.Copy(LTemp, LPos, LEnd - LPos);
+
+      // Check if this is the last segment and contains IPv4 notation
+      if (LEnd = System.Length(LTemp)) and (TPlatform.IndexOf(LValue, '.') > 0) then
+      begin
+        // Add an extra one as address covers 2 words
+        System.Inc(LSegmentCount);
+        if LSegmentCount = 8 then
+        begin
+          Result := False;
+          Exit;
+        end;
+
+        if not IsValidIPv4(LValue) then
+        begin
+          Result := False;
+          Exit;
+        end;
+      end
+      else if not IsParseableIPv6Segment(LTemp, LPos, LEnd) then
+      begin
+        Result := False;
+        Exit;
+      end;
+    end
+    else
+    begin
+      // Empty segment (double colon)
+      if (LEnd <> 2) and (LEnd <> System.Length(LTemp)) and LDoubleColonFound then
+      begin
+        Result := False;
+        Exit;
+      end;
+
+      LDoubleColonFound := True;
+    end;
+
+    LPos := LEnd + 1; // Skip the ':' character
+    System.Inc(LSegmentCount);
+  end;
+
+  Result := (LSegmentCount = 8) or LDoubleColonFound;
+end;
+
+class function TIPAddressUtilities.IsValidIPv6WithNetmask(const AAddress: String): Boolean;
+var
+  LIndex: Int32;
+  LBefore, LAfter: String;
+begin
+  LIndex := TPlatform.IndexOf(AAddress, '/');
+  if LIndex = 0 then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // LIndex is 1-based position of '/'
+  LBefore := System.Copy(AAddress, 1, LIndex - 1);
+  LAfter := System.Copy(AAddress, LIndex + 1, System.Length(AAddress) - LIndex);
+
+  Result := IsValidIPv6(LBefore) and (IsValidIPv6(LAfter) or IsParseableIPv6Mask(LAfter));
+end;
+
+class function TIPAddressUtilities.IsParseableIPv4Mask(const AStr: String): Boolean;
+begin
+  Result := IsParseableDecimal(AStr, 1, System.Length(AStr) + 1, 2, False, 0, 32);
+end;
+
+class function TIPAddressUtilities.IsParseableIPv4Octet(const AStr: String; APos, AEnd: Int32): Boolean;
+begin
+  // APos and AEnd are 1-based
+  Result := IsParseableDecimal(AStr, APos, AEnd, 3, True, 0, 255);
+end;
+
+class function TIPAddressUtilities.IsParseableIPv6Mask(const AStr: String): Boolean;
+begin
+  Result := IsParseableDecimal(AStr, 1, System.Length(AStr) + 1, 3, False, 1, 128);
+end;
+
+class function TIPAddressUtilities.IsParseableIPv6Segment(const AStr: String; APos, AEnd: Int32): Boolean;
+begin
+  // APos and AEnd are 1-based
+  Result := IsParseableHexadecimal(AStr, APos, AEnd, 4, True, $0000, $FFFF);
+end;
+
+class function TIPAddressUtilities.IsParseableDecimal(const AStr: String; APos, AEnd, AMaxLength: Int32;
+  AAllowLeadingZero: Boolean; AMinValue, AMaxValue: Int32): Boolean;
+var
+  LLength, LValue: Int32;
+  LD: Int32;
+begin
+  // APos and AEnd are 1-based
+  LLength := AEnd - APos;
+  if (LLength < 1) or (LLength > AMaxLength) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // Check for leading zero
+  if (LLength > 1) and (not AAllowLeadingZero) and (AStr[APos] = '0') then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LValue := 0;
+  while APos < AEnd do
+  begin
+    LD := GetDigitDecimal(AStr, APos);
+    if LD < 0 then
+    begin
+      Result := False;
+      Exit;
+    end;
+
+    LValue := LValue * 10;
+    LValue := LValue + LD;
+    System.Inc(APos);
+  end;
+
+  Result := (LValue >= AMinValue) and (LValue <= AMaxValue);
+end;
+
+class function TIPAddressUtilities.IsParseableHexadecimal(const AStr: String; APos, AEnd, AMaxLength: Int32;
+  AAllowLeadingZero: Boolean; AMinValue, AMaxValue: Int32): Boolean;
+var
+  LLength, LValue: Int32;
+  LD: Int32;
+begin
+  // APos and AEnd are 1-based
+  LLength := AEnd - APos;
+  if (LLength < 1) or (LLength > AMaxLength) then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  // Check for leading zero
+  if (LLength > 1) and (not AAllowLeadingZero) and (AStr[APos] = '0') then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  LValue := 0;
+  while APos < AEnd do
+  begin
+    LD := GetDigitHexadecimal(AStr, APos);
+    if LD < 0 then
+    begin
+      Result := False;
+      Exit;
+    end;
+
+    LValue := LValue * 16;
+    LValue := LValue + LD;
+    System.Inc(APos);
+  end;
+
+  Result := (LValue >= AMinValue) and (LValue <= AMaxValue);
+end;
+
+class function TIPAddressUtilities.GetDigitDecimal(const AStr: String; APos: Int32): Int32;
+var
+  LC: Char;
+  LD: UInt32;
+begin
+  // APos is 1-based
+  LC := AStr[APos];
+  LD := UInt32(Ord(LC) - Ord('0'));
+  if LD <= 9 then
+    Result := Int32(LD)
+  else
+    Result := -1;
+end;
+
+class function TIPAddressUtilities.GetDigitHexadecimal(const AStr: String; APos: Int32): Int32;
+var
+  LC: Char;
+  LD: UInt32;
+begin
+  // APos is 1-based
+  LC := AStr[APos];
+  // Convert to lowercase for comparison
+  LD := UInt32(Ord(LC)) or $20;
+  if LD >= UInt32(Ord('a')) then
+    LD := LD - (UInt32(Ord('a')) - 10)
+  else
+    LD := LD - UInt32(Ord('0'));
+  
+  if LD <= 16 then
+    Result := Int32(LD)
+  else
+    Result := -1;
+end;
+
+end.

+ 192 - 0
CryptoLib/src/Utils/ClpPlatform.pas

@@ -43,6 +43,54 @@ type
     /// Compare two strings ignoring case.
     /// </summary>
     class function EqualsIgnoreCase(const A, B: String): Boolean; static;
+    /// <summary>
+    /// Trim whitespace from both ends of a string.
+    /// </summary>
+    class function Trim(const AStr: String): String; static;
+    /// <summary>
+    /// Check if a string starts with a specified value (case-sensitive).
+    /// </summary>
+    class function StartsWith(const AValue: String; const AStr: String): Boolean; overload; static;
+    /// <summary>
+    /// Check if a string starts with a specified value (optionally case-insensitive).
+    /// </summary>
+    class function StartsWith(const AValue: String; const AStr: String; AIgnoreCase: Boolean): Boolean; overload; static;
+    /// <summary>
+    /// Convert string to lowercase using invariant culture.
+    /// </summary>
+    class function ToLowerInvariant(const AStr: String): String; static;
+    /// <summary>
+    /// Find the index of a character in a string (1-based, returns 0 if not found).
+    /// </summary>
+    class function IndexOf(const ASource: String; AValue: Char): Int32; overload; static;
+    /// <summary>
+    /// Find the index of a character in a string starting from a position (1-based, returns 0 if not found).
+    /// </summary>
+    class function IndexOf(const ASource: String; AValue: Char; AStartIndex: Int32): Int32; overload; static;
+    /// <summary>
+    /// Find the index of a substring in a string (1-based, returns 0 if not found).
+    /// </summary>
+    class function IndexOf(const ASource: String; const AValue: String): Int32; overload; static;
+    /// <summary>
+    /// Find the index of a substring in a string starting from a position (1-based, returns 0 if not found).
+    /// </summary>
+    class function IndexOf(const ASource: String; const AValue: String; AStartIndex: Int32): Int32; overload; static;
+    /// <summary>
+    /// Check if a string ends with a specified value (case-sensitive).
+    /// </summary>
+    class function EndsWith(const ASource: String; const ASuffix: String): Boolean; overload; static;
+    /// <summary>
+    /// Check if a string ends with a specified value (optionally case-insensitive).
+    /// </summary>
+    class function EndsWith(const ASource: String; const ASuffix: String; AIgnoreCase: Boolean): Boolean; overload; static;
+    /// <summary>
+    /// Find the last index of a substring in a string (1-based, returns 0 if not found).
+    /// </summary>
+    class function LastIndexOf(const ASource: String; const AValue: String): Int32; static;
+    /// <summary>
+    /// Check if the current process is 64-bit.
+    /// </summary>
+    class function Is64BitProcess: Boolean; static;
   end;
 
 implementation
@@ -81,4 +129,148 @@ begin
   Result := SameText(A, B);
 end;
 
+class function TPlatform.Trim(const AStr: String): String;
+begin
+  Result := SysUtils.Trim(AStr);
+end;
+
+class function TPlatform.StartsWith(const AValue: String; const AStr: String): Boolean;
+begin
+  Result := StartsWith(AValue, AStr, False);
+end;
+
+class function TPlatform.StartsWith(const AValue: String; const AStr: String; AIgnoreCase: Boolean): Boolean;
+var
+  LValueLen, LStrLen: Int32;
+  LSubStr: String;
+begin
+  LValueLen := System.Length(AValue);
+  LStrLen := System.Length(AStr);
+  if (LValueLen = 0) or (LStrLen < LValueLen) then
+  begin
+    Result := False;
+    Exit;
+  end;
+  LSubStr := System.Copy(AStr, 1, LValueLen);
+  if AIgnoreCase then
+    Result := SameText(LSubStr, AValue)
+  else
+    Result := (LSubStr = AValue);
+end;
+
+class function TPlatform.ToLowerInvariant(const AStr: String): String;
+begin
+  Result := LowerCase(AStr);
+end;
+
+class function TPlatform.IndexOf(const ASource: String; AValue: Char): Int32;
+begin
+  Result := System.Pos(AValue, ASource);
+end;
+
+class function TPlatform.IndexOf(const ASource: String; AValue: Char; AStartIndex: Int32): Int32;
+var
+  LPos: Int32;
+  LSubStr: String;
+begin
+  // AStartIndex is 1-based for Pascal strings
+  if (AStartIndex < 1) or (AStartIndex > System.Length(ASource)) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Search in substring starting from AStartIndex (1-based)
+  LSubStr := System.Copy(ASource, AStartIndex, System.Length(ASource) - AStartIndex + 1);
+  LPos := System.Pos(AValue, LSubStr);
+  if LPos > 0 then
+    Result := AStartIndex + (LPos - 1)  // Convert relative position to absolute (1-based)
+  else
+    Result := 0;
+end;
+
+class function TPlatform.IndexOf(const ASource: String; const AValue: String): Int32;
+begin
+  Result := System.Pos(AValue, ASource);
+end;
+
+class function TPlatform.IndexOf(const ASource: String; const AValue: String; AStartIndex: Int32): Int32;
+var
+  LPos: Int32;
+  LSubStr: String;
+begin
+  // AStartIndex is 1-based for Pascal strings
+  if (AStartIndex < 1) or (AStartIndex > System.Length(ASource)) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Search in substring starting from AStartIndex (1-based)
+  LSubStr := System.Copy(ASource, AStartIndex, System.Length(ASource) - AStartIndex + 1);
+  LPos := System.Pos(AValue, LSubStr);
+  if LPos > 0 then
+    Result := AStartIndex + (LPos - 1)  // Convert relative position to absolute (1-based)
+  else
+    Result := 0;
+end;
+
+class function TPlatform.EndsWith(const ASource: String; const ASuffix: String): Boolean;
+begin
+  Result := EndsWith(ASource, ASuffix, False);
+end;
+
+class function TPlatform.EndsWith(const ASource: String; const ASuffix: String; AIgnoreCase: Boolean): Boolean;
+var
+  LSourceLen, LSuffixLen: Int32;
+  LSubStr: String;
+begin
+  LSourceLen := System.Length(ASource);
+  LSuffixLen := System.Length(ASuffix);
+  if (LSuffixLen = 0) or (LSourceLen < LSuffixLen) then
+  begin
+    Result := False;
+    Exit;
+  end;
+  LSubStr := System.Copy(ASource, LSourceLen - LSuffixLen + 1, LSuffixLen);
+  if AIgnoreCase then
+    Result := SameText(LSubStr, ASuffix)
+  else
+    Result := (LSubStr = ASuffix);
+end;
+
+class function TPlatform.LastIndexOf(const ASource: String; const AValue: String): Int32;
+var
+  I, LSourceLen, LValueLen: Int32;
+  LSubStr: String;
+begin
+  LSourceLen := System.Length(ASource);
+  LValueLen := System.Length(AValue);
+  
+  if (LValueLen = 0) or (LSourceLen < LValueLen) then
+  begin
+    Result := 0;
+    Exit;
+  end;
+  
+  // Search backwards from the end
+  for I := LSourceLen - LValueLen + 1 downto 1 do
+  begin
+    LSubStr := System.Copy(ASource, I, LValueLen);
+    if LSubStr = AValue then
+    begin
+      Result := I;  // 1-based position
+      Exit;
+    end;
+  end;
+  
+  Result := 0;  // Not found
+end;
+
+class function TPlatform.Is64BitProcess: Boolean;
+begin
+  // Check if SizeOf(Pointer) is 8 bytes (64-bit) or 4 bytes (32-bit)
+  Result := SizeOf(Pointer) = 8;
+end;
+
 end.

+ 680 - 0
CryptoLib/src/Utils/Pem/ClpPemObjects.pas

@@ -0,0 +1,680 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPemObjects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Generics.Collections,
+  ClpIPemObjects,
+  ClpCryptoLibTypes,
+  ClpEncoders,
+  ClpPlatform,
+  ClpStreams,
+  ClpStringUtils,
+  ClpConverters;
+
+type
+  /// <summary>
+  /// PEM header implementation.
+  /// </summary>
+  TPemHeader = class sealed(TInterfacedObject, IPemHeader)
+  strict private
+    FName: String;
+    FValue: String;
+
+    function GetName: String;
+    function GetValue: String;
+    function GetHashCodeInternal(const AStr: String): Int32;
+
+  public
+    constructor Create(const AName, AValue: String);
+
+    function GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt; {$ENDIF DELPHI}
+    function Equals(const AObj: IPemHeader): Boolean;
+    function ToString(): String; override;
+
+    property Name: String read GetName;
+    property Value: String read GetValue;
+  end;
+
+  /// <summary>
+  /// PEM object implementation.
+  /// </summary>
+  TPemObject = class sealed(TInterfacedObject, IPemObject, IPemObjectGenerator)
+  strict private
+    FType: String;
+    FHeaders: TCryptoLibGenericArray<IPemHeader>;
+    FContent: TCryptoLibByteArray;
+
+    function GetType: String;
+    function GetHeaders: TCryptoLibGenericArray<IPemHeader>;
+    function GetContent: TCryptoLibByteArray;
+
+  public
+    constructor Create(const AType: String; const AContent: TCryptoLibByteArray); overload;
+    constructor Create(const AType: String;
+      const AHeaders: TCryptoLibGenericArray<IPemHeader>;
+      const AContent: TCryptoLibByteArray); overload;
+
+    function Generate(): IPemObject;
+
+    property &Type: String read GetType;
+    property Headers: TCryptoLibGenericArray<IPemHeader> read GetHeaders;
+    property Content: TCryptoLibByteArray read GetContent;
+  end;
+
+  /// <summary>
+  /// PEM reader implementation.
+  /// </summary>
+  TPemReader = class sealed(TInterfacedObject, IPemReader)
+  strict private
+  const
+    LineLength = Int32(64);
+  var
+    FReader: TStream;
+    FTextBuffer: TStringBuilder;
+    FPushback: TStack<Int32>;
+
+    function BufferedString(): String;
+    function SeekDash(): Boolean;
+    function SeekColon(AUpTo: Int32): Boolean;
+    function ConsumeDash(): Boolean;
+    procedure SkipWhiteSpace();
+    function Expect(const AValue: String): Boolean;
+    function BufferUntilStopChar(AStopChar: Char; ASkipWhiteSpace: Boolean): Boolean;
+    procedure PushBack(AValue: Int32);
+    function ReadChar(): Int32;
+
+  public
+    constructor Create(const AReader: TStream);
+    destructor Destroy(); override;
+
+    function GetReader: TStream;
+    function ReadPemObject(): IPemObject;
+
+    property Reader: TStream read GetReader;
+  end;
+
+  /// <summary>
+  /// PEM writer implementation.
+  /// </summary>
+  TPemWriter = class sealed(TInterfacedObject, IPemWriter)
+  strict private
+  const
+    LineLength = Int32(64);
+  var
+    FWriter: TStream;
+    FNlLength: Int32;
+    FBuffer: TCryptoLibByteArray;
+
+    procedure WriteEncoded(const ABytes: TCryptoLibByteArray);
+    procedure WritePreEncapsulationBoundary(const AType: String);
+    procedure WritePostEncapsulationBoundary(const AType: String);
+    procedure WriteString(const AStr: String);
+    procedure WriteLine();
+
+  public
+    constructor Create(const AWriter: TStream);
+    destructor Destroy(); override;
+
+    function GetWriter: TStream;
+    function GetOutputSize(const AObj: IPemObject): Int32;
+    procedure WriteObject(const AObjGen: IPemObjectGenerator);
+
+    property Writer: TStream read GetWriter;
+  end;
+
+implementation
+
+{ TPemHeader }
+
+constructor TPemHeader.Create(const AName, AValue: String);
+begin
+  Inherited Create();
+  FName := AName;
+  FValue := AValue;
+end;
+
+function TPemHeader.GetName: String;
+begin
+  Result := FName;
+end;
+
+function TPemHeader.GetValue: String;
+begin
+  Result := FValue;
+end;
+
+function TPemHeader.GetHashCodeInternal(const AStr: String): Int32;
+begin
+  if AStr = '' then
+    Result := 1
+  else
+    Result := TStringUtils.GetStringHashCode(AStr);
+end;
+
+function TPemHeader.GetHashCode(): {$IFDEF DELPHI}Int32; {$ELSE}PtrInt; {$ENDIF DELPHI}
+begin
+  Result := GetHashCodeInternal(FName) + 31 * GetHashCodeInternal(FValue);
+end;
+
+function TPemHeader.Equals(const AObj: IPemHeader): Boolean;
+begin
+  if AObj = Self as IPemHeader then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  if AObj = nil then
+  begin
+    Result := False;
+    Exit;
+  end;
+
+  Result := (FName = AObj.Name) and (FValue = AObj.Value);
+end;
+
+function TPemHeader.ToString(): String;
+begin
+  Result := FName + ':' + FValue;
+end;
+
+{ TPemObject }
+
+constructor TPemObject.Create(const AType: String; const AContent: TCryptoLibByteArray);
+var
+  LEmptyHeaders: TCryptoLibGenericArray<IPemHeader>;
+begin
+  System.SetLength(LEmptyHeaders, 0);
+  Create(AType, LEmptyHeaders, AContent);
+end;
+
+constructor TPemObject.Create(const AType: String;
+  const AHeaders: TCryptoLibGenericArray<IPemHeader>;
+  const AContent: TCryptoLibByteArray);
+var
+  I: Int32;
+begin
+  Inherited Create();
+  FType := AType;
+  System.SetLength(FHeaders, System.Length(AHeaders));
+  for I := 0 to System.Length(AHeaders) - 1 do
+  begin
+    FHeaders[I] := AHeaders[I];
+  end;
+  FContent := AContent;
+end;
+
+function TPemObject.GetType: String;
+begin
+  Result := FType;
+end;
+
+function TPemObject.GetHeaders: TCryptoLibGenericArray<IPemHeader>;
+begin
+  Result := FHeaders;
+end;
+
+function TPemObject.GetContent: TCryptoLibByteArray;
+begin
+  Result := FContent;
+end;
+
+function TPemObject.Generate(): IPemObject;
+begin
+  Result := Self as IPemObject;
+end;
+
+{ TPemReader }
+
+constructor TPemReader.Create(const AReader: TStream);
+begin
+  Inherited Create();
+  if AReader = nil then
+    raise EArgumentNilCryptoLibException.Create('Reader cannot be nil');
+  FReader := AReader;
+  FTextBuffer := TStringBuilder.Create();
+  FPushback := TStack<Int32>.Create();
+end;
+
+destructor TPemReader.Destroy();
+begin
+  FPushback.Free();
+  FTextBuffer.Free();
+  Inherited Destroy();
+end;
+
+function TPemReader.GetReader: TStream;
+begin
+  Result := FReader;
+end;
+
+function TPemReader.ReadChar(): Int32;
+var
+  LByte: Byte;
+  LBytesRead: Int32;
+begin
+  if FPushback.Count > 0 then
+  begin
+    Result := FPushback.Pop();
+    Exit;
+  end;
+
+  LBytesRead := FReader.Read(LByte, 1);
+  if LBytesRead = 0 then
+    Result := -1
+  else
+    Result := Int32(LByte);
+end;
+
+procedure TPemReader.PushBack(AValue: Int32);
+begin
+  FPushback.Push(AValue);
+end;
+
+function TPemReader.BufferedString(): String;
+begin
+  Result := FTextBuffer.ToString();
+  FTextBuffer.Length := 0;
+end;
+
+function TPemReader.SeekDash(): Boolean;
+var
+  LC: Int32;
+begin
+  LC := ReadChar();
+  while LC >= 0 do
+  begin
+    if LC = Ord('-') then
+      Break;
+    LC := ReadChar();
+  end;
+
+  PushBack(LC);
+  Result := LC >= 0;
+end;
+
+function TPemReader.SeekColon(AUpTo: Int32): Boolean;
+var
+  LC: Int32;
+  LRead: TList<Int32>;
+  I, LReadPos: Int32;
+  LColonFound: Boolean;
+begin
+  LC := 0;
+  LColonFound := False;
+  LRead := TList<Int32>.Create();
+  try
+    while (AUpTo >= 0) and (LC >= 0) do
+    begin
+      LC := ReadChar();
+      LRead.Add(LC);
+      if LC = Ord(':') then
+      begin
+        LColonFound := True;
+        Break;
+      end;
+      System.Dec(AUpTo);
+    end;
+
+    LReadPos := LRead.Count;
+    while LReadPos > 0 do
+    begin
+      System.Dec(LReadPos);
+      PushBack(LRead[LReadPos]);
+    end;
+
+    Result := LColonFound;
+  finally
+    LRead.Free();
+  end;
+end;
+
+function TPemReader.ConsumeDash(): Boolean;
+var
+  LC: Int32;
+begin
+  LC := ReadChar();
+  while LC >= 0 do
+  begin
+    if LC <> Ord('-') then
+      Break;
+    LC := ReadChar();
+  end;
+
+  PushBack(LC);
+  Result := LC >= 0;
+end;
+
+procedure TPemReader.SkipWhiteSpace();
+var
+  LC: Int32;
+begin
+  LC := ReadChar();
+  while LC >= 0 do
+  begin
+    if LC > Ord(' ') then
+      Break;
+    LC := ReadChar();
+  end;
+
+  PushBack(LC);
+end;
+
+function TPemReader.Expect(const AValue: String): Boolean;
+var
+  I, LPos: Int32;
+  LC: Int32;
+begin
+  for I := 1 to System.Length(AValue) do
+  begin
+    LC := ReadChar();
+    if LC <> Ord(AValue[I]) then
+    begin
+      // Push back the character we just read
+      PushBack(LC);
+      // Push back all previously read characters
+      LPos := I - 1;
+      while LPos > 0 do
+      begin
+        PushBack(Ord(AValue[LPos]));
+        System.Dec(LPos);
+      end;
+      Result := False;
+      Exit;
+    end;
+  end;
+  Result := True;
+end;
+
+function TPemReader.BufferUntilStopChar(AStopChar: Char; ASkipWhiteSpace: Boolean): Boolean;
+var
+  LC: Int32;
+begin
+  LC := ReadChar();
+  while LC >= 0 do
+  begin
+    if ASkipWhiteSpace and (LC <= Ord(' ')) then
+    begin
+      LC := ReadChar();
+      Continue;
+    end;
+
+    if LC = Ord(AStopChar) then
+    begin
+      PushBack(LC);
+      Break;
+    end;
+
+    // Append character to buffer
+    FTextBuffer.Append(Char(LC));
+    LC := ReadChar();
+  end;
+
+  Result := LC >= 0;
+end;
+
+function TPemReader.ReadPemObject(): IPemObject;
+var
+  LType: String;
+  LHeaders: TList<IPemHeader>;
+  LKey, LValue: String;
+  LC: Int32;
+  LPayload: String;
+  LDecodedContent: TCryptoLibByteArray;
+  I: Int32;
+  LHeadersArray: TCryptoLibGenericArray<IPemHeader>;
+begin
+  Result := nil;
+
+  // Look for BEGIN
+  while True do
+  begin
+    // Seek a leading dash, ignore anything up to that point.
+    if not SeekDash() then
+      Exit;
+
+    // consume dash [-----]BEGIN ...
+    if not ConsumeDash() then
+      raise EIOCryptoLibException.Create('no data after consuming leading dashes');
+
+    SkipWhiteSpace();
+
+    if Expect('BEGIN') then
+      Break;
+  end;
+
+  SkipWhiteSpace();
+
+  // Consume type, accepting whitespace
+  if not BufferUntilStopChar('-', False) then
+    raise EIOCryptoLibException.Create('ran out of data before consuming type');
+
+  LType := TPlatform.Trim(BufferedString());
+
+  // Consume dashes after type.
+  if not ConsumeDash() then
+    raise EIOCryptoLibException.Create('ran out of data consuming header');
+
+  SkipWhiteSpace();
+
+  // Read ahead looking for headers.
+  // Look for a colon for up to 64 characters, as an indication there might be a header.
+  LHeaders := TList<IPemHeader>.Create();
+  try
+    while SeekColon(LineLength) do
+    begin
+      if not BufferUntilStopChar(':', False) then
+        raise EIOCryptoLibException.Create('ran out of data reading header key value');
+
+      LKey := TPlatform.Trim(BufferedString());
+
+      LC := ReadChar();
+      if LC <> Ord(':') then
+        raise EIOCryptoLibException.Create('expected colon');
+
+      // We are going to look for well formed headers, if they do not end with a "LF" we cannot
+      // discern where they end.
+      if not BufferUntilStopChar(#10, False) then
+        raise EIOCryptoLibException.Create('ran out of data before consuming header value');
+
+      SkipWhiteSpace();
+
+      LValue := TPlatform.Trim(BufferedString());
+      LHeaders.Add(TPemHeader.Create(LKey, LValue));
+    end;
+
+    // Consume payload, ignoring all white space until we encounter a '-'
+    SkipWhiteSpace();
+
+    if not BufferUntilStopChar('-', True) then
+      raise EIOCryptoLibException.Create('ran out of data before consuming payload');
+
+    LPayload := BufferedString();
+
+    // Seek the start of the end.
+    if not SeekDash() then
+      raise EIOCryptoLibException.Create('did not find leading ''-''');
+
+    if not ConsumeDash() then
+      raise EIOCryptoLibException.Create('no data after consuming trailing dashes');
+
+    if not Expect('END ' + LType) then
+      raise EIOCryptoLibException.Create('END ' + LType + ' was not found.');
+
+    if not SeekDash() then
+      raise EIOCryptoLibException.Create('did not find ending ''-''');
+
+    // consume trailing dashes.
+    ConsumeDash();
+
+    // Decode base64 payload
+    LDecodedContent := TBase64.Decode(LPayload);
+
+    // Convert headers list to array
+    System.SetLength(LHeadersArray, LHeaders.Count);
+    for I := 0 to LHeaders.Count - 1 do
+    begin
+      LHeadersArray[I] := LHeaders[I];
+    end;
+
+    Result := TPemObject.Create(LType, LHeadersArray, LDecodedContent);
+  finally
+    LHeaders.Free();
+  end;
+end;
+
+{ TPemWriter }
+
+constructor TPemWriter.Create(const AWriter: TStream);
+begin
+  Inherited Create();
+  if AWriter = nil then
+    raise EArgumentNilCryptoLibException.Create('Writer cannot be nil');
+  FWriter := AWriter;
+  FNlLength := System.Length(sLineBreak);
+  System.SetLength(FBuffer, LineLength);
+end;
+
+destructor TPemWriter.Destroy();
+begin
+  Inherited Destroy();
+end;
+
+function TPemWriter.GetWriter: TStream;
+begin
+  Result := FWriter;
+end;
+
+procedure TPemWriter.WriteString(const AStr: String);
+var
+  LBytes: TCryptoLibByteArray;
+begin
+  LBytes := TConverters.ConvertStringToBytes(AStr, TEncoding.ASCII);
+  if System.Length(LBytes) > 0 then
+  begin
+    FWriter.Write(LBytes[0], System.Length(LBytes));
+  end;
+end;
+
+procedure TPemWriter.WriteLine();
+begin
+  WriteString(sLineBreak);
+end;
+
+procedure TPemWriter.WritePreEncapsulationBoundary(const AType: String);
+begin
+  WriteString('-----BEGIN ' + AType + '-----');
+  WriteLine();
+end;
+
+procedure TPemWriter.WritePostEncapsulationBoundary(const AType: String);
+begin
+  WriteString('-----END ' + AType + '-----');
+  WriteLine();
+end;
+
+procedure TPemWriter.WriteEncoded(const ABytes: TCryptoLibByteArray);
+var
+  LEncoded: String;
+  LEncodedBytes: TCryptoLibByteArray;
+  I, LIndex, LRemaining: Int32;
+begin
+  LEncoded := TBase64.Encode(ABytes);
+  LEncodedBytes := TConverters.ConvertStringToBytes(LEncoded, TEncoding.ASCII);
+
+  I := 0;
+  while I < System.Length(LEncodedBytes) do
+  begin
+    LIndex := 0;
+    LRemaining := System.Length(LEncodedBytes) - I;
+    if LRemaining > LineLength then
+      LRemaining := LineLength;
+
+    while LIndex < LRemaining do
+    begin
+      FBuffer[LIndex] := LEncodedBytes[I + LIndex];
+      System.Inc(LIndex);
+    end;
+
+    FWriter.Write(FBuffer[0], LIndex);
+    WriteLine();
+    System.Inc(I, LineLength);
+  end;
+end;
+
+function TPemWriter.GetOutputSize(const AObj: IPemObject): Int32;
+var
+  LDataLen, I: Int32;
+  LHeader: IPemHeader;
+begin
+  // BEGIN and END boundaries.
+  Result := (2 * (System.Length(AObj.&Type) + 10 + FNlLength)) + 6 + 4;
+
+  if System.Length(AObj.Headers) > 0 then
+  begin
+    for I := 0 to System.Length(AObj.Headers) - 1 do
+    begin
+      LHeader := AObj.Headers[I];
+      Result := Result + System.Length(LHeader.Name) + 2 + System.Length(LHeader.Value) + FNlLength;
+    end;
+
+    Result := Result + FNlLength;
+  end;
+
+  // base64 encoding
+  LDataLen := ((System.Length(AObj.Content) + 2) div 3) * 4;
+
+  Result := Result + LDataLen + (((LDataLen + LineLength - 1) div LineLength) * FNlLength);
+end;
+
+procedure TPemWriter.WriteObject(const AObjGen: IPemObjectGenerator);
+var
+  LObj: IPemObject;
+  I: Int32;
+  LHeader: IPemHeader;
+begin
+  LObj := AObjGen.Generate();
+
+  WritePreEncapsulationBoundary(LObj.&Type);
+
+  if System.Length(LObj.Headers) > 0 then
+  begin
+    for I := 0 to System.Length(LObj.Headers) - 1 do
+    begin
+      LHeader := LObj.Headers[I];
+      WriteString(LHeader.Name);
+      WriteString(': ');
+      WriteString(LHeader.Value);
+      WriteLine();
+    end;
+
+    WriteLine();
+  end;
+
+  WriteEncoded(LObj.Content);
+  WritePostEncapsulationBoundary(LObj.&Type);
+end;
+
+end.