Quellcode durchsuchen

some PEM reader and writer enhancements

Ugochukwu Mmaduekwe vor 1 Tag
Ursprung
Commit
dfee06abb0
37 geänderte Dateien mit 3121 neuen und 724 gelöschten Zeilen
  1. 16 0
      CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr
  2. 11 3
      CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi
  3. 3 2
      CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr
  4. 10 2
      CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi
  5. 6 6
      CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr
  6. 213 1
      CryptoLib.Tests/src/Asn1/Pkcs/Pkcs10CertRequestTests.pas
  7. 2 0
      CryptoLib.Tests/src/Asn1/X509/DeltaCertificateTests.pas
  8. 393 0
      CryptoLib.Tests/src/OpenSsl/OpenSslReaderTests.pas
  9. 233 0
      CryptoLib.Tests/src/OpenSsl/OpenSslWriterTests.pas
  10. 93 1
      CryptoLib.Tests/src/Utils/Pem/PemReaderTests.pas
  11. 4 3
      CryptoLib/src/Asn1/ClpAsn1Objects.pas
  12. 2 2
      CryptoLib/src/Asn1/Pkcs/ClpPkcsAsn1Objects.pas
  13. 171 0
      CryptoLib/src/Asn1/Pkcs/ClpPkcsDHAsn1Objects.pas
  14. 201 0
      CryptoLib/src/Factories/ClpPrivateKeyInfoFactory.pas
  15. 1 1
      CryptoLib/src/Factories/ClpPublicKeyFactory.pas
  16. 36 0
      CryptoLib/src/GeneralUtilities/ClpArrayUtilities.pas
  17. 2 27
      CryptoLib/src/Interfaces/Asn1/Pkcs/ClpIPkcsAsn1Objects.pas
  18. 49 0
      CryptoLib/src/Interfaces/Asn1/Pkcs/ClpIPkcsDHAsn1Objects.pas
  19. 41 0
      CryptoLib/src/Interfaces/OpenSsl/ClpIOpenSslPemReader.pas
  20. 40 0
      CryptoLib/src/Interfaces/OpenSsl/ClpIOpenSslPemWriter.pas
  21. 2 63
      CryptoLib/src/Interfaces/Pem/ClpIPemObjects.pas
  22. 45 0
      CryptoLib/src/Interfaces/Pem/ClpIPemParser.pas
  23. 51 0
      CryptoLib/src/Interfaces/Pem/ClpIPemReader.pas
  24. 58 0
      CryptoLib/src/Interfaces/Pem/ClpIPemWriter.pas
  25. 273 0
      CryptoLib/src/OpenSsl/ClpOpenSslMiscPemGenerator.pas
  26. 364 0
      CryptoLib/src/OpenSsl/ClpOpenSslPemReader.pas
  27. 67 0
      CryptoLib/src/OpenSsl/ClpOpenSslPemWriter.pas
  28. 0 0
      CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk
  29. 5 1
      CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas
  30. 0 603
      CryptoLib/src/Pem/ClpPemObjects.pas
  31. 154 0
      CryptoLib/src/Pem/ClpPemParser.pas
  32. 368 0
      CryptoLib/src/Pem/ClpPemReader.pas
  33. 198 0
      CryptoLib/src/Pem/ClpPemWriter.pas
  34. 3 3
      CryptoLib/src/Pkcs/ClpPkcs10CertificationRequest.pas
  35. 2 2
      CryptoLib/src/X509/ClpX509AttrCertParser.pas
  36. 2 2
      CryptoLib/src/X509/ClpX509CertificateParser.pas
  37. 2 2
      CryptoLib/src/X509/ClpX509CrlParser.pas

+ 16 - 0
CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr

@@ -499,6 +499,20 @@ uses
   ClpICmsAsn1Objects in '..\..\CryptoLib\src\Interfaces\Asn1\Cms\ClpICmsAsn1Objects.pas',
   ClpICmsParsers in '..\..\CryptoLib\src\Interfaces\Asn1\Cms\ClpICmsParsers.pas',
   ClpCurve25519KeyUtilities in '..\..\CryptoLib\src\Crypto\Parameters\ClpCurve25519KeyUtilities.pas',
+  ClpOpenSslMiscPemGenerator in '..\..\CryptoLib\src\OpenSsl\ClpOpenSslMiscPemGenerator.pas',
+  ClpPrivateKeyInfoFactory in '..\..\CryptoLib\src\Factories\ClpPrivateKeyInfoFactory.pas',
+  ClpPkcsDHAsn1Objects in '..\..\CryptoLib\src\Asn1\Pkcs\ClpPkcsDHAsn1Objects.pas',
+  ClpIPkcsDHAsn1Objects in '..\..\CryptoLib\src\Interfaces\Asn1\Pkcs\ClpIPkcsDHAsn1Objects.pas',
+  ClpOpenSslPemWriter in '..\..\CryptoLib\src\OpenSsl\ClpOpenSslPemWriter.pas',
+  ClpIOpenSslPemWriter in '..\..\CryptoLib\src\Interfaces\OpenSsl\ClpIOpenSslPemWriter.pas',
+  ClpOpenSslPemReader in '..\..\CryptoLib\src\OpenSsl\ClpOpenSslPemReader.pas',
+  ClpIOpenSslPemReader in '..\..\CryptoLib\src\Interfaces\OpenSsl\ClpIOpenSslPemReader.pas',
+  ClpIPemParser in '..\..\CryptoLib\src\Interfaces\Pem\ClpIPemParser.pas',
+  ClpIPemReader in '..\..\CryptoLib\src\Interfaces\Pem\ClpIPemReader.pas',
+  ClpIPemWriter in '..\..\CryptoLib\src\Interfaces\Pem\ClpIPemWriter.pas',
+  ClpPemParser in '..\..\CryptoLib\src\Pem\ClpPemParser.pas',
+  ClpPemReader in '..\..\CryptoLib\src\Pem\ClpPemReader.pas',
+  ClpPemWriter in '..\..\CryptoLib\src\Pem\ClpPemWriter.pas',
   ClpFixedSecureRandom in '..\src\Utils\ClpFixedSecureRandom.pas',
   ClpShortenedDigest in '..\src\Utils\ClpShortenedDigest.pas',
   BlowfishTestVectors in '..\src\Crypto\BlowfishTestVectors.pas',
@@ -598,6 +612,8 @@ uses
   X509CertGenTests in '..\src\X509\X509CertGenTests.pas',
   CertTests in '..\src\Others\CertTests.pas',
   Curve25519KeyUtilitiesTests in '..\src\Others\Curve25519KeyUtilitiesTests.pas',
+  OpenSslReaderTests in '..\src\OpenSsl\OpenSslReaderTests.pas',
+  OpenSslWriterTests in '..\src\OpenSsl\OpenSslWriterTests.pas',
   CryptoLibTestBase in '..\src\CryptoLibTestBase.pas';
 
 begin

+ 11 - 3
CryptoLib.Tests/FreePascal.Tests/CryptoLib.Tests.lpi

@@ -23,7 +23,7 @@
           </Target>
           <SearchPaths>
             <IncludeFiles Value="$(ProjOutDir)"/>
-            <OtherUnitFiles Value="..\src\Asn1;..\src\Math;..\src\Math\EC\Custom\Sec;..\src\Others;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net"/>
+            <OtherUnitFiles Value="..\src\Asn1;..\src\Math;..\src\Math\EC\Custom\Sec;..\src\Others;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net;..\src\OpenSsl"/>
             <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
           </SearchPaths>
           <Parsing>
@@ -79,7 +79,7 @@
         <PackageName Value="FCL"/>
       </Item4>
     </RequiredPackages>
-    <Units Count="101">
+    <Units Count="103">
       <Unit0>
         <Filename Value="CryptoLib.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -485,6 +485,14 @@
         <Filename Value="..\src\Others\Curve25519KeyUtilitiesTests.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit100>
+      <Unit101>
+        <Filename Value="..\src\OpenSsl\OpenSslReaderTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit101>
+      <Unit102>
+        <Filename Value="..\src\OpenSsl\OpenSslWriterTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit102>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -495,7 +503,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="..\src\Asn1;..\src\Math;..\src\Math\EC\Custom\Sec;..\src\Others;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net"/>
+      <OtherUnitFiles Value="..\src\Asn1;..\src\Math;..\src\Math\EC\Custom\Sec;..\src\Others;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net;..\src\OpenSsl"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <CodeGeneration>

+ 3 - 2
CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr

@@ -26,8 +26,9 @@ uses
   X509ExtensionsTests, X509NameTests, SubjectKeyIdentifierTests, KeyUsageTests,
   GeneralNameTests, KMacTests, RSATests, PssTests, ISO9796Tests,
   RSABlindedTests, RSADigestSignerTests, X931SignerTests, CryptoLibTestBase,
-  X509CertGenTests, ClpFixedSecureRandom, ClpShortenedDigest,
-  IPAddressUtilitiesTests, PemReaderTests;
+  OpenSslReaderTests, OpenSslWriterTests, X509CertGenTests,
+  ClpFixedSecureRandom, ClpShortenedDigest, IPAddressUtilitiesTests,
+  PemReaderTests;
 
 {$R *.res}
 

+ 10 - 2
CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpi

@@ -39,7 +39,7 @@
         <PackageName Value="FCL"/>
       </Item2>
     </RequiredPackages>
-    <Units Count="101">
+    <Units Count="103">
       <Unit0>
         <Filename Value="CryptoLibConsole.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -444,6 +444,14 @@
         <Filename Value="..\src\Others\Curve25519KeyUtilitiesTests.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit100>
+      <Unit101>
+        <Filename Value="..\src\OpenSsl\OpenSslReaderTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit101>
+      <Unit102>
+        <Filename Value="..\src\OpenSsl\OpenSslWriterTests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit102>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -454,7 +462,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="..\src\Others;..\src\Asn1;..\src\Math\EC\Custom\Sec;..\src\Math;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net"/>
+      <OtherUnitFiles Value="..\src\Others;..\src\Asn1;..\src\Math\EC\Custom\Sec;..\src\Math;..\src\Security;..\src\Utils;..\src\Crypto;..\src\Math\EC;..\src\Math\EC\Rfc7748;..\src\Math\EC\Rfc8032;..\src;..\src\Asn1\X509;..\src\X509;..\src\Asn1\Pkcs;..\src\Utils\Pem;..\src\Utils\Net;..\src\OpenSsl"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
   </CompilerOptions>

+ 6 - 6
CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr

@@ -17,17 +17,17 @@ uses
   PaddingTests, DSATests, DeterministicDsaTests, Salsa20Tests, XSalsa20Tests,
   ChaChaTests, StreamCipherResetTests, CTSTests, X25519Tests, Ed25519Tests,
   X25519HigherLevelTests, Ed25519HigherLevelTests, Curve25519KeyUtilitiesTests,
-  ShortenedDigestTests,
-  Kdf1GeneratorTests, Kdf2GeneratorTests, Argon2Tests, ScryptTests, DigestTests,
-  CertTests, DigestUtilitiesTests, DHTests, Asn1IntegerTests,
-  GeneralizedTimeTests, BitStringTests, InputStreamTests,
+  ShortenedDigestTests, Kdf1GeneratorTests, Kdf2GeneratorTests, Argon2Tests,
+  ScryptTests, DigestTests, CertTests, DigestUtilitiesTests, DHTests,
+  Asn1IntegerTests, GeneralizedTimeTests, BitStringTests, InputStreamTests,
   UtcTimeTests, RelativeOidTests, OctetStringTests, SetTests, X9Tests,
   PrivateKeyInfoTests, Pkcs10CertRequestTests, DeltaCertificateTests,
   CertificateTests, X509AltTests, X509ExtensionsTests, X509NameTests,
   SubjectKeyIdentifierTests, KeyUsageTests, GeneralNameTests, KMacTests,
   PssTests, ISO9796Tests, RSABlindedTests, RSADigestSignerTests, RSATests,
-  X931SignerTests, CryptoLibTestBase, X509CertGenTests, ClpFixedSecureRandom,
-  ClpShortenedDigest, IPAddressUtilitiesTests, PemReaderTests;
+  X931SignerTests, CryptoLibTestBase, OpenSslReaderTests, OpenSslWriterTests,
+  X509CertGenTests, ClpFixedSecureRandom, ClpShortenedDigest,
+  IPAddressUtilitiesTests, PemReaderTests;
 
 type
 

+ 213 - 1
CryptoLib.Tests/src/Asn1/Pkcs/Pkcs10CertRequestTests.pas

@@ -25,20 +25,57 @@ interface
 
 uses
   SysUtils,
+  Generics.Collections,
 {$IFDEF FPC}
   fpcunit,
   testregistry,
 {$ELSE}
   TestFramework,
 {$ENDIF FPC}
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIX509Extension,
+  ClpBigInteger,
+  ClpCryptoLibTypes,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpGeneratorUtilities,
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpKeyGenerationParameters,
+  ClpIKeyGenerationParameters,
+  ClpRsaParameters,
+  ClpIRsaParameters,
+  ClpRsaGenerators,
   ClpPkcsAsn1Objects,
   ClpIPkcsAsn1Objects,
-  ClpCryptoLibTypes,
+  ClpPkcsObjectIdentifiers,
+  ClpPkcs10CertificationRequest,
+  ClpIPkcs10CertificationRequest,
+  ClpX509Asn1Objects,
+  ClpIX509Asn1Objects,
   CryptoLibTestBase;
 
 type
 
   TPkcs10CertRequestTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    const
+      EmptyExtensionsReqBase64 =
+        'MIICVDCCATwCAQAwADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKy8' +
+        '4oC/QPFkRBE04LIA5njEulZx/EEh+J2spnThoRwk+oycYEVKp95NSfGTAoNjTwUv' +
+        'TdB9c1PCPE1DmgZIVLEVvouB7sZbMbLSI0d//oMO/Wr/CZmvjPGB8DID7RJs0eqO' +
+        'gLgSuyBVrwbcSKtxH4NrNDsS5IZXCcE3xzkxMDdz72m9jvIrl2ivi+YmJ7cJo3N+' +
+        'DBEqHZW28oytOmVo+8zhxvnHb9w26GJEOxN5zYbiIVW2vU9OfeF9te+Rhnks43Pk' +
+        'YDDP2U4hR7q0BYrdkeWdA1ReleYyn/haeAoIVLZMANIOXobiqASKqSusVq9tLD67' +
+        '7TAywl5AVq8GOBzlXZUCAwEAAaAPMA0GCSqGSIb3DQEJDjEAMA0GCSqGSIb3DQEB' +
+        'CwUAA4IBAQAXck62gJw1deVOLVFAwBNVNXgJarHtDg3pauHTHvN+pSbdOTe1aRzb' +
+        'Tt4/govtuuGZsGWlUqiglLpl6qeS7Pe9m+WJwhH5yXnJ3yvy2Lc/XkeVQ0kt8uFg' +
+        '30UyrgKng6LDgUGFjDSiFr3dK8S/iYpDu/qpl1bWJPWmfmnIXzZWWvBdUTKlfoD9' +
+        '/NLIWINEzHQIBXGy2uLhutYOvDq0WDGOgtdFC8my/QajaJh5lo6mM/PlmcYjK286' +
+        'EdGSIxdME7hoW/ljA5355S820QZDkYx1tI/Y/YaY5KVOntwfDQzQiwWZ2PtpTqSK' +
+        'KYe2Ujb362yaERCE13DJC4Us9j8OOXcW';
+
   strict private
     var
       FReq1: TCryptoLibByteArray;
@@ -46,6 +83,7 @@ type
 
     procedure SetUpTestData;
     procedure BasicPkcs10Test(const ATestName: String; const AReq: TCryptoLibByteArray);
+    procedure BuildPerformRequestPair(out AReq1, AReq2: IPkcs10CertificationRequest);
 
   protected
     procedure SetUp; override;
@@ -53,6 +91,11 @@ type
   published
     procedure TestBasicCR;
     procedure TestUniversalCR;
+    procedure TestEmptyExtRequest;
+    procedure TestBrokenRequestWithDuplicateExtension;
+    procedure TestPerformRoundTrip;
+    procedure TestPerformVerify;
+    procedure TestPerformPublicKeyMatch;
 
   end;
 
@@ -117,6 +160,175 @@ begin
   BasicPkcs10Test('Universal CR', FReq2);
 end;
 
+procedure TPkcs10CertRequestTest.TestEmptyExtRequest;
+var
+  LReq: IPkcs10CertificationRequest;
+  LEncoded: TCryptoLibByteArray;
+begin
+  LEncoded := DecodeBase64(EmptyExtensionsReqBase64);
+  LReq := TPkcs10CertificationRequest.Create(LEncoded);
+  try
+    LReq.GetRequestedExtensions();
+    Fail('no exception thrown');
+  except
+    on E: EInvalidOperationCryptoLibException do
+      CheckEquals('pkcs_9_at_extensionRequest present but has no value', E.Message, 'Exception message');
+    on E: Exception do
+      Fail('Expected EInvalidOperationCryptoLibException, got ' + E.ClassName + ': ' + E.Message);
+  end;
+end;
+
+procedure TPkcs10CertRequestTest.TestBrokenRequestWithDuplicateExtension;
+var
+  LKpg: IAsymmetricCipherKeyPairGenerator;
+  LKp: IAsymmetricCipherKeyPair;
+  LOrder: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LValues: TCryptoLibStringArray;
+  LSubject: IX509Name;
+  LName1, LName2: IGeneralName;
+  LGenNames1, LGenNames2: IGeneralNames;
+  LExtSeq: IAsn1Sequence;
+  LAttrSet: IAsn1Set;
+  LAttr: IAttributePkcs;
+  LAttrs: IAsn1Set;
+  LReq1, LReq2: IPkcs10CertificationRequest;
+  LBytes: TCryptoLibByteArray;
+  LExtensions: IX509Extensions;
+  LExt: IX509Extension;
+  LReturnedNames: IGeneralNames;
+  LEnc1, LEnc2: TCryptoLibByteArray;
+  LRsaPub1, LRsaPub2: IRsaKeyParameters;
+  LKeyGenParams: IKeyGenerationParameters;
+begin
+  LKpg := TGeneratorUtilities.GetKeyPairGenerator('RSA');
+  LKeyGenParams := TKeyGenerationParameters.Create(TSecureRandom.MasterRandom, 2048);
+  LKpg.Init(LKeyGenParams);
+  LKp := LKpg.GenerateKeyPair();
+
+  SetLength(LOrder, 5);
+  LOrder[0] := TX509Name.C;
+  LOrder[1] := TX509Name.O;
+  LOrder[2] := TX509Name.L;
+  LOrder[3] := TX509Name.ST;
+  LOrder[4] := TX509Name.EmailAddress;
+  SetLength(LValues, 5);
+  LValues[0] := 'AU';
+  LValues[1] := 'The Legion of the Bouncy Castle';
+  LValues[2] := 'Melbourne';
+  LValues[3] := 'Victoria';
+  LValues[4] := '[email protected]';
+  LSubject := TX509Name.Create(LOrder, LValues);
+
+  LName1 := TGeneralName.Create(TGeneralName.DnsName, 'bc1.local');
+  LName2 := TGeneralName.Create(TGeneralName.DnsName, 'bc2.local');
+
+  LGenNames1 := TGeneralNames.Create(LName1);
+  LGenNames2 := TGeneralNames.Create(LName2);
+  LExtSeq := TDerSequence.FromElements(
+    TDerSequence.Create([
+      TX509Extensions.SubjectAlternativeName,
+      TDerOctetString.Create(LGenNames1.GetEncoded()) as IDerOctetString
+    ]) as IDerSequence,
+    TDerSequence.Create([
+      TX509Extensions.SubjectAlternativeName,
+      TDerOctetString.Create(LGenNames2.GetEncoded()) as IDerOctetString
+    ]) as IDerSequence
+  );
+  LAttrSet := TDerSet.FromElement(LExtSeq);
+  LAttr := TAttributePkcs.Create(TPkcsObjectIdentifiers.Pkcs9AtExtensionRequest, LAttrSet);
+  LAttrs := TDerSet.FromElement(LAttr);
+
+  LReq1 := TPkcs10CertificationRequest.Create(
+    'SHA256withRSA', LSubject, LKp.Public, LAttrs, LKp.Private);
+  LBytes := LReq1.GetEncoded();
+  LReq2 := TPkcs10CertificationRequest.Create(LBytes);
+
+  CheckTrue(LReq2.Verify(), 'SHA256withRSA: Failed Verify check');
+
+  if Supports(LReq2.GetPublicKey(), IRsaKeyParameters, LRsaPub2) and
+     Supports(LReq1.GetPublicKey(), IRsaKeyParameters, LRsaPub1) then
+    CheckTrue(LRsaPub1.Equals(LRsaPub2), 'RSA: Failed public key check')
+  else
+    Fail('RSA: Failed to get RSA public keys');
+
+  LExtensions := LReq2.GetRequestedExtensions();
+  Check(LExtensions <> nil, 'expected extensions');
+  LExt := LExtensions.GetExtension(TX509Extensions.SubjectAlternativeName);
+  Check(LExt <> nil, 'expected SubjectAlternativeName extension');
+  LReturnedNames := TGeneralNames.GetInstance(LExt.GetParsedValue());
+  CheckEquals(2, LReturnedNames.GetCount(), 'expected 2 names');
+  LEnc1 := LName1.GetEncoded();
+  LEnc2 := LName2.GetEncoded();
+  CheckTrue(AreEqual(LReturnedNames.GetNames[0].GetEncoded(), LEnc1), 'expected name 1');
+  CheckTrue(AreEqual(LReturnedNames.GetNames[1].GetEncoded(), LEnc2), 'expected name 2');
+end;
+
+procedure TPkcs10CertRequestTest.BuildPerformRequestPair(out AReq1, AReq2: IPkcs10CertificationRequest);
+var
+  LKpg: IAsymmetricCipherKeyPairGenerator;
+  LKp: IAsymmetricCipherKeyPair;
+  LOrder: TCryptoLibGenericArray<IDerObjectIdentifier>;
+  LValues: TCryptoLibStringArray;
+  LSubject: IX509Name;
+  LBytes: TCryptoLibByteArray;
+  LKeyGenParams: IKeyGenerationParameters;
+begin
+  LKpg := TGeneratorUtilities.GetKeyPairGenerator('RSA');
+  LKeyGenParams := TRsaKeyGenerationParameters.Create(
+    TBigInteger.ValueOf($10001), TSecureRandom.MasterRandom, 512, 25);
+  LKpg.Init(LKeyGenParams);
+  LKp := LKpg.GenerateKeyPair();
+
+  SetLength(LOrder, 5);
+  LOrder[0] := TX509Name.C;
+  LOrder[1] := TX509Name.O;
+  LOrder[2] := TX509Name.L;
+  LOrder[3] := TX509Name.ST;
+  LOrder[4] := TX509Name.EmailAddress;
+  SetLength(LValues, 5);
+  LValues[0] := 'NG';
+  LValues[1] := 'CryptoLib4Pascal';
+  LValues[2] := 'Alausa';
+  LValues[3] := 'Lagos';
+  LValues[4] := '[email protected]';
+  LSubject := TX509Name.Create(LOrder, LValues);
+
+  AReq1 := TPkcs10CertificationRequest.Create(
+    'SHA1withRSA', LSubject, LKp.Public, nil, LKp.Private);
+  LBytes := AReq1.GetEncoded();
+  AReq2 := TPkcs10CertificationRequest.Create(LBytes);
+end;
+
+procedure TPkcs10CertRequestTest.TestPerformRoundTrip;
+var
+  LReq1, LReq2: IPkcs10CertificationRequest;
+begin
+  BuildPerformRequestPair(LReq1, LReq2);
+  Check(LReq1 <> nil, 'request before round-trip');
+  Check(LReq2 <> nil, 'request after round-trip');
+end;
+
+procedure TPkcs10CertRequestTest.TestPerformVerify;
+var
+  LReq1, LReq2: IPkcs10CertificationRequest;
+begin
+  BuildPerformRequestPair(LReq1, LReq2);
+  CheckTrue(LReq2.Verify(), 'Failed verify check');
+end;
+
+procedure TPkcs10CertRequestTest.TestPerformPublicKeyMatch;
+var
+  LReq1, LReq2: IPkcs10CertificationRequest;
+  LRsaPub1, LRsaPub2: IRsaKeyParameters;
+begin
+  BuildPerformRequestPair(LReq1, LReq2);
+  if Supports(LReq2.GetPublicKey(), IRsaKeyParameters, LRsaPub2) and
+     Supports(LReq1.GetPublicKey(), IRsaKeyParameters, LRsaPub1) then
+    CheckTrue(LRsaPub1.Equals(LRsaPub2), 'Failed public key check')
+  else
+    Fail('Failed to get RSA public keys for comparison');
+end;
+
 initialization
 
 {$IFDEF FPC}

+ 2 - 0
CryptoLib.Tests/src/Asn1/X509/DeltaCertificateTests.pas

@@ -41,6 +41,8 @@ uses
   ClpDeltaCertificateTool,
   ClpPemObjects,
   ClpIPemObjects,
+  ClpIPemReader,
+  ClpPemReader,
   ClpArrayUtilities,
   ClpCryptoLibTypes,
   ClpBigInteger,

+ 393 - 0
CryptoLib.Tests/src/OpenSsl/OpenSslReaderTests.pas

@@ -0,0 +1,393 @@
+{ *********************************************************************************** }
+{ *                              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 OpenSslReaderTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+  Classes,
+  Rtti,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpPemObjects,
+  ClpIPemObjects,
+  ClpIPemWriter,
+  ClpPemWriter,
+  ClpIOpenSslPemWriter,
+  ClpOpenSslPemWriter,
+  ClpIOpenSslPemReader,
+  ClpOpenSslPemReader,
+  ClpIAsymmetricCipherKeyPair,
+  ClpAsymmetricCipherKeyPair,
+  ClpIAsymmetricKeyParameter,
+  ClpBigInteger,
+  ClpRsaGenerators,
+  ClpIRsaGenerators,
+  ClpIRsaParameters,
+  ClpRsaParameters,
+  ClpIKeyGenerationParameters,
+  ClpKeyGenerationParameters,
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpDsaParameters,
+  ClpIDsaParameters,
+  ClpDsaGenerators,
+  ClpIDsaGenerators,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpICmsAsn1Objects,
+  ClpCmsAsn1Objects,
+  ClpCmsObjectIdentifiers,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes,
+  CryptoLibTestBase;
+
+type
+
+  TOpenSslReaderTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    const
+      (* PKCS7 ContentInfo with contentType id-envelopedData (from bc-reference openssl/pkcs7.pem). *)
+      Pkcs7Pem =
+        '-----BEGIN PKCS7-----' + sLineBreak +
+        'MIIJogYJKoZIhvcNAQcDoIIJkzCCCY8CAQAxgfgwgfUCAQAwXjBZMQswCQYDVQQG' + sLineBreak +
+        'EwJHQjESMBAGA1UECBMJQmVya3NoaXJlMRAwDgYDVQQHEwdOZXdidXJ5MRcwFQYD' + sLineBreak +
+        'VQQKEw5NeSBDb21wYW55IEx0ZDELMAkGA1UEAxMCWFgCAQAwDQYJKoZIhvcNAQEB' + sLineBreak +
+        'BQAEgYAikb9cD39oDYpMHzLuqA4BonNpPx+jYtqlUIaJv30V03nUz1MLm7IH7TFt' + sLineBreak +
+        'ZhL6BXAbdC2iwk62KVS66ZCLBKdsqtD3w9N2HtxTEW6AdaNHKNUb6z83yarNQGzB' + sLineBreak +
+        '67llZjeCLeipP7RWIvBZcV0OoqCgLcpZkpZqzrmz5MjxTCmB/DCCCI0GCSqGSIb3' + sLineBreak +
+        'DQEHATAUBggqhkiG9w0DBwQIja9nGhuQE1GAgghocswhe5MZRov9Zo1gnB25S0P8' + sLineBreak +
+        'Mw3463VaOcb+ljX1mXkT3fivkBv0plLlmVT+m+CRgczup9p21+t1OqsdaITNIyrG' + sLineBreak +
+        'hYSVETWyFA/Yn7dQupK+cdCaVLKC3lT8f13iPrU40wnbeo4ZKi2vbv/X3uU4fRMZ' + sLineBreak +
+        'wSlyczFozcviUYURtA5MZaS2e6/2r1eLZcUlcZ0BDcuD+FNdryGbKztSWa2ye0Ym' + sLineBreak +
+        'Uilu+GAZr5CQi3IxpRxDqrS+RUQZNllcg8nGZ2UP5W8FjH+Z568NJ7djoziCX0EH' + sLineBreak +
+        'yd4vp+g0LRG2dkhGXIff4ufO2U3QOAgCIOuZmG5YSpRN2U7F6T8W/FwShFO1u+QH' + sLineBreak +
+        'YduA3pA/5K+IDfCbEZDMWznd13lTEZQlLSXV7dLNCqpR30JWpGg956rJR0k2bT7G' + sLineBreak +
+        'KFTXhSUK/Puac5y6IVmJwPxqAkjH+xjXpE32/AcRHi77La3nKp1aQEKo5uHg7HEg' + sLineBreak +
+        'w160S1LUenJSqcmOuk5XWvM1wdsUJl5Qk4m9a0VyovLPm/RrnulMtUjRugxJLfZK' + sLineBreak +
+        '27NivOrLl9h/Wm6BXYq4PohM5d+5zPYqupn5ipKHsA68Ps7rnDEGS3VzOQ32hqu4' + sLineBreak +
+        'kdm6xI2zLWK0+6mQnusBPO0IAxtja6BPz8vTMlWjZtWZgEIMppQMhQJKBEQG6HTV' + sLineBreak +
+        'z+/gkFds2pFO0v8pLcMBy9+8nqhzwGacymnupXJzB6l3gon2t/e2zJjAPKUSCbHI' + sLineBreak +
+        'QhCjW2JK9tGKTbF40uYMUGMIPhxr7j1u4LKNEhKCNhlUz82NSsdJ00YNQdwuDMWN' + sLineBreak +
+        'CTAE9/STmRGF3ZHT9KWmz5MQECp/pGORD7LtOQslbUYiMH5oCYP1jD8eM+KxCljv' + sLineBreak +
+        '1pFPf+sZdpboAkdaXKcZVnKqOuPBP3Y1jBkLCZykgnXkVbEYM7gSdvsCGK52GcxH' + sLineBreak +
+        'yi/gOhfOIgywmFB3B4Yk4mDtU84WpK5sVlrZ2vZuTaAmOHaTIkVMvkq30F/jpVy3' + sLineBreak +
+        'OF4v9/EbEAJGv6rqHMhKmuIHP530CKtWkUUfGv7qQilZ1Qi6NyFJJTfb1bhyENJt' + sLineBreak +
+        'j8A1QQFIYHDzMolmUoQgqOXJ/6xc9AtCv0fU2LijLUNFjB4rapJggo5UnZE98+Iq' + sLineBreak +
+        'UAT7tWalpbFisOdX5Dy582hhvcFn/1DDpISXpF0kgE8TV/swkJ7zuu+hO/Yj1HNd' + sLineBreak +
+        'cwG6NC9+wUCjaRqAobBtvPQyK666I8C12pnW0AeuqtznnZve2B0/a83ECS0tUmxC' + sLineBreak +
+        'PO9zv9RNwcakynklrupw7B4PcXEaEbxpvHE+/zNLgfrPRggoFdqSIRFS9xQRPE9T' + sLineBreak +
+        'uO7jEh+tyh70eLqce2jqKpRwxItZst3ABT5XarJ6vfGxxcs55sJG7xjv52xuMikY' + sLineBreak +
+        'gOagSKpETRdkeE1aAmKwpa/vEFu2J4Oq1Aiv+D2Gc7G04cOsdc+6P+N9EEv70v0R' + sLineBreak +
+        '3NA4vg3gTBcO3wxwnJZAS7GwUJOcrqC1cAaQkc5NR0lUx0lMzgWWDDS5qKX+YwIU' + sLineBreak +
+        '7KEQiyhqQ74rkf6hxQyfesaBxqxCZZkikbwBHlDZwoPfwnfrV4X4/xyo3cqCqbhf' + sLineBreak +
+        'FFlHOAXissz14wsTPh4XQumj5RZSnwj8gGK2xou9H9wMrwuZ2eAT/3L3OtbIr/Sz' + sLineBreak +
+        'Cbp8Y95Tz8FgmrJXvygMVO1xv77PA1DzE9SLiLyB6TL8lsxFQ1ZF2D8JhpDeIPpj' + sLineBreak +
+        'L0k2vTrmCgENJ+tCc0ngZO55ZgRbo1fbB/RUfkTRgEKF9WmJYnlXUVoh77kZ0cc9' + sLineBreak +
+        'Y+KsueEZp1woSTywJb3tc/jXeRGSmcaWe6pa0DcfM50coV0y4lw1ednEV3zkA1r4' + sLineBreak +
+        'zVtUBw8Xvr9GKcNfWdmqgIJKsQraq6WCeIxCPPJw708+/RERQBoUobXI4+Jatw/z' + sLineBreak +
+        'XiV9SjrjK9nJ4H1YKyOjyz3SAbeYrgdgrTGvkETCPAALb+4Rg1FHymSMfDquwOsB' + sLineBreak +
+        '63Mdl63DIkJpicA6CY6yk/LgOADQzEipjcdKqzQOjlb4hsQZxN83kzGJiWB0qZOL' + sLineBreak +
+        'XVLrGXP4xRYS2bUFB0T8pon0K5qsZ9oKKf+HZaHMYkni43Ef9IRA0qeDl4FfAupA' + sLineBreak +
+        'kL0lLnBjgGRHc6rMBy4qL18xRjTtR9hsn4Z/pYhIgqMm3QEVkK/aOgTOlwXHdIwu' + sLineBreak +
+        '+Hvzx0Y/BgMdCZSlrspPbQBDgrlWzr+PjcjEvDf3LYj9whtRJP5cXVxiYqi/SpCk' + sLineBreak +
+        'Ghy47RfNYfkkJs/gbojlO/lDvM8oo+XPi22zAN6yFLuxr65lJZK7QIvabHvTkEIN' + sLineBreak +
+        'wmpnWcRH+MwcFZO3yKt6lxY7nJWuW5hh8O7k4/oN0pNdGtv1/2XgXFOCREQ4CcPn' + sLineBreak +
+        'Zm/vXULLCCh7oP+RyklnwyedvfeSfY4lpldwyHCIsYyYmfZHMw32zqH5jCnSxZA4' + sLineBreak +
+        'fHBrblr4Mj/5jyHLUF5xGsJdm5RtDfwJWe6NelO/kJMs35UjA6dhSOfHEkw73M5P' + sLineBreak +
+        'jcRo1OtYZGu19x2QguhILpZxuAvNtLpOt88z3PtsxA6Fc0BGpQXPJTYwtXiPf1lj' + sLineBreak +
+        'fUd5KFsPohPJOIEJAaFHL3GTwmWFtK1dHofPQukiOTb6pC6yKlM/zGWLOyzTM4qP' + sLineBreak +
+        'UvuUSwg1UY8GplCeqhCJNTieNmyY70vzG2CWcotAwRPeVbpa4MEWRXHf9ft4Mawb' + sLineBreak +
+        'qn2J48iW4Zgh82vFHNYcGRjKRJqLzp4VBn/qpRaX+aWEsdXq4shRgFOAOKyQNMex' + sLineBreak +
+        'GZyd9amkblqjEOOEzzxPUdmt8k+QEm+JC80NR2sv1mw80PqU/his5zUJ1Aj4tzkF' + sLineBreak +
+        'fi4jy2nPNvVSpjWiAI6cpZsbdhdh9iayij4YdQg3HB20+1K9VcFnTmBqLKiBbG2o' + sLineBreak +
+        '4oX2oNPE9Vr3H9Y8YaVoeUU+Kiqo5g==' + sLineBreak +
+        '-----END PKCS7-----';
+      (* Unencrypted DSA private key (from bc-reference openssl/dsa/openssl_dsa_unencrypted.pem). *)
+      DsaUnencryptedPem =
+        '-----BEGIN DSA PRIVATE KEY-----' + sLineBreak +
+        'MIIDPgIBAAKCAQEAyKItMopMK218pcmy6PkrMVXAv5dt07TdGBuNhVWpQ52ldK9X' + sLineBreak +
+        'mL7CPKpo4Dy85EZRPvRNyOnhe+LRJZ+ReKntpEkEiar/ZsKVkUthPsiUMpoM5S79' + sLineBreak +
+        'JK8iT8F9HdFjIFKaXGySBZ4xcrj8HQ/v75iolYCso+66Ybgjs9/nsWS0UQyGE6rc' + sLineBreak +
+        'ibx7xPAtcbaGZUBaBtdkNER7+P2ueJwej89aNZxj+AKuvrWrArq6/5zOIhGR12wQ' + sLineBreak +
+        'EQQjj7FQ66ZFivJ/AYsv1yXDS7mZBNp5eMuxk8Kmis/++HKcP7tdbVRnlfTGdBuN' + sLineBreak +
+        'BMyOcBTIsE11jwikcI+KIbr9cEZoaikkm4KyuwIVAP4DZEC+/JZJ0PHSEtJTt6uz' + sLineBreak +
+        'yn1hAoIBAHhLbqDib7zqaFBrNnbaBSNiltY3GxWM6uQT88NH9YWz3wXRb6i4KJFH' + sLineBreak +
+        '9NtLbK0RF7MoQVprJY6LuGQHZ/e61Fs2EabDwT4vB2HT619fVUvDndbuSW6qfUR4' + sLineBreak +
+        'y9kbG7zLkE4Mnym/ikmUwLABLA67cZUS9yjtcRXGpOkiTAQfCGBeUH6nWOFEaWjI' + sLineBreak +
+        'fGNMQ5awKvZhIvGyN4Zvd+mE+199s/kAsCKFux2Sq9tYw3qS0Tw2IEebHsHvX7A3' + sLineBreak +
+        'bvxV6p7czVxlO9+O0w7bBTekPpw1BnCYmPyy0H36g/7aF2V70UCWzER8zT1Pfh7d' + sLineBreak +
+        '3P0hLqHYzX375l/7oxuDawtcDAV++iwCggEASajPdllHVQ8JvKdPH6qDkjC5XJTZ' + sLineBreak +
+        'RK46mYm1cCu8Q9Dy9ZfL67CcJBpwKVHNC3sXmk4XPfs91AZf/t01qbMJvCrR8NHs' + sLineBreak +
+        'jRyJkNIaMyDeWcFmO0KmMi374BQpFyIDQ6mK1y9BilneZ6gHDdfHMsKniLFW+SQf' + sLineBreak +
+        '9hlwlArIPYuELu7riJhNcuRUTJEfybDHwM4/ht0IFbyUIFl00mMdTrozk+e/esEs' + sLineBreak +
+        'QdWbx2UBjNs8meZPivFbT2HpQF1I0qZhtn3e7jcR5YatBQ3e4abnu1RrDc73q7d4' + sLineBreak +
+        'g2SYQK3PmIWwxiFhJQTzeiQtl5rKzEn76knAydOtPVRgjXWzHUoW6Az0qwIVAMvw' + sLineBreak +
+        'thRrEZxNdxELdnwW3rpYBm6B' + sLineBreak +
+        '-----END DSA PRIVATE KEY-----';
+      (* Unencrypted RSA private key (from bc-reference openssl/rsa/openssl_rsa_unencrypted.pem). *)
+      RsaUnencryptedPem =
+        '-----BEGIN RSA PRIVATE KEY-----' + sLineBreak +
+        'MIIEpAIBAAKCAQEAyGOvloI+jKnRHWKuhYB+cTIEwZhagKJ0f3rIY8WNgujB7Plp' + sLineBreak +
+        'gpjUg4pNjYGViGjg7zhfbjhCtlNGXyRBti3GcaHiBIIP5nyCNn+Ay8tSWGo5v5Zc' + sLineBreak +
+        '8BQcwHf0ZHLN6sD9m2uVSp/6UqjS5ZyhzF5FzvvUo3xw8fecdnStXQfHhkBnLpTj' + sLineBreak +
+        'HE5t7iu1JVjTuE0pcBvah2dWqDNxiIOQtXyKW8Sag1YxaunxQGqRNykSFiEJindx' + sLineBreak +
+        'OSAnAxK6q/wGqcZ3zvFBTcVVkji1u2QH4rOMP3PPxAIMkB8ONkdHTco1DmbE6BfD' + sLineBreak +
+        'HArDqUYxqJUlPGlMqrKb3fCFiT3eXehwR7nlzQIDAQABAoIBAFd6vTKVVT0O/U04' + sLineBreak +
+        'wTtiptA/p7fkDM5PHVBxh32Wxno5pj8PerIaiduKyuRVh7PvJRMJpw903BrAK95o' + sLineBreak +
+        '847WWOVOaF7TcKGMBURJUS6maiJS7TboK1ZbUVnsg/I99ArhiVUKGDhlsl/Xd4np' + sLineBreak +
+        'YPDYztzXLzLXpm7bS6CiuvP762x9dfVu8K+afP8cjH8pfXLq55ghZOUKidRQaYz1' + sLineBreak +
+        'mNOTQyAQlCQdLRgKlYgqcRHlj0pb28XBJaln3W7Z7GFMWFPojkxx6LaCp8+Jyx2C' + sLineBreak +
+        'tv54zIZQhMjF37tQyTnfK4Ocl3sCRb+jYV4FkrUnsQE9W2dey0Tms1XB31gfUJlx' + sLineBreak +
+        'dRZu7zkCgYEA/nWcTwzot2OIAhXoJ2fnqTcpdmj05LHhGcayKjyix7BsVH2I0KpF' + sLineBreak +
+        '9kXX066tr3+LxZTergl4UpWSl3yx/4kPBQM6np4VVRytn7+cQdEhOczZnBw6x7IZ' + sLineBreak +
+        'fv81DSNruQDBRAlTtklW4KBY74JKLhaJSvF1F3x32+H+99i1MmCNJRMCgYEAyZpF' + sLineBreak +
+        'h4c3pM9z+YlmgLdUh/G2abdoamugcQOFbzHbZowsRAxEzdEW9wj2McN6mt8Rn1tc' + sLineBreak +
+        'tY/+PcYuIK+vcmk9k23GuzxRlJlkaDicHwlAebgVIulFcrStfTlSkXjpuOuusfD9' + sLineBreak +
+        '2DuHMcUiPx3qElNB0dZJF/axpq7BjTIFENefhZ8CgYACn+vw1M1BtwEcJGW0olm9' + sLineBreak +
+        'YRhIZGTCRyNvRKFp1h5HuQYlCPZ0UI1QMQA86rxX5xTmANcbLHXVRD2y2lJrtFo3' + sLineBreak +
+        'TwU3xaGqsxUHZM6TzzhshDRqa9AfZzLkIHXHoOnnip5zuTTn2HHQ91ZzggCJ4Smh' + sLineBreak +
+        'YEQ47cu+tOIQZGfaESzjiQKBgQCCfnZlDJRq/NFwA40y4fg4arANa+eNgw7+OC5F' + sLineBreak +
+        '1HrUvQTmIx7iLmZ0Dvv1KDgTSTLJ+MRgzczexYoUJEQnhZGS/Wq2xYt06XlBsOr1' + sLineBreak +
+        'd/KhFxOvXllSrzrhJJqaiS6YQQ36JijZr2aKQ7UwL7fUlsmy/safWVKStumX8Hmw' + sLineBreak +
+        '9jFOtwKBgQDmtirdNQ8aKolokD/3bDHPcDsNcybEpiCu8BIltxZAs/LsN1IIxfcp' + sLineBreak +
+        'mGP2AFt3mbblKbsRM8hDW/X9taeG9s2KGe5wlKOE5lV8YAo4hFoJYN2/0d8Y0K9X' + sLineBreak +
+        'QAAYU3iPG1zL+a/7TFLJ0u/biqsBg9hnNbMnN/tOeSuKnH2Rx9F1rg==' + sLineBreak +
+        '-----END RSA PRIVATE KEY-----';
+  strict private
+    function CreatePemReader(AStream: TStringStream): IOpenSslPemReader;
+    function CreatePemWriter(AStream: TStringStream): IOpenSslPemWriter;
+    procedure KeyPairTest(const AName: string; const APair: IAsymmetricCipherKeyPair);
+    procedure DoOpenSslTestData(const APemData: string; AExpectDsa: Boolean);
+  published
+    procedure TestPkcs7EnvelopedData;
+    procedure TestKeyPairRsaRoundTrip;
+    procedure TestKeyPairDsaRoundTrip;
+    procedure TestPkcs7RoundTrip;
+    procedure TestOpenSslDsaUnencrypted;
+    procedure TestOpenSslRsaUnencrypted;
+  end;
+
+implementation
+
+{ TOpenSslReaderTest }
+
+function TOpenSslReaderTest.CreatePemReader(AStream: TStringStream): IOpenSslPemReader;
+begin
+  AStream.Position := 0;
+  Result := TOpenSslPemReader.Create(AStream);
+end;
+
+function TOpenSslReaderTest.CreatePemWriter(AStream: TStringStream): IOpenSslPemWriter;
+begin
+  Result := TOpenSslPemWriter.Create(AStream);
+end;
+
+procedure TOpenSslReaderTest.KeyPairTest(const AName: string;
+  const APair: IAsymmetricCipherKeyPair);
+var
+  LWriteStream: TStringStream;
+  LReadStream: TStringStream;
+  LWriter: IOpenSslPemWriter;
+  LReader: IOpenSslPemReader;
+  LReadVal: TValue;
+  LPubK: IAsymmetricKeyParameter;
+  LReadPair: IAsymmetricCipherKeyPair;
+begin
+  LWriteStream := TStringStream.Create('', TEncoding.ASCII);
+  try
+    LWriter := CreatePemWriter(LWriteStream);
+    LWriter.WriteObject(TValue.From<IAsymmetricKeyParameter>(APair.Public));
+
+    LReadStream := TStringStream.Create(LWriteStream.DataString, TEncoding.ASCII);
+    try
+      LReader := CreatePemReader(LReadStream);
+      LReadVal := LReader.ReadObject();
+      Check(not LReadVal.IsEmpty, 'Public key should read back');
+      Check(LReadVal.TryAsType<IAsymmetricKeyParameter>(LPubK), 'Should be public key');
+      Check(LPubK.Equals(APair.Public), 'Failed public key read: ' + AName);
+    finally
+      LReadStream.Free;
+    end;
+  finally
+    LWriteStream.Free;
+  end;
+
+  LWriteStream := TStringStream.Create('', TEncoding.ASCII);
+  try
+    LWriter := CreatePemWriter(LWriteStream);
+    LWriter.WriteObject(TValue.From<IAsymmetricKeyParameter>(APair.Private));
+
+    LReadStream := TStringStream.Create(LWriteStream.DataString, TEncoding.ASCII);
+    try
+      LReader := CreatePemReader(LReadStream);
+      LReadVal := LReader.ReadObject();
+      Check(LReadVal.TryAsType<IAsymmetricCipherKeyPair>(LReadPair), 'Should be key pair');
+      Check(LReadPair.Private.Equals(APair.Private), 'Failed private key read: ' + AName);
+      Check(LReadPair.Public.Equals(APair.Public), 'Failed private key public read: ' + AName);
+    finally
+      LReadStream.Free;
+    end;
+  finally
+    LWriteStream.Free;
+  end;
+end;
+
+procedure TOpenSslReaderTest.DoOpenSslTestData(const APemData: string;
+  AExpectDsa: Boolean);
+var
+  LStream: TStringStream;
+  LReader: IOpenSslPemReader;
+  LVal: TValue;
+  LKp: IAsymmetricCipherKeyPair;
+  LDummy: IInterface;
+begin
+  LStream := TStringStream.Create(APemData, TEncoding.ASCII);
+  try
+    LReader := CreatePemReader(LStream);
+    LVal := LReader.ReadObject();
+    Check(not LVal.IsEmpty, 'ReadObject should return key');
+    Check(LVal.TryAsType<IAsymmetricCipherKeyPair>(LKp), 'Should be key pair');
+    Check(LKp <> nil, 'Didn''t find OpenSSL key');
+    if AExpectDsa then
+      Check(Supports(LKp.Private, IDsaPrivateKeyParameters, LDummy), 'Returned key not DSA private')
+    else
+      Check(Supports(LKp.Private, IRsaPrivateCrtKeyParameters, LDummy), 'Returned key not RSA private');
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TOpenSslReaderTest.TestPkcs7EnvelopedData;
+var
+  LStream: TStringStream;
+  LReader: IOpenSslPemReader;
+  LVal: TValue;
+  LCmsContent: ICmsContentInfo;
+begin
+  LStream := TStringStream.Create(Pkcs7Pem, TEncoding.ASCII);
+  try
+    LReader := CreatePemReader(LStream);
+    LVal := LReader.ReadObject();
+    Check(not LVal.IsEmpty, 'ReadObject should return PKCS7');
+    Check(LVal.TryAsType<ICmsContentInfo>(LCmsContent), 'Should be CmsContentInfo');
+    Check(LCmsContent <> nil, 'ContentInfo should not be nil');
+    Check(LCmsContent.ContentType.Equals(TCmsObjectIdentifiers.EnvelopedData),
+      'ContentType should be EnvelopedData');
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TOpenSslReaderTest.TestKeyPairRsaRoundTrip;
+var
+  LGen: IAsymmetricCipherKeyPairGenerator;
+  LPair: IAsymmetricCipherKeyPair;
+  LSecRandom: ISecureRandom;
+begin
+  LSecRandom := TSecureRandom.Create();
+  LGen := TRsaKeyPairGenerator.Create();
+  LGen.Init(TRsaKeyGenerationParameters.Create(TBigInteger.ValueOf($10001),
+    LSecRandom, 768, 25) as IKeyGenerationParameters);
+  LPair := LGen.GenerateKeyPair();
+  KeyPairTest('RSA', LPair);
+end;
+
+procedure TOpenSslReaderTest.TestKeyPairDsaRoundTrip;
+var
+  LGen: IAsymmetricCipherKeyPairGenerator;
+  LDsaParamsGen: IDsaParametersGenerator;
+  LDsaParams: IDsaParameters;
+  LDsaKeyParams: IKeyGenerationParameters;
+  LPair: IAsymmetricCipherKeyPair;
+  LSecRandom: ISecureRandom;
+begin
+  LSecRandom := TSecureRandom.Create();
+  LDsaParamsGen := TDsaParametersGenerator.Create();
+  LDsaParamsGen.Init(512, 80, LSecRandom);
+  LDsaParams := LDsaParamsGen.GenerateParameters();
+  LDsaKeyParams := TDsaKeyGenerationParameters.Create(LSecRandom, LDsaParams) as IKeyGenerationParameters;
+  LGen := TDsaKeyPairGenerator.Create();
+  LGen.Init(LDsaKeyParams);
+  LPair := LGen.GenerateKeyPair();
+  KeyPairTest('DSA', LPair);
+end;
+
+procedure TOpenSslReaderTest.TestPkcs7RoundTrip;
+var
+  LStream: TStringStream;
+  LReader: IOpenSslPemReader;
+  LVal: TValue;
+  LCmsContent: ICmsContentInfo;
+  LWriter: IOpenSslPemWriter;
+  LOutStream: TStringStream;
+  LReader2: IOpenSslPemReader;
+  LVal2: TValue;
+  LCmsContent2: ICmsContentInfo;
+begin
+  LStream := TStringStream.Create(Pkcs7Pem, TEncoding.ASCII);
+  try
+    LReader := CreatePemReader(LStream);
+    LVal := LReader.ReadObject();
+    Check(LVal.TryAsType<ICmsContentInfo>(LCmsContent), 'Should be CmsContentInfo');
+    Check(LCmsContent <> nil, 'ContentInfo should not be nil');
+
+    LOutStream := TStringStream.Create('', TEncoding.ASCII);
+    try
+      LWriter := CreatePemWriter(LOutStream);
+      LWriter.WriteObject(TValue.From<ICmsContentInfo>(LCmsContent));
+      LReader2 := CreatePemReader(LOutStream);
+      LVal2 := LReader2.ReadObject();
+      Check(LVal2.TryAsType<ICmsContentInfo>(LCmsContent2), 'Read back should be CmsContentInfo');
+      Check(LCmsContent2.ContentType.Equals(TCmsObjectIdentifiers.EnvelopedData),
+        'failed envelopedData recode check');
+    finally
+      LOutStream.Free;
+    end;
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TOpenSslReaderTest.TestOpenSslDsaUnencrypted;
+begin
+  DoOpenSslTestData(DsaUnencryptedPem, True);
+end;
+
+procedure TOpenSslReaderTest.TestOpenSslRsaUnencrypted;
+begin
+  DoOpenSslTestData(RsaUnencryptedPem, False);
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TOpenSslReaderTest);
+{$ELSE}
+RegisterTest(TOpenSslReaderTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 233 - 0
CryptoLib.Tests/src/OpenSsl/OpenSslWriterTests.pas

@@ -0,0 +1,233 @@
+{ *********************************************************************************** }
+{ *                              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 OpenSslWriterTests;
+
+interface
+
+{$IFDEF FPC}
+{$MODE DELPHI}
+{$ENDIF FPC}
+
+uses
+  SysUtils,
+  Classes,
+  Rtti,
+{$IFDEF FPC}
+  fpcunit,
+  testregistry,
+{$ELSE}
+  TestFramework,
+{$ENDIF FPC}
+  ClpPemObjects,
+  ClpIPemObjects,
+  ClpIPemWriter,
+  ClpPemWriter,
+  ClpIOpenSslPemWriter,
+  ClpOpenSslPemWriter,
+  ClpIOpenSslPemReader,
+  ClpOpenSslPemReader,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIAsymmetricKeyParameter,
+  ClpAsymmetricCipherKeyPair,
+  ClpBigInteger,
+  ClpRsaGenerators,
+  ClpIRsaGenerators,
+  ClpIRsaParameters,
+  ClpRsaParameters,
+  ClpIKeyGenerationParameters,
+  ClpKeyGenerationParameters,
+  ClpSecureRandom,
+  ClpISecureRandom,
+  ClpDsaParameters,
+  ClpIDsaParameters,
+  ClpDsaGenerators,
+  ClpIDsaGenerators,
+  ClpIAsymmetricCipherKeyPairGenerator,
+  ClpECGenerators,
+  ClpIECGenerators,
+  ClpSecObjectIdentifiers,
+  ClpPrivateKeyFactory,
+  ClpIPkcsAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpIX9ECAsn1Objects,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpECParameters,
+  ClpIECParameters,
+  ClpCryptoLibTypes,
+  ClpEncoders,
+  CryptoLibTestBase;
+
+type
+
+  TOpenSslWriterTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    const
+      (* Base64 of EC P-256 private key (PKCS#8), from WriterTest.cs lines 41-45. *)
+      TestEcDsaKeyBytesBase64 =
+        'MIG/AgEAMBAGByqGSM49AgEGBSuBBAAiBIGnMIGkAgEBBDCSBU3vo7ieeKs0ABQamy/ynxlde7Ylr8HmyfLaNnMr' +
+        'jAwPp9R+KMUEhB7zxSAXv9KgBwYFK4EEACKhZANiAQQyyolMpg+TyB4o9kPWqafHIOe8o9K1glus+w2sY8OIPQQWGb5i5LdAyi' +
+        '/SscwU24rZM0yiL3BHodp9ccwyhLrFYgXJUOQcCN2dno1GMols5497in5gL5+zn0yMsRtyv5o=';
+  strict private
+    class function GetTestRsaKey: IRsaPrivateCrtKeyParameters; static;
+    class function GetTestDsaParams: IDsaParameters; static;
+    procedure DoWriteReadTest(const APrivateKey: IAsymmetricKeyParameter);
+  published
+    procedure TestWriteReadDsaKey;
+    procedure TestWriteReadRsaKey;
+    procedure TestWriteReadEcKeyFromBytes;
+    procedure TestWriteReadEcKeyGenerated;
+    procedure TestWritePemObjectOverride;
+  end;
+
+implementation
+
+{ TOpenSslWriterTest }
+
+class function TOpenSslWriterTest.GetTestRsaKey: IRsaPrivateCrtKeyParameters;
+var
+  LModulus, LPubExp, LPrivExp, LP, LQ, LDP, LDQ, LQInv: TBigInteger;
+begin
+  LModulus := TBigInteger.Create('b4a7e46170574f16a97082b22be58b6a2a629798419be12872a4bdba626cfae9900f76abfb12139dce5de56564fab2b6543165a040c606887420e33d91ed7ed7', 16);
+  LPubExp := TBigInteger.Create('11', 16);
+  LPrivExp := TBigInteger.Create('9f66f6b05410cd503b2709e88115d55daced94d1a34d4e32bf824d0dde6028ae79c5f07b580f5dce240d7111f7ddb130a7945cd7d957d1920994da389f490c89', 16);
+  LP := TBigInteger.Create('c0a0758cdf14256f78d4708c86becdead1b50ad4ad6c5c703e2168fbf37884cb', 16);
+  LQ := TBigInteger.Create('f01734d7960ea60070f1b06f2bb81bfac48ff192ae18451d5e56c734a5aab8a5', 16);
+  LDP := TBigInteger.Create('b54bb9edff22051d9ee60f9351a48591b6500a319429c069a3e335a1d6171391', 16);
+  LDQ := TBigInteger.Create('d3d83daf2a0cecd3367ae6f8ae1aeb82e9ac2f816c6fc483533d8297dd7884cd', 16);
+  LQInv := TBigInteger.Create('b8f52fc6f38593dabb661d3f50f8897f8106eee68b1bce78a95b132b4e5b5d19', 16);
+  Result := TRsaPrivateCrtKeyParameters.Create(LModulus, LPubExp, LPrivExp, LP, LQ, LDP, LDQ, LQInv);
+end;
+
+class function TOpenSslWriterTest.GetTestDsaParams: IDsaParameters;
+var
+  LP, LQ, LG: TBigInteger;
+begin
+  LP := TBigInteger.Create('7434410770759874867539421675728577177024889699586189000788950934679315164676852047058354758883833299702695428196962057871264685291775577130504050839126673');
+  LQ := TBigInteger.Create('1138656671590261728308283492178581223478058193247');
+  LG := TBigInteger.Create('4182906737723181805517018315469082619513954319976782448649747742951189003482834321192692620856488639629011570381138542789803819092529658402611668375788410');
+  Result := TDsaParameters.Create(LP, LQ, LG);
+end;
+
+procedure TOpenSslWriterTest.DoWriteReadTest(const APrivateKey: IAsymmetricKeyParameter);
+var
+  LStream: TStringStream;
+  LWriter: IOpenSslPemWriter;
+  LReader: IOpenSslPemReader;
+  LReadVal: TValue;
+  LReadPair: IAsymmetricCipherKeyPair;
+  LPriv: IAsymmetricKeyParameter;
+begin
+  LStream := TStringStream.Create('', TEncoding.ASCII);
+  try
+    LWriter := TOpenSslPemWriter.Create(LStream);
+    LWriter.WriteObject(TValue.From<IAsymmetricKeyParameter>(APrivateKey));
+    LStream.Position := 0;
+    LReader := TOpenSslPemReader.Create(LStream);
+    LReadVal := LReader.ReadObject();
+    Check(not LReadVal.IsEmpty, 'ReadObject should return key');
+    Check(LReadVal.TryAsType<IAsymmetricCipherKeyPair>(LReadPair), 'Should be key pair');
+    Check(LReadPair <> nil, 'Key pair should not be nil');
+    Check(Supports(LReadPair.Private, IAsymmetricKeyParameter, LPriv), 'Private should be IAsymmetricKeyParameter');
+    Check(LPriv.Equals(APrivateKey), 'Failed to read back test key');
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TOpenSslWriterTest.TestWriteReadDsaKey;
+var
+  LGen: IAsymmetricCipherKeyPairGenerator;
+  LDsaKeyParams: IKeyGenerationParameters;
+  LPair: IAsymmetricCipherKeyPair;
+  LSecRandom: ISecureRandom;
+begin
+  LSecRandom := TSecureRandom.Create();
+  LDsaKeyParams := TDsaKeyGenerationParameters.Create(LSecRandom, GetTestDsaParams) as IKeyGenerationParameters;
+  LGen := TDsaKeyPairGenerator.Create();
+  LGen.Init(LDsaKeyParams);
+  LPair := LGen.GenerateKeyPair();
+  DoWriteReadTest(LPair.Private);
+end;
+
+procedure TOpenSslWriterTest.TestWriteReadRsaKey;
+begin
+  DoWriteReadTest(GetTestRsaKey);
+end;
+
+procedure TOpenSslWriterTest.TestWriteReadEcKeyFromBytes;
+var
+  LKeyBytes: TCryptoLibByteArray;
+  LPrivKey: IAsymmetricKeyParameter;
+  LPrivInfo: IPrivateKeyInfo;
+begin
+  LKeyBytes := DecodeBase64(TestEcDsaKeyBytesBase64);
+  LPrivInfo := TPrivateKeyInfo.GetInstance(LKeyBytes);
+  LPrivKey := TPrivateKeyFactory.CreateKey(LPrivInfo);
+  DoWriteReadTest(LPrivKey);
+end;
+
+procedure TOpenSslWriterTest.TestWriteReadEcKeyGenerated;
+var
+  LEcGen: IAsymmetricCipherKeyPairGenerator;
+  LPair: IAsymmetricCipherKeyPair;
+  LSecRandom: ISecureRandom;
+begin
+  LSecRandom := TSecureRandom.Create();
+  LEcGen := TECKeyPairGenerator.Create();
+  LEcGen.Init(TKeyGenerationParameters.Create(LSecRandom, 239) as IKeyGenerationParameters);
+  LPair := LEcGen.GenerateKeyPair();
+  DoWriteReadTest(LPair.Private);
+end;
+
+procedure TOpenSslWriterTest.TestWritePemObjectOverride;
+var
+  LContent: TCryptoLibByteArray;
+  LObj: IPemObjectGenerator;
+  LStream: TStringStream;
+  LWriter: IOpenSslPemWriter;
+  I: Int32;
+  LOut: String;
+begin
+  System.SetLength(LContent, 100);
+  for I := 0 to 99 do
+    LContent[I] := Byte(I mod 256);
+  LObj := TPemObject.Create('FRED', LContent);
+
+  LStream := TStringStream.Create('', TEncoding.ASCII);
+  try
+    LWriter := TOpenSslPemWriter.Create(LStream);
+    LWriter.WriteObject(TValue.From<IPemObjectGenerator>(LObj));
+    Check(LStream.Size > 0, 'PEM output should not be empty');
+    LStream.Position := 0;
+    LOut := LStream.DataString;
+    Check(Pos('FRED', LOut) > 0, 'PEM should contain type FRED');
+  finally
+    LStream.Free;
+  end;
+end;
+
+initialization
+
+{$IFDEF FPC}
+RegisterTest(TOpenSslWriterTest);
+{$ELSE}
+RegisterTest(TOpenSslWriterTest.Suite);
+{$ENDIF FPC}
+
+end.

+ 93 - 1
CryptoLib.Tests/src/Utils/Pem/PemReaderTests.pas

@@ -34,6 +34,10 @@ uses
 {$ENDIF FPC}
   ClpPemObjects,
   ClpIPemObjects,
+  ClpIPemReader,
+  ClpPemReader,
+  ClpIPemWriter,
+  ClpPemWriter,
   ClpAsn1Objects,
   ClpPkcsAsn1Objects,
   ClpIPkcsAsn1Objects,
@@ -45,12 +49,17 @@ uses
 type
 
   TPemReaderTest = class(TCryptoLibAlgorithmTestCase)
+  strict private
+    procedure LengthTest(const AType: String;
+      const AHeaders: TCryptoLibGenericArray<IPemHeader>;
+      const AData: TCryptoLibByteArray);
   published
     procedure TestMalformedInput;
     procedure TestSaneInput;
     procedure TestWithHeaders;
     procedure TestNoWhiteSpace;
-
+    procedure TestPemLength;
+    procedure TestMalformed;
   end;
 
 implementation
@@ -264,6 +273,89 @@ begin
   end;
 end;
 
+procedure TPemReaderTest.LengthTest(const AType: String;
+  const AHeaders: TCryptoLibGenericArray<IPemHeader>;
+  const AData: TCryptoLibByteArray);
+var
+  LPemObj: IPemObject;
+  LStream: TStringStream;
+  LWriter: IPemWriter;
+  LOutputLen: Int32;
+begin
+  LPemObj := TPemObject.Create(AType, AHeaders, AData);
+  LStream := TStringStream.Create('', TEncoding.ASCII);
+  try
+    LWriter := TPemWriter.Create(LStream);
+    LWriter.WriteObject(LPemObj as IPemObjectGenerator);
+    LOutputLen := LWriter.GetOutputSize(LPemObj);
+    CheckEquals(LStream.Size, LOutputLen,
+      Format('PEM output length should match GetOutputSize for type %s', [AType]));
+  finally
+    LStream.Free;
+  end;
+end;
+
+procedure TPemReaderTest.TestPemLength;
+var
+  I: Int32;
+  LEmptyHeaders: TCryptoLibGenericArray<IPemHeader>;
+  LHeaders: TCryptoLibGenericArray<IPemHeader>;
+  LData: TCryptoLibByteArray;
+begin
+  SetLength(LEmptyHeaders, 0);
+  for I := 1 to 59 do
+  begin
+    SetLength(LData, I);
+    LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  end;
+
+  SetLength(LData, 100);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 101);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 102);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 103);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+
+  SetLength(LData, 1000);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 1001);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 1002);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+  SetLength(LData, 1003);
+  LengthTest('CERTIFICATE', LEmptyHeaders, LData);
+
+  SetLength(LHeaders, 2);
+  LHeaders[0] := TPemHeader.Create('Proc-Type', '4,ENCRYPTED');
+  LHeaders[1] := TPemHeader.Create('DEK-Info', 'DES3,0001020304050607');
+  SetLength(LData, 103);
+  LengthTest('RSA PRIVATE KEY', LHeaders, LData);
+end;
+
+procedure TPemReaderTest.TestMalformed;
+var
+  LStream: TStringStream;
+  LPemReader: IPemReader;
+begin
+  LStream := TStringStream.Create('-----BEGIN ' + sLineBreak, TEncoding.ASCII);
+  try
+    LPemReader := TPemReader.Create(LStream);
+    try
+      LPemReader.ReadPemObject();
+      Fail('must fail on malformed');
+    except
+      on E: EIOCryptoLibException do
+        CheckEquals('ran out of data before consuming type', E.Message, 'Exception message');
+      on E: Exception do
+        Fail('Expected EIOCryptoLibException, got ' + E.ClassName + ': ' + E.Message);
+    end;
+  finally
+    LStream.Free;
+  end;
+end;
+
 initialization
 
 {$IFDEF FPC}

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

@@ -6755,9 +6755,10 @@ end;
 
 function TDerBitString.GetBufferStream(): TFixedBufferStream;
 begin
- if FBufferStream = nil then
-  FBufferStream := TFixedBufferStream.Create(FContents, 1, System.Length(FContents) - 1, False);
-
+  if FBufferStream = nil then
+    FBufferStream := TFixedBufferStream.Create(FContents, 1, System.Length(FContents) - 1, False)
+  else
+    FBufferStream.Position := 0;
   Result := FBufferStream;
 end;
 

+ 2 - 2
CryptoLib/src/Asn1/Pkcs/ClpPkcsAsn1Objects.pas

@@ -887,7 +887,7 @@ begin
   Result := TDerSequence.Create(LV);
 end;
 
-{ TContentInfo }
+{ TPkcsContentInfo }
 
 class function TPkcsContentInfo.GetInstance(AObj: TObject): IPkcsContentInfo;
 begin
@@ -996,7 +996,7 @@ begin
   Result := TBerSequence.Create(LV);
 end;
 
-{ TSignedData }
+{ TPkcsSignedData }
 
 class function TPkcsSignedData.GetInstance(AObj: TObject): IPkcsSignedData;
 begin

+ 171 - 0
CryptoLib/src/Asn1/Pkcs/ClpPkcsDHAsn1Objects.pas

@@ -0,0 +1,171 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPkcsDHAsn1Objects;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpAsn1Core,
+  ClpIAsn1Core,
+  ClpIPkcsDHAsn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SBadSequenceSize = 'Bad sequence size: %d';
+
+type
+  /// <summary>
+  /// The DHParameter object (PKCS#3: P, G, optional L).
+  /// </summary>
+  TDHParameter = class(TAsn1Encodable, IDHParameter)
+  strict private
+  var
+    FP, FG: IDerInteger;
+    FL: IDerInteger;
+
+    function GetP: TBigInteger; inline;
+    function GetG: TBigInteger; inline;
+    function GetL: IDerInteger; inline;
+
+    constructor Create(const ASeq: IAsn1Sequence); overload;
+
+  public
+    constructor Create(const AP, AG: TBigInteger; AL: Int32); overload;
+
+    class function GetInstance(AObj: TObject): IDHParameter; overload; static;
+    class function GetInstance(const AObj: IAsn1Convertible): IDHParameter; overload; static;
+    class function GetInstance(const AEncoded: TCryptoLibByteArray): IDHParameter; overload; static;
+    class function GetInstance(const AObj: IAsn1TaggedObject;
+      AExplicitly: Boolean): IDHParameter; overload; static;
+    class function GetTagged(const ATaggedObject: IAsn1TaggedObject;
+      ADeclaredExplicit: Boolean): IDHParameter; static;
+
+    function ToAsn1Object: IAsn1Object; override;
+
+    property P: TBigInteger read GetP;
+    property G: TBigInteger read GetG;
+    property L: IDerInteger read GetL;
+  end;
+
+implementation
+
+{ TDHParameter }
+
+constructor TDHParameter.Create(const ASeq: IAsn1Sequence);
+var
+  LCount: Int32;
+begin
+  inherited Create();
+  LCount := ASeq.Count;
+  if (LCount < 2) or (LCount > 3) then
+    raise EArgumentCryptoLibException.CreateResFmt(@SBadSequenceSize, [LCount]);
+  FP := TDerInteger.GetInstance(ASeq[0]);
+  FG := TDerInteger.GetInstance(ASeq[1]);
+  if LCount > 2 then
+    FL := TDerInteger.GetInstance(ASeq[2])
+  else
+    FL := nil;
+end;
+
+constructor TDHParameter.Create(const AP, AG: TBigInteger; AL: Int32);
+begin
+  inherited Create();
+  FP := TDerInteger.Create(AP);
+  FG := TDerInteger.Create(AG);
+  if AL <> 0 then
+    FL := TDerInteger.ValueOf(AL)
+  else
+    FL := nil;
+end;
+
+function TDHParameter.GetP: TBigInteger;
+begin
+  Result := FP.PositiveValue;
+end;
+
+function TDHParameter.GetG: TBigInteger;
+begin
+  Result := FG.PositiveValue;
+end;
+
+function TDHParameter.GetL: IDerInteger;
+begin
+  Result := FL;
+end;
+
+class function TDHParameter.GetInstance(AObj: TObject): IDHParameter;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  if Supports(AObj, IDHParameter, Result) then
+    Exit;
+  Result := TDHParameter.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TDHParameter.GetInstance(const AObj: IAsn1Convertible): IDHParameter;
+begin
+  if AObj = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  if Supports(AObj, IDHParameter, Result) then
+    Exit;
+  Result := TDHParameter.Create(TAsn1Sequence.GetInstance(AObj));
+end;
+
+class function TDHParameter.GetInstance(const AEncoded: TCryptoLibByteArray): IDHParameter;
+begin
+  if AEncoded = nil then
+  begin
+    Result := nil;
+    Exit;
+  end;
+  Result := TDHParameter.Create(TAsn1Sequence.GetInstance(AEncoded));
+end;
+
+class function TDHParameter.GetInstance(const AObj: IAsn1TaggedObject;
+  AExplicitly: Boolean): IDHParameter;
+begin
+  Result := TDHParameter.Create(TAsn1Sequence.GetInstance(AObj, AExplicitly));
+end;
+
+class function TDHParameter.GetTagged(const ATaggedObject: IAsn1TaggedObject;
+  ADeclaredExplicit: Boolean): IDHParameter;
+begin
+  Result := TDHParameter.Create(TAsn1Sequence.GetTagged(ATaggedObject, ADeclaredExplicit));
+end;
+
+function TDHParameter.ToAsn1Object: IAsn1Object;
+begin
+  if FL <> nil then
+    Result := TDerSequence.Create([FP, FG, FL])
+  else
+    Result := TDerSequence.Create([FP, FG]);
+end;
+
+end.

+ 201 - 0
CryptoLib/src/Factories/ClpPrivateKeyInfoFactory.pas

@@ -0,0 +1,201 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPrivateKeyInfoFactory;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIAsymmetricKeyParameter,
+  ClpIRsaParameters,
+  ClpIDsaParameters,
+  ClpIECParameters,
+  ClpIDHParameters,
+  ClpIEd25519Parameters,
+  ClpIX25519Parameters,
+  ClpPkcsObjectIdentifiers,
+  ClpX9ObjectIdentifiers,
+  ClpEdECObjectIdentifiers,
+  ClpIPkcsAsn1Objects,
+  ClpIPkcsDHAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpPkcsDHAsn1Objects,
+  ClpPkcsRsaAsn1Objects,
+  ClpIPkcsRsaAsn1Objects,
+  ClpSecECAsn1Objects,
+  ClpIX509DsaAsn1Objects,
+  ClpX509DsaAsn1Objects,
+  ClpX9ECAsn1Objects,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpIX9ECAsn1Objects,
+  ClpISecECAsn1Objects,
+  ClpECGenerators,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Factory for creating PrivateKeyInfo from asymmetric private key parameters.
+  /// </summary>
+  TPrivateKeyInfoFactory = class sealed(TObject)
+  public
+    class function CreatePrivateKeyInfo(const APrivateKey: IAsymmetricKeyParameter): IPrivateKeyInfo; overload; static;
+    class function CreatePrivateKeyInfo(const APrivateKey: IAsymmetricKeyParameter;
+      const AAttributes: IAsn1Set): IPrivateKeyInfo; overload; static;
+  end;
+
+implementation
+
+{ TPrivateKeyInfoFactory }
+
+class function TPrivateKeyInfoFactory.CreatePrivateKeyInfo(const APrivateKey: IAsymmetricKeyParameter): IPrivateKeyInfo;
+begin
+  Result := CreatePrivateKeyInfo(APrivateKey, nil);
+end;
+
+class function TPrivateKeyInfoFactory.CreatePrivateKeyInfo(const APrivateKey: IAsymmetricKeyParameter;
+  const AAttributes: IAsn1Set): IPrivateKeyInfo;
+var
+  LAlgID: IAlgorithmIdentifier;
+  LRsaKey: IRsaKeyParameters;
+  LCrtKey: IRsaPrivateCrtKeyParameters;
+  LKeyStruct: IRsaPrivateKeyStructure;
+  LDsaKey: IDsaPrivateKeyParameters;
+  LECKey: IECPrivateKeyParameters;
+  LDhKey: IDHPrivateKeyParameters;
+  LX25519Key: IX25519PrivateKeyParameters;
+  LEd25519Key: IEd25519PrivateKeyParameters;
+  LPub: IECPublicKeyParameters;
+  LPubEnc: TCryptoLibByteArray;
+  LDerPub: IDerBitString;
+  LParams: IECDomainParameters;
+  LX962: IX962Parameters;
+  LOrderBitLength: Int32;
+  LEC: IECPrivateKeyStructure;
+  LAlgParams: IDHParameter;
+  LPrivBytes: TCryptoLibByteArray;
+  LPubBytes: TCryptoLibByteArray;
+  LDhParams: IDHParameters;
+begin
+  if APrivateKey = nil then
+    raise EArgumentNilCryptoLibException.Create('APrivateKey');
+  if not APrivateKey.IsPrivate then
+    raise EArgumentCryptoLibException.Create('Public key passed - private key expected');
+
+  // RSA
+  if Supports(APrivateKey, IRsaPrivateCrtKeyParameters, LCrtKey) then
+  begin
+    LAlgID := TAlgorithmIdentifier.Create(TPkcsObjectIdentifiers.RsaEncryption, TDerNull.Instance);
+    LKeyStruct := TRsaPrivateKeyStructure.Create(
+      LCrtKey.Modulus,
+      LCrtKey.PublicExponent,
+      LCrtKey.Exponent,
+      LCrtKey.P,
+      LCrtKey.Q,
+      LCrtKey.DP,
+      LCrtKey.DQ,
+      LCrtKey.QInv);
+    Result := TPrivateKeyInfo.Create(LAlgID, LKeyStruct, AAttributes);
+    Exit;
+  end;
+
+  if Supports(APrivateKey, IRsaKeyParameters, LRsaKey) then
+  begin
+    LAlgID := TAlgorithmIdentifier.Create(TPkcsObjectIdentifiers.RsaEncryption, TDerNull.Instance);
+    LKeyStruct := TRsaPrivateKeyStructure.Create(
+      LRsaKey.Modulus,
+      TBigInteger.Zero,
+      LRsaKey.Exponent,
+      TBigInteger.Zero,
+      TBigInteger.Zero,
+      TBigInteger.Zero,
+      TBigInteger.Zero,
+      TBigInteger.Zero);
+    Result := TPrivateKeyInfo.Create(LAlgID, LKeyStruct, AAttributes);
+    Exit;
+  end;
+
+  // DSA
+  if Supports(APrivateKey, IDsaPrivateKeyParameters, LDsaKey) then
+  begin
+    if LDsaKey.Parameters = nil then
+      raise EArgumentCryptoLibException.Create('DSA private key requires parameters.');
+    LAlgID := TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.IdDsa,
+      TDsaParameter.Create(LDsaKey.Parameters.P, LDsaKey.Parameters.Q, LDsaKey.Parameters.G) as IDsaParameter);
+    Result := TPrivateKeyInfo.Create(LAlgID, TDerInteger.Create(LDsaKey.X) as IDerInteger, AAttributes);
+    Exit;
+  end;
+
+  // EC
+  if Supports(APrivateKey, IECPrivateKeyParameters, LECKey) then
+  begin
+    LPub := TECKeyPairGenerator.GetCorrespondingPublicKey(LECKey);
+    LPubEnc := LPub.Q.GetEncoded(False);
+    LDerPub := TDerBitString.Create(LPubEnc);
+    LParams := LECKey.Parameters;
+    if LParams = nil then
+      raise EArgumentCryptoLibException.Create('EC private key requires parameters.');
+    LX962 := LParams.ToX962Parameters();
+    LOrderBitLength := LParams.N.BitLength;
+    LEC := TECPrivateKeyStructure.Create(LOrderBitLength, LECKey.D, LDerPub, LX962);
+    LAlgID := TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.IdECPublicKey, LX962);
+    Result := TPrivateKeyInfo.Create(LAlgID, LEC, AAttributes);
+    Exit;
+  end;
+
+  // DH
+  if Supports(APrivateKey, IDHPrivateKeyParameters, LDhKey) then
+  begin
+    LDhParams := LDhKey.Parameters;
+    if LDhParams = nil then
+      raise EArgumentCryptoLibException.Create('DH private key requires parameters.');
+    LAlgParams := TDHParameter.Create(LDhParams.P, LDhParams.G, LDhParams.L);
+    LAlgID := TAlgorithmIdentifier.Create(LDhKey.AlgorithmOid, LAlgParams);
+    Result := TPrivateKeyInfo.Create(LAlgID, TDerInteger.Create(LDhKey.X) as IDerInteger, AAttributes);
+    Exit;
+  end;
+
+  // X25519
+  if Supports(APrivateKey, IX25519PrivateKeyParameters, LX25519Key) then
+  begin
+    LAlgID := TAlgorithmIdentifier.Create(TEdECObjectIdentifiers.IdX25519);
+    LPrivBytes := LX25519Key.GetEncoded();
+    LPubBytes := LX25519Key.GeneratePublicKey().GetEncoded();
+    Result := TPrivateKeyInfo.Create(LAlgID, TDerOctetString.Create(LPrivBytes) as IDerOctetString, AAttributes, LPubBytes);
+    Exit;
+  end;
+
+  // Ed25519
+  if Supports(APrivateKey, IEd25519PrivateKeyParameters, LEd25519Key) then
+  begin
+    LAlgID := TAlgorithmIdentifier.Create(TEdECObjectIdentifiers.IdEd25519);
+    LPrivBytes := LEd25519Key.GetEncoded();
+    LPubBytes := LEd25519Key.GeneratePublicKey().GetEncoded();
+    Result := TPrivateKeyInfo.Create(LAlgID, TDerOctetString.Create(LPrivBytes) as IDerOctetString, AAttributes, LPubBytes);
+    Exit;
+  end;
+
+  raise ENotSupportedCryptoLibException.Create('Key type not supported for PrivateKeyInfo (supported: RSA, DSA, EC, DH, X25519, Ed25519).');
+end;
+
+end.

+ 1 - 1
CryptoLib/src/Factories/ClpPublicKeyFactory.pas

@@ -114,7 +114,7 @@ begin
     LDsaY := TDerInteger.GetInstance(AKeyInfo.ParsePublicKey());
     if LAlgID.Parameters <> nil then
     begin
-      LDsaPara := TDsaParameter.GetInstance(LAlgID.Parameters.ToAsn1Object());
+      LDsaPara := TDsaParameter.GetInstance(LAlgID.Parameters);
       LDsaParams := TDsaParameters.Create(LDsaPara.P, LDsaPara.Q, LDsaPara.G);
     end
     else

+ 36 - 0
CryptoLib/src/GeneralUtilities/ClpArrayUtilities.pas

@@ -101,6 +101,16 @@ type
     class function ToString<T>(const AData: TCryptoLibGenericArray<T>;
       const AConverter: TFunc<T, String>): String; reintroduce; overload; static;
 
+    /// <summary>
+    /// Reverse array elements in place.
+    /// </summary>
+    class procedure ReverseInPlace<T>(var AArray: TCryptoLibGenericArray<T>); overload; static;
+    /// <summary>
+    /// Reverse the range [AFromIndex, AToIndex) in place.
+    /// </summary>
+    class procedure ReverseInPlace<T>(var AArray: TCryptoLibGenericArray<T>;
+      AFromIndex, AToIndex: Int32); overload; static;
+
   end;
 
 implementation
@@ -488,4 +498,30 @@ begin
   end;
 end;
 
+class procedure TArrayUtilities.ReverseInPlace<T>(var AArray: TCryptoLibGenericArray<T>);
+begin
+  ReverseInPlace<T>(AArray, 0, System.Length(AArray));
+end;
+
+class procedure TArrayUtilities.ReverseInPlace<T>(var AArray: TCryptoLibGenericArray<T>;
+  AFromIndex, AToIndex: Int32);
+var
+  LLeft: Int32;
+  LRight: Int32;
+  LTemp: T;
+begin
+  if (AFromIndex < 0) or (AToIndex > System.Length(AArray)) or (AFromIndex > AToIndex) then
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidLength, [AFromIndex, AToIndex]);
+  LLeft := AFromIndex;
+  LRight := AToIndex - 1;
+  while LLeft < LRight do
+  begin
+    LTemp := AArray[LLeft];
+    AArray[LLeft] := AArray[LRight];
+    AArray[LRight] := LTemp;
+    System.Inc(LLeft);
+    System.Dec(LRight);
+  end;
+end;
+
 end.

+ 2 - 27
CryptoLib/src/Interfaces/Asn1/Pkcs/ClpIPkcsAsn1Objects.pas

@@ -107,32 +107,7 @@ type
   end;
 
   /// <summary>
-  /// Interface for RsaPrivateKeyStructure.
-  /// </summary>
-  IRsaPrivateKeyStructure = interface(IAsn1Encodable)
-    ['{2F80DE02-A5DF-4F82-8011-A6A658DF5473}']
-
-    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 ContentInfo.
+  /// Interface for PkcsContentInfo.
   /// </summary>
   IPkcsContentInfo = interface(IAsn1Encodable)
     ['{B9C0D1E2-F3A4-5678-9012-3456789ABCDE}']
@@ -145,7 +120,7 @@ type
   end;
 
   /// <summary>
-  /// Interface for SignedData (PKCS#7).
+  /// Interface for PkcsSignedData (PKCS#7).
   /// </summary>
   IPkcsSignedData = interface(IAsn1Encodable)
     ['{C0D1E2F3-A4B5-6789-0123-456789ABCDEF}']

+ 49 - 0
CryptoLib/src/Interfaces/Asn1/Pkcs/ClpIPkcsDHAsn1Objects.pas

@@ -0,0 +1,49 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPkcsDHAsn1Objects;
+
+{$I ..\..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIAsn1Core,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// Interface for DHParameter (PKCS#3: P, G, optional L).
+  /// </summary>
+  IDHParameter = interface(IAsn1Encodable)
+    ['{7E8F9A0B-1C2D-4E5F-A6B7-8C9D0E1F2A3B}']
+
+    function GetP: TBigInteger;
+    function GetG: TBigInteger;
+    /// <summary>Optional L; nil when not present in sequence.</summary>
+    function GetL: IDerInteger;
+
+    property P: TBigInteger read GetP;
+    property G: TBigInteger read GetG;
+    property L: IDerInteger read GetL;
+  end;
+
+implementation
+
+end.

+ 41 - 0
CryptoLib/src/Interfaces/OpenSsl/ClpIOpenSslPemReader.pas

@@ -0,0 +1,41 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIOpenSslPemReader;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Rtti,
+  ClpIPemReader;
+
+type
+  /// <summary>
+  /// Interface for OpenSSL PEM reader. Read OpenSSL PEM encoded streams containing
+  /// X509 certificates, PKCS#8 encoded keys and PKCS#7/CMS objects; returns typed
+  /// values as TValue (e.g. IX509Certificate, IAsymmetricKeyParameter, IAsymmetricCipherKeyPair).
+  /// </summary>
+  IOpenSslPemReader = interface(IPemReader)
+    ['{068FE91C-61DC-43D9-B75B-5D8A04F86647}']
+    function ReadObject(): TValue;
+  end;
+
+implementation
+
+end.

+ 40 - 0
CryptoLib/src/Interfaces/OpenSsl/ClpIOpenSslPemWriter.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 ClpIOpenSslPemWriter;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Rtti,
+  ClpIPemWriter;
+
+type
+  /// <summary>
+  /// Interface for OpenSSL PEM writer. Write any supported object as PEM
+  /// (type and encoding determined by TMiscPemGenerator).
+  /// </summary>
+  IOpenSslPemWriter = interface(IPemWriter)
+    ['{23403EC4-0046-4F52-8539-B5D49C0ED6E3}']
+    procedure WriteObject(const AObj: TValue);
+  end;
+
+implementation
+
+end.

+ 2 - 63
CryptoLib/src/Interfaces/Pem/ClpIPemObjects.pas

@@ -23,8 +23,7 @@ interface
 
 uses
   Classes,
-  ClpCryptoLibTypes,
-  ClpIAsn1Objects;
+  ClpCryptoLibTypes;
 
 type
   IPemHeader = interface;
@@ -100,67 +99,7 @@ type
     property Content: TCryptoLibByteArray read GetContent;
   end;
 
-  /// <summary>
-  /// Interface for PEM reader.
-  /// </summary>
-  IPemReader = interface(IInterface)
-    ['{0139877B-ED23-46C7-BE39-E5010AC26507}']
-
-    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)
-    ['{E534B37C-C6B0-4066-9AB9-758BDAD3C3A0}']
-
-    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;
-
-  /// <summary>
-  /// Interface for PEM parser.
-  /// </summary>
-  IPemParser = interface(IInterface)
-    ['{8C91EC3F-A5D3-4714-8A3E-A68C381FF754}']
-
-    /// <summary>
-    /// Read a PEM object from the stream and return it as an ASN.1 sequence.
-    /// </summary>
-    /// <param name="AInStream">The input stream to read from</param>
-    /// <returns>An ASN.1 sequence, or nil if no PEM object found</returns>
-    function ReadPemObject(const AInStream: TStream): IAsn1Sequence;
-  end;
-
 implementation
 
 end.
+

+ 45 - 0
CryptoLib/src/Interfaces/Pem/ClpIPemParser.pas

@@ -0,0 +1,45 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIPemParser;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIAsn1Objects;
+
+type
+  /// <summary>
+  /// Interface for PEM parser.
+  /// </summary>
+  IPemParser = interface(IInterface)
+    ['{8C91EC3F-A5D3-4714-8A3E-A68C381FF754}']
+
+    /// <summary>
+    /// Read a PEM object from the stream and return it as an ASN.1 sequence.
+    /// </summary>
+    /// <param name="AInStream">The input stream to read from</param>
+    /// <returns>An ASN.1 sequence, or nil if no PEM object found</returns>
+    function ReadPemObject(const AInStream: TStream): IAsn1Sequence;
+  end;
+
+implementation
+
+end.

+ 51 - 0
CryptoLib/src/Interfaces/Pem/ClpIPemReader.pas

@@ -0,0 +1,51 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPemReader;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIPemObjects;
+
+type
+  /// <summary>
+  /// Interface for PEM reader.
+  /// </summary>
+  IPemReader = interface(IInterface)
+    ['{0139877B-ED23-46C7-BE39-E5010AC26507}']
+
+    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;
+
+implementation
+
+end.

+ 58 - 0
CryptoLib/src/Interfaces/Pem/ClpIPemWriter.pas

@@ -0,0 +1,58 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIPemWriter;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  ClpIPemObjects;
+
+type
+  /// <summary>
+  /// Interface for PEM writer.
+  /// </summary>
+  IPemWriter = interface(IInterface)
+    ['{E534B37C-C6B0-4066-9AB9-758BDAD3C3A0}']
+
+    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.

+ 273 - 0
CryptoLib/src/OpenSsl/ClpOpenSslMiscPemGenerator.pas

@@ -0,0 +1,273 @@
+{ *********************************************************************************** }
+{ *                              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 ClpOpenSslMiscPemGenerator;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Rtti,
+  ClpIPemObjects,
+  ClpPemObjects,
+  ClpIAsymmetricKeyParameter,
+  ClpPrivateKeyInfoFactory,
+  ClpSubjectPublicKeyInfoFactory,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpIDsaParameters,
+  ClpIPkcsAsn1Objects,
+  ClpPkcsObjectIdentifiers,
+  ClpX9ObjectIdentifiers,
+  ClpOiwObjectIdentifiers,
+  ClpIX509Asn1Objects,
+  ClpX509DsaAsn1Objects,
+  ClpIX509DsaAsn1Objects,
+  ClpAsn1Core,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIX509Certificate,
+  ClpIX509Crl,
+  ClpIX509V2AttributeCertificate,
+  ClpIPkcs10CertificationRequest,
+  ClpICmsAsn1Objects,
+  ClpX509Certificate,
+  ClpX509Crl,
+  ClpX509V2AttributeCertificate,
+  ClpPkcs10CertificationRequest,
+  ClpCmsAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpBigInteger,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// PEM generator for the original set of PEM objects used in OpenSSL.
+  /// </summary>
+  TOpenSslMiscPemGenerator = class sealed(TInterfacedObject, IPemObjectGenerator)
+  strict private
+    FObj: TValue;
+
+    class function CreatePemObject(const AObj: TValue): IPemObject; static;
+    class function EncodePrivateKey(const AAkp: IAsymmetricKeyParameter;
+      out AKeyType: String): TCryptoLibByteArray; static;
+    class function EncodePrivateKeyInfo(const AInfo: IPrivateKeyInfo;
+      out AKeyType: String): TCryptoLibByteArray; static;
+    class function EncodePublicKey(const AAkp: IAsymmetricKeyParameter;
+      out AKeyType: String): TCryptoLibByteArray; static;
+    class function EncodePublicKeyInfo(const AInfo: ISubjectPublicKeyInfo;
+      out AKeyType: String): TCryptoLibByteArray; static;
+  public
+    constructor Create(const AObj: TValue);
+    function Generate(): IPemObject;
+  end;
+
+implementation
+
+{ TOpenSslMiscPemGenerator }
+
+constructor TOpenSslMiscPemGenerator.Create(const AObj: TValue);
+begin
+  inherited Create();
+  FObj := AObj;
+end;
+
+function TOpenSslMiscPemGenerator.Generate: IPemObject;
+begin
+  try
+    Result := CreatePemObject(FObj);
+  except
+    on E: Exception do
+      raise EPemGenerationCryptoLibException.Create('encoding exception');
+  end;
+end;
+
+class function TOpenSslMiscPemGenerator.CreatePemObject(const AObj: TValue): IPemObject;
+var
+  LKp: IAsymmetricCipherKeyPair;
+  LPemObj: IPemObject;
+  LPemGen: IPemObjectGenerator;
+  LCert: IX509Certificate;
+  LCrl: IX509Crl;
+  LAkp: IAsymmetricKeyParameter;
+  LPrivInfo: IPrivateKeyInfo;
+  LPubInfo: ISubjectPublicKeyInfo;
+  LAttrCert: IX509V2AttributeCertificate;
+  LCertReq: IPkcs10CertificationRequest;
+  LCmsContent: ICmsContentInfo;
+  LPkcsContent: IPkcsContentInfo;
+  LType: String;
+  LEncoding: TCryptoLibByteArray;
+begin
+  if AObj.IsEmpty then
+    raise EArgumentNilCryptoLibException.Create('obj');
+
+  // Key pair -> recurse with private key
+  if AObj.TryAsType<IAsymmetricCipherKeyPair>(LKp) then
+    Exit(CreatePemObject(TValue.From<IAsymmetricKeyParameter>(LKp.Private)));
+
+  // PEM object identity
+  if AObj.TryAsType<IPemObject>(LPemObj) then
+    Exit(LPemObj);
+
+  // PEM object generator
+  if AObj.TryAsType<IPemObjectGenerator>(LPemGen) then
+    Exit(LPemGen.Generate());
+
+  // X509 Certificate
+  if AObj.TryAsType<IX509Certificate>(LCert) then
+  begin
+    try
+      LEncoding := LCert.GetEncoded();
+    except
+      on E: Exception do
+        raise EPemGenerationCryptoLibException.Create('Cannot Encode object: ' + E.Message);
+    end;
+    Exit(TPemObject.Create('CERTIFICATE', LEncoding));
+  end;
+
+  // X509 CRL
+  if AObj.TryAsType<IX509Crl>(LCrl) then
+  begin
+    try
+      LEncoding := LCrl.GetEncoded();
+    except
+      on E: Exception do
+        raise EPemGenerationCryptoLibException.Create('Cannot Encode object: ' + E.Message);
+    end;
+    Exit(TPemObject.Create('X509 CRL', LEncoding));
+  end;
+
+  // Asymmetric key (private or public)
+  if AObj.TryAsType<IAsymmetricKeyParameter>(LAkp) then
+  begin
+    if LAkp.IsPrivate then
+      LEncoding := EncodePrivateKey(LAkp, LType)
+    else
+      LEncoding := EncodePublicKey(LAkp, LType);
+    Exit(TPemObject.Create(LType, LEncoding));
+  end;
+
+  // PrivateKeyInfo
+  if AObj.TryAsType<IPrivateKeyInfo>(LPrivInfo) then
+  begin
+    LEncoding := EncodePrivateKeyInfo(LPrivInfo, LType);
+    Exit(TPemObject.Create(LType, LEncoding));
+  end;
+
+  // SubjectPublicKeyInfo
+  if AObj.TryAsType<ISubjectPublicKeyInfo>(LPubInfo) then
+  begin
+    LEncoding := EncodePublicKeyInfo(LPubInfo, LType);
+    Exit(TPemObject.Create(LType, LEncoding));
+  end;
+
+  // X509V2 Attribute Certificate
+  if AObj.TryAsType<IX509V2AttributeCertificate>(LAttrCert) then
+    Exit(TPemObject.Create('ATTRIBUTE CERTIFICATE', LAttrCert.GetEncoded()));
+
+  // PKCS#10 Certification Request
+  if AObj.TryAsType<IPkcs10CertificationRequest>(LCertReq) then
+    Exit(TPemObject.Create('CERTIFICATE REQUEST', LCertReq.GetEncoded()));
+
+  // CMS ContentInfo
+  if AObj.TryAsType<ICmsContentInfo>(LCmsContent) then
+    Exit(TPemObject.Create('PKCS7', LCmsContent.GetEncoded()));
+
+  // PKCS ContentInfo
+  if AObj.TryAsType<IPkcsContentInfo>(LPkcsContent) then
+    Exit(TPemObject.Create('PKCS7', LPkcsContent.GetEncoded()));
+
+  raise EPemGenerationCryptoLibException.Create('Object type not supported');
+end;
+
+class function TOpenSslMiscPemGenerator.EncodePrivateKey(const AAkp: IAsymmetricKeyParameter;
+  out AKeyType: String): TCryptoLibByteArray;
+var
+  LInfo: IPrivateKeyInfo;
+begin
+  LInfo := TPrivateKeyInfoFactory.CreatePrivateKeyInfo(AAkp);
+  Result := EncodePrivateKeyInfo(LInfo, AKeyType);
+end;
+
+class function TOpenSslMiscPemGenerator.EncodePrivateKeyInfo(const AInfo: IPrivateKeyInfo;
+  out AKeyType: String): TCryptoLibByteArray;
+var
+  LAlgID: IAlgorithmIdentifier;
+  LAlgOid: IDerObjectIdentifier;
+  LDsaP: IDsaParameter;
+  LX: TBigInteger;
+  LY: TBigInteger;
+  LSeq: IAsn1Sequence;
+begin
+  LAlgID := AInfo.PrivateKeyAlgorithm;
+  LAlgOid := LAlgID.Algorithm;
+
+  if LAlgOid.Equals(TPkcsObjectIdentifiers.RsaEncryption) then
+  begin
+    AKeyType := 'RSA PRIVATE KEY';
+    Result := AInfo.ParsePrivateKey().GetEncoded();
+    Exit;
+  end;
+
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdECPublicKey) then
+  begin
+    AKeyType := 'EC PRIVATE KEY';
+    Result := AInfo.ParsePrivateKey().GetEncoded();
+    Exit;
+  end;
+
+  if LAlgOid.Equals(TX9ObjectIdentifiers.IdDsa) or LAlgOid.Equals(TOiwObjectIdentifiers.DsaWithSha1) then
+  begin
+    AKeyType := 'DSA PRIVATE KEY';
+    LDsaP := TDsaParameter.GetInstance(LAlgID.Parameters);
+    LX := TDerInteger.GetInstance(AInfo.ParsePrivateKey()).Value;
+    LY := LDsaP.G.ModPow(LX, LDsaP.P);
+    LSeq := TDerSequence.Create([
+      TDerInteger.Zero,
+      TDerInteger.Create(LDsaP.P) as IDerInteger,
+      TDerInteger.Create(LDsaP.Q) as IDerInteger,
+      TDerInteger.Create(LDsaP.G) as IDerInteger,
+      TDerInteger.Create(LY) as IDerInteger,
+      TDerInteger.Create(LX) as IDerInteger
+    ]);
+    Result := LSeq.GetEncoded();
+    Exit;
+  end;
+
+  AKeyType := 'PRIVATE KEY';
+  Result := AInfo.GetEncoded();
+end;
+
+class function TOpenSslMiscPemGenerator.EncodePublicKey(const AAkp: IAsymmetricKeyParameter;
+  out AKeyType: String): TCryptoLibByteArray;
+var
+  LInfo: ISubjectPublicKeyInfo;
+begin
+  LInfo := TSubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(AAkp);
+  Result := EncodePublicKeyInfo(LInfo, AKeyType);
+end;
+
+class function TOpenSslMiscPemGenerator.EncodePublicKeyInfo(const AInfo: ISubjectPublicKeyInfo;
+  out AKeyType: String): TCryptoLibByteArray;
+begin
+  AKeyType := 'PUBLIC KEY';
+  Result := AInfo.GetEncoded();
+end;
+
+end.

+ 364 - 0
CryptoLib/src/OpenSsl/ClpOpenSslPemReader.pas

@@ -0,0 +1,364 @@
+{ *********************************************************************************** }
+{ *                              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 ClpOpenSslPemReader;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Generics.Collections,
+  Rtti,
+  ClpIPemObjects,
+  ClpPemReader,
+  ClpIOpenSslPemReader,
+  ClpIAsymmetricKeyParameter,
+  ClpIAsymmetricCipherKeyPair,
+  ClpIX509Certificate,
+  ClpIX509Crl,
+  ClpIX509V2AttributeCertificate,
+  ClpIPkcs10CertificationRequest,
+  ClpICmsAsn1Objects,
+  ClpAsn1Core,
+  ClpIAsn1Core,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpPublicKeyFactory,
+  ClpPrivateKeyFactory,
+  ClpAsymmetricCipherKeyPair,
+  ClpX509Certificate,
+  ClpX509Crl,
+  ClpX509V2AttributeCertificate,
+  ClpPkcs10CertificationRequest,
+  ClpCmsAsn1Objects,
+  ClpX509RsaAsn1Objects,
+  ClpIX509RsaAsn1Objects,
+  ClpPkcsRsaAsn1Objects,
+  ClpIPkcsRsaAsn1Objects,
+  ClpPkcsAsn1Objects,
+  ClpIPkcsAsn1Objects,
+  ClpSecECAsn1Objects,
+  ClpISecECAsn1Objects,
+  ClpX509Asn1Objects,
+  ClpRsaParameters,
+  ClpDsaParameters,
+  ClpIDsaParameters,
+  ClpX9ObjectIdentifiers,
+  ClpECGenerators,
+  ClpIECGenerators,
+  ClpIECParameters,
+  ClpIX509Asn1Objects,
+  ClpStringUtilities,
+  ClpCollectionUtilities,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SUnrecognisedObject = 'unrecognised object: %s';
+  SProblemParsingCert = 'problem parsing cert: %s';
+  SProblemParsingPkcs7 = 'problem parsing PKCS7 object: %s';
+  SMalformedSequenceRsa = 'malformed sequence in RSA private key';
+  SMalformedSequenceDsa = 'malformed sequence in DSA private key';
+  SProblemCreatingPrivateKey = 'problem creating %s private key: %s';
+  SUnknownKeyType = 'Unknown key type: %s';
+  SEncryptedPrivateKeyNotSupported = 'Encrypted private key is not supported';
+
+type
+  /// <summary>
+  /// Reader for OpenSSL PEM encoded streams containing X509 certificates,
+  /// PKCS#8 encoded keys and PKCS#7/CMS objects. Returns typed values as TValue.
+  /// Encrypted private keys are not supported.
+  /// </summary>
+  TOpenSslPemReader = class(TPemReader, IOpenSslPemReader)
+  strict private
+    function ReadRsaPublicKey(const APemObject: IPemObject): IAsymmetricKeyParameter;
+    function ReadPublicKey(const APemObject: IPemObject): IAsymmetricKeyParameter;
+    function ReadCertificate(const APemObject: IPemObject): IX509Certificate;
+    function ReadCrl(const APemObject: IPemObject): IX509Crl;
+    function ReadCertificateRequest(const APemObject: IPemObject): IPkcs10CertificationRequest;
+    function ReadAttributeCertificate(const APemObject: IPemObject): IX509V2AttributeCertificate;
+    function ReadPkcs7(const APemObject: IPemObject): ICmsContentInfo;
+    function ReadPrivateKey(const APemObject: IPemObject): TValue;
+  public
+    constructor Create(const AReader: TStream);
+    function ReadObject(): TValue;
+  end;
+
+implementation
+
+{ TOpenSslPemReader }
+
+constructor TOpenSslPemReader.Create(const AReader: TStream);
+begin
+  inherited Create(AReader);
+end;
+
+function TOpenSslPemReader.ReadObject(): TValue;
+var
+  LObj: IPemObject;
+  LType: String;
+begin
+  LObj := ReadPemObject();
+  if LObj = nil then
+  begin
+    Result := TValue.Empty;
+    Exit;
+  end;
+
+  LType := LObj.&Type;
+
+  if TStringUtilities.EndsWith(LType, 'PRIVATE KEY') then
+  begin
+    Result := ReadPrivateKey(LObj);
+    Exit;
+  end;
+
+  if LType = 'PUBLIC KEY' then
+  begin
+    Result := TValue.From<IAsymmetricKeyParameter>(ReadPublicKey(LObj));
+    Exit;
+  end;
+
+  if LType = 'RSA PUBLIC KEY' then
+  begin
+    Result := TValue.From<IAsymmetricKeyParameter>(ReadRsaPublicKey(LObj));
+    Exit;
+  end;
+
+  if (LType = 'CERTIFICATE REQUEST') or (LType = 'NEW CERTIFICATE REQUEST') then
+  begin
+    Result := TValue.From<IPkcs10CertificationRequest>(ReadCertificateRequest(LObj));
+    Exit;
+  end;
+
+  if (LType = 'CERTIFICATE') or (LType = 'X509 CERTIFICATE') then
+  begin
+    Result := TValue.From<IX509Certificate>(ReadCertificate(LObj));
+    Exit;
+  end;
+
+  if (LType = 'PKCS7') or (LType = 'CMS') then
+  begin
+    Result := TValue.From<ICmsContentInfo>(ReadPkcs7(LObj));
+    Exit;
+  end;
+
+  if LType = 'X509 CRL' then
+  begin
+    Result := TValue.From<IX509Crl>(ReadCrl(LObj));
+    Exit;
+  end;
+
+  if LType = 'ATTRIBUTE CERTIFICATE' then
+  begin
+    Result := TValue.From<IX509V2AttributeCertificate>(ReadAttributeCertificate(LObj));
+    Exit;
+  end;
+
+  raise EIOCryptoLibException.CreateResFmt(@SUnrecognisedObject, [LType]);
+end;
+
+function TOpenSslPemReader.ReadRsaPublicKey(const APemObject: IPemObject): IAsymmetricKeyParameter;
+var
+  LRsaPublicKey: IRsaPublicKeyStructure;
+begin
+  LRsaPublicKey := TRsaPublicKeyStructure.GetInstance(APemObject.Content);
+  Result := TRsaKeyParameters.Create(False, LRsaPublicKey.Modulus, LRsaPublicKey.PublicExponent);
+end;
+
+function TOpenSslPemReader.ReadPublicKey(const APemObject: IPemObject): IAsymmetricKeyParameter;
+begin
+  Result := TPublicKeyFactory.CreateKey(APemObject.Content);
+end;
+
+function TOpenSslPemReader.ReadCertificate(const APemObject: IPemObject): IX509Certificate;
+begin
+  try
+    Result := TX509Certificate.Create(APemObject.Content);
+  except
+    on E: Exception do
+      raise EPemGenerationCryptoLibException.CreateResFmt(@SProblemParsingCert, [E.Message]);
+  end;
+end;
+
+function TOpenSslPemReader.ReadCrl(const APemObject: IPemObject): IX509Crl;
+begin
+  try
+    Result := TX509Crl.Create(APemObject.Content);
+  except
+    on E: Exception do
+      raise EPemGenerationCryptoLibException.CreateResFmt(@SProblemParsingCert, [E.Message]);
+  end;
+end;
+
+function TOpenSslPemReader.ReadCertificateRequest(const APemObject: IPemObject): IPkcs10CertificationRequest;
+begin
+  try
+    Result := TPkcs10CertificationRequest.Create(APemObject.Content);
+  except
+    on E: Exception do
+      raise EPemGenerationCryptoLibException.CreateResFmt(@SProblemParsingCert, [E.Message]);
+  end;
+end;
+
+function TOpenSslPemReader.ReadAttributeCertificate(const APemObject: IPemObject): IX509V2AttributeCertificate;
+begin
+  Result := TX509V2AttributeCertificate.Create(APemObject.Content);
+end;
+
+function TOpenSslPemReader.ReadPkcs7(const APemObject: IPemObject): ICmsContentInfo;
+begin
+  try
+    Result := TCmsContentInfo.GetInstance(APemObject.Content);
+  except
+    on E: Exception do
+      raise EPemGenerationCryptoLibException.CreateResFmt(@SProblemParsingPkcs7, [E.Message]);
+  end;
+end;
+
+function TOpenSslPemReader.ReadPrivateKey(const APemObject: IPemObject): TValue;
+var
+  LType: String;
+  LKeyBytes: TCryptoLibByteArray;
+  LFields: TDictionary<String, String>;
+  LProcType: String;
+  LHeader: IPemHeader;
+  I: Int32;
+  LSeq: IAsn1Sequence;
+  LRsa: IRsaPrivateKeyStructure;
+  LPubSpec, LPrivSpec: IAsymmetricKeyParameter;
+  LP, LQ, LG, LY, LX: IDerInteger;
+  LDsaParams: IDsaParameters;
+  LPKey: IECPrivateKeyStructure;
+  LAlgId: IAlgorithmIdentifier;
+  LPrivInfo: IPrivateKeyInfo;
+  LPubKey: IDerBitString;
+  LPubInfo: ISubjectPublicKeyInfo;
+  LECPriv: IECPrivateKeyParameters;
+begin
+  if not TStringUtilities.EndsWith(APemObject.&Type, 'PRIVATE KEY') then
+    raise EArgumentCryptoLibException.Create('Expected type ending with PRIVATE KEY');
+
+  LType := TStringUtilities.Trim(TStringUtilities.Substring(APemObject.&Type, 1,
+    Length(APemObject.&Type) - Length('PRIVATE KEY')));
+  LKeyBytes := APemObject.Content;
+
+  LFields := TDictionary<String, String>.Create();
+  try
+    for I := 0 to Length(APemObject.Headers) - 1 do
+    begin
+      LHeader := APemObject.Headers[I];
+      LFields.AddOrSetValue(LHeader.Name, LHeader.Value);
+    end;
+
+    LProcType := TCollectionUtilities.GetValueOrNull<String, String>(LFields, 'Proc-Type');
+    if LProcType = '4,ENCRYPTED' then
+      raise ENotSupportedCryptoLibException.Create(SEncryptedPrivateKeyNotSupported);
+
+    try
+      LSeq := TAsn1Sequence.GetInstance(LKeyBytes);
+
+      if LType = 'RSA' then
+      begin
+        if LSeq.Count <> 9 then
+          raise EPemGenerationCryptoLibException.Create(SMalformedSequenceRsa);
+
+        LRsa := TRsaPrivateKeyStructure.GetInstance(LSeq);
+
+        LPubSpec := TRsaKeyParameters.Create(False, LRsa.Modulus, LRsa.PublicExponent);
+        LPrivSpec := TRsaPrivateCrtKeyParameters.Create(
+          LRsa.Modulus, LRsa.PublicExponent, LRsa.PrivateExponent,
+          LRsa.Prime1, LRsa.Prime2, LRsa.Exponent1, LRsa.Exponent2,
+          LRsa.Coefficient);
+
+        Result := TValue.From<IAsymmetricCipherKeyPair>(
+          TAsymmetricCipherKeyPair.Create(LPubSpec, LPrivSpec));
+        Exit;
+      end;
+
+      if LType = 'DSA' then
+      begin
+        if LSeq.Count <> 6 then
+          raise EPemGenerationCryptoLibException.Create(SMalformedSequenceDsa);
+
+        LP := TDerInteger.GetInstance(LSeq[1]);
+        LQ := TDerInteger.GetInstance(LSeq[2]);
+        LG := TDerInteger.GetInstance(LSeq[3]);
+        LY := TDerInteger.GetInstance(LSeq[4]);
+        LX := TDerInteger.GetInstance(LSeq[5]);
+
+        LDsaParams := TDsaParameters.Create(LP.Value, LQ.Value, LG.Value);
+
+        LPrivSpec := TDsaPrivateKeyParameters.Create(LX.Value, LDsaParams);
+        LPubSpec := TDsaPublicKeyParameters.Create(LY.Value, LDsaParams);
+
+        Result := TValue.From<IAsymmetricCipherKeyPair>(
+          TAsymmetricCipherKeyPair.Create(LPubSpec, LPrivSpec));
+        Exit;
+      end;
+
+      if LType = 'EC' then
+      begin
+        LPKey := TECPrivateKeyStructure.GetInstance(LSeq);
+        LAlgId := TAlgorithmIdentifier.Create(TX9ObjectIdentifiers.IdECPublicKey, LPKey.Parameters);
+
+        LPrivInfo := TPrivateKeyInfo.Create(LAlgId, LPKey.ToAsn1Object);
+        LPrivSpec := TPrivateKeyFactory.CreateKey(LPrivInfo);
+
+        LPubKey := LPKey.PublicKey;
+        if LPubKey <> nil then
+        begin
+          LPubInfo := TSubjectPublicKeyInfo.Create(LAlgId, LPubKey);
+          LPubSpec := TPublicKeyFactory.CreateKey(LPubInfo);
+        end
+        else
+        begin
+          if not Supports(LPrivSpec, IECPrivateKeyParameters, LECPriv) then
+            raise EPemGenerationCryptoLibException.Create('EC private key expected');
+          LPubSpec := TECKeyPairGenerator.GetCorrespondingPublicKey(LECPriv);
+        end;
+
+        Result := TValue.From<IAsymmetricCipherKeyPair>(
+          TAsymmetricCipherKeyPair.Create(LPubSpec, LPrivSpec));
+        Exit;
+      end;
+
+      if LType = 'ENCRYPTED' then
+        raise ENotSupportedCryptoLibException.Create(SEncryptedPrivateKeyNotSupported);
+
+      if LType = '' then
+      begin
+        Result := TValue.From<IAsymmetricKeyParameter>(
+          TPrivateKeyFactory.CreateKey(TPrivateKeyInfo.GetInstance(LSeq)));
+        Exit;
+      end;
+
+      raise EArgumentCryptoLibException.CreateResFmt(@SUnknownKeyType, [LType]);
+    except
+      on EIOCryptoLibException do
+        raise;
+      on E: Exception do
+        raise EPemGenerationCryptoLibException.CreateResFmt(@SProblemCreatingPrivateKey, [LType, E.Message]);
+    end;
+  finally
+    LFields.Free;
+  end;
+end;
+
+end.

+ 67 - 0
CryptoLib/src/OpenSsl/ClpOpenSslPemWriter.pas

@@ -0,0 +1,67 @@
+{ *********************************************************************************** }
+{ *                              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 ClpOpenSslPemWriter;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  Classes,
+  Rtti,
+  SysUtils,
+  ClpIOpenSslPemWriter,
+  ClpIPemObjects,
+  ClpPemWriter,
+  ClpOpenSslMiscPemGenerator,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// General-purpose writer for OpenSSL PEM objects. Inherits from
+  /// TPemWriter; accepts any supported object and writes it as PEM by wrapping
+  /// it in TMiscPemGenerator.
+  /// </summary>
+  TOpenSslPemWriter = class(TPemWriter, IOpenSslPemWriter)
+  public
+    constructor Create(const AWriter: TStream);
+    procedure WriteObject(const AObj: TValue); overload;
+  end;
+
+implementation
+
+{ TOpenSslPemWriter }
+
+constructor TOpenSslPemWriter.Create(const AWriter: TStream);
+begin
+  inherited Create(AWriter);
+end;
+
+procedure TOpenSslPemWriter.WriteObject(const AObj: TValue);
+begin
+  try
+    inherited WriteObject(TOpenSslMiscPemGenerator.Create(AObj) as IPemObjectGenerator);
+  except
+    on E: EPemGenerationCryptoLibException do
+    begin
+      raise;
+    end;
+  end;
+end;
+
+end.

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.lpk


+ 5 - 1
CryptoLib/src/Packages/FPC/CryptoLib4PascalPackage.pas

@@ -156,7 +156,11 @@ uses
   ClpDeltaCertificateTool, ClpX509Attribute, ClpX509ExtensionBase, 
   ClpX509ExtensionUtilities, ClpDevRandomReader, ClpBaseRandomProvider, 
   ClpCmsAsn1Objects, ClpCmsObjectIdentifiers, ClpCmsParsers, 
-  ClpICmsAsn1Objects, ClpICmsParsers;
+  ClpICmsAsn1Objects, ClpICmsParsers, ClpOpenSslMiscPemGenerator, 
+  ClpOpenSslPemReader, ClpOpenSslPemWriter, ClpIOpenSslPemReader, 
+  ClpIOpenSslPemWriter, ClpPkcsDHAsn1Objects, ClpIPkcsDHAsn1Objects, 
+  ClpIPemParser, ClpIPemReader, ClpIPemWriter, ClpPemParser, ClpPemReader, 
+  ClpPemWriter;
 
 implementation
 

+ 0 - 603
CryptoLib/src/Pem/ClpPemObjects.pas

@@ -31,8 +31,6 @@ uses
   ClpCryptoLibTypes,
   ClpEncoders,
   ClpStringUtilities,
-  ClpStreamUtilities,
-  ClpConverters,
   ClpAsn1Objects,
   ClpIAsn1Objects,
   ClpCollectionUtilities;
@@ -87,85 +85,6 @@ type
     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;
-
-  /// <summary>
-  /// PEM parser implementation.
-  /// </summary>
-  TPemParser = class sealed(TInterfacedObject, IPemParser)
-  strict private
-    FHeader1: String;
-    FHeader2: String;
-    FFooter1: String;
-    FFooter2: String;
-
-    function ReadLine(const AInStream: TStream): String;
-
-  public
-    constructor Create(const AType: String);
-
-    function ReadPemObject(const AInStream: TStream): IAsn1Sequence;
-  end;
-
 implementation
 
 { TPemHeader }
@@ -268,526 +187,4 @@ 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>;
-  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;
-  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 := TStringUtilities.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 := TStringUtilities.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 := TStringUtilities.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 := TBase64Encoder.Decode(LPayload);
-
-    // Convert headers list to array
-    LHeadersArray := TCollectionUtilities.ToArray<IPemHeader>(LHeaders);
-
-    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 := TBase64Encoder.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;
-
-{ TPemParser }
-
-constructor TPemParser.Create(const AType: String);
-begin
-  Inherited Create();
-  FHeader1 := '-----BEGIN ' + AType + '-----';
-  FHeader2 := '-----BEGIN X509 ' + AType + '-----';
-  FFooter1 := '-----END ' + AType + '-----';
-  FFooter2 := '-----END X509 ' + AType + '-----';
-end;
-
-function TPemParser.ReadLine(const AInStream: TStream): String;
-var
-  LC: Int32;
-  LBuilder: TStringBuilder;
-begin
-  LBuilder := TStringBuilder.Create;
-  try
-    repeat
-      while True do
-      begin
-        // ReadByte returns 0..255, or -1 on EOF
-        LC := AInStream.ReadByte;
-
-        // EOF
-        if LC < 0 then
-          Break;
-
-        // Stop on CR or LF - terminate on either one
-        if (LC = Ord(#13)) or (LC = Ord(#10)) then
-          Break;
-
-        LBuilder.Append(Char(LC));
-      end;
-    until (LC < 0) or (LBuilder.Length > 0);
-
-    if LC < 0 then
-      Result := ''
-    else
-      Result := LBuilder.ToString;
-  finally
-    LBuilder.Free;
-  end;
-end;
-
-function TPemParser.ReadPemObject(const AInStream: TStream): IAsn1Sequence;
-var
-  LLine: String;
-  LPemBuf: TStringBuilder;
-  LDecoded: TCryptoLibByteArray;
-  LAsn1Obj: IAsn1Object;
-begin
-  Result := nil;
-  LPemBuf := TStringBuilder.Create();
-  try
-    // Skip until we find the header
-    while True do
-    begin
-      LLine := ReadLine(AInStream);
-      if LLine = '' then
-      begin
-        Exit;
-      end;
-
-      if TStringUtilities.StartsWith(LLine, FHeader1) or TStringUtilities.StartsWith(LLine, FHeader2) then
-        Break;
-    end;
-
-    // Read until we find the footer
-    while True do
-    begin
-      LLine := ReadLine(AInStream);
-      if LLine = '' then
-      begin
-        Exit;
-      end;
-
-      if TStringUtilities.StartsWith(LLine, FFooter1) or TStringUtilities.StartsWith(LLine, FFooter2) then
-        Break;
-
-      LPemBuf.Append(LLine);
-    end;
-
-    if LPemBuf.Length > 0 then
-    begin
-      LDecoded := TBase64Encoder.Decode(LPemBuf.ToString());
-      LAsn1Obj := TAsn1Object.FromByteArray(LDecoded);
-
-      if not Supports(LAsn1Obj, IAsn1Sequence, Result) then
-        raise EIOCryptoLibException.Create('malformed PEM data encountered');
-    end;
-  finally
-    LPemBuf.Free();
-  end;
-end;
-
 end.

+ 154 - 0
CryptoLib/src/Pem/ClpPemParser.pas

@@ -0,0 +1,154 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPemParser;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  ClpIPemParser,
+  ClpEncoders,
+  ClpStreamUtilities,
+  ClpStringUtilities,
+  ClpIAsn1Core,
+  ClpAsn1Core,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes;
+
+type
+  /// <summary>
+  /// PEM parser implementation.
+  /// </summary>
+  TPemParser = class sealed(TInterfacedObject, IPemParser)
+  strict private
+    FHeader1: String;
+    FHeader2: String;
+    FFooter1: String;
+    FFooter2: String;
+
+    function ReadLine(const AInStream: TStream): String;
+
+  public
+    constructor Create(const AType: String);
+
+    function ReadPemObject(const AInStream: TStream): IAsn1Sequence;
+  end;
+
+implementation
+
+{ TPemParser }
+
+constructor TPemParser.Create(const AType: String);
+begin
+  Inherited Create();
+  FHeader1 := '-----BEGIN ' + AType + '-----';
+  FHeader2 := '-----BEGIN X509 ' + AType + '-----';
+  FFooter1 := '-----END ' + AType + '-----';
+  FFooter2 := '-----END X509 ' + AType + '-----';
+end;
+
+function TPemParser.ReadLine(const AInStream: TStream): String;
+var
+  LC: Int32;
+  LBuilder: TStringBuilder;
+begin
+  LBuilder := TStringBuilder.Create;
+  try
+    repeat
+      while True do
+      begin
+        // ReadByte returns 0..255, or -1 on EOF
+        LC := AInStream.ReadByte;
+
+        // EOF
+        if LC < 0 then
+          Break;
+
+        // Stop on CR or LF - terminate on either one
+        if (LC = Ord(#13)) or (LC = Ord(#10)) then
+          Break;
+
+        LBuilder.Append(Char(LC));
+      end;
+    until (LC < 0) or (LBuilder.Length > 0);
+
+    if LC < 0 then
+      Result := ''
+    else
+      Result := LBuilder.ToString;
+  finally
+    LBuilder.Free;
+  end;
+end;
+
+function TPemParser.ReadPemObject(const AInStream: TStream): IAsn1Sequence;
+var
+  LLine: String;
+  LPemBuf: TStringBuilder;
+  LDecoded: TCryptoLibByteArray;
+  LAsn1Obj: IAsn1Object;
+begin
+  Result := nil;
+  LPemBuf := TStringBuilder.Create();
+  try
+    // Skip until we find the header
+    while True do
+    begin
+      LLine := ReadLine(AInStream);
+      if LLine = '' then
+      begin
+        Exit;
+      end;
+
+      if TStringUtilities.StartsWith(LLine, FHeader1) or TStringUtilities.StartsWith(LLine, FHeader2) then
+        Break;
+    end;
+
+    // Read until we find the footer
+    while True do
+    begin
+      LLine := ReadLine(AInStream);
+      if LLine = '' then
+      begin
+        Exit;
+      end;
+
+      if TStringUtilities.StartsWith(LLine, FFooter1) or TStringUtilities.StartsWith(LLine, FFooter2) then
+        Break;
+
+      LPemBuf.Append(LLine);
+    end;
+
+    if LPemBuf.Length > 0 then
+    begin
+      LDecoded := TBase64Encoder.Decode(LPemBuf.ToString());
+      LAsn1Obj := TAsn1Object.FromByteArray(LDecoded);
+
+      if not Supports(LAsn1Obj, IAsn1Sequence, Result) then
+        raise EIOCryptoLibException.Create('malformed PEM data encountered');
+    end;
+  finally
+    LPemBuf.Free();
+  end;
+end;
+
+end.

+ 368 - 0
CryptoLib/src/Pem/ClpPemReader.pas

@@ -0,0 +1,368 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPemReader;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  Generics.Collections,
+  ClpIPemReader,
+  ClpIPemObjects,
+  ClpPemObjects,
+  ClpCryptoLibTypes,
+  ClpEncoders,
+  ClpStringUtilities,
+  ClpStreamUtilities,
+  ClpAsn1Objects,
+  ClpIAsn1Objects,
+  ClpCollectionUtilities;
+
+type
+  /// <summary>
+  /// PEM reader implementation.
+  /// </summary>
+  TPemReader = class(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;
+
+implementation
+
+{ 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>;
+  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;
+  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 := TStringUtilities.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 := TStringUtilities.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 := TStringUtilities.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 := TBase64Encoder.Decode(LPayload);
+
+    // Convert headers list to array
+    LHeadersArray := TCollectionUtilities.ToArray<IPemHeader>(LHeaders);
+
+    Result := TPemObject.Create(LType, LHeadersArray, LDecodedContent);
+  finally
+    LHeaders.Free();
+  end;
+end;
+
+end.

+ 198 - 0
CryptoLib/src/Pem/ClpPemWriter.pas

@@ -0,0 +1,198 @@
+{ *********************************************************************************** }
+{ *                              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 ClpPemWriter;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Classes,
+  ClpIPemWriter,
+  ClpIPemObjects,
+  ClpCryptoLibTypes,
+  ClpEncoders,
+  ClpStringUtilities,
+  ClpConverters;
+
+type
+  /// <summary>
+  /// PEM writer implementation.
+  /// </summary>
+  TPemWriter = class(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); overload;
+
+    property Writer: TStream read GetWriter;
+  end;
+
+implementation
+
+{ 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 := TBase64Encoder.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.

+ 3 - 3
CryptoLib/src/Pkcs/ClpPkcs10CertificationRequest.pas

@@ -307,7 +307,7 @@ constructor TPkcs10CertificationRequest.Create(const ASignatureAlgorithm: String
   const AAttributes: IAsn1Set; const ASigningKey: IAsymmetricKeyParameter);
 begin
   inherited Create();
-  Init(TAsn1SignatureFactory.Create(ASignatureAlgorithm, ASigningKey), ASubject, APublicKey, AAttributes);
+  Init(TAsn1SignatureFactory.Create(ASignatureAlgorithm, ASigningKey) as ISignatureFactory, ASubject, APublicKey, AAttributes);
 end;
 
 constructor TPkcs10CertificationRequest.Create(const ASignatureAlgorithm: String;
@@ -315,7 +315,7 @@ constructor TPkcs10CertificationRequest.Create(const ASignatureAlgorithm: String
   const AAttributes: IAsn1Set; const ASigningKey: IAsymmetricKeyParameter);
 begin
   inherited Create();
-  Init(TAsn1SignatureFactory.Create(ASignatureAlgorithm, ASigningKey), ASubject, APubInfo, AAttributes);
+  Init(TAsn1SignatureFactory.Create(ASignatureAlgorithm, ASigningKey) as ISignatureFactory, ASubject, APubInfo, AAttributes);
 end;
 
 constructor TPkcs10CertificationRequest.Create(const ASignatureFactory: ISignatureFactory;
@@ -379,7 +379,7 @@ end;
 
 function TPkcs10CertificationRequest.Verify(const APublicKey: IAsymmetricKeyParameter): Boolean;
 begin
-  Result := Verify(TAsn1VerifierFactoryProvider.Create(APublicKey));
+  Result := Verify(TAsn1VerifierFactoryProvider.Create(APublicKey) as IVerifierFactoryProvider);
 end;
 
 function TPkcs10CertificationRequest.Verify(const AVerifierProvider: IVerifierFactoryProvider): Boolean;

+ 2 - 2
CryptoLib/src/X509/ClpX509AttrCertParser.pas

@@ -33,8 +33,8 @@ uses
   ClpAsn1Objects,
   ClpIAsn1Objects,
   ClpIAsn1Core,
-  ClpPemObjects,
-  ClpIPemObjects,
+  ClpIPemParser,
+  ClpPemParser,
   ClpAsn1Streams,
   ClpAsn1Utilities,
   ClpCryptoLibTypes,

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

@@ -32,8 +32,8 @@ uses
   ClpX509Asn1Objects,
   ClpAsn1Objects,
   ClpIAsn1Objects,
-  ClpPemObjects,
-  ClpIPemObjects,
+  ClpIPemParser,
+  ClpPemParser,
   ClpAsn1Streams,
   ClpAsn1Utilities,
   ClpCryptoLibTypes,

+ 2 - 2
CryptoLib/src/X509/ClpX509CrlParser.pas

@@ -32,8 +32,8 @@ uses
   ClpIX509Asn1Objects,
   ClpX509Asn1Objects,
   ClpX509Crl,
-  ClpPemObjects,
-  ClpIPemObjects,
+  ClpIPemParser,
+  ClpPemParser,
   ClpAsn1Streams,
   ClpAsn1Utilities,
   ClpPkcsAsn1Objects,

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.