Tomas Mraz wrote: > Jonathan Wernberg wrote: >> Hi openssl-users mailing list. >> >> We are having some troubles converting some code from OpenSSL 1.x to >> OpenSSL 3.x APIs, to get rid of deprecation warnings, and hope >> someone may be able to give us some hints in the right direction. >> >> One thing we want to do is to convert an EC private key from raw >> format into a EVP_PKEY. Today we do as below (error checking, freeing >> and secure memory context things removed for brevity, private key is >> in "privkey" and curve in "nid"): >> >> BIGNUM *privkey_bn = BN_bin2bn(privkey, privkey_len, NULL); >> EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); >> const EC_GROUP *group = EC_KEY_get0_group(eckey); >> EC_POINT *pubkey_point = EC_POINT_new(group); >> EC_POINT_mul(group, pubkey_point, privkey_bn, NULL, NULL, NULL); >> EC_KEY_set_private_key(eckey, privkey_bn); >> EC_KEY_set_public_key(eckey, pubkey_point); >> EVP_PKEY *pkey = EVP_PKEY_new(); >> EVP_PKEY_assign_EC_KEY(pkey, eckey); >> >> Basically we chained a lot of operations because we could not find >> any single function that did it for us. Some of these operations are >> now deprecated, such as the EC_KEY ones. We tried experimenting with >> the OSSL fromdata() function instead (omitted the mapping from "nid" >> to "sn" for brevity): >> >> BIGNUM *privkey_bn = BN_bin2bn(privkey, privkey_len, NULL); >> EC_GROUP *group = EC_GROUP_new_by_curve_name(nid); >> EC_POINT *pubkey_point = EC_POINT_new(group); >> EC_POINT_mul(group, pubkey_point, privkey_bn, NULL, NULL, NULL); >> unsigned char pubkey_buf[65]; // size just an example >> EC_POINT_point2oct(grp, pubkey_point, POINT_CONVERSION_UNCOMPRESSED, >> pubkey_buf, sizeof(pubkey_buf), NULL); >> OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new(); >> OSSL_PARAM_BLD_push_utf8_string(param_bld, >> OSSL_PKEY_PARAM_GROUP_NAME, sn, 0); >> OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_PRIV_KEY, >> privkey_bn); >> OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY, >> pubkey_buf, sizeof(pubkey_buf)); >> OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(param_bld); >> EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); >> EVP_PKEY_fromdata_init(ctx); >> EVP_PKEY *pkey = NULL; >> EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params); >> EVP_PKEY_CTX_free(ctx); >> ctx = EVP_PKEY_CTX_new(pkey, NULL); >> EVP_PKEY_check(ctx); >> >> Although it works, it does not feel right. We ended up chaining many >> more operations than before. Our understanding was that the new >> OpenSSL 3.x API was redesigned partially to remove low-level >> manipulations like these. We have looked though both the migration >> document and the reference API without finding anything that does our >> job better. OSSL_DECODERs as frequently suggested in the migration >> documentation do not seem to support raw EC key formats at all. The >> EVP_PKEY_new_raw_private_key() functions mentioned in the reference >> API does not appear to support NIST P curves, according to the >> documentation. The OSSL fromdata() way above does not calculate the >> public key from the private one itself, nor does it verify that the >> points are on the curve, and we are uncertain if there are anything >> else it does not do that we need to do to not compromise security. We >> could use d2i_PrivateKey() or d2i_AutoPrivateKey(), which both seem >> to read in the key data in a secure way and derive the public part >> automatically. But that way would require us to implement custom >> logic in our code to manually put together DER data from the raw key >> data, for multiple curve types. >> >> What is the recommended and safe way to read in an EC private key >> from raw format into an EVP_PKEY object ready to be used? >> >> Another thing we want to do is to convert an RSA public key from raw >> modulus and exponent components into proper DER encoded >> SubjectPublicKeyInfo data. Today we piggyback on OpenSSL to >> accomplish this like this: >> >> BIGNUM *n = BN_bin2bn(modulus, (int)modulus_len, NULL); >> BIGNUM *e = BN_bin2bn(exponent, (int)exponent_len, NULL); >> RSA *rsa = RSA_new(); >> RSA_set0_key(rsa, n, e, NULL); >> int data_len = i2d_RSA_PUBKEY(rsa, NULL); >> uint8_t *data_buf = malloc((size_t)data_len); >> uint8_t *pdata = data_buf; >> data_len = i2d_RSA_PUBKEY(rsa, &pdata); >> >> However, some of those functions are now deprecated. Unfortunately >> our best attempt with OpenSSL 3.x compatible APIs ended up being this >> comparably long sequence of operations: >> >> BIGNUM *n = BN_bin2bn(modulus, (int)modulus_len, NULL); >> BIGNUM *e = BN_bin2bn(exponent, (int)exponent_len, NULL); >> EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); >> OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new(); >> OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n); >> OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e); >> OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(param_bld); >> EVP_PKEY_fromdata_init(ctx); >> EVP_PKEY *pkey = NULL; >> EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params); >> EVP_PKEY_CTX_free(ctx); >> ctx = EVP_PKEY_CTX_new(pkey, NULL); >> EVP_PKEY_public_check(ctx); >> int data_len = i2d_PUBKEY(pkey, NULL); >> uint8_t *data_buf = malloc((size_t)data_len); >> uint8_t *pdata = data_buf; >> data_len = i2d_PUBKEY(pkey, &pdata); >> >> This also does not feel quite right. Especially the conversion from >> raw modulus and exponent ended up being much longer, and we failed to >> find an easier way to do it. >> >> What is the easiest or most recommended way to convert an RSA public >> key from raw modulus and exponent components to proper DER encoded >> SubjectPublicKeyInfo data using non-deprecated OpenSSL 3.x APIs? > > Basically what you have done is right. With the RSA public keys there > is not much that could be improved even on the OpenSSL side apart from > providing some wrapper functions that would do basically the same thing > internally. > > The deficiency of the API that is really missing is a high level API > way to generate the public key from the private key if the public key > is missing for the EC algorithm. You did it the only way that avoids > using deprecated API calls however it should really be done inside a > provider. I've opened https://github.com/openssl/openssl/issues/19046 > for this RFE. Ok, thank you. Good to hear we were on the right track and did not miss anything. We will watch the ticket, as a solution here would likely simplify our code a bit.