Quellcode durchsuchen

add X509NameBuilder

Ugochukwu Mmaduekwe vor 3 Tagen
Ursprung
Commit
1f9a9df010

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

@@ -490,6 +490,8 @@ uses
   ClpCustomNamedCurves in '..\..\CryptoLib\src\Crypto\EC\ClpCustomNamedCurves.pas',
   ClpECUtilities in '..\..\CryptoLib\src\Crypto\EC\ClpECUtilities.pas',
   ClpX962NamedCurves in '..\..\CryptoLib\src\Asn1\X9\ClpX962NamedCurves.pas',
+  ClpX509NameBuilder in '..\..\CryptoLib\src\X509\ClpX509NameBuilder.pas',
+  ClpIX509NameBuilder in '..\..\CryptoLib\src\Interfaces\X509\ClpIX509NameBuilder.pas',
   ClpFixedSecureRandom in '..\src\Utils\ClpFixedSecureRandom.pas',
   ClpShortenedDigest in '..\src\Utils\ClpShortenedDigest.pas',
   BlowfishTestVectors in '..\src\Crypto\BlowfishTestVectors.pas',

+ 29 - 5
CryptoLib.Tests/src/Others/CertTests.pas

@@ -72,6 +72,8 @@ uses
   ClpAsn1Objects,
   ClpCryptoLibTypes,
   ClpAsn1Comparers,
+  ClpIX509NameBuilder,
+  ClpX509NameBuilder,
   ClpPkcsObjectIdentifiers,
   ClpPkcsAsn1Objects,
   ClpIPkcsAsn1Objects,
@@ -127,6 +129,7 @@ type
     procedure SetUp; override;
 
   published
+  procedure TestX509NameBuilderMatchesRegular;
     procedure TestCert1;
     procedure TestCert2;
     procedure TestCert3;
@@ -434,11 +437,11 @@ begin
   LAttrs := TDictionary<IDerObjectIdentifier, String>.Create(TAsn1Comparers.OidEqualityComparer);
   LOrd := TList<IDerObjectIdentifier>.Create;
   try
-    LAttrs.Add(TX509Name.C, 'AU');
-    LAttrs.Add(TX509Name.O, 'The Legion of the Bouncy Castle');
-    LAttrs.Add(TX509Name.L, 'Melbourne');
-    LAttrs.Add(TX509Name.ST, 'Victoria');
-    LAttrs.Add(TX509Name.E, 'feedback-crypto@bouncycastle.org');
+    LAttrs.Add(TX509Name.C, 'NG');
+    LAttrs.Add(TX509Name.O, 'CryptoLib4Pascal');
+    LAttrs.Add(TX509Name.L, 'Alausa');
+    LAttrs.Add(TX509Name.ST, 'Lagos');
+    LAttrs.Add(TX509Name.E, 'feedback-crypto@cryptolib4pascal.org');
 
     LOrd.Add(TX509Name.C);
     LOrd.Add(TX509Name.O);
@@ -534,6 +537,27 @@ begin
     SetUpKeys;
 end;
 
+procedure TCertTest.TestX509NameBuilderMatchesRegular;
+var
+  LRegular: IX509Name;
+  LBuilder: IX509NameBuilder;
+  LViaBuilder: IX509Name;
+begin
+  LRegular := CreateX509Name;
+  LBuilder := TX509NameBuilder.Create;
+  LViaBuilder := LBuilder
+    .AddCountry('NG')
+    .AddOrganization('CryptoLib4Pascal')
+    .AddLocality('Alausa')
+    .AddState('Lagos')
+    .AddEmailAddress('[email protected]')
+    .Build();
+  if not LRegular.Equivalent(LViaBuilder, True) then
+    Fail('X509Name from builder did not match regular creation (Equivalent)');
+  if LRegular.ToString <> LViaBuilder.ToString then
+    Fail('X509Name from builder did not match regular creation (ToString)');
+end;
+
 procedure TCertTest.TestCert1;
 begin
   CheckCertificate(1, DecodeBase64(

+ 95 - 0
CryptoLib/src/Interfaces/X509/ClpIX509NameBuilder.pas

@@ -0,0 +1,95 @@
+{ *********************************************************************************** }
+{ *                              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 ClpIX509NameBuilder;
+
+{$I ..\..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  ClpIAsn1Objects,
+  ClpIX509Asn1Objects;
+
+type
+  /// <summary>
+  /// Interface for building X509Name objects with method chaining.
+  /// </summary>
+  IX509NameBuilder = interface(IInterface)
+    ['{F1A2B3C4-D5E6-7890-ABCD-EF1234567890}']
+
+    /// <summary>
+    /// Add an RDN (Relative Distinguished Name) by OID and value.
+    /// </summary>
+    function AddRdn(const AOid: IDerObjectIdentifier; const AValue: String): IX509NameBuilder; overload;
+
+    /// <summary>
+    /// Add an RDN by standard name (e.g., "C", "O", "CN") and value.
+    /// </summary>
+    function AddRdn(const AName: String; const AValue: String): IX509NameBuilder; overload;
+
+    /// <summary>
+    /// Add Country attribute.
+    /// </summary>
+    function AddCountry(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Organization attribute.
+    /// </summary>
+    function AddOrganization(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Organizational Unit attribute.
+    /// </summary>
+    function AddOrganizationalUnit(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Locality attribute.
+    /// </summary>
+    function AddLocality(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add State attribute.
+    /// </summary>
+    function AddState(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Common Name attribute.
+    /// </summary>
+    function AddCommonName(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Email Address attribute.
+    /// </summary>
+    function AddEmailAddress(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Reset the builder, clearing all added RDNs so it can be reused.
+    /// </summary>
+    function Reset(): IX509NameBuilder;
+
+    /// <summary>
+    /// Build and return the X509Name object.
+    /// </summary>
+    function Build(): IX509Name;
+
+  end;
+
+implementation
+
+end.
+

+ 1 - 1
CryptoLib/src/Math/ClpBigInteger.pas

@@ -976,7 +976,7 @@ var
   LNBits2: Int32;
   LM, LNext: UInt32;
 begin
-  LNInts := (UInt32(AN) shr 5) + AStart;
+  LNInts := Int32(UInt32(AN) shr 5) + AStart;
   LNBits := AN and 31;
   LMagEnd := System.Length(AMag) - 1;
   if LNInts <> AStart then

+ 289 - 0
CryptoLib/src/X509/ClpX509NameBuilder.pas

@@ -0,0 +1,289 @@
+{ *********************************************************************************** }
+{ *                              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 ClpX509NameBuilder;
+
+{$I ..\Include\CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  Generics.Collections,
+  ClpAsn1Comparers,
+  ClpIX509NameBuilder,
+  ClpIX509Asn1Objects,
+  ClpX509Asn1Objects,
+  ClpIAsn1Objects,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SUnknownOid = 'Unknown Oid';
+
+type
+  /// <summary>
+  /// Builder class for creating X509Name objects with method chaining.
+  /// </summary>
+  TX509NameBuilder = class sealed(TInterfacedObject, IX509NameBuilder)
+
+  strict private
+  var
+    FOrdering: TList<IDerObjectIdentifier>;
+    FValues: TList<String>;
+
+    function GetOidByName(const AName: String): IDerObjectIdentifier;
+
+  public
+    /// <summary>
+    /// Create a new builder instance.
+    /// </summary>
+    constructor Create;
+    destructor Destroy; override;
+
+    /// <summary>
+    /// Add an RDN (Relative Distinguished Name) by OID and value.
+    /// </summary>
+    function AddRdn(const AOid: IDerObjectIdentifier; const AValue: String): IX509NameBuilder; overload;
+
+    /// <summary>
+    /// Add an RDN by standard name (e.g., "C", "O", "CN") and value.
+    /// </summary>
+    function AddRdn(const AName: String; const AValue: String): IX509NameBuilder; overload;
+
+    /// <summary>
+    /// Add Country attribute.
+    /// </summary>
+    function AddCountry(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Organization attribute.
+    /// </summary>
+    function AddOrganization(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Organizational Unit attribute.
+    /// </summary>
+    function AddOrganizationalUnit(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Locality attribute.
+    /// </summary>
+    function AddLocality(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add State attribute.
+    /// </summary>
+    function AddState(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Common Name attribute.
+    /// </summary>
+    function AddCommonName(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Add Email Address attribute.
+    /// </summary>
+    function AddEmailAddress(const AValue: String): IX509NameBuilder;
+
+    /// <summary>
+    /// Reset the builder, clearing all added RDNs so it can be reused.
+    /// </summary>
+    function Reset(): IX509NameBuilder;
+
+    /// <summary>
+    /// Build and return the X509Name object.
+    /// </summary>
+    function Build(): IX509Name;
+
+  end;
+
+implementation
+
+{ TX509NameBuilder }
+
+constructor TX509NameBuilder.Create;
+begin
+  inherited Create();
+  FOrdering := TList<IDerObjectIdentifier>.Create(TAsn1Comparers.OidComparer);
+  FValues := TList<String>.Create();
+end;
+
+destructor TX509NameBuilder.Destroy;
+begin
+  FOrdering.Free;
+  FValues.Free;
+  inherited Destroy;
+end;
+
+function TX509NameBuilder.AddCountry(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.C, AValue);
+end;
+
+function TX509NameBuilder.AddCommonName(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.CN, AValue);
+end;
+
+function TX509NameBuilder.AddEmailAddress(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.EmailAddress, AValue);
+end;
+
+function TX509NameBuilder.AddLocality(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.L, AValue);
+end;
+
+function TX509NameBuilder.AddOrganization(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.O, AValue);
+end;
+
+function TX509NameBuilder.AddOrganizationalUnit(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.OU, AValue);
+end;
+
+function TX509NameBuilder.AddRdn(const AOid: IDerObjectIdentifier;
+  const AValue: String): IX509NameBuilder;
+var
+  LIdx: Int32;
+begin
+  if AOid = nil then
+    raise EArgumentNilCryptoLibException.Create('oid');
+  if AValue = '' then
+    raise EArgumentCryptoLibException.Create('value cannot be empty');
+
+  LIdx := FOrdering.IndexOf(AOid);
+  if LIdx >= 0 then
+    FValues[LIdx] := AValue
+  else
+  begin
+    FOrdering.Add(AOid);
+    FValues.Add(AValue);
+  end;
+  Result := Self;
+end;
+
+function TX509NameBuilder.AddRdn(const AName: String; const AValue: String): IX509NameBuilder;
+var
+  LOid: IDerObjectIdentifier;
+begin
+  LOid := GetOidByName(AName);
+  if LOid = nil then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SUnknownOid, [AName]);
+  end;
+  Result := AddRdn(LOid, AValue);
+end;
+
+function TX509NameBuilder.AddState(const AValue: String): IX509NameBuilder;
+begin
+  Result := AddRdn(TX509Name.ST, AValue);
+end;
+
+function TX509NameBuilder.Reset(): IX509NameBuilder;
+begin
+  FOrdering.Clear();
+  FValues.Clear();
+  Result := Self;
+end;
+
+function TX509NameBuilder.Build(): IX509Name;
+begin
+  if FOrdering.Count = 0 then
+  begin
+    raise EArgumentCryptoLibException.Create('X509Name must have at least one RDN');
+  end;
+  Result := TX509Name.Create(FOrdering, FValues);
+end;
+
+function TX509NameBuilder.GetOidByName(const AName: String): IDerObjectIdentifier;
+begin
+  Result := nil;
+
+  // Check standard OIDs using case-insensitive comparison
+  if SameText(AName, 'C') or SameText(AName, 'Country') then
+    Result := TX509Name.C
+  else if SameText(AName, 'O') or SameText(AName, 'Organization') then
+    Result := TX509Name.O
+  else if SameText(AName, 'OU') or SameText(AName, 'OrganizationalUnit') then
+    Result := TX509Name.OU
+  else if SameText(AName, 'L') or SameText(AName, 'Locality') then
+    Result := TX509Name.L
+  else if SameText(AName, 'ST') or SameText(AName, 'State') or SameText(AName, 'SP') or
+    SameText(AName, 'StateProvince') then
+    Result := TX509Name.ST
+  else if SameText(AName, 'CN') or SameText(AName, 'CommonName') then
+    Result := TX509Name.CN
+  else if SameText(AName, 'E') or SameText(AName, 'Email') or SameText(AName, 'EmailAddress') then
+    Result := TX509Name.EmailAddress
+  else if SameText(AName, 'Street') then
+    Result := TX509Name.Street
+  else if SameText(AName, 'SerialNumber') then
+    Result := TX509Name.SerialNumber
+  else if SameText(AName, 'Surname') then
+    Result := TX509Name.Surname
+  else if SameText(AName, 'GivenName') then
+    Result := TX509Name.GivenName
+  else if SameText(AName, 'Initials') then
+    Result := TX509Name.Initials
+  else if SameText(AName, 'Generation') then
+    Result := TX509Name.Generation
+  else if SameText(AName, 'UniqueIdentifier') or SameText(AName, 'UID') then
+    Result := TX509Name.UniqueIdentifier
+  else if SameText(AName, 'Description') then
+    Result := TX509Name.Description
+  else if SameText(AName, 'BusinessCategory') then
+    Result := TX509Name.BusinessCategory
+  else if SameText(AName, 'PostalCode') then
+    Result := TX509Name.PostalCode
+  else if SameText(AName, 'DnQualifier') then
+    Result := TX509Name.DnQualifier
+  else if SameText(AName, 'Pseudonym') then
+    Result := TX509Name.Pseudonym
+  else if SameText(AName, 'Role') then
+    Result := TX509Name.Role
+  else if SameText(AName, 'DateOfBirth') then
+    Result := TX509Name.DateOfBirth
+  else if SameText(AName, 'PlaceOfBirth') then
+    Result := TX509Name.PlaceOfBirth
+  else if SameText(AName, 'Gender') then
+    Result := TX509Name.Gender
+  else if SameText(AName, 'CountryOfCitizenship') then
+    Result := TX509Name.CountryOfCitizenship
+  else if SameText(AName, 'CountryOfResidence') then
+    Result := TX509Name.CountryOfResidence
+  else if SameText(AName, 'NameAtBirth') then
+    Result := TX509Name.NameAtBirth
+  else if SameText(AName, 'PostalAddress') then
+    Result := TX509Name.PostalAddress
+  else if SameText(AName, 'DmdName') then
+    Result := TX509Name.DmdName
+  else if SameText(AName, 'TelephoneNumber') then
+    Result := TX509Name.TelephoneNumber
+  else if SameText(AName, 'OrganizationIdentifier') then
+    Result := TX509Name.OrganizationIdentifier
+  else if SameText(AName, 'Name') then
+    Result := TX509Name.Name
+  else if SameText(AName, 'DC') or SameText(AName, 'DomainComponent') then
+    Result := TX509Name.DC;
+end;
+
+end.
+

+ 1 - 1
CryptoLib/src/X509/ClpX509V2AttributeCertificate.pas

@@ -368,7 +368,7 @@ begin
     Result := False;
     Exit;
   end;
-  if Self = (AOther as TX509V2AttributeCertificate) then
+  if (Self as IX509V2AttributeCertificate) = AOther then
   begin
     Result := True;
     Exit;