Blog
All Blog Posts | Next Post | Previous Post
Demystifying SSH crypto containers: architecture, X.509 differences, OpenSSL creation, and TMS Crypto creation
Today
When managing secure access to remote infrastructure, understanding how cryptographic material is structured and stored is essential. While terms like "keys," "certificates," and "containers" are often used interchangeably, they represent distinct layers of security abstraction.
Our TMS Cryptography Pack contains all cryptographic primitives to generate SSH objects but so far, generation and decoding of such containers was not possible. This is fixed with version 5.3.3.0.
In this blog post, we will explore SSH crypto containers, compare them with traditional X.509 certificates and keys, outline available options, and walk through creating one using OpenSSL, and, as usual, show how simple it is to generate and read them with TMS Cryptography Pack in native Delphi.
What is an SSH crypto container?
An SSH crypto container refers to a structured file or format designed to securely encapsulate cryptographic material (such as private keys, public keys, and metadata) used within the Secure Shell (SSH) ecosystem.
Rather than storing a private key as plain text on a disk, modern crypto containers or formats (like PEM-formatted encrypted keys, PKCS#8, or OpenSSH's native key formats) wrap the sensitive material inside an authenticated, encrypted envelope. This ensures that even if unauthorized users gain read access to the storage medium, the private key remains protected behind a symmetric encryption cipher (such as AES-256) and a user-defined passphrase.
SSH containers vs. X.509 certificates and keys
It is common to confuse SSH authentication formats with X.509 certificates (commonly used in TLS/HTTPS, web browsers, and mTLS). However, they serve different ecosystems and follow distinct design philosophies:
When working with cryptographic keys for SSH, you generally encounter a few standard options:
- Traditional PEM / OpenSSH Private Key Format: The default plaintext or ciphertext file generated by tools like
ssh-keygen. - PKCS#8 Containers: A standardized syntax for storing private key information, allowing better algorithm agility and stronger encryption wrappers across different tools.
- Encrypted PEM with Symmetric Ciphers: Wrapping standard keys using algorithms like
aes256-cbcorchacha20-poly1305, requiring a passphrase for extraction. - Hardware Tokens / Secure Enclaves: Storing keys in YubiKeys, TPMs, or HSMs (PKCS#11) where the container acts as a logical reference to hardware-isolated crypto operations.
Hands-On: Creating an Encrypted SSH Crypto Container with OpenSSL
You can use OpenSSL to generate a secure, password-protected private key container that can later be adapted or converted for cryptographic operations.
Step 1: Generate an Encrypted RSA Private Key Container
Run the following command to generate a 4096-bit RSA private key encrypted with AES-256:
openssl genpkey -algorithm RSA -out ssh_crypto_container.pem -aes256 -pkeyopt rsa_keygen_bits:4096-aes256: Ensures the private key is wrapped inside an encrypted container, prompting you for a secure passphrase.-out ssh_crypto_container.pem: Specifies the output file acting as your crypto container.
Step 2: Inspect the Container Structure
Because the container is encrypted, attempting to read the private key directly will show ciphertext wrapper headers:
cat ssh_crypto_container.pem
Output preview:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIJrDBOBgkqhkiG9w0BBQ0wMjAbBgkqhkiG9w0BBQwwDgQI...
-----END ENCRYPTED PRIVATE KEY-----
Step 3: Extract the Public Key Component
To use this container for authentication workflows, you need to extract the corresponding public key:
openssl rsa -in ssh_crypto_container.pem -pubout -out ssh_public_key.pub
(Note: You will be prompted to enter the passphrase you created in Step 1).
What does TMS Cryptography Pack provide for SSH?
The TSSHKeyHandler class is a self-contained Delphi unit to generate, read, write and convert SSH keys, interoperable with OpenSSH (ssh-keygen, ssh) and OpenSSL. It builds on the librarys own primitives (RSA, ECDSA, Ed25519, AES, bcrypt - added) and needs no external tool or DLL at run time.
- Key types: RSA (10244096 bits), ECDSA (P-256, P-384, P-521) and Ed25519. OpenSSH private keys (-----BEGIN OPENSSH PRIVATE KEY-----): read and write, in clear or protected by a passphrase (bcrypt KDF with aes128/192/256-ctr on reading, aes256-ctr on writing).
- PKCS#8 / PEM files; readable by OpenSSL: private key (optionally encrypted), public key (SubjectPublicKeyInfo), and the one-line ssh-rsa / ecdsa-sha2- / ssh-ed25519 public key.
- Public keys: read and write .pub files with their comments, and compute SHA256 fingerprints identical to ssh-keygen -lPassphrases and comments may contain any Unicode character (stored as UTF-8).
Some features are not supported:
ssh-dss, FIDO (sk-*) keys, and the ciphers chacha20-poly1305, aes*-gcm and aes*-cbc for OpenSSH private keys (AES GCM and CBC might be added later).- OpenSSH rejects private key files with CRLF line endings, so SSHObj always writes them with LF, whatever the platform.
- Secrets are wiped from memory on a best-effort basis; Delphi strings and dynamic arrays cannot guarantee it completely.
- The demo project (SSHDemo) exercises the whole API against containers generated by OpenSSH and OpenSSL.
How to use the class
Everything is exposed through the static methods of TSSHKeyHandler. Keys are exchanged through the records TSSHPublicKey and TSSHPrivateKey, whose fields are hexadecimal strings (modulus, exponent, EC point, private exponent or seed, RSA primes, comment).
Here is an example of code.
uses SSHObj;
var
K: TSSHPrivateKey;
begin
// Generate an Ed25519 key pair in OpenSSH format, protected by a passphrase
TSSHKeyHandler.GenerateOpenSSHKeyFiles(ktED25519, 'my passphrase', 'me@host',
'id_ed25519', 'id_ed25519.pub');
// RSA works the same way; the last two arguments are bcrypt rounds and key size
TSSHKeyHandler.GenerateOpenSSHKeyFiles(ktRSA, '', 'me@host', 'id_rsa', 'id_rsa.pub', 16, 3072);
// Read a key (raises ECryptoPack if the passphrase is wrong or missing)
if TSSHKeyHandler.FromSSHPrivateKeyFile(K, 'my passphrase', 'id_ed25519') then
Writeln(K.Algorithm, ' ', K.Size, ' bits, ', K.Comment);
// Change the passphrase (an empty passphrase writes the key in clear)
TSSHKeyHandler.ToSSHPrivateKeyFile(K, 'new passphrase', 'id_ed25519_new');
// SHA256 fingerprint, as printed by ssh-keygen -l
Writeln(TSSHKeyHandler.FingerPrintKeyFile('id_ed25519.pub'));
// PKCS#8 + PEM public key + ssh public key, for OpenSSL
TSSHKeyHandler.ToKeyFiles(ktECDSA_P256, 0, 'pwd', 'key.pem', 'key.pub', 'key.pub.pem');
end;
Conclusion
Understanding how SSH crypto containers operate gives you deeper control over your infrastructure's security posture. By leveraging encrypted containers via SSHObj from your applications, you ensure that your credentials remain safe even if storage boundaries are compromised.
Are you currently using file-based SSH keys, or have you transitioned to hardware tokens and certificate-based authorities in your environment?
Bernard Roussely
This blog post has not received any comments yet.
All Blog Posts | Next Post | Previous Post