jwtk/jjwt · error · IllegalArgumentException
Unable to convert Base64 String '${s}' to X509Certificate in
Error message
Unable to convert Base64 String '${s}' to X509Certificate instance. Cause: ${e.getMessage()} What it means
JwtX509StringConverter.applyFrom decodes a Base64 string (the x5c header value) back into an X509Certificate. If Base64 decoding or certificate parsing fails for any reason, it throws IllegalArgumentException with this message wrapping the original cause. Note the input must be standard Base64, not Base64URL, per RFC 7515.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JwtX509StringConverter.java:71
} finally {
Bytes.clear(der);
}
}
// visible for testing
protected X509Certificate toCert(final byte[] der) throws SecurityException {
return new JcaTemplate("X.509").generateX509Certificate(der);
}
@Override
public X509Certificate applyFrom(CharSequence s) {
Assert.hasText(s, "X.509 Certificate encoded string cannot be null or empty.");
try {
byte[] der = Decoders.BASE64.decode(s); //RFC requires Base64, not Base64Url
return toCert(der);
} catch (Exception e) {
String msg = "Unable to convert Base64 String '" + s + "' to X509Certificate instance. Cause: " + e.getMessage();
throw new IllegalArgumentException(msg, e);
}
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Verify the string is standard Base64 (alphabet A-Za-z0-9+/ with = padding), not Base64URL.
- Strip PEM headers/footers and whitespace before passing the value.
- Decode and validate the string manually with java.util.Base64.getDecoder() and CertificateFactory to see the underlying cause.
- Use JwtX509StringConverter (or jjwt) to produce the string in the first place instead of hand-rolling encoding.
Example fix
// before String x5c = Base64.getUrlEncoder().encodeToString(cert.getEncoded()); // wrong alphabet // after String x5c = Base64.getEncoder().encodeToString(cert.getEncoded()); // standard Base64 per RFC
Defensive patterns
Strategy: validation
Validate before calling
boolean isStandardBase64(String s) {
return s != null && s.matches("[A-Za-z0-9+/]+={0,2}");
} Try / catch
try {
X509Certificate cert = converter.applyFrom(x5cValue);
} catch (IllegalArgumentException e) {
logger.error("Bad x5c value '{}': {}", x5cValue, e.getCause());
// fix encoding (URL-safe -> standard) or re-encode from the cert
} Prevention
- Use java.util.Base64.getEncoder() (not getUrlEncoder) for x5c values.
- Strip PEM BEGIN/END lines and whitespace before embedding certificates in x5c.
- Round-trip test: applyTo then applyFrom should reproduce the certificate.
When it happens
Trigger: Reading an x5c header value that is invalid Base64, Base64URL-encoded instead of Base64 (contains '-'/'_'), has whitespace/newlines, does not include the leading certificate bytes, or is otherwise not parseable as X.509 DER.
Common situations: Manually building an x5c header from a PEM file while forgetting to strip the BEGIN/END lines; using a Base64URL encoder by mistake; header values altered by URL-encoding or line-wrapping; certificates generated by tooling with wrong formats.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Unable to access X509Certificate encoded bytes necessary to
- X509Certificate encoded bytes cannot be null or empty. Cert
- Illegal ${name} character: '${c}'
- Unable to decode input: ${e.getMessage()}
- Compact JWE string represents an encrypted key, but the key
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/d9430cb0be20850e.
Report an issue: GitHub.