OSSL-GUIDE-MIGRATION(7SSL)
π NAME π
ossl-guide-migration, migration_guide, ossl-migration-guide β bestie, welcome to the OpenSSL Guide: Migrating from older OpenSSL versions. This is OSSL-GUIDE-MIGRATION(7SSL) and yeah she's long, but she's iconic and we're getting through her together. π¦
π SYNOPSIS π
See the individual manual pages for details.
Like, this page is the vibe check, not the whole story β go check the individual manual pages for the actual details. π
πΈ DESCRIPTION πΈ
Okay so here's the tea: this guide details the changes required to migrate to new versions of OpenSSL, covering OpenSSL 3.0 and later versions. If you're dealing with earlier versions, go refer to https://github.com/openssl/openssl/blob/master/CHANGES.md instead.
And if you want an overview of some of the key concepts introduced since OpenSSL 3.0, see crypto(7). Run, don't walk. π«Ά
π OPENSSL 4.0 π
β¨ Main Changes from OpenSSL 3.6 β¨
Major Release
OpenSSL 4.0 is a major release, and consequently any application that currently uses an older version of OpenSSL will at the very least need to be recompiled in order to work with the new version. No skipping that step, babe. π
It is the intention that many applications will work unchanged with OpenSSL 4.0 if those applications previously worked with OpenSSL 3.6 and did not use deprecated API. However β and this is the important part β this is not guaranteed and some changes may be required in some cases. Changes may be required to applications using previously deprecated API. Changes may also be required if applications need to take advantage of some of the new features available in OpenSSL 4.0.
Some functions have been removed that were deprecated in previous versions of OpenSSL. See ossl-removed-api(7).
The ASN1_STRING type has been made opaque. Accessors for the needed values from this type have been present since OpenSSL 1.1, so code which directly attempts to access internals of ASN1_STRING should be converted to use the accessors. The ASN1_FLAGS have also been made private β most of which were purely internal. ASN1_STRING_FLAG_BITS_LEFT, which used to be exposed to allow manipulating the internal representation of a bit string, should be replaced by using the appropriate setter for an ASN1_BIT_STRING type instead of direct flag and structure manipulation. πΈ
π Upgrading from OpenSSL 3.x π
The ASN1_STRING type is now opaque
ASN1_STRING is the basis for many types in OpenSSL. Since OpenSSL 1.1.1, accessor functions have been available to access and create ASN1_STRING values of various types. See ASN1_STRING_type_new(3), ASN1_STRING_get0_data(3), ASN1_STRING_set(3) and ASN1_STRING_length(3).
If your code uses direct access to ASN1_STRING structure members, you will need to convert it to allocate the correct type ASN1_STRING, and use accessors.
If your code has in the past used an ASN1_STRING as a stack allocated object, you will need to allocate it from the heap with ASN1_STRING_new(3) or ASN1_STRING_type_new(3). Heap girlie now, sorry. π
The flags member of ASN1_STRING has become inaccessible, and the definitions of the flags are no longer public. That includes the public definition of these flags:
ASN1_STRING_FLAG_NDEFASN1_STRING_FLAG_CONTASN1_STRING_FLAG_MSTRINGASN1_STRING_FLAG_EMBEDASN1_STRING_FLAG_BITS_LEFT
For the first four values, these were internal use flags which were never user settable in a way that would not cause things to break.
The final flag was used to indicate that an ASN1_BIT_STRING has a value of unused bits left β most applications do not touch this. You should not use ASN1_STRING_set() to set the value of an ASN1_BIT_STRING that may have unused bits on the end. To ensure the number of unused bits is correctly set in an ASN1_BIT_STRING type, use the functions ASN1_BIT_STRING_set_bit(3) or ASN1_BIT_STRING_set1(3) to set the value of an ASN1_BIT_STRING, and ensure that the number of unused bits is correctly set. The function ASN1_BIT_STRING_get_length(3) may be used to retrieve the length in bytes, and the number of unused bits of an ASN1_BIT_STRING. β
Constification of X509 functions
A large number of public API functions that access an X509 * object have been constified. This change has been made to allow for future improvements to the X509 layer to reduce the number of memory allocations and copies that are made. These changes can impact code which uses these functions if a returned pointer value which was not const in previous versions of OpenSSL has now been made const. When such a value is assigned to a non-const pointer variable you will get a compiler warning. π
The returned values being const is an indication that you may not mutate these values safely.
Typically, you will need to change any variables holding these values to be const. If for some reason you need to mutate the returned object, you should make a copy of the returned const object into a mutable non-const object, bearing in mind that your copy has no effect on the original X509 object itself.
The following functions have had arguments / return values related to X509 constified. For full details see their relevant manual pages.
NAME_CONSTRAINTS_checkNAME_CONSTRAINTS_check_CNX509_add_cert(3)X509_add_ext(3)X509_alias_get0X509_build_chain(3)X509_chain_check_suitebX509_check_ca(3)X509_check_email(3)X509_check_host(3)X509_check_ip(3)X509_check_ip_asc(3)X509_check_issued(3)X509_check_purpose(3)X509_check_trustX509_CRL_get0_by_cert(3)X509_find_by_issuer_and_serialX509_find_by_subjectX509_get_ext(3)X509_get_extended_key_usage(3)X509_get_extension_flags(3)X509_get_key_usage(3)X509_get_pathlen(3)X509_get_proxy_pathlen(3)X509_get_pubkey(3)X509_get_pubkey_parametersX509_get_signature_info(3)X509_get0_authority_issuer(3)X509_get0_authority_key_id(3)X509_get0_authority_serial(3)X509_get0_pubkey(3)X509_get0_reject_objectsX509_get0_subject_key_id(3)X509_get0_trust_objectsX509_get1_emailX509_get1_ocspX509_issuer_and_serial_hashX509_issuer_name_hash(3)X509_issuer_name_hash_oldX509_keyid_get0X509_load_http(3)X509_print_ex_fpX509_print_fpX509_REQ_get1_emailX509_REQ_to_X509X509_self_signed(3)X509_STORE_add_cert(3)X509_STORE_CTX_get1_issuer(3)X509_subject_name_hash(3)X509_subject_name_hash_oldX509_to_X509_REQX509_TRUST_addX509v3_addr_validate_resource_setX509v3_asid_validate_resource_set
The following two functions we "un-constified", as they were documented as returning an explicitly mutable pointer from within an X509 object:
Removal of atexit() usage
libcrypto no longer arms the OPENSSL_cleanup() function as an atexit(3) handler. Memory leak detectors may report there is allocated, but still reachable, allocated memory at application exit. If a clean report is desired, then the application must call OPENSSL_cleanup() explicitly before main() returns.
Removal of fixed (D)TLS version method functions
These were deprecated way back in the OpenSSL 1.1.0 release:
SSLv3_method()SSLv3_server_method()SSLv3_client_method()TLSv1_method()TLSv1_server_method()TLSv1_client_method()TLSv1_1_method()TLSv1_1_server_method()TLSv1_1_client_method()TLSv1_2_method()TLSv1_2_server_method()TLSv1_2_client_method()DTLSv1_method()DTLSv1_server_method()DTLSv1_client_method()DTLSv1_2_method()DTLSv1_2_server_method()DTLSv1_2_client_method()
Migrating applications should use DTLS_method(), DTLS_server_method(), DTLS_client_method(), TLS_method(), TLS_server_method(), TLS_client_method(), instead, and set the version with the SSL_CTX_set_min_proto_version() and/or SSL_CTX_set_max_proto_version() API's. Slay. π
Deprecation of EVP_MD_CTX_get0_md_data()
The function EVP_MD_CTX_get0_md_data() has been deprecated in 4.0. This function is only useful in previous versions to obtain the data stored in an EVP_MD_CTX by a custom EVP_MD implementation. However, the ability to create custom EVP_MD implementations has been removed in OpenSSL 4.0, so this function is no longer useful. It always returns NULL, so it should simply be removed from applications that call it. Applications wishing to use a custom digest implementation should instead use the provider API.
Deprecation of ASN1_OBJECT_new()
The function ASN1_OBJECT_new() has been deprecated in 4.0. The function is only useful internally because the type is opaque and there exist no setter functions. Hence, there never was a real use case for the function and calls to it can simply be removed.
Practical behaviour change of EVP_get_cipherby* and EVP_get_digestby* functions
Before 4.0, the functions EVP_get_cipherby* and EVP_get_digestby* were often used as a fallback for EVP_CIPHER_fetch() and EVP_MD_fetch(), implying the possibility to access an algorithm implementation provided by an engine. In the case when an engine-provided algorithm was not available, these functions on success return the implementation from the default OpenSSL library context with default properties. Version 4.0 doesn't support engines, so, on success, these functions always return the implementation from the default OpenSSL library context with default properties. This is probably not the desired result for applications that use a nondefault library context. π¬
The other difference between EVP_get_cipherby*/EVP_get_digestby* and EVP_CIPHER_fetch()/EVP_MD_fetch() is that the former functions deal with algorithm aliases added via OBJ_NAME_add(), and the newer ones don't (except the ones declared in a specific provider as explicit aliases). Applications wishing to preserve alias handling behaviour can first check for an alias using OBJ_NAME_get() (with OBJ_NAME_TYPE_CIPHER_METH or OBJ_NAME_TYPE_MD_METH) and fall back to EVP_CIPHER_fetch() or EVP_MD_fetch() if no match is found.
π OPENSSL 3.6 π
β¨ Main Changes from OpenSSL 3.5 β¨
The functions EVP_PKEY_asn1_*() and EVP_PKEY_get0_asn1() were deprecated in favour of the provider API. EVP_PKEY_assign_[DSA|SIPHASH]() were also deprecated. Please use the new high level and provider API. π«Ά
π¦ OPENSSL 3.1 π¦
β¨ Main Changes from OpenSSL 3.0 β¨
The FIPS provider in OpenSSL 3.1 includes some non-FIPS validated algorithms, consequently the property query "fips=yes" is mandatory for applications that want to operate in a FIPS approved manner. The algorithms are:
- Triple DES ECB
- Triple DES CBC
- EdDSA
There are no other changes requiring additional migration measures since OpenSSL 3.0. Easy peasy. π
π OPENSSL 3.0 π
β¨ Main Changes from OpenSSL 1.1.1 β¨
Major Release
OpenSSL 3.0 is a major release, and consequently any application that currently uses an older version of OpenSSL will at the very least need to be recompiled in order to work with the new version. It is the intention that the large majority of applications will work unchanged with OpenSSL 3.0 if those applications previously worked with OpenSSL 1.1.1. However, this is not guaranteed and some changes may be required in some cases. Changes may also be required if applications need to take advantage of some of the new features available in OpenSSL 3.0, such as the availability of the FIPS module.
License Change
okay bestie, plot twist era incoming π In previous versions, OpenSSL was living that dual-license life β the OpenSSL license and the SSLeay license, and both of them apply (https://www.openssl.org/source/license-openssl-ssleay.txt). But starting with OpenSSL 3.0? That whole situation got replaced by the Apache License v2 (https://www.openssl.org/source/apache-license-2.0.txt). Glow-up fr fr. β¨
π Providers and FIPS support π
Okay so literally one of the biggest changes from OpenSSL 1.1.1 is that the whole Provider concept just... showed up. Providers are these little collections that gather up algorithm implementations and make them available. With OpenSSL 3.0 you can pick which providers any given app uses β either programmatically or through a config file. So much freedom, we love that for you. π«Ά
OpenSSL 3.0 ships with 5 different providers as standard, and over time third parties might drop even more providers that you can plug into OpenSSL. (The collab era.) All the algorithm implementations you get via providers are reached through the "high level" APIs β think the functions prefixed with EVP. They are not accessible through the "Low Level APIs". No sneaking in the back door. π
One of the standard providers is the FIPS provider, which makes FIPS validated cryptographic algorithms available. Important: it's disabled by default and you have to enable it explicitly at configuration time using the enable-fips option. If you do enable it, the FIPS provider gets built and installed on top of the other standard providers β no separate installation procedure needed. π
That said, there's a dedicated install_fips make target that does one very specific thing: it installs only the FIPS provider into an existing OpenSSL installation. Niche but iconic.
β οΈ Now listen β not every algorithm is always available to your app at any particular moment. So if your application code uses any digest or cipher algorithm via the EVP interface, you should verify the result of the EVP_EncryptInit(3), EVP_EncryptInit_ex(3), and EVP_DigestInit(3) functions. If the algorithm you asked for isn't available, these functions will straight-up fail. No cap.
Also see "Legacy Algorithms" for the tea on the legacy provider. π΅
And also see "Completing the installation of the FIPS Module" and "Using the FIPS Module in applications".
π§ Low Level APIs π§
So OpenSSL has historically provided two sets of APIs for invoking cryptographic algorithms: the "high level" APIs (like the EVP APIs) and the "low level" APIs. The high level ones are typically designed to work across all algorithm types. The "low level" ones are targeted at one specific algorithm implementation. Big difference, bestie.
For example, the EVP APIs give you EVP_EncryptInit_ex(3), EVP_EncryptUpdate(3) and EVP_EncryptFinal(3) to do symmetric encryption β and those functions work with AES, CHACHA, 3DES, etc. Meanwhile, doing AES encryption with the low level APIs means calling AES-specific functions like AES_set_encrypt_key(3), AES_encrypt(3), and so on. And the functions for 3DES? Completely different. π
Use of the low level APIs has been informally discouraged by the OpenSSL development team for a long time. But in OpenSSL 3.0 it got formal. All such low level APIs have been deprecated. You may still use them in your applications, but you may start seeing deprecation warnings during compilation (depends on whether your compiler supports that). And heads up: deprecated APIs may be removed from future versions of OpenSSL, so you're strongly encouraged to update your code and use the high level APIs instead. Please bestie, for your own good. π
This whole thing is described in more detail in "Deprecation of Low Level Functions".
π΅ Legacy Algorithms π΅
Some cryptographic algorithms β like MD2 and DES β that used to be available via the EVP APIs are now considered legacy and their use is strongly discouraged. (Like, why are you texting your ex energy.) These legacy EVP algorithms are still available in OpenSSL 3.0 but not by default. If you want them, you must load the legacy provider. That can be as simple as a config file change, or you can do it programmatically. π
See OSSL_PROVIDER-legacy(7) for a complete list of algorithms.
Applications using the EVP APIs to access these algorithms should instead use more modern algorithms. If that's genuinely not possible, then those applications should make sure the legacy provider has been loaded β either programmatically or via configuration. See the crypto(7) man page for more info about providers.
π§ Engines and "METHOD" APIs π§
The refactoring to support Providers conflicts internally with the APIs used to support engines β including the ENGINE API and any function that creates or modifies custom "METHODS". We're talking EVP_MD_meth_new(), EVP_CIPHER_meth_new(), EVP_PKEY_meth_new(), RSA_meth_new(3), EC_KEY_METHOD_new(3), etc. These functions are being deprecated in OpenSSL 3.0. π«
And users of these APIs should know that their use can likely bypass provider selection and configuration, with unintended consequences. This is especially relevant for applications written to use the OpenSSL 3.0 FIPS module, as detailed below. Authors and maintainers of external engines are strongly encouraged to refactor their code β transform engines into providers using the new Provider API and avoid those deprecated methods. New era, new you. π¦
Support of legacy engines
If openssl is not built without engine support or deprecated API support, engines will still work. But their applicability will be limited. π
New algorithms provided via engines will still work.
Engine-backed keys can be loaded via a custom OSSL_STORE implementation. In that case, the EVP_PKEY objects created via ENGINE_load_private_key(3) will be considered legacy and will continue to work.
To ensure future compatibility, the engines should be turned into providers. And to prefer the provider-based hardware offload, you can specify the default properties to prefer your provider.
Setting engine-based or application-based default low-level crypto method such as RSA_METHOD or EC_KEY_METHOD is still possible, and keys inside the default provider will use the engine-based implementation for the crypto operations. However β EVP_PKEYs created by decoding using OSSL_DECODER, PEM_ or d2i_ APIs will be provider-based. So to create fully legacy EVP_PKEYs, you must use EVP_PKEY_set1_RSA(3), EVP_PKEY_set1_EC_KEY(3) or similar functions. Sneaky detail, remember it. π
π Versioning Scheme π
The OpenSSL versioning scheme got a whole rebrand with the OpenSSL 3.0 release. The new format is:
MAJOR.MINOR.PATCH
For OpenSSL 1.1.1 and below, different patch levels were indicated by a letter at the end of the release version number. That's not a thing anymore β now the patch level is the final number in the version. A change in the second (MINOR) number means new features may have been added. And here's the key bit: OpenSSL versions with the same major number are API and ABI compatible. If the major number changes, then API and ABI compatibility is not guaranteed. Fr fr, check before you upgrade. β¨
For more info, see OpenSSL_version(3).
π Other major new features π
Certificate Management Protocol (CMP, RFC 9810)
This also covers CRMF (RFC 4211) and HTTP transfer (RFC 9811). See openssl-cmp(1) and OSSL_CMP_exec_certreq(3) as your starting points. π
HTTP(S) client
A proper HTTP(S) client that supports GET and POST, redirection, plain and ASN.1-encoded contents, proxies, and timeouts. It's giving actually capable. π
Key Derivation Function API (EVP_KDF)
This simplifies the process of adding new KDF and PRF implementations. Previously, KDF algorithms had been shoe-horned into using the EVP_PKEY object β which was not a logical mapping. (Literally we were forcing it. π)
Existing applications that use KDF algorithms via EVP_PKEY (scrypt, TLS1 PRF and HKDF) may be slower, because they use an EVP_KDF bridge internally. All new applications should use the new EVP_KDF(3) interface.
See also "Key Derivation Function (KDF)" in OSSL_PROVIDER-default(7) and "Key Derivation Function (KDF)" in OSSL_PROVIDER-FIPS(7).
Message Authentication Code API (EVP_MAC)
This simplifies the process of adding MAC implementations. π
It includes a generic EVP_PKEY to EVP_MAC bridge, to facilitate the continued use of MACs through raw private keys in functionality such as EVP_DigestSign(3) and EVP_DigestVerify(3).
All new applications should use the new EVP_MAC(3) interface. See also "Message Authentication Code (MAC)" in OSSL_PROVIDER-default(7) and "Message Authentication Code (MAC)" in OSSL_PROVIDER-FIPS(7).
Algorithm Fetching
Using calls to convenience functions such as EVP_sha256() and EVP_aes_256_gcm() may incur a performance penalty when using providers. Retrieving algorithms from providers involves searching for an algorithm by name β which is much slower than directly accessing a method table. So it's recommended to prefetch algorithms if an algorithm is used many times. iykyk. π₯
See "Performance" in crypto(7), "Explicit fetching" in crypto(7) and "Implicit fetching" in crypto(7).
Support for Linux Kernel TLS
To use KTLS, support for it must be compiled in using the enable-ktls configuration option. It must also be enabled at run time using the SSL_OP_ENABLE_KTLS option. Two steps, don't skip either. β
New Algorithms
- KDF algorithms "SINGLE STEP" and "SSH"
See EVP_KDF-SS(7) and EVP_KDF-SSHKDF(7)
- MAC Algorithms "GMAC" and "KMAC"
See EVP_MAC-GMAC(7) and EVP_MAC-KMAC(7).
- KEM Algorithm "RSASVE"
See EVP_KEM-RSA(7).
- Cipher Algorithm "AES-SIV"
See "SIV Mode" in EVP_EncryptInit(3).
- AES Key Wrap inverse ciphers supported by EVP layer.
The inverse ciphers use AES decryption for wrapping, and AES encryption for unwrapping. The algorithms are: "AES-128-WRAP-INV", "AES-192-WRAP-INV", "AES-256-WRAP-INV", "AES-128-WRAP-PAD-INV", "AES-192-WRAP-PAD-INV" and "AES-256-WRAP-PAD-INV".
- CTS ciphers added to EVP layer.
The algorithms are "AES-128-CBC-CTS", "AES-192-CBC-CTS", "AES-256-CBC-CTS", "CAMELLIA-128-CBC-CTS", "CAMELLIA-192-CBC-CTS" and "CAMELLIA-256-CBC-CTS". CS1, CS2 and CS3 variants are supported.
CMS and PKCS#7 updates
- Added CAdES-BES signature verification support.
- Added CAdES-BES signature scheme and attributes support (RFC 5126) to CMS API.
- Added AuthEnvelopedData content type structure (RFC 5083) using
AES_GCM.
This uses the AES-GCM parameter (RFC 5084) for the Cryptographic Message Syntax. Its purpose is to support encryption and decryption of a digital envelope that is both authenticated and encrypted using AES GCM mode. Main character energy, honestly. π
PKCS7_get_octet_string(3)andPKCS7_type_is_other(3)were made public. Coming out era. π
PKCS#12 API updates
So like, the default algorithms for making a pkcs12 with PKCS12_create() got glow-upped to the more modern PBKDF2 and AES based ones. β¨ The default MAC iteration count became PKCS12_DEFAULT_ITER so it matches the password-based encryption iteration count, and the default digest for the MAC computation is now SHA-256. And the pkcs12 app got a -legacy option that brings back all the old default algorithms, for when you need to vibe with legacy systems. π«Ά
π Enhanced PKCS#12 APIs (with a libctx, bestie) π
They also added enhanced PKCS#12 APIs that accept a library context OSSL_LIB_CTX and (where relevant) a property query. Other APIs handling PKCS#7 and PKCS#8 objects got enhanced where needed too. The lineup:
PKCS12_add_key_ex(3)PKCS12_add_safe_ex(3)PKCS12_add_safes_ex(3)PKCS12_create_ex(3)PKCS12_decrypt_skey_ex(3)PKCS12_init_ex(3)PKCS12_item_decrypt_d2i_ex(3)PKCS12_item_i2d_encrypt_ex(3)PKCS12_key_gen_asc_ex(3)PKCS12_key_gen_uni_ex(3)PKCS12_key_gen_utf8_ex(3)PKCS12_pack_p7encdata_ex(3)PKCS12_pbe_crypt_ex(3)PKCS12_PBE_keyivgen_ex(3)PKCS12_SAFEBAG_create_pkcs8_encrypt_ex(3)PKCS5_pbe2_set_iv_ex(3)PKCS5_pbe_set0_algor_ex(3)PKCS5_pbe_set_ex(3)PKCS5_pbkdf2_set_ex(3)PKCS5_v2_PBE_keyivgen_ex(3)PKCS5_v2_scrypt_keyivgen_ex(3)PKCS8_decrypt_ex(3)PKCS8_encrypt_ex(3)PKCS8_set0_pbe_ex(3)
As part of this whole thing, the EVP_PBE_xxx APIs can also take a library context and property query, and they'll call an extended version of the key/IV derivation function that supports those parameters. That includes EVP_PBE_CipherInit_ex(3), EVP_PBE_find_ex(3) and EVP_PBE_scrypt_ex(3). π
π¨ PKCS#12 KDF versus FIPS π¨
Unlike in 1.x.y, the PKCS12KDF algorithm used when a PKCS#12 structure is created with a MAC does not work with the FIPS provider, because PKCS12KDF is not a FIPS approvable mechanism. It's giving incompatible, fr fr. π
See EVP_KDF-PKCS12KDF(7), PKCS12_create(3), openssl-pkcs12(1), OSSL_PROVIDER-FIPS(7).
πͺ Windows thread synchronization changes πͺ
On Windows, thread synchronization now uses read/write primitives (SRWLock) when the OS supports it β otherwise CriticalSection keeps being used like before.
π΅οΈ Trace API π΅οΈ
There's a whole new generic trace API that supports enabling instrumentation through trace output. It's mainly meant as a lil helper for developers and it's disabled by default. To actually use it, OpenSSL has to be configured with the "enable-trace" option. π
If the tracing API is enabled, the app can turn on trace output by registering BIOs as trace channels for a bunch of tracing and debugging categories. See OSSL_trace_enabled(3).
π Key validation updates π
EVP_PKEY_public_check(3) and EVP_PKEY_param_check(3) now work for way more key types β RSA, DSA, ED25519, X25519, ED448 and X448 are all in the group chat now. Before (in 1.1.1) they'd just return -2. And for key types that don't have parameters, EVP_PKEY_param_check(3) will always return 1. Slay.
π Other notable deprecations and changes π
The function code part of an OpenSSL error code is no longer relevant
This code is now always set to zero, bestie. Related functions are deprecated. π
STACK and HASH macros have been cleaned up
The type-safe wrappers are now declared everywhere and implemented once. See DEFINE_STACK_OF(3) and DEFINE_LHASH_OF_EX(3).
The RAND_DRBG subsystem has been removed
The new EVP_RAND(3) is a partial replacement β the DRBG callback framework is just gone. The RAND_DRBG API did not fit well into the new provider concept as implemented by EVP_RAND and EVP_RAND_CTX. Not me crying about it. π
Removed FIPS_mode() and FIPS_mode_set()
These are legacy APIs that just don't apply to the new provider model. Applications should use EVP_default_properties_is_fips_enabled(3) and EVP_default_properties_enable_fips(3) instead. Periodt.
Key generation is slower
The Miller-Rabin test now uses 64 rounds, and that's used for all prime generation, including RSA key generation. So yeah, larger key sizes take longer. β³
Also, the default key generation method for regular 2-prime RSA keys got changed to the FIPS186-4 B.3.6 method (Generation of Probable Primes with Conditions Based on Auxiliary Probable Primes). That method is slower than the original one. No cap.
Change PBKDF2 to conform to SP800-132 instead of the older PKCS5 RFC2898
This checks that:
- the salt length is at least 128 bits
- the derived key length is at least 112 bits
- the iteration count is at least 1000
For backwards compatibility these checks are disabled by default in the default provider, but they're enabled by default in the FIPS provider.
To turn the checks on or off, see OSSL_KDF_PARAM_PKCS5 in EVP_KDF-PBKDF2(7). The parameter can be set using EVP_KDF_derive(3).
Enforce a minimum DH modulus size of 512 bits
Smaller sizes now just straight up result in an error. π
SM2 key changes
EC EVP_PKEYs with the SM2 curve have been reworked so they automatically become EVP_PKEY_SM2 instead of EVP_PKEY_EC.
Unlike in previous OpenSSL versions, this means applications cannot call EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2) to get SM2 computations.
Parameter and key generation is also reworked so you can actually generate EVP_PKEY_SM2 parameters and keys. Applications now have to generate SM2 keys directly and must not create an EVP_PKEY_EC key first. And it's no longer possible to import an SM2 key with domain parameters other than the SM2 elliptic curve ones.
Validation of SM2 keys has also been separated from validation of regular EC keys, which lets the SM2 validation process get better at rejecting loaded private keys that aren't conforming to the SM2 ISO standard. Specifically, a private scalar k outside the range 1 <= k < n-1 is now correctly rejected. π₯
EVP_PKEY_set_alias_type() method has been removed
This function let an EVP_PKEY object be mutable after it had been set up. In OpenSSL 3.0 it was decided that a provided key should not be able to change its type, so this function is gone, bestie. Bye. π
Functions that return an internal key should be treated as read only
Functions like EVP_PKEY_get0_RSA(3) behave a little differently in OpenSSL 3.0. Before, they returned a pointer to the low-level key used internally by libcrypto. From OpenSSL 3.0 onward, that key may now be held in a provider. Calling these functions will only return a handle on the internal key where the EVP_PKEY was constructed using this key in the first place β for example using a function or macro like EVP_PKEY_assign_RSA(3), EVP_PKEY_set1_RSA(3), etc.
Where the EVP_PKEY holds a provider managed key, these functions now return a cached copy of the key. So heads up:
- Changes to the internal provider key that happen after the first time the cached key is accessed will not be reflected back in the cached copy.
- Any changes application code makes to the cached copy will not be reflected back in the internal provider key.
For all of the above reasons, the keys returned from these functions should typically be treated as read-only. To really emphasise it, the value returned from EVP_PKEY_get0_RSA(3), EVP_PKEY_get0_DSA(3), EVP_PKEY_get0_EC_KEY(3) and EVP_PKEY_get0_DH(3) has been made const. π
This may break some existing code. Applications broken by this change should be modified. The preferred solution is to refactor the code to avoid the use of these deprecated functions. Failing that, the code should be modified to use a const pointer instead.
The EVP_PKEY_get1_RSA(3), EVP_PKEY_get1_DSA(3), EVP_PKEY_get1_EC_KEY(3) and EVP_PKEY_get1_DH(3) functions still return a non-const pointer so they can be "freed". But ngl, they should be treated as read-only too. π
The public key check has moved from EVP_PKEY_derive() to EVP_PKEY_derive_set_peer()
Which means you might get an error in EVP_PKEY_derive_set_peer(3) instead of during EVP_PKEY_derive(3). To disable this check, use EVP_PKEY_derive_set_peer_ex(dh, peer, 0).
The print format has cosmetic changes for some functions
The output from a whole bunch of "printing" functions β like X509_signature_print(3), X509_print_ex(3), X509_CRL_print_ex(3), and other similar ones β has been amended, so there may be cosmetic differences between the output you saw in 1.1.1 and 3.0. This also applies to the -text output from the openssl x509 and openssl crl applications. It's giving glow-up, but the diff is lowkey just vibes. β¨
Interactive mode from the openssl program has been removed
From now on, running it without arguments is equivalent to openssl help.
The error return values from some control calls (ctrl) have changed
One significant change: controls that used to return -2 for invalid inputs now return -1, indicating a generic error condition instead.
DH and DHX key types have different settable parameters
Before (in 1.1.1) these conflicting parameters were allowed, but now they'll result in errors. See EVP_PKEY-DH(7) for more details. This affects the behaviour of openssl-genpkey(1) for DH parameter generation.
EVP_CIPHER_CTX_set_flags() ordering change
If you're using a cipher from a provider, the EVP_CIPH_FLAG_LENGTH_BITS flag can only be set after the cipher has been assigned to the cipher context. See "FLAGS" in EVP_EncryptInit(3) for more info.
Validation of operation context parameters
Because cryptographic operations moved into providers, validation of various operation parameters can be postponed until the actual operation is executed, where previously it happened immediately when an operation parameter was set.
For example, when setting an unsupported curve with EVP_PKEY_CTX_set_ec_paramgen_curve_nid(), this function call will not fail β but later keygen operations with the EVP_PKEY_CTX will fail. Surprise! π
Removal of function code from the error codes
The function code part of the error code is now always set to 0. For that reason the ERR_GET_FUNC() macro was removed. Applications must resolve the error codes only using the library number and the reason code.
ChaCha20-Poly1305 cipher does not allow a truncated IV length to be used
In OpenSSL 3.0, setting the IV length to any value other than 12 will result in an error. Prior to OpenSSL 3.0, the ivlen could be smaller than the required 12 byte length, using EVP_CIPHER_CTX_ctrl(ctx, EVP_CRTL_AEAD_SET_IVLEN, ivlen, NULL). And that resulted in an IV that had leading zero padding. π
π οΈ Installation and Compilation π οΈ
Please refer to the INSTALL.md file in the top of the distribution for instructions on how to build and install OpenSSL 3.0. Please also refer to the various platform specific NOTES files for your specific platform. π«Ά
π¦ Upgrading from OpenSSL 1.1.1 π¦
okay bestie, real talk: going from OpenSSL 1.1.1 up to OpenSSL 3.0 should be relatively straight forward in most cases. fr fr. The place you're most likely to trip is if you've been using low level APIs in your code (yeah, the ones we literally just talked about above π). If that's you, expect to start seeing deprecation warnings when you compile your app. And if that happens? You have 3 options:
- Ignore the warnings. They're just warnings, bestie. The deprecated functions are still present and you may still use them. But β and this is the important part π β be aware that they may be removed from a future version of OpenSSL. So like, they're not gone, but they're on thin ice. π
- Suppress the warnings. Go check your compiler documentation for how to do this. That's a you-and-your-compiler conversation, I'm just the messenger. π
- Remove your usage of the low level APIs. In this case you will need to rewrite your code to use the high level APIs instead. Slay, glow-up, etc. β¨
π Error code changes π
So OpenSSL 3.0 brings a brand new Encoder/Decoder mechanism for working with widely used file formats, and that means application code that checks for particular error reason codes on key loading failures might need an update. it's giving "surprise, your error handling has a new side quest" π
Password-protected keys may deserve special attention, gorgeous. If only some errors are treated as an indicator that the user should be asked about the password again, it's worth testing these scenarios and processing the newly relevant codes.
And ngl, there may be more cases to treat specially, depending on the calling application code. iykyk. π«Ά
π Upgrading from OpenSSL 1.0.2 π
Jumping to OpenSSL 3.0 from OpenSSL 1.0.2 is likely to be significantly more difficult. not gonna sugarcoat it, this one's a journey πΆββοΈ In addition to the issues discussed above in the section about "Upgrading from OpenSSL 1.1.1", the main things to be aware of are:
- The build and installation procedure has changed significantly.
Check the file INSTALL.md in the top of the installation for instructions on how to build and install OpenSSL for your platform. Also read the various NOTES files in the same directory, as applicable for your platform. yes, actually read them, bestie, they're not decorative π
- Many structures have been made opaque in OpenSSL 3.0.
The structure definitions have been removed from the public header files and moved to internal header files. In practice this means that you can no longer stack allocate some structures. Instead they must be heap allocated through some function call (typically those function names have a "_new" suffix to them). Additionally you must use "setter" or "getter" functions to access the fields within those structures. it's giving β¨encapsulationβ¨
For example code that previously looked like this:
EVP_MD_CTX md_ctx;
/* This line will now generate compiler errors */
EVP_MD_CTX_init(&md_ctx);
The code needs to be amended to look like this:
EVP_MD_CTX *md_ctx;
md_ctx = EVP_MD_CTX_new();
...
...
EVP_MD_CTX_free(md_ctx);
see? pointer era. iconic honestly π
- Support for TLSv1.3 has been added.
This has a number of implications for SSL/TLS applications. See the TLS1.3 page https://github.com/openssl/openssl/wiki/TLS1.3 for further details.
More details about the breaking changes between OpenSSL versions 1.0.2 and 1.1.0 can be found on the OpenSSL 1.1.0 Changes page https://github.com/openssl/openssl/wiki/OpenSSL_1.1.0_Changes.
π§ Upgrading from the OpenSSL 2.0 FIPS Object Module π§
The OpenSSL 2.0 FIPS Object Module was a separate download that had to be built separately and then integrated into your main OpenSSL 1.0.2 build. In OpenSSL 3.0 the FIPS support is fully integrated into the mainline version of OpenSSL and is no longer a separate download. glow-up of the century, honestly π For further information see "Completing the installation of the FIPS Module".
Also, heads up: the function calls FIPS_mode() and FIPS_mode_set() have been removed from OpenSSL 3.0. You should rewrite your application to not use them. See fips_module(7) and OSSL_PROVIDER-FIPS(7) for details.
π Completing the installation of the FIPS Module π
The FIPS Module will be built and installed automatically if FIPS support has been configured. The current documentation can be found in the README-FIPS file at https://github.com/openssl/openssl/blob/master/README-FIPS.md.
πΈ Programming πΈ
Applications written to work with OpenSSL 1.1.1 will mostly just work with OpenSSL 3.0. periodt. However, changes will be required if you want to take advantage of some of the new features that OpenSSL 3.0 makes available. And to do that, you need to understand some new concepts introduced in OpenSSL 3.0. Read "Library contexts" in crypto(7) for further information. π
π Library Context π
A library context allows different components of a complex application to each use a different library context and have different providers loaded with different configuration settings. See "Library contexts" in crypto(7) for further info. it's giving "everyone gets their own little world" πβ¨
If the user creates an OSSL_LIB_CTX via OSSL_LIB_CTX_new(3) then many functions may need to be changed to pass additional parameters to handle the library context.
π¦ Using a Library Context - Old functions that should be changed π¦
If a library context is needed then all EVP_* digest functions that return a const EVP_MD * such as EVP_sha256() should be replaced with a call to EVP_MD_fetch(3). See "ALGORITHM FETCHING" in crypto(7).
If a library context is needed then all EVP_* cipher functions that return a const EVP_CIPHER * such as EVP_aes_128_cbc() should be replaced with a call to EVP_CIPHER_fetch(3). See "ALGORITHM FETCHING" in crypto(7).
Some functions can be passed an object that has already been set up with a library context such as d2i_X509(3), d2i_X509_CRL(3), d2i_X509_REQ(3) and d2i_X509_PUBKEY(3). If NULL is passed instead then the created object will be set up with the default library context. Use X509_new_ex(3), X509_CRL_new_ex(3), X509_REQ_new_ex(3) and X509_PUBKEY_new_ex(3) if a library context is required.
All functions listed below with a NAME have a replacement function NAME_ex that takes OSSL_LIB_CTX as an additional argument. Functions that have other mappings are listed along with the respective name. okay here comes the list, hydrate first π§
ASN1_item_new(3),ASN1_item_d2i(3),ASN1_item_d2i_fp(3),ASN1_item_d2i_bio(3),ASN1_item_sign(3)andASN1_item_verify(3)BIO_new(3)b2i_RSA_PVK_bio()andi2b_PVK_bio()BN_CTX_new(3)andBN_CTX_secure_new(3)CMS_AuthEnvelopedData_create(3),CMS_ContentInfo_new(3),CMS_data_create(3),CMS_digest_create(3),CMS_EncryptedData_encrypt(3),CMS_encrypt(3),CMS_EnvelopedData_create(3),CMS_ReceiptRequest_create0(3)andCMS_sign(3)CONF_modules_load_file(3)CTLOG_new(3),CTLOG_new_from_base64(3)andCTLOG_STORE_new(3)CT_POLICY_EVAL_CTX_new(3)d2i_AutoPrivateKey(3),d2i_PrivateKey(3)andd2i_PUBKEY(3)d2i_PrivateKey_bio(3)andd2i_PrivateKey_fp(3)
Use d2i_PrivateKey_ex_bio(3) and d2i_PrivateKey_ex_fp(3)
Use EC_GROUP_new_by_curve_name_ex(3) or EC_GROUP_new_from_params(3).
EVP_DigestSignInit(3)andEVP_DigestVerifyInit(3)EVP_PBE_CipherInit(3),EVP_PBE_find(3)andEVP_PBE_scrypt(3)PKCS5_PBE_keyivgen(3)EVP_PKCS82PKEY(3)EVP_PKEY_CTX_new_id(3)
Use EVP_PKEY_CTX_new_from_name(3)
EVP_PKEY_derive_set_peer(3),EVP_PKEY_new_raw_private_key(3)andEVP_PKEY_new_raw_public_key(3)EVP_SignFinal(3)andEVP_VerifyFinal(3)NCONF_new(3)OCSP_RESPID_match(3)andOCSP_RESPID_set_by_key(3)OPENSSL_thread_stop(3)OSSL_STORE_open(3)PEM_read_bio_Parameters(3),PEM_read_bio_PrivateKey(3),PEM_read_bio_PUBKEY(3),PEM_read_PrivateKey(3)andPEM_read_PUBKEY(3)PEM_write_bio_PrivateKey(3),PEM_write_bio_PUBKEY(3),PEM_write_PrivateKey(3)andPEM_write_PUBKEY(3)PEM_X509_INFO_read_bio(3)andPEM_X509_INFO_read(3)PKCS12_add_key(3),PKCS12_add_safe(3),PKCS12_add_safes(3),PKCS12_create(3),PKCS12_decrypt_skey(3),PKCS12_init(3),PKCS12_item_decrypt_d2i(3),PKCS12_item_i2d_encrypt(3),PKCS12_key_gen_asc(3),PKCS12_key_gen_uni(3),PKCS12_key_gen_utf8(3),PKCS12_pack_p7encdata(3),PKCS12_pbe_crypt(3),PKCS12_PBE_keyivgen(3),PKCS12_SAFEBAG_create_pkcs8_encrypt(3)PKCS5_pbe_set0_algor(3),PKCS5_pbe_set(3),PKCS5_pbe2_set_iv(3),PKCS5_pbkdf2_set(3)andPKCS5_v2_scrypt_keyivgen(3)PKCS7_encrypt(3),PKCS7_new(3)andPKCS7_sign(3)PKCS8_decrypt(3),PKCS8_encrypt(3)andPKCS8_set0_pbe(3)RAND_bytes(3)andRAND_priv_bytes(3)SMIME_write_ASN1(3)SSL_load_client_CA_file(3)SSL_CTX_new(3)TS_RESP_CTX_new(3)X509_CRL_new(3)X509_load_cert_crl_file(3)andX509_load_cert_file(3)X509_LOOKUP_by_subject(3)andX509_LOOKUP_ctrl(3)X509_NAME_hash(3)X509_new(3)X509_REQ_new(3)andX509_REQ_verify(3)X509_STORE_CTX_new(3),X509_STORE_set_default_paths(3),X509_STORE_load_file(3),X509_STORE_load_locations(3)andX509_STORE_load_store(3)
whew. okay. breathe. you survived. π«Ά
β New functions that use a Library context β
The following functions can be passed a library context if required. Passing NULL will use the default library context. easy peasy π
BIO_new_from_core_bio(3)EVP_ASYM_CIPHER_fetch(3)andEVP_ASYM_CIPHER_do_all_provided(3)EVP_CIPHER_fetch(3)andEVP_CIPHER_do_all_provided(3)EVP_default_properties_enable_fips(3)andEVP_default_properties_is_fips_enabled(3)EVP_KDF_fetch(3)andEVP_KDF_do_all_provided(3)EVP_KEM_fetch(3)andEVP_KEM_do_all_provided(3)EVP_KEYEXCH_fetch(3)andEVP_KEYEXCH_do_all_provided(3)EVP_KEYMGMT_fetch(3)andEVP_KEYMGMT_do_all_provided(3)EVP_MAC_fetch(3)andEVP_MAC_do_all_provided(3)EVP_MD_fetch(3)andEVP_MD_do_all_provided(3)EVP_PKEY_CTX_new_from_pkey(3)EVP_PKEY_Q_keygen(3)EVP_Q_mac(3)andEVP_Q_digest(3)EVP_RAND(3)andEVP_RAND_do_all_provided(3)EVP_set_default_properties(3)EVP_SIGNATURE_fetch(3)andEVP_SIGNATURE_do_all_provided(3)OSSL_CMP_CTX_new(3)andOSSL_CMP_SRV_CTX_new(3)OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert(3)OSSL_CRMF_MSG_create_popo(3)andOSSL_CRMF_MSGS_verify_popo(3)OSSL_CRMF_pbm_new(3)andOSSL_CRMF_pbmp_new(3)OSSL_DECODER_CTX_add_extra(3)andOSSL_DECODER_CTX_new_for_pkey(3)OSSL_DECODER_fetch(3)andOSSL_DECODER_do_all_provided(3)OSSL_ENCODER_CTX_add_extra(3)OSSL_ENCODER_fetch(3)andOSSL_ENCODER_do_all_provided(3)OSSL_LIB_CTX_free(3),OSSL_LIB_CTX_load_config(3)andOSSL_LIB_CTX_set0_default(3)
and that's the list, gorgeous. keep reading, the next part's coming π
OSSL_PROVIDER_add_builtin(3),OSSL_PROVIDER_available(3),OSSL_PROVIDER_do_all(3),OSSL_PROVIDER_load(3),OSSL_PROVIDER_set_default_search_path(3)andOSSL_PROVIDER_try_load(3)OSSL_SELF_TEST_get_callback(3)andOSSL_SELF_TEST_set_callback(3)OSSL_STORE_attach(3)OSSL_STORE_LOADER_fetch(3)andOSSL_STORE_LOADER_do_all_provided(3)RAND_get0_primary(3),RAND_get0_private(3),RAND_get0_public(3),RAND_set_DRBG_type(3)andRAND_set_seed_source_type(3)
π§ Providers π§
okay so providers are the whole moment, and they get their own deep dive in "Providers" in crypto(7). Also go peek at "OPENSSL PROVIDERS" in crypto(7) while you're there bestie π
β¨ Fetching algorithms and property queries β¨
Implicit and Explicit Fetching is spelled out in full detail over at "ALGORITHM FETCHING" in crypto(7). literally the lore.
π Mapping EVP controls and flags to provider OSSL_PARAM(3) parameters π
So the functions that already exist for controls (think EVP_CIPHER_CTX_ctrl(3)) and for messing with flags (think EVP_MD_CTX_set_flags(3)) are internally using OSSL_PARAMS to shuttle info to and from provider objects. Want the extra lore on params? It's all in OSSL_PARAM(3) π«Ά
For ciphers, go read "CONTROLS" in EVP_EncryptInit(3), "FLAGS" in EVP_EncryptInit(3) and "PARAMETERS" in EVP_EncryptInit(3).
For digests, it's "CONTROLS" in EVP_DigestInit(3), "FLAGS" in EVP_DigestInit(3) and "PARAMETERS" in EVP_DigestInit(3).
π Deprecation of Low Level Functions π
A significant number of APIs got deprecated in OpenSSL 3.0 π no bc it's a lot. This section walks you through some of the common categories of those deprecations. And for the actual list of deprecated functions that fall into these categories, go look at "Deprecated function mappings" π
Providers are a replacement for engines and low-level method overrides
Any accessor that uses an ENGINE is deprecated, periodt (yes, that includes EVP_PKEY_set1_engine()). If your app is on engines, switch to providers instead π
Back before providers existed, algorithms got overridden by swapping out the methods the algorithm used. Every single one of those methods β like RSA_new_method() and RSA_meth_new() β is deprecated now, and you replace all of it by using providers. slay.
Deprecated i2d and d2i functions for low-level key types
Any i2d and d2i function that takes a low-level key type β d2i_DHparams() is the example β is deprecated π Applications should be using the OSSL_DECODER(3) and OSSL_ENCODER(3) APIs to read and write files instead. More details live at "Migration" in d2i_RSAPrivateKey(3) π
Deprecated low-level key object getters and setters
If your app sets or gets low-level key objects (we're talking EVP_PKEY_set1_DH() or EVP_PKEY_get0() energy), swap over to the OSSL_ENCODER API (see OSSL_ENCODER_to_bio(3)) or the OSSL_DECODER API (see OSSL_DECODER_from_bio(3)). Or, alternate route bestie, use EVP_PKEY_fromdata(3) or EVP_PKEY_todata(3) π¦
Deprecated low-level key parameter getters
Functions that reach directly into low-level objects β like RSA_get0_n(3) β are deprecated now. π Applications should use one of these instead:
EVP_PKEY_get_bn_param(3)EVP_PKEY_get_int_param(3)EVP_PKEY_get_size_t_param(3)EVP_PKEY_get_utf8_string_param(3)EVP_PKEY_get_octet_string_param(3)EVP_PKEY_get_params(3)
that's how you access fields from an EVP_PKEY β¨
The parameters you can actually get are listed here: "Common RSA parameters" in EVP_PKEY-RSA(7), "Common EC parameters" in EVP_PKEY-EC(7), "DSA parameters" in EVP_PKEY-DSA(7), "DH parameters" in EVP_PKEY-DH(7), "FFC parameters" in EVP_PKEY-FFC(7), "Common X25519, X448, ED25519 and ED448 parameters" in EVP_PKEY-X25519(7), "Common parameters" in EVP_PKEY-ML-DSA(7), and "Common parameters" in EVP_PKEY-ML-KEM(7). And if you want all the fields at once, apps can also use EVP_PKEY_todata(3) π
Deprecated low-level key parameter setters
Functions that touch low-level objects directly, like RSA_set0_crt_params(3), are deprecated now. π Applications should use EVP_PKEY_fromdata(3) to make new keys out of user-provided key data.
β οΈ Keys should be immutable once they're created, so if you genuinely need to modify one, the user may useEVP_PKEY_todata(3),OSSL_PARAM_merge(3), andEVP_PKEY_fromdata(3)together to build a modified key.
See "Examples" in EVP_PKEY-DH(7) for more. And for generating a key from parameters, go read "Deprecated low-level key generation functions" π
Deprecated low-level object creation
Back in the day, low-level objects were created with methods like RSA_new(3), RSA_up_ref(3) and RSA_free(3). Applications should use the high-level EVP_PKEY APIs instead, e.g. EVP_PKEY_new(3), EVP_PKEY_up_ref(3) and EVP_PKEY_free(3). Also go see EVP_PKEY_CTX_new_from_name(3) and EVP_PKEY_CTX_new_from_pkey(3) β¨
And bestie, EVP_PKEYs can be created in a whole bunch of ways: see also "Deprecated low-level key generation functions", "Deprecated low-level key reading and writing functions" and "Deprecated low-level key parameter setters". we love options.
Deprecated low-level encryption functions
Low-level encryption functions like AES_encrypt(3) and AES_decrypt(3) have been informally discouraged from use for a long time π Applications should instead use the high level EVP APIs: EVP_EncryptInit_ex(3), EVP_EncryptUpdate(3), and EVP_EncryptFinal_ex(3), or EVP_DecryptInit_ex(3), EVP_DecryptUpdate(3) and EVP_DecryptFinal_ex(3).
Deprecated low-level digest functions
Using low-level digest functions like SHA1_Init(3) has also been informally discouraged for a long time. Applications should be on the high level EVP APIs instead: EVP_DigestInit_ex(3), EVP_DigestUpdate(3) and EVP_DigestFinal_ex(3), or the quick one-shot EVP_Q_digest(3) πΈ
Also heads up: the functions SHA1(3), SHA224(3), SHA256(3), SHA384(3) and SHA512(3) have changed into macros that use EVP_Q_digest(3). not me finding out things changed π
Deprecated low-level signing functions
Low-level signing functions like DSA_sign(3) have been informally discouraged for a long time. Instead, applications should use EVP_DigestSign(3) and EVP_DigestVerify(3). See also EVP_SIGNATURE-RSA(7), EVP_SIGNATURE-DSA(7), EVP_SIGNATURE-ECDSA(7) and EVP_SIGNATURE-ED25519(7) π«Ά
Deprecated low-level MAC functions
Low-level MAC functions like CMAC_Init(3) are deprecated. Applications should use the new EVP_MAC(3) interface instead, using EVP_MAC_CTX_new(3), EVP_MAC_CTX_free(3), EVP_MAC_init(3), EVP_MAC_update(3) and EVP_MAC_final(3) β or the single-shot MAC function EVP_Q_mac(3).
Extra reading: EVP_MAC(3), EVP_MAC-HMAC(7), EVP_MAC-CMAC(7), EVP_MAC-GMAC(7), EVP_MAC-KMAC(7), EVP_MAC-BLAKE2(7), EVP_MAC-Poly1305(7) and EVP_MAC-Siphash(7) π
And note: the one-shot method HMAC() is still available for compatibility purposes β it's not gone, bestie β but if you need a library context, that can also be replaced by using EVP_Q_MAC β¨
Deprecated low-level validation functions
Low-level validation functions like DH_check(3) have been informally discouraged from use for a long time. Applications should use the high-level EVP_PKEY APIs instead, like EVP_PKEY_check(3), EVP_PKEY_param_check(3), EVP_PKEY_param_check_quick(3), EVP_PKEY_public_check(3), EVP_PKEY_public_check_quick(3), EVP_PKEY_private_check(3), and EVP_PKEY_pairwise_check(3). look at that list, she's thorough π
Deprecated low-level key exchange functions
A bunch of low-level functions have been informally discouraged from use for a long time. Applications should use EVP_PKEY_derive(3) instead. See EVP_KEYEXCH-DH(7), EVP_KEYEXCH-ECDH(7) and EVP_KEYEXCH-X25519(7) π¦
Deprecated low-level key generation functions
Same story, many low-level functions have been informally discouraged from use for a long time. Applications should use EVP_PKEY_keygen_init(3) and EVP_PKEY_generate(3) instead, exactly as described in EVP_PKEY-DSA(7), EVP_PKEY-DH(7), EVP_PKEY-RSA(7), EVP_PKEY-EC(7) and EVP_PKEY-X25519(7).
There's also the 'quick' one-shot function EVP_PKEY_Q_keygen(3), plus macros for the most common cases: <EVP_RSA_gen(3)> and EVP_EC_gen(3) β those may be used too π
Deprecated low-level key reading and writing functions
Using low-level objects (like DSA) has been informally discouraged for a long time. Functions that read and write these low-level objects β e.g. PEM_read_DSA_PUBKEY() β should be replaced. Applications should use OSSL_ENCODER_to_bio(3) and OSSL_DECODER_from_bio(3) instead π
Deprecated low-level key printing functions
Again: using low-level objects (like DSA) has been informally discouraged for a long time. Functions that print these low-level objects, like DSA_print(), should be replaced with the equivalent EVP_PKEY functions. Applications should use one of EVP_PKEY_print_public(3), EVP_PKEY_print_private(3), EVP_PKEY_print_params(3), EVP_PKEY_print_public_fp(3), EVP_PKEY_print_private_fp(3) or EVP_PKEY_print_params_fp(3). And fun fact for the lore: internally, these use OSSL_ENCODER_to_bio(3) and OSSL_DECODER_from_bio(3) π
π Deprecated function mappings π
The following functions have been deprecated in 3.0. buckle up bestie, it's a long list π
AES_bi_ige_encrypt()andAES_ige_encrypt()
There is no replacement for the IGE functions. New code should not use these modes. These undocumented functions were never integrated into the EVP layer. They implemented the AES Infinite Garble Extension (IGE) mode and AES Bi-directional IGE mode. These modes were never formally standardised, and usage of these functions is believed to be very small. In particular AES_bi_ige_encrypt() has a known bug: it accepts 2 AES keys, but only one is ever used π The security implications are believed to be minimal, but this issue was never fixed, for backwards compatibility reasons.
AES_encrypt(),AES_decrypt(),AES_set_encrypt_key(),AES_set_decrypt_key(),AES_cbc_encrypt(),AES_cfb128_encrypt(),AES_cfb1_encrypt(),AES_cfb8_encrypt(),AES_ecb_encrypt(),AES_ofb128_encrypt()
(yes, the whole squad π)
AES_unwrap_key(),AES_wrap_key()
See "Deprecated low-level encryption functions"
AES_options()
There is no replacement. It returned a string indicating if the AES code was unrolled.
ASN1_digest(),ASN1_sign(),ASN1_verify()
There are no replacements. These old functions are not used, and could be disabled with the macro NO_ASN1_OLD since OpenSSL 0.9.7.
ASN1_STRING_length_set()
Use ASN1_STRING_set(3) or ASN1_STRING_set0(3) instead. This was a potentially unsafe function that could change the bounds of a previously passed in pointer π
BF_encrypt(),BF_decrypt(),BF_set_key(),BF_cbc_encrypt(),BF_cfb64_encrypt(),BF_ecb_encrypt(),BF_ofb64_encrypt()
See "Deprecated low-level encryption functions". The Blowfish algorithm has been moved to the Legacy Provider.
BF_options()
There is no replacement. This option returned a constant string.
BIO_get_callback(),BIO_set_callback(),BIO_debug_callback()
Use the respective non-deprecated _ex() functions.
BN_is_prime_ex(),BN_is_prime_fasttest_ex()- Use
BN_check_prime(3)which avoids possible misuse and always uses at least 64 rounds of the Miller-Rabin primality test. π§ BN_pseudo_rand(),BN_pseudo_rand_range()β bestie these are done. UseBN_rand(3)andBN_rand_range(3). β¨BN_X931_derive_prime_ex(),BN_X931_generate_prime_ex(),BN_X931_generate_Xpq()β okay so there are literally no replacements for these low-level functions π« they were used internally byRSA_X931_derive_ex()andRSA_X931_generate_key_ex(), and guess what, those are deprecated too. UseEVP_PKEY_keygen(3)instead. πCamellia_encrypt(),Camellia_decrypt(),Camellia_set_key(),Camellia_cbc_encrypt(),Camellia_cfb128_encrypt(),Camellia_cfb1_encrypt(),Camellia_cfb8_encrypt(),Camellia_ctr128_encrypt(),Camellia_ecb_encrypt(),Camellia_ofb128_encrypt()β go read"Deprecated low-level encryption functions", it's all there. πΈCAST_encrypt(),CAST_decrypt(),CAST_set_key(),CAST_cbc_encrypt(),CAST_cfb64_encrypt(),CAST_ecb_encrypt(),CAST_ofb64_encrypt()β same deal, see"Deprecated low-level encryption functions". Also heads up: the CAST algorithm has been moved to the Legacy Provider. πCMAC_CTX_new(),CMAC_CTX_cleanup(),CMAC_CTX_copy(),CMAC_CTX_free(),CMAC_CTX_get0_cipher_ctx()β yep,"Deprecated low-level MAC functions". πCMAC_Init(),CMAC_Update(),CMAC_Final(),CMAC_resume()β also"Deprecated low-level MAC functions", no crumbs left. πCRYPTO_mem_ctrl(),CRYPTO_mem_debug_free(),CRYPTO_mem_debug_malloc(),CRYPTO_mem_debug_pop(),CRYPTO_mem_debug_push(),CRYPTO_mem_debug_realloc(),CRYPTO_mem_leaks(),CRYPTO_mem_leaks_cb(),CRYPTO_mem_leaks_fp(),CRYPTO_set_mem_debug()β memory-leak checking has been deprecated in favor of more modern development tools, such as compiler memory and leak sanitizers or Valgrind. it's giving glow-up energy ngl πCRYPTO_cts128_encrypt_block(),CRYPTO_cts128_encrypt(),CRYPTO_cts128_decrypt_block(),CRYPTO_cts128_decrypt(),CRYPTO_nistcts128_encrypt_block(),CRYPTO_nistcts128_encrypt(),CRYPTO_nistcts128_decrypt_block(),CRYPTO_nistcts128_decrypt()β use the higher level functionsEVP_CipherInit_ex2(),EVP_CipherUpdate()andEVP_CipherFinal_ex()instead π«Ά check the"cts_mode"parameter in"Gettable and Settable EVP_CIPHER_CTX parameters"inEVP_EncryptInit(3), and slide into"EXAMPLES"inEVP_EncryptInit(3)for a AES-256-CBC-CTS example. β¨d2i_DHparams(),d2i_DHxparams(),d2i_DSAparams(),d2i_DSAPrivateKey(),d2i_DSAPrivateKey_bio(),d2i_DSAPrivateKey_fp(),d2i_DSA_PUBKEY(),d2i_DSA_PUBKEY_bio(),d2i_DSA_PUBKEY_fp(),d2i_DSAPublicKey(),d2i_ECParameters(),d2i_ECPrivateKey(),d2i_ECPrivateKey_bio(),d2i_ECPrivateKey_fp(),d2i_EC_PUBKEY(),d2i_EC_PUBKEY_bio(),d2i_EC_PUBKEY_fp(),d2i_RSAPrivateKey(),d2i_RSAPrivateKey_bio(),d2i_RSAPrivateKey_fp(),d2i_RSA_PUBKEY(),d2i_RSA_PUBKEY_bio(),d2i_RSA_PUBKEY_fp(),d2i_RSAPublicKey(),d2i_RSAPublicKey_bio(),d2i_RSAPublicKey_fp()β see"Deprecated i2d and d2i functions for low-level key types"πo2i_ECPublicKey()β useEVP_PKEY_set1_encoded_public_key(3). see"Deprecated low-level key parameter setters"πDES_crypt(),DES_fcrypt(),DES_encrypt1(),DES_encrypt2(),DES_encrypt3(),DES_decrypt3(),DES_ede3_cbc_encrypt(),DES_ede3_cfb64_encrypt(),DES_ede3_cfb_encrypt(),DES_ede3_ofb64_encrypt(),DES_ecb_encrypt(),DES_ecb3_encrypt(),DES_ofb64_encrypt(),DES_ofb_encrypt(),DES_cfb64_encrypt,DES_cfb_encrypt(),DES_cbc_encrypt(),DES_ncbc_encrypt(),DES_pcbc_encrypt(),DES_xcbc_encrypt(),DES_cbc_cksum(),DES_quad_cksum(),DES_check_key_parity(),DES_is_weak_key(),DES_key_sched(),DES_options(),DES_random_key(),DES_set_key(),DES_set_key_checked(),DES_set_key_unchecked(),DES_set_odd_parity(),DES_string_to_2keys(),DES_string_to_key()β okay bestie, that's a whole roster π see"Deprecated low-level encryption functions". and the algorithms for"DESX-CBC","DES-ECB","DES-CBC","DES-OFB","DES-CFB","DES-CFB1"and"DES-CFB8"have been moved to the Legacy Provider.DH_bits(),DH_security_bits(),DH_size()β useEVP_PKEY_get_bits(3),EVP_PKEY_get_security_bits(3)andEVP_PKEY_get_size(3). βDH_check(),DH_check_ex(),DH_check_params(),DH_check_params_ex(),DH_check_pub_key(),DH_check_pub_key_ex()β see"Deprecated low-level validation functions"π¦DH_clear_flags(),DH_test_flags(),DH_set_flags()β so theDH_FLAG_CACHE_MONT_Pflag has been deprecated without replacement π andDH_FLAG_TYPE_DHandDH_FLAG_TYPE_DHXhave been deprecated too. useEVP_PKEY_is_a()to determine the type of a key. there is no replacement for setting these flags. periodt.DH_compute_key()DH_compute_key_padded()β see"Deprecated low-level key exchange functions". πDH_new(),DH_new_by_nid(),DH_free(),DH_up_ref()β see"Deprecated low-level object creation"πDH_generate_key(),DH_generate_parameters_ex()β see"Deprecated low-level key generation functions". β¨DH_get0_pqg(),DH_get0_p(),DH_get0_q(),DH_get0_g(),DH_get0_key(),DH_get0_priv_key(),DH_get0_pub_key(),DH_get_length(),DH_get_nid()β see"Deprecated low-level key parameter getters"πDH_get_1024_160(),DH_get_2048_224(),DH_get_2048_256()β applications should instead set theOSSL_PKEY_PARAM_GROUP_NAMEas specified in"DH parameters"inEVP_PKEY-DH(7))to one of"dh_1024_160","dh_2048_224"or"dh_2048_256"when generating a DH key. π₯DH_KDF_X9_42()β applications should useEVP_PKEY_CTX_set_dh_kdf_type(3)instead. πDH_get_default_method(),DH_get0_engine(),DH_meth_*(),DH_new_method(),DH_OpenSSL(),DH_get_ex_data(),DH_set_default_method(),DH_set_method(),DH_set_ex_data()β see"Providers are a replacement for engines and low-level method overrides"πDHparams_print(),DHparams_print_fp()β see"Deprecated low-level key printing functions"πDH_set0_key(),DH_set0_pqg(),DH_set_length()β see"Deprecated low-level key parameter setters"πΈDSA_bits(),DSA_security_bits(),DSA_size()β useEVP_PKEY_get_bits(3),EVP_PKEY_get_security_bits(3)andEVP_PKEY_get_size(3). βDHparams_dup(),DSA_dup_DH()β there is no direct replacement π applications may useEVP_PKEY_copy_parameters(3)andEVP_PKEY_dup(3)instead. π«ΆDSA_generate_key(),DSA_generate_parameters_ex()β see"Deprecated low-level key generation functions". β¨DSA_get0_engine(),DSA_get_default_method(),DSA_get_ex_data(),DSA_get_method(),DSA_meth_*(),DSA_new_method(),DSA_OpenSSL(),DSA_set_default_method(),DSA_set_ex_data(),DSA_set_method()β see"Providers are a replacement for engines and low-level method overrides". πDSA_get0_p(),DSA_get0_q(),DSA_get0_g(),DSA_get0_pqg(),DSA_get0_key(),DSA_get0_priv_key(),DSA_get0_pub_key()β see"Deprecated low-level key parameter getters". πDSA_new(),DSA_free(),DSA_up_ref()β see"Deprecated low-level object creation"πDSAparams_dup()β no direct replacement, sorry bestie π applications may useEVP_PKEY_copy_parameters(3)andEVP_PKEY_dup(3)instead.DSAparams_print(),DSAparams_print_fp(),DSA_print(),DSA_print_fp()β see"Deprecated low-level key printing functions"π¦DSA_set0_key(),DSA_set0_pqg()β see"Deprecated low-level key parameter setters"πΈDSA_set_flags(),DSA_clear_flags(),DSA_test_flags()β theDSA_FLAG_CACHE_MONT_Pflag has been deprecated without replacement. that's it, that's the whole note. πDSA_sign(),DSA_do_sign(),DSA_sign_setup(),DSA_verify(),DSA_do_verify()β see"Deprecated low-level signing functions". β¨ECDH_compute_key()β see"Deprecated low-level key exchange functions". πECDH_KDF_X9_62()β applications may either set this using the helper functionEVP_PKEY_CTX_set_ecdh_kdf_type(3)or by setting anOSSL_PARAM(3)using the"kdf-type"as shown in"EXAMPLES"inEVP_KEYEXCH-ECDH(7)πECDSA_sign(),ECDSA_sign_ex(),ECDSA_sign_setup(),ECDSA_do_sign(),ECDSA_do_sign_ex(),ECDSA_verify(),ECDSA_do_verify()β see"Deprecated low-level signing functions". πECDSA_size()β applications should useEVP_PKEY_get_size(3). βEC_GF2m_simple_method(),EC_GFp_mont_method(),EC_GFp_nist_method(),EC_GFp_nistp224_method(),EC_GFp_nistp256_method(),EC_GFp_nistp521_method(),EC_GFp_simple_method()β there are no replacements for these functions π« applications should rely on the library automatically assigning a suitable method internally when anEC_GROUPis constructed. the library handles it, bestie, trust. β¨EC_GROUP_clear_free()β useEC_GROUP_free(3)instead. πEC_GROUP_get_curve_GF2m(),EC_GROUP_get_curve_GFp(),EC_GROUP_set_curve_GF2m(),EC_GROUP_set_curve_GFp()β applications should useEC_GROUP_get_curve(3)andEC_GROUP_set_curve(3). πΈEC_GROUP_have_precompute_mult(),EC_GROUP_precompute_mult(),EC_KEY_precompute_mult()β these functions are not widely used. applications should instead switch to named curves which OpenSSL has hardcoded lookup tables for. π₯EC_GROUP_new(),EC_GROUP_method_of(),EC_POINT_method_of()βEC_METHODis now an internal-only concept and a suitableEC_METHODis assigned internally without application intervention. users ofEC_GROUP_new()should switch to a different suitable constructor. πEC_KEY_can_sign()β applications should useEVP_PKEY_can_sign(3)instead. πEC_KEY_check_key()β see"Deprecated low-level validation functions"π¦EC_KEY_set_flags(),EC_KEY_get_flags(),EC_KEY_clear_flags()β see"Common EC parameters"inEVP_PKEY-EC(7)which handles flags as separate parameters forOSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE,OSSL_PKEY_PARAM_EC_ENCODING,OSSL_PKEY_PARAM_USE_COFACTOR_ECDHandOSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC. see also"EXAMPLES"inEVP_PKEY-EC(7)β¨EC_KEY_dup(),EC_KEY_copy()β there is no direct replacement π applications may useEVP_PKEY_copy_parameters(3)andEVP_PKEY_dup(3)instead. π«ΆEC_KEY_decoded_from_explicit_params()β there is no replacement. that's the whole thing. πEC_KEY_generate_key()β see"Deprecated low-level key generation functions". πEC_KEY_get0_group(),EC_KEY_get0_private_key(),EC_KEY_get0_public_key(),EC_KEY_get_conv_form(),EC_KEY_get_enc_flags()β see"Deprecated low-level key parameter getters". πEC_KEY_get0_engine(),EC_KEY_get_default_method(),EC_KEY_get_method(),EC_KEY_new_method(),EC_KEY_get_ex_data(),EC_KEY_OpenSSL(),EC_KEY_set_ex_data(),EC_KEY_set_default_method(),EC_KEY_METHOD_*(),EC_KEY_set_method()β yeah all of these are caught up in the whole engine / method-override mess π. Go read the section "Providers are a replacement for engines and low-level method overrides".EC_METHOD_get_field_type()β swap it forEC_GROUP_get_field_type(3), bestie. Same section as above: "Providers are a replacement for engines and low-level method overrides".EC_KEY_key2buf(),EC_KEY_oct2key(),EC_KEY_oct2priv(),EC_KEY_priv2buf(),EC_KEY_priv2oct()β okay so, there is literally nothing to replace these with. No replacement. Nada. πEC_KEY_new(),EC_KEY_new_by_curve_name(),EC_KEY_free(),EC_KEY_up_ref()β checkout "Deprecated low-level object creation".EC_KEY_print(),EC_KEY_print_fp()β peep "Deprecated low-level key printing functions".EC_KEY_set_asn1_flag(),EC_KEY_set_conv_form(),EC_KEY_set_enc_flags()β "Deprecated low-level key parameter setters", babes.EC_KEY_set_group(),EC_KEY_set_private_key(),EC_KEY_set_public_key(),EC_KEY_set_public_key_affine_coordinates()β also "Deprecated low-level key parameter setters" β¨.ECParameters_print(),ECParameters_print_fp(),ECPKParameters_print(),ECPKParameters_print_fp()β head to "Deprecated low-level key printing functions".EC_POINT_bn2point(),EC_POINT_point2bn()β these ones were never it, fr fr. They weren't really useful, because EC point serialization formats are not individual big-endian integers. Like, that's just not what's happening there π .EC_POINT_get_affine_coordinates_GF2m(),EC_POINT_get_affine_coordinates_GFp(),EC_POINT_set_affine_coordinates_GF2m(),EC_POINT_set_affine_coordinates_GFp()β apps should useEC_POINT_get_affine_coordinates(3)andEC_POINT_set_affine_coordinates(3)instead π«Ά.EC_POINT_get_Jprojective_coordinates_GFp(),EC_POINT_set_Jprojective_coordinates_GFp()β not widely used, ngl. Applications should instead use theEC_POINT_set_affine_coordinates(3)andEC_POINT_get_affine_coordinates(3)functions.EC_POINT_make_affine(),EC_POINTs_make_affine()β no replacement, sorry bestie. They were not widely used, and OpenSSL just automatically does this conversion when it needs to. Main character energy: it handles it itself π.EC_POINT_set_compressed_coordinates_GF2m(),EC_POINT_set_compressed_coordinates_GFp()β applications should useEC_POINT_set_compressed_coordinates(3)instead.EC_POINTs_mul()β not widely used. Applications should instead use theEC_POINT_mul(3)function.ENGINE_*()β okay so all engine functions are deprecated, periodt. An engine should be rewritten as a provider. See "Providers are a replacement for engines and low-level method overrides".ERR_load_*(),ERR_func_error_string(),ERR_get_error_line(),ERR_get_error_line_data(),ERR_get_state()β not needed anymore because OpenSSL now loads error strings automatically β¨.ERR_peek_error_line_data(),ERR_peek_last_error_line_data()β the new functions areERR_peek_error_func(3),ERR_peek_last_error_func(3),ERR_peek_error_data(3),ERR_peek_last_error_data(3),ERR_get_error_all(3),ERR_peek_error_all(3)andERR_peek_last_error_all(3). Applications should useERR_get_error_all(3), or pick information with theERR_peekfunctions and finish off with getting the error code by usingERR_get_error(3).EVP_CIPHER_CTX_iv(),EVP_CIPHER_CTX_iv_noconst(),EVP_CIPHER_CTX_original_iv()β applications should instead useEVP_CIPHER_CTX_get_updated_iv(3),EVP_CIPHER_CTX_get_updated_iv(3)andEVP_CIPHER_CTX_get_original_iv(3)respectively. SeeEVP_CIPHER_CTX_get_original_iv(3)for further information.EVP_CIPHER_meth_*(),EVP_MD_CTX_set_update_fn(),EVP_MD_CTX_update_fn(),EVP_MD_meth_*()β go read "Providers are a replacement for engines and low-level method overrides".EVP_PKEY_CTRL_PKCS7_ENCRYPT(),EVP_PKEY_CTRL_PKCS7_DECRYPT(),EVP_PKEY_CTRL_PKCS7_SIGN(),EVP_PKEY_CTRL_CMS_ENCRYPT(),EVP_PKEY_CTRL_CMS_DECRYPT(), andEVP_PKEY_CTRL_CMS_SIGN()β these control operations aren't invoked by the OpenSSL library anymore π . They're replaced by direct checks of the key operation against the key type when the operation is initialized.EVP_PKEY_CTX_get0_dh_kdf_ukm(),EVP_PKEY_CTX_get0_ecdh_kdf_ukm()β see the "kdf-ukm" item in "DH key exchange parameters" inEVP_KEYEXCH-DH(7)and "ECDH Key Exchange parameters" inEVP_KEYEXCH-ECDH(7). These functions are obsolete and should not be required, like, at all.EVP_PKEY_CTX_set_rsa_keygen_pubexp()β applications should useEVP_PKEY_CTX_set1_rsa_keygen_pubexp(3)instead.EVP_PKEY_cmp(),EVP_PKEY_cmp_parameters()β applications should useEVP_PKEY_eq(3)andEVP_PKEY_parameters_eq(3)instead. SeeEVP_PKEY_copy_parameters(3)for further details.EVP_PKEY_encrypt_old(),EVP_PKEY_decrypt_old(), β applications should useEVP_PKEY_encrypt_init(3)andEVP_PKEY_encrypt(3)orEVP_PKEY_decrypt_init(3)andEVP_PKEY_decrypt(3)instead.EVP_PKEY_get0()β heads up π¨ this function returnsNULLif the key comes from a provider.EVP_PKEY_get0_DH(),EVP_PKEY_get0_DSA(),EVP_PKEY_get0_EC_KEY(),EVP_PKEY_get0_RSA(),EVP_PKEY_get1_DH(),EVP_PKEY_get1_DSA(),EVP_PKEY_get1_EC_KEYandEVP_PKEY_get1_RSA(),EVP_PKEY_get0_hmac(),EVP_PKEY_get0_poly1305(),EVP_PKEY_get0_siphash()β see "Functions that return an internal key should be treated as read only".EVP_PKEY_meth_*()β see "Providers are a replacement for engines and low-level method overrides".EVP_PKEY_new_CMAC_key()β see "Deprecated low-level MAC functions".EVP_PKEY_assign(),EVP_PKEY_set1_DH(),EVP_PKEY_set1_DSA(),EVP_PKEY_set1_EC_KEY(),EVP_PKEY_set1_RSA()β see "Deprecated low-level key object getters and setters".EVP_PKEY_set1_tls_encodedpoint()EVP_PKEY_get1_tls_encodedpoint()β these used to be libssl's way of setting or getting an encoded public key into/from anEVP_PKEYobject. With OpenSSL 3.0 they're replaced by the more generic functionsEVP_PKEY_set1_encoded_public_key(3)andEVP_PKEY_get1_encoded_public_key(3). The old versions have been converted to deprecated macros that just call the new functions π¦.EVP_PKEY_set1_engine(),EVP_PKEY_get0_engine()β see "Providers are a replacement for engines and low-level method overrides".EVP_PKEY_set_alias_type()β this function has been removed. There is no replacement. See "EVP_PKEY_set_alias_type() method has been removed" π.HMAC_Init_ex(),HMAC_Update(),HMAC_Final(),HMAC_size()β see "Deprecated low-level MAC functions".HMAC_CTX_new(),HMAC_CTX_free(),HMAC_CTX_copy(),HMAC_CTX_reset(),HMAC_CTX_set_flags(),HMAC_CTX_get_md()β see "Deprecated low-level MAC functions".i2d_DHparams(),i2d_DHxparams()β see "Deprecated low-level key reading and writing functions" and "Migration" ind2i_RSAPrivateKey(3).i2d_DSAparams(),i2d_DSAPrivateKey(),i2d_DSAPrivateKey_bio(),i2d_DSAPrivateKey_fp(),i2d_DSA_PUBKEY(),i2d_DSA_PUBKEY_bio(),i2d_DSA_PUBKEY_fp(),i2d_DSAPublicKey()β see "Deprecated low-level key reading and writing functions" and "Migration" ind2i_RSAPrivateKey(3).i2d_ECParameters(),i2d_ECPrivateKey(),i2d_ECPrivateKey_bio(),i2d_ECPrivateKey_fp(),i2d_EC_PUBKEY(),i2d_EC_PUBKEY_bio(),i2d_EC_PUBKEY_fp()β see "Deprecated low-level key reading and writing functions" and "Migration" ind2i_RSAPrivateKey(3).i2o_ECPublicKey()β useEVP_PKEY_get1_encoded_public_key(3). See "Deprecated low-level key parameter getters".i2d_RSAPrivateKey(),i2d_RSAPrivateKey_bio(),i2d_RSAPrivateKey_fp(),i2d_RSA_PUBKEY(),i2d_RSA_PUBKEY_bio(),i2d_RSA_PUBKEY_fp(),i2d_RSAPublicKey(),i2d_RSAPublicKey_bio(),i2d_RSAPublicKey_fp()β see "Deprecated low-level key reading and writing functions" and "Migration" ind2i_RSAPrivateKey(3).IDEA_encrypt(),IDEA_set_decrypt_key(),IDEA_set_encrypt_key(),IDEA_cbc_encrypt(),IDEA_cfb64_encrypt(),IDEA_ecb_encrypt(),IDEA_ofb64_encrypt()β see "Deprecated low-level encryption functions". IDEA has been moved to the Legacy Provider π§³.IDEA_options()β no replacement. This function returned a constant string, that's it, that's the whole gig.MD2(),MD2_Init(),MD2_Update(),MD2_Final()β see "Deprecated low-level encryption functions". MD2 has been moved to the Legacy Provider.MD2_options()β no replacement. This function returned a constant string.MD4(),MD4_Init(),MD4_Update(),MD4_Final(),MD4_Transform()β see "Deprecated low-level encryption functions". MD4 has been moved to the Legacy Provider.MDC2(),MDC2_Init(),MDC2_Update(),MDC2_Final()β see "Deprecated low-level encryption functions". MDC2 has been moved to the Legacy Provider.MD5(),MD5_Init(),MD5_Update(),MD5_Final(),MD5_Transform()β see "Deprecated low-level encryption functions".NCONF_WIN32()β this undocumented function has no replacement. See "HISTORY" inconfig(5)for more details.OCSP_parse_url()β useOSSL_HTTP_parse_url(3)instead.OCSP_REQ_CTXtype andOCSP_REQ_CTX_*()functions β these methods were used to collect all the necessary data to form an HTTP request, and to perform the HTTP transfer with that request. With OpenSSL 3.0, the type isOSSL_HTTP_REQ_CTX, and the deprecated functions are replaced withOSSL_HTTP_REQ_CTX_*(). SeeOSSL_HTTP_REQ_CTX(3)for additional details.OPENSSL_fork_child(),OPENSSL_fork_parent(),OPENSSL_fork_prepare()β there is no replacement for these functions. These pthread fork support methods were unused by OpenSSL π.OSSL_STORE_ctrl(),OSSL_STORE_do_all_loaders(),OSSL_STORE_LOADER_get0_engine(),OSSL_STORE_LOADER_get0_scheme(),OSSL_STORE_LOADER_new(),OSSL_STORE_LOADER_set_attach(),OSSL_STORE_LOADER_set_close(),OSSL_STORE_LOADER_set_ctrl(),OSSL_STORE_LOADER_set_eof(),OSSL_STORE_LOADER_set_error(),OSSL_STORE_LOADER_set_expect(),OSSL_STORE_LOADER_set_find(),OSSL_STORE_LOADER_set_load(),OSSL_STORE_LOADER_set_open(),OSSL_STORE_LOADER_set_open_ex(),OSSL_STORE_register_loader(),OSSL_STORE_unregister_loader(),OSSL_STORE_vctrl()- Genuinely obsessed with this glow-up era for key & engine stuff, bestie. Engines used to get so much main character energy from these functions that made loaders for whatever schemes they supported. But plot twist: they're all deprecated now, and the provider era said "you're cancelled." Use provider implementations instead β check
provider-storemgmt(7)for the tea. π PEM_read_DHparams(),PEM_read_bio_DHparams(),PEM_read_DSAparams(),PEM_read_bio_DSAparams(),PEM_read_DSAPrivateKey(),PEM_read_DSA_PUBKEY(),PEM_read_bio_DSAPrivateKeyandPEM_read_bio_DSA_PUBKEY(),PEM_read_ECPKParameters(),PEM_read_ECPrivateKey(),PEM_read_EC_PUBKEY(),PEM_read_bio_ECPKParameters(),PEM_read_bio_ECPrivateKey(),PEM_read_bio_EC_PUBKEY(),PEM_read_RSAPrivateKey(),PEM_read_RSA_PUBKEY(),PEM_read_RSAPublicKey(),PEM_read_bio_RSAPrivateKey(),PEM_read_bio_RSA_PUBKEY(),PEM_read_bio_RSAPublicKey(),PEM_write_bio_DHparams(),PEM_write_bio_DHxparams(),PEM_write_DHparams(),PEM_write_DHxparams(),PEM_write_DSAparams(),PEM_write_DSAPrivateKey(),PEM_write_DSA_PUBKEY(),PEM_write_bio_DSAparams(),PEM_write_bio_DSAPrivateKey(),PEM_write_bio_DSA_PUBKEY(),PEM_write_ECPKParameters(),PEM_write_ECPrivateKey(),PEM_write_EC_PUBKEY(),PEM_write_bio_ECPKParameters(),PEM_write_bio_ECPrivateKey(),PEM_write_bio_EC_PUBKEY(),PEM_write_RSAPrivateKey(),PEM_write_RSA_PUBKEY(),PEM_write_RSAPublicKey(),PEM_write_bio_RSAPrivateKey(),PEM_write_bio_RSA_PUBKEY(),PEM_write_bio_RSAPublicKey()β yeah it's a whole paragraph of vibes. See "Deprecated low-level key reading and writing functions" for the full saga. πPKCS1_MGF1()β see "Deprecated low-level encryption functions". It's giving "we don't do that here anymore." β¨RAND_get_rand_method(),RAND_set_rand_method(),RAND_OpenSSL(),RAND_set_rand_engine()β the RAND glow-up era has arrived. Apps should useRAND_set_DRBG_type(3),EVP_RAND(3)andEVP_RAND(7)instead. PeepRAND_set_rand_method(3)for the full scoop. π₯RC2_encrypt(),RC2_decrypt(),RC2_set_key(),RC2_cbc_encrypt(),RC2_cfb64_encrypt(),RC2_ecb_encrypt(),RC2_ofb64_encrypt(),RC4(),RC4_set_key(),RC4_options(),RC5_32_encrypt(),RC5_32_set_key(),RC5_32_decrypt(),RC5_32_cbc_encrypt(),RC5_32_cfb64_encrypt(),RC5_32_ecb_encrypt(),RC5_32_ofb64_encrypt()β see "Deprecated low-level encryption functions." Also the algorithms"RC2","RC4"and"RC5"have been moved to the Legacy Provider. They're living in the retirement home now. π‘RIPEMD160(),RIPEMD160_Init(),RIPEMD160_Update(),RIPEMD160_Final(),RIPEMD160_Transform()β see "Deprecated low-level digest functions." The RIPE algorithm got sent to the Legacy Provider. Not me saying RIPE like it's a fruit emoji πRSA_bits(),RSA_security_bits(),RSA_size()β use the glow-up versions:EVP_PKEY_get_bits(3),EVP_PKEY_get_security_bits(3)andEVP_PKEY_get_size(3). Same energy, better API. πRSA_check_key(),RSA_check_key_ex()β see "Deprecated low-level validation functions." Validation season is over, bestie. β βRSA_clear_flags(),RSA_flags(),RSA_set_flags(),RSA_test_flags(),RSA_setup_blinding(),RSA_blinding_off(),RSA_blinding_on()β all these RSA flags have been deprecated without replacement. RIP to:RSA_FLAG_BLINDING,RSA_FLAG_CACHE_PRIVATE,RSA_FLAG_CACHE_PUBLIC,RSA_FLAG_EXT_PKEY,RSA_FLAG_NO_BLINDING,RSA_FLAG_THREAD_SAFE,RSA_METHOD_FLAG_NO_CHECK. Gone but not forgotten. πRSA_generate_key_ex(),RSA_generate_multi_prime_key()β see "Deprecated low-level key generation functions." Key gen era is ovah. πRSA_get0_engine()β see "Providers are a replacement for engines and low-level method overrides." Engines said "bye." Providers said "hi bestie." π«ΆRSA_get0_crt_params(),RSA_get0_d(),RSA_get0_dmp1(),RSA_get0_dmq1(),RSA_get0_e(),RSA_get0_factors(),RSA_get0_iqmp(),RSA_get0_key(),RSA_get0_multi_prime_crt_params(),RSA_get0_multi_prime_factors(),RSA_get0_n(),RSA_get0_p(),RSA_get0_pss_params(),RSA_get0_q(),RSA_get_multi_prime_extra_count()β see "Deprecated low-level key parameter getters." These were lowkey the info-gathering squad. π΅οΈββοΈRSA_new(),RSA_free(),RSA_up_ref()β see "Deprecated low-level object creation." Creating RSA objects manually? No ma'am. βRSA_get_default_method(),RSA_get_ex_dataandRSA_get_method()β see "Providers are a replacement for engines and low-level method overrides." Same energy as the engine thing. πRSA_get_version()β there is no replacement. It's just gone. Periodt. πRSA_meth_*(),RSA_new_method(),RSA_null_methodandRSA_PKCS1_OpenSSL()β see "Providers are a replacement for engines and low-level method overrides." The method squad has been dissolved. π«‘RSA_padding_add_*(),RSA_padding_check_*()β see "Deprecated low-level signing functions" and "Deprecated low-level encryption functions." Padding party is cancelled. πRSA_print(),RSA_print_fp()β see "Deprecated low-level key printing functions." No more printing keys to files like it's 1999. π¨οΈRSA_public_encrypt(),RSA_private_decrypt()β see "Deprecated low-level encryption functions." Encryption era? Over. πRSA_private_encrypt(),RSA_public_decrypt()β this is equivalent to doing sign and verify recover operations (with a padding mode of none). See "Deprecated low-level signing functions." It's giving "we used to do it this way and honestly it was a choice." πRSAPrivateKey_dup(),RSAPublicKey_dup()β there is no direct replacement. Apps may useEVP_PKEY_dup(3)instead. Dupe era but make it modern. π―RSAPublicKey_it(),RSAPrivateKey_it()β see "Deprecated low-level key reading and writing functions." These were the "it" functions but now they're justβ¦ not it. πRSA_set0_crt_params(),RSA_set0_factors(),RSA_set0_key(),RSA_set0_multi_prime_params()β see "Deprecated low-level key parameter setters." Setting parameters manually is so 2010s. ποΈRSA_set_default_method(),RSA_set_method(),RSA_set_ex_data()β see "Providers are a replacement for engines and low-level method overrides." Method girlies, your time has come. π¬RSA_sign(),RSA_sign_ASN1_OCTET_STRING(),RSA_verify(),RSA_verify_ASN1_OCTET_STRING(),RSA_verify_PKCS1_PSS(),RSA_verify_PKCS1_PSS_mgf1()β see "Deprecated low-level signing functions." Signing and verifying lowkey got a whole new era. βοΈRSA_X931_derive_ex(),RSA_X931_generate_key_ex(),RSA_X931_hash_id()β there are no replacements for these functions. X931 padding can be set using "Signature Parameters" inEVP_SIGNATURE-RSA(7). SeeOSSL_SIGNATURE_PARAM_PAD_MODE. The X931 era has been archived. ποΈSEED_encrypt(),SEED_decrypt(),SEED_set_key(),SEED_cbc_encrypt(),SEED_cfb128_encrypt(),SEED_ecb_encrypt(),SEED_ofb128_encrypt()β see "Deprecated low-level encryption functions." The SEED algorithm has been moved to the Legacy Provider. It's planted in the retirement garden now. π±SHA1_Init(),SHA1_Update(),SHA1_Final(),SHA1_Transform(),SHA224_Init(),SHA224_Update(),SHA224_Final(),SHA256_Init(),SHA256_Update(),SHA256_Final(),SHA256_Transform(),SHA384_Init(),SHA384_Update(),SHA384_Final(),SHA512_Init(),SHA512_Update(),SHA512_Final(),SHA512_Transform()β see "Deprecated low-level digest functions." The SHA family reunion is cancelled, sorry besties. πSRP_Calc_A(),SRP_Calc_B(),SRP_Calc_client_key(),SRP_Calc_server_key(),SRP_Calc_u(),SRP_Calc_x(),SRP_check_known_gN_param(),SRP_create_verifier(),SRP_create_verifier_BN(),SRP_get_default_gN(),SRP_user_pwd_free(),SRP_user_pwd_new(),SRP_user_pwd_set0_sv(),SRP_user_pwd_set1_ids(),SRP_user_pwd_set_gN(),SRP_VBASE_add0_user(),SRP_VBASE_free(),SRP_VBASE_get1_by_user(),SRP_VBASE_init(),SRP_VBASE_new(),SRP_Verify_A_mod_N(),SRP_Verify_B_mod_N()β there are no replacements for the SRP functions. It's giving "we don't know her anymore." πSSL_CTX_set_tmp_dh_callback(),SSL_set_tmp_dh_callback(),SSL_CTX_set_tmp_dh(),SSL_set_tmp_dh()β these set the Diffie-Hellman (DH) parameters used by servers that require ephemeral DH keys. Instead, apps should consider using the built-in DH parameters available by callingSSL_CTX_set_dh_auto(3)orSSL_set_dh_auto(3). If custom parameters are necessary, apps can use the alternative functionsSSL_CTX_set0_tmp_dh_pkey(3)andSSL_set0_tmp_dh_pkey(3). There's no direct replacement for the "callback" functions. The callback was originally useful for having different parameters for export vs non-export ciphersuites, but export ciphersuites are no longer supported by OpenSSL. Use of the callback functions should be replaced by one of the other methods described above. The callback era was a different time. π°οΈSSL_CTX_set_tlsext_ticket_key_cb()β use the newSSL_CTX_set_tlsext_ticket_key_evp_cb(3)function instead. New function just dropped. π«WHIRLPOOL(),WHIRLPOOL_Init(),WHIRLPOOL_Update(),WHIRLPOOL_Final(),WHIRLPOOL_BitUpdate()β see "Deprecated low-level digest functions." The Whirlpool algorithm has been moved to the Legacy Provider. It's swirling in the retirement pool now. πX509_certificate_type()β this was an undocumented function. Apps can useX509_get0_pubkey(3)andX509_get0_signature(3)instead. Our girl was mysterious and undocumented, but now she's retired. πX509_http_nbio(),X509_CRL_http_nbio()β useX509_load_http(3)andX509_CRL_load_http(3)instead. HTTP era but make it functional. π
NID handling for provided keys and algorithms π’
The following functions for NID (numeric id) handling have changed semantics. This is a vibe shift, besties. β¨
EVP_PKEY_id(),EVP_PKEY_get_id()β this function used to reliably return the NID of anEVP_PKEYobject, e.g., to look up the algorithm name by callingOBJ_nid2sn(3). But with the introduction ofprovider(7)s,EVP_PKEY_id()or its new equivalentEVP_PKEY_get_id(3)might now also return the value-1(EVP_PKEY_KEYMGMT), which indicates the use of a provider to implement theEVP_PKEYobject. So the recommended glow-up is to useEVP_PKEY_get0_type_name(3)for retrieving the name of theEVP_PKEYalgorithm. The old way still works but it's giving "it's complicated." π
Using the FIPS Module in applications ποΈ
See fips_module(7) and OSSL_PROVIDER-FIPS(7) for details. FIPS era is serious era. No cap. π©
OpenSSL command line application changes π»
New applications β¨
openssl kdf uses the new EVP_KDF(3) API. openssl kdf uses the new EVP_MAC(3) API. New apps just dropped and they're serving. π
Added options β
-provider_path and -provider are available to all apps and can be used multiple times to load any providers, like the 'legacy' provider or third party providers. If used, then the 'default' provider would also need to be specified if required. The -provider_path must be specified before the -provider option. Order matters, bestie. π
The list app has many new options. See openssl-list(1) for more info. The list app ate and left no crumbs. π
-crl_lastupdate and -crl_nextupdate are used by openssl ca and allow explicit setting of fields in the generated CRL. CRL era but make it custom. π
Removed options ποΈ
Interactive mode is no longer available. It's gone. G-O-N-E. π
The -crypt option used by openssl passwd. The -c option used by openssl x509, openssl dhparam, openssl dsaparam, and openssl ecparam. These options have left the chat. π
Other Changes π
The output of Command line applications may have minor changes. These are primarily changes in capitalisation and white space. However, in some cases, there are additional differences. For example, the DH parameters output from openssl dhparam now lists 'P', 'Q', 'G' and 'pcounter' instead of 'prime', 'generator', 'subgroup order' and 'counter' respectively. New labels, same tea. β
okay bestie, the openssl commands that read keys, certificates, and CRLs are now doing the most β they auto-detect whether your input file is PEM or DER on their own, so you literally do not have to spell out the input format anymore. β¨ but if you do go ahead and use the input format option, then the format you picked is the format it's gonna require. no take-backs, no vibes-based guessing. π
openssl speed is no longer leaning on those crusty low-level API calls. π this means some of the performance numbers might not line up with previous releases because there's more overhead now β and it's highkey noticeable when you're measuring performance on the smaller data chunks. π
openssl dhparam, openssl dsa, openssl gendsa, openssl dsaparam, openssl genrsa and openssl rsa got glow-ups to use PKEY APIs. π«Ά and openssl genrsa plus openssl rsa now write PKCS #8 keys by default. slay. π
π Default settings π
"SHA256" is now the default digest for TS query used by openssl ts. periodt. π
π Deprecated apps π
openssl rsautlβ deprecated, bestie. useopenssl pkeyutlinstead. πopenssl dhparam,openssl dsa,openssl gendsa,openssl dsaparam,openssl genrsa,openssl rsa,openssl genrsaandopenssl rsaβ these are now in maintenance mode and no new features will be added to them. not me delivering the news, but there it is. π
π TLS Changes π
- TLS 1.3 FFDHE key exchange support added π₯
this uses DH safe prime named groups.
- Support for fully "pluggable" TLSv1.3 groups. π§
which means providers may supply their own group implementations (using either the "key exchange" or the "key encapsulation" methods), and libssl will automatically detect and use them. iconic behaviour honestly.
- SSL and SSL_CTX options are now 64 bit instead of 32 bit. β
the signatures of the functions to get and set options on SSL and SSL_CTX objects changed from "unsigned long" to uint64_t type.
this may require source code changes, bestie. for example it is no longer possible to use the SSL_OP_ macro values in preprocessor "#if" conditions. however it is still possible to test whether these macros are defined or not.
see SSL_CTX_get_options(3), SSL_CTX_set_options(3), SSL_get_options(3) and SSL_set_options(3).
SSL_set1_host()andSSL_add1_host()Changes πΈ
these functions now take IP literal addresses as well as actual hostnames. inclusivity ate. π
- Added SSL option
SSL_OP_CLEANSE_PLAINTEXTβ¨
if the option is set, openssl cleanses (zeroizes) plaintext bytes from internal buffers after delivering them to the application. note β the application is still responsible for cleansing other copies (e.g.: data received by SSL_read(3)). π
- Client-initiated renegotiation is disabled by default. π
to allow it, use the -client_renegotiation option, the SSL_OP_ALLOW_CLIENT_RENEGOTIATION flag, or the "ClientRenegotiation" config parameter as appropriate.
- Secure renegotiation is now required by default for TLS connections π₯
support for RFC 5746 secure renegotiation is now required by default for SSL or TLS connections to succeed. applications that require the ability to connect to legacy peers will need to explicitly set SSL_OP_LEGACY_SERVER_CONNECT. accordingly, SSL_OP_LEGACY_SERVER_CONNECT is no longer set as part of SSL_OP_ALL. π
- Combining the Configure options
no-ecandno-dhno longer disables TLSv1.3 π
typically if OpenSSL has no EC or DH algorithms then it cannot support connections with TLSv1.3. however OpenSSL now supports "pluggable" groups through providers. therefore third party providers may supply group implementations even where there are no built-in ones. attempting to create TLS connections in such a build without also disabling TLSv1.3 at run time or using third party provider groups may result in handshake failures. TLSv1.3 can be disabled at compile time using the "no-tls1_3" Configure option.
SSL_CTX_set_ciphersuites()andSSL_set_ciphersuites()changes. π
the methods now ignore unknown ciphers. they're unbothered. π
- Security callback change. π¦
the security callback, which can be customised by application code, supports the security operation SSL_SECOP_TMP_DH. this is defined to take an EVP_PKEY in the "other" parameter. in most places this is what is passed. all these places occur server side. however there was one client side call of this security operation and it passed a DH object instead. this is incorrect according to the definition of SSL_SECOP_TMP_DH, and is inconsistent with all of the other locations. therefore this client side call has been changed to pass an EVP_PKEY instead. giving "consistency is the look" energy. β¨
- New SSL option
SSL_OP_IGNORE_UNEXPECTED_EOFβ
the SSL option SSL_OP_IGNORE_UNEXPECTED_EOF is introduced. if that option is set, an unexpected EOF is ignored, it pretends a close notify was received instead and so the returned error becomes SSL_ERROR_ZERO_RETURN. gaslight gatekeep girlboss, but for EOFs. π
- The security strength of SHA1 and MD5 based signatures in TLS has been reduced. π»
this results in SSL 3, TLS 1.0, TLS 1.1 and DTLS 1.0 no longer working at the default security level of 1 and instead requires security level 0. the security level can be changed either using the cipher string with @SECLEVEL, or calling SSL_CTX_set_security_level(3). this also means that where the signature algorithms extension is missing from a ClientHello then the handshake will fail in TLS 1.2 at security level 1. this is because, although this extension is optional, failing to provide one means that OpenSSL will fallback to a default set of signature algorithms. this default set requires the availability of SHA1.
- X509 certificates signed using SHA1 are no longer allowed at security level 1 and above. π
in TLS/SSL the default security level is 1. it can be set either using the cipher string with @SECLEVEL, or calling SSL_CTX_set_security_level(3). if the leaf certificate is signed with SHA-1, a call to SSL_CTX_use_certificate(3) will fail if the security level is not lowered first. outside TLS/SSL, the default security level is -1 (effectively 0). it can be set using X509_VERIFY_PARAM_set_auth_level(3) or using the -auth_level options of the commands.
π SEE ALSO π
π HISTORY π
the migration guide was created for OpenSSL 3.0. the origin story, if you will. β¨
π COPYRIGHT π
Copyright 2021-2026 The OpenSSL Project Authors. All Rights Reserved.
licensed under the Apache License 2.0 (the "License"). you may not use this file except in compliance with the License. you can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>. π«Ά
and that's a wrap on the whole migration guide, bestie!! π this page is from <highlight>4.0.3</highlight>, dated <highlight>2026-09-29</highlight>, and it lives at OSSL-GUIDE-MIGRATION(7SSL) β go forth and migrate, you absolute icon. ππ
β¨
π¦ where this page comes from
- ubuntu stonking: openssl 4.0.3-1ubuntu1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms