spring-projects/spring-boot · critical · IllegalStateException

Unable to get X.509 certificate factory

Error message

Unable to get X.509 certificate factory

What it means

PemCertificateParser.getCertificateFactory calls CertificateFactory.getInstance("X.509") and wraps any CertificateException in IllegalStateException. Like SHA-256, the X.509 certificate factory is mandatory in every Java SE implementation, so this only fires on a non-compliant or stripped runtime that does not register the sun.security.x509 provider.

Source

Thrown at buildpack/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/docker/ssl/PemCertificateParser.java:77

	 */
	@Contract("!null -> !null")
	static @Nullable List<X509Certificate> parse(@Nullable String text) {
		if (text == null) {
			return null;
		}
		CertificateFactory factory = getCertificateFactory();
		List<X509Certificate> certs = new ArrayList<>();
		readCertificates(text, factory, certs::add);
		Assert.state(!CollectionUtils.isEmpty(certs), "Missing certificates or unrecognized format");
		return List.copyOf(certs);
	}

	private static CertificateFactory getCertificateFactory() {
		try {
			return CertificateFactory.getInstance("X.509");
		}
		catch (CertificateException ex) {
			throw new IllegalStateException("Unable to get X.509 certificate factory", ex);
		}
	}

	private static void readCertificates(String text, CertificateFactory factory, Consumer<X509Certificate> consumer) {
		try {
			Matcher matcher = PATTERN.matcher(text);
			while (matcher.find()) {
				String encodedText = matcher.group(1);
				byte[] decodedBytes = decodeBase64(encodedText);
				ByteArrayInputStream inputStream = new ByteArrayInputStream(decodedBytes);
				while (inputStream.available() > 0) {
					consumer.accept((X509Certificate) factory.generateCertificate(inputStream));
				}
			}
		}
		catch (CertificateException ex) {
			throw new IllegalStateException("Error reading certificate: " + ex.getMessage(), ex);
		}

View on GitHub (pinned to 270dfe353f)

Solutions

  1. Run on a stock OpenJDK distribution where X.509 is always available.
  2. If using jlink, do not exclude the certificate-related providers.
  3. Smoke-test: `CertificateFactory.getInstance("X.509")` in jshell.
Defensive patterns

Strategy: validation

Validate before calling

// Startup smoke-test for the X.509 certificate factory
try {
    CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
    throw new IllegalStateException(
        "This JRE does not provide an X.509 certificate factory; use a stock OpenJDK.", e);
}

Prevention

When it happens

Trigger: CertificateFactory.getInstance("X.509") throws CertificateException because no provider offers the X.509 factory; caught at line 76.

Common situations: A jlink-built runtime that excluded certificate providers; a FIPS-hardened or policy-restricted JVM; a non-standard JVM with an incomplete java.security.

Understand the failure class

Related errors


AI-assisted analysis of spring-projects/spring-boot@270dfe353f (2026-08-11). Data as JSON: /api/errors/a62132d232164b6f. Report an issue: GitHub.