Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e3bbbc6
Challenge Solution 20.10
alnakschbandi Oct 20, 2025
64eacb8
reverting unnecessary changes
alnakschbandi Oct 27, 2025
4103a31
merging main
alnakschbandi Nov 4, 2025
be86a96
Merge the challenge into the new branch
alnakschbandi Feb 16, 2026
c4320f8
Adding basic tests for Crl Distribution Points and Freshest Crl, Addi…
alnakschbandi Feb 16, 2026
b025d99
Changing the BasicCRLDistributionPointsTests to test the newly added …
alnakschbandi Mar 16, 2026
d2d678b
Adding two new tests + modifying the TlsServerAuthVerifierAdapterDock…
alnakschbandi Mar 23, 2026
840dd56
modifying TlsServerAuthVerifierAdapterDocker.java again with verify_r…
alnakschbandi Mar 23, 2026
6637314
Adding the crldp extension to the initial certs for feature extraction
alnakschbandi Mar 23, 2026
bf6acde
Broken build: Trying to extract private keys and CAcerts in order to …
alnakschbandi Mar 29, 2026
44f6f89
working version: extracting keys and generating CRLs from Java
alnakschbandi Apr 11, 2026
4df8cc9
Cleaning up code, adding CrlUtils static class so the code is reusable
alnakschbandi Apr 11, 2026
1d9f8eb
Cleaning up temp files after generating crls
alnakschbandi Apr 11, 2026
6bd63d4
Using new unique identifier, restructing the CRL generation
alnakschbandi Apr 19, 2026
d25ae6f
Adding test cases and fixing old ones
alnakschbandi Apr 24, 2026
0d6db12
Adding more tests using CRLConfig
alnakschbandi May 5, 2026
bc1bc08
Tests cleanup
alnakschbandi May 28, 2026
761773d
Test fixes
alnakschbandi Jun 8, 2026
47fc529
Some cleanup and comments
alnakschbandi Jun 8, 2026
34c5e1d
Readding tests to rfc5280.json
alnakschbandi Jun 16, 2026
8ae8797
Removing unused imports
alnakschbandi Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
resources/*.pem
*.crl
resources/out/
X509-Testsuite/resources/*.pem
test.sh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class TestConfig extends TLSDelegateConfig {
@Parameter(
names = "-verifierAdapterType",
description = "Whether to test TLS servers or TLS clients.")
private VerifierAdapterType verifierAdapterType = VerifierAdapterType.TLS_CLIENT_AUTH;
private VerifierAdapterType verifierAdapterType = VerifierAdapterType.TLS_SERVER_AUTH;

@JsonProperty("docker")
@Parameter(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
package de.rub.nds.x509anvil.framework.crls;

import de.rub.nds.protocol.crypto.signature.RsaPkcs1SignatureComputations;
import de.rub.nds.x509anvil.framework.x509.config.X509Util;
import de.rub.nds.x509attacker.config.CrlConfig;
import de.rub.nds.x509attacker.config.X509CertificateConfig;
import de.rub.nds.x509attacker.x509.model.X509Certificate;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CRL;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.util.*;

public class CrlUtils {
private static final String outPath = new File("resources").getAbsolutePath() + "/out/";
private static final Object CRL_LOCK = new Object();

public static String getUniqueID() {
return System.currentTimeMillis() + "_" + UUID.randomUUID();
}

public static void GenerateCRLs(X509CertificateConfig entityConfig, List<X509Certificate> certificateChain) {
String uniqueID = entityConfig.getCrlUniqueID();
// Define a directory for the current test case
String handshakeDirectory = outPath + "certs_for_crls/" + uniqueID;
// Iterate through all CRLs for this test
for (int i = 0; i < entityConfig.getCrlConfigs().size(); i++) {
CrlConfig crlConfig = entityConfig.getCrlConfigs().get(i);
String uniqueIDToUse = uniqueID + crlConfig.getCrlNameSuffix();
// Define a unique name for the current CRL
String crlDirectory = outPath + "certs_for_crls/" + uniqueIDToUse;
X509Util.exportCertificates(certificateChain, crlDirectory);
// For the first CRL of the test, create the necessary file for the CA config file
if (i == 0) {
try {
Files.write(Path.of(handshakeDirectory + "/crlnumber"), "00".getBytes());
Files.write(Path.of(handshakeDirectory + "/index.txt"), "".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
System.out.println("Generating CRLs for " + uniqueIDToUse);

IdpConfig idpConfig = new IdpConfig();
if (crlConfig.getOnlySomeReasons() == null) {
idpConfig = null;
} else {
idpConfig.onlySomeReasons = crlConfig.getOnlySomeReasons();
}
// use the CRL_LOCK to prevent threads from competing
synchronized (CRL_LOCK) {
writeCnf(handshakeDirectory + "/index.txt", handshakeDirectory + "/crlnumber", handshakeDirectory + "/ca.cnf", idpConfig);


if (crlConfig.isRootAsIssuer()) {
X509Certificate rootCert = certificateChain.getFirst();
RsaPkcs1SignatureComputations rootCertSignatureComputations = (RsaPkcs1SignatureComputations) rootCert.getSignatureComputations();
generateCRLKeyfile(rootCertSignatureComputations.getModulus().getValue(), rootCertSignatureComputations.getPrivateKey().getValue(), uniqueIDToUse);
generateCrl(outPath + "../crls/" + uniqueIDToUse + ".crl", handshakeDirectory + "/ca.cnf", crlDirectory + "/crl-key.pem", crlDirectory + "/root_cert.pem", crlConfig.isPemInsteadOfDer(), crlConfig.isRevokedCert(), crlDirectory + "/leaf_cert.pem");

} else {
X509Certificate leafCert = certificateChain.getLast();
RsaPkcs1SignatureComputations leafCertSignatureComputations = (RsaPkcs1SignatureComputations) leafCert.getSignatureComputations();
generateCRLKeyfile(leafCertSignatureComputations.getModulus().getValue(), leafCertSignatureComputations.getPrivateKey().getValue(), uniqueIDToUse);
generateCrl(outPath + "../crls/" + uniqueIDToUse + ".crl", handshakeDirectory + "/ca.cnf", crlDirectory + "/crl-key.pem", getHighestInterCert(handshakeDirectory), crlConfig.isPemInsteadOfDer(), crlConfig.isRevokedCert(), crlDirectory + "/leaf_cert.pem");

}
}


}

}


public static String getHighestInterCert(String directory) {
try {
return Files.list(Paths.get(directory))
.filter(p -> p.getFileName().toString().matches("inter_cert_\\d+\\.pem"))
.max(Comparator.comparingInt(p ->
Integer.parseInt(p.getFileName().toString().replace("inter_cert_", "").replace(".pem", ""))))
.map(Path::toString)
.orElseThrow(() -> new RuntimeException("No inter_cert_x.pem found in " + directory));
} catch (Exception e) {
return directory + "/root_cert.pem";
}
}

private static void generateCrl(String outputFile, String cnfPath, String keyPath, String issuerCertPath, boolean pemInsteadOfDer, boolean revoked, String entityCertPath) {

if (revoked) {
// revoke the certificate before generating the CRL
runCommand("openssl", "ca",
"-config", cnfPath,
"-revoke", entityCertPath,
"-keyfile", keyPath,
"-cert", issuerCertPath,
"-batch");
}
// Generate the CRL
runCommand("openssl", "ca",
"-config", cnfPath,
"-gencrl",
"-keyfile", keyPath,
"-cert", issuerCertPath,
"-out", outputFile,
"-crldays", "30",
"-batch");


if (!pemInsteadOfDer) {
// Convert from PEM to DER
runCommand("openssl", "crl",
"-in", outputFile,
"-inform", "PEM",
"-outform", "DER",
"-out", outputFile);
}

}

private static void runCommand(String... cmd) {
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process p = null;
try {
p = pb.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
String output = null;
try {
output = new String(p.getInputStream().readAllBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
int exitCode = 0;
try {
exitCode = p.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (exitCode != 0) {
throw new RuntimeException("Command failed: " + String.join(" ", cmd) + "\n" + output);
}
}


public static void writeCnf(String indexPath, String crlNumberPath, String cnfPath, IdpConfig idp) {

StringBuilder cnf = new StringBuilder();
cnf.append("[ca]\n")
.append("default_ca = CA_default\n\n")
.append("[CA_default]\n")
.append("database = ").append(indexPath).append("\n")
.append("crlnumber = ").append(crlNumberPath).append("\n")
.append("default_md = sha256\n")
.append("default_crl_days = 30\n")
.append("crl_extensions = crl_ext\n\n")
.append("[crl_ext]\n")
.append("authorityKeyIdentifier = keyid:always\n");
if (idp != null) {
cnf.append("issuingDistributionPoint = ")
.append("critical, ")
.append("@idp_section\n\n");

cnf.append("[idp_section]\n");
if (idp.onlySomeReasons != null) {
cnf.append("onlysomereasons = ").append(String.join(", ", idp.onlySomeReasons)).append("\n");
}
}


try {
Files.write(Paths.get(cnfPath), cnf.toString().getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}


public static void generateCRLKeyfile(BigInteger modulus, BigInteger privateExponent, String folder) {

/*
* Source: https://di-mgt.com.au/rsa_factorize_n.html
* Input: N, e, d.
* Output: p and q where pq=N.
*
* [Initialize] Set k←de−1.
* [Try a random g] Choose g at random from {2,…,N−1} and set t←k.
* [Next t] If t is divisible by 2, set t←t/2 and x←g^t mod N. Otherwise go to step 2.
* [Finished?] If x>1 and y=gcd(x−1,N)>1 then set p←y and q←N/y, output (p,q) and terminate the algorithm. Otherwise go to step 3.
* */
BigInteger publicExponent = BigInteger.valueOf(65537);

BigInteger k = privateExponent.multiply(publicExponent).subtract(BigInteger.ONE);
BigInteger p = null, q = null;
Random rng = new Random();
BigInteger g;

outer:
while (true) {
do {
g = new BigInteger(modulus.bitLength(), rng);
} while (g.compareTo(BigInteger.valueOf(2)) < 0 || g.compareTo(modulus) >= 0);
BigInteger t = k;
while (!t.testBit(0)) {
t = t.shiftRight(1);
BigInteger x = g.modPow(t, modulus);
if (x.compareTo(BigInteger.ONE) > 0) {
BigInteger y = x.subtract(BigInteger.ONE).gcd(modulus);
if (y.compareTo(BigInteger.ONE) > 0) {
p = y;
q = modulus.divide(p);
break outer;
}
}
}
}

BigInteger dp = privateExponent.mod(p.subtract(BigInteger.ONE));
BigInteger dq = privateExponent.mod(q.subtract(BigInteger.ONE));
BigInteger qInv = q.modInverse(p);
RSAPrivateCrtKeySpec spec =
new RSAPrivateCrtKeySpec(
modulus, publicExponent, privateExponent, p, q, dp, dq, qInv);
PrivateKey privateKey = null;
try {
privateKey = KeyFactory.getInstance("RSA").generatePrivate(spec);
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] encoded = privateKey.getEncoded();
//System.out.println("\n\n=========\n" + "Creating Private Key file for CRL:\nModulus is: " + modulus + "\nprivate exponent is: " + privateExponent + "\nfolder is: " + folder);
String pem =
"-----BEGIN PRIVATE KEY-----\n"
+ Base64.getMimeEncoder(64, new byte[]{'\n'}).encodeToString(encoded)
+ "\n-----END PRIVATE KEY-----\n";
File RESOURCES_PATH = new File("resources");
try (FileWriter fw =
new FileWriter(
RESOURCES_PATH.getAbsolutePath()
+ "/out/certs_for_crls/" + folder + "/crl-key.pem")) {
fw.write(pem);
} catch (IOException e) {
System.out.println(e.getMessage());
}

}

public static void clean() {
emptyDirectory(outPath + "../crls/");
emptyDirectory(outPath + "certs_for_crls/");
}

private static void emptyDirectory(String directory) {
try {
Files.walk(Paths.get(directory))
.sorted(Comparator.reverseOrder())
.filter(p -> !p.equals(Paths.get(directory)))
.forEach(p -> {
try {
Files.delete(p);
} catch (Exception e) {
throw new RuntimeException("Failed to delete " + p, e);
}
});
} catch (IOException e) {
throw new RuntimeException(e);
}
}

public static class IdpConfig {
public String fullName;
public Set<String> onlySomeReasons;
public Boolean onlyUser;
public Boolean onlyCA;
public Boolean onlyAA;
public Boolean indirectCRL;
public boolean critical = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,29 @@
import de.rub.nds.x509anvil.framework.x509.generator.CertificateGeneratorException;
import de.rub.nds.x509anvil.framework.x509.generator.X509CertificateChainGenerator;
import de.rub.nds.x509attacker.x509.model.X509Certificate;

import java.util.List;

public abstract class SimpleProbe implements Probe {


@Override
public ProbeResult execute() throws ProbeException {
// dual config initialization here
X509CertificateChainConfig config = prepareConfig();
X509CertificateChainGenerator certificateChainGenerator =
new X509CertificateChainGenerator(config);
X509CertificateChainGenerator certificateChainGenerator = new X509CertificateChainGenerator(config);
try {
certificateChainGenerator.generateCertificateChain();
} catch (CertificateGeneratorException e) {
throw new ProbeException("Unable to generate certificate from config", e);
}
List<X509Certificate> certificateChain =
certificateChainGenerator.retrieveCertificateChain();
List<X509Certificate> certificateChain = certificateChainGenerator.retrieveCertificateChain();


TestConfig testConfig = ContextHelper.getTestConfig();
VerifierAdapter verifierAdapter =
VerifierAdapterFactory.getInstance(
testConfig.getVerifierAdapterType(), testConfig.getVerifierAdapterConfig());
VerifierAdapter verifierAdapter = VerifierAdapterFactory.getInstance(testConfig.getVerifierAdapterType(), testConfig.getVerifierAdapterConfig());
try {
VerifierResult verifierResult =
verifierAdapter.invokeVerifier(
config.getEntityCertificateConfig(), certificateChain);
VerifierResult verifierResult = verifierAdapter.invokeVerifier(config.getEntityCertificateConfig(), certificateChain);
return createResult(verifierResult);
} catch (VerifierException e) {
throw new ProbeException("Invoking the verifier for probe failed", e);
Expand All @@ -55,3 +52,5 @@ public ProbeResult execute() throws ProbeException {

protected abstract ProbeResult createResult(VerifierResult verifierResult);
}


Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private static DockerTlsClientInstance spinUpServer(TlsAuthVerifierAdapterConfig
private static String supplementStartCommand(TlsImplementationType tlsImplementationType) {
return (switch (tlsImplementationType) {
case OPENSSL, LIBRESSL ->
"-verify 5 -verify_return_error";
"-verify 5 -verify_return_error -crl_check -crl_download -extended_crl";
case BOTAN -> "--skip-hostname-check";
default -> "";
});
Expand Down
Loading