quarkusio/quarkus · error · RuntimeException
Failure to create a certificate
Error message
Failure to create a certificate
What it means
LetsEncryptHelpers.getCertificate() decodes a base64-encoded certificate and parses it via CertificateFactory X.509. Any exception during decoding or parsing (malformed base64, empty input, not a valid X.509 structure) is wrapped in this RuntimeException. It signals that data retrieved from the Let's Encrypt/ACME order could not be turned into a usable X509Certificate.
Source
Thrown at extensions/tls-registry/cli/src/main/java/io/quarkus/tls/cli/letsencrypt/LetsEncryptHelpers.java:259
}
private static JsonObject readAccountJson(File letsEncryptPath) {
LOGGER.debugf("Reading account information from %s", letsEncryptPath);
java.nio.file.Path accountPath = Paths.get(letsEncryptPath + "/account.json");
try (FileInputStream fis = new FileInputStream(accountPath.toString())) {
return new JsonObject(new String(fis.readAllBytes(), StandardCharsets.US_ASCII));
} catch (IOException e) {
throw new RuntimeException("Unable to read the account file, you must create account first");
}
}
private static X509Certificate getCertificate(String encodedCert) {
try {
byte[] encodedBytes = Base64.getDecoder().decode(encodedCert);
return (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(new ByteArrayInputStream(encodedBytes));
} catch (Exception ex) {
throw new RuntimeException("Failure to create a certificate", ex);
}
}
private static PrivateKey getPrivateKey(String encodedKey, String keyAlgorithm) {
try {
KeyFactory f = KeyFactory.getInstance((keyAlgorithm == null || "RSA".equals(keyAlgorithm) ? "RSA" : "EC"));
byte[] encodedBytes = Base64.getDecoder().decode(encodedKey);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(encodedBytes);
return f.generatePrivate(spec);
} catch (Exception ex) {
throw new RuntimeException("Failure to create a private key", ex);
}
}
public static void renewCertificate(AcmeClient acmeClient,
File letsEncryptPath,
boolean staging,
String domain,View on GitHub (pinned to e1c734241f)
Solutions
- Inspect the source file/URL the encoded certificate came from and re-download it; verify the file starts with valid base64 payload
- Strip PEM headers/footers and whitespace before passing the string, or decode PEM body only
- Validate the base64 string decodes and parses offline (openssl x509) before feeding it to the helper
- Delete the stale file under the letsencrypt directory and run renewal again so ACME issues a fresh certificate
Example fix
// before
X509Certificate cert = LetsEncryptHelpers.getCertificate(rawPemWithHeaders);
// after
String b64 = rawPemWithHeaders.replaceAll("-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\\s", "");
X509Certificate cert = LetsEncryptHelpers.getCertificate(b64); Defensive patterns
Strategy: validation
Validate before calling
static boolean looksLikeValidCertBase64(String s) {
if (s == null || s.isBlank()) return false;
try {
byte[] der = Base64.getDecoder().decode(s.replaceAll("-----[A-Z ]+-----|\\s", ""));
CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(der));
return true;
} catch (Exception e) { return false; }
} Type guard
if (raw == null || raw.isBlank() || !looksLikeValidCertBase64(raw)) { reissueOrAbort(); } Try / catch
try {
X509Certificate cert = LetsEncryptHelpers.getCertificate(encoded);
} catch (RuntimeException e) {
LOGGER.error("Corrupted certificate payload; delete stored cert and renew", e);
renewCertificate();
} Prevention
- Strip PEM headers/footers and whitespace before decoding
- Validate stored cert with openssl x509 after each renewal
- Re-download the certificate rather than reusing a possibly truncated file
- Check disk space and download completion (HTTP status) before persisting
When it happens
Trigger: getCertificate(encodedCert) throws when the encoded string is not valid base64 (IllegalArgumentException), is empty/null, or CertificateFactory.generateCertificate fails because the bytes are not a DER/PEM X.509 certificate. Called from privateKey()/certificate() accessors during certificate renewal.
Common situations: The downloaded ACME certificate chain file is truncated, HTML error page saved instead of cert, PEM headers ('-----BEGIN CERTIFICATE-----') left in the base64 string, or an interrupted download corrupted the stored cert file.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Leaf certificate has no Subject Alternative Names
- No certificate found with alias: <alias>
- Invalid PEM file: No PEM content found.
- Failed to obtain certificate: <reason>
- Failure to create a private key
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/89aec4e803f475e5.
Report an issue: GitHub.