On Tue, Nov 22, 2022 at 11:09:07AM -0600, Nico Williams wrote: > > Not exactly, PKCS#8-based typing is used in d2i_PKCS8_PRIV_KEY_INFO() > > (for unencrypted PKCS#8 blobs, so no password callback). The > > d2i_PrivateKey() function takes an explicit pkey_type instead. > > Hmmm, well, d2i_PrivateKey() takes an explicit pkey_type, yes, but it's > not sufficiently informative for ECDH, being just EVP_PKEY_EC. Or are > there more informative type values I've not discovered yet? When I call > d2i_PrivateKey(EVP_PKEY_EC, ...) it wants a PKCS#8 encoded private key. Actually, it supports *both* the PKCS#8 and the legacy type-specific formats. The algorithm-specific PEM key formats may not be defined for some newer key types and are deprecated, so sure, you're supposed to use PKCS#8 whenever possible. For EC private keys, the underlying legacy DER codecs are: d2i_ECPrivateKey() i2d_ECPrivateKey() with signatures: EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len); int i2d_ECPrivateKey(const EC_KEY *a, unsigned char **out); for d2i_ECPrivateKey(), you get back an EC_KEY() which you can convert to an EVP_PKEY via the deprecated (copy vs. take ownership): EVP_PKEY_set1_EC_KEY(3), or EVP_PKEY_assign_EC_KEY(3) which direct you to EVP_PKEY_fromdata(3) (much scaffolding...). However, d2i_PrivateKey(EVP_PKEY_EC, ...) given non-PKCS#8 input internally tries: static int old_ec_priv_decode(EVP_PKEY *pkey, const unsigned char **pder, int derlen) { EC_KEY *ec; if ((ec = d2i_ECPrivateKey(NULL, pder, derlen)) == NULL) return 0; EVP_PKEY_assign_EC_KEY(pkey, ec); return 1; } which does the expected thing without much fuss. That said, you mentioned ECDH in passing, so I'm not sure we're taking about the same things... -- Viktor.