Skip to content

Commit bdd7a0a

Browse files
committed
Add PKCS#10 CSR generation with ECDSA and EdDSA support
- Add AlgorithmIdentifier, SubjectPublicKeyInfo, X500Name ASN.1 types - Implement fluent X500NameBuilder for constructing distinguished names - Add unified CreateFromPublicKey factory for SubjectPublicKeyInfo supporting ECDSA and Ed25519 key types with auto-detection - Create inheritance-based CSR builder hierarchy: - TPkcs10CertificationRequestBuilderBase (shared SetSubject, CreateCSR) - TECDSACertificationRequestBuilder (digest in constructor) - TEdDSACertificationRequestBuilder (Ed25519, extensible for Ed448) - Use class var dictionary for ECDSA digest-to-OID mapping with TX9ObjectIdentifiers constants - Support DER and PEM encoding output - Add comprehensive unit tests
1 parent 8b0b175 commit bdd7a0a

17 files changed

Lines changed: 2542 additions & 4 deletions

CryptoLib.Samples/Delphi.Samples/UsageSamples.dpr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,21 @@ uses
365365
ClpECCompUtilities in '..\..\CryptoLib\src\Math\EC\ClpECCompUtilities.pas',
366366
ClpIKMac in '..\..\CryptoLib\src\Interfaces\ClpIKMac.pas',
367367
ClpKMac in '..\..\CryptoLib\src\Crypto\Macs\ClpKMac.pas',
368+
ClpAlgorithmIdentifier in '..\..\CryptoLib\src\Asn1\X509\ClpAlgorithmIdentifier.pas',
369+
ClpIAlgorithmIdentifier in '..\..\CryptoLib\src\Interfaces\ClpIAlgorithmIdentifier.pas',
370+
ClpSubjectPublicKeyInfo in '..\..\CryptoLib\src\Asn1\X509\ClpSubjectPublicKeyInfo.pas',
371+
ClpISubjectPublicKeyInfo in '..\..\CryptoLib\src\Interfaces\ClpISubjectPublicKeyInfo.pas',
372+
ClpX500Name in '..\..\CryptoLib\src\Asn1\X509\ClpX500Name.pas',
373+
ClpIX500Name in '..\..\CryptoLib\src\Interfaces\ClpIX500Name.pas',
374+
ClpPkcs10CertificationRequest in '..\..\CryptoLib\src\Asn1\Pkcs\ClpPkcs10CertificationRequest.pas',
375+
ClpIPkcs10CertificationRequest in '..\..\CryptoLib\src\Interfaces\ClpIPkcs10CertificationRequest.pas',
368376
UsageExamples in '..\src\UsageExamples.pas';
369377

370378
begin
371379
try
372380
{ TODO -oUser -cConsole Main : Insert code here }
381+
TUsageExamples.GenerateCSRWithECDSA;
382+
TUsageExamples.GenerateCSRWithEdDSA;
373383
TUsageExamples.GenerateKeyPairAndSignECDSA;
374384
TUsageExamples.GenerateKeyPairAndSignECSchnorr;
375385
TUsageExamples.GetPublicKeyFromPrivateKey;

CryptoLib.Samples/src/UsageExamples.pas

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,22 @@ interface
8383
ClpEncoders,
8484
// ClpSecNamedCurves,
8585
ClpCustomNamedCurves,
86-
ClpConverters;
86+
ClpConverters,
87+
ClpX500Name,
88+
ClpIX500Name,
89+
ClpPkcs10CertificationRequest,
90+
ClpIPkcs10CertificationRequest,
91+
// Ed25519 support
92+
ClpEd25519KeyPairGenerator,
93+
ClpIEd25519KeyPairGenerator,
94+
ClpEd25519KeyGenerationParameters,
95+
ClpIEd25519KeyGenerationParameters,
96+
ClpIEd25519PublicKeyParameters,
97+
ClpIEd25519PrivateKeyParameters,
98+
ClpEd25519,
99+
ClpIEd25519,
100+
ClpAsn1Objects,
101+
ClpIAsn1Objects;
87102

88103
type
89104

@@ -163,6 +178,16 @@ TUsageExamples = class sealed(TObject)
163178
class procedure BinaryCompatiblePascalCoinECIESDecryptExistingPayloadDemo
164179
(const PrivateKeyInHex, EncryptedMessageInHex,
165180
ACurveName: string); static;
181+
182+
/// <summary>
183+
/// Generate a PKCS#10 Certificate Signing Request using ECDSA
184+
/// </summary>
185+
class procedure GenerateCSRWithECDSA(); static;
186+
187+
/// <summary>
188+
/// Generate a PKCS#10 Certificate Signing Request using Ed25519 (EdDSA)
189+
/// </summary>
190+
class procedure GenerateCSRWithEdDSA(); static;
166191
end;
167192

168193
implementation
@@ -997,4 +1022,161 @@ class procedure TUsageExamples.RecreatePublicKeyFromXAndYCoordByteArray;
9971022
FCurve := GetCurveByName(CurveName);
9981023
end;
9991024

1025+
class procedure TUsageExamples.GenerateCSRWithECDSA;
1026+
1027+
procedure GenerateCSRForCurve(const curveName, digestName: string);
1028+
var
1029+
curve: IX9ECParameters;
1030+
domain: IECDomainParameters;
1031+
generator: IECKeyPairGenerator;
1032+
keygenParams: IECKeyGenerationParameters;
1033+
KeyPair: IAsymmetricCipherKeyPair;
1034+
privParams: IECPrivateKeyParameters;
1035+
pubParams: IECPublicKeyParameters;
1036+
subject: IX500Name;
1037+
builder: IPkcs10CertificationRequestBuilder;
1038+
csr: IPkcs10CertificationRequest;
1039+
digest: IDigest;
1040+
pemString: string;
1041+
customExtOid: IDerObjectIdentifier;
1042+
customExtValue: IDerUtf8String;
1043+
begin
1044+
Writeln('=== Generating CSR with curve: ' + curveName + ' ===' + sLineBreak);
1045+
1046+
// 1. Generate EC Key Pair
1047+
curve := GetCurveByName(curveName);
1048+
domain := TECDomainParameters.Create(curve.Curve, curve.G, curve.N,
1049+
curve.H, curve.GetSeed);
1050+
generator := TECKeyPairGenerator.Create('ECDSA');
1051+
keygenParams := TECKeyGenerationParameters.Create(domain, FRandom);
1052+
generator.Init(keygenParams);
1053+
1054+
KeyPair := generator.GenerateKeyPair();
1055+
privParams := KeyPair.Private as IECPrivateKeyParameters;
1056+
pubParams := KeyPair.Public as IECPublicKeyParameters;
1057+
1058+
Writeln('Generated EC Key Pair using curve: ' + curveName);
1059+
1060+
// 2. Build X.500 Distinguished Name
1061+
subject := TX500NameBuilder.Create
1062+
.AddCommonName('Example CSR - ' + curveName)
1063+
.AddOrganization('CryptoLib4Pascal')
1064+
.AddOrganizationalUnit('Development')
1065+
.AddCountry('US')
1066+
.Build;
1067+
1068+
// 3. Create digest for signing
1069+
digest := TDigestUtilities.GetDigest(digestName);
1070+
Writeln('Using digest algorithm: ' + digest.AlgorithmName);
1071+
1072+
// 4. Create custom extension (example: application-specific OID)
1073+
customExtOid := TDerObjectIdentifier.Create('1.2.3.4.5.6.7.8.9');
1074+
customExtValue := TDerUtf8String.Create('CryptoLib4Pascal Custom Extension');
1075+
1076+
// 5. Build PKCS#10 CSR with extensions
1077+
builder := TECDSACertificationRequestBuilder.Create(digest);
1078+
csr := builder
1079+
.SetSubject(subject)
1080+
.SetPublicKey(pubParams)
1081+
.AddExtension(customExtOid, False, customExtValue) // Custom extension
1082+
.AddSubjectKeyIdentifier() // X.509 Subject Key Identifier
1083+
.Build(privParams);
1084+
1085+
Writeln('CSR built successfully with extensions!');
1086+
1087+
// 6. Get PEM encoded CSR
1088+
pemString := csr.GetPemEncoded;
1089+
1090+
Writeln('PEM Encoded CSR:');
1091+
Writeln(pemString);
1092+
end;
1093+
1094+
const
1095+
MethodName = 'GenerateCSRWithECDSA';
1096+
begin
1097+
Writeln('MethodName is: ' + MethodName + sLineBreak);
1098+
Writeln('Demonstrating PKCS#10 CSR generation with various ECDSA curves' + sLineBreak);
1099+
1100+
// Demonstrate with different curves
1101+
// P-256 (NIST prime256v1) - most commonly used for web certificates
1102+
GenerateCSRForCurve('P-256', 'SHA-256');
1103+
1104+
// P-384 (NIST secp384r1) - stronger security
1105+
GenerateCSRForCurve('P-384', 'SHA-384');
1106+
1107+
// P-521 (NIST secp521r1) - highest NIST security level
1108+
GenerateCSRForCurve('P-521', 'SHA-512');
1109+
1110+
// secp256k1 - used by Bitcoin and Ethereum
1111+
GenerateCSRForCurve('secp256k1', 'SHA-256');
1112+
1113+
Writeln('');
1114+
end;
1115+
1116+
class procedure TUsageExamples.GenerateCSRWithEdDSA;
1117+
var
1118+
ed25519Instance: IEd25519;
1119+
generator: IEd25519KeyPairGenerator;
1120+
keygenParams: IEd25519KeyGenerationParameters;
1121+
KeyPair: IAsymmetricCipherKeyPair;
1122+
privParams: IEd25519PrivateKeyParameters;
1123+
pubParams: IEd25519PublicKeyParameters;
1124+
subject: IX500Name;
1125+
builder: IPkcs10CertificationRequestBuilder;
1126+
csr: IPkcs10CertificationRequest;
1127+
pemString: string;
1128+
customExtOid: IDerObjectIdentifier;
1129+
customExtValue: IDerUtf8String;
1130+
const
1131+
MethodName = 'GenerateCSRWithEdDSA';
1132+
begin
1133+
Writeln('MethodName is: ' + MethodName + sLineBreak);
1134+
Writeln('Demonstrating PKCS#10 CSR generation with Ed25519 (EdDSA)' + sLineBreak);
1135+
1136+
// 1. Generate Ed25519 Key Pair
1137+
ed25519Instance := TEd25519.Create();
1138+
generator := TEd25519KeyPairGenerator.Create(ed25519Instance);
1139+
keygenParams := TEd25519KeyGenerationParameters.Create(FRandom);
1140+
generator.Init(keygenParams);
1141+
1142+
KeyPair := generator.GenerateKeyPair();
1143+
privParams := KeyPair.Private as IEd25519PrivateKeyParameters;
1144+
pubParams := KeyPair.Public as IEd25519PublicKeyParameters;
1145+
1146+
Writeln('Generated Ed25519 Key Pair');
1147+
1148+
// 2. Build X.500 Distinguished Name
1149+
subject := TX500NameBuilder.Create
1150+
.AddCommonName('Example Ed25519 CSR')
1151+
.AddOrganization('CryptoLib4Pascal')
1152+
.AddOrganizationalUnit('Development')
1153+
.AddCountry('US')
1154+
.Build;
1155+
1156+
Writeln('Built X.500 Distinguished Name');
1157+
1158+
// 3. Create custom extension (example: application-specific OID)
1159+
customExtOid := TDerObjectIdentifier.Create('1.2.3.4.5.6.7.8.9');
1160+
customExtValue := TDerUtf8String.Create('CryptoLib4Pascal Ed25519 Extension');
1161+
1162+
// 4. Build PKCS#10 CSR using Ed25519 with extensions
1163+
builder := TEdDSACertificationRequestBuilder.Create;
1164+
csr := builder
1165+
.SetSubject(subject)
1166+
.SetPublicKey(pubParams)
1167+
.AddExtension(customExtOid, False, customExtValue) // Custom extension
1168+
.AddSubjectKeyIdentifier() // X.509 Subject Key Identifier
1169+
.Build(privParams);
1170+
1171+
Writeln('CSR built successfully with Ed25519 and extensions!');
1172+
1173+
// 5. Get PEM encoded CSR
1174+
pemString := csr.GetPemEncoded;
1175+
1176+
Writeln('PEM Encoded CSR:');
1177+
Writeln(pemString);
1178+
1179+
Writeln('');
1180+
end;
1181+
10001182
end.

CryptoLib.Tests/Delphi.Tests/CryptoLib.Tests.dpr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,14 @@ uses
382382
ClpIFixedSecureRandom in '..\src\Utils\ClpIFixedSecureRandom.pas',
383383
ClpIShortenedDigest in '..\src\Utils\ClpIShortenedDigest.pas',
384384
ClpShortenedDigest in '..\src\Utils\ClpShortenedDigest.pas',
385+
ClpAlgorithmIdentifier in '..\..\CryptoLib\src\Asn1\X509\ClpAlgorithmIdentifier.pas',
386+
ClpIAlgorithmIdentifier in '..\..\CryptoLib\src\Interfaces\ClpIAlgorithmIdentifier.pas',
387+
ClpSubjectPublicKeyInfo in '..\..\CryptoLib\src\Asn1\X509\ClpSubjectPublicKeyInfo.pas',
388+
ClpISubjectPublicKeyInfo in '..\..\CryptoLib\src\Interfaces\ClpISubjectPublicKeyInfo.pas',
389+
ClpX500Name in '..\..\CryptoLib\src\Asn1\X509\ClpX500Name.pas',
390+
ClpIX500Name in '..\..\CryptoLib\src\Interfaces\ClpIX500Name.pas',
391+
ClpPkcs10CertificationRequest in '..\..\CryptoLib\src\Asn1\Pkcs\ClpPkcs10CertificationRequest.pas',
392+
ClpIPkcs10CertificationRequest in '..\..\CryptoLib\src\Interfaces\ClpIPkcs10CertificationRequest.pas',
385393
BlowfishTestVectors in '..\src\Crypto\BlowfishTestVectors.pas',
386394
BlockCipherVectorTests in '..\src\Crypto\BlockCipherVectorTests.pas',
387395
AESTestVectors in '..\src\Crypto\AESTestVectors.pas',
@@ -450,6 +458,7 @@ uses
450458
DHTests in '..\src\Crypto\DHTests.pas',
451459
Asn1IntegerTests in '..\src\Asn1\Asn1IntegerTests.pas',
452460
KMacTests in '..\src\Crypto\KMacTests.pas',
461+
Pkcs10CertificationRequestTests in '..\src\Asn1\Pkcs10CertificationRequestTests.pas',
453462
CryptoLibTestBase in '..\src\CryptoLibTestBase.pas';
454463

455464
begin

CryptoLib.Tests/FreePascal.Tests/CryptoLib.lpr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
DHTests,
7575
Asn1IntegerTests,
7676
KMacTests,
77+
Pkcs10CertificationRequestTests,
7778
CryptoLibTestBase,
7879
ClpFixedSecureRandom,
7980
ClpIFixedSecureRandom,

CryptoLib.Tests/FreePascal.Tests/CryptoLibConsole.lpr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
DHTests,
7373
Asn1IntegerTests,
7474
KMacTests,
75+
Pkcs10CertificationRequestTests,
7576
CryptoLibTestBase,
7677
ClpFixedSecureRandom,
7778
ClpIFixedSecureRandom,

0 commit comments

Comments
 (0)