Fixing “Illegal Base64 Character 2d” When Loading a Private Key in Java

When implementing JWT authentication or cryptographic signing in a Java or Spring Boot application, you may encounter:

java.lang.IllegalArgumentException: Illegal base64 character 2d

This error often appears when Java tries to load a private key stored in PEM format.

The good news is that the private key may be perfectly valid. The problem is usually how the PEM value is being decoded.

Security note: Never publish real private keys in source code, logs, tickets, documentation, or blog posts. Always use anonymized examples.

Understanding the Error

A typical private key looks like this:

-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BA...
...
-----END PRIVATE KEY-----

A common mistake is:

byte[] decoded = Base64.getDecoder().decode(privateKey);

The problem is that the complete PEM document is not Base64.

Only the content between:

-----BEGIN PRIVATE KEY-----

and:

-----END PRIVATE KEY-----

is Base64 encoded.

Why Does Java Say 2d?

This is actually a useful clue.

Hexadecimal:

2D

represents the ASCII character:

-

And what does a PEM file start with?

-----BEGIN PRIVATE KEY-----
^

The Base64 decoder encounters the - character from the PEM header and rejects it.

So:

Illegal base64 character 2d

often means:

You’re trying to Base64-decode the PEM header together with the actual key.

The Correct Solution

Remove the PEM header, footer, and whitespace before decoding:

String privateKeyContent = privateKey
        .replace("-----BEGIN PRIVATE KEY-----", "")
        .replace("-----END PRIVATE KEY-----", "")
        .replaceAll("\\s", "");

byte[] decodedKey =
        Base64.getDecoder().decode(privateKeyContent);

For a PKCS#8 RSA key, you can then create the Java private key:

PKCS8EncodedKeySpec keySpec =
        new PKCS8EncodedKeySpec(decodedKey);

KeyFactory keyFactory =
        KeyFactory.getInstance("RSA");

PrivateKey rsaPrivateKey =
        keyFactory.generatePrivate(keySpec);

A reusable helper could therefore look like:

public static PrivateKey loadPrivateKey(String pem)
        throws GeneralSecurityException {

    String content = pem
            .replace("-----BEGIN PRIVATE KEY-----", "")
            .replace("-----END PRIVATE KEY-----", "")
            .replaceAll("\\s", "");

    byte[] decoded =
            Base64.getDecoder().decode(content);

    PKCS8EncodedKeySpec spec =
            new PKCS8EncodedKeySpec(decoded);

    return KeyFactory
            .getInstance("RSA")
            .generatePrivate(spec);
}

PKCS#8 vs PKCS#1

Pay attention to the PEM header.

If you have:

-----BEGIN PRIVATE KEY-----

the key is normally PKCS#8, which works with:

PKCS8EncodedKeySpec

However, if you have:

-----BEGIN RSA PRIVATE KEY-----

you are usually dealing with PKCS#1.

You cannot necessarily remove the header and pass a PKCS#1 key directly to:

PKCS8EncodedKeySpec

The key may first need to be converted or parsed with a library that supports PKCS#1.

Base64 vs Base64URL

JWT applications can make this even more confusing.

Java provides:

Base64.getDecoder()

for standard Base64 and:

Base64.getUrlDecoder()

for Base64URL.

JWT components use Base64URL, but a PEM private key normally contains standard Base64.

Therefore, don’t switch to:

Base64.getUrlDecoder()

just because Java complains about a -.

If the - belongs to:

-----BEGIN PRIVATE KEY-----

the PEM header is the real problem.

Spring Boot, Docker, and Kubernetes

Another common source of problems is passing private keys through configuration:

Secret
   ↓
Environment variable
   ↓
Spring property
   ↓
Java String
   ↓
Base64 decoder

Before decoding, determine what the application actually receives.

It could be:

  • the complete PEM document;
  • only the Base64 body;
  • Base64 encoding of the entire PEM document;
  • a PKCS#1 key;
  • a PKCS#8 key.

Kubernetes can make this especially confusing because Secret values are themselves commonly represented as Base64.

You can therefore accidentally create multiple encoding layers.

Debugging Safely

Never do this:

log.info("Private key: {}", privateKey);

Instead, log only structural information:

log.debug(
    "Key length: {}, PEM header detected: {}",
    privateKey.length(),
    privateKey.contains("BEGIN PRIVATE KEY")
);

This lets you investigate the problem without exposing sensitive cryptographic material.

Quick Troubleshooting Checklist

If you encounter:

Illegal base64 character 2d

check:

  1. Does the value contain -----BEGIN PRIVATE KEY-----?
  2. Are you removing the PEM header and footer?
  3. Are spaces and line breaks removed before decoding?
  4. Are you using Base64.getDecoder() rather than the JWT Base64URL decoder?
  5. Is the key PKCS#8 or PKCS#1?
  6. Does the selected Java KeySpec match the key format?
  7. Has the key been Base64-encoded more than once by configuration or Kubernetes?

Conclusion

The exception:

java.lang.IllegalArgumentException:
Illegal base64 character 2d

usually doesn’t mean the private key is corrupted.

With PEM keys, 2d often points directly to the - character in:

-----BEGIN PRIVATE KEY-----

The correct processing flow is:

PEM → remove header/footer → remove whitespace → Base64 decode → create KeySpec → create PrivateKey

Understanding the difference between PEM, Base64, Base64URL, PKCS#1, and PKCS#8 makes these Java cryptography problems much easier to diagnose.

SEO Elements

SEO Title: Java Illegal Base64 Character 2d: Fix PEM Private Key Errors

Meta Description: Learn why Java throws “Illegal base64 character 2d” when loading PEM private keys and how to correctly handle Base64, PKCS#8, RSA, JWT, and Spring Boot.

Focus Keyphrase: Java illegal base64 character 2d

Suggested Slug: java-illegal-base64-character-2d-private-key

Keywords: Java Base64 error, Java PEM private key, Spring Boot JWT, PKCS8EncodedKeySpec, PKCS#8 Java, PKCS#1 Java, RSA private key, JWT authentication, Java cryptography

WordPress Tags: Java, Spring Boot, JWT, Base64, PEM, RSA, PKCS8, Private Key, Cryptography, Java Security

This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back