I have the following python code that creates a piece of DER data "x25519_pubic_der".
#!/usr/bin/python3
from cryptography.hazmat.primitives.asymmetric import x25519
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
x25519_key = x25519.X25519PrivateKey.generate()
x25519_public_der = x25519_key.public_key().public_bytes(Encoding.DER,
PublicFormat.SubjectPublicKeyInfo)
I try to decode this piece of data back to its original binary bits using C code, but not working. Below is one test program (assuming der data is transferred correctly, also the length).
const unsigned char* ptr = x25519_public_der;
ASN1_OCTET_STRING* octet_string = d2i_ASN1_OCTET_STRING(NULL, &ptr, x25519_public_der_len);
if (!octet_string) {
fprintf(stderr, "Error decoding DER data to binary bytes\n");
return 1;
}
I suspect PublicFormat.SubjectPublicKeyInfo may add some extra encoding, or I am not using the right d2i_ function (?), only a guess...
Basically x25519_public_der contains 44 bytes and I want to restore it to 32 bytes using C programming. I think this question may be relevant, How do I pass a 44 Bytes x25519 public key created by openssl to CryptoKit which requires a key length of 32 Bytes, but I do not have enough background to implement it in C.
Since the raw public key is located at the end of the SPKI/DER X25519 key, the last 32 bytes can simply be used.
A more general approach is to import the SPKI/DER key as EVP_PKEY
and extract the raw key with EVP_PKEY_get_raw_public_key
, e.g. as of OpenSSL v3.0 (for the sake of simplicity, without exception handling):
const char spki_der[] = { 0x30, 0x2A, 0x30, 0x05, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x03, 0x21, 0x00, 0xBB, 0x13, 0xF6, 0x6B, 0x4E, 0xC0, 0x9E, 0xE2, 0xD5, 0xC6, 0xE5, 0x49, 0xDF, 0xE9, 0x06, 0x41, 0x4A, 0x79, 0x99, 0x98, 0xE1, 0xE3, 0x93, 0x46, 0x13, 0xD6, 0xBE, 0xD3, 0xC2, 0xEE, 0x9E, 0x66 };
const unsigned char* data = (const unsigned char*)spki_der;
size_t datalen = sizeof(spki_der);
// Import SPKI/DER X25519 key
OSSL_DECODER_CTX* dctx;
EVP_PKEY* pkeyPub = NULL;
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkeyPub, "DER", NULL, "X25519", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, NULL, NULL);
OSSL_DECODER_from_data(dctx, &data, &datalen);
// Export raw X25519 key (32 bytes)
unsigned char rawPub[32];
size_t rawPubLen = 32;
EVP_PKEY_get_raw_public_key(pkeyPub, rawPub, &rawPubLen); // rawPub: 0xbb13f66b4ec09ee2d5c6e549dfe906414a799998e1e3934613d6bed3c2ee9e66
For the sake of completeness:
OSSL_DECODER_CTX_new_for_pkey()
can be used to import public (5th parameter: OSSL_KEYMGMT_SELECT_PUBLIC_KEY
) and private (5th parameter: OSSL_KEYMGMT_SELECT_KEYPAIR
) keys of different types (4th parameter, e.g. "X25519"
) and encodings (2nd parameter, e.g. "PEM"
or "DER"
).
For private keys, EVP_PKEY_get_raw_private_key()
must be used to export the raw key.
Depending on OSSL_DECODER_from_...
, different data sources can be chosen.