openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out key.pemopenssl pkey -in key.pem -pubout -out pub.pemopenssl pkey -in key.pem -text -noout # inspect curve and key material# SEC1 container: "BEGIN EC PRIVATE KEY"openssl ecparam -name prime256v1 -genkey -noout -out legacy.pem # convert legacy SEC1 → modern PKCS#8openssl pkey -in legacy.pem -out key-pkcs8.pemopenssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 \ -aes-256-cbc -out key-enc.pem # prompts for a passphraseHow it works
genpkeywrites PKCS#8 (BEGIN PRIVATE KEY) — the format Node.js, Go, Python and WebCrypto all read without conversion.-pkeyopt ec_paramgen_curve:acceptsP-256,P-384,P-521andsecp256k1;ecparam -namewants the OpenSSL spellingprime256v1.pkey -puboutderives the SPKI public key — the private key file contains everything needed.openssl ecparam -list_curvesprints every curve name this build supports — the quickest answer to "what do I type afterec_paramgen_curve:".
Gotchas
ecparam -genkeywithout-nooutprepends anEC PARAMETERSblock to the file — some parsers choke on it. The moderngenpkeyform avoids the issue entirely.BEGIN EC PRIVATE KEY(SEC1) andBEGIN PRIVATE KEY(PKCS#8) are different containers for the same key. WebCrypto accepts only PKCS#8 — convert withopenssl pkeyrather than editing headers.- P-256 has three names that all mean the same curve: P-256 (NIST), prime256v1 (OpenSSL/X9.62), secp256r1 (SEC). But secp256k1 is a genuinely different curve, not a fourth alias.