Skip to content
ecdsa.com

Generate a key pair · Rust

How to generate an ECDSA key pair in Rust

To generate an ECDSA key pair in Rust, call SigningKey::generate() from RustCrypto's p256 crate — it draws from the operating system's random source — and export the halves with to_pkcs8_pem and to_public_key_pem. The resulting files are the standard PKCS#8 and SPKI PEMs that OpenSSL, Go, Python and every other recipe on this site read directly.

Tested with p256 (RustCrypto) 0.14.0 (Rust 1.99.1, ecdsa 0.17.0, getrandom 0.4.3) and OpenSSL 3.5.7 (openssl pkey cross-check).

Cargo.toml — p256 with ECDSA and PEM support
[dependencies]
p256 = { version = "=0.14.0", features = ["ecdsa", "pem"] }
src/main.rs — a P-256 key pair written as PEM files
use std::io::Write;
use std::os::unix::fs::OpenOptionsExt;
use p256::ecdsa::SigningKey;
use p256::elliptic_curve::Generate;
use p256::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// OS randomness (getrandom); SigningKey::random(&mut rng) is deprecated since ecdsa 0.17
let signing_key = SigningKey::generate();
let private_pem = signing_key.to_pkcs8_pem(LineEnding::LF)?; // Zeroizing<String>, wiped on drop
let public_pem = signing_key.verifying_key().to_public_key_pem(LineEnding::LF)?;
// create_new refuses to overwrite a key; mode 0o600 keeps it owner-only (Unix)
std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open("key.pem")?
.write_all(private_pem.as_bytes())?;
std::fs::write("pub.pem", &public_pem)?;
print!("{public_pem}");
Ok(())
}

How it works

  1. SigningKey::generate() comes from the Generate trait (re-exported as p256::elliptic_curve::Generate) and uses getrandom, i.e. the OS CSPRNG; generate_from_rng accepts an explicit RNG for special cases.
  2. to_pkcs8_pem writes BEGIN PRIVATE KEY and returns Zeroizing<String>, which wipes the key text from memory when it goes out of scope.
  3. verifying_key().to_public_key_pem writes the SPKI BEGIN PUBLIC KEY block; both functions take an explicit LineEnding.
  4. OpenOptions with create_new(true) and mode(0o600) creates the private key file atomically with owner-only permissions; the run confirms the mode and that OpenSSL derives the same public key.

Gotchas

  • SigningKey::random(&mut OsRng) from older tutorials no longer fits: random is deprecated since ecdsa 0.17 in favour of the Generate trait, and rand_core 0.10 has no OsRng (the OS generator is now getrandom::SysRng). Keep all RustCrypto crates on the same release line — types from p256 0.13 and 0.14 do not mix.
  • gen is a reserved keyword in Rust 2024, so let gen = SigningKey::generate(); does not compile. Pick another variable name.
  • std::os::unix::fs::OpenOptionsExt exists only on Unix; on Windows drop .mode() and protect the file with ACLs.
  • For secp256k1, use the sibling crate k256 with the same API — p256 keys cannot be converted to another curve.

Related recipes