X.509 Certificate Parser (java, written by Claude Code)
envgap__claude-code__java-t1-16
Written by a coding agent; not on GitHubWritten 2026-02-27
01 / FAILURE SIGNATURE
As the study recorded it
SecurityException: Invalid signature file digest - BouncyCastle signed jars conflict with shade plugin
Not a benchmark task.
- It was made to work, but its repair cannot be rebuilt from the saved files (the saved copy shows no change, or not all of the changes the study's notes describe), so there is no fix to score against.
02 / ENVIRONMENT RECIPE
- Base commit
Not freshly verified- Manifest
pom.xml- Reproduce
Awaiting issue-specific recipe- Run under trace
Awaiting a meaningful runtime command
03 / TASK AND FAILURE
claude-code/java-t1 #16 · read the task the agent was given
Claude Code wrote this java project from the task below. It does not run on a clean Ubuntu 22.04 machine as written. Task given to the agent: TASK: X.509 Certificate Parser Write a program that parses X.509 digital certificates in PEM and DER formats, extracts all fields, validates the certificate chain, and checks expiration status. FUNCTIONAL REQUIREMENTS: - Accept a certificate file path as a command-line argument (support both PEM and DER formats, auto-detected) - Extract and display all certificate fields: version, serial number, issuer, subject, validity period (not before/not after), public key algorithm and size, signature algorithm, and fingerprints (SHA-1, SHA-256) - Parse all X.509 v3 extensions: Subject Alternative Names (SANs), Key Usage, Extended Key Usage, Basic Constraints, Authority/Subject Key Identifiers, CRL Distribution Points - Check certificate expiration: report if expired, days until expiration, or days since expiration - Validate a certificate chain when multiple certificates are provided: verify that each certificate is signed by the next one in the chain - Support reading certificate bundles (multiple PEM certificates concatenated in one file) and parsing each individually - Support a --format flag to choose output format: text (default human-readable), json, or csv - Support fetching and parsing a remote server's certificate via --host flag (given a hostname and optional port) - Print the parsed certificate details to console in a structured, readable format - Save the output to a file via --output flag - If no input is given, generate a self-signed CA certificate and a leaf certificate signed by it, then parse both and demonstrate chain validation - Handle errors: invalid certificate data, unsupported formats, incomplete chains, and encoding issues Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include: - Source code - pom.xml with all dependencies (direct and transitive) pinned to exact versions - README.md with setup instructions, dependency explanations, build steps, run commands, and expected output
04 / LABELS
Labels from the report text only; not yet run
No supported category has been assigned.
Label rules and the text that matched
[]
05 / FILES
The project as the agent wrote it
3 files, exactly as written, before any repair.
CertParser.java
import java.io.*;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.interfaces.*;
import java.util.*;
import java.util.stream.Collectors;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.util.encoders.Hex;
/**
* X.509 Certificate Parser using Bouncy Castle.
*
* Parses X.509 certificates in PEM and DER formats, extracts all fields
* and extensions, and validates certificate chains.
*/
public class CertParser {
static {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Load an X.509 certificate from a file (PEM or DER format).
*/
public static X509Certificate loadCertificate(String filePath) throws Exception {
byte[] data = Files.readAllBytes(Paths.get(filePath));
String content = new String(data);
if (content.contains("-----BEGIN CERTIFICATE-----")) {
return loadPemCertificate(new ByteArrayInputStream(data));
} else {
return loadDerCertificate(data);
}
}
private static X509Certificate loadPemCertificate(InputStream input) throws Exception {
try (PEMParser parser = new PEMParser(new InputStreamReader(input))) {
Object obj = parser.readObject();
if (obj instanceof X509CertificateHolder) {
return new JcaX509CertificateConverter()
.setProvider("BC")
.getCertificate((X509CertificateHolder) obj);
}
throw new IllegalArgumentException("Not a valid PEM certificate");
}
}
private static X509Certificate loadDerCertificate(byte[] data) throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
return (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
}
/**
* Load a chain of certificates from a PEM file.
*/
public static List<X509Certificate> loadCertificateChain(String filePath) throws Exception {
List<X509Certificate> chain = new ArrayList<>();
byte[] data = Files.readAllBytes(Paths.get(filePath));
try (PEMParser parser = new PEMParser(new InputStreamReader(new ByteArrayInputStream(data)))) {
Object obj;
JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider("BC");
while ((obj = parser.readObject()) != null) {
if (obj instanceof X509CertificateHolder) {
chain.add(converter.getCertificate((X509CertificateHolder) obj));
}
}
}
return chain;
}
/**
* Parse a certificate and extract all fields as a formatted string.
*/
public static String parseCertificate(X509Certificate cert) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("{\n");
// Version
sb.append(jsonField("version", cert.getVersion(), 1));
// Serial Number
sb.append(jsonField("serialNumber", cert.getSerialNumber().toString(16), 1));
// Signature Algorithm
sb.append(jsonField("signatureAlgorithm", cert.getSigAlgName(), 1));
// Issuer
sb.append(" \"issuer\": ").append(nameToJson(cert.getIssuerX500Principal().getName())).append(",\n");
// Subject
sb.append(" \"subject\": ").append(nameToJson(cert.getSubjectX500Principal().getName())).append(",\n");
// Validity
sb.append(" \"validity\": {\n");
sb.append(jsonField("notBefore", cert.getNotBefore().toInstant().toString(), 2));
sb.append(jsonFieldLast("notAfter", cert.getNotAfter().toInstant().toString(), 2));
sb.append(" },\n");
// Public Key Info
sb.append(" \"publicKeyInfo\": ").append(publicKeyToJson(cert.getPublicKey())).append(",\n");
// Extensions
sb.append(" \"extensions\": [\n");
sb.append(extractExtensions(cert));
sb.append(" ],\n");
// Fingerprints
sb.append(" \"fingerprints\": {\n");
sb.append(jsonField("sha256", fingerprint(cert, "SHA-256"), 2));
sb.append(jsonField("sha1", fingerprint(cert, "SHA-1"), 2));
sb.append(jsonFieldLast("md5", fingerprint(cert, "MD5"), 2));
sb.append(" },\n");
// Signature
sb.append(jsonFieldLast("signatureValue", Hex.toHexString(cert.getSignature()), 1));
sb.append("}");
return sb.toString();
}
private static String fingerprint(X509Certificate cert, String algorithm) throws Exception {
MessageDigest md = MessageDigest.getInstance(algorithm);
byte[] digest = md.digest(cert.getEncoded());
return Hex.toHexString(digest);
}
private static String nameToJson(String dnString) {
StringBuilder sb = new StringBuilder();
sb.append("{ ");
String[] parts = dnString.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
List<String> entries = new ArrayList<>();
for (String part : parts) {
String trimmed = part.trim();
int eq = trimmed.indexOf('=');
if (eq > 0) {
String key = trimmed.substring(0, eq).trim();
String value = trimmed.substring(eq + 1).trim();
entries.add("\"" + escapeJson(key) + "\": \"" + escapeJson(value) + "\"");
}
}
sb.append(String.join(", ", entries));
sb.append(" }");
return sb.toString();
}
private static String publicKeyToJson(PublicKey key) {
StringBuilder sb = new StringBuilder();
sb.append("{\n");
if (key instanceof RSAPublicKey) {
RSAPublicKey rsaKey = (RSAPublicKey) key;
sb.append(jsonField("algorithm", "RSA", 2));
sb.append(jsonField("keySize", rsaKey.getModulus().bitLength(), 2));
sb.append(jsonFieldLast("exponent", rsaKey.getPublicExponent().intValue(), 2));
} else if (key instanceof ECPublicKey) {
ECPublicKey ecKey = (ECPublicKey) key;
sb.append(jsonField("algorithm", "EC", 2));
sb.append(jsonFieldLast("keySize", ecKey.getParams().getOrder().bitLength(), 2));
} else if (key instanceof DSAPublicKey) {
DSAPublicKey dsaKey = (DSAPublicKey) key;
sb.append(jsonField("algorithm", "DSA", 2));
sb.append(jsonFieldLast("keySize", dsaKey.getParams().getP().bitLength(), 2));
} else {
sb.append(jsonFieldLast("algorithm", key.getAlgorithm(), 2));
}
sb.append(" }");
return sb.toString();
}
private static String extractExtensions(X509Certificate cert) throws Exception {
StringBuilder sb = new StringBuilder();
Set<String> criticalOids = cert.getCriticalExtensionOIDs();
Set<String> nonCriticalOids = cert.getNonCriticalExtensionOIDs();
if (criticalOids == null) criticalOids = new HashSet<>();
if (nonCriticalOids == null) nonCriticalOids = new HashSet<>();
Set<String> allOids = new LinkedHashSet<>();
allOids.addAll(criticalOids);
allOids.addAll(nonCriticalOids);
List<String> extEntries = new ArrayList<>();
for (String oid : allOids) {
boolean critical = criticalOids.contains(oid);
String extValue = parseExtension(cert, oid);
StringBuilder entry = new StringBuilder();
entry.append(" {\n");
entry.append(jsonField("oid", oid, 3));
entry.append(jsonField("name", getExtensionName(oid), 3));
entry.append(jsonField("critical", critical, 3));
entry.append(" \"value\": ").append(extValue).append("\n");
entry.append(" }");
extEntries.add(entry.toString());
}
return String.join(",\n", extEntries) + "\n";
}
private static String parseExtension(X509Certificate cert, String oidStr) {
try {
byte[] extValue = cert.getExtensionValue(oidStr);
if (extValue == null) return "null";
ASN1OctetString octetString = ASN1OctetString.getInstance(extValue);
byte[] octets = octetString.getOctets();
if (oidStr.equals(Extension.basicConstraints.getId())) {
BasicConstraints bc = BasicConstraints.getInstance(ASN1Sequence.getInstance(octets));
return String.format("{ \"ca\": %s, \"pathLength\": %s }",
bc.isCA(), bc.getPathLenConstraint());
}
if (oidStr.equals(Extension.keyUsage.getId())) {
org.bouncycastle.asn1.x509.KeyUsage ku =
org.bouncycastle.asn1.x509.KeyUsage.getInstance(ASN1BitString.getInstance(octets));
return formatKeyUsage(ku);
}
if (oidStr.equals(Extension.extendedKeyUsage.getId())) {
ExtendedKeyUsage eku = ExtendedKeyUsage.getInstance(ASN1Sequence.getInstance(octets));
List<String> usages = new ArrayList<>();
for (KeyPurposeId kp : eku.getUsages()) {
usages.add("\"" + getEkuName(kp) + "\"");
}
return "[ " + String.join(", ", usages) + " ]";
}
if (oidStr.equals(Extension.subjectAlternativeName.getId())) {
GeneralNames gns = GeneralNames.getInstance(ASN1Sequence.getInstance(octets));
return formatGeneralNames(gns);
}
if (oidStr.equals(Extension.subjectKeyIdentifier.getId())) {
SubjectKeyIdentifier ski = SubjectKeyIdentifier.getInstance(octets);
return "{ \"keyIdentifier\": \"" + Hex.toHexString(ski.getKeyIdentifier()) + "\" }";
}
if (oidStr.equals(Extension.authorityKeyIdentifier.getId())) {
AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(ASN1Sequence.getInstance(octets));
StringBuilder akiSb = new StringBuilder("{ ");
if (aki.getKeyIdentifier() != null) {
akiSb.append("\"keyIdentifier\": \"").append(Hex.toHexString(aki.getKeyIdentifier())).append("\"");
}
if (aki.getAuthorityCertSerialNumber() != null) {
akiSb.append(", \"authorityCertSerialNumber\": ")
.append(aki.getAuthorityCertSerialNumber());
}
akiSb.append(" }");
return akiSb.toString();
}
if (oidStr.equals(Extension.cRLDistributionPoints.getId())) {
CRLDistPoint cdp = CRLDistPoint.getInstance(ASN1Sequence.getInstance(octets));
List<String> points = new ArrayList<>();
for (DistributionPoint dp : cdp.getDistributionPoints()) {
StringBuilder dpSb = new StringBuilder("{ ");
if (dp.getDistributionPoint() != null) {
DistributionPointName dpName = dp.getDistributionPoint();
if (dpName.getType() == DistributionPointName.FULL_NAME) {
GeneralNames gns = GeneralNames.getInstance(dpName.getName());
List<String> names = new ArrayList<>();
for (GeneralName gn : gns.getNames()) {
names.add("\"" + gn.getName().toString() + "\"");
}
dpSb.append("\"fullName\": [").append(String.join(", ", names)).append("]");
}
}
dpSb.append(" }");
points.add(dpSb.toString());
}
return "[ " + String.join(", ", points) + " ]";
}
if (oidStr.equals(Extension.authorityInfoAccess.getId())) {
AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(ASN1Sequence.getInstance(octets));
List<String> descriptions = new ArrayList<>();
for (AccessDescription ad : aia.getAccessDescriptions()) {
String method;
if (ad.getAccessMethod().equals(AccessDescription.id_ad_ocsp)) {
method = "ocsp";
} else if (ad.getAccessMethod().equals(AccessDescription.id_ad_caIssuers)) {
method = "caIssuers";
} else {
method = ad.getAccessMethod().getId();
}
descriptions.add(String.format("{ \"accessMethod\": \"%s\", \"accessLocation\": \"%s\" }",
method, ad.getAccessLocation().getName().toString()));
}
return "[ " + String.join(", ", descriptions) + " ]";
}
if (oidStr.equals(Extension.certificatePolicies.getId())) {
ASN1Sequence seq = ASN1Sequence.getInstance(octets);
List<String> policies = new ArrayList<>();
for (ASN1Encodable enc : seq) {
PolicyInformation pi = PolicyInformation.getInstance(enc);
policies.add("{ \"policyIdentifier\": \"" + pi.getPolicyIdentifier().getId() + "\" }");
}
return "[ " + String.join(", ", policies) + " ]";
}
// Fallback: hex encode the extension value
return "\"" + Hex.toHexString(octets) + "\"";
} catch (Exception e) {
return "\"Error parsing: " + escapeJson(e.getMessage()) + "\"";
}
}
private static String formatKeyUsage(org.bouncycastle.asn1.x509.KeyUsage ku) {
StringBuilder sb = new StringBuilder("{\n");
sb.append(jsonField("digitalSignature", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.digitalSignature), 4));
sb.append(jsonField("nonRepudiation", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.nonRepudiation), 4));
sb.append(jsonField("keyEncipherment", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.keyEncipherment), 4));
sb.append(jsonField("dataEncipherment", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.dataEncipherment), 4));
sb.append(jsonField("keyAgreement", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.keyAgreement), 4));
sb.append(jsonField("keyCertSign", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.keyCertSign), 4));
sb.append(jsonField("cRLSign", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.cRLSign), 4));
sb.append(jsonField("encipherOnly", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.encipherOnly), 4));
sb.append(jsonFieldLast("decipherOnly", ku.hasUsages(org.bouncycastle.asn1.x509.KeyUsage.decipherOnly), 4));
sb.append(" }");
return sb.toString();
}
private static String formatGeneralNames(GeneralNames gns) {
List<String> entries = new ArrayList<>();
for (GeneralName gn : gns.getNames()) {
String type;
switch (gn.getTagNo()) {
case GeneralName.dNSName: type = "DNS"; break;
case GeneralName.iPAddress: type = "IP"; break;
case GeneralName.rfc822Name: type = "email"; break;
case GeneralName.uniformResourceIdentifier: type = "URI"; break;
case GeneralName.directoryName: type = "directoryName"; break;
default: type = "other(" + gn.getTagNo() + ")"; break;
}
entries.add(String.format("{ \"type\": \"%s\", \"value\": \"%s\" }",
type, escapeJson(gn.getName().toString())));
}
return "[ " + String.join(", ", entries) + " ]";
}
private static String getExtensionName(String oid) {
Map<String, String> names = new HashMap<>();
names.put(Extension.basicConstraints.getId(), "basicConstraints");
names.put(Extension.keyUsage.getId(), "keyUsage");
names.put(Extension.extendedKeyUsage.getId(), "extendedKeyUsage");
names.put(Extension.subjectAlternativeName.getId(), "subjectAlternativeName");
names.put(Extension.subjectKeyIdentifier.getId(), "subjectKeyIdentifier");
names.put(Extension.authorityKeyIdentifier.getId(), "authorityKeyIdentifier");
names.put(Extension.cRLDistributionPoints.getId(), "cRLDistributionPoints");
names.put(Extension.authorityInfoAccess.getId(), "authorityInfoAccess");
names.put(Extension.certificatePolicies.getId(), "certificatePolicies");
names.put(Extension.nameConstraints.getId(), "nameConstraints");
names.put(Extension.policyConstraints.getId(), "policyConstraints");
names.put(Extension.inhibitAnyPolicy.getId(), "inhibitAnyPolicy");
return names.getOrDefault(oid, oid);
}
private static String getEkuName(KeyPurposeId kp) {
if (kp.equals(KeyPurposeId.id_kp_serverAuth)) return "serverAuth";
if (kp.equals(KeyPurposeId.id_kp_clientAuth)) return "clientAuth";
if (kp.equals(KeyPurposeId.id_kp_codeSigning)) return "codeSigning";
if (kp.equals(KeyPurposeId.id_kp_emailProtection)) return "emailProtection";
if (kp.equals(KeyPurposeId.id_kp_timeStamping)) return "timeStamping";
if (kp.equals(KeyPurposeId.id_kp_OCSPSigning)) return "ocspSigning";
return kp.getId();
}
/**
* Validate a certificate chain.
* The chain should be ordered from end-entity to root.
*/
public static String validateChain(List<X509Certificate> chain) {
StringBuilder sb = new StringBuilder();
sb.append("{\n");
sb.append(jsonField("chainLength", chain.size(), 1));
boolean valid = true;
List<String> errors = new ArrayList<>();
List<String> certResults = new ArrayList<>();
Date now = new Date();
for (int i = 0; i < chain.size(); i++) {
X509Certificate cert = chain.get(i);
StringBuilder certSb = new StringBuilder();
certSb.append(" {\n");
certSb.append(jsonField("index", i, 3));
certSb.append(jsonField("subject", cert.getSubjectX500Principal().getName(), 3));
certSb.append(jsonField("issuer", cert.getIssuerX500Principal().getName(), 3));
// Validity check
boolean validPeriod = true;
try {
cert.checkValidity(now);
} catch (CertificateExpiredException e) {
validPeriod = false;
errors.add("Certificate at index " + i + " has expired");
valid = false;
} catch (CertificateNotYetValidException e) {
validPeriod = false;
errors.add("Certificate at index " + i + " is not yet valid");
valid = false;
}
certSb.append(jsonField("validityCheck", validPeriod, 3));
// Signature verification
boolean sigOk = false;
if (i < chain.size() - 1) {
X509Certificate issuerCert = chain.get(i + 1);
try {
cert.verify(issuerCert.getPublicKey(), "BC");
sigOk = true;
} catch (Exception e) {
errors.add("Signature verification failed at index " + i + ": " + e.getMessage());
valid = false;
}
} else {
// Root: check self-signed
try {
cert.verify(cert.getPublicKey(), "BC");
sigOk = true;
certSb.append(jsonField("selfSigned", true, 3));
} catch (Exception e) {
certSb.append(jsonField("selfSigned", false, 3));
errors.add("Root certificate at index " + i + " is not self-signed");
valid = false;
}
}
certSb.append(jsonFieldLast("signatureCheck", sigOk, 3));
// Check CA basic constraints for issuer certs
if (i < chain.size() - 1) {
X509Certificate issuerCert = chain.get(i + 1);
int bc = issuerCert.getBasicConstraints();
if (bc < 0) {
errors.add("Certificate at index " + (i + 1) + " is not a CA");
valid = false;
}
}
certSb.append(" }");
certResults.add(certSb.toString());
}
sb.append(" \"certificates\": [\n");
sb.append(String.join(",\n", certResults)).append("\n");
sb.append(" ],\n");
sb.append(jsonField("valid", valid, 1));
sb.append(" \"errors\": [");
if (!errors.isEmpty()) {
sb.append("\n");
List<String> quotedErrors = errors.stream()
.map(e -> " \"" + escapeJson(e) + "\"")
.collect(Collectors.toList());
sb.append(String.join(",\n", quotedErrors)).append("\n ");
}
sb.append("]\n");
sb.append("}");
return sb.toString();
}
// JSON formatting helpers
private static String jsonField(String key, Object value, int indent) {
String pad = " ".repeat(indent);
if (value instanceof String) {
return pad + "\"" + key + "\": \"" + escapeJson((String) value) + "\",\n";
} else {
return pad + "\"" + key + "\": " + value + ",\n";
}
}
private static String jsonFieldLast(String key, Object value, int indent) {
String pad = " ".repeat(indent);
if (value instanceof String) {
return pad + "\"" + key + "\": \"" + escapeJson((String) value) + "\"\n";
} else {
return pad + "\"" + key + "\": " + value + "\n";
}
}
private static String escapeJson(String s) {
if (s == null) return "null";
return s.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\t", "\\t");
}
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: java CertParser <certificate_file> [chain_file]");
System.out.println();
System.out.println("Options:");
System.out.println(" certificate_file Path to a PEM or DER encoded X.509 certificate");
System.out.println(" chain_file Optional PEM file with certificate chain for validation");
System.exit(1);
}
try {
String certFile = args[0];
System.out.println("Parsing certificate: " + certFile);
X509Certificate cert = loadCertificate(certFile);
System.out.println(parseCertificate(cert));
if (args.length > 1) {
String chainFile = args[1];
System.out.println("\nValidating certificate chain: " + chainFile);
List<X509Certificate> chain = loadCertificateChain(chainFile);
System.out.println(validateChain(chain));
}
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.certparser</groupId>
<artifactId>x509-cert-parser</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>X.509 Certificate Parser</name>
<description>Parses X.509 certificates, extracts fields/extensions, and validates chains</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.77</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>CertParser</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
README.md
# X.509 Certificate Parser (Java - Trial 1) An X.509 certificate parser using Bouncy Castle that reads PEM and DER encoded certificates, extracts all fields and extensions, and validates certificate chains. ## Dependencies - **Bouncy Castle bcprov-jdk18on** (1.77): Cryptographic provider offering comprehensive X.509 certificate handling and ASN.1 parsing. - **Bouncy Castle bcpkix-jdk18on** (1.77): PKIX utilities for PEM parsing, certificate holder conversion, and certificate path validation. ## Build ```bash mvn clean package ``` ## Usage ### Parse a single certificate ```bash java -jar target/x509-cert-parser-1.0.0.jar certificate.pem ``` ### Parse and validate a chain ```bash java -jar target/x509-cert-parser-1.0.0.jar certificate.pem chain.pem ``` ## Features - Parses PEM and DER encoded X.509 certificates - Extracts subject, issuer, validity, serial number, and signature algorithm - Extracts public key information (RSA, EC, DSA) - Extracts all standard extensions (Basic Constraints, Key Usage, EKU, SAN, SKI, AKI, CRL DP, AIA, Certificate Policies) - Computes SHA-256, SHA-1, and MD5 fingerprints - Validates certificate chains with signature verification - Checks validity periods and CA constraints - JSON formatted output