"error in libcrypto" (SSH Key Loading Error): Causes and Fix
"Load key "...": error in libcrypto" means OpenSSH tried to read your private key and the underlying crypto library couldn't parse it — this happens before authentication is ever attempted. This is a local private-key lo
"Load key "...": error in libcrypto" means OpenSSH tried to read your private key and the underlying crypto library couldn't parse it — this happens before authentication is ever attempted. This is a local private-key loading/parsing failure, so troubleshooting should start with the key data and how it is being supplied — before looking at server-side authentication or network connectivity. Common causes include a missing newline, changed line endings, malformed key data, or a CI/CD pipeline altering how the key is supplied.
The error itself doesn't say which of those happened — it's a generic message OpenSSH returns any time a low-level OpenSSL/libcrypto call fails while parsing key material, regardless of the specific reason. That's why guessing at a single fix rarely works on the first try. This guide starts with a one-command diagnostic, then walks through causes in the order they're worth checking.
Quick Diagnosis: Is It the Key or the Environment?
Before touching CI variables or line endings, isolate whether the problem is the key file itself or how it's being passed in. Run this directly against the file:
ssh-keygen -y -f /path/to/private_key
ssh-keygen -y reads a private key and derives its public key — it uses the exact same key-loading code path as ssh and ssh-add, so it reproduces the same failure independent of any CI system, agent, or pipe. If you want to isolate the key from a CI environment entirely, this also works from any machine with the key file present — a local terminal, or a desktop SSH client if you already use one to manage connections.
- 1*Same "error in libcrypto" comes back:* the key file itself is malformed. Move to the causes below — it isn't a CI or agent issue.
- 2*A different error appears* (permissions, passphrase prompt, or it succeeds): the file is fine, and the problem is in how it's being delivered into the failing command — a CI variable, a pipe, or a copy-paste step.
- 3*It succeeds and prints a public key:* the file is valid. If
sshorssh-addstill fail elsewhere, re-check that you're pointing at the same file and not a stale copy.
If the key checks out this way but fails specifically inside a CI job, skip ahead to the CI/CD section — the next step is to inspect how the pipeline reconstructs and supplies the key.
Why "error in libcrypto" Doesn't Tell You More
OpenSSH's own source code uses a generic result code whenever an underlying OpenSSL/libcrypto call fails partway through a cryptographic operation, rather than a distinct code per failure reason — the same pattern shows up elsewhere in OpenSSH's codebase, where a failed libcrypto call during key generation returns a single generic error regardless of what specifically went wrong inside OpenSSL. Key-loading follows the same approach: whether the failure is bad base64, a corrupted PEM structure, a truncated buffer, or unexpected bytes from a stray line ending, OpenSSH surfaces the same "error in libcrypto" text. The message correctly identifies which library failed; it just doesn't say why.
Cause 1: Missing Trailing Newline
The single most commonly reported cause, especially when a key is pasted into a form field, a CI/CD secret, or a shell variable rather than read from an intact file. Both classic PEM and the newer OpenSSH key format are expected to end with a newline character after the final -----END ... KEY----- line, and copy-pasting a key into a text box often drops that trailing newline.
Check for it directly:
tail -c 50 /path/to/private_key | xxd | tail -3
If the last visible byte before the file ends isn't a newline (0a), add one:
printf '\n' >> /path/to/private_key
In a CI variable, the fix is the same in spirit — make sure the value as stored actually ends with a line break, not just visually appears to in the editor.
Cause 2: CRLF Line Endings
CRLF line endings can cause key-loading failures in some OpenSSH builds and key-format paths, especially after a key has been copied or edited on Windows. If the problem appeared after moving the key between Windows and Unix-like environments, checking for carriage-return characters is a useful diagnostic step.
Strip carriage returns with any of these:
# dos2unix, if installed
dos2unix /path/to/private_key
# sed
sed -i 's/\r$//' /path/to/private_key
# tr, useful when piping a value rather than editing a file
cat /path/to/private_key | tr -d '\r' > /path/to/private_key.fixed
Windows-specific note: if you're on Windows and the key still fails after stripping \r, check which OpenSSH build you're using. Windows' built-in OpenSSH client and Git for Windows' bundled OpenSSH are two separate builds maintained by different projects, and at least one reported case found Git for Windows' bundled client lacking a CRLF-handling fix present in the other — worth testing the same key with Windows' built-in ssh (where ssh in PowerShell shows which binary runs) if Git Bash's version keeps failing.
Cause 3: Truncated or Mangled Key From a Copy-Paste
Beyond newlines specifically, a key can simply be incomplete — a partial copy, a line accidentally left out when pasting into a YAML/JSON/env file, or extra escaping characters introduced by a shell or config format wrapping the value in quotes.
Quick sanity checks before assuming the key is unrecoverable:
- ✓The file starts with
-----BEGINand ends with-----END, both intact and unbroken. - ✓No extra quote characters (
"or') got pasted in as part of the key body. - ✓Compare the file with a known-good copy if you have one; don't try to reconstruct missing key material manually.
- ✓You're pointing at the private key file, not the matching
.pubfile (see private vs. public key if that distinction isn't clear).
If the key is genuinely corrupted beyond repair — not just a line-ending or whitespace issue — regenerating it is faster than trying to reconstruct missing bytes. See generating a new SSH key, then install the new public key wherever the old one was authorized.
Cause 4: CI/CD Secret Storage Mangling the Key
This is where the error shows up most often in practice — a key that works fine locally starts failing the moment it's stored as a pipeline secret. A few platform-specific things to know:
GitLab CI
GitLab's CI/CD variables come in two types: a plain Variable or a File variable, and community reports consistently point to the File type preserving multi-line values more reliably for keys. GitLab has also tracked multiple historical bugs around multi-line variable values specifically — including CRLF-style line endings appearing in multi-line secret variables, and multi-line values gaining unwanted leading whitespace — so treat any multi-line secret as worth verifying, not assuming correct. When in doubt, strip carriage returns defensively before use:
printf '%s\n' "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
GitHub Actions and Other CI Platforms
If a key works locally but fails after being supplied through a CI secret, compare the bytes reconstructed in the job with the original key rather than assuming the platform is at fault. Shell quoting, YAML parsing, environment-variable handling, copy-paste, or a missing final newline can all change how a multi-line value reaches ssh-add by the time it's actually used — the fix is the same regardless of which of those introduced the change.
The More Robust Fix: Base64-Encode the Secret
Rather than fighting whichever specific whitespace behavior your CI platform has this month, encode the key as a single line before storing it as a secret, then decode it inside the job:
# Locally, before saving the secret:
base64 -w0 ~/.ssh/deploy_key > deploy_key.b64
# Paste the contents of deploy_key.b64 as the secret value
# In the CI job:
echo "$SSH_PRIVATE_KEY_B64" | base64 -d > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keygen -y -f ~/.ssh/deploy_key >/dev/null || echo "Key is malformed after decode"
A single-line base64 string has no internal line breaks for a secret-storage system to normalize, strip, or reformat — it either decodes back to the exact original bytes or it doesn't. Adding the ssh-keygen -y check as a pre-flight step means a bad decode fails the job immediately with a clear message instead of surfacing as a confusing downstream authentication error.
Cause 5: Wrong Key Format
Two format mismatches produce a similar-looking failure:
| Situation | Fix |
|---|---|
| Key is in PuTTY's .ppk format | .ppk isn't OpenSSH format at all. Convert it with PuTTYgen first — see the PEM and PuTTY notes in our private-key connection guide. |
| Pointing at the .pub file by mistake | The public key can't be loaded as a private key. Double-check the path has no .pub extension. |
The Sibling Error: "invalid format"
You may also see Error loading key "...": invalid format in near-identical scenarios — a stripped CI variable, a mangled paste, a truncated file. It's the same category of problem (OpenSSH's key parser rejected the data) surfacing through a different code path than the libcrypto-specific failure. Every diagnostic step above applies equally; there's no separate fix to look for.
FAQ
Does this mean my key is compromised?
No. This is a formatting/parsing failure, not a security event. Nothing about "error in libcrypto" implies the key was exposed or tampered with maliciously — it means the bytes on disk (or in the CI variable) don't form a valid key structure.
Why does it work locally but fail only in CI?
The key file on your machine is intact; the copy stored as a CI secret picked up a formatting change somewhere between pasting it in and the pipeline reading it back out. See the CI/CD section above.
Do I need to regenerate the key?
Usually not. Missing newlines and CRLF corruption are both recoverable without creating a new key pair. Regenerate only if the key is genuinely truncated or you can't otherwise get a clean copy of the original.
Conclusion
"error in libcrypto" is OpenSSH telling you a libcrypto call failed while parsing key data — not which specific problem caused it. Start with ssh-keygen -y -f keyfile to determine whether the key itself can be read. Then check the common failure points: a missing trailing newline, changed line endings, truncated or malformed key data, the wrong key format, or changes introduced while passing the key through CI/CD. For pipelines, storing the key as base64 can avoid many multiline-secret formatting problems.
Originally published on SSHFlow.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.