Crypt::PK::X25519(3)
π Crypt::PK::X25519 β the manual page, but make it make sense
okay so bestie, welcome to Crypt::PK::X25519, a page in the User Contributed Perl Documentation section of the manual. It's the Perl module for asymmetric cryptography based on X25519 β yes, that Curve25519 bestie, the one everybody's obsessed with π
β¨ NAME β¨
Crypt::PK::X25519 β Asymmetric cryptography based on X25519. That's the whole vibe. Public/private key pairs, shared secrets, the works. Slay.
π SYNOPSIS π
okay so here's your first group-chat demo β Alice and Bob doing the classic shared-secret handshake, no cap:
use Crypt::PK::X25519;
my $alice = Crypt::PK::X25519->new->generate_key;
my $bob = Crypt::PK::X25519->new->generate_key;
my $alice_secret = $alice->shared_secret($bob);
my $bob_secret = $bob->shared_secret($alice);
die "ERROR" unless $alice_secret eq $bob_secret;
like, they both compute the same secret and if they don't, die. iconic.
Then loading a key from hex, because sometimes you already have the key and you're just importing it:
#Load key
my $pk = Crypt::PK::X25519->new;
my $pk_hex = "EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41";
$pk->import_key_raw(pack("H*", $pk_hex), "public");
my $sk = Crypt::PK::X25519->new;
my $sk_hex = "002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651";
$sk->import_key_raw(pack("H*", $sk_hex), "private");
And here's the full glow-up tour of key generation + every export format, all at once:
#Key generation
my $pk = Crypt::PK::X25519->new->generate_key;
my $private_der = $pk->export_key_der('private');
my $public_der = $pk->export_key_der('public');
my $private_pem = $pk->export_key_pem('private');
my $public_pem = $pk->export_key_pem('public');
my $private_raw = $pk->export_key_raw('private');
my $public_raw = $pk->export_key_raw('public');
my $private_jwk = $pk->export_key_jwk('private');
my $public_jwk = $pk->export_key_jwk('public');
π¦ DESCRIPTION π¦
Since: CryptX-0.067 β yes, she's been around, and she's been serving since then π
πΈ METHODS πΈ
π new
okay so new is your main character constructor. It can make you an empty object, or slurp a key straight out of a file or a buffer. Look:
my $source = Crypt::PK::X25519->new();
$source->generate_key;
my $public_der = $source->export_key_der('public');
my $pub = Crypt::PK::X25519->new(\$public_der);
my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
my $priv = Crypt::PK::X25519->new(\$private_pem, 'secret');
Passing $filename or "\$buffer" to new is equivalent β both forms immediately import the key material into the new object. fr fr, same thing, no difference, pick your fave π
β¨ generate_key β¨
She uses the bundled chacha20 PRNG via rng_make_prng(). The exact OS entropy source is handled by the underlying LibTomCrypt RNG setup β that's the tea, we don't gotta worry about it.
Returns the object itself (for chaining), so you can go ->new->generate_key and keep the party going π
$pk->generate_key;
π§ import_key π§
Loads a private or public key in DER or PEM format. Also eats hashrefs, we'll get there.
my $source = Crypt::PK::X25519->new();
$source->generate_key;
my $public_der = $source->export_key_der('public');
my $pub = Crypt::PK::X25519->new();
$pub->import_key(\$public_der);
my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
my $priv = Crypt::PK::X25519->new();
$priv->import_key(\$private_pem, 'secret');
The same method also accepts filenames instead of buffers. convenient king π
Loading private or public keys from a Perl HASH:
$pk->import_key($hashref);
# the $hashref is either a key exported via key2hash
$pk->import_key({
curve => "x25519",
pub => "EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41",
priv => "002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651",
});
# or a hash with items corresponding to JWK (JSON Web Key)
$pk->import_key({
kty => "OKP",
crv => "X25519",
d => "AC-T0Qulco2N2OlSdyHaujJhwLsb7957S72sYx1FRlE",
x => "6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE",
});
π Supported key formats π
all formats can be loaded from a file:
my $pk = Crypt::PK::X25519->new($filename);
or from a buffer containing the key:
my $pk = Crypt::PK::X25519->new(\$buffer_with_key);
okay now the lineup, one by one, all of them, no skips:
- X25519 private keys in PEM format
-----BEGIN X25519 PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIAAvk9ELpXKNjdjpUnch2royYcC7G+/ee0u9rGMdRUZR
-----END X25519 PRIVATE KEY-----
- X25519 private keys in password protected PEM format
-----BEGIN X25519 PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-CBC,DEEFD3D6B714E75A
dfFWP5bKn49aZ993NVAhQQPdFWgsTb4j8CWhRjGBVTPl6ITstAL17deBIRBwZb7h
pAyIka81Kfs=
-----END X25519 PRIVATE KEY-----
- X25519 public keys in PEM format
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VuAyEA6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE=
-----END PUBLIC KEY-----
- PKCS#8 private keys
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VuBCIEIAAvk9ELpXKNjdjpUnch2royYcC7G+/ee0u9rGMdRUZR
-----END PRIVATE KEY-----
- PKCS#8 encrypted private keys
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGHMEsGCSqGSIb3DQEFDTA+MCkGCSqGSIb3DQEFDDAcBAiS0NOFZmjJswICCAAw
DAYIKoZIhvcNAgkFADARBgUrDgMCBwQIGd40Hdso8Y4EONSRCTrqvftl9hl3zbH9
2QmHF1KJ4HDMdLDRxD7EynonCw2SV7BO+XNRHzw2yONqiTybfte7nk9t
-----END ENCRYPTED PRIVATE KEY-----
- X25519 private keys in JSON Web Key (JWK) format
See <https://www.rfc-editor.org/rfc/rfc8037>
{
"kty":"OKP",
"crv":"X25519",
"x":"6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE",
"d":"AC-T0Qulco2N2OlSdyHaujJhwLsb7957S72sYx1FRlE",
}
BEWARE: For JWK support you need to have JSON module installed. like, mandatory bestie, no JSON module no JWK π
- X25519 public keys in JSON Web Key (JWK) format
{
"kty":"OKP",
"crv":"X25519",
"x":"6ngG9yGoVwUSyPbvtOjWIMSaUp5N9eqnfexkb7HofkE",
}
BEWARE: For JWK support you need to have JSON module installed. same deal, install the module, thank me later π«Ά
π import_key_raw π
Imports a raw public/private key β and yes, it can load raw key data exported by export_key_raw. Round trip, bestie π
$pk->import_key_raw($key, 'public');
$pk->import_key_raw($key, 'private');
π export_key_der π
Returns the key as a binary DER-encoded string. bytes, not vibes.
my $private_der = $pk->export_key_der('private');
#or
my $public_der = $pk->export_key_der('public');
π export_key_pem π
Returns the key as a PEM-encoded string (ASCII). the pretty text one, with the -----BEGIN lines, you know the one β¨
my $private_pem = $pk->export_key_pem('private');
#or
my $public_pem = $pk->export_key_pem('public');
π Support for password protected PEM keys π
yes she can lock it up for you:
my $private_pem = $pk->export_key_pem('private', $password);
#or
my $private_pem = $pk->export_key_pem('private', $password, $cipher);
# supported ciphers: 'DES-CBC'
# 'DES-EDE3-CBC'
# 'SEED-CBC'
# 'CAMELLIA-128-CBC'
# 'CAMELLIA-192-CBC'
# 'CAMELLIA-256-CBC'
# 'AES-128-CBC'
# 'AES-192-CBC'
# 'AES-256-CBC' (DEFAULT)
AES-256-CBC is the DEFAULT bestie, the one she reaches for automatically π
β export_key_jwk β
Returns a JSON string, or a hashref if the optional second argument is true. your choice, both are valid π
Exports public/private keys as a JSON Web Key (JWK):
my $private_json_text = $pk->export_key_jwk('private');
#or
my $public_json_text = $pk->export_key_jwk('public');
Also exports public/private keys as a Perl HASH with JWK structure:
my $jwk_hash = $pk->export_key_jwk('private', 1);
#or
my $jwk_hash = $pk->export_key_jwk('public', 1);
BEWARE: For JWK support you need to have JSON module installed. again. yes. it's a whole thing π
π§ export_key_raw π§
Returns the raw key as a binary string. no wrapping, no decoration, just the raw bytes, she's lowkey minimal like that.
Export raw public/private key:
my $private_bytes = $pk->export_key_raw('private');
#or
my $public_bytes = $pk->export_key_raw('public');
π shared_secret π
Returns the shared secret as a binary string (raw bytes). this is THE method, the moment, the main event β the whole point of the module, honestly π«Ά
# Alice having her priv key $pk and Bob's public key $pkb
my $pk = Crypt::PK::X25519->new($priv_key_filename);
my $pkb = Crypt::PK::X25519->new($pub_key_filename);
my $shared_secret = $pk->shared_secret($pkb);
# Bob having his priv key $pk and Alice's public key $pka
my $pk = Crypt::PK::X25519->new($priv_key_filename);
my $pka = Crypt::PK::X25519->new($pub_key_filename);
my $shared_secret = $pk->shared_secret($pka); # same value as computed by Alice
see that comment? same value as computed by Alice. that's the magic. that's the tea. both sides get the identical secret without ever sending it over the wire. iconic behaviour, no notes π
π¦ is_private π¦
the vibe check for your key object:
my $rv = $pk->is_private;
# 1 .. private key loaded
# 0 .. public key loaded
# undef .. no key loaded
1 = private, 0 = public, undef = nothing loaded yet. three states, know them, love them π
πΈ key2hash πΈ
Returns a hashref with the key components, or undef if no key is loaded.
my $hash = $pk->key2hash;
# returns hash like this (or undef if no key loaded):
{
curve => "x25519",
# raw public key as a hexadecimal string
pub => "EA7806F721A8570512C8F6EFB4E8D620C49A529E4DF5EAA77DEC646FB1E87E41",
# raw private key as a hexadecimal string. undef if key is public only
priv => "002F93D10BA5728D8DD8E9527721DABA3261C0BB1BEFDE7B4BBDAC631D454651",
}
note the priv field: undef if the key is public only. don't go looking for a private key that isn't there bestie π
π OpenSSL interoperability π
yes, she plays nice with OpenSSL. crossover episode, we love to see it π€
# Generate a key with OpenSSL
# openssl genpkey -algorithm x25519 -out x25519_priv.pem
# openssl pkey -in x25519_priv.pem -pubout -out x25519_pub.pem
# Load the OpenSSL-generated key in CryptX
use Crypt::PK::X25519;
my $alice = Crypt::PK::X25519->new("x25519_priv.pem");
my $bob_pub = Crypt::PK::X25519->new("bob_x25519_pub.pem");
# Derive shared secret
my $shared_secret = $alice->shared_secret($bob_pub);
# Export CryptX key for OpenSSL
my $pem = $alice->export_key_pem('private');
# then: openssl pkey -in priv.pem -text -noout
so you can generate over there, load over here, derive that shared secret, and export right back. the crossover nobody asked for but everybody needed π₯
π SEE ALSO π
go read the lore, iykyk π
and that's the whole page bestie π this was perl v5.42.0 dropping on 2026-05-11, and the page is Crypt::PK::X25519(3) β now go forth and be cryptographically iconic πβ¨
π¦ where this page comes from
- opensuse-leap 16.0: perl-CryptX 0.89.0-bp160.1.1 Β· Artistic-1.0 OR GPL-1.0-or-later
- opensuse-leap 16.0: perl-CryptX 0.89.0-bp160.1.1 Β· Artistic-1.0 OR GPL-1.0-or-later
- opensuse-leap 16.0: perl-CryptX 0.89.0-bp160.1.1 Β· Artistic-1.0 OR GPL-1.0-or-later
- opensuse-leap 16.0: perl-CryptX 0.89.0-bp160.1.1 Β· Artistic-1.0 OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms