OPENSSL-PKEYUTL(1ossl)

πŸ“¦
version 15.1 Β· 2 versions
, in freebsd 15.1

πŸ’– OPENSSL-PKEYUTL πŸ’– (that's openssl-pkeyutl, section 1ossl, from the OpenSSL fam β€” hey bestie, welcome to the asymmetric key command πŸŽ€)

okay so. sit down, grab a strawberry drink πŸ“, because this one is the low-level asymmetric key command and it does a LOT. no cap.


✨ NAME ✨

openssl-pkeyutl β€” the asymmetric key command. that's it, that's the name, she's iconic πŸ‘‘


🌸 SYNOPSIS 🌸

openssl pkeyutl [-help] [-in file] [-rawin] [-digest algorithm] [-out
file] [-secret file] [-sigfile file] [-inkey filename|uri] [-keyform
DER|PEM|P12|ENGINE] [-passin arg] [-pubin] [-certin] [-rev] [-sign]
[-verify] [-verifyrecover] [-encrypt] [-decrypt] [-derive] [-peerkey
file] [-peerform DER|PEM|P12|ENGINE] [-encap] [-decap] [-kdf algorithm]
[-kdflen length] [-kemop mode] [-pkeyopt opt:value] [-pkeyopt_passin
opt[:passarg]] [-hexdump] [-asn1parse] [-engine id] [-engine_impl]
[-rand files] [-writerand file] [-provider name] [-provider-path path]
[-provparam [name:]key=value] [-propquery propq] [-config configfile]

like, look at her. a whole menu 🫢


πŸ¦‹ DESCRIPTION πŸ¦‹

so this command does low-level operations on asymmetric keys β€” public or private β€” using any algorithm that's supported. any of them. she's versatile like that πŸ’…

and the default? if you don't say what you want, she assumes you want the signing operation (that's the -sign option, keep that in your back pocket). main character default energy, honestly.


πŸŽ€ OPTIONS πŸŽ€

okay this is the long part. buckle up, we're going through all of them, no skipping, i promise πŸ™ˆ

  • -help β€” prints out a usage message. the classic "wait what do i do" button ✨
  • -in file β€” tells her which input file to read data from. if you don't specify it, she reads from standard input. default behavior, love that for us.
  • -rawin β€” this one says: the signature (or verification) input data is raw, like, genuinely not hashed by any message digest algorithm at all. except with EdDSA, you can pick a digest algorithm with the -digest option. for signature algorithms like RSA, DSA and ECDSA, the default digest algorithm is SHA256. for SM2, it's SM3. πŸ”₯

this option can only be used with -sign and -verify. for EdDSA (that's the Ed25519 and Ed448 algorithms) this option is implied since OpenSSL 3.5, and it was required in earlier versions. so, yeah, glow-up timeline.

also: the -digest option implies -rawin since OpenSSL 3.5. iykyk.

  • -digest algorithm β€” can only be used with -sign and -verify (same club as -rawin, they're besties). it specifies the digest algorithm used to hash the input data before signing or verifying it with the input key. you could omit this if the signature algorithm doesn't need the input preprocessed through a pluggable hash function before signing β€” EdDSA is the example here. but if you omit it, and the signature algorithm does need one, and you gave -rawin, then a default value gets used (peep -rawin for the deets πŸ‘€). and if this option is present, then -rawin is implied since OpenSSL 3.5, and was required in earlier versions.
🫧 heads up bestie: at this time, HashEdDSA (the ph or "prehash" variant of EdDSA) is not supported, so -digest cannot be used with EdDSA. periodt.
  • -out filename β€” the output filename to write to. or standard output, by default. her choice πŸ’…
  • -secret filename β€” the shared-secret output filename, for when you're doing encapsulation via -encap or decapsulation via -decap. heads up: -encap also produces a separate (public) ciphertext output, which by default goes to standard output β€” but since it's binary non-text data, you'll typically also redirect that to a file picked with -out. two outputs, one mechanism, she's doing the most 🌈
  • -sigfile file β€” signature file. required and only allowed for -verify operations. no sneaking it anywhere else πŸ”’
  • -inkey filename|uri β€” the input key. by default it should be a private key (spoiler: -pubin changes that).
  • -keyform DER|PEM|P12|ENGINE β€” the key format. unspecified by default. see openssl-format-options(1) for the details πŸ“š
  • -passin arg β€” the input key password source. for more info about the format of arg, see openssl-passphrase-options(1) πŸ—οΈ
  • -pubin β€” by default a private key is read from the key input. with this option, a public key is read instead. and if the input contains no public key but it does have a private key, then its public part is used. neat little fallback, love her for that 🫢
  • -certin β€” the input is a certificate containing a public key. that's the whole vibe πŸŽ“
  • -rev β€” reverses the order of the input buffer. useful for some libraries (like CryptoAPI) that represent the buffer in little-endian format. πŸŒ€ cannot be used in conjunction with -rawin β€” pick a lane, bestie.
  • -sign β€” signs the input data and outputs the signed result. requires a private key πŸ”. using a message digest operation along with this is recommended when applicable β€” see the -rawin and -digest options for the details. otherwise, the input data given with -in is assumed to already be a digest, but that may then require an additional -pkeyopt "digest:"md in some cases (e.g. RSA with the default PKCS#1 padding mode). and even for other algorithms like ECDSA β€” where the additional -pkeyopt option doesn't affect the signature output β€” it's still recommended, because it lets her check that the input length is consistent with the intended digest. little safety check, we stan ✨
  • -verify β€” verifies the input data against the signature given with the -sigfile option, and tells you if verification succeeded or failed βœ…βŒ. the input data given with -in is assumed to be a hash value unless -rawin is specified or implied. with raw data, when a digest algorithm is applicable β€” though it may be inferred from the signature or take a default value β€” it should also be specified. specify it. be a good citizen πŸ’…
  • -verifyrecover β€” verifies the given signature and outputs the recovered data (that's the signature payload). for example, with RSA PKCS#1 the recovered data is the EMSA-PKCS-v1_5 DER encoding of the digest algorithm OID and value, as specified in RFC8017 Section 9.2 (https://datatracker.ietf.org/doc/html/rfc8017#section-9.2). πŸ“œ
πŸ™ˆ note note note: here the input given with -in is not a signature input (like it is with -sign and -verify) β€” it's a signature output value, typically produced using -sign. flip your brain around, it's fine.

this option is available only for use with RSA keys πŸ”‘.

  • -encrypt β€” encrypts the input data using a public key πŸ”’βœ¨
  • -decrypt β€” decrypts the input data using a private key πŸ”“
  • -derive β€” derives a shared secret using your own private (EC)DH key and the peer key. team work makes the dream work 🀝
  • -peerkey file β€” file containing the peer public or private (EC)DH key to use with the key derivation (agreement) operation. its type must match the type of the own private key given with -inkey. mismatched keys? not on her watch 🚫
  • -peerform DER|PEM|P12|ENGINE β€” the peer key format. unspecified by default. see openssl-format-options(1) for details πŸ“–
  • -encap β€” uses a Key Encapsulation Mechanism (KEM) to encapsulate a shared-secret to a peer's public key 🎁. the encapsulated result (or ciphertext β€” non-text binary data, beware) is written to standard output by default, or else to the file specified with -out. the -secret option must also be provided, to specify the output file for the derived shared-secret value generated in the encapsulation process. encapsulation is supported with a number of public key algorithms β€” currently: ML-KEM, X25519, X448, and EC. the ECX and EC algorithms use the RFC9180 (https://www.rfc-editor.org/rfc/rfc9180) DHKEM construction. encapsulation is also supported with RSA keys via the RSASVE construction πŸ”₯
πŸ’€ at the API level, encapsulation and decapsulation are also supported for a few hybrid ECDHE (no DHKEM) plus ML-KEM algorithms β€” but those are intended primarily for use with TLS and should not be used standalone. and anyway, there are no standard public and private key formats for the hybrid algorithms, so it's not possible to provide the required key material. so, like, don't even try.
  • -decap β€” decodes an encapsulated secret, using a -private key, to derive the same shared-secret as the one you got when the secret was encapsulated to the corresponding public key πŸ”“πŸŽ. the encapsulated secret is by default read from standard input, or else from the file specified with -in. the derived shared-secret is written to the file specified with the -secret option β€” which must also be provided. decapsulation is supported with a number of public key algorithms, currently: ML-KEM, X25519, X448, and EC. the ECX and EC algorithms use the RFC9180 (https://www.rfc-editor.org/rfc/rfc9180) DHKEM construction. decapsulation is also supported with RSA keys via the RSASVE construction ✨
  • -kemop mode β€” used with the -encap/-decap commands, and specifies the KEM mode specific to the key algorithm when there is no default way to encapsulate and decapsulate shared secrets with the chosen key type. all the supported algorithms presently support only their default mode, and this option β€” though available β€” is not required. so she exists, but she's chilling πŸ’…
  • -kdf algorithm β€” use key derivation function algorithm. the supported algorithms are at present TLS1-PRF and HKDF. πŸ“Œ note: additional parameters and the KDF output length will normally have to be set for this to work. see EVP_PKEY_CTX_set_hkdf_md(3) and EVP_PKEY_CTX_set_tls1_prf_md(3) for the supported string parameters of each algorithm. read the docs, bestie, they're there for you πŸ“š
  • -kdflen length β€” sets the output length for the KDF. πŸ“
  • -pkeyopt opt:value β€” public key options, specified as opt:value. see NOTES below for more details πŸ‘€
  • -pkeyopt_passin opt[:passarg] β€” lets you read a public key option opt from stdin or a password source. if you only specify opt, the user gets prompted to enter a password on stdin. alternatively you can specify passarg, which can be any value supported by openssl-passphrase-options(1) πŸ—οΈπŸ«Ά
  • -hexdump β€” hex dumps the output data. for when you want to see the vibes in hex πŸ”’
  • -asn1parse β€” parses the ASN.1 output data to check its DER encoding, and prints any errors. 🧐 when combined with the -verifyrecover option, this may be useful in case an ASN.1 DER-encoded structure had been signed directly (without hashing it), and when checking a signature in PKCS#1 v1.5 format, which has a DER encoding. niche but iconic.
  • -engine id β€” see "Engine Options" in openssl(1). ⚠️ this option is deprecated. she's old news, but we still list her out of respect.
  • -engine_impl β€” when used with the -engine option, it specifies to also use engine id for crypto operations. πŸ”§
  • -rand files, -writerand file β€” see "Random State Options" in openssl(1) for details. 🎲
  • -provider name, -provider-path path, -provparam [name:]key=value, -propquery propq β€” see "Provider Options" in openssl(1), provider(7), and property(7). the whole provider squad, all listed together πŸ’œ
  • -config configfile β€” see "Configuration Option" in openssl(1). βš™οΈ

πŸ’… NOTES πŸ’…

ok so real talk, bestie: what operations and options actually work depends on the key algorithm and its implementation. The OpenSSL operations and options are listed below, mwah πŸ’–

Unless it says otherwise somewhere, the -pkeyopt option supports the "digest:"alg argument for all public-key types, and it says which digest is in use for the signing and verification operations. That alg value should be a digest name like the ones you'd pass to the EVP_get_digestbyname() function, for example sha256. Heads up though: this value is not used to hash your input data. It's used (by some algorithms) for sanity-checking the lengths of data passed in, and for creating the structures that make up the signature (e.g. DigestInfo in RSASSA PKCS#1 v1.5 signatures). iykyk 🫢

So like, for instance: if the value of the -pkeyopt option "digest" argument is sha256, then your signature or verification input should be the 32-bytes-long binary value of the SHA256 hash function output. No cap, it's just gotta line up. ✨

Unless -rawin is used or implied, this command does not hash the input data β€” it uses the data directly as input to the signature algorithm. Depending on the key type, signature type, and mode of padding, the maximum sensible lengths of input data differ 😭

  • With RSA, the signed data cannot be longer than the key modulus.
  • In the case of ECDSA and DSA, the data should not be longer than the field size, otherwise it will be silently truncated to the field size πŸ’€ (yes, silently, we don't love that either).
  • In any event the input size must not be larger than the largest supported digest output size EVP_MAX_MD_SIZE, which currently is 64 bytes.

πŸ”₯ RSA ALGORITHM πŸ”₯

The RSA algorithm generally supports the encrypt, decrypt, sign, verify and verifyrecover operations. However β€” some padding modes only support a subset of these, so vibe check your padding first πŸ‘‘ The following additional pkeyopt values are supported:

  • rsa_padding_mode:mode β€” sets the RSA padding mode. Acceptable values for mode are pkcs1 for PKCS#1 padding, none for no padding, oaep for OAEP mode, x931 for X9.31 mode and pss for PSS.

In PKCS#1 padding, if the message digest is not set, then the supplied data is signed or verified directly instead of using a DigestInfo structure. If a digest is set, then the DigestInfo structure is used and its length must correspond to the digest type.

🚨 Note, for pkcs1 padding, as a protection against the Bleichenbacher attack, the decryption will not fail in case of padding check failures. So if you actually wanna know, use none and manually inspect the decrypted message to verify if the decrypted value has correct PKCS#1 v1.5 padding. Yeah. It's giving "security tradeoffs". πŸ™ˆ

For oaep mode only encryption and decryption is supported.

For x931, if the digest type is set it's used to format the block data, otherwise the first byte is used to specify the X9.31 digest ID. sign, verify and verifyrecover can be performed in this mode.

For pss mode only sign and verify are supported, and the digest type must be specified. periodt.

  • rsa_pss_saltlen:len β€” for pss mode only, this specifies the salt length πŸ§‚ Three special values are supported: digest sets the salt length to the digest length, max sets the salt length to the maximum permissible value. When verifying, auto causes the salt length to be automatically determined based on the PSS block structure. Slay.
  • rsa_mgf1_md:digest β€” for PSS and OAEP padding, sets the MGF1 digest. If the MGF1 digest is not explicitly set in PSS mode, then the signing digest is used.
  • rsa_oaep_md:digest β€” sets the digest used for the OAEP hash function. If not explicitly set, then SHA256 is used. ✨
  • rsa_pkcs1_implicit_rejection:flag β€” disables (when set to 0) or enables (when set to 1) the use of implicit rejection with PKCS#1 v1.5 decryption. When enabled (the default), as a protection against the Bleichenbacher attack, the library will generate a deterministic random plaintext that it will return to the caller in case of padding check failure. When disabled, it's the callers' responsibility to handle the returned errors in a side-channel free manner. 😭 that's a lot of responsibility, bestie.

🌸 RSA-PSS ALGORITHM 🌸

The RSA-PSS algorithm is like RSA but on a restricted diet β€” it's a restricted version of the RSA algorithm which only supports the sign and verify operations with PSS padding. The following additional -pkeyopt values are supported:

  • rsa_padding_mode:mode, rsa_pss_saltlen:len, rsa_mgf1_md:digest β€” these have the same meaning as the RSA algorithm, with some additional restrictions. The padding mode can only be set to pss, which is the default value.

If the key has parameter restrictions, then the digest, MGF1 digest and salt length are set to the values specified in the parameters. The digest and MGF1 digest cannot be changed, and the salt length cannot be set to a value less than the minimum restriction. πŸ”’ no wiggle room, it's giving locked-in.


πŸ¦‹ DSA ALGORITHM πŸ¦‹

The DSA algorithm supports signing and verification operations only. Currently there are no additional -pkeyopt options other than digest. The SHA256 digest is assumed by default. simple, clean, minimal β€” honestly iconic πŸ’…


πŸŽ€ DH ALGORITHM πŸŽ€

The DH algorithm only supports the derivation operation and has no additional -pkeyopt options. That's it. That's the whole section. short king 🫢


⭐ EC ALGORITHM ⭐

The EC algorithm supports sign, verify and derive operations. The sign and verify operations use ECDSA, and derive uses ECDH. SHA256 is assumed by default for the -pkeyopt digest option. cute, consistent, we love her πŸ’–


🌈 X25519 AND X448 ALGORITHMS 🌈

The X25519 and X448 algorithms support key derivation only. Currently there are no additional options. ✨


🧚 SLH-DSA ALGORITHMS 🧚

The SLH-DSA algorithms (SLH-DSA-SHA2-128s, SLH-DSA-SHA2-128f, SLH-DSA-SHA2-192s, SLH-DSA-SHA2-192f, SLH-DSA-SHA2-256s, SLH-DSA-SHA2-256f) are post-quantum signature algorithms πŸ‘‘ queen behaviour, honestly. When using SLH-DSA with pkeyutl, the following options are available:

  • -sign β€” sign the input data using an SLH-DSA private key. For example:
$ openssl pkeyutl -sign -in file.txt -inkey slhdsa.pem -out sig
  • -verify β€” verify the signature using an SLH-DSA public key. For example:
$ openssl pkeyutl -verify -in file.txt -inkey slhdsa.pem -sigfile sig

See EVP_PKEY-SLH-DSA(7) and EVP_SIGNATURE-SLH-DSA(7) for additional details about the SLH-DSA algorithm and its implementation. πŸ¦‹


πŸ’œ ML-DSA-44, ML-DSA-65 AND ML-DSA-87 ALGORITHMS πŸ’œ

The ML-DSA algorithms are post-quantum signature algorithms that support signing and verification of "raw" messages. No preliminary hashing is performed β€” none, zero, nada. When using ML-DSA with pkeyutl, the following options are available:

  • -sign β€” sign the input data using an ML-DSA private key. For example:
$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig
  • -verify β€” verify the signature using an ML-DSA public key. For example:
$ openssl pkeyutl -verify -in file.txt -inkey mldsa65.pem -sigfile sig
  • -pkeyopt opt:value β€” additional options for ML-DSA signing and verification:

message-encoding:value

Specifies the message encoding mode used for signing. This controls how the input message is processed before signing. Valid values are described in EVP_SIGNATURE-ML-DSA(7). For example:

$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt message-encoding:1

test-entropy:value

Specifies a test entropy value for deterministic signing. For example:

$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt test-entropy:abcdefghijklmnopqrstuvwxyz012345

hextest-entropy:value

Specifies a test entropy value in hex format. For example:

$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt hextest-entropy:000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

deterministic:value

Enables deterministic signing. For example:

$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt deterministic:1

mu:value

Specifies the mu parameter. For example:

$ echo -n "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" >file.txt
$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt mu:1
  • context-string:string β€” specifies a context string for both signing and verification operations. The context string must be the same for verification to succeed, fr fr β€” mismatch and it's over 😭 For example:
$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt context-string:mycontext
$ openssl pkeyutl -verify -in file.txt -inkey mldsa65.pem -sigfile sig -pkeyopt context-string:mycontext
  • hexcontext-string:string β€” specifies a context string in hex format, allowing binary control values. For example:
$ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt hexcontext-string:6d79636f6e74657874

The signing operation supports a deterministic:bool option, with bool set to 1 if a deterministic signature is to be generated with a fixed all zero random input. By default, or if the bool is 0, a random entropy value is used. A deterministic result can also be obtained by specifying an explicit entropy value via the hextest-entropy:value parameter.

🚨 Deterministic ML-DSA signing should only be used in tests. seriously bestie, tests only. πŸ’€

See EVP_SIGNATURE-ML-DSA(7) for additional details about the ML-DSA algorithms and their implementation. πŸŽ€


πŸ“ ML-KEM-512, ML-KEM-768 AND ML-KEM-1024 ALGORITHMS πŸ“

The ML-KEM algorithms support encapsulation and decapsulation only. The encapsulation operation supports a hexikme:entropy option, with entropy the 64 hexadecimal digit encoding of a 32-byte value.

🚨 This should only be used in tests β€” known or leaked values of the option may compromise the generated shared secret. do not do this in prod, I'm begging πŸ™ˆ

See EVP_KEM-ML-KEM(7) for additional detail.


πŸ’€ ED25519 AND ED448 ALGORITHMS πŸ’€

These algorithms only support signing and verifying. OpenSSL only implements the "pure" variants of these algorithms, so raw data can be passed directly to them without hashing them first β€” no preprocessing, we go straight in πŸ”₯

OpenSSL only supports "oneshot" operation with these algorithms. This means the entire file to be signed/verified must be read into memory before processing it. Signing or verifying very large files should be avoided. Additionally, the size of the file must be known for this to work. If the size of the file cannot be determined (for example if the input is stdin), then the sign or verify operation will fail. 😭 so like… no piping, bestie. know your file size or perish.

🌸 SM2 🌸

okay so SM2 is that girl β€” she does it all: sign, verify, encrypt and decrypt. full main-character energy, no notes πŸ’…

but bestie, for sign and verify she's a little picky: you must pass in a Distinguishing ID string. no ID, no signature. here are the -pkeyopt values she accepts:

  • distid:string β€” sets the ID string used for SM2 sign/verify. and listen 🫢 while you're verifying an SM2 signature, the ID string has to be the exact same one you used when you signed the data. if it's not, verification just fails, periodt. 😭
  • hexdistid:hex_string β€” same deal, same rule (matching ID or verification fails, no cap), except this one takes the ID string as a valid hexadecimal value. hex girls stay winning πŸ’œ

✨ EXAMPLES ✨

sign some data using a private key:

 openssl pkeyutl -sign -in file -inkey key.pem -out sig

recover the signed data (e.g. if an RSA key is used):

 openssl pkeyutl -verifyrecover -in sig -inkey key.pem

verify the signature (e.g. a DSA key):

 openssl pkeyutl -verify -in file -sigfile sig -inkey key.pem

sign data using a message digest value (this is currently only valid for RSA, fyi):

 openssl pkeyutl -sign -in file -inkey key.pem -out sig -pkeyopt digest:sha256

derive a shared secret value:

 openssl pkeyutl -derive -inkey key.pem -peerkey pubkey.pem -out secret

hexdump 48 bytes of TLS1 PRF using digest SHA256 and shared secret and seed consisting of the single byte 0xFF:

 openssl pkeyutl -kdf TLS1-PRF -kdflen 48 -pkeyopt md:SHA256 \
    -pkeyopt hexsecret:ff -pkeyopt hexseed:ff -hexdump

derive a key using scrypt where the password is read from command line:

 openssl pkeyutl -kdf scrypt -kdflen 16 -pkeyopt_passin pass \
    -pkeyopt hexsalt:aabbcc -pkeyopt N:16384 -pkeyopt r:8 -pkeyopt p:1

derive using the same algorithm, but read key from environment variable MYPASS:

 openssl pkeyutl -kdf scrypt -kdflen 16 -pkeyopt_passin pass:env:MYPASS \
    -pkeyopt hexsalt:aabbcc -pkeyopt N:16384 -pkeyopt r:8 -pkeyopt p:1

sign some data using an SM2(7) private key and a specific ID:

 openssl pkeyutl -sign -in file -inkey sm2.key -out sig -rawin -digest sm3 \
    -pkeyopt distid:someid

verify some data using an SM2(7) certificate and a specific ID:

 openssl pkeyutl -verify -certin -in file -inkey sm2.cert -sigfile sig \
    -rawin -digest sm3 -pkeyopt distid:someid

decrypt some data using a private key with OAEP padding using SHA256:

 openssl pkeyutl -decrypt -in file -inkey key.pem -out secret \
    -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256

create an ML-DSA key pair and sign data with a specific context string:

  $ openssl genpkey -algorithm ML-DSA-65 -out mldsa65.pem
  $ openssl pkeyutl -sign -in file.txt -inkey mldsa65.pem -out sig -pkeyopt context-string:example

verify a signature using ML-DSA with the same context string:

  $ openssl pkeyutl -verify -in file.txt -inkey mldsa65.pem -sigfile sig -pkeyopt context-string:example

generate an ML-KEM key pair and use it for encapsulation:

  $ openssl genpkey -algorithm ML-KEM-768 -out mlkem768.pem
  $ openssl pkey -in mlkem768.pem -pubout -out mlkem768_pub.pem
  $ openssl pkeyutl -encap -inkey mlkem768_pub.pem -pubin -out ciphertext -secret shared_secret.bin

decapsulate a shared secret using an ML-KEM private key:

  $ openssl pkeyutl -decap -inkey mlkem768.pem -in ciphertext -secret decapsulated_secret.bin

create an SLH-DSA key pair and sign data:

  $ openssl genpkey -algorithm SLH-DSA-SHA2-128s -out slh-dsa.pem
  $ openssl pkeyutl -sign -in file.txt -inkey slh-dsa.pem -out sig

verify a signature using SLH-DSA:

  $ openssl pkeyutl -verify -in file.txt -inkey slh-dsa.pem -sigfile sig

πŸ¦‹ SEE ALSO πŸ¦‹

the friend group you should also go follow, fr fr:

(yes that trailing comma is canon, don't come for me πŸ™ˆ)


πŸŽ€ HISTORY πŸŽ€

okay lore drop, gather round 🧚

  • since OpenSSL 3.5, the -digest option implies -rawin, and those two options are no longer required when you're signing or verifying with an Ed25519 or Ed448 key. streamlining era πŸ’…
  • also since OpenSSL 3.5, the -kemop option is no longer required for any of the supported algorithms β€” the only supported mode is now the default. she simplified her life, we love that.
  • the -engine option was deprecated in OpenSSL 3.0. rip bozo, it's giving legacy 😭

Copyright 2006-2026 The OpenSSL Project Authors. All Rights Reserved.

Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.


aaaand that's the whole page bestie, you made it to the end 🫢✨ this gorgeous thing is from version 3.5.6, dated 2026-04-07 β€” go forth and pkeyutl responsibly, mwah πŸ’–πŸŒΈ

πŸ“¦ where this page comes from