You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2023/06/07 13:58:01 UTC

[tomcat] branch main updated: Add Panama module for Java 21

This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/main by this push:
     new 873051516c Add Panama module for Java 21
873051516c is described below

commit 873051516c2ad91b1bbff1ff4f6a68e4a775fccb
Author: remm <re...@apache.org>
AuthorDate: Wed Jun 7 15:57:33 2023 +0200

    Add Panama module for Java 21
    
    Panama is still a preview API in Java 21 (up from incubating in Java
    17), so the code needs to specifically target it and be compiled with
    the specific Java version.
    I plan to remove the Java 17 version once Java 21 is released.
    openssl-foreign will continue to track the most recent API changes until
    it is no longer preview (at which point it would be merged into the main
    Tomcat code).
---
 modules/openssl-java21/LICENSE                     |  201 ++
 modules/openssl-java21/README.md                   |   90 +
 modules/openssl-java21/addlicense.sh               |   22 +
 modules/openssl-java21/license.header              |   17 +
 modules/openssl-java21/openssl-tomcat.conf         |  357 ++
 modules/openssl-java21/openssl.h                   |   31 +
 modules/openssl-java21/pom.xml                     |   98 +
 .../util/net/openssl/panama/OpenSSLContext.java    | 1406 ++++++++
 .../util/net/openssl/panama/OpenSSLEngine.java     | 1797 ++++++++++
 .../net/openssl/panama/OpenSSLImplementation.java  |   51 +
 .../openssl/panama/OpenSSLLifecycleListener.java   |  463 +++
 .../net/openssl/panama/OpenSSLSessionContext.java  |  163 +
 .../net/openssl/panama/OpenSSLSessionStats.java    |  128 +
 .../util/net/openssl/panama/OpenSSLStatus.java     |   60 +
 .../util/net/openssl/panama/OpenSSLUtil.java       |  128 +
 .../net/openssl/panama/OpenSSLX509Certificate.java |  192 ++
 .../apache/tomcat/util/openssl/Constants$root.java |   42 +
 .../apache/tomcat/util/openssl/RuntimeHelper.java  |  258 ++
 .../SSL_CTX_set_cert_verify_callback$cb.java       |   50 +
 .../openssl/SSL_CTX_set_tmp_dh_callback$dh.java    |   50 +
 .../util/openssl/SSL_set_info_callback$cb.java     |   50 +
 .../apache/tomcat/util/openssl/constants$0.java    |   76 +
 .../apache/tomcat/util/openssl/constants$1.java    |   78 +
 .../apache/tomcat/util/openssl/constants$10.java   |   85 +
 .../apache/tomcat/util/openssl/constants$11.java   |   82 +
 .../apache/tomcat/util/openssl/constants$12.java   |   76 +
 .../apache/tomcat/util/openssl/constants$13.java   |   78 +
 .../apache/tomcat/util/openssl/constants$14.java   |   80 +
 .../apache/tomcat/util/openssl/constants$15.java   |   80 +
 .../apache/tomcat/util/openssl/constants$16.java   |   82 +
 .../apache/tomcat/util/openssl/constants$17.java   |   82 +
 .../apache/tomcat/util/openssl/constants$18.java   |   73 +
 .../apache/tomcat/util/openssl/constants$19.java   |   77 +
 .../apache/tomcat/util/openssl/constants$2.java    |   76 +
 .../apache/tomcat/util/openssl/constants$20.java   |   77 +
 .../apache/tomcat/util/openssl/constants$21.java   |   84 +
 .../apache/tomcat/util/openssl/constants$22.java   |   76 +
 .../apache/tomcat/util/openssl/constants$23.java   |   76 +
 .../apache/tomcat/util/openssl/constants$24.java   |   81 +
 .../apache/tomcat/util/openssl/constants$25.java   |   79 +
 .../apache/tomcat/util/openssl/constants$26.java   |   82 +
 .../apache/tomcat/util/openssl/constants$27.java   |   82 +
 .../apache/tomcat/util/openssl/constants$28.java   |   76 +
 .../apache/tomcat/util/openssl/constants$29.java   |   34 +
 .../apache/tomcat/util/openssl/constants$3.java    |   75 +
 .../apache/tomcat/util/openssl/constants$4.java    |   77 +
 .../apache/tomcat/util/openssl/constants$5.java    |   75 +
 .../apache/tomcat/util/openssl/constants$6.java    |   77 +
 .../apache/tomcat/util/openssl/constants$7.java    |   77 +
 .../apache/tomcat/util/openssl/constants$8.java    |   77 +
 .../apache/tomcat/util/openssl/constants$9.java    |   84 +
 .../tomcat/util/openssl/openssl_compat_h.java      |  121 +
 .../org/apache/tomcat/util/openssl/openssl_h.java  | 3447 ++++++++++++++++++++
 .../net/openssl/panama/LocalStrings.properties     |   97 +
 54 files changed, 11633 insertions(+)

diff --git a/modules/openssl-java21/LICENSE b/modules/openssl-java21/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/modules/openssl-java21/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/modules/openssl-java21/README.md b/modules/openssl-java21/README.md
new file mode 100644
index 0000000000..f3e61f6282
--- /dev/null
+++ b/modules/openssl-java21/README.md
@@ -0,0 +1,90 @@
+# OpenSSL support for Apache Tomcat
+
+## This module is experimental
+
+It uses the JEP 442 API. More details on this API are available
+at `https://openjdk.java.net/jeps/442`.
+
+## Building
+
+The module can be built using Java 21. This will be the only Java version that
+is supported as the JEP 442 API is a preview API and will continue to evolve.
+It can be built and run with Apache Tomcat 9.0 or newer.
+
+## Running
+
+The module uses the OpenSSL 3.0 API. It requires an API compatible version of
+OpenSSL or a compatible alternative library, that can be loaded from the JVM
+library path. OpenSSL 1.1 is also supported.
+
+Copy `tomcat-coyote-openssl-java21-1.0.jar` to the Apache Tomcat `lib` folder.
+
+Remove `AprLifecycleListener` from `server.xml`. The
+`org.apache.tomcat.util.net.openssl.panama.OpenSSLLifecycleListener` can be
+used as a replacement with the same configuration options (such as FIPS)
+and shutdown cleanup, but is not required.
+
+Define a `Connector` using the value
+`org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation` for the
+`sslImplementationName` attribute.
+
+Example connector:
+```
+    <Connector port="8443" protocol="HTTP/1.1"
+               SSLEnabled="true" scheme="https" secure="true"
+               socket.directBuffer="true" socket.directSslBuffer="true"
+               sslImplementationName="org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation">
+        <SSLHostConfig certificateVerification="none">
+            <Certificate certificateKeyFile="${catalina.home}/conf/localhost-rsa-key.pem"
+                         certificateFile="${catalina.home}/conf/localhost-rsa-cert.pem"
+                         certificateChainFile="${catalina.home}/conf/localhost-rsa-chain.pem"
+                         type="RSA" />
+        </SSLHostConfig>
+        <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
+    </Connector>
+```
+
+Run Tomcat using the additional Java options that allow access to the API and
+native code:
+```
+export JAVA_OPTS="--enable-preview --enable-native-access=ALL-UNNAMED"
+```
+
+## Generating the OpenSSL API code using jextract (optional)
+
+jextract is now available in its own standalone repository. Clone
+`https://github.com/openjdk/jextract` in some location and
+checkout the branch that supports Java 21. Please refer to the
+instructions from the repository for building. It should be the
+`panama` branch.
+
+This step is only useful to be able to use additional native APIs from OpenSSL
+or stdlib.
+
+Find include paths using `gcc -xc -E -v -`, on Fedora it is
+`/usr/lib/gcc/x86_64-redhat-linux/12/include`. Edit `openssl-tomcat.conf`
+accordingly to set the appropriate path.
+
+```
+export JEXTRACT_HOME=<pathto>/jextract/build/jextract
+$JEXTRACT_HOME/bin/jextract @openssl-tomcat.conf openssl.h
+```
+Note: The build path for the JDK will be different on other platforms.
+
+The code included was generated using OpenSSL 3.0. As long as things remain
+API compatible, the generated code will still work.
+
+The `openssl-tomcat.conf` will generate a trimmed down OpenSSL API. When
+developing new features, the full API can be generated instead using:
+```
+$JEXTRACT_HOME/bin/jextract --source -t org.apache.tomcat.util.openssl -lssl -I /usr/lib/gcc/x86_64-redhat-linux/12/include openssl.h --output src/main/java
+```
+
+The `openssl.conf` file lists all the API calls and constants that can be
+generated using jextract, as a reference to what is available. Some macros are
+not supported and have to be reproduced in code.
+
+Before committing updated generated files, they need to have the license header
+added. The `addlicense.sh` script can do that and process all Java source files
+in the `src/main/java/org/apache/tomcat/util/openssl` directory.
+
diff --git a/modules/openssl-java21/addlicense.sh b/modules/openssl-java21/addlicense.sh
new file mode 100755
index 0000000000..6349703675
--- /dev/null
+++ b/modules/openssl-java21/addlicense.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+for generated in src/main/java/org/apache/tomcat/util/openssl/*.java; do
+    cat license.header $generated >> $generated.$$
+    mv $generated.$$ $generated
+    echo Updated $generated
+done
diff --git a/modules/openssl-java21/license.header b/modules/openssl-java21/license.header
new file mode 100644
index 0000000000..4b326ae5c2
--- /dev/null
+++ b/modules/openssl-java21/license.header
@@ -0,0 +1,17 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
diff --git a/modules/openssl-java21/openssl-tomcat.conf b/modules/openssl-java21/openssl-tomcat.conf
new file mode 100644
index 0000000000..cd512210ee
--- /dev/null
+++ b/modules/openssl-java21/openssl-tomcat.conf
@@ -0,0 +1,357 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+--source
+-t org.apache.tomcat.util.openssl
+-lssl
+# Configure include path
+-I /usr/lib/gcc/x86_64-redhat-linux/12/include
+--output src/main/java
+
+#### Extracted from: /usr/include/openssl/asn1.h
+
+--include-function ASN1_STRING_get0_data                  # header: /usr/include/openssl/asn1.h
+--include-function ASN1_STRING_length                     # header: /usr/include/openssl/asn1.h
+
+#### Extracted from: /usr/include/openssl/bio.h
+
+--include-function BIO_ctrl                             # header: /usr/include/openssl/bio.h
+--include-function BIO_ctrl_pending                     # header: /usr/include/openssl/bio.h
+--include-function BIO_free                             # header: /usr/include/openssl/bio.h
+--include-function BIO_new                              # header: /usr/include/openssl/bio.h
+--include-function BIO_new_bio_pair                     # header: /usr/include/openssl/bio.h
+--include-function BIO_new_file                         # header: /usr/include/openssl/bio.h
+--include-function BIO_read                             # header: /usr/include/openssl/bio.h
+--include-function BIO_s_bio                            # header: /usr/include/openssl/bio.h
+--include-function BIO_s_file                           # header: /usr/include/openssl/bio.h
+--include-function BIO_s_mem                            # header: /usr/include/openssl/bio.h
+--include-function BIO_write                            # header: /usr/include/openssl/bio.h
+--include-constant BIO_CLOSE                               # header: /usr/include/openssl/bio.h
+--include-constant BIO_CTRL_RESET                          # header: /usr/include/openssl/bio.h
+--include-constant BIO_C_SET_FILENAME                      # header: /usr/include/openssl/bio.h
+--include-constant BIO_FP_READ                             # header: /usr/include/openssl/bio.h
+
+#### Extracted from: /usr/include/openssl/bn.h
+
+--include-function BN_get_rfc2409_prime_1024      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc2409_prime_768       # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_1536      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_2048      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_3072      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_4096      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_6144      # header: /usr/include/openssl/bn.h
+--include-function BN_get_rfc3526_prime_8192      # header: /usr/include/openssl/bn.h
+--include-function BN_new                         # header: /usr/include/openssl/bn.h
+--include-function BN_set_word                    # header: /usr/include/openssl/bn.h
+
+#### Extracted from: /usr/include/openssl/crypto.h
+
+--include-function CRYPTO_free                         # header: /usr/include/openssl/crypto.h
+--include-function OpenSSL_version                     # header: /usr/include/openssl/crypto.h
+--include-function OpenSSL_version_num                 # header: /usr/include/openssl/crypto.h
+--include-constant OPENSSL_INIT_ENGINE_ALL_BUILTIN        # header: /usr/include/openssl/crypto.h
+
+#### Extracted from: /usr/include/openssl/dh.h
+
+--include-function DH_free                                # header: /usr/include/openssl/dh.h
+--include-function DH_new                                 # header: /usr/include/openssl/dh.h
+--include-function DH_set0_pqg                            # header: /usr/include/openssl/dh.h
+
+#### Extracted from: /usr/include/openssl/ec.h
+
+--include-function EC_GROUP_free                            # header: /usr/include/openssl/ec.h
+--include-function EC_GROUP_get_curve_name                  # header: /usr/include/openssl/ec.h
+--include-function EC_KEY_free                              # header: /usr/include/openssl/ec.h
+--include-function EC_KEY_new_by_curve_name                 # header: /usr/include/openssl/ec.h
+
+#### Extracted from: /usr/include/openssl/engine.h
+
+--include-function ENGINE_by_id                             # header: /usr/include/openssl/engine.h
+--include-function ENGINE_ctrl_cmd_string                   # header: /usr/include/openssl/engine.h
+--include-function ENGINE_free                              # header: /usr/include/openssl/engine.h
+--include-function ENGINE_load_private_key                  # header: /usr/include/openssl/engine.h
+--include-function ENGINE_register_all_complete             # header: /usr/include/openssl/engine.h
+--include-function ENGINE_set_default                       # header: /usr/include/openssl/engine.h
+--include-constant ENGINE_METHOD_ALL                           # header: /usr/include/openssl/engine.h
+
+#### Extracted from: /usr/include/openssl/err.h
+
+--include-function ERR_clear_error                         # header: /usr/include/openssl/err.h
+--include-function ERR_error_string                        # header: /usr/include/openssl/err.h
+--include-function ERR_get_error                           # header: /usr/include/openssl/err.h
+--include-function ERR_peek_last_error                     # header: /usr/include/openssl/err.h
+
+#### Extracted from: /usr/include/openssl/evp.h
+
+--include-function EVP_MD_fetch                           # header: /usr/include/openssl/evp.h
+--include-function EVP_MD_free                            # header: /usr/include/openssl/evp.h
+--include-function EVP_MD_get0_provider                   # header: /usr/include/openssl/evp.h
+--include-function EVP_PKEY_get_base_id                   # header: /usr/include/openssl/evp.h
+--include-function EVP_PKEY_get_bits                      # header: /usr/include/openssl/evp.h
+--include-constant EVP_PKEY_DSA                              # header: /usr/include/openssl/evp.h
+--include-constant EVP_PKEY_NONE                             # header: /usr/include/openssl/evp.h
+--include-constant EVP_PKEY_RSA                              # header: /usr/include/openssl/evp.h
+
+#### Extracted from: /usr/include/openssl/obj_mac.h
+
+--include-constant NID_info_access                                               # header: /usr/include/openssl/obj_mac.h
+
+#### Extracted from: /usr/include/openssl/ocsp.h
+
+--include-function OCSP_BASICRESP_free                      # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_CERTID_free                         # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_REQUEST_free                        # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_REQUEST_new                         # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_RESPONSE_free                       # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_cert_to_id                          # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_request_add0_id                     # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_response_get1_basic                 # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_response_status                     # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_resp_find                           # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_resp_get0                           # header: /usr/include/openssl/ocsp.h
+--include-function OCSP_single_get0_status                  # header: /usr/include/openssl/ocsp.h
+--include-function d2i_OCSP_RESPONSE                        # header: /usr/include/openssl/ocsp.h
+--include-function i2d_OCSP_REQUEST                         # header: /usr/include/openssl/ocsp.h
+--include-constant OCSP_RESPONSE_STATUS_SUCCESSFUL             # header: /usr/include/openssl/ocsp.h
+--include-constant V_OCSP_CERTSTATUS_GOOD                      # header: /usr/include/openssl/ocsp.h
+--include-constant V_OCSP_CERTSTATUS_REVOKED                   # header: /usr/include/openssl/ocsp.h
+--include-constant V_OCSP_CERTSTATUS_UNKNOWN                   # header: /usr/include/openssl/ocsp.h
+
+#### Extracted from: /usr/include/openssl/opensslconf-x86_64.h
+
+--include-constant OPENSSL_API_COMPAT    # header: /usr/include/openssl/opensslconf-x86_64.h
+--include-constant OPENSSL_FILE          # header: /usr/include/openssl/opensslconf-x86_64.h
+--include-constant OPENSSL_LINE          # header: /usr/include/openssl/opensslconf-x86_64.h
+--include-constant OPENSSL_MIN_API       # header: /usr/include/openssl/opensslconf-x86_64.h
+
+#### Extracted from: /usr/include/openssl/pem.h
+
+--include-function PEM_read_bio_DHparams                # header: /usr/include/openssl/pem.h
+--include-function PEM_read_bio_ECPKParameters          # header: /usr/include/openssl/pem.h
+--include-function PEM_read_bio_PrivateKey              # header: /usr/include/openssl/pem.h
+--include-function PEM_read_bio_X509_AUX                # header: /usr/include/openssl/pem.h
+
+#### Extracted from: /usr/include/openssl/pemerr.h
+
+--include-constant PEM_R_NO_START_LINE                           # header: /usr/include/openssl/pemerr.h
+
+#### Extracted from: /usr/include/openssl/pkcs12.h
+
+--include-function PKCS12_free                         # header: /usr/include/openssl/pkcs12.h
+--include-function PKCS12_parse                        # header: /usr/include/openssl/pkcs12.h
+--include-function PKCS12_verify_mac                   # header: /usr/include/openssl/pkcs12.h
+--include-function d2i_PKCS12_bio                      # header: /usr/include/openssl/pkcs12.h
+
+#### Extracted from: /usr/include/openssl/provider.h
+
+--include-function OSSL_PROVIDER_get0_name               # header: /usr/include/openssl/provider.h
+
+#### Extracted from: /usr/include/openssl/rand.h
+
+--include-function RAND_load_file                # header: /usr/include/openssl/rand.h
+--include-function RAND_seed                     # header: /usr/include/openssl/rand.h
+
+#### Extracted from: /usr/include/openssl/ssl.h
+
+--include-function OPENSSL_init_ssl                              # header: /usr/include/openssl/ssl.h
+--include-function SSL_CIPHER_get_auth_nid                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_CIPHER_get_kx_nid                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_CIPHER_get_name                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_CTX_finish                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_CTX_free                             # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_CTX_new                              # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_CTX_set_flags                        # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_CTX_set_ssl_ctx                      # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_cmd                                  # header: /usr/include/openssl/ssl.h
+--include-function SSL_CONF_cmd_value_type                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_add_client_CA                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_check_private_key                     # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_clear_options                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_ctrl                                  # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_free                                  # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_get_cert_store                        # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_get_ciphers                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_get_client_CA_list                    # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_get_options                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_get_timeout                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_load_verify_locations                 # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_new                                   # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_alpn_select_cb                    # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_cert_verify_callback              # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_cipher_list                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_ciphersuites                      # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_client_CA_list                    # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_default_passwd_cb                 # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_default_verify_paths              # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_options                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_session_id_context                # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_timeout                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_tmp_dh_callback                   # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_set_verify                            # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_use_certificate                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_use_certificate_chain_file            # header: /usr/include/openssl/ssl.h
+--include-function SSL_CTX_use_PrivateKey                        # header: /usr/include/openssl/ssl.h
+--include-function SSL_SESSION_get_id                            # header: /usr/include/openssl/ssl.h
+--include-function SSL_SESSION_get_time                          # header: /usr/include/openssl/ssl.h
+--include-function SSL_add_file_cert_subjects_to_stack           # header: /usr/include/openssl/ssl.h
+--include-function SSL_do_handshake                              # header: /usr/include/openssl/ssl.h
+--include-function SSL_free                                      # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_ciphers                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_current_cipher                        # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_ex_data_X509_STORE_CTX_idx            # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_options                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_peer_cert_chain                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_privatekey                            # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_session                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_shutdown                              # header: /usr/include/openssl/ssl.h
+--include-function SSL_get_version                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_get0_alpn_selected                        # header: /usr/include/openssl/ssl.h
+--include-function SSL_get1_peer_certificate                     # header: /usr/include/openssl/ssl.h
+--include-function SSL_in_init                                   # header: /usr/include/openssl/ssl.h
+--include-function SSL_load_client_CA_file                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_new                                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_pending                                   # header: /usr/include/openssl/ssl.h
+--include-function SSL_read                                      # header: /usr/include/openssl/ssl.h
+--include-function SSL_renegotiate                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_renegotiate_pending                       # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_accept_state                          # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_bio                                   # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_cipher_list                           # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_connect_state                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_info_callback                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_options                               # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_verify                                # header: /usr/include/openssl/ssl.h
+--include-function SSL_set_verify_result                         # header: /usr/include/openssl/ssl.h
+--include-function SSL_shutdown                                  # header: /usr/include/openssl/ssl.h
+--include-function SSL_verify_client_post_handshake              # header: /usr/include/openssl/ssl.h
+--include-function SSL_write                                     # header: /usr/include/openssl/ssl.h
+--include-function TLS_server_method                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CB_HANDSHAKE_DONE                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_FLAG_CERTIFICATE                        # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_FLAG_FILE                               # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_FLAG_SERVER                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_FLAG_SHOW_ERRORS                        # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_TYPE_DIR                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_TYPE_FILE                               # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CONF_TYPE_UNKNOWN                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_CHAIN_CERT                              # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_GET_SESS_CACHE_MODE                     # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_GET_SESS_CACHE_SIZE                     # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_ACCEPT                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_ACCEPT_GOOD                        # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_ACCEPT_RENEGOTIATE                 # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_CACHE_FULL                         # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_CB_HIT                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_CONNECT                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_CONNECT_GOOD                       # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_CONNECT_RENEGOTIATE                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_HIT                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_MISSES                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_NUMBER                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SESS_TIMEOUTS                           # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_MAX_PROTO_VERSION                   # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_MIN_PROTO_VERSION                   # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_SESS_CACHE_MODE                     # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_SESS_CACHE_SIZE                     # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_TLSEXT_TICKET_KEYS                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_TMP_DH                              # header: /usr/include/openssl/ssl.h
+--include-constant SSL_CTRL_SET_TMP_ECDH                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_ERROR_NONE                                   # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_ALL                                       # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION         # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_CIPHER_SERVER_PREFERENCE                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_COMPRESSION                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION    # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_SSLv2                                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_SSLv3                                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_TICKET                                 # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_TLSv1                                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_TLSv1_1                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_TLSv1_2                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_NO_TLSv1_3                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_SINGLE_DH_USE                             # header: /usr/include/openssl/ssl.h
+--include-constant SSL_OP_SINGLE_ECDH_USE                           # header: /usr/include/openssl/ssl.h
+--include-constant SSL_RECEIVED_SHUTDOWN                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_SENT_SHUTDOWN                                # header: /usr/include/openssl/ssl.h
+--include-constant SSL_SESS_CACHE_OFF                               # header: /usr/include/openssl/ssl.h
+--include-constant SSL_SESS_CACHE_SERVER                            # header: /usr/include/openssl/ssl.h
+--include-constant SSL_VERIFY_FAIL_IF_NO_PEER_CERT                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_VERIFY_NONE                                  # header: /usr/include/openssl/ssl.h
+--include-constant SSL_VERIFY_PEER                                  # header: /usr/include/openssl/ssl.h
+
+#### Extracted from: /usr/include/openssl/ssl2.h
+
+--include-constant SSL2_VERSION            # header: /usr/include/openssl/ssl2.h
+
+#### Extracted from: /usr/include/openssl/ssl3.h
+
+--include-constant SSL3_VERSION                            # header: /usr/include/openssl/ssl3.h
+
+#### Extracted from: /usr/include/openssl/tls1.h
+
+--include-constant SSL_TLSEXT_ERR_NOACK                                  # header: /usr/include/openssl/tls1.h
+--include-constant SSL_TLSEXT_ERR_OK                                     # header: /usr/include/openssl/tls1.h
+--include-constant TLS1_1_VERSION                                        # header: /usr/include/openssl/tls1.h
+--include-constant TLS1_2_VERSION                                        # header: /usr/include/openssl/tls1.h
+--include-constant TLS1_3_VERSION                                        # header: /usr/include/openssl/tls1.h
+--include-constant TLS1_VERSION                                          # header: /usr/include/openssl/tls1.h
+
+#### Extracted from: /usr/include/openssl/stack.h
+
+--include-function OPENSSL_sk_num          # header: /usr/include/openssl/stack.h
+--include-function OPENSSL_sk_value        # header: /usr/include/openssl/stack.h
+
+#### Extracted from: /usr/include/openssl/x509.h
+
+--include-function i2d_X509                         # header: /usr/include/openssl/x509.h
+--include-function d2i_X509                         # header: /usr/include/openssl/x509.h
+--include-function d2i_X509_bio                     # header: /usr/include/openssl/x509.h
+--include-function X509_EXTENSION_get_data          # header: /usr/include/openssl/x509.h
+--include-function X509_free                        # header: /usr/include/openssl/x509.h
+--include-function X509_get_ext                     # header: /usr/include/openssl/x509.h
+--include-function X509_get_ext_by_NID              # header: /usr/include/openssl/x509.h
+--include-constant X509_FILETYPE_PEM                   # header: /usr/include/openssl/x509.h
+
+#### Extracted from: /usr/include/openssl/x509v3.h
+
+--include-function X509_check_issued                          # header: /usr/include/openssl/x509v3.h
+
+#### Extracted from: /usr/include/openssl/x509_vfy.h
+
+--include-function X509_LOOKUP_ctrl                                # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_LOOKUP_file                                # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_LOOKUP_hash_dir                            # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_add_lookup                           # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get_current_cert                 # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get_error                        # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get_error_depth                  # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get_ex_data                      # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get0_current_issuer              # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_get0_untrusted                   # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_CTX_set_error                        # header: /usr/include/openssl/x509_vfy.h
+--include-function X509_STORE_set_flags                            # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_L_ADD_DIR                                     # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_L_FILE_LOAD                                   # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_APPLICATION_VERIFICATION                # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_CERT_UNTRUSTED                          # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_CRL_HAS_EXPIRED                         # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT             # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN               # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY       # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE         # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_FLAG_CRL_CHECK                              # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_FLAG_CRL_CHECK_ALL                          # header: /usr/include/openssl/x509_vfy.h
+--include-constant X509_V_OK                                          # header: /usr/include/openssl/x509_vfy.h
+
diff --git a/modules/openssl-java21/openssl.h b/modules/openssl-java21/openssl.h
new file mode 100644
index 0000000000..e31fad9e1b
--- /dev/null
+++ b/modules/openssl-java21/openssl.h
@@ -0,0 +1,31 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <openssl/opensslv.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include <openssl/dh.h>
+#include <openssl/bn.h>
+#include <openssl/engine.h>
+#include <openssl/ocsp.h>
+#include <openssl/provider.h>
diff --git a/modules/openssl-java21/pom.xml b/modules/openssl-java21/pom.xml
new file mode 100644
index 0000000000..d0f2569256
--- /dev/null
+++ b/modules/openssl-java21/pom.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>27</version>
+    </parent>
+
+    <groupId>org.apache.tomcat</groupId>
+    <artifactId>tomcat-coyote-openssl-java21</artifactId>
+    <name>Apache Tomcat OpenSSL support for Java 21</name>
+    <description>OpenSSL support using the Panama JEP 442 API included in Java 21</description>
+    <version>0.1-SNAPSHOT</version>
+
+    <properties>
+        <tomcat.version>9.0.73</tomcat.version>
+        <project.build.outputTimestamp>2021-12-02T12:00:00Z</project.build.outputTimestamp>
+    </properties>
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/tomcat.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/tomcat.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=tomcat.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <mailingLists>
+        <mailingList>
+            <name>Development List</name>
+            <subscribe>dev-subscribe@tomcat.apache.org</subscribe>
+            <unsubscribe>dev-unsubscribe@tomcat.apache.org</unsubscribe>
+            <post>dev@tomcat.apache.org</post>
+        </mailingList>
+        <mailingList>
+            <name>Users List</name>
+            <subscribe>users-subscribe@tomcat.apache.org</subscribe>
+            <unsubscribe>users-unsubscribe@tomcat.apache.org</unsubscribe>
+            <post>users@tomcat.apache.org</post>
+        </mailingList>
+    </mailingLists>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.tomcat</groupId>
+            <artifactId>tomcat-catalina</artifactId>
+            <version>${tomcat.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomcat</groupId>
+            <artifactId>tomcat-coyote</artifactId>
+            <version>${tomcat.version}</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>21</source>
+                    <target>21</target>
+                    <compilerArgs>
+                        <arg>--enable-preview</arg>
+                    </compilerArgs>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <source>19</source> 
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
new file mode 100644
index 0000000000..61d953a1c4
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java
@@ -0,0 +1,1406 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.io.File;
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.SegmentAllocator;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.ref.Cleaner;
+import java.lang.ref.Cleaner.Cleanable;
+import java.nio.charset.StandardCharsets;
+import java.security.PrivateKey;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSessionContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509KeyManager;
+import javax.net.ssl.X509TrustManager;
+
+import static org.apache.tomcat.util.openssl.openssl_compat_h.*;
+import static org.apache.tomcat.util.openssl.openssl_h.*;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.AbstractEndpoint;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfig.CertificateVerification;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type;
+import org.apache.tomcat.util.net.openssl.OpenSSLConf;
+import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd;
+import org.apache.tomcat.util.res.StringManager;
+
+public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext {
+
+    private static final Log log = LogFactory.getLog(OpenSSLContext.class);
+
+    private static final StringManager netSm = StringManager.getManager(AbstractEndpoint.class);
+    private static final StringManager sm = StringManager.getManager(OpenSSLContext.class);
+
+    private static final Cleaner cleaner = Cleaner.create();
+
+    private static final String defaultProtocol = "TLS";
+
+    private static final int SSL_AIDX_RSA     = 0;
+    private static final int SSL_AIDX_DSA     = 1;
+    private static final int SSL_AIDX_ECC     = 3;
+    private static final int SSL_AIDX_MAX     = 4;
+
+    public static final int SSL_PROTOCOL_NONE  = 0;
+    public static final int SSL_PROTOCOL_SSLV2 = (1<<0);
+    public static final int SSL_PROTOCOL_SSLV3 = (1<<1);
+    public static final int SSL_PROTOCOL_TLSV1 = (1<<2);
+    public static final int SSL_PROTOCOL_TLSV1_1 = (1<<3);
+    public static final int SSL_PROTOCOL_TLSV1_2 = (1<<4);
+    public static final int SSL_PROTOCOL_TLSV1_3 = (1<<5);
+    public static final int SSL_PROTOCOL_ALL = (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 |
+            SSL_PROTOCOL_TLSV1_3);
+
+    private static final String BEGIN_KEY = "-----BEGIN PRIVATE KEY-----\n";
+    private static final Object END_KEY = "\n-----END PRIVATE KEY-----";
+
+    private static final byte[] HTTP_11_PROTOCOL =
+            new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' };
+
+    private static final byte[] DEFAULT_SESSION_ID_CONTEXT =
+            new byte[] { 'd', 'e', 'f', 'a', 'u', 'l', 't' };
+
+    static final CertificateFactory X509_CERT_FACTORY;
+    static {
+        try {
+            X509_CERT_FACTORY = CertificateFactory.getInstance("X.509");
+        } catch (CertificateException e) {
+            throw new IllegalStateException(sm.getString("openssl.X509FactoryError"), e);
+        }
+    }
+
+    private static final MethodHandle openSSLCallbackVerifyHandle;
+    private static final MethodHandle openSSLCallbackPasswordHandle;
+    private static final MethodHandle openSSLCallbackCertVerifyHandle;
+    private static final MethodHandle openSSLCallbackAlpnSelectProtoHandle;
+    private static final MethodHandle openSSLCallbackTmpDHHandle;
+
+    private static final FunctionDescriptor openSSLCallbackVerifyFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+    private static final FunctionDescriptor openSSLCallbackPasswordFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.JAVA_INT,
+            ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+    private static final FunctionDescriptor openSSLCallbackCertVerifyFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS);
+    private static final FunctionDescriptor openSSLCallbackAlpnSelectProtoFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS,
+            ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS,
+            ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+    private static final FunctionDescriptor openSSLCallbackTmpDHFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.ADDRESS,
+            ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
+
+    static {
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        try {
+            openSSLCallbackVerifyHandle = lookup.findStatic(OpenSSLContext.class, "openSSLCallbackVerify",
+                    MethodType.methodType(int.class, int.class, MemorySegment.class));
+            openSSLCallbackPasswordHandle = lookup.findStatic(OpenSSLContext.class, "openSSLCallbackPassword",
+                    MethodType.methodType(int.class, MemorySegment.class, int.class, int.class, MemorySegment.class));
+            openSSLCallbackCertVerifyHandle = lookup.findStatic(OpenSSLContext.class, "openSSLCallbackCertVerify",
+                    MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class));
+            openSSLCallbackAlpnSelectProtoHandle = lookup.findStatic(OpenSSLContext.class, "openSSLCallbackAlpnSelectProto",
+                    MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class,
+                            MemorySegment.class, MemorySegment.class, int.class, MemorySegment.class));
+            openSSLCallbackTmpDHHandle = lookup.findStatic(OpenSSLContext.class, "openSSLCallbackTmpDH",
+                    MethodType.methodType(MemorySegment.class, MemorySegment.class, int.class, int.class));
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    static final boolean OPENSSL_3 = (OpenSSL_version_num() >= 0x3000000fL);
+
+    private final SSLHostConfig sslHostConfig;
+    private final SSLHostConfigCertificate certificate;
+    private final boolean alpn;
+    private final int minTlsVersion;
+    private final int maxTlsVersion;
+
+    private OpenSSLSessionContext sessionContext;
+    private String enabledProtocol;
+    private boolean initialized = false;
+
+    private boolean noOcspCheck = false;
+
+    // Password callback
+    private final MemorySegment openSSLCallbackPassword;
+
+    private static final ConcurrentHashMap<Long, ContextState> states = new ConcurrentHashMap<>();
+
+    static ContextState getState(MemorySegment ctx) {
+        return states.get(Long.valueOf(ctx.address()));
+    }
+
+    private final ContextState state;
+    private final Arena contextArena;
+    private final Cleanable cleanable;
+
+    private static String[] getCiphers(MemorySegment sslCtx) {
+        MemorySegment sk = SSL_CTX_get_ciphers(sslCtx);
+        int len = OPENSSL_sk_num(sk);
+        if (len <= 0) {
+            return null;
+        }
+        ArrayList<String> ciphers = new ArrayList<>(len);
+        for (int i = 0; i < len; i++) {
+            MemorySegment cipher = OPENSSL_sk_value(sk, i);
+            MemorySegment cipherName = SSL_CIPHER_get_name(cipher);
+            ciphers.add(cipherName.getUtf8String(0));
+        }
+        return ciphers.toArray(new String[0]);
+    }
+
+    public OpenSSLContext(SSLHostConfigCertificate certificate, List<String> negotiableProtocols)
+            throws SSLException {
+
+        // Check that OpenSSL was initialized
+        if (!OpenSSLStatus.isInitialized()) {
+            try {
+                OpenSSLLifecycleListener.init();
+            } catch (Exception e) {
+                throw new SSLException(e);
+            }
+        }
+
+        this.sslHostConfig = certificate.getSSLHostConfig();
+        this.certificate = certificate;
+        contextArena = Arena.ofAuto();
+
+        MemorySegment sslCtx = MemorySegment.NULL;
+        MemorySegment confCtx = MemorySegment.NULL;
+        List<byte[]> negotiableProtocolsBytes = null;
+        boolean success = false;
+        try {
+            // Create OpenSSLConfCmd context if used
+            OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf();
+            if (openSslConf != null) {
+                try {
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("openssl.makeConf"));
+                    }
+                    confCtx = SSL_CONF_CTX_new();
+                    long errCode = ERR_get_error();
+                    if (errCode != 0) {
+                        try (var localArena = Arena.ofConfined()) {
+                            var buf = localArena.allocateArray(ValueLayout.JAVA_BYTE, new byte[128]);
+                            ERR_error_string(errCode, buf);
+                            log.error(sm.getString("openssl.errorLoadingCertificate", buf.getUtf8String(0)));
+                        }
+                    }
+                    SSL_CONF_CTX_set_flags(confCtx, SSL_CONF_FLAG_FILE() |
+                            SSL_CONF_FLAG_SERVER() |
+                            SSL_CONF_FLAG_CERTIFICATE() |
+                            SSL_CONF_FLAG_SHOW_ERRORS());
+                } catch (Exception e) {
+                    throw new SSLException(sm.getString("openssl.errMakeConf"), e);
+                }
+            }
+
+            // SSL protocol
+            sslCtx = SSL_CTX_new(TLS_server_method());
+
+            int protocol = SSL_PROTOCOL_NONE;
+            for (String enabledProtocol : sslHostConfig.getEnabledProtocols()) {
+                if (Constants.SSL_PROTO_SSLv2Hello.equalsIgnoreCase(enabledProtocol)) {
+                    // NO-OP. OpenSSL always supports SSLv2Hello
+                } else if (Constants.SSL_PROTO_SSLv2.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_SSLV2;
+                } else if (Constants.SSL_PROTO_SSLv3.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_SSLV3;
+                } else if (Constants.SSL_PROTO_TLSv1.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_TLSV1;
+                } else if (Constants.SSL_PROTO_TLSv1_1.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_TLSV1_1;
+                } else if (Constants.SSL_PROTO_TLSv1_2.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_TLSV1_2;
+                } else if (Constants.SSL_PROTO_TLSv1_3.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_TLSV1_3;
+                } else if (Constants.SSL_PROTO_ALL.equalsIgnoreCase(enabledProtocol)) {
+                    protocol |= SSL_PROTOCOL_ALL;
+                } else {
+                    // Should not happen since filtering to build
+                    // enabled protocols removes invalid values.
+                    throw new Exception(netSm.getString(
+                            "endpoint.apr.invalidSslProtocol", enabledProtocol));
+                }
+            }
+            // Set maximum and minimum protocol versions
+            int prot = SSL2_VERSION();
+            if ((protocol & SSL_PROTOCOL_TLSV1_3) > 0) {
+                prot = TLS1_3_VERSION();
+            } else if ((protocol & SSL_PROTOCOL_TLSV1_2) > 0) {
+                prot = TLS1_2_VERSION();
+            } else if ((protocol & SSL_PROTOCOL_TLSV1_1) > 0) {
+                prot = TLS1_1_VERSION();
+            } else if ((protocol & SSL_PROTOCOL_TLSV1) > 0) {
+                prot = TLS1_VERSION();
+            } else if ((protocol & SSL_PROTOCOL_SSLV3) > 0) {
+                prot = SSL3_VERSION();
+            }
+            maxTlsVersion = prot;
+            // # define SSL_CTX_set_max_proto_version(sslCtx, version) \
+            //          SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
+            SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_MAX_PROTO_VERSION(), prot, MemorySegment.NULL);
+            if (prot == TLS1_3_VERSION() && (protocol & SSL_PROTOCOL_TLSV1_2) > 0) {
+                prot = TLS1_2_VERSION();
+            }
+            if (prot == TLS1_2_VERSION() && (protocol & SSL_PROTOCOL_TLSV1_1) > 0) {
+                prot = TLS1_1_VERSION();
+            }
+            if (prot == TLS1_1_VERSION() && (protocol & SSL_PROTOCOL_TLSV1) > 0) {
+                prot = TLS1_VERSION();
+            }
+            if (prot == TLS1_VERSION() && (protocol & SSL_PROTOCOL_SSLV3) > 0) {
+                prot = SSL3_VERSION();
+            }
+            minTlsVersion = prot;
+            //# define SSL_CTX_set_min_proto_version(sslCtx, version) \
+            //         SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
+            SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_MIN_PROTO_VERSION(), prot, MemorySegment.NULL);
+
+            // Disable compression, usually unsafe
+            SSL_CTX_set_options(sslCtx, SSL_OP_NO_COMPRESSION());
+
+            // Disallow a session from being resumed during a renegotiation,
+            // so that an acceptable cipher suite can be negotiated.
+            SSL_CTX_set_options(sslCtx, SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION());
+
+            SSL_CTX_set_options(sslCtx, SSL_OP_SINGLE_DH_USE());
+            SSL_CTX_set_options(sslCtx, SSL_OP_SINGLE_ECDH_USE());
+
+            // Default session context id and cache size
+            // # define SSL_CTX_sess_set_cache_size(sslCtx,t) \
+            //          SSL_CTX_ctrl(sslCtx,SSL_CTRL_SET_SESS_CACHE_SIZE,t,NULL)
+            SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_SESS_CACHE_SIZE(), 256, MemorySegment.NULL);
+
+            // Session cache is disabled by default
+            // # define SSL_CTX_set_session_cache_mode(sslCtx,m) \
+            //          SSL_CTX_ctrl(sslCtx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL)
+            SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_SESS_CACHE_MODE(), SSL_SESS_CACHE_OFF(), MemorySegment.NULL);
+
+            // Longer session timeout
+            SSL_CTX_set_timeout(sslCtx, 14400);
+
+            // From SSLContext.make, possibly set ssl_callback_ServerNameIndication
+            // From SSLContext.make, possibly set ssl_callback_ClientHello
+            // Probably not needed
+
+            // Set int pem_password_cb(char *buf, int size, int rwflag, void *u) callback
+            openSSLCallbackPassword =
+                    Linker.nativeLinker().upcallStub(openSSLCallbackPasswordHandle,
+                    openSSLCallbackPasswordFunctionDescriptor, contextArena);
+            SSL_CTX_set_default_passwd_cb(sslCtx, openSSLCallbackPassword);
+
+            alpn = (negotiableProtocols != null && negotiableProtocols.size() > 0);
+            if (alpn) {
+                negotiableProtocolsBytes = new ArrayList<>(negotiableProtocols.size() + 1);
+                for (String negotiableProtocol : negotiableProtocols) {
+                    negotiableProtocolsBytes.add(negotiableProtocol.getBytes(StandardCharsets.ISO_8859_1));
+                }
+                negotiableProtocolsBytes.add(HTTP_11_PROTOCOL);
+            }
+
+            success = true;
+        } catch(Exception e) {
+            throw new SSLException(sm.getString("openssl.errorSSLCtxInit"), e);
+        } finally {
+            state = new ContextState(sslCtx, confCtx, negotiableProtocolsBytes);
+            /*
+             * When an SSLHostConfig is replaced at runtime, it is not possible to
+             * call destroy() on the associated OpenSSLContext since it is likely
+             * that there will be in-progress connections using the OpenSSLContext.
+             * A reference chain has been deliberately established (see
+             * OpenSSLSessionContext) to ensure that the OpenSSLContext remains
+             * ineligible for GC while those connections are alive. Once those
+             * connections complete, the OpenSSLContext will become eligible for GC
+             * and the memory session will ensure that the associated native
+             * resources are cleaned up.
+             */
+            cleanable = cleaner.register(this, state);
+
+            if (!success) {
+                destroy();
+            }
+        }
+    }
+
+
+    public String getEnabledProtocol() {
+        return enabledProtocol;
+    }
+
+
+    public void setEnabledProtocol(String protocol) {
+        enabledProtocol = (protocol == null) ? defaultProtocol : protocol;
+    }
+
+
+    @Override
+    public void destroy() {
+        cleanable.clean();
+    }
+
+
+    private boolean checkConf(OpenSSLConf conf) throws Exception {
+        boolean result = true;
+        OpenSSLConfCmd cmd;
+        String name;
+        String value;
+        int rc;
+        for (OpenSSLConfCmd command : conf.getCommands()) {
+            cmd = command;
+            name = cmd.getName();
+            value = cmd.getValue();
+            if (name == null) {
+                log.error(sm.getString("opensslconf.noCommandName", value));
+                result = false;
+                continue;
+            }
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("opensslconf.checkCommand", name, value));
+            }
+            try (var localArena = Arena.ofConfined()) {
+                // rc = SSLConf.check(confCtx, name, value);
+                if (name.equals("NO_OCSP_CHECK")) {
+                    rc = 1;
+                } else {
+                    int code = SSL_CONF_cmd_value_type(state.confCtx, localArena.allocateUtf8String(name));
+                    rc = 1;
+                    long errCode = ERR_get_error();
+                    if (errCode != 0) {
+                        var buf = localArena.allocateArray(ValueLayout.JAVA_BYTE, new byte[128]);
+                        ERR_error_string(errCode, buf);
+                        log.error(sm.getString("opensslconf.checkFailed", buf.getUtf8String(0)));
+                        rc = 0;
+                    }
+                    if (code == SSL_CONF_TYPE_UNKNOWN()) {
+                        log.error(sm.getString("opensslconf.typeUnknown", name));
+                        rc = 0;
+                    }
+                    if (code == SSL_CONF_TYPE_FILE()) {
+                        // Check file
+                        File file = new File(value);
+                        if (!file.isFile() && !file.canRead()) {
+                            log.error(sm.getString("opensslconf.badFile", name, value));
+                            rc = 0;
+                        }
+                    }
+                    if (code == SSL_CONF_TYPE_DIR()) {
+                        // Check dir
+                        File file = new File(value);
+                        if (!file.isDirectory()) {
+                            log.error(sm.getString("opensslconf.badDirectory", name, value));
+                            rc = 0;
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error(sm.getString("opensslconf.checkFailed", e.getLocalizedMessage()));
+                return false;
+            }
+            if (rc <= 0) {
+                log.error(sm.getString("opensslconf.failedCommand", name, value,
+                        Integer.toString(rc)));
+                result = false;
+            } else if (log.isDebugEnabled()) {
+                log.debug(sm.getString("opensslconf.resultCommand", name, value,
+                        Integer.toString(rc)));
+            }
+        }
+        if (!result) {
+            log.error(sm.getString("opensslconf.checkFailed"));
+        }
+        return result;
+    }
+
+
+    private boolean applyConf(OpenSSLConf conf) throws Exception {
+        boolean result = true;
+        // SSLConf.assign(confCtx, sslCtx);
+        SSL_CONF_CTX_set_ssl_ctx(state.confCtx, state.sslCtx);
+        OpenSSLConfCmd cmd;
+        String name;
+        String value;
+        int rc;
+        for (OpenSSLConfCmd command : conf.getCommands()) {
+            cmd = command;
+            name = cmd.getName();
+            value = cmd.getValue();
+            if (name == null) {
+                log.error(sm.getString("opensslconf.noCommandName", value));
+                result = false;
+                continue;
+            }
+            if (log.isDebugEnabled()) {
+                log.debug(sm.getString("opensslconf.applyCommand", name, value));
+            }
+            try (var localArena = Arena.ofConfined()) {
+                // rc = SSLConf.apply(confCtx, name, value);
+                if (name.equals("NO_OCSP_CHECK")) {
+                    noOcspCheck = Boolean.valueOf(value);
+                    rc = 1;
+                } else {
+                    rc = SSL_CONF_cmd(state.confCtx, localArena.allocateUtf8String(name),
+                            localArena.allocateUtf8String(value));
+                    long errCode = ERR_get_error();
+                    if (rc <= 0 || errCode != 0) {
+                        var buf = localArena.allocateArray(ValueLayout.JAVA_BYTE, new byte[128]);
+                        ERR_error_string(errCode, buf);
+                        log.error(sm.getString("opensslconf.commandError", name, value, buf.getUtf8String(0)));
+                        rc = 0;
+                    }
+                }
+            } catch (Exception e) {
+                log.error(sm.getString("opensslconf.applyFailed"));
+                return false;
+            }
+            if (rc <= 0) {
+                log.error(sm.getString("opensslconf.failedCommand", name, value,
+                        Integer.toString(rc)));
+                result = false;
+            } else if (log.isDebugEnabled()) {
+                log.debug(sm.getString("opensslconf.resultCommand", name, value,
+                        Integer.toString(rc)));
+            }
+        }
+        // rc = SSLConf.finish(confCtx);
+        rc = SSL_CONF_CTX_finish(state.confCtx);
+        if (rc <= 0) {
+            log.error(sm.getString("opensslconf.finishFailed", Integer.toString(rc)));
+            result = false;
+        }
+        if (!result) {
+            log.error(sm.getString("opensslconf.applyFailed"));
+        }
+        return result;
+    }
+
+    private static final int OPTIONAL_NO_CA = 3;
+
+    /**
+     * Setup the SSL_CTX.
+     *
+     * @param kms Must contain a KeyManager of the type
+     *            {@code OpenSSLKeyManager}
+     * @param tms Must contain a TrustManager of the type
+     *            {@code X509TrustManager}
+     * @param sr Is not used for this implementation.
+     */
+    @Override
+    public synchronized void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) {
+        if (initialized) {
+            log.warn(sm.getString("openssl.doubleInit"));
+            return;
+        }
+        try (var localArena = Arena.ofConfined()) {
+            if (sslHostConfig.getInsecureRenegotiation()) {
+                SSL_CTX_set_options(state.sslCtx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION());
+            } else {
+                SSL_CTX_clear_options(state.sslCtx, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION());
+            }
+
+            // Use server's preference order for ciphers (rather than
+            // client's)
+            if (sslHostConfig.getHonorCipherOrder()) {
+                SSL_CTX_set_options(state.sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE());
+            } else {
+                SSL_CTX_clear_options(state.sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE());
+            }
+
+            // Disable compression if requested
+            if (sslHostConfig.getDisableCompression()) {
+                SSL_CTX_set_options(state.sslCtx, SSL_OP_NO_COMPRESSION());
+            } else {
+                SSL_CTX_clear_options(state.sslCtx, SSL_OP_NO_COMPRESSION());
+            }
+
+            // Disable TLS Session Tickets (RFC4507) to protect perfect forward secrecy
+            if (sslHostConfig.getDisableSessionTickets()) {
+                SSL_CTX_set_options(state.sslCtx, SSL_OP_NO_TICKET());
+            } else {
+                SSL_CTX_clear_options(state.sslCtx, SSL_OP_NO_TICKET());
+            }
+
+            // List the ciphers that the client is permitted to negotiate
+            if (minTlsVersion <= TLS1_2_VERSION()) {
+                if (SSL_CTX_set_cipher_list(state.sslCtx,
+                        localArena.allocateUtf8String(sslHostConfig.getCiphers())) <= 0) {
+                    log.warn(sm.getString("engine.failedCipherList", sslHostConfig.getCiphers()));
+                }
+            }
+            if (maxTlsVersion >= TLS1_3_VERSION() && (sslHostConfig.getCiphers() != SSLHostConfig.DEFAULT_TLS_CIPHERS)) {
+                if (SSL_CTX_set_ciphersuites(state.sslCtx,
+                        localArena.allocateUtf8String(sslHostConfig.getCiphers())) <= 0) {
+                    log.warn(sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers()));
+                }
+            }
+
+            if (certificate.getCertificateFile() == null) {
+                certificate.setCertificateKeyManager(OpenSSLUtil.chooseKeyManager(kms));
+            }
+
+            addCertificate(certificate, localArena);
+
+            // Client certificate verification
+            int value = 0;
+            switch (sslHostConfig.getCertificateVerification()) {
+            case NONE:
+                value = SSL_VERIFY_NONE();
+                break;
+            case OPTIONAL:
+                value = SSL_VERIFY_PEER();
+                break;
+            case OPTIONAL_NO_CA:
+                value = OPTIONAL_NO_CA;
+                break;
+            case REQUIRED:
+                value = SSL_VERIFY_FAIL_IF_NO_PEER_CERT();
+                break;
+            }
+
+            // SSLContext.setVerify(state.ctx, value, sslHostConfig.getCertificateVerificationDepth());
+            if (SSL_CTX_set_default_verify_paths(state.sslCtx) > 0) {
+                var store = SSL_CTX_get_cert_store(state.sslCtx);
+                X509_STORE_set_flags(store, 0);
+            }
+
+            // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback
+            var openSSLCallbackVerify =
+                    Linker.nativeLinker().upcallStub(openSSLCallbackVerifyHandle,
+                    openSSLCallbackVerifyFunctionDescriptor, contextArena);
+            // Leave this just in case but in Tomcat this is always set again by the engine
+            SSL_CTX_set_verify(state.sslCtx, value, openSSLCallbackVerify);
+
+            // Trust and certificate verification
+            if (tms != null) {
+                // Client certificate verification based on custom trust managers
+                state.x509TrustManager = chooseTrustManager(tms);
+                var openSSLCallbackCertVerify =
+                        Linker.nativeLinker().upcallStub(openSSLCallbackCertVerifyHandle,
+                                openSSLCallbackCertVerifyFunctionDescriptor, contextArena);
+                SSL_CTX_set_cert_verify_callback(state.sslCtx, openSSLCallbackCertVerify, state.sslCtx);
+
+                // Pass along the DER encoded certificates of the accepted client
+                // certificate issuers, so that their subjects can be presented
+                // by the server during the handshake to allow the client choosing
+                // an acceptable certificate
+                for (X509Certificate caCert : state.x509TrustManager.getAcceptedIssuers()) {
+                    //SSLContext.addClientCACertificateRaw(state.ctx, caCert.getEncoded());
+                    var rawCACertificate = localArena.allocateArray(ValueLayout.JAVA_BYTE, caCert.getEncoded());
+                    var rawCACertificatePointer = localArena.allocate(ValueLayout.ADDRESS, rawCACertificate);
+                    var x509CACert = d2i_X509(MemorySegment.NULL, rawCACertificatePointer, rawCACertificate.byteSize());
+                    if (MemorySegment.NULL.equals(x509CACert)) {
+                        logLastError(localArena, "openssl.errorLoadingCertificate");
+                    } else if (SSL_CTX_add_client_CA(state.sslCtx, x509CACert) <= 0) {
+                        logLastError(localArena, "openssl.errorAddingCertificate");
+                    } else if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("openssl.addedClientCaCert", caCert.toString()));
+                    }
+                }
+            } else {
+                // Client certificate verification based on trusted CA files and dirs
+                //SSLContext.setCACertificate(state.ctx,
+                //        SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()),
+                //        SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()));
+                MemorySegment caCertificateFileNative = sslHostConfig.getCaCertificateFile() != null
+                        ? localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile())) : null;
+                MemorySegment caCertificatePathNative = sslHostConfig.getCaCertificatePath() != null
+                        ? localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())) : null;
+                if ((sslHostConfig.getCaCertificateFile() != null || sslHostConfig.getCaCertificatePath() != null)
+                        && SSL_CTX_load_verify_locations(state.sslCtx,
+                                caCertificateFileNative == null ? MemorySegment.NULL : caCertificateFileNative,
+                                        caCertificatePathNative == null ? MemorySegment.NULL : caCertificatePathNative) <= 0) {
+                    logLastError(localArena, "openssl.errorConfiguringLocations");
+                } else {
+                    var caCerts = SSL_CTX_get_client_CA_list(state.sslCtx);
+                    if (MemorySegment.NULL.equals(caCerts)) {
+                        caCerts = SSL_load_client_CA_file(caCertificateFileNative == null ? MemorySegment.NULL : caCertificateFileNative);
+                        if (!MemorySegment.NULL.equals(caCerts)) {
+                            SSL_CTX_set_client_CA_list(state.sslCtx, caCerts);
+                        }
+                    } else {
+                        if (SSL_add_file_cert_subjects_to_stack(caCerts,
+                                caCertificateFileNative == null ? MemorySegment.NULL : caCertificateFileNative) <= 0) {
+                            caCerts = MemorySegment.NULL;
+                        }
+                    }
+                    if (MemorySegment.NULL.equals(caCerts)) {
+                        log.warn(sm.getString("openssl.noCACerts"));
+                    }
+                }
+            }
+
+            if (state.negotiableProtocols != null && state.negotiableProtocols.size() > 0) {
+                // int openSSLCallbackAlpnSelectProto(MemoryAddress ssl, MemoryAddress out, MemoryAddress outlen,
+                //        MemoryAddress in, int inlen, MemoryAddress arg
+                var openSSLCallbackAlpnSelectProto =
+                        Linker.nativeLinker().upcallStub(openSSLCallbackAlpnSelectProtoHandle,
+                        openSSLCallbackAlpnSelectProtoFunctionDescriptor, contextArena);
+                SSL_CTX_set_alpn_select_cb(state.sslCtx, openSSLCallbackAlpnSelectProto, state.sslCtx);
+            }
+
+            // Apply OpenSSLConfCmd if used
+            OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf();
+            if (openSslConf != null && !MemorySegment.NULL.equals(state.confCtx)) {
+                // Check OpenSSLConfCmd if used
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("openssl.checkConf"));
+                }
+                try {
+                    if (!checkConf(openSslConf)) {
+                        log.error(sm.getString("openssl.errCheckConf"));
+                        throw new Exception(sm.getString("openssl.errCheckConf"));
+                    }
+                } catch (Exception e) {
+                    throw new Exception(sm.getString("openssl.errCheckConf"), e);
+                }
+                if (log.isDebugEnabled()) {
+                    log.debug(sm.getString("openssl.applyConf"));
+                }
+                try {
+                    if (!applyConf(openSslConf)) {
+                        log.error(sm.getString("openssl.errApplyConf"));
+                        throw new SSLException(sm.getString("openssl.errApplyConf"));
+                    }
+                } catch (Exception e) {
+                    throw new SSLException(sm.getString("openssl.errApplyConf"), e);
+                }
+                // Reconfigure the enabled protocols
+                long opts = SSL_CTX_get_options(state.sslCtx);
+                List<String> enabled = new ArrayList<>();
+                // Seems like there is no way to explicitly disable SSLv2Hello
+                // in OpenSSL so it is always enabled
+                enabled.add(Constants.SSL_PROTO_SSLv2Hello);
+                if ((opts & SSL_OP_NO_TLSv1()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_TLSv1);
+                }
+                if ((opts & SSL_OP_NO_TLSv1_1()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_TLSv1_1);
+                }
+                if ((opts & SSL_OP_NO_TLSv1_2()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_TLSv1_2);
+                }
+                if ((opts & SSL_OP_NO_TLSv1_3()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_TLSv1_3);
+                }
+                if ((opts & SSL_OP_NO_SSLv2()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_SSLv2);
+                }
+                if ((opts & SSL_OP_NO_SSLv3()) == 0) {
+                    enabled.add(Constants.SSL_PROTO_SSLv3);
+                }
+                sslHostConfig.setEnabledProtocols(
+                        enabled.toArray(new String[0]));
+                // Reconfigure the enabled ciphers
+                sslHostConfig.setEnabledCiphers(getCiphers(state.sslCtx));
+            }
+
+            sessionContext = new OpenSSLSessionContext(this);
+            // If client authentication is being used, OpenSSL requires that
+            // this is set so always set it in case an app is configured to
+            // require it
+            sessionContext.setSessionIdContext(DEFAULT_SESSION_ID_CONTEXT);
+            sslHostConfig.setOpenSslContext(state.sslCtx.address());
+            initialized = true;
+        } catch (Exception e) {
+            log.warn(sm.getString("openssl.errorSSLCtxInit"), e);
+            destroy();
+        }
+    }
+
+
+    public MemorySegment getSSLContext() {
+        return state.sslCtx;
+    }
+
+    // DH *(*tmp_dh_callback)(SSL *ssl, int is_export, int keylength)
+    public static MemorySegment openSSLCallbackTmpDH(MemorySegment ssl, int isExport, int keylength) {
+        var pkey = SSL_get_privatekey(ssl);
+        int type = (MemorySegment.NULL.equals(pkey)) ? EVP_PKEY_NONE()
+                : (OPENSSL_3 ? EVP_PKEY_get_base_id(pkey) : EVP_PKEY_base_id(pkey));
+        /*
+         * OpenSSL will call us with either keylen == 512 or keylen == 1024
+         * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h).
+         * Adjust the DH parameter length according to the size of the
+         * RSA/DSA private key used for the current connection, and always
+         * use at least 1024-bit parameters.
+         * Note: This may cause interoperability issues with implementations
+         * which limit their DH support to 1024 bit - e.g. Java 7 and earlier.
+         * In this case, SSLCertificateFile can be used to specify fixed
+         * 1024-bit DH parameters (with the effect that OpenSSL skips this
+         * callback).
+         */
+        int keylen = 0;
+        if ((type == EVP_PKEY_RSA()) || (type == EVP_PKEY_DSA())) {
+            keylen = (OPENSSL_3 ? EVP_PKEY_get_bits(pkey) : EVP_PKEY_bits(pkey));
+        }
+        for (int i = 0; i < OpenSSLLifecycleListener.dhParameters.length; i++) {
+            if (keylen >= OpenSSLLifecycleListener.dhParameters[i].min) {
+                return OpenSSLLifecycleListener.dhParameters[i].dh;
+            }
+        }
+        return MemorySegment.NULL;
+    }
+
+    // int SSL_callback_alpn_select_proto(SSL* ssl, const unsigned char **out, unsigned char *outlen,
+    //        const unsigned char *in, unsigned int inlen, void *arg)
+    public static int openSSLCallbackAlpnSelectProto(MemorySegment ssl, MemorySegment out, MemorySegment outlen,
+            MemorySegment in, int inlen, MemorySegment arg) {
+        ContextState state = getState(arg);
+        if (state == null) {
+            log.warn(sm.getString("context.noSSL", Long.valueOf(arg.address())));
+            return SSL_TLSEXT_ERR_NOACK();
+        }
+        try (var localArena = Arena.ofConfined()) {
+            MemorySegment inSeg = in.reinterpret(inlen, localArena, null);
+            byte[] advertisedBytes = inSeg.toArray(ValueLayout.JAVA_BYTE);
+            for (byte[] negotiableProtocolBytes : state.negotiableProtocols) {
+                for (int i = 0; i <= advertisedBytes.length - negotiableProtocolBytes.length; i++) {
+                    if (advertisedBytes[i] == negotiableProtocolBytes[0]) {
+                        for (int j = 0; j < negotiableProtocolBytes.length; j++) {
+                            if (advertisedBytes[i + j] == negotiableProtocolBytes[j]) {
+                                if (j == negotiableProtocolBytes.length - 1) {
+                                    // Match
+                                    MemorySegment outSeg = out.reinterpret(ValueLayout.ADDRESS.byteSize(), localArena, null);
+                                    outSeg.set(ValueLayout.ADDRESS, 0, inSeg.asSlice(i));
+                                    MemorySegment outlenSeg = outlen.reinterpret(ValueLayout.JAVA_BYTE.byteSize(), localArena, null);
+                                    outlenSeg.set(ValueLayout.JAVA_BYTE, 0, (byte) negotiableProtocolBytes.length);
+                                    return SSL_TLSEXT_ERR_OK();
+                                }
+                            } else {
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return SSL_TLSEXT_ERR_NOACK();
+    }
+
+    public static int openSSLCallbackVerify(int preverify_ok, MemorySegment /*X509_STORE_CTX*/ x509ctx) {
+        return OpenSSLEngine.openSSLCallbackVerify(preverify_ok, x509ctx);
+    }
+
+
+    public static int openSSLCallbackCertVerify(MemorySegment /*X509_STORE_CTX*/ x509_ctx, MemorySegment param) {
+        if (log.isDebugEnabled()) {
+            log.debug("Certificate verification");
+        }
+        if (MemorySegment.NULL.equals(param)) {
+            return 0;
+        }
+        ContextState state = getState(param);
+        if (state == null) {
+            log.warn(sm.getString("context.noSSL", Long.valueOf(param.address())));
+            return 0;
+        }
+        MemorySegment ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+        MemorySegment /*STACK_OF(X509)*/ sk = X509_STORE_CTX_get0_untrusted(x509_ctx);
+        int len = OPENSSL_sk_num(sk);
+        byte[][] certificateChain = new byte[len][];
+        try (var localArena = Arena.ofConfined()) {
+            for (int i = 0; i < len; i++) {
+                MemorySegment/*(X509*)*/ x509 = OPENSSL_sk_value(sk, i);
+                MemorySegment bufPointer = localArena.allocate(ValueLayout.ADDRESS, MemorySegment.NULL);
+                int length = i2d_X509(x509, bufPointer);
+                if (length < 0) {
+                    certificateChain[i] = new byte[0];
+                    continue;
+                }
+                MemorySegment buf = bufPointer.get(ValueLayout.ADDRESS, 0);
+                certificateChain[i] = buf.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+                CRYPTO_free(buf, MemorySegment.NULL, 0); // OPENSSL_free macro
+            }
+            MemorySegment cipher = SSL_get_current_cipher(ssl);
+            String authMethod = (MemorySegment.NULL.equals(cipher)) ? "UNKNOWN"
+                    : getCipherAuthenticationMethod(SSL_CIPHER_get_auth_nid(cipher), SSL_CIPHER_get_kx_nid(cipher));
+            X509Certificate[] peerCerts = certificates(certificateChain);
+            try {
+                state.x509TrustManager.checkClientTrusted(peerCerts, authMethod);
+                return 1;
+            } catch (Exception e) {
+                log.debug(sm.getString("openssl.certificateVerificationFailed"), e);
+            }
+        }
+        return 0;
+    }
+
+    private static final int NID_kx_rsa = 1037/*NID_kx_rsa()*/;
+    //private static final int NID_kx_dhe = NID_kx_dhe();
+    //private static final int NID_kx_ecdhe = NID_kx_ecdhe();
+
+    //private static final int NID_auth_rsa = NID_auth_rsa();
+    //private static final int NID_auth_dss = NID_auth_dss();
+    //private static final int NID_auth_null = NID_auth_null();
+    //private static final int NID_auth_ecdsa = NID_auth_ecdsa();
+
+    //private static final int SSL_kRSA = 1;
+    private static final int SSL_kDHr = 2;
+    private static final int SSL_kDHd = 4;
+    private static final int SSL_kEDH = 8;
+    private static final int SSL_kDHE = SSL_kEDH;
+    private static final int SSL_kKRB5 = 10;
+    private static final int SSL_kECDHr = 20;
+    private static final int SSL_kECDHe = 40;
+    private static final int SSL_kEECDH = 80;
+    private static final int SSL_kECDHE = SSL_kEECDH;
+    //private static final int SSL_kPSK = 100;
+    //private static final int SSL_kGOST = 200;
+    //private static final int SSL_kSRP = 400;
+
+    private static final int SSL_aRSA = 1;
+    private static final int SSL_aDSS = 2;
+    private static final int SSL_aNULL = 4;
+    //private static final int SSL_aDH = 8;
+    //private static final int SSL_aECDH = 10;
+    //private static final int SSL_aKRB5 = 20;
+    private static final int SSL_aECDSA = 40;
+    //private static final int SSL_aPSK = 80;
+    //private static final int SSL_aGOST94 = 100;
+    //private static final int SSL_aGOST01 = 200;
+    //private static final int SSL_aSRP = 400;
+
+    private static final String SSL_TXT_RSA = "RSA";
+    private static final String SSL_TXT_DH = "DH";
+    private static final String SSL_TXT_DSS = "DSS";
+    private static final String SSL_TXT_KRB5 = "KRB5";
+    private static final String SSL_TXT_ECDH = "ECDH";
+    private static final String SSL_TXT_ECDSA = "ECDSA";
+
+    private static String getCipherAuthenticationMethod(int auth, int kx) {
+        switch (kx) {
+        case NID_kx_rsa:
+            return SSL_TXT_RSA;
+        case SSL_kDHr:
+            return SSL_TXT_DH + "_" + SSL_TXT_RSA;
+        case SSL_kDHd:
+            return SSL_TXT_DH + "_" + SSL_TXT_DSS;
+        case SSL_kDHE:
+            switch (auth) {
+            case SSL_aDSS:
+                return "DHE_" + SSL_TXT_DSS;
+            case SSL_aRSA:
+                return "DHE_" + SSL_TXT_RSA;
+            case SSL_aNULL:
+                return SSL_TXT_DH + "_anon";
+            default:
+                return "UNKNOWN";
+            }
+        case SSL_kKRB5:
+            return SSL_TXT_KRB5;
+        case SSL_kECDHr:
+            return SSL_TXT_ECDH + "_" + SSL_TXT_RSA;
+        case SSL_kECDHe:
+            return SSL_TXT_ECDH + "_" + SSL_TXT_ECDSA;
+        case SSL_kECDHE:
+            switch (auth) {
+            case SSL_aECDSA:
+                return "ECDHE_" + SSL_TXT_ECDSA;
+            case SSL_aRSA:
+                return "ECDHE_" + SSL_TXT_RSA;
+            case SSL_aNULL:
+                return SSL_TXT_ECDH + "_anon";
+            default:
+                return "UNKNOWN";
+            }
+        default:
+            return "UNKNOWN";
+        }
+    }
+
+    private static ThreadLocal<String> callbackPasswordTheadLocal = new ThreadLocal<>();
+
+    public static int openSSLCallbackPassword(MemorySegment /*char **/ buf, int bufsiz, int verify, MemorySegment /*void **/ cb) {
+        if (log.isDebugEnabled()) {
+            log.debug("Return password for certificate");
+        }
+        String callbackPassword = callbackPasswordTheadLocal.get();
+        if (callbackPassword != null && callbackPassword.length() > 0) {
+            try (var localArena = Arena.ofConfined()) {
+                MemorySegment callbackPasswordNative = localArena.allocateUtf8String(callbackPassword);
+                if (callbackPasswordNative.byteSize() > bufsiz) {
+                    // The password is too long
+                    log.error(sm.getString("openssl.passwordTooLong"));
+                } else {
+                    MemorySegment bufSegment = buf.reinterpret(bufsiz, localArena, null);
+                    bufSegment.copyFrom(callbackPasswordNative);
+                    return (int) callbackPasswordNative.byteSize();
+                }
+            }
+        }
+        return 0;
+    }
+
+
+    private void addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception {
+        int index = getCertificateIndex(certificate);
+        // Load Server key and certificate
+        if (certificate.getCertificateFile() != null) {
+            // Set certificate
+            //SSLContext.setCertificate(state.ctx,
+            //        SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()),
+            //        SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()),
+            //        certificate.getCertificateKeyPassword(), getCertificateIndex(certificate));
+            var certificateFileNative = localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()));
+            var certificateKeyFileNative = (certificate.getCertificateKeyFile() == null) ? certificateFileNative
+                    : localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()));
+            MemorySegment bio;
+            MemorySegment cert = MemorySegment.NULL;
+            MemorySegment key = MemorySegment.NULL;
+            if (certificate.getCertificateFile().endsWith(".pkcs12")) {
+                // Load pkcs12
+                bio = BIO_new(BIO_s_file());
+                //#  define BIO_read_filename(b,name)
+                //        (int)BIO_ctrl(b,BIO_C_SET_FILENAME, BIO_CLOSE|BIO_FP_READ,(char *)(name))
+                if (BIO_ctrl(bio, BIO_C_SET_FILENAME(), BIO_CLOSE() | BIO_FP_READ(), certificateFileNative) <= 0) {
+                    BIO_free(bio);
+                    log.error(sm.getString("openssl.errorLoadingCertificate", "[0]:" + certificate.getCertificateFile()));
+                    return;
+                }
+                MemorySegment p12 = d2i_PKCS12_bio(bio, MemorySegment.NULL);
+                BIO_free(bio);
+                if (MemorySegment.NULL.equals(p12)) {
+                    log.error(sm.getString("openssl.errorLoadingCertificate", "[1]:" + certificate.getCertificateFile()));
+                    return;
+                }
+                MemorySegment passwordAddress = MemorySegment.NULL;
+                int passwordLength = 0;
+                String callbackPassword = certificate.getCertificateKeyPassword();
+                if (callbackPassword != null && callbackPassword.length() > 0) {
+                    passwordAddress = localArena.allocateUtf8String(callbackPassword);
+                    passwordLength = (int) (passwordAddress.byteSize() - 1);
+                }
+                if (PKCS12_verify_mac(p12, passwordAddress, passwordLength) <= 0) {
+                    // Bad password
+                    log.error(sm.getString("openssl.errorLoadingCertificate", "[2]:" + certificate.getCertificateFile()));
+                    PKCS12_free(p12);
+                    return;
+                }
+                MemorySegment certPointer = localArena.allocate(ValueLayout.ADDRESS);
+                MemorySegment keyPointer = localArena.allocate(ValueLayout.ADDRESS);
+                if (PKCS12_parse(p12, passwordAddress, keyPointer, certPointer, MemorySegment.NULL) <= 0) {
+                    log.error(sm.getString("openssl.errorLoadingCertificate", "[3]:" + certificate.getCertificateFile()));
+                    PKCS12_free(p12);
+                    return;
+                }
+                PKCS12_free(p12);
+                cert = certPointer.get(ValueLayout.ADDRESS, 0);
+                key = keyPointer.get(ValueLayout.ADDRESS, 0);
+            } else {
+                // Load key
+                bio = BIO_new(BIO_s_file());
+                //#  define BIO_read_filename(b,name)
+                //        (int)BIO_ctrl(b,BIO_C_SET_FILENAME, BIO_CLOSE|BIO_FP_READ,(char *)(name))
+                if (BIO_ctrl(bio, BIO_C_SET_FILENAME(), BIO_CLOSE() | BIO_FP_READ(), certificateKeyFileNative) <= 0) {
+                    BIO_free(bio);
+                    log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateKeyFile()));
+                    return;
+                }
+                key = MemorySegment.NULL;
+                for (int i = 0; i < 3; i++) {
+                    try {
+                        callbackPasswordTheadLocal.set(certificate.getCertificateKeyPassword());
+                        key = PEM_read_bio_PrivateKey(bio, MemorySegment.NULL, openSSLCallbackPassword, MemorySegment.NULL);
+                    } finally {
+                        callbackPasswordTheadLocal.set(null);
+                    }
+                    if (!MemorySegment.NULL.equals(key)) {
+                        break;
+                    }
+                    BIO_ctrl(bio, BIO_CTRL_RESET(), 0, MemorySegment.NULL);
+                }
+                BIO_free(bio);
+                if (MemorySegment.NULL.equals(key)) {
+                    if (!MemorySegment.NULL.equals(OpenSSLLifecycleListener.enginePointer)) {
+                        key = ENGINE_load_private_key(OpenSSLLifecycleListener.enginePointer, certificateKeyFileNative,
+                                MemorySegment.NULL, MemorySegment.NULL);
+                    }
+                }
+                if (MemorySegment.NULL.equals(key)) {
+                    log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateKeyFile()));
+                    return;
+                }
+                // Load certificate
+                bio = BIO_new(BIO_s_file());
+                if (BIO_ctrl(bio, BIO_C_SET_FILENAME(), BIO_CLOSE() | BIO_FP_READ(), certificateFileNative) <= 0) {
+                    BIO_free(bio);
+                    log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateFile()));
+                    return;
+                }
+                try {
+                    callbackPasswordTheadLocal.set(certificate.getCertificateKeyPassword());
+                    cert = PEM_read_bio_X509_AUX(bio, MemorySegment.NULL, openSSLCallbackPassword, MemorySegment.NULL);
+                } finally {
+                    callbackPasswordTheadLocal.set(null);
+                }
+                if (MemorySegment.NULL.equals(cert) &&
+                        // Missing ERR_GET_REASON(ERR_peek_last_error())
+                        /*int ERR_GET_REASON(unsigned long errcode) {
+                         *    if (ERR_SYSTEM_ERROR(errcode))
+                         *        return errcode & ERR_SYSTEM_MASK;
+                         *    return errcode & ERR_REASON_MASK;
+                         *}
+                         *# define ERR_SYSTEM_ERROR(errcode)      (((errcode) & ERR_SYSTEM_FLAG) != 0)
+                         *# define ERR_SYSTEM_FLAG                ((unsigned int)INT_MAX + 1)
+                         *# define ERR_SYSTEM_MASK                ((unsigned int)INT_MAX)
+                         *# define ERR_REASON_MASK                0X7FFFFF
+                         */
+                        ((ERR_peek_last_error() & 0X7FFFFF) == PEM_R_NO_START_LINE())) {
+                    ERR_clear_error();
+                    BIO_ctrl(bio, BIO_CTRL_RESET(), 0, MemorySegment.NULL);
+                    cert = d2i_X509_bio(bio, MemorySegment.NULL);
+                }
+                BIO_free(bio);
+                if (MemorySegment.NULL.equals(cert)) {
+                    log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateFile()));
+                    return;
+                }
+            }
+            if (SSL_CTX_use_certificate(state.sslCtx, cert) <= 0) {
+                logLastError(localArena, "openssl.errorLoadingCertificate");
+                return;
+            }
+            if (SSL_CTX_use_PrivateKey(state.sslCtx, key) <= 0) {
+                logLastError(localArena, "openssl.errorLoadingPrivateKey");
+                return;
+            }
+            if (SSL_CTX_check_private_key(state.sslCtx) <= 0) {
+                logLastError(localArena, "openssl.errorPrivateKeyCheck");
+                return;
+            }
+            // Try to read DH parameters from the (first) SSLCertificateFile
+            if (index == SSL_AIDX_RSA) {
+                bio = BIO_new_file(certificateFileNative, localArena.allocateUtf8String("r"));
+                var dh = PEM_read_bio_DHparams(bio, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
+                BIO_free(bio);
+                // #  define SSL_CTX_set_tmp_dh(sslCtx,dh) \
+                //           SSL_CTX_ctrl(sslCtx,SSL_CTRL_SET_TMP_DH,0,(char *)(dh))
+                if (!MemorySegment.NULL.equals(dh)) {
+                    SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_TMP_DH(), 0, dh);
+                    DH_free(dh);
+                }
+            }
+            // Similarly, try to read the ECDH curve name from SSLCertificateFile...
+            bio = BIO_new_file(certificateFileNative, localArena.allocateUtf8String("r"));
+            var ecparams = PEM_read_bio_ECPKParameters(bio, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
+            BIO_free(bio);
+            if (!MemorySegment.NULL.equals(ecparams)) {
+                int nid = EC_GROUP_get_curve_name(ecparams);
+                var eckey = EC_KEY_new_by_curve_name(nid);
+                // #  define SSL_CTX_set_tmp_ecdh(sslCtx,ecdh) \
+                //           SSL_CTX_ctrl(sslCtx,SSL_CTRL_SET_TMP_ECDH,0,(char *)(ecdh))
+                SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_TMP_ECDH(), 0, eckey);
+                EC_KEY_free(eckey);
+                EC_GROUP_free(ecparams);
+            }
+            // Set callback for DH parameters
+            var openSSLCallbackTmpDH = Linker.nativeLinker().upcallStub(openSSLCallbackTmpDHHandle,
+                    openSSLCallbackTmpDHFunctionDescriptor, contextArena);
+            SSL_CTX_set_tmp_dh_callback(state.sslCtx, openSSLCallbackTmpDH);
+            // Set certificate chain file
+            if (certificate.getCertificateChainFile() != null) {
+                var certificateChainFileNative =
+                        localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()));
+                // SSLContext.setCertificateChainFile(state.ctx,
+                //        SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false);
+                if (SSL_CTX_use_certificate_chain_file(state.sslCtx, certificateChainFileNative) <= 0) {
+                    log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateChainFile()));
+                }
+            }
+            // Set revocation
+            //SSLContext.setCARevocation(state.ctx,
+            //        SSLHostConfig.adjustRelativePath(
+            //                sslHostConfig.getCertificateRevocationListFile()),
+            //        SSLHostConfig.adjustRelativePath(
+            //                sslHostConfig.getCertificateRevocationListPath()));
+            MemorySegment certificateStore = SSL_CTX_get_cert_store(state.sslCtx);
+            if (sslHostConfig.getCertificateRevocationListFile() != null) {
+                MemorySegment x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_file());
+                var certificateRevocationListFileNative =
+                        localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile()));
+                //X509_LOOKUP_ctrl(lookup,X509_L_FILE_LOAD,file,type,NULL)
+                if (X509_LOOKUP_ctrl(x509Lookup, X509_L_FILE_LOAD(), certificateRevocationListFileNative,
+                        X509_FILETYPE_PEM(), MemorySegment.NULL) <= 0) {
+                    log.error(sm.getString("openssl.errorLoadingCertificateRevocationList", sslHostConfig.getCertificateRevocationListFile()));
+                }
+            }
+            if (sslHostConfig.getCertificateRevocationListPath() != null) {
+                MemorySegment x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_hash_dir());
+                var certificateRevocationListPathNative =
+                        localArena.allocateUtf8String(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath()));
+                //X509_LOOKUP_ctrl(lookup,X509_L_ADD_DIR,path,type,NULL)
+                if (X509_LOOKUP_ctrl(x509Lookup, X509_L_ADD_DIR(), certificateRevocationListPathNative,
+                        X509_FILETYPE_PEM(), MemorySegment.NULL) <= 0) {
+                    log.error(sm.getString("openssl.errorLoadingCertificateRevocationList", sslHostConfig.getCertificateRevocationListPath()));
+                }
+            }
+            X509_STORE_set_flags(certificateStore, X509_V_FLAG_CRL_CHECK() | X509_V_FLAG_CRL_CHECK_ALL());
+        } else {
+            String alias = certificate.getCertificateKeyAlias();
+            X509KeyManager x509KeyManager = certificate.getCertificateKeyManager();
+            if (alias == null) {
+                alias = "tomcat";
+            }
+            X509Certificate[] chain = x509KeyManager.getCertificateChain(alias);
+            if (chain == null) {
+                alias = findAlias(x509KeyManager, certificate);
+                chain = x509KeyManager.getCertificateChain(alias);
+            }
+            PrivateKey key = x509KeyManager.getPrivateKey(alias);
+            StringBuilder sb = new StringBuilder(BEGIN_KEY);
+            sb.append(Base64.getMimeEncoder(64, new byte[] {'\n'}).encodeToString(key.getEncoded()));
+            sb.append(END_KEY);
+            //SSLContext.setCertificateRaw(state.ctx, chain[0].getEncoded(),
+            //        sb.toString().getBytes(StandardCharsets.US_ASCII),
+            //        getCertificateIndex(certificate));
+            var rawCertificate = localArena.allocateArray(ValueLayout.JAVA_BYTE, chain[0].getEncoded());
+            var rawCertificatePointer = localArena.allocate(ValueLayout.ADDRESS, rawCertificate);
+            var rawKey = localArena.allocateArray(ValueLayout.JAVA_BYTE, sb.toString().getBytes(StandardCharsets.US_ASCII));
+            var x509cert = d2i_X509(MemorySegment.NULL, rawCertificatePointer, rawCertificate.byteSize());
+            if (MemorySegment.NULL.equals(x509cert)) {
+                logLastError(localArena, "openssl.errorLoadingCertificate");
+                return;
+            }
+            var bio = BIO_new(BIO_s_mem());
+            BIO_write(bio, rawKey, (int) rawKey.byteSize());
+            MemorySegment privateKeyAddress = PEM_read_bio_PrivateKey(bio, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
+            BIO_free(bio);
+            if (MemorySegment.NULL.equals(privateKeyAddress)) {
+                logLastError(localArena, "openssl.errorLoadingPrivateKey");
+                return;
+            }
+            if (SSL_CTX_use_certificate(state.sslCtx, x509cert) <= 0) {
+                logLastError(localArena, "openssl.errorLoadingCertificate");
+                return;
+            }
+            if (SSL_CTX_use_PrivateKey(state.sslCtx, privateKeyAddress) <= 0) {
+                logLastError(localArena, "openssl.errorLoadingPrivateKey");
+                return;
+            }
+            if (SSL_CTX_check_private_key(state.sslCtx) <= 0) {
+                logLastError(localArena, "openssl.errorPrivateKeyCheck");
+                return;
+            }
+            // Set callback for DH parameters
+            var openSSLCallbackTmpDH = Linker.nativeLinker().upcallStub(openSSLCallbackTmpDHHandle,
+                    openSSLCallbackTmpDHFunctionDescriptor, contextArena);
+            SSL_CTX_set_tmp_dh_callback(state.sslCtx, openSSLCallbackTmpDH);
+            for (int i = 1; i < chain.length; i++) {
+                //SSLContext.addChainCertificateRaw(state.ctx, chain[i].getEncoded());
+                var rawCertificateChain = localArena.allocateArray(ValueLayout.JAVA_BYTE, chain[i].getEncoded());
+                var rawCertificateChainPointer = localArena.allocate(ValueLayout.ADDRESS, rawCertificateChain);
+                var x509certChain = d2i_X509(MemorySegment.NULL, rawCertificateChainPointer, rawCertificateChain.byteSize());
+                if (MemorySegment.NULL.equals(x509certChain)) {
+                    logLastError(localArena, "openssl.errorLoadingCertificate");
+                    return;
+                }
+                // # define SSL_CTX_add0_chain_cert(sslCtx,x509) SSL_CTX_ctrl(sslCtx,SSL_CTRL_CHAIN_CERT,0,(char *)(x509))
+                if (SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_CHAIN_CERT(), 0, x509certChain) <= 0) {
+                    logLastError(localArena, "openssl.errorAddingCertificate");
+                    return;
+                }
+            }
+        }
+    }
+
+
+    private static int getCertificateIndex(SSLHostConfigCertificate certificate) {
+        int result = -1;
+        // If the type is undefined there will only be one certificate (enforced
+        // in SSLHostConfig) so use the RSA slot.
+        if (certificate.getType() == Type.RSA || certificate.getType() == Type.UNDEFINED) {
+            result = SSL_AIDX_RSA;
+        } else if (certificate.getType() == Type.EC) {
+            result = SSL_AIDX_ECC;
+        } else if (certificate.getType() == Type.DSA) {
+            result = SSL_AIDX_DSA;
+        } else {
+            result = SSL_AIDX_MAX;
+        }
+        return result;
+    }
+
+
+    /*
+     * Find a valid alias when none was specified in the config.
+     */
+    private static String findAlias(X509KeyManager keyManager,
+            SSLHostConfigCertificate certificate) {
+
+        Type type = certificate.getType();
+        String result = null;
+
+        List<Type> candidateTypes = new ArrayList<>();
+        if (Type.UNDEFINED.equals(type)) {
+            // Try all types to find an suitable alias
+            candidateTypes.addAll(Arrays.asList(Type.values()));
+            candidateTypes.remove(Type.UNDEFINED);
+        } else {
+            // Look for the specific type to find a suitable alias
+            candidateTypes.add(type);
+        }
+
+        Iterator<Type> iter = candidateTypes.iterator();
+        while (result == null && iter.hasNext()) {
+            result = keyManager.chooseServerAlias(iter.next().toString(),  null,  null);
+        }
+
+        return result;
+    }
+
+    private static X509TrustManager chooseTrustManager(TrustManager[] managers) {
+        for (TrustManager m : managers) {
+            if (m instanceof X509TrustManager) {
+                return (X509TrustManager) m;
+            }
+        }
+        throw new IllegalStateException(sm.getString("openssl.trustManagerMissing"));
+    }
+
+    private static X509Certificate[] certificates(byte[][] chain) {
+        X509Certificate[] peerCerts = new X509Certificate[chain.length];
+        for (int i = 0; i < peerCerts.length; i++) {
+            peerCerts[i] = new OpenSSLX509Certificate(chain[i]);
+        }
+        return peerCerts;
+    }
+
+
+    private static void logLastError(SegmentAllocator allocator, String string) {
+        var buf = allocator.allocateArray(ValueLayout.JAVA_BYTE, new byte[128]);
+        ERR_error_string(ERR_get_error(), buf);
+        String err = buf.getUtf8String(0);
+        log.error(sm.getString(string, err));
+    }
+
+
+    @Override
+    public SSLSessionContext getServerSessionContext() {
+        return sessionContext;
+    }
+
+    @Override
+    public synchronized SSLEngine createSSLEngine() {
+        return new OpenSSLEngine(cleaner, state.sslCtx, defaultProtocol, false, sessionContext,
+                alpn, initialized,
+                sslHostConfig.getCertificateVerificationDepth(),
+                sslHostConfig.getCertificateVerification() == CertificateVerification.OPTIONAL_NO_CA,
+                noOcspCheck);
+    }
+
+    @Override
+    public SSLServerSocketFactory getServerSocketFactory() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public SSLParameters getSupportedSSLParameters() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public X509Certificate[] getCertificateChain(String alias) {
+        X509Certificate[] chain = null;
+        X509KeyManager x509KeyManager = certificate.getCertificateKeyManager();
+        if (x509KeyManager != null) {
+            if (alias == null) {
+                alias = "tomcat";
+            }
+            chain = x509KeyManager.getCertificateChain(alias);
+            if (chain == null) {
+                alias = findAlias(x509KeyManager, certificate);
+                chain = x509KeyManager.getCertificateChain(alias);
+            }
+        }
+
+        return chain;
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        X509Certificate[] acceptedCerts = null;
+        if (state.x509TrustManager != null) {
+            acceptedCerts = state.x509TrustManager.getAcceptedIssuers();
+        }
+        return acceptedCerts;
+    }
+
+
+    private static class ContextState implements Runnable {
+
+        private final Arena stateArena = Arena.ofShared();
+        private final MemorySegment sslCtx;
+        private final MemorySegment confCtx;
+        private final List<byte[]> negotiableProtocols;
+
+        private X509TrustManager x509TrustManager = null;
+
+        private ContextState(MemorySegment sslCtx, MemorySegment confCtx, List<byte[]> negotiableProtocols) {
+            states.put(Long.valueOf(sslCtx.address()), this);
+            this.negotiableProtocols = negotiableProtocols;
+            // Use another arena to avoid keeping a reference through segments
+            // This also allows making further accesses to the main pointers safer
+            this.sslCtx = sslCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, null);
+            if (!MemorySegment.NULL.equals(confCtx)) {
+                this.confCtx = confCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, null);
+            } else {
+                this.confCtx = null;
+            }
+        }
+
+        @Override
+        public void run() {
+            try {
+                states.remove(Long.valueOf(sslCtx.address()));
+                SSL_CTX_free(sslCtx);
+                if (confCtx != null) {
+                    SSL_CONF_CTX_free(confCtx);
+                }
+            } finally {
+                stateArena.close();
+            }
+        }
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
new file mode 100644
index 0000000000..b7e6c15578
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java
@@ -0,0 +1,1797 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.lang.foreign.Arena;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.ref.Cleaner;
+import java.lang.ref.Cleaner.Cleanable;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionBindingEvent;
+import javax.net.ssl.SSLSessionBindingListener;
+import javax.net.ssl.SSLSessionContext;
+
+import static org.apache.tomcat.util.openssl.openssl_compat_h.*;
+import static org.apache.tomcat.util.openssl.openssl_h.*;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.Asn1Parser;
+import org.apache.tomcat.util.net.Constants;
+import org.apache.tomcat.util.net.SSLUtil;
+import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Implements a {@link SSLEngine} using
+ * <a href="https://www.openssl.org/docs/crypto/BIO_s_bio.html#EXAMPLE">OpenSSL
+ * BIO abstractions</a>.
+ */
+public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolInfo {
+
+    private static final Log log = LogFactory.getLog(OpenSSLEngine.class);
+    private static final StringManager sm = StringManager.getManager(OpenSSLEngine.class);
+
+    private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
+
+    public static final Set<String> AVAILABLE_CIPHER_SUITES;
+
+    public static final Set<String> IMPLEMENTED_PROTOCOLS_SET;
+
+    private static final MethodHandle openSSLCallbackInfoHandle;
+    private static final MethodHandle openSSLCallbackVerifyHandle;
+
+    private static final FunctionDescriptor openSSLCallbackInfoFunctionDescriptor =
+            FunctionDescriptor.ofVoid(ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
+    private static final FunctionDescriptor openSSLCallbackVerifyFunctionDescriptor =
+            FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS);
+
+    static {
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        try {
+            openSSLCallbackInfoHandle = lookup.findStatic(OpenSSLEngine.class, "openSSLCallbackInfo",
+                    MethodType.methodType(void.class, MemorySegment.class, int.class, int.class));
+            openSSLCallbackVerifyHandle = lookup.findStatic(OpenSSLEngine.class, "openSSLCallbackVerify",
+                    MethodType.methodType(int.class, int.class, MemorySegment.class));
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+
+        OpenSSLLifecycleListener.initLibrary();
+
+        final Set<String> availableCipherSuites = new LinkedHashSet<>(128);
+        try (var localArena = Arena.ofConfined()) {
+            var sslCtx = SSL_CTX_new(TLS_server_method());
+            try {
+                SSL_CTX_set_options(sslCtx, SSL_OP_ALL());
+                SSL_CTX_set_cipher_list(sslCtx, localArena.allocateUtf8String("ALL"));
+                var ssl = SSL_new(sslCtx);
+                SSL_set_accept_state(ssl);
+                try {
+                    for (String c : getCiphers(ssl)) {
+                        // Filter out bad input.
+                        if (c == null || c.length() == 0 || availableCipherSuites.contains(c)) {
+                            continue;
+                        }
+                        availableCipherSuites.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c));
+                    }
+                } finally {
+                    SSL_free(ssl);
+                }
+            } finally {
+                SSL_CTX_free(sslCtx);
+            }
+        } catch (Exception e) {
+            log.warn(sm.getString("engine.ciphersFailure"), e);
+        }
+        AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites);
+
+        HashSet<String> protocols = new HashSet<>();
+        protocols.add(Constants.SSL_PROTO_SSLv2Hello);
+        protocols.add(Constants.SSL_PROTO_SSLv2);
+        protocols.add(Constants.SSL_PROTO_SSLv3);
+        protocols.add(Constants.SSL_PROTO_TLSv1);
+        protocols.add(Constants.SSL_PROTO_TLSv1_1);
+        protocols.add(Constants.SSL_PROTO_TLSv1_2);
+        protocols.add(Constants.SSL_PROTO_TLSv1_3);
+        IMPLEMENTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols);
+    }
+
+    private static String[] getCiphers(MemorySegment ssl) {
+        MemorySegment sk = SSL_get_ciphers(ssl);
+        int len = OPENSSL_sk_num(sk);
+        if (len <= 0) {
+            return null;
+        }
+        ArrayList<String> ciphers = new ArrayList<>(len);
+        for (int i = 0; i < len; i++) {
+            MemorySegment cipher = OPENSSL_sk_value(sk, i);
+            MemorySegment cipherName = SSL_CIPHER_get_name(cipher);
+            ciphers.add(cipherName.getUtf8String(0));
+        }
+        return ciphers.toArray(new String[0]);
+    }
+
+    private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
+    private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
+    private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
+
+    // Protocols
+    static final int VERIFY_DEPTH = 10;
+
+    // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256)
+    static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
+
+    static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
+
+    enum ClientAuthMode {
+        NONE,
+        OPTIONAL,
+        REQUIRE,
+    }
+
+    private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
+
+    private static final ConcurrentHashMap<Long, EngineState> states = new ConcurrentHashMap<>();
+    private static EngineState getState(MemorySegment ssl) {
+        return states.get(Long.valueOf(ssl.address()));
+    }
+
+    private final EngineState state;
+    private final Arena engineArena;
+    private final Cleanable cleanable;
+    private MemorySegment bufSegment = null;
+
+    private enum Accepted { NOT, IMPLICIT, EXPLICIT }
+    private Accepted accepted = Accepted.NOT;
+    private enum PHAState { NONE, START, COMPLETE }
+    private boolean handshakeFinished;
+    private int currentHandshake;
+    private boolean receivedShutdown;
+    private volatile boolean destroyed;
+
+    // Use an invalid cipherSuite until the handshake is completed
+    // See http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLEngine.html#getSession()
+    private volatile String version;
+    private volatile String cipher;
+    private volatile String applicationProtocol;
+
+    private volatile Certificate[] peerCerts;
+    private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE;
+
+    // SSL Engine status variables
+    private boolean isInboundDone;
+    private boolean isOutboundDone;
+    private boolean engineClosed;
+    private boolean sendHandshakeError = false;
+
+    private final boolean clientMode;
+    private final String fallbackApplicationProtocol;
+    private final OpenSSLSessionContext sessionContext;
+    private final boolean alpn;
+    private final boolean initialized;
+    private final boolean certificateVerificationOptionalNoCA;
+
+    private String selectedProtocol = null;
+
+    private final OpenSSLSession session;
+
+    /**
+     * Creates a new instance
+     *
+     * @param sslCtx an OpenSSL {@code SSL_CTX} object
+     * @param fallbackApplicationProtocol the fallback application protocol
+     * @param clientMode {@code true} if this is used for clients, {@code false}
+     * otherwise
+     * @param sessionContext the {@link OpenSSLSessionContext} this
+     * {@link SSLEngine} belongs to.
+     * @param alpn {@code true} if alpn should be used, {@code false}
+     * otherwise
+     * @param initialized {@code true} if this instance gets its protocol,
+     * cipher and client verification from the {@code SSL_CTX} {@code sslCtx}
+     * @param certificateVerificationDepth Certificate verification depth
+     * @param certificateVerificationOptionalNoCA Skip CA verification in
+     *   optional mode
+     */
+    OpenSSLEngine(Cleaner cleaner, MemorySegment sslCtx, String fallbackApplicationProtocol,
+            boolean clientMode, OpenSSLSessionContext sessionContext, boolean alpn,
+            boolean initialized, int certificateVerificationDepth,
+            boolean certificateVerificationOptionalNoCA, boolean noOcspCheck) {
+        if (sslCtx == null) {
+            throw new IllegalArgumentException(sm.getString("engine.noSSLContext"));
+        }
+        engineArena = Arena.ofAuto();
+        bufSegment = engineArena.allocate(MAX_ENCRYPTED_PACKET_LENGTH);
+        session = new OpenSSLSession();
+        var ssl = SSL_new(sslCtx);
+        // Set ssl_info_callback
+        var openSSLCallbackInfo = Linker.nativeLinker().upcallStub(openSSLCallbackInfoHandle,
+                openSSLCallbackInfoFunctionDescriptor, engineArena);
+        SSL_set_info_callback(ssl, openSSLCallbackInfo);
+        if (clientMode) {
+            SSL_set_connect_state(ssl);
+        } else {
+            SSL_set_accept_state(ssl);
+        }
+        SSL_set_verify_result(ssl, X509_V_OK());
+        try (var localArena = Arena.ofConfined()) {
+            var internalBIOPointer = localArena.allocate(ValueLayout.ADDRESS);
+            var networkBIOPointer = localArena.allocate(ValueLayout.ADDRESS);
+            BIO_new_bio_pair(internalBIOPointer, 0, networkBIOPointer, 0);
+            var internalBIO = internalBIOPointer.get(ValueLayout.ADDRESS, 0);
+            var networkBIO = networkBIOPointer.get(ValueLayout.ADDRESS, 0);
+            SSL_set_bio(ssl, internalBIO, internalBIO);
+            state = new EngineState(ssl, networkBIO, certificateVerificationDepth, noOcspCheck);
+        }
+        this.fallbackApplicationProtocol = fallbackApplicationProtocol;
+        this.clientMode = clientMode;
+        this.sessionContext = sessionContext;
+        this.alpn = alpn;
+        this.initialized = initialized;
+        this.certificateVerificationOptionalNoCA = certificateVerificationOptionalNoCA;
+        cleanable = cleaner.register(this, state);
+    }
+
+    @Override
+    public String getNegotiatedProtocol() {
+        return selectedProtocol;
+    }
+
+    /**
+     * Destroys this engine.
+     */
+    public synchronized void shutdown() {
+        if (!destroyed) {
+            destroyed = true;
+            cleanable.clean();
+            // internal errors can cause shutdown without marking the engine closed
+            isInboundDone = isOutboundDone = engineClosed = true;
+            bufSegment = null;
+        }
+    }
+
+    /**
+     * Write plain text data to the OpenSSL internal BIO
+     *
+     * Calling this function with src.remaining == 0 is undefined.
+     * @throws SSLException if the OpenSSL error check fails
+     */
+    private int writePlaintextData(final MemorySegment ssl, final ByteBuffer src) throws SSLException {
+        clearLastError();
+        final int pos = src.position();
+        final int len = Math.min(src.remaining(), MAX_PLAINTEXT_LENGTH);
+        MemorySegment srcSegment = src.isDirect() ? MemorySegment.ofBuffer(src) : bufSegment;
+        if (!src.isDirect()) {
+            MemorySegment.copy(src.array(), pos, bufSegment, ValueLayout.JAVA_BYTE, 0, len);
+        }
+        final int sslWrote = SSL_write(ssl, srcSegment, len);
+        if (sslWrote > 0) {
+            src.position(pos + sslWrote);
+            return sslWrote;
+        } else {
+            checkLastError();
+        }
+        return 0;
+    }
+
+    /**
+     * Write encrypted data to the OpenSSL network BIO.
+     * @throws SSLException if the OpenSSL error check fails
+     */
+    private int writeEncryptedData(final MemorySegment networkBIO, final ByteBuffer src) throws SSLException {
+        clearLastError();
+        final int pos = src.position();
+        final int len = src.remaining();
+        MemorySegment srcSegment = src.isDirect() ? MemorySegment.ofBuffer(src) : bufSegment;
+        if (!src.isDirect()) {
+            MemorySegment.copy(src.array(), pos, bufSegment, ValueLayout.JAVA_BYTE, 0, len);
+        }
+        final int netWrote = BIO_write(networkBIO, srcSegment, len);
+        if (netWrote > 0) {
+            src.position(pos + netWrote);
+            return netWrote;
+        } else {
+            checkLastError();
+        }
+        return 0;
+    }
+
+    /**
+     * Read plain text data from the OpenSSL internal BIO
+     * @throws SSLException if the OpenSSL error check fails
+     */
+    private int readPlaintextData(final MemorySegment ssl, final ByteBuffer dst) throws SSLException {
+        clearLastError();
+        final int pos = dst.position();
+        final int len = Math.min(dst.remaining(), MAX_ENCRYPTED_PACKET_LENGTH);
+        MemorySegment dstSegment = dst.isDirect() ? MemorySegment.ofBuffer(dst) : bufSegment;
+        final int sslRead = SSL_read(ssl, dstSegment, len);
+        if (sslRead > 0) {
+            if (!dst.isDirect()) {
+                MemorySegment.copy(dstSegment, ValueLayout.JAVA_BYTE, 0, dst.array(), pos, sslRead);
+            }
+            dst.position(pos + sslRead);
+            return sslRead;
+        } else {
+            checkLastError();
+        }
+        return 0;
+    }
+
+    /**
+     * Read encrypted data from the OpenSSL network BIO
+     * @throws SSLException if the OpenSSL error check fails
+     */
+    private int readEncryptedData(final MemorySegment networkBIO, final ByteBuffer dst, final int pending) throws SSLException {
+        clearLastError();
+        final int pos = dst.position();
+        MemorySegment dstSegment = dst.isDirect() ? MemorySegment.ofBuffer(dst) : bufSegment;
+        final int bioRead = BIO_read(networkBIO, dstSegment, pending);
+        if (bioRead > 0) {
+            if (!dst.isDirect()) {
+                MemorySegment.copy(dstSegment, ValueLayout.JAVA_BYTE, 0, dst.array(), pos, bioRead);
+            }
+            dst.position(pos + bioRead);
+            return bioRead;
+        } else {
+            checkLastError();
+        }
+        return 0;
+    }
+
+    @Override
+    public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
+        // Check to make sure the engine has not been closed
+        if (destroyed) {
+            return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+        }
+
+        // Throw required runtime exceptions
+        if (srcs == null || dst == null) {
+            throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+        }
+        if (offset >= srcs.length || offset + length > srcs.length) {
+            throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray",
+                    Integer.toString(offset), Integer.toString(length),
+                    Integer.toString(srcs.length)));
+        }
+        if (dst.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        // Prepare OpenSSL to work in server mode and receive handshake
+        if (accepted == Accepted.NOT) {
+            beginHandshakeImplicitly();
+        }
+
+        // In handshake or close_notify stages, check if call to wrap was made
+        // without regard to the handshake status.
+        SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+
+        if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
+            return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_UNWRAP, 0, 0);
+        }
+
+        int bytesProduced = 0;
+        int pendingNet;
+
+        // Check for pending data in the network BIO
+        pendingNet = (int) BIO_ctrl_pending(state.networkBIO);
+        if (pendingNet > 0) {
+            // Do we have enough room in destination to write encrypted data?
+            int capacity = dst.remaining();
+            if (capacity < pendingNet) {
+                return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, handshakeStatus, 0, 0);
+            }
+
+            // Write the pending data from the network BIO into the dst buffer
+            try {
+                bytesProduced = readEncryptedData(state.networkBIO, dst, pendingNet);
+            } catch (Exception e) {
+                throw new SSLException(e);
+            }
+
+            // If isOutboundDone is set, then the data from the network BIO
+            // was the close_notify message -- we are not required to wait
+            // for the receipt the peer's close_notify message -- shutdown.
+            if (isOutboundDone) {
+                shutdown();
+            }
+
+            return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
+        }
+
+        // There was no pending data in the network BIO -- encrypt any application data
+        int bytesConsumed = 0;
+        int endOffset = offset + length;
+        for (int i = offset; i < endOffset; ++i) {
+            final ByteBuffer src = srcs[i];
+            if (src == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+            }
+            while (src.hasRemaining()) {
+
+                int bytesWritten = 0;
+                // Write plain text application data to the SSL engine
+                try {
+                    bytesWritten = writePlaintextData(state.ssl, src);
+                    bytesConsumed += bytesWritten;
+                } catch (Exception e) {
+                    throw new SSLException(e);
+                }
+
+                if (bytesWritten == 0) {
+                    throw new IllegalStateException(sm.getString("engine.failedToWriteBytes"));
+                }
+
+                // Check to see if the engine wrote data into the network BIO
+                pendingNet = (int) BIO_ctrl_pending(state.networkBIO);
+                if (pendingNet > 0) {
+                    // Do we have enough room in dst to write encrypted data?
+                    int capacity = dst.remaining();
+                    if (capacity < pendingNet) {
+                        return new SSLEngineResult(
+                                SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
+                    }
+
+                    // Write the pending data from the network BIO into the dst buffer
+                    try {
+                        bytesProduced += readEncryptedData(state.networkBIO, dst, pendingNet);
+                    } catch (Exception e) {
+                        throw new SSLException(e);
+                    }
+
+                    return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+                }
+            }
+        }
+
+        return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
+    }
+
+    @Override
+    public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
+        // Check to make sure the engine has not been closed
+        if (destroyed) {
+            return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0);
+        }
+
+        // Throw required runtime exceptions
+        if (src == null || dsts == null) {
+            throw new IllegalArgumentException(sm.getString("engine.nullBuffer"));
+        }
+        if (offset >= dsts.length || offset + length > dsts.length) {
+            throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray",
+                    Integer.toString(offset), Integer.toString(length),
+                    Integer.toString(dsts.length)));
+        }
+        int capacity = 0;
+        final int endOffset = offset + length;
+        for (int i = offset; i < endOffset; i++) {
+            ByteBuffer dst = dsts[i];
+            if (dst == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullBufferInArray"));
+            }
+            if (dst.isReadOnly()) {
+                throw new ReadOnlyBufferException();
+            }
+            capacity += dst.remaining();
+        }
+
+        // Prepare OpenSSL to work in server mode and receive handshake
+        if (accepted == Accepted.NOT) {
+            beginHandshakeImplicitly();
+        }
+
+        // In handshake or close_notify stages, check if call to unwrap was made
+        // without regard to the handshake status.
+        SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
+        if ((!handshakeFinished || engineClosed) && handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
+            return new SSLEngineResult(getEngineStatus(), SSLEngineResult.HandshakeStatus.NEED_WRAP, 0, 0);
+        }
+
+        int len = src.remaining();
+
+        // protect against protocol overflow attack vector
+        if (len > MAX_ENCRYPTED_PACKET_LENGTH) {
+            isInboundDone = true;
+            isOutboundDone = true;
+            engineClosed = true;
+            shutdown();
+            throw new SSLException(sm.getString("engine.oversizedPacket"));
+        }
+
+        // Write encrypted data to network BIO
+        int written = 0;
+        try {
+            written = writeEncryptedData(state.networkBIO, src);
+        } catch (Exception e) {
+            throw new SSLException(e);
+        }
+
+        // There won't be any application data until we're done handshaking
+        //
+        // We first check handshakeFinished to eliminate the overhead of extra JNI call if possible.
+        int pendingApp = pendingReadableBytesInSSL();
+        if (!handshakeFinished) {
+            pendingApp = 0;
+        }
+        int bytesProduced = 0;
+        int idx = offset;
+        // Do we have enough room in dsts to write decrypted data?
+        if (capacity == 0) {
+            return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), written, 0);
+        }
+
+        while (pendingApp > 0) {
+            if (idx == endOffset) {
+                // Destination buffer state changed (no remaining space although
+                // capacity is still available), so break loop with an error
+                throw new IllegalStateException(sm.getString("engine.invalidDestinationBuffersState"));
+            }
+            // Write decrypted data to dsts buffers
+            while (idx < endOffset) {
+                ByteBuffer dst = dsts[idx];
+                if (!dst.hasRemaining()) {
+                    idx++;
+                    continue;
+                }
+
+                if (pendingApp <= 0) {
+                    break;
+                }
+
+                int bytesRead;
+                try {
+                    bytesRead = readPlaintextData(state.ssl, dst);
+                } catch (Exception e) {
+                    throw new SSLException(e);
+                }
+
+                if (bytesRead == 0) {
+                    // This should not be possible. pendingApp is positive
+                    // therefore the read should have read at least one byte.
+                    throw new IllegalStateException(sm.getString("engine.failedToReadAvailableBytes"));
+                }
+
+                bytesProduced += bytesRead;
+                pendingApp -= bytesRead;
+                capacity -= bytesRead;
+
+                if (!dst.hasRemaining()) {
+                    idx++;
+                }
+            }
+            if (capacity == 0) {
+                break;
+            } else if (pendingApp == 0) {
+                pendingApp = pendingReadableBytesInSSL();
+            }
+        }
+
+        // Check to see if we received a close_notify message from the peer
+        if (!receivedShutdown && (SSL_get_shutdown(state.ssl) & SSL_RECEIVED_SHUTDOWN()) == SSL_RECEIVED_SHUTDOWN()) {
+            receivedShutdown = true;
+            closeOutbound();
+            closeInbound();
+        }
+
+        if (bytesProduced == 0 && (written == 0 || (written > 0 && !src.hasRemaining() && handshakeFinished))) {
+            return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, getHandshakeStatus(), written, 0);
+        } else {
+            return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), written, bytesProduced);
+        }
+    }
+
+    private int pendingReadableBytesInSSL()
+            throws SSLException {
+        // NOTE: Calling a fake read is necessary before calling pendingReadableBytesInSSL because
+        // SSL_pending will return 0 if OpenSSL has not started the current TLS record
+        // See https://www.openssl.org/docs/manmaster/man3/SSL_pending.html
+        clearLastError();
+        int lastPrimingReadResult = SSL_read(state.ssl, MemorySegment.NULL, 0); // priming read
+        // check if SSL_read returned <= 0. In this case we need to check the error and see if it was something
+        // fatal.
+        if (lastPrimingReadResult <= 0) {
+            checkLastError();
+        }
+        int pendingReadableBytesInSSL = SSL_pending(state.ssl);
+
+        // TLS 1.0 needs additional handling
+        if (Constants.SSL_PROTO_TLSv1.equals(version) && lastPrimingReadResult == 0 &&
+                pendingReadableBytesInSSL == 0) {
+            // Perform another priming read
+            lastPrimingReadResult = SSL_read(state.ssl, MemorySegment.NULL, 0);
+            if (lastPrimingReadResult <= 0) {
+                checkLastError();
+            }
+            pendingReadableBytesInSSL = SSL_pending(state.ssl);
+        }
+
+        return pendingReadableBytesInSSL;
+    }
+
+    @Override
+    public Runnable getDelegatedTask() {
+        // Currently, we do not delegate SSL computation tasks
+        return null;
+    }
+
+    @Override
+    public synchronized void closeInbound() throws SSLException {
+        if (isInboundDone) {
+            return;
+        }
+
+        isInboundDone = true;
+        engineClosed = true;
+
+        shutdown();
+
+        if (accepted != Accepted.NOT && !receivedShutdown) {
+            throw new SSLException(sm.getString("engine.inboundClose"));
+        }
+    }
+
+    @Override
+    public synchronized boolean isInboundDone() {
+        return isInboundDone || engineClosed;
+    }
+
+    @Override
+    public synchronized void closeOutbound() {
+        if (isOutboundDone) {
+            return;
+        }
+
+        isOutboundDone = true;
+        engineClosed = true;
+
+        if (accepted != Accepted.NOT && !destroyed) {
+            int mode = SSL_get_shutdown(state.ssl);
+            if ((mode & SSL_SENT_SHUTDOWN()) != SSL_SENT_SHUTDOWN()) {
+                SSL_shutdown(state.ssl);
+            }
+        } else {
+            // engine closing before initial handshake
+            shutdown();
+        }
+    }
+
+    @Override
+    public synchronized boolean isOutboundDone() {
+        return isOutboundDone;
+    }
+
+    @Override
+    public String[] getSupportedCipherSuites() {
+        Set<String> availableCipherSuites = AVAILABLE_CIPHER_SUITES;
+        return availableCipherSuites.toArray(new String[0]);
+    }
+
+    @Override
+    public synchronized String[] getEnabledCipherSuites() {
+        if (destroyed) {
+            return new String[0];
+        }
+        String[] enabled = getCiphers(state.ssl);
+        if (enabled == null) {
+            return new String[0];
+        } else {
+            for (int i = 0; i < enabled.length; i++) {
+                String mapped = OpenSSLCipherConfigurationParser.openSSLToJsse(enabled[i]);
+                if (mapped != null) {
+                    enabled[i] = mapped;
+                }
+            }
+            return enabled;
+        }
+    }
+
+    @Override
+    public synchronized void setEnabledCipherSuites(String[] cipherSuites) {
+        if (initialized) {
+            return;
+        }
+        if (cipherSuites == null) {
+            throw new IllegalArgumentException(sm.getString("engine.nullCipherSuite"));
+        }
+        if (destroyed) {
+            return;
+        }
+        final StringBuilder buf = new StringBuilder();
+        for (String cipherSuite : cipherSuites) {
+            if (cipherSuite == null) {
+                break;
+            }
+            String converted = OpenSSLCipherConfigurationParser.jsseToOpenSSL(cipherSuite);
+            if (!AVAILABLE_CIPHER_SUITES.contains(cipherSuite)) {
+                log.debug(sm.getString("engine.unsupportedCipher", cipherSuite, converted));
+            }
+            if (converted != null) {
+                cipherSuite = converted;
+            }
+
+            buf.append(cipherSuite);
+            buf.append(':');
+        }
+
+        if (buf.length() == 0) {
+            throw new IllegalArgumentException(sm.getString("engine.emptyCipherSuite"));
+        }
+        buf.setLength(buf.length() - 1);
+
+        final String cipherSuiteSpec = buf.toString();
+        try (var localArena = Arena.ofConfined()) {
+            SSL_set_cipher_list(state.ssl, localArena.allocateUtf8String(cipherSuiteSpec));
+        } catch (Exception e) {
+            throw new IllegalStateException(sm.getString("engine.failedCipherSuite", cipherSuiteSpec), e);
+        }
+    }
+
+    @Override
+    public String[] getSupportedProtocols() {
+        return IMPLEMENTED_PROTOCOLS_SET.toArray(new String[0]);
+    }
+
+    @Override
+    public synchronized String[] getEnabledProtocols() {
+        if (destroyed) {
+            return new String[0];
+        }
+        List<String> enabled = new ArrayList<>();
+        // Seems like there is no way to explicitly disable SSLv2Hello in OpenSSL so it is always enabled
+        enabled.add(Constants.SSL_PROTO_SSLv2Hello);
+        long opts = SSL_get_options(state.ssl);
+        if ((opts & SSL_OP_NO_TLSv1()) == 0) {
+            enabled.add(Constants.SSL_PROTO_TLSv1);
+        }
+        if ((opts & SSL_OP_NO_TLSv1_1()) == 0) {
+            enabled.add(Constants.SSL_PROTO_TLSv1_1);
+        }
+        if ((opts & SSL_OP_NO_TLSv1_2()) == 0) {
+            enabled.add(Constants.SSL_PROTO_TLSv1_2);
+        }
+        if ((opts & SSL_OP_NO_TLSv1_3()) == 0) {
+            enabled.add(Constants.SSL_PROTO_TLSv1_3);
+        }
+        if ((opts & SSL_OP_NO_SSLv2()) == 0) {
+            enabled.add(Constants.SSL_PROTO_SSLv2);
+        }
+        if ((opts & SSL_OP_NO_SSLv3()) == 0) {
+            enabled.add(Constants.SSL_PROTO_SSLv3);
+        }
+        int size = enabled.size();
+        if (size == 0) {
+            return new String[0];
+        } else {
+            return enabled.toArray(new String[size]);
+        }
+    }
+
+    @Override
+    public synchronized void setEnabledProtocols(String[] protocols) {
+        if (initialized) {
+            return;
+        }
+        if (protocols == null) {
+            // This is correct from the API docs
+            throw new IllegalArgumentException();
+        }
+        if (destroyed) {
+            return;
+        }
+        boolean sslv2 = false;
+        boolean sslv3 = false;
+        boolean tlsv1 = false;
+        boolean tlsv1_1 = false;
+        boolean tlsv1_2 = false;
+        boolean tlsv1_3 = false;
+        for (String p : protocols) {
+            if (!IMPLEMENTED_PROTOCOLS_SET.contains(p)) {
+                throw new IllegalArgumentException(sm.getString("engine.unsupportedProtocol", p));
+            }
+            if (p.equals(Constants.SSL_PROTO_SSLv2)) {
+                sslv2 = true;
+            } else if (p.equals(Constants.SSL_PROTO_SSLv3)) {
+                sslv3 = true;
+            } else if (p.equals(Constants.SSL_PROTO_TLSv1)) {
+                tlsv1 = true;
+            } else if (p.equals(Constants.SSL_PROTO_TLSv1_1)) {
+                tlsv1_1 = true;
+            } else if (p.equals(Constants.SSL_PROTO_TLSv1_2)) {
+                tlsv1_2 = true;
+            } else if (p.equals(Constants.SSL_PROTO_TLSv1_3)) {
+                tlsv1_3 = true;
+            }
+        }
+        // Enable all and then disable what we not want
+        SSL_set_options(state.ssl, SSL_OP_ALL());
+
+        if (!sslv2) {
+            SSL_set_options(state.ssl, SSL_OP_NO_SSLv2());
+        }
+        if (!sslv3) {
+            SSL_set_options(state.ssl, SSL_OP_NO_SSLv3());
+        }
+        if (!tlsv1) {
+            SSL_set_options(state.ssl, SSL_OP_NO_TLSv1());
+        }
+        if (!tlsv1_1) {
+            SSL_set_options(state.ssl, SSL_OP_NO_TLSv1_1());
+        }
+        if (!tlsv1_2) {
+            SSL_set_options(state.ssl, SSL_OP_NO_TLSv1_2());
+        }
+        if (!tlsv1_3) {
+            SSL_set_options(state.ssl, SSL_OP_NO_TLSv1_3());
+        }
+    }
+
+    @Override
+    public SSLSession getSession() {
+        return session;
+    }
+
+    @Override
+    public synchronized void beginHandshake() throws SSLException {
+        if (engineClosed || destroyed) {
+            throw new SSLException(sm.getString("engine.engineClosed"));
+        }
+        switch (accepted) {
+        case NOT:
+            handshake();
+            accepted = Accepted.EXPLICIT;
+            break;
+        case IMPLICIT:
+            // A user did not start handshake by calling this method by themselves,
+            // but handshake has been started already by wrap() or unwrap() implicitly.
+            // Because it's the user's first time to call this method, it is unfair to
+            // raise an exception.  From the user's standpoint, they never asked for
+            // renegotiation.
+
+            accepted = Accepted.EXPLICIT; // Next time this method is invoked by the user, we should raise an exception.
+            break;
+        case EXPLICIT:
+            renegotiate();
+            break;
+        }
+    }
+
+    private byte[] getPeerCertificate() {
+        try (var localArena = Arena.ofConfined()) {
+            MemorySegment/*(X509*)*/ x509 = (OpenSSLContext.OPENSSL_3 ? SSL_get1_peer_certificate(state.ssl) : SSL_get_peer_certificate(state.ssl));
+            MemorySegment bufPointer = localArena.allocate(ValueLayout.ADDRESS, MemorySegment.NULL);
+            int length = i2d_X509(x509, bufPointer);
+            if (length <= 0) {
+                return null;
+            }
+            MemorySegment buf = bufPointer.get(ValueLayout.ADDRESS, 0);
+            byte[] certificate = buf.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+            X509_free(x509);
+            CRYPTO_free(buf, MemorySegment.NULL, 0); // OPENSSL_free macro
+            return certificate;
+        }
+    }
+
+    private byte[][] getPeerCertChain() {
+        MemorySegment/*STACK_OF(X509)*/ sk = SSL_get_peer_cert_chain(state.ssl);
+        int len = OPENSSL_sk_num(sk);
+        if (len <= 0) {
+            return null;
+        }
+        byte[][] certificateChain = new byte[len][];
+        try (var localArena = Arena.ofConfined()) {
+            for (int i = 0; i < len; i++) {
+                MemorySegment/*(X509*)*/ x509 = OPENSSL_sk_value(sk, i);
+                MemorySegment bufPointer = localArena.allocate(ValueLayout.ADDRESS, MemorySegment.NULL);
+                int length = i2d_X509(x509, bufPointer);
+                if (length < 0) {
+                    certificateChain[i] = new byte[0];
+                    continue;
+                }
+                MemorySegment buf = bufPointer.get(ValueLayout.ADDRESS, 0);
+                byte[] certificate = buf.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+                certificateChain[i] = certificate;
+                CRYPTO_free(buf, MemorySegment.NULL, 0); // OPENSSL_free macro
+            }
+            return certificateChain;
+        }
+    }
+
+    private String getProtocolNegotiated() {
+        try (var localArena = Arena.ofConfined()) {
+            MemorySegment lenAddress = localArena.allocate(ValueLayout.JAVA_INT, 0);
+            MemorySegment protocolPointer = localArena.allocate(ValueLayout.ADDRESS, MemorySegment.NULL);
+            SSL_get0_alpn_selected(state.ssl, protocolPointer, lenAddress);
+            if (MemorySegment.NULL.equals(protocolPointer)) {
+                return null;
+            }
+            int length = lenAddress.get(ValueLayout.JAVA_INT, 0);
+            if (length == 0) {
+                return null;
+            }
+            MemorySegment protocolAddress = protocolPointer.get(ValueLayout.ADDRESS, 0);
+            byte[] name = protocolAddress.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+            if (log.isDebugEnabled()) {
+                log.debug("Protocol negotiated [" + new String(name) + "]");
+            }
+            return new String(name);
+        }
+    }
+
+    private void beginHandshakeImplicitly() throws SSLException {
+        handshake();
+        accepted = Accepted.IMPLICIT;
+    }
+
+    private void handshake() throws SSLException {
+        currentHandshake = state.handshakeCount;
+        clearLastError();
+        int code = SSL_do_handshake(state.ssl);
+        if (code <= 0) {
+            checkLastError();
+        } else {
+            if (alpn) {
+                selectedProtocol = getProtocolNegotiated();
+            }
+            session.lastAccessedTime = System.currentTimeMillis();
+            // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update
+            // handshakeFinished directly and so eliminate unnecessary calls to SSL.isInInit(...)
+            handshakeFinished = true;
+        }
+    }
+
+    private synchronized void renegotiate() throws SSLException {
+        if (log.isDebugEnabled()) {
+            log.debug("Start renegotiate");
+        }
+        clearLastError();
+        int code;
+        if (SSL_get_version(state.ssl).getUtf8String(0).equals(Constants.SSL_PROTO_TLSv1_3)) {
+            state.phaState = PHAState.START;
+            code = SSL_verify_client_post_handshake(state.ssl);
+        } else {
+            code = SSL_renegotiate(state.ssl);
+        }
+        if (code <= 0) {
+            checkLastError();
+        }
+        handshakeFinished = false;
+        peerCerts = null;
+        currentHandshake = state.handshakeCount;
+        int code2 = SSL_do_handshake(state.ssl);
+        if (code2 <= 0) {
+            checkLastError();
+        }
+    }
+
+    private void checkLastError() throws SSLException {
+        String sslError = getLastError();
+        if (sslError != null) {
+            // Many errors can occur during handshake and need to be reported
+            if (!handshakeFinished) {
+                sendHandshakeError = true;
+            } else {
+                throw new SSLException(sslError);
+            }
+        }
+    }
+
+
+    /**
+     * Clear out any errors, but log a warning.
+     */
+    private void clearLastError() {
+        getLastError();
+    }
+
+    /**
+     * Many calls to SSL methods do not check the last error. Those that do
+     * check the last error need to ensure that any previously ignored error is
+     * cleared prior to the method call else errors may be falsely reported.
+     * Ideally, before any SSL_read, SSL_write, clearLastError should always
+     * be called, and getLastError should be called after on any negative or
+     * zero result.
+     * @return the first error in the stack
+     */
+    private String getLastError() {
+        String sslError = null;
+        long error = ERR_get_error();
+        if (error != SSL_ERROR_NONE()) {
+            try (var localArena = Arena.ofConfined()) {
+                do {
+                    // Loop until getLastErrorNumber() returns SSL_ERROR_NONE
+                    var buf = localArena.allocateArray(ValueLayout.JAVA_BYTE, new byte[128]);
+                    ERR_error_string(error, buf);
+                    String err = buf.getUtf8String(0);
+                    if (sslError == null) {
+                        sslError = err;
+                    }
+                    if (log.isDebugEnabled()) {
+                        log.debug(sm.getString("engine.openSSLError", Long.toString(error), err));
+                    }
+                } while ((error = ERR_get_error()) != SSL_ERROR_NONE());
+            }
+        }
+        return sslError;
+    }
+
+    private SSLEngineResult.Status getEngineStatus() {
+        return engineClosed ? SSLEngineResult.Status.CLOSED : SSLEngineResult.Status.OK;
+    }
+
+    @Override
+    public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
+        if (accepted == Accepted.NOT || destroyed) {
+            return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+        }
+
+        // Check if we are in the initial handshake phase
+        if (!handshakeFinished) {
+
+            // There is pending data in the network BIO -- call wrap
+            if (sendHandshakeError || BIO_ctrl_pending(state.networkBIO) != 0) {
+                if (sendHandshakeError) {
+                    // After a last wrap, consider it is going to be done
+                    sendHandshakeError = false;
+                    currentHandshake++;
+                }
+                return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+            }
+
+            /*
+             * Tomcat Native stores a count of the completed handshakes in the
+             * SSL instance and increments it every time a handshake is
+             * completed. Comparing the handshake count when the handshake
+             * started to the current handshake count enables this code to
+             * detect when the handshake has completed.
+             *
+             * Obtaining client certificates after the connection has been
+             * established requires additional checks. We need to trigger
+             * additional reads until the certificates have been read but we
+             * don't know how many reads we will need as it depends on both
+             * client and network behaviour.
+             *
+             * The additional reads are triggered by returning NEED_UNWRAP
+             * rather than FINISHED. This allows the standard I/O code to be
+             * used.
+             *
+             * For TLSv1.2 and below, the handshake completes before the
+             * renegotiation. We therefore use SSL.renegotiatePending() to
+             * check on the current status of the renegotiation and return
+             * NEED_UNWRAP until it completes which means the client
+             * certificates will have been read from the client.
+             *
+             * For TLSv1.3, Tomcat Native sets a flag when post handshake
+             * authentication is started and updates it once the client
+             * certificate has been received. We therefore use
+             * SSL.getPostHandshakeAuthInProgress() to check the current status
+             * and return NEED_UNWRAP until that methods indicates that PHA is
+             * no longer in progress.
+             */
+
+            // No pending data to be sent to the peer
+            // Check to see if we have finished handshaking
+            if (state.handshakeCount != currentHandshake && SSL_renegotiate_pending(state.ssl) == 0 &&
+                    (state.phaState != PHAState.START)) {
+                if (alpn) {
+                    selectedProtocol = getProtocolNegotiated();
+                }
+                session.lastAccessedTime = System.currentTimeMillis();
+                version = SSL_get_version(state.ssl).getUtf8String(0);
+                handshakeFinished = true;
+                return SSLEngineResult.HandshakeStatus.FINISHED;
+            }
+
+            // No pending data
+            // Still handshaking / renegotiation / post-handshake auth pending
+            // Must be waiting on the peer to send more data
+            return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+        }
+
+        // Check if we are in the shutdown phase
+        if (engineClosed) {
+            // Waiting to send the close_notify message
+            if (BIO_ctrl_pending(state.networkBIO) != 0) {
+                return SSLEngineResult.HandshakeStatus.NEED_WRAP;
+            }
+
+            // Must be waiting to receive the close_notify message
+            return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
+        }
+
+        return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+    }
+
+    @Override
+    public void setUseClientMode(boolean clientMode) {
+        if (clientMode != this.clientMode) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    public boolean getUseClientMode() {
+        return clientMode;
+    }
+
+    @Override
+    public void setNeedClientAuth(boolean b) {
+        setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE);
+    }
+
+    @Override
+    public boolean getNeedClientAuth() {
+        return clientAuth == ClientAuthMode.REQUIRE;
+    }
+
+    @Override
+    public void setWantClientAuth(boolean b) {
+        setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE);
+    }
+
+    @Override
+    public boolean getWantClientAuth() {
+        return clientAuth == ClientAuthMode.OPTIONAL;
+    }
+
+    private static final int OPTIONAL_NO_CA = 3;
+
+    private void setClientAuth(ClientAuthMode mode) {
+        if (clientMode) {
+            return;
+        }
+        synchronized (this) {
+            if (clientAuth == mode) {
+                return;
+            }
+            state.certificateVerifyMode = switch (mode) {
+                case NONE -> SSL_VERIFY_NONE();
+                case REQUIRE -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT();
+                case OPTIONAL -> certificateVerificationOptionalNoCA ? OPTIONAL_NO_CA : SSL_VERIFY_PEER();
+            };
+            // SSL.setVerify(state.ssl, value, certificateVerificationDepth);
+            // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback
+            var openSSLCallbackVerify =
+                    Linker.nativeLinker().upcallStub(openSSLCallbackVerifyHandle,
+                    openSSLCallbackVerifyFunctionDescriptor, engineArena);
+            int value = switch (mode) {
+                case NONE -> SSL_VERIFY_NONE();
+                case REQUIRE -> SSL_VERIFY_PEER() | SSL_VERIFY_FAIL_IF_NO_PEER_CERT();
+                case OPTIONAL -> SSL_VERIFY_PEER();
+            };
+            SSL_set_verify(state.ssl, value, openSSLCallbackVerify);
+            clientAuth = mode;
+        }
+    }
+
+    public static void openSSLCallbackInfo(MemorySegment ssl, int where, int ret) {
+        EngineState state = getState(ssl);
+        if (state == null) {
+            log.warn(sm.getString("engine.noSSL", Long.valueOf(ssl.address())));
+            return;
+        }
+        if (0 != (where & SSL_CB_HANDSHAKE_DONE())) {
+            state.handshakeCount++;
+        }
+    }
+
+    public static int openSSLCallbackVerify(int preverify_ok, MemorySegment /*X509_STORE_CTX*/ x509ctx) {
+        MemorySegment ssl = X509_STORE_CTX_get_ex_data(x509ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+        EngineState state = getState(ssl);
+        if (state == null) {
+            log.warn(sm.getString("engine.noSSL", Long.valueOf(ssl.address())));
+            return 0;
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("Verification in engine with mode [" + state.certificateVerifyMode + "] for " + state.ssl);
+        }
+        int ok = preverify_ok;
+        int errnum = X509_STORE_CTX_get_error(x509ctx);
+        int errdepth = X509_STORE_CTX_get_error_depth(x509ctx);
+        state.phaState = PHAState.COMPLETE;
+        if (state.certificateVerifyMode == -1 /*SSL_CVERIFY_UNSET*/ || state.certificateVerifyMode == SSL_VERIFY_NONE()) {
+            return 1;
+        }
+        /*SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) -> ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
+                || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
+                || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
+                || (errnum == X509_V_ERR_CERT_UNTRUSTED)
+                || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))*/
+        boolean verifyErrorIsOptional = (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT())
+                || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN())
+                || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY())
+                || (errnum == X509_V_ERR_CERT_UNTRUSTED())
+                || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE());
+        if (verifyErrorIsOptional && (state.certificateVerifyMode == OPTIONAL_NO_CA)) {
+            ok = 1;
+            SSL_set_verify_result(state.ssl, X509_V_OK());
+        }
+        /*
+         * Expired certificates vs. "expired" CRLs: by default, OpenSSL
+         * turns X509_V_ERR_CRL_HAS_EXPIRED into a "certificate_expired(45)"
+         * SSL alert, but that's not really the message we should convey to the
+         * peer (at the very least, it's confusing, and in many cases, it's also
+         * inaccurate, as the certificate itself may very well not have expired
+         * yet). We set the X509_STORE_CTX error to something which OpenSSL's
+         * s3_both.c:ssl_verify_alarm_type() maps to SSL_AD_CERTIFICATE_UNKNOWN,
+         * i.e. the peer will receive a "certificate_unknown(46)" alert.
+         * We do not touch errnum, though, so that later on we will still log
+         * the "real" error, as returned by OpenSSL.
+         */
+        if (ok == 0 && errnum == X509_V_ERR_CRL_HAS_EXPIRED()) {
+            X509_STORE_CTX_set_error(x509ctx, -1);
+        }
+
+        // OCSP
+        if (!state.noOcspCheck && (ok > 0)) {
+            /* If there was an optional verification error, it's not
+             * possible to perform OCSP validation since the issuer may be
+             * missing/untrusted.  Fail in that case.
+             */
+            if (verifyErrorIsOptional) {
+                if (state.certificateVerifyMode != OPTIONAL_NO_CA) {
+                    X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION());
+                    errnum = X509_V_ERR_APPLICATION_VERIFICATION();
+                    ok = 0;
+                }
+            } else {
+                int ocspResponse = processOCSP(x509ctx);
+                if (ocspResponse == V_OCSP_CERTSTATUS_REVOKED()) {
+                    ok = 0;
+                    errnum = X509_STORE_CTX_get_error(x509ctx);
+                } else if (ocspResponse == V_OCSP_CERTSTATUS_UNKNOWN()) {
+                    errnum = X509_STORE_CTX_get_error(x509ctx);
+                    if (errnum <= 0) {
+                        ok = 0;
+                    }
+                }
+            }
+        }
+
+        if (errdepth > state.certificateVerificationDepth) {
+            // Certificate Verification: Certificate Chain too long
+            ok = 0;
+        }
+        return ok;
+    }
+
+    static int processOCSP(MemorySegment /*X509_STORE_CTX*/ x509ctx) {
+        int ocspResponse = V_OCSP_CERTSTATUS_UNKNOWN();
+        // ocspResponse = ssl_verify_OCSP(x509_ctx);
+        MemorySegment x509 = X509_STORE_CTX_get_current_cert(x509ctx);
+        if (!MemorySegment.NULL.equals(x509)) {
+            // No need to check cert->valid, because ssl_verify_OCSP() only
+            // is called if OpenSSL already successfully verified the certificate
+            // (parameter "ok" in SSL_callback_SSL_verify() must be true).
+            if (X509_check_issued(x509, x509) == X509_V_OK()) {
+                // don't do OCSP checking for valid self-issued certs
+                X509_STORE_CTX_set_error(x509ctx, X509_V_OK());
+            } else {
+                // If we can't get the issuer, we cannot perform OCSP verification
+                MemorySegment issuer = X509_STORE_CTX_get0_current_issuer(x509ctx);
+                if (!MemorySegment.NULL.equals(issuer)) {
+                    // sslutils.c ssl_ocsp_request(x509, issuer, x509ctx);
+                    int nid = X509_get_ext_by_NID(x509, NID_info_access(), -1);
+                    if (nid >= 0) {
+                        try (var localArenal = Arena.ofConfined()) {
+                            MemorySegment ext = X509_get_ext(x509, nid);
+                            MemorySegment os = X509_EXTENSION_get_data(ext);
+                            int length = ASN1_STRING_length(os);
+                            MemorySegment data = ASN1_STRING_get0_data(os);
+                            // ocsp_urls = decode_OCSP_url(os);
+                            byte[] asn1String = data.reinterpret(length, localArenal, null).toArray(ValueLayout.JAVA_BYTE);
+                            Asn1Parser parser = new Asn1Parser(asn1String);
+                            // Parse the byte sequence
+                            ArrayList<String> urls = new ArrayList<>();
+                            try {
+                                parseOCSPURLs(parser, urls);
+                            } catch (Exception e) {
+                                log.error(sm.getString("engine.ocspParseError"), e);
+                            }
+                            if (!urls.isEmpty()) {
+                                // Use OpenSSL to build OCSP request
+                                for (String urlString : urls) {
+                                    try {
+                                        URL url = (new URI(urlString)).toURL();
+                                        ocspResponse = processOCSPRequest(url, issuer, x509, x509ctx, localArenal);
+                                        if (log.isDebugEnabled()) {
+                                            log.debug("OCSP response for URL: " + urlString + " was " + ocspResponse);
+                                        }
+                                    } catch (MalformedURLException | URISyntaxException e) {
+                                        log.warn(sm.getString("engine.invalidOCSPURL", urlString));
+                                    }
+                                    if (ocspResponse != V_OCSP_CERTSTATUS_UNKNOWN()) {
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return ocspResponse;
+    }
+
+    private static final int ASN1_SEQUENCE = 0x30;
+    private static final int ASN1_OID      = 0x06;
+    private static final int ASN1_STRING   = 0x86;
+    private static final byte[] OCSP_OID = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01};
+
+    private static void parseOCSPURLs(Asn1Parser parser, ArrayList<String> urls) {
+        while (!parser.eof()) {
+            int tag = parser.peekTag();
+            if (tag == ASN1_SEQUENCE) {
+                parser.parseTag(ASN1_SEQUENCE);
+                parser.parseFullLength();
+            } else if (tag == ASN1_OID) {
+                parser.parseTag(ASN1_OID);
+                int oidLen = parser.parseLength();
+                byte[] oid = new byte[oidLen];
+                parser.parseBytes(oid);
+                if (Arrays.compareUnsigned(oid, 0, OCSP_OID.length, OCSP_OID, 0, OCSP_OID.length) == 0) {
+                    parser.parseTag(ASN1_STRING);
+                    int urlLen = parser.parseLength();
+                    byte[] url = new byte[urlLen];
+                    parser.parseBytes(url);
+                    urls.add(new String(url));
+                }
+            } else {
+                return;
+            }
+        }
+    }
+
+    private static int processOCSPRequest(URL url, MemorySegment issuer, MemorySegment x509,
+            MemorySegment /*X509_STORE_CTX*/ x509ctx, Arena localArena) {
+        MemorySegment ocspRequest = MemorySegment.NULL;
+        MemorySegment ocspResponse = MemorySegment.NULL;
+        MemorySegment id = MemorySegment.NULL;
+        MemorySegment ocspOneReq = MemorySegment.NULL;
+        HttpURLConnection connection = null;
+        MemorySegment basicResponse = MemorySegment.NULL;
+        MemorySegment certId = MemorySegment.NULL;
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            ocspRequest = OCSP_REQUEST_new();
+            if (MemorySegment.NULL.equals(ocspRequest)) {
+                return V_OCSP_CERTSTATUS_UNKNOWN();
+            }
+            id = OCSP_cert_to_id(MemorySegment.NULL, x509, issuer);
+            if (MemorySegment.NULL.equals(id)) {
+                return V_OCSP_CERTSTATUS_UNKNOWN();
+            }
+            ocspOneReq = OCSP_request_add0_id(ocspRequest, id);
+            if (MemorySegment.NULL.equals(ocspOneReq)) {
+                return V_OCSP_CERTSTATUS_UNKNOWN();
+            }
+            MemorySegment bufPointer = localArena.allocate(ValueLayout.ADDRESS, MemorySegment.NULL);
+            int requestLength = i2d_OCSP_REQUEST(ocspRequest, bufPointer);
+            if (requestLength <= 0) {
+                return V_OCSP_CERTSTATUS_UNKNOWN();
+            }
+            MemorySegment buf = bufPointer.get(ValueLayout.ADDRESS, 0);
+            // HTTP request with the following header
+            // POST urlPath HTTP/1.1
+            // Host: urlHost:urlPort
+            // Content-Type: application/ocsp-request
+            // Content-Length: ocspRequestData.length
+            byte[] ocspRequestData = buf.reinterpret(requestLength, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+            connection = (HttpURLConnection) url.openConnection();
+            connection.setRequestMethod("POST");
+            connection.setDoInput(true);
+            connection.setDoOutput(true);
+            connection.setFixedLengthStreamingMode(requestLength);
+            connection.setRequestProperty("Content-Type", "application/ocsp-request");
+            connection.connect();
+            connection.getOutputStream().write(ocspRequestData);
+            int responseCode = connection.getResponseCode();
+            if (responseCode != HttpURLConnection.HTTP_OK) {
+                return V_OCSP_CERTSTATUS_UNKNOWN();
+            }
+            InputStream is = connection.getInputStream();
+            int read = 0;
+            byte[] responseBuf = new byte[1024];
+            while ((read = is.read(responseBuf)) > 0) {
+                baos.write(responseBuf, 0, read);
+            }
+            byte[] responseData = baos.toByteArray();
+            var nativeResponseData = localArena.allocateArray(ValueLayout.JAVA_BYTE, responseData);
+            var nativeResponseDataPointer = localArena.allocate(ValueLayout.ADDRESS, nativeResponseData);
+            ocspResponse = d2i_OCSP_RESPONSE(MemorySegment.NULL, nativeResponseDataPointer, responseData.length);
+            if (!MemorySegment.NULL.equals(ocspResponse)) {
+                if (OCSP_response_status(ocspResponse) == OCSP_RESPONSE_STATUS_SUCCESSFUL()) {
+                    basicResponse = OCSP_response_get1_basic(ocspResponse);
+                    certId = OCSP_cert_to_id(MemorySegment.NULL, x509, issuer);
+                    if (MemorySegment.NULL.equals(certId)) {
+                        return V_OCSP_CERTSTATUS_UNKNOWN();
+                    }
+                    // Find by serial number and get the matching response
+                    MemorySegment singleResponse = OCSP_resp_get0(basicResponse, OCSP_resp_find(basicResponse, certId, -1));
+                    return OCSP_single_get0_status(singleResponse, MemorySegment.NULL,
+                            MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
+                }
+            }
+        } catch (Exception e) {
+            log.warn(sm.getString("engine.ocspRequestError", url.toString()), e);
+        } finally {
+            if (MemorySegment.NULL.equals(ocspResponse)) {
+                // Failed to get a valid response
+                X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION());
+            }
+            OCSP_CERTID_free(certId);
+            OCSP_BASICRESP_free(basicResponse);
+            OCSP_RESPONSE_free(ocspResponse);
+            OCSP_REQUEST_free(ocspRequest);
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+        return V_OCSP_CERTSTATUS_UNKNOWN();
+    }
+
+    @Override
+    public void setEnableSessionCreation(boolean b) {
+        if (!b) {
+            String msg = sm.getString("engine.noRestrictSessionCreation");
+            throw new UnsupportedOperationException(msg);
+        }
+    }
+
+    @Override
+    public boolean getEnableSessionCreation() {
+        return true;
+    }
+
+
+    private class OpenSSLSession implements SSLSession {
+
+        // lazy init for memory reasons
+        private Map<String, Object> values;
+
+        // Last accessed time
+        private long lastAccessedTime = -1;
+
+        @Override
+        public byte[] getId() {
+            byte[] id = null;
+            synchronized (OpenSSLEngine.this) {
+                if (!destroyed) {
+                    try (var localArena = Arena.ofConfined()) {
+                        MemorySegment lenPointer = localArena.allocate(ValueLayout.ADDRESS);
+                        var session = SSL_get_session(state.ssl);
+                        if (MemorySegment.NULL.equals(session)) {
+                            return new byte[0];
+                        }
+                        MemorySegment sessionId = SSL_SESSION_get_id(session, lenPointer);
+                        int len = lenPointer.get(ValueLayout.JAVA_INT, 0);
+                        id = (len == 0) ? new byte[0]
+                                : sessionId.reinterpret(len, localArena, null).toArray(ValueLayout.JAVA_BYTE);
+                    }
+                }
+            }
+
+            return id;
+        }
+
+        @Override
+        public SSLSessionContext getSessionContext() {
+            return sessionContext;
+        }
+
+        @Override
+        public long getCreationTime() {
+            // We need to multiply by 1000 as OpenSSL uses seconds and we need milliseconds.
+            long creationTime = 0;
+            synchronized (OpenSSLEngine.this) {
+                if (!destroyed) {
+                    var session = SSL_get_session(state.ssl);
+                    if (!MemorySegment.NULL.equals(session)) {
+                        creationTime = SSL_SESSION_get_time(session);
+                    }
+                }
+            }
+            return creationTime * 1000L;
+        }
+
+        @Override
+        public long getLastAccessedTime() {
+            return (lastAccessedTime > 0) ? lastAccessedTime : getCreationTime();
+        }
+
+        @Override
+        public void invalidate() {
+            // NOOP
+        }
+
+        @Override
+        public boolean isValid() {
+            return false;
+        }
+
+        @Override
+        public void putValue(String name, Object value) {
+            if (name == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullName"));
+            }
+            if (value == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullValue"));
+            }
+            Map<String, Object> values = this.values;
+            if (values == null) {
+                // Use size of 2 to keep the memory overhead small
+                values = this.values = new HashMap<>(2);
+            }
+            Object old = values.put(name, value);
+            if (value instanceof SSLSessionBindingListener) {
+                ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
+            }
+            notifyUnbound(old, name);
+        }
+
+        @Override
+        public Object getValue(String name) {
+            if (name == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullName"));
+            }
+            if (values == null) {
+                return null;
+            }
+            return values.get(name);
+        }
+
+        @Override
+        public void removeValue(String name) {
+            if (name == null) {
+                throw new IllegalArgumentException(sm.getString("engine.nullName"));
+            }
+            Map<String, Object> values = this.values;
+            if (values == null) {
+                return;
+            }
+            Object old = values.remove(name);
+            notifyUnbound(old, name);
+        }
+
+        @Override
+        public String[] getValueNames() {
+            Map<String, Object> values = this.values;
+            if (values == null || values.isEmpty()) {
+                return new String[0];
+            }
+            return values.keySet().toArray(new String[0]);
+        }
+
+        private void notifyUnbound(Object value, String name) {
+            if (value instanceof SSLSessionBindingListener) {
+                ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
+            }
+        }
+
+        @Override
+        public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
+            // these are lazy created to reduce memory overhead
+            Certificate[] c = peerCerts;
+            if (c == null) {
+                byte[] clientCert;
+                byte[][] chain;
+                synchronized (OpenSSLEngine.this) {
+                    if (destroyed || SSL_in_init(state.ssl) != 0) {
+                        throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer"));
+                    }
+                    chain = getPeerCertChain();
+                    if (!clientMode) {
+                        // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
+                        // We use SSL_get_peer_certificate to get it in this case and add it to our array later.
+                        //
+                        // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
+                        clientCert = getPeerCertificate();
+                    } else {
+                        clientCert = null;
+                    }
+                }
+                if (chain == null && clientCert == null) {
+                    return null;
+                }
+                int len = 0;
+                if (chain != null) {
+                    len += chain.length;
+                }
+
+                int i = 0;
+                Certificate[] certificates;
+                if (clientCert != null) {
+                    len++;
+                    certificates = new Certificate[len];
+                    certificates[i++] = new OpenSSLX509Certificate(clientCert);
+                } else {
+                    certificates = new Certificate[len];
+                }
+                if (chain != null) {
+                    int a = 0;
+                    for (; i < certificates.length; i++) {
+                        certificates[i] = new OpenSSLX509Certificate(chain[a++]);
+                    }
+                }
+                c = peerCerts = certificates;
+            }
+            return c;
+        }
+
+        @Override
+        public Certificate[] getLocalCertificates() {
+            // FIXME (if possible): Not available in the OpenSSL API
+            return EMPTY_CERTIFICATES;
+        }
+
+        @Override
+        public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+            Certificate[] peer = getPeerCertificates();
+            if (peer == null || peer.length == 0) {
+                return null;
+            }
+            return principal(peer);
+        }
+
+        @Override
+        public Principal getLocalPrincipal() {
+            Certificate[] local = getLocalCertificates();
+            if (local == null || local.length == 0) {
+                return null;
+            }
+            return principal(local);
+        }
+
+        private Principal principal(Certificate[] certs) {
+            return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
+        }
+
+        @Override
+        public String getCipherSuite() {
+            if (cipher == null) {
+                String ciphers;
+                synchronized (OpenSSLEngine.this) {
+                    if (!handshakeFinished) {
+                        return INVALID_CIPHER;
+                    }
+                    if (destroyed) {
+                        return INVALID_CIPHER;
+                    }
+                    ciphers = SSL_CIPHER_get_name(SSL_get_current_cipher(state.ssl)).getUtf8String(0);
+                }
+                String c = OpenSSLCipherConfigurationParser.openSSLToJsse(ciphers);
+                if (c != null) {
+                    cipher = c;
+                }
+            }
+            return cipher;
+        }
+
+        @Override
+        public String getProtocol() {
+            String applicationProtocol = OpenSSLEngine.this.applicationProtocol;
+            if (applicationProtocol == null) {
+                applicationProtocol = fallbackApplicationProtocol;
+                if (applicationProtocol != null) {
+                    OpenSSLEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
+                } else {
+                    OpenSSLEngine.this.applicationProtocol = applicationProtocol = "";
+                }
+            }
+            String version = null;
+            synchronized (OpenSSLEngine.this) {
+                if (!destroyed) {
+                    version = SSL_get_version(state.ssl).getUtf8String(0);
+                }
+            }
+            if (applicationProtocol.isEmpty()) {
+                return version;
+            } else {
+                return version + ':' + applicationProtocol;
+            }
+        }
+
+        @Override
+        public String getPeerHost() {
+            // Not available for now in Tomcat (needs to be passed during engine creation)
+            return null;
+        }
+
+        @Override
+        public int getPeerPort() {
+            // Not available for now in Tomcat (needs to be passed during engine creation)
+            return 0;
+        }
+
+        @Override
+        public int getPacketBufferSize() {
+            return MAX_ENCRYPTED_PACKET_LENGTH;
+        }
+
+        @Override
+        public int getApplicationBufferSize() {
+            return MAX_PLAINTEXT_LENGTH;
+        }
+
+    }
+
+    private static class EngineState implements Runnable {
+
+        private final Arena stateArena = Arena.ofShared();
+        private final MemorySegment ssl;
+        private final MemorySegment networkBIO;
+        private final int certificateVerificationDepth;
+        private final boolean noOcspCheck;
+
+        private PHAState phaState = PHAState.NONE;
+        private int certificateVerifyMode = 0;
+        private int handshakeCount = 0;
+
+        private EngineState(MemorySegment ssl, MemorySegment networkBIO,
+                int certificateVerificationDepth, boolean noOcspCheck) {
+            states.put(Long.valueOf(ssl.address()), this);
+            this.certificateVerificationDepth = certificateVerificationDepth;
+            this.noOcspCheck = noOcspCheck;
+            // Use another arena to avoid keeping a reference through segments
+            // This also allows making further accesses to the main pointers safer
+            this.ssl = ssl.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, null);
+            this.networkBIO = networkBIO.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, null);
+        }
+
+        @Override
+        public void run() {
+            try {
+                states.remove(Long.valueOf(ssl.address()));
+                BIO_free(networkBIO);
+                SSL_free(ssl);
+            } finally {
+                stateArena.close();
+            }
+        }
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java
new file mode 100644
index 0000000000..28d7ae2b8f
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLImplementation;
+import org.apache.tomcat.util.net.SSLSupport;
+import org.apache.tomcat.util.net.SSLUtil;
+import org.apache.tomcat.util.net.jsse.JSSESupport;
+
+public class OpenSSLImplementation extends SSLImplementation {
+
+    @Deprecated
+    public SSLSupport getSSLSupport(SSLSession session) {
+        return new JSSESupport(session, null);
+    }
+
+    @Override
+    public SSLSupport getSSLSupport(SSLSession session, Map<String, List<String>> additionalAttributes) {
+        return new JSSESupport(session, additionalAttributes);
+    }
+
+    @Override
+    public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
+        return new OpenSSLUtil(certificate);
+    }
+
+    public boolean isAlpnSupported() {
+        // OpenSSL supported ALPN
+        return true;
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLifecycleListener.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLifecycleListener.java
new file mode 100644
index 0000000000..3c2dbdda94
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLifecycleListener.java
@@ -0,0 +1,463 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.ValueLayout;
+import java.security.SecureRandom;
+
+import static org.apache.tomcat.util.openssl.openssl_compat_h.FIPS_mode;
+import static org.apache.tomcat.util.openssl.openssl_compat_h.FIPS_mode_set;
+import static org.apache.tomcat.util.openssl.openssl_h.*;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Server;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.res.StringManager;
+
+
+
+/**
+ * Implementation of <code>LifecycleListener</code> that will do the global
+ * initialization of OpenSSL according to specified configuration parameters.
+ * Using the listener is completely optional, but is needed for configuration
+ * and full cleanup of a few native memory allocations.
+ */
+public class OpenSSLLifecycleListener implements LifecycleListener {
+
+    private static final Log log = LogFactory.getLog(OpenSSLLifecycleListener.class);
+
+    /**
+     * The string manager for this package.
+     */
+    protected static final StringManager sm = StringManager.getManager(OpenSSLLifecycleListener.class);
+
+
+    // ---------------------------------------------- Properties
+    protected static String SSLEngine = "on"; //default on
+    protected static String FIPSMode = "off"; // default off, valid only when SSLEngine="on"
+    protected static String SSLRandomSeed = "builtin";
+    protected static boolean fipsModeActive = false;
+
+    /**
+     * The "FIPS mode" level that we use as the argument to OpenSSL method
+     * <code>FIPS_mode_set()</code> to enable FIPS mode and that we expect as
+     * the return value of <code>FIPS_mode()</code> when FIPS mode is enabled.
+     * <p>
+     * In the future the OpenSSL library might grow support for different
+     * non-zero "FIPS" modes that specify different allowed subsets of ciphers
+     * or whatever, but nowadays only "1" is the supported value.
+     * </p>
+     * @see <a href="http://wiki.openssl.org/index.php/FIPS_mode_set%28%29">OpenSSL method FIPS_mode_set()</a>
+     * @see <a href="http://wiki.openssl.org/index.php/FIPS_mode%28%29">OpenSSL method FIPS_mode()</a>
+     */
+    private static final int FIPS_ON = 1;
+
+    private static final int FIPS_OFF = 0;
+
+    protected static final Object lock = new Object();
+
+    public static boolean isAvailable() {
+        if (OpenSSLStatus.isInstanceCreated()) {
+            synchronized (lock) {
+                init();
+            }
+        }
+        return OpenSSLStatus.isAvailable();
+    }
+
+    public OpenSSLLifecycleListener() {
+        OpenSSLStatus.setInstanceCreated(true);
+    }
+
+    // ---------------------------------------------- LifecycleListener Methods
+
+    /**
+     * Primary entry point for startup and shutdown events.
+     *
+     * @param event The event that has occurred
+     */
+    @Override
+    public void lifecycleEvent(LifecycleEvent event) {
+
+        boolean initError = false;
+        if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {
+            if (!(event.getLifecycle() instanceof Server)) {
+                log.warn(sm.getString("listener.notServer",
+                        event.getLifecycle().getClass().getSimpleName()));
+            }
+            try {
+                init();
+            } catch (Throwable t) {
+                t = ExceptionUtils.unwrapInvocationTargetException(t);
+                ExceptionUtils.handleThrowable(t);
+                log.error(sm.getString("listener.sslInit"), t);
+                initError = true;
+            }
+            // Failure to initialize FIPS mode is fatal
+            if (!(null == FIPSMode || "off".equalsIgnoreCase(FIPSMode)) && !isFIPSModeActive()) {
+                String errorMessage = sm.getString("listener.initializeFIPSFailed");
+                Error e = new Error(errorMessage);
+                // Log here, because thrown error might be not logged
+                log.fatal(errorMessage, e);
+                initError = true;
+            }
+        }
+        if (initError || Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) {
+            // Note: Without the listener, destroy will never be called (which is not a significant problem)
+            try {
+                destroy();
+            } catch (Throwable t) {
+                t = ExceptionUtils.unwrapInvocationTargetException(t);
+                ExceptionUtils.handleThrowable(t);
+                log.info(sm.getString("listener.destroy"));
+            }
+        }
+
+    }
+
+    static MemorySegment enginePointer = MemorySegment.NULL;
+
+    static void initLibrary() {
+        synchronized (lock) {
+            if (OpenSSLStatus.isLibraryInitialized()) {
+                return;
+            }
+            OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN(), MemorySegment.NULL);
+            OpenSSLStatus.setLibraryInitialized(true);
+        }
+    }
+
+    /*
+    { BN_get_rfc3526_prime_8192, NULL, 6145 },
+    { BN_get_rfc3526_prime_6144, NULL, 4097 },
+    { BN_get_rfc3526_prime_4096, NULL, 3073 },
+    { BN_get_rfc3526_prime_3072, NULL, 2049 },
+    { BN_get_rfc3526_prime_2048, NULL, 1025 },
+    { BN_get_rfc2409_prime_1024, NULL, 0 }
+     */
+    static final class DHParam {
+        final MemorySegment dh;
+        final int min;
+        private DHParam(MemorySegment dh, int min) {
+            this.dh = dh;
+            this.min = min;
+        }
+    }
+    static final DHParam[] dhParameters = new DHParam[6];
+
+    private static void initDHParameters() {
+        var dh = DH_new();
+        var p = BN_get_rfc3526_prime_8192(MemorySegment.NULL);
+        var g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[0] = new DHParam(dh, 6145);
+        dh = DH_new();
+        p = BN_get_rfc3526_prime_6144(MemorySegment.NULL);
+        g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[1] = new DHParam(dh, 4097);
+        dh = DH_new();
+        p = BN_get_rfc3526_prime_4096(MemorySegment.NULL);
+        g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[2] = new DHParam(dh, 3073);
+        dh = DH_new();
+        p = BN_get_rfc3526_prime_3072(MemorySegment.NULL);
+        g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[3] = new DHParam(dh, 2049);
+        dh = DH_new();
+        p = BN_get_rfc3526_prime_2048(MemorySegment.NULL);
+        g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[4] = new DHParam(dh, 1025);
+        dh = DH_new();
+        p = BN_get_rfc2409_prime_1024(MemorySegment.NULL);
+        g = BN_new();
+        BN_set_word(g, 2);
+        DH_set0_pqg(dh, p, MemorySegment.NULL, g);
+        dhParameters[5] = new DHParam(dh, 0);
+    }
+
+    private static void freeDHParameters() {
+        for (int i = 0; i < dhParameters.length; i++) {
+            if (dhParameters[i] != null) {
+                MemorySegment dh = dhParameters[i].dh;
+                if (dh != null && !MemorySegment.NULL.equals(dh)) {
+                    DH_free(dh);
+                    dhParameters[i] = null;
+                }
+            }
+        }
+    }
+
+    static void init() {
+        synchronized (lock) {
+
+            if (OpenSSLStatus.isInitialized()) {
+                return;
+            }
+            OpenSSLStatus.setInitialized(true);
+
+            if ("off".equalsIgnoreCase(SSLEngine)) {
+                return;
+            }
+
+            try (var memorySession = Arena.ofConfined()) {
+
+                // Main library init
+                initLibrary();
+
+                // Setup engine
+                String engineName = "on".equalsIgnoreCase(SSLEngine) ? null : SSLEngine;
+                if (engineName != null) {
+                    if ("auto".equals(engineName)) {
+                        ENGINE_register_all_complete();
+                    } else {
+                        var engine = memorySession.allocateUtf8String(engineName);
+                        enginePointer = ENGINE_by_id(engine);
+                        if (MemorySegment.NULL.equals(enginePointer)) {
+                            enginePointer = ENGINE_by_id(memorySession.allocateUtf8String("dynamic"));
+                            if (enginePointer != null) {
+                                if (ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateUtf8String("SO_PATH"), engine, 0) == 0
+                                        || ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateUtf8String("LOAD"),
+                                                MemorySegment.NULL, 0) == 0) {
+                                    // Engine load error
+                                    ENGINE_free(enginePointer);
+                                    enginePointer = MemorySegment.NULL;
+                                }
+                            }
+                        }
+                        if (!MemorySegment.NULL.equals(enginePointer)) {
+                            if (ENGINE_set_default(enginePointer, ENGINE_METHOD_ALL()) == 0) {
+                                // Engine load error
+                                ENGINE_free(enginePointer);
+                                enginePointer = MemorySegment.NULL;
+                            }
+                        }
+                        if (MemorySegment.NULL.equals(enginePointer)) {
+                            throw new IllegalStateException(sm.getString("listener.engineError"));
+                        }
+                    }
+                }
+
+                // Set the random seed, translated to the Java way
+                boolean seedDone = false;
+                if (SSLRandomSeed != null || SSLRandomSeed.length() != 0 || !"builtin".equals(SSLRandomSeed)) {
+                    var randomSeed = memorySession.allocateUtf8String(SSLRandomSeed);
+                    seedDone = RAND_load_file(randomSeed, 128) > 0;
+                }
+                if (!seedDone) {
+                    // Use a regular random to get some bytes
+                    SecureRandom random = new SecureRandom();
+                    byte[] randomBytes = random.generateSeed(128);
+                    RAND_seed(memorySession.allocateArray(ValueLayout.JAVA_BYTE, randomBytes), 128);
+                }
+
+                initDHParameters();
+
+                // OpenSSL 3 onwards uses providers
+                boolean usingProviders = (OpenSSL_version_num() >= 0x3000000fL);
+
+                if (usingProviders || !(null == FIPSMode || "off".equalsIgnoreCase(FIPSMode))) {
+                    fipsModeActive = false;
+                    final boolean enterFipsMode;
+                    int fipsModeState = FIPS_OFF;
+                    if (usingProviders) {
+                        var md = EVP_MD_fetch(MemorySegment.NULL, memorySession.allocateUtf8String("SHA-512"), MemorySegment.NULL);
+                        var provider = EVP_MD_get0_provider(md);
+                        String name = OSSL_PROVIDER_get0_name(provider).getUtf8String(0);
+                        EVP_MD_free(md);
+                        if ("fips".equals(name)) {
+                            fipsModeState = FIPS_ON;
+                        }
+                    } else {
+                        fipsModeState = FIPS_mode();
+                    }
+
+                    if(log.isDebugEnabled()) {
+                        log.debug(sm.getString("listener.currentFIPSMode", Integer.valueOf(fipsModeState)));
+                    }
+
+                    if (null == FIPSMode || "off".equalsIgnoreCase(FIPSMode)) {
+                        if (fipsModeState == FIPS_ON) {
+                            fipsModeActive = true;
+                        }
+                        enterFipsMode = false;
+                    } else if ("on".equalsIgnoreCase(FIPSMode)) {
+                        if (fipsModeState == FIPS_ON) {
+                            if (!usingProviders) {
+                                log.info(sm.getString("listener.skipFIPSInitialization"));
+                            }
+                            fipsModeActive = true;
+                            enterFipsMode = false;
+                        } else {
+                            if (usingProviders) {
+                                throw new IllegalStateException(sm.getString("listener.FIPSProviderNotDefault", FIPSMode));
+                            } else {
+                                enterFipsMode = true;
+                            }
+                        }
+                    } else if ("require".equalsIgnoreCase(FIPSMode)) {
+                        if (fipsModeState == FIPS_ON) {
+                            fipsModeActive = true;
+                            enterFipsMode = false;
+                        } else {
+                            if (usingProviders) {
+                                throw new IllegalStateException(sm.getString("listener.FIPSProviderNotDefault", FIPSMode));
+                            } else {
+                                throw new IllegalStateException(sm.getString("listener.requireNotInFIPSMode"));
+                            }
+                        }
+                    } else if ("enter".equalsIgnoreCase(FIPSMode)) {
+                        if (fipsModeState == FIPS_OFF) {
+                            if (usingProviders) {
+                                throw new IllegalStateException(sm.getString("listener.FIPSProviderNotDefault", FIPSMode));
+                            } else {
+                                enterFipsMode = true;
+                            }
+                        } else {
+                            if (usingProviders) {
+                                fipsModeActive = true;
+                                enterFipsMode = false;
+                            } else {
+                                throw new IllegalStateException(sm.getString(
+                                        "listener.enterAlreadyInFIPSMode", Integer.valueOf(fipsModeState)));
+                            }
+                        }
+                    } else {
+                        throw new IllegalArgumentException(sm.getString(
+                                "listener.wrongFIPSMode", FIPSMode));
+                    }
+
+                    if (enterFipsMode) {
+                        log.info(sm.getString("listener.initializingFIPS"));
+
+                        fipsModeState = FIPS_mode_set(FIPS_ON);
+                        if (fipsModeState != FIPS_ON) {
+                            // This case should be handled by the native method,
+                            // but we'll make absolutely sure, here.
+                            String message = sm.getString("listener.initializeFIPSFailed");
+                            log.error(message);
+                            throw new IllegalStateException(message);
+                        }
+
+                        fipsModeActive = true;
+                        log.info(sm.getString("listener.initializeFIPSSuccess"));
+                    }
+
+                    if (usingProviders && fipsModeActive) {
+                        log.info(sm.getString("aprListener.usingFIPSProvider"));
+                    }
+                }
+
+                log.info(sm.getString("listener.initializedOpenSSL", OpenSSL_version(0).getUtf8String(0)));
+                OpenSSLStatus.setAvailable(true);
+            }
+        }
+    }
+
+    static void destroy() {
+        synchronized (lock) {
+            if (!OpenSSLStatus.isInitialized()) {
+                return;
+            }
+            OpenSSLStatus.setAvailable(false);
+
+            try {
+                freeDHParameters();
+                if (!MemorySegment.NULL.equals(enginePointer)) {
+                    ENGINE_free(enginePointer);
+                }
+                if (OpenSSL_version_num() < 0x3000000fL) {
+                    FIPS_mode_set(0);
+                }
+            } finally {
+                OpenSSLStatus.setInitialized(false);
+                fipsModeActive = false;
+            }
+        }
+    }
+
+    public String getSSLEngine() {
+        return SSLEngine;
+    }
+
+    public void setSSLEngine(String SSLEngine) {
+        if (!SSLEngine.equals(OpenSSLLifecycleListener.SSLEngine)) {
+            // Ensure that the SSLEngine is consistent with that used for SSL init
+            if (OpenSSLStatus.isInitialized()) {
+                throw new IllegalStateException(
+                        sm.getString("listener.tooLateForSSLEngine"));
+            }
+
+            OpenSSLLifecycleListener.SSLEngine = SSLEngine;
+        }
+    }
+
+    public String getSSLRandomSeed() {
+        return SSLRandomSeed;
+    }
+
+    public void setSSLRandomSeed(String SSLRandomSeed) {
+        if (!SSLRandomSeed.equals(OpenSSLLifecycleListener.SSLRandomSeed)) {
+            // Ensure that the random seed is consistent with that used for SSL init
+            if (OpenSSLStatus.isInitialized()) {
+                throw new IllegalStateException(
+                        sm.getString("listener.tooLateForSSLRandomSeed"));
+            }
+
+            OpenSSLLifecycleListener.SSLRandomSeed = SSLRandomSeed;
+        }
+    }
+
+    public String getFIPSMode() {
+        return FIPSMode;
+    }
+
+    public void setFIPSMode(String FIPSMode) {
+        if (!FIPSMode.equals(OpenSSLLifecycleListener.FIPSMode)) {
+            // Ensure that the FIPS mode is consistent with that used for SSL init
+            if (OpenSSLStatus.isInitialized()) {
+                throw new IllegalStateException(
+                        sm.getString("listener.tooLateForFIPSMode"));
+            }
+
+            OpenSSLLifecycleListener.FIPSMode = FIPSMode;
+        }
+    }
+
+    public boolean isFIPSModeActive() {
+        return fipsModeActive;
+    }
+
+    public static boolean isInstanceCreated() {
+        return OpenSSLStatus.isInstanceCreated();
+    }
+
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java
new file mode 100644
index 0000000000..713bb88673
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.ValueLayout;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSessionContext;
+
+import static org.apache.tomcat.util.openssl.openssl_h.*;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * OpenSSL specific {@link SSLSessionContext} implementation.
+ */
+public class OpenSSLSessionContext implements SSLSessionContext {
+    private static final StringManager sm = StringManager.getManager(OpenSSLSessionContext.class);
+    private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
+
+    private static final int TICKET_KEYS_SIZE = 48;
+
+    private final OpenSSLSessionStats stats;
+    private final OpenSSLContext context;
+
+    OpenSSLSessionContext(OpenSSLContext context) {
+        this.context = context;
+        stats = new OpenSSLSessionStats(context.getSSLContext());
+    }
+
+    @Override
+    public SSLSession getSession(byte[] bytes) {
+        return null;
+    }
+
+    @Override
+    public Enumeration<byte[]> getIds() {
+        return EMPTY;
+    }
+
+    /**
+     * Sets the SSL session ticket keys of this context.
+     *
+     * @param keys The session ticket keys
+     */
+    public void setTicketKeys(byte[] keys) {
+        if (keys == null) {
+            throw new IllegalArgumentException(sm.getString("sessionContext.nullTicketKeys"));
+        }
+        if (keys.length != TICKET_KEYS_SIZE) {
+            throw new IllegalArgumentException(sm.getString("sessionContext.invalidTicketKeysLength", keys.length));
+        }
+        try (var memorySession = Arena.ofConfined()) {
+            var array = memorySession.allocateArray(ValueLayout.JAVA_BYTE, keys);
+            // #define SSL_CTX_set_tlsext_ticket_keys(ctx, keys, keylen)
+            //     SSL_CTX_ctrl((ctx),SSL_CTRL_SET_TLSEXT_TICKET_KEYS, (keylen), (keys))
+            SSL_CTX_ctrl(context.getSSLContext(), SSL_CTRL_SET_TLSEXT_TICKET_KEYS(), TICKET_KEYS_SIZE, array);
+        }
+    }
+
+    /**
+     * Enable or disable caching of SSL sessions.
+     *
+     * @param enabled {@code true} to enable caching, {@code false} to disable
+     */
+    public void setSessionCacheEnabled(boolean enabled) {
+        long mode = enabled ? SSL_SESS_CACHE_SERVER() : SSL_SESS_CACHE_OFF();
+        // # define SSL_CTX_set_session_cache_mode(ctx,m) \
+        //     SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL)
+        SSL_CTX_ctrl(context.getSSLContext(), SSL_CTRL_SET_SESS_CACHE_MODE(), mode, null);
+    }
+
+    /**
+     * @return {@code true} if caching of SSL sessions is enabled, {@code false}
+     *         otherwise.
+     */
+    public boolean isSessionCacheEnabled() {
+        // # define SSL_CTX_get_session_cache_mode(ctx) \
+        //    SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_MODE,0,NULL)
+        return SSL_CTX_ctrl(context.getSSLContext(), SSL_CTRL_GET_SESS_CACHE_MODE(), 0, null) == SSL_SESS_CACHE_SERVER();
+    }
+
+    /**
+     * @return The statistics for this context.
+     */
+    public OpenSSLSessionStats stats() {
+        return stats;
+    }
+
+    @Override
+    public void setSessionTimeout(int seconds) {
+        if (seconds < 0) {
+            throw new IllegalArgumentException();
+        }
+        SSL_CTX_set_timeout(context.getSSLContext(), seconds);
+    }
+
+    @Override
+    public int getSessionTimeout() {
+        return (int) SSL_CTX_get_timeout(context.getSSLContext());
+    }
+
+    @Override
+    public void setSessionCacheSize(int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException();
+        }
+        // # define SSL_CTX_sess_set_cache_size(ctx,t) \
+        //     SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_SIZE,t,NULL)
+        SSL_CTX_ctrl(context.getSSLContext(), SSL_CTRL_SET_SESS_CACHE_SIZE(), size, null);
+    }
+
+    @Override
+    public int getSessionCacheSize() {
+        // # define SSL_CTX_sess_get_cache_size(ctx) \
+        //     SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_SIZE,0,NULL)
+        return (int) SSL_CTX_ctrl(context.getSSLContext(), SSL_CTRL_GET_SESS_CACHE_SIZE(), 0, null);
+    }
+
+    /**
+     * Set the context within which session be reused (server side only)
+     * See <a href="http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html">
+     *     man SSL_CTX_set_session_id_context</a>
+     *
+     * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name
+     *               of the application and/or the hostname and/or service name
+     * @return {@code true} if success, {@code false} otherwise.
+     */
+    public boolean setSessionIdContext(byte[] sidCtx) {
+        try (var memorySession = Arena.ofConfined()) {
+            var array = memorySession.allocateArray(ValueLayout.JAVA_BYTE, sidCtx);
+            return (SSL_CTX_set_session_id_context(context.getSSLContext(), array, sidCtx.length) == 1);
+        }
+    }
+
+    private static final class EmptyEnumeration implements Enumeration<byte[]> {
+        @Override
+        public boolean hasMoreElements() {
+            return false;
+        }
+
+        @Override
+        public byte[] nextElement() {
+            throw new NoSuchElementException();
+        }
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java
new file mode 100644
index 0000000000..a63e524381
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.lang.foreign.MemorySegment;
+
+import static org.apache.tomcat.util.openssl.openssl_h.*;
+
+/**
+ * Stats exposed by an OpenSSL session context.
+ *
+ * @see <a href="https://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html"><code>SSL_CTX_sess_number</code></a>
+ */
+public final class OpenSSLSessionStats {
+
+    private final MemorySegment ctx;
+
+    OpenSSLSessionStats(MemorySegment ctx) {
+        this.ctx = ctx;
+    }
+
+    /**
+     * @return The current number of sessions in the internal session cache.
+     */
+    public long number() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_NUMBER(), 0, null);
+    }
+
+    /**
+     * @return The number of started SSL/TLS handshakes in client mode.
+     */
+    public long connect() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT() ,0, null);
+    }
+
+    /**
+     * @return The number of successfully established SSL/TLS sessions in client mode.
+     */
+    public long connectGood() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_GOOD() , 0, null);
+    }
+
+    /**
+     * @return The number of start renegotiations in client mode.
+     */
+    public long connectRenegotiate() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_RENEGOTIATE() , 0, null);
+    }
+
+    /**
+     * @return The number of started SSL/TLS handshakes in server mode.
+     */
+    public long accept() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_ACCEPT(), 0, null);
+    }
+
+    /**
+     * @return The number of successfully established SSL/TLS sessions in server mode.
+     */
+    public long acceptGood() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_ACCEPT_GOOD(), 0, null);
+    }
+
+    /**
+     * @return The number of start renegotiations in server mode.
+     */
+    public long acceptRenegotiate() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_ACCEPT_RENEGOTIATE(), 0, null);
+    }
+
+    /**
+     * @return The number of successfully reused sessions. In client mode, a
+     *         session set with {@code SSL_set_session} successfully reused is
+     *         counted as a hit. In server mode, a session successfully
+     *         retrieved from internal or external cache is counted as a hit.
+     */
+    public long hits() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_HIT(), 0, null);
+    }
+
+    /**
+     * @return The number of successfully retrieved sessions from the external
+     *         session cache in server mode.
+     */
+    public long cbHits() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CB_HIT(), 0, null);
+    }
+
+    /**
+     * @return The number of sessions proposed by clients that were not found in
+     *         the internal session cache in server mode.
+     */
+    public long misses() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_MISSES(), 0, null);
+    }
+
+    /**
+     * @return The number of sessions proposed by clients and either found in
+     *         the internal or external session cache in server mode, but that
+     *         were invalid due to timeout. These sessions are not included in
+     *         the {@link #hits()} count.
+     */
+    public long timeouts() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_TIMEOUTS(), 0, null);
+    }
+
+    /**
+     * @return The number of sessions that were removed because the maximum
+     *         session cache size was exceeded.
+     */
+    public long cacheFull() {
+        return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CACHE_FULL(), 0, null);
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLStatus.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLStatus.java
new file mode 100644
index 0000000000..a45f442708
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLStatus.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+/**
+ * Holds OpenSSL status without the need to load other classes.
+ */
+public class OpenSSLStatus {
+    private static volatile boolean libraryInitialized = false;
+    private static volatile boolean initialized = false;
+    private static volatile boolean available = false;
+    private static volatile boolean instanceCreated = false;
+
+
+    public static boolean isLibraryInitialized() {
+        return libraryInitialized;
+    }
+
+    public static boolean isInitialized() {
+        return initialized;
+    }
+
+    public static boolean isAvailable() {
+        return available;
+    }
+
+    public static boolean isInstanceCreated() {
+        return instanceCreated;
+    }
+
+    public static void setLibraryInitialized(boolean libraryInitialized) {
+        OpenSSLStatus.libraryInitialized = libraryInitialized;
+    }
+
+    public static void setInitialized(boolean initialized) {
+        OpenSSLStatus.initialized = initialized;
+    }
+
+    public static void setAvailable(boolean available) {
+        OpenSSLStatus.available = available;
+    }
+
+    public static void setInstanceCreated(boolean instanceCreated) {
+        OpenSSLStatus.instanceCreated = instanceCreated;
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java
new file mode 100644
index 0000000000..781dd4889a
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.io.IOException;
+import java.security.KeyStoreException;
+import java.util.List;
+import java.util.Set;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.X509KeyManager;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.SSLContext;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
+import org.apache.tomcat.util.net.SSLUtilBase;
+import org.apache.tomcat.util.net.jsse.JSSEKeyManager;
+import org.apache.tomcat.util.res.StringManager;
+
+public class OpenSSLUtil extends SSLUtilBase {
+
+    private static final Log log = LogFactory.getLog(OpenSSLUtil.class);
+    private static final StringManager sm = StringManager.getManager(OpenSSLUtil.class);
+
+
+    public OpenSSLUtil(SSLHostConfigCertificate certificate) {
+        super(certificate);
+    }
+
+
+    @Override
+    protected Log getLog() {
+        return log;
+    }
+
+
+    @Override
+    protected Set<String> getImplementedProtocols() {
+        return OpenSSLEngine.IMPLEMENTED_PROTOCOLS_SET;
+    }
+
+
+    @Override
+    protected Set<String> getImplementedCiphers() {
+        return OpenSSLEngine.AVAILABLE_CIPHER_SUITES;
+    }
+
+
+    @Override
+    protected boolean isTls13RenegAuthAvailable() {
+        // OpenSSL does support authentication after the initial handshake
+        return true;
+    }
+
+
+    @Override
+    public SSLContext createSSLContextInternal(List<String> negotiableProtocols) throws Exception {
+        return new OpenSSLContext(certificate, negotiableProtocols);
+    }
+
+
+    public static X509KeyManager chooseKeyManager(KeyManager[] managers) throws Exception {
+        if (managers == null) {
+            return null;
+        }
+        for (KeyManager manager : managers) {
+            if (manager instanceof JSSEKeyManager) {
+                return (JSSEKeyManager) manager;
+            }
+        }
+        for (KeyManager manager : managers) {
+            if (manager instanceof X509KeyManager) {
+                return (X509KeyManager) manager;
+            }
+        }
+        throw new IllegalStateException(sm.getString("openssl.keyManagerMissing"));
+    }
+
+
+    @Override
+    public KeyManager[] getKeyManagers() throws Exception {
+        try {
+            return super.getKeyManagers();
+        } catch (IllegalArgumentException e) {
+            // No (or invalid?) certificate chain was provided for the cert
+            String msg = sm.getString("openssl.nonJsseChain", certificate.getCertificateChainFile());
+            if (log.isDebugEnabled()) {
+                log.info(msg, e);
+            } else {
+                log.info(msg);
+            }
+            return null;
+        } catch (KeyStoreException | IOException e) {
+            // Depending on what is presented, JSSE may also throw
+            // KeyStoreException or IOException if it doesn't understand the
+            // provided file.
+            if (certificate.getCertificateFile() != null) {
+                String msg = sm.getString("openssl.nonJsseCertificate",
+                        certificate.getCertificateFile(), certificate.getCertificateKeyFile());
+                if (log.isDebugEnabled()) {
+                    log.info(msg, e);
+                } else {
+                    log.info(msg);
+                }
+                // Assume JSSE processing of the certificate failed, try again with OpenSSL
+                // without a key manager
+                return null;
+            }
+            throw e;
+        }
+    }
+
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java
new file mode 100644
index 0000000000..fc52abe8d6
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net.openssl.panama;
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+final class OpenSSLX509Certificate extends X509Certificate {
+
+    private final byte[] bytes;
+    private X509Certificate wrapped;
+
+    OpenSSLX509Certificate(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    @Override
+    public void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {
+        unwrap().checkValidity();
+    }
+
+    @Override
+    public void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {
+        unwrap().checkValidity(date);
+    }
+
+    @Override
+    public int getVersion() {
+        return unwrap().getVersion();
+    }
+
+    @Override
+    public BigInteger getSerialNumber() {
+        return unwrap().getSerialNumber();
+    }
+
+    @Override
+    @Deprecated
+    public Principal getIssuerDN() {
+        return unwrap().getIssuerDN();
+    }
+
+    @Override
+    @Deprecated
+    public Principal getSubjectDN() {
+        return unwrap().getSubjectDN();
+    }
+
+    @Override
+    public Date getNotBefore() {
+        return unwrap().getNotBefore();
+    }
+
+    @Override
+    public Date getNotAfter() {
+        return unwrap().getNotAfter();
+    }
+
+    @Override
+    public byte[] getTBSCertificate() throws CertificateEncodingException {
+        return unwrap().getTBSCertificate();
+    }
+
+    @Override
+    public byte[] getSignature() {
+        return unwrap().getSignature();
+    }
+
+    @Override
+    public String getSigAlgName() {
+        return unwrap().getSigAlgName();
+    }
+
+    @Override
+    public String getSigAlgOID() {
+        return unwrap().getSigAlgOID();
+    }
+
+    @Override
+    public byte[] getSigAlgParams() {
+        return unwrap().getSigAlgParams();
+    }
+
+    @Override
+    public boolean[] getIssuerUniqueID() {
+        return unwrap().getIssuerUniqueID();
+    }
+
+    @Override
+    public boolean[] getSubjectUniqueID() {
+        return unwrap().getSubjectUniqueID();
+    }
+
+    @Override
+    public boolean[] getKeyUsage() {
+        return unwrap().getKeyUsage();
+    }
+
+    @Override
+    public int getBasicConstraints() {
+        return unwrap().getBasicConstraints();
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return bytes.clone();
+    }
+
+    @Override
+    public void verify(PublicKey key)
+            throws CertificateException, NoSuchAlgorithmException,
+            InvalidKeyException, NoSuchProviderException, SignatureException {
+        unwrap().verify(key);
+    }
+
+    @Override
+    public void verify(PublicKey key, String sigProvider)
+            throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+            NoSuchProviderException, SignatureException {
+        unwrap().verify(key, sigProvider);
+    }
+
+    @Override
+    public String toString() {
+        return unwrap().toString();
+    }
+
+    @Override
+    public PublicKey getPublicKey() {
+        return unwrap().getPublicKey();
+    }
+
+    @Override
+    public boolean hasUnsupportedCriticalExtension() {
+        return unwrap().hasUnsupportedCriticalExtension();
+    }
+
+    @Override
+    public Set<String> getCriticalExtensionOIDs() {
+        return unwrap().getCriticalExtensionOIDs();
+    }
+
+    @Override
+    public Set<String> getNonCriticalExtensionOIDs() {
+        return unwrap().getNonCriticalExtensionOIDs();
+    }
+
+    @Override
+    public byte[] getExtensionValue(String oid) {
+        return unwrap().getExtensionValue(oid);
+    }
+
+    private X509Certificate unwrap() {
+        X509Certificate wrapped = this.wrapped;
+        if (wrapped == null) {
+            try {
+                wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY.generateCertificate(
+                        new ByteArrayInputStream(bytes));
+            } catch (CertificateException e) {
+                throw new IllegalStateException(e);
+            }
+        }
+        return wrapped;
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/Constants$root.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/Constants$root.java
new file mode 100644
index 0000000000..2359448a3b
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/Constants$root.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class Constants$root {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private Constants$root() {}
+    static final OfBoolean C_BOOL$LAYOUT = JAVA_BOOLEAN;
+    static final OfByte C_CHAR$LAYOUT = JAVA_BYTE;
+    static final OfShort C_SHORT$LAYOUT = JAVA_SHORT;
+    static final OfInt C_INT$LAYOUT = JAVA_INT;
+    static final OfLong C_LONG$LAYOUT = JAVA_LONG;
+    static final OfLong C_LONG_LONG$LAYOUT = JAVA_LONG;
+    static final OfFloat C_FLOAT$LAYOUT = JAVA_FLOAT;
+    static final OfDouble C_DOUBLE$LAYOUT = JAVA_DOUBLE;
+    static final AddressLayout C_POINTER$LAYOUT = ADDRESS.withByteAlignment(8).withTargetLayout(MemoryLayout.sequenceLayout(Constants$root.C_CHAR$LAYOUT));
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/RuntimeHelper.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/RuntimeHelper.java
new file mode 100644
index 0000000000..beab875552
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/RuntimeHelper.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.openssl;
+// Generated by jextract
+
+import java.lang.foreign.Linker;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.GroupLayout;
+import java.lang.foreign.SymbolLookup;
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.Arena;
+import java.lang.foreign.SegmentAllocator;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import static java.lang.foreign.Linker.*;
+import static java.lang.foreign.ValueLayout.*;
+
+final class RuntimeHelper {
+
+    private static final Linker LINKER = Linker.nativeLinker();
+    private static final ClassLoader LOADER = RuntimeHelper.class.getClassLoader();
+    private static final MethodHandles.Lookup MH_LOOKUP = MethodHandles.lookup();
+    private static final SymbolLookup SYMBOL_LOOKUP;
+    private static final SegmentAllocator THROWING_ALLOCATOR = (x, y) -> { throw new AssertionError("should not reach here"); };
+
+    final static SegmentAllocator CONSTANT_ALLOCATOR =
+            (size, align) -> Arena.ofAuto().allocate(size, align);
+
+    static {
+        System.loadLibrary("ssl");
+        SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
+        SYMBOL_LOOKUP = name -> loaderLookup.find(name).or(() -> LINKER.defaultLookup().find(name));
+    }
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private RuntimeHelper() {}
+
+    static <T> T requireNonNull(T obj, String symbolName) {
+        if (obj == null) {
+            throw new UnsatisfiedLinkError("unresolved symbol: " + symbolName);
+        }
+        return obj;
+    }
+
+    static MemorySegment lookupGlobalVariable(String name, MemoryLayout layout) {
+        return SYMBOL_LOOKUP.find(name)
+                .map(s -> s.reinterpret(layout.byteSize()))
+                .orElse(null);
+    }
+
+    static MethodHandle downcallHandle(String name, FunctionDescriptor fdesc) {
+        return SYMBOL_LOOKUP.find(name).
+                map(addr -> LINKER.downcallHandle(addr, fdesc)).
+                orElse(null);
+    }
+
+    static MethodHandle downcallHandle(FunctionDescriptor fdesc) {
+        return LINKER.downcallHandle(fdesc);
+    }
+
+    static MethodHandle downcallHandleVariadic(String name, FunctionDescriptor fdesc) {
+        return SYMBOL_LOOKUP.find(name).
+                map(addr -> VarargsInvoker.make(addr, fdesc)).
+                orElse(null);
+    }
+
+    static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fdesc) {
+        try {
+            return MH_LOOKUP.findVirtual(fi, name, fdesc.toMethodType());
+        } catch (Throwable ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
+    static <Z> MemorySegment upcallStub(MethodHandle fiHandle, Z z, FunctionDescriptor fdesc, Arena scope) {
+        try {
+            fiHandle = fiHandle.bindTo(z);
+            return LINKER.upcallStub(fiHandle, fdesc, scope);
+        } catch (Throwable ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
+    static MemorySegment asArray(MemorySegment addr, MemoryLayout layout, int numElements, Arena arena) {
+         return addr.reinterpret(numElements * layout.byteSize(), arena, null);
+    }
+
+    // Internals only below this point
+
+    private static final class VarargsInvoker {
+        private static final MethodHandle INVOKE_MH;
+        private final MemorySegment symbol;
+        private final FunctionDescriptor function;
+
+        private VarargsInvoker(MemorySegment symbol, FunctionDescriptor function) {
+            this.symbol = symbol;
+            this.function = function;
+        }
+
+        static {
+            try {
+                INVOKE_MH = MethodHandles.lookup().findVirtual(VarargsInvoker.class, "invoke", MethodType.methodType(Object.class, SegmentAllocator.class, Object[].class));
+            } catch (ReflectiveOperationException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        static MethodHandle make(MemorySegment symbol, FunctionDescriptor function) {
+            VarargsInvoker invoker = new VarargsInvoker(symbol, function);
+            MethodHandle handle = INVOKE_MH.bindTo(invoker).asCollector(Object[].class, function.argumentLayouts().size() + 1);
+            MethodType mtype = MethodType.methodType(function.returnLayout().isPresent() ? carrier(function.returnLayout().get(), true) : void.class);
+            for (MemoryLayout layout : function.argumentLayouts()) {
+                mtype = mtype.appendParameterTypes(carrier(layout, false));
+            }
+            mtype = mtype.appendParameterTypes(Object[].class);
+            boolean needsAllocator = function.returnLayout().isPresent() &&
+                                function.returnLayout().get() instanceof GroupLayout;
+            if (needsAllocator) {
+                mtype = mtype.insertParameterTypes(0, SegmentAllocator.class);
+            } else {
+                handle = MethodHandles.insertArguments(handle, 0, THROWING_ALLOCATOR);
+            }
+            return handle.asType(mtype);
+        }
+
+        static Class<?> carrier(MemoryLayout layout, boolean ret) {
+            if (layout instanceof ValueLayout valueLayout) {
+                return valueLayout.carrier();
+            } else if (layout instanceof GroupLayout) {
+                return MemorySegment.class;
+            } else {
+                throw new AssertionError("Cannot get here!");
+            }
+        }
+
+        private Object invoke(SegmentAllocator allocator, Object[] args) throws Throwable {
+            // one trailing Object[]
+            int nNamedArgs = function.argumentLayouts().size();
+            assert(args.length == nNamedArgs + 1);
+            // The last argument is the array of vararg collector
+            Object[] unnamedArgs = (Object[]) args[args.length - 1];
+
+            int argsCount = nNamedArgs + unnamedArgs.length;
+            Class<?>[] argTypes = new Class<?>[argsCount];
+            MemoryLayout[] argLayouts = new MemoryLayout[nNamedArgs + unnamedArgs.length];
+
+            int pos = 0;
+            for (pos = 0; pos < nNamedArgs; pos++) {
+                argLayouts[pos] = function.argumentLayouts().get(pos);
+            }
+
+            assert pos == nNamedArgs;
+            for (Object o: unnamedArgs) {
+                argLayouts[pos] = variadicLayout(normalize(o.getClass()));
+                pos++;
+            }
+            assert pos == argsCount;
+
+            FunctionDescriptor f = (function.returnLayout().isEmpty()) ?
+                    FunctionDescriptor.ofVoid(argLayouts) :
+                    FunctionDescriptor.of(function.returnLayout().get(), argLayouts);
+            MethodHandle mh = LINKER.downcallHandle(symbol, f);
+            boolean needsAllocator = function.returnLayout().isPresent() &&
+                                            function.returnLayout().get() instanceof GroupLayout;
+            if (needsAllocator) {
+                mh = mh.bindTo(allocator);
+            }
+            // flatten argument list so that it can be passed to an asSpreader MH
+            Object[] allArgs = new Object[nNamedArgs + unnamedArgs.length];
+            System.arraycopy(args, 0, allArgs, 0, nNamedArgs);
+            System.arraycopy(unnamedArgs, 0, allArgs, nNamedArgs, unnamedArgs.length);
+
+            return mh.asSpreader(Object[].class, argsCount).invoke(allArgs);
+        }
+
+        private static Class<?> unboxIfNeeded(Class<?> clazz) {
+            if (clazz == Boolean.class) {
+                return boolean.class;
+            } else if (clazz == Void.class) {
+                return void.class;
+            } else if (clazz == Byte.class) {
+                return byte.class;
+            } else if (clazz == Character.class) {
+                return char.class;
+            } else if (clazz == Short.class) {
+                return short.class;
+            } else if (clazz == Integer.class) {
+                return int.class;
+            } else if (clazz == Long.class) {
+                return long.class;
+            } else if (clazz == Float.class) {
+                return float.class;
+            } else if (clazz == Double.class) {
+                return double.class;
+            } else {
+                return clazz;
+            }
+        }
+
+        private Class<?> promote(Class<?> c) {
+            if (c == byte.class || c == char.class || c == short.class || c == int.class) {
+                return long.class;
+            } else if (c == float.class) {
+                return double.class;
+            } else {
+                return c;
+            }
+        }
+
+        private Class<?> normalize(Class<?> c) {
+            c = unboxIfNeeded(c);
+            if (c.isPrimitive()) {
+                return promote(c);
+            }
+            if (c == MemorySegment.class) {
+                return MemorySegment.class;
+            }
+            throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName());
+        }
+
+        private MemoryLayout variadicLayout(Class<?> c) {
+            if (c == long.class) {
+                return JAVA_LONG;
+            } else if (c == double.class) {
+                return JAVA_DOUBLE;
+            } else if (c == MemorySegment.class) {
+                return ADDRESS;
+            } else {
+                throw new IllegalArgumentException("Unhandled variadic argument class: " + c);
+            }
+        }
+    }
+}
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java
new file mode 100644
index 0000000000..4686d04ca0
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+/**
+ * {@snippet :
+ * int (*SSL_CTX_set_cert_verify_callback$cb)(X509_STORE_CTX*,void*);
+ * }
+ */
+public interface SSL_CTX_set_cert_verify_callback$cb {
+
+    int apply(java.lang.foreign.MemorySegment _x0, java.lang.foreign.MemorySegment _x1);
+    static MemorySegment allocate(SSL_CTX_set_cert_verify_callback$cb fi, Arena scope) {
+        return RuntimeHelper.upcallStub(constants$15.SSL_CTX_set_cert_verify_callback$cb_UP$MH, fi, constants$15.SSL_CTX_set_cert_verify_callback$cb$FUNC, scope);
+    }
+    static SSL_CTX_set_cert_verify_callback$cb ofAddress(MemorySegment addr, Arena arena) {
+        MemorySegment symbol = addr.reinterpret(arena, null);
+        return (java.lang.foreign.MemorySegment __x0, java.lang.foreign.MemorySegment __x1) -> {
+            try {
+                return (int)constants$15.SSL_CTX_set_cert_verify_callback$cb_DOWN$MH.invokeExact(symbol, __x0, __x1);
+            } catch (Throwable ex$) {
+                throw new AssertionError("should not reach here", ex$);
+            }
+        };
+    }
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java
new file mode 100644
index 0000000000..77a68cda04
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+/**
+ * {@snippet :
+ * DH* (*SSL_CTX_set_tmp_dh_callback$dh)(SSL*,int,int);
+ * }
+ */
+public interface SSL_CTX_set_tmp_dh_callback$dh {
+
+    java.lang.foreign.MemorySegment apply(java.lang.foreign.MemorySegment _x0, int _x1, int _x2);
+    static MemorySegment allocate(SSL_CTX_set_tmp_dh_callback$dh fi, Arena scope) {
+        return RuntimeHelper.upcallStub(constants$21.SSL_CTX_set_tmp_dh_callback$dh_UP$MH, fi, constants$21.SSL_CTX_set_tmp_dh_callback$dh$FUNC, scope);
+    }
+    static SSL_CTX_set_tmp_dh_callback$dh ofAddress(MemorySegment addr, Arena arena) {
+        MemorySegment symbol = addr.reinterpret(arena, null);
+        return (java.lang.foreign.MemorySegment __x0, int __x1, int __x2) -> {
+            try {
+                return (java.lang.foreign.MemorySegment)constants$22.SSL_CTX_set_tmp_dh_callback$dh_DOWN$MH.invokeExact(symbol, __x0, __x1, __x2);
+            } catch (Throwable ex$) {
+                throw new AssertionError("should not reach here", ex$);
+            }
+        };
+    }
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java
new file mode 100644
index 0000000000..16b7144980
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+/**
+ * {@snippet :
+ * void (*SSL_set_info_callback$cb)(const SSL*,int,int);
+ * }
+ */
+public interface SSL_set_info_callback$cb {
+
+    void apply(java.lang.foreign.MemorySegment _x0, int _x1, int _x2);
+    static MemorySegment allocate(SSL_set_info_callback$cb fi, Arena scope) {
+        return RuntimeHelper.upcallStub(constants$21.SSL_set_info_callback$cb_UP$MH, fi, constants$21.SSL_set_info_callback$cb$FUNC, scope);
+    }
+    static SSL_set_info_callback$cb ofAddress(MemorySegment addr, Arena arena) {
+        MemorySegment symbol = addr.reinterpret(arena, null);
+        return (java.lang.foreign.MemorySegment __x0, int __x1, int __x2) -> {
+            try {
+                constants$21.SSL_set_info_callback$cb_DOWN$MH.invokeExact(symbol, __x0, __x1, __x2);
+            } catch (Throwable ex$) {
+                throw new AssertionError("should not reach here", ex$);
+            }
+        };
+    }
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$0.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$0.java
new file mode 100644
index 0000000000..60a76fd651
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$0.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$0 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$0() {}
+    static final FunctionDescriptor OPENSSL_sk_num$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OPENSSL_sk_num$MH = RuntimeHelper.downcallHandle(
+        "OPENSSL_sk_num",
+        constants$0.OPENSSL_sk_num$FUNC
+    );
+    static final FunctionDescriptor OPENSSL_sk_value$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle OPENSSL_sk_value$MH = RuntimeHelper.downcallHandle(
+        "OPENSSL_sk_value",
+        constants$0.OPENSSL_sk_value$FUNC
+    );
+    static final FunctionDescriptor OpenSSL_version_num$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT);
+    static final MethodHandle OpenSSL_version_num$MH = RuntimeHelper.downcallHandle(
+        "OpenSSL_version_num",
+        constants$0.OpenSSL_version_num$FUNC
+    );
+    static final FunctionDescriptor OpenSSL_version$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle OpenSSL_version$MH = RuntimeHelper.downcallHandle(
+        "OpenSSL_version",
+        constants$0.OpenSSL_version$FUNC
+    );
+    static final FunctionDescriptor CRYPTO_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle CRYPTO_free$MH = RuntimeHelper.downcallHandle(
+        "CRYPTO_free",
+        constants$0.CRYPTO_free$FUNC
+    );
+    static final FunctionDescriptor BIO_ctrl_pending$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BIO_ctrl_pending$MH = RuntimeHelper.downcallHandle(
+        "BIO_ctrl_pending",
+        constants$0.BIO_ctrl_pending$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$1.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$1.java
new file mode 100644
index 0000000000..55942ad213
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$1.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$1 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$1() {}
+    static final FunctionDescriptor BIO_s_file$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle BIO_s_file$MH = RuntimeHelper.downcallHandle(
+        "BIO_s_file",
+        constants$1.BIO_s_file$FUNC
+    );
+    static final FunctionDescriptor BIO_new_file$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BIO_new_file$MH = RuntimeHelper.downcallHandle(
+        "BIO_new_file",
+        constants$1.BIO_new_file$FUNC
+    );
+    static final FunctionDescriptor BIO_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BIO_new$MH = RuntimeHelper.downcallHandle(
+        "BIO_new",
+        constants$1.BIO_new$FUNC
+    );
+    static final FunctionDescriptor BIO_free$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BIO_free$MH = RuntimeHelper.downcallHandle(
+        "BIO_free",
+        constants$1.BIO_free$FUNC
+    );
+    static final FunctionDescriptor BIO_read$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle BIO_read$MH = RuntimeHelper.downcallHandle(
+        "BIO_read",
+        constants$1.BIO_read$FUNC
+    );
+    static final FunctionDescriptor BIO_write$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle BIO_write$MH = RuntimeHelper.downcallHandle(
+        "BIO_write",
+        constants$1.BIO_write$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$10.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$10.java
new file mode 100644
index 0000000000..dda8ba2ede
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$10.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$10 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$10() {}
+    static final FunctionDescriptor PEM_read_bio_ECPKParameters$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PEM_read_bio_ECPKParameters$MH = RuntimeHelper.downcallHandle(
+        "PEM_read_bio_ECPKParameters",
+        constants$10.PEM_read_bio_ECPKParameters$FUNC
+    );
+    static final FunctionDescriptor PEM_read_bio_DHparams$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PEM_read_bio_DHparams$MH = RuntimeHelper.downcallHandle(
+        "PEM_read_bio_DHparams",
+        constants$10.PEM_read_bio_DHparams$FUNC
+    );
+    static final FunctionDescriptor PEM_read_bio_PrivateKey$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PEM_read_bio_PrivateKey$MH = RuntimeHelper.downcallHandle(
+        "PEM_read_bio_PrivateKey",
+        constants$10.PEM_read_bio_PrivateKey$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_get_options$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_get_options$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_get_options",
+        constants$10.SSL_CTX_get_options$FUNC
+    );
+    static final FunctionDescriptor SSL_get_options$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_options$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_options",
+        constants$10.SSL_get_options$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_clear_options$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_clear_options$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_clear_options",
+        constants$10.SSL_CTX_clear_options$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$11.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$11.java
new file mode 100644
index 0000000000..4e57c7f21a
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$11.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$11 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$11() {}
+    static final FunctionDescriptor SSL_CTX_set_options$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_options$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_options",
+        constants$11.SSL_CTX_set_options$FUNC
+    );
+    static final FunctionDescriptor SSL_set_options$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle SSL_set_options$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_options",
+        constants$11.SSL_set_options$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_alpn_select_cb$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_alpn_select_cb$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_alpn_select_cb",
+        constants$11.SSL_CTX_set_alpn_select_cb$FUNC
+    );
+    static final FunctionDescriptor SSL_get0_alpn_selected$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get0_alpn_selected$MH = RuntimeHelper.downcallHandle(
+        "SSL_get0_alpn_selected",
+        constants$11.SSL_get0_alpn_selected$FUNC
+    );
+    static final FunctionDescriptor SSL_in_init$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_in_init$MH = RuntimeHelper.downcallHandle(
+        "SSL_in_init",
+        constants$11.SSL_in_init$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_cipher_list$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_cipher_list$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_cipher_list",
+        constants$11.SSL_CTX_set_cipher_list$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$12.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$12.java
new file mode 100644
index 0000000000..1fa3c12a0b
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$12.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$12 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$12() {}
+    static final FunctionDescriptor SSL_CTX_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_new$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_new",
+        constants$12.SSL_CTX_new$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_free$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_free",
+        constants$12.SSL_CTX_free$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_timeout$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_timeout$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_timeout",
+        constants$12.SSL_CTX_set_timeout$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_get_timeout$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_get_timeout$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_get_timeout",
+        constants$12.SSL_CTX_get_timeout$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_get_cert_store$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_get_cert_store$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_get_cert_store",
+        constants$12.SSL_CTX_get_cert_store$FUNC
+    );
+    static final FunctionDescriptor SSL_get_current_cipher$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_current_cipher$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_current_cipher",
+        constants$12.SSL_get_current_cipher$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$13.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$13.java
new file mode 100644
index 0000000000..bcab07b600
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$13.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$13 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$13() {}
+    static final FunctionDescriptor SSL_CIPHER_get_name$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CIPHER_get_name$MH = RuntimeHelper.downcallHandle(
+        "SSL_CIPHER_get_name",
+        constants$13.SSL_CIPHER_get_name$FUNC
+    );
+    static final FunctionDescriptor SSL_CIPHER_get_kx_nid$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CIPHER_get_kx_nid$MH = RuntimeHelper.downcallHandle(
+        "SSL_CIPHER_get_kx_nid",
+        constants$13.SSL_CIPHER_get_kx_nid$FUNC
+    );
+    static final FunctionDescriptor SSL_CIPHER_get_auth_nid$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CIPHER_get_auth_nid$MH = RuntimeHelper.downcallHandle(
+        "SSL_CIPHER_get_auth_nid",
+        constants$13.SSL_CIPHER_get_auth_nid$FUNC
+    );
+    static final FunctionDescriptor SSL_pending$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_pending$MH = RuntimeHelper.downcallHandle(
+        "SSL_pending",
+        constants$13.SSL_pending$FUNC
+    );
+    static final FunctionDescriptor SSL_set_bio$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_bio$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_bio",
+        constants$13.SSL_set_bio$FUNC
+    );
+    static final FunctionDescriptor SSL_set_cipher_list$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_cipher_list$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_cipher_list",
+        constants$13.SSL_set_cipher_list$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$14.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$14.java
new file mode 100644
index 0000000000..b36fcb3ade
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$14.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$14 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$14() {}
+    static final FunctionDescriptor SSL_CTX_set_ciphersuites$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_ciphersuites$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_ciphersuites",
+        constants$14.SSL_CTX_set_ciphersuites$FUNC
+    );
+    static final FunctionDescriptor SSL_set_verify$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_verify$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_verify",
+        constants$14.SSL_set_verify$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_use_certificate_chain_file$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_use_certificate_chain_file$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_use_certificate_chain_file",
+        constants$14.SSL_CTX_use_certificate_chain_file$FUNC
+    );
+    static final FunctionDescriptor SSL_load_client_CA_file$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_load_client_CA_file$MH = RuntimeHelper.downcallHandle(
+        "SSL_load_client_CA_file",
+        constants$14.SSL_load_client_CA_file$FUNC
+    );
+    static final FunctionDescriptor SSL_add_file_cert_subjects_to_stack$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_add_file_cert_subjects_to_stack$MH = RuntimeHelper.downcallHandle(
+        "SSL_add_file_cert_subjects_to_stack",
+        constants$14.SSL_add_file_cert_subjects_to_stack$FUNC
+    );
+    static final FunctionDescriptor SSL_SESSION_get_time$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_SESSION_get_time$MH = RuntimeHelper.downcallHandle(
+        "SSL_SESSION_get_time",
+        constants$14.SSL_SESSION_get_time$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$15.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$15.java
new file mode 100644
index 0000000000..a3a26ec317
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$15.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$15 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$15() {}
+    static final FunctionDescriptor SSL_SESSION_get_id$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_SESSION_get_id$MH = RuntimeHelper.downcallHandle(
+        "SSL_SESSION_get_id",
+        constants$15.SSL_SESSION_get_id$FUNC
+    );
+    static final FunctionDescriptor SSL_get1_peer_certificate$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get1_peer_certificate$MH = RuntimeHelper.downcallHandle(
+        "SSL_get1_peer_certificate",
+        constants$15.SSL_get1_peer_certificate$FUNC
+    );
+    static final FunctionDescriptor SSL_get_peer_cert_chain$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_peer_cert_chain$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_peer_cert_chain",
+        constants$15.SSL_get_peer_cert_chain$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_verify$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_verify$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_verify",
+        constants$15.SSL_CTX_set_verify$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_cert_verify_callback$cb$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final FunctionDescriptor SSL_CTX_set_cert_verify_callback$cb_UP$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_cert_verify_callback$cb_UP$MH = RuntimeHelper.upcallHandle(SSL_CTX_set_cert_verify_callback$cb.class, "apply", constants$15.SSL_CTX_set_cert_verify_callback$cb_UP$FUNC);
+    static final FunctionDescriptor SSL_CTX_set_cert_verify_callback$cb_DOWN$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_cert_verify_callback$cb_DOWN$MH = RuntimeHelper.downcallHandle(
+        constants$15.SSL_CTX_set_cert_verify_callback$cb_DOWN$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$16.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$16.java
new file mode 100644
index 0000000000..c2d5833d2d
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$16.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$16 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$16() {}
+    static final FunctionDescriptor SSL_CTX_set_cert_verify_callback$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_cert_verify_callback$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_cert_verify_callback",
+        constants$16.SSL_CTX_set_cert_verify_callback$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_use_PrivateKey$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_use_PrivateKey$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_use_PrivateKey",
+        constants$16.SSL_CTX_use_PrivateKey$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_use_certificate$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_use_certificate$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_use_certificate",
+        constants$16.SSL_CTX_use_certificate$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_default_passwd_cb$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_default_passwd_cb$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_default_passwd_cb",
+        constants$16.SSL_CTX_set_default_passwd_cb$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_check_private_key$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_check_private_key$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_check_private_key",
+        constants$16.SSL_CTX_check_private_key$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_session_id_context$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_session_id_context$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_session_id_context",
+        constants$16.SSL_CTX_set_session_id_context$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$17.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$17.java
new file mode 100644
index 0000000000..0d56bb9da5
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$17.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$17 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$17() {}
+    static final FunctionDescriptor SSL_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_new$MH = RuntimeHelper.downcallHandle(
+        "SSL_new",
+        constants$17.SSL_new$FUNC
+    );
+    static final FunctionDescriptor SSL_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_free$MH = RuntimeHelper.downcallHandle(
+        "SSL_free",
+        constants$17.SSL_free$FUNC
+    );
+    static final FunctionDescriptor SSL_read$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_read$MH = RuntimeHelper.downcallHandle(
+        "SSL_read",
+        constants$17.SSL_read$FUNC
+    );
+    static final FunctionDescriptor SSL_write$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_write$MH = RuntimeHelper.downcallHandle(
+        "SSL_write",
+        constants$17.SSL_write$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_ctrl$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_ctrl$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_ctrl",
+        constants$17.SSL_CTX_ctrl$FUNC
+    );
+    static final FunctionDescriptor SSL_get_version$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_version$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_version",
+        constants$17.SSL_get_version$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$18.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$18.java
new file mode 100644
index 0000000000..e80b1c1201
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$18.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$18 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$18() {}
+    static final FunctionDescriptor TLS_server_method$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle TLS_server_method$MH = RuntimeHelper.downcallHandle(
+        "TLS_server_method",
+        constants$18.TLS_server_method$FUNC
+    );
+    static final FunctionDescriptor SSL_get_ciphers$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_ciphers$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_ciphers",
+        constants$18.SSL_get_ciphers$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_get_ciphers$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_get_ciphers$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_get_ciphers",
+        constants$18.SSL_CTX_get_ciphers$FUNC
+    );
+    static final FunctionDescriptor SSL_do_handshake$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_do_handshake$MH = RuntimeHelper.downcallHandle(
+        "SSL_do_handshake",
+        constants$18.SSL_do_handshake$FUNC
+    );
+    static final FunctionDescriptor SSL_renegotiate$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_renegotiate$MH = RuntimeHelper.downcallHandle(
+        "SSL_renegotiate",
+        constants$18.SSL_renegotiate$FUNC
+    );
+    static final FunctionDescriptor SSL_renegotiate_pending$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_renegotiate_pending$MH = RuntimeHelper.downcallHandle(
+        "SSL_renegotiate_pending",
+        constants$18.SSL_renegotiate_pending$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$19.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$19.java
new file mode 100644
index 0000000000..d213369fd8
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$19.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$19 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$19() {}
+    static final FunctionDescriptor SSL_shutdown$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_shutdown$MH = RuntimeHelper.downcallHandle(
+        "SSL_shutdown",
+        constants$19.SSL_shutdown$FUNC
+    );
+    static final FunctionDescriptor SSL_verify_client_post_handshake$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_verify_client_post_handshake$MH = RuntimeHelper.downcallHandle(
+        "SSL_verify_client_post_handshake",
+        constants$19.SSL_verify_client_post_handshake$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_client_CA_list$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_client_CA_list$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_client_CA_list",
+        constants$19.SSL_CTX_set_client_CA_list$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_get_client_CA_list$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_get_client_CA_list$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_get_client_CA_list",
+        constants$19.SSL_CTX_get_client_CA_list$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_add_client_CA$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_add_client_CA$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_add_client_CA",
+        constants$19.SSL_CTX_add_client_CA$FUNC
+    );
+    static final FunctionDescriptor SSL_set_connect_state$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_connect_state$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_connect_state",
+        constants$19.SSL_set_connect_state$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$2.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$2.java
new file mode 100644
index 0000000000..4966508649
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$2.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$2 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$2() {}
+    static final FunctionDescriptor BIO_ctrl$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BIO_ctrl$MH = RuntimeHelper.downcallHandle(
+        "BIO_ctrl",
+        constants$2.BIO_ctrl$FUNC
+    );
+    static final FunctionDescriptor BIO_s_mem$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle BIO_s_mem$MH = RuntimeHelper.downcallHandle(
+        "BIO_s_mem",
+        constants$2.BIO_s_mem$FUNC
+    );
+    static final FunctionDescriptor BIO_s_bio$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle BIO_s_bio$MH = RuntimeHelper.downcallHandle(
+        "BIO_s_bio",
+        constants$2.BIO_s_bio$FUNC
+    );
+    static final FunctionDescriptor BIO_new_bio_pair$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle BIO_new_bio_pair$MH = RuntimeHelper.downcallHandle(
+        "BIO_new_bio_pair",
+        constants$2.BIO_new_bio_pair$FUNC
+    );
+    static final FunctionDescriptor BN_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle BN_new$MH = RuntimeHelper.downcallHandle(
+        "BN_new",
+        constants$2.BN_new$FUNC
+    );
+    static final FunctionDescriptor BN_set_word$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle BN_set_word$MH = RuntimeHelper.downcallHandle(
+        "BN_set_word",
+        constants$2.BN_set_word$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$20.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$20.java
new file mode 100644
index 0000000000..7c0f69a470
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$20.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$20 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$20() {}
+    static final FunctionDescriptor SSL_set_accept_state$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_accept_state$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_accept_state",
+        constants$20.SSL_set_accept_state$FUNC
+    );
+    static final FunctionDescriptor SSL_get_privatekey$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_privatekey$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_privatekey",
+        constants$20.SSL_get_privatekey$FUNC
+    );
+    static final FunctionDescriptor SSL_get_shutdown$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_shutdown$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_shutdown",
+        constants$20.SSL_get_shutdown$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_default_verify_paths$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_default_verify_paths$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_default_verify_paths",
+        constants$20.SSL_CTX_set_default_verify_paths$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_load_verify_locations$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_load_verify_locations$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_load_verify_locations",
+        constants$20.SSL_CTX_load_verify_locations$FUNC
+    );
+    static final FunctionDescriptor SSL_get_session$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_get_session$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_session",
+        constants$20.SSL_get_session$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$21.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$21.java
new file mode 100644
index 0000000000..0832c55217
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$21.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$21 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$21() {}
+    static final FunctionDescriptor SSL_set_info_callback$cb$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final FunctionDescriptor SSL_set_info_callback$cb_UP$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_set_info_callback$cb_UP$MH = RuntimeHelper.upcallHandle(SSL_set_info_callback$cb.class, "apply", constants$21.SSL_set_info_callback$cb_UP$FUNC);
+    static final FunctionDescriptor SSL_set_info_callback$cb_DOWN$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_set_info_callback$cb_DOWN$MH = RuntimeHelper.downcallHandle(
+        constants$21.SSL_set_info_callback$cb_DOWN$FUNC
+    );
+    static final FunctionDescriptor SSL_set_info_callback$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_set_info_callback$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_info_callback",
+        constants$21.SSL_set_info_callback$FUNC
+    );
+    static final FunctionDescriptor SSL_set_verify_result$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle SSL_set_verify_result$MH = RuntimeHelper.downcallHandle(
+        "SSL_set_verify_result",
+        constants$21.SSL_set_verify_result$FUNC
+    );
+    static final FunctionDescriptor SSL_get_ex_data_X509_STORE_CTX_idx$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT);
+    static final MethodHandle SSL_get_ex_data_X509_STORE_CTX_idx$MH = RuntimeHelper.downcallHandle(
+        "SSL_get_ex_data_X509_STORE_CTX_idx",
+        constants$21.SSL_get_ex_data_X509_STORE_CTX_idx$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_tmp_dh_callback$dh$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final FunctionDescriptor SSL_CTX_set_tmp_dh_callback$dh_UP$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_tmp_dh_callback$dh_UP$MH = RuntimeHelper.upcallHandle(SSL_CTX_set_tmp_dh_callback$dh.class, "apply", constants$21.SSL_CTX_set_tmp_dh_callback$dh_UP$FUNC);
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$22.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$22.java
new file mode 100644
index 0000000000..6b2c032610
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$22.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$22 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$22() {}
+    static final FunctionDescriptor SSL_CTX_set_tmp_dh_callback$dh_DOWN$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_tmp_dh_callback$dh_DOWN$MH = RuntimeHelper.downcallHandle(
+        constants$22.SSL_CTX_set_tmp_dh_callback$dh_DOWN$FUNC
+    );
+    static final FunctionDescriptor SSL_CTX_set_tmp_dh_callback$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CTX_set_tmp_dh_callback$MH = RuntimeHelper.downcallHandle(
+        "SSL_CTX_set_tmp_dh_callback",
+        constants$22.SSL_CTX_set_tmp_dh_callback$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_CTX_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle SSL_CONF_CTX_new$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_CTX_new",
+        constants$22.SSL_CONF_CTX_new$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_CTX_finish$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_CTX_finish$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_CTX_finish",
+        constants$22.SSL_CONF_CTX_finish$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_CTX_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_CTX_free$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_CTX_free",
+        constants$22.SSL_CONF_CTX_free$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_CTX_set_flags$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_CTX_set_flags$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_CTX_set_flags",
+        constants$22.SSL_CONF_CTX_set_flags$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$23.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$23.java
new file mode 100644
index 0000000000..234851e93c
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$23.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$23 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$23() {}
+    static final FunctionDescriptor SSL_CONF_CTX_set_ssl_ctx$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_CTX_set_ssl_ctx$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_CTX_set_ssl_ctx",
+        constants$23.SSL_CONF_CTX_set_ssl_ctx$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_cmd$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_cmd$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_cmd",
+        constants$23.SSL_CONF_cmd$FUNC
+    );
+    static final FunctionDescriptor SSL_CONF_cmd_value_type$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle SSL_CONF_cmd_value_type$MH = RuntimeHelper.downcallHandle(
+        "SSL_CONF_cmd_value_type",
+        constants$23.SSL_CONF_cmd_value_type$FUNC
+    );
+    static final FunctionDescriptor OPENSSL_init_ssl$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OPENSSL_init_ssl$MH = RuntimeHelper.downcallHandle(
+        "OPENSSL_init_ssl",
+        constants$23.OPENSSL_init_ssl$FUNC
+    );
+    static final FunctionDescriptor ERR_get_error$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT);
+    static final MethodHandle ERR_get_error$MH = RuntimeHelper.downcallHandle(
+        "ERR_get_error",
+        constants$23.ERR_get_error$FUNC
+    );
+    static final FunctionDescriptor ERR_peek_last_error$FUNC = FunctionDescriptor.of(Constants$root.C_LONG_LONG$LAYOUT);
+    static final MethodHandle ERR_peek_last_error$MH = RuntimeHelper.downcallHandle(
+        "ERR_peek_last_error",
+        constants$23.ERR_peek_last_error$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$24.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$24.java
new file mode 100644
index 0000000000..a8a2e4201c
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$24.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$24 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$24() {}
+    static final FunctionDescriptor ERR_clear_error$FUNC = FunctionDescriptor.ofVoid();
+    static final MethodHandle ERR_clear_error$MH = RuntimeHelper.downcallHandle(
+        "ERR_clear_error",
+        constants$24.ERR_clear_error$FUNC
+    );
+    static final FunctionDescriptor ERR_error_string$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ERR_error_string$MH = RuntimeHelper.downcallHandle(
+        "ERR_error_string",
+        constants$24.ERR_error_string$FUNC
+    );
+    static final FunctionDescriptor PKCS12_verify_mac$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle PKCS12_verify_mac$MH = RuntimeHelper.downcallHandle(
+        "PKCS12_verify_mac",
+        constants$24.PKCS12_verify_mac$FUNC
+    );
+    static final FunctionDescriptor PKCS12_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PKCS12_free$MH = RuntimeHelper.downcallHandle(
+        "PKCS12_free",
+        constants$24.PKCS12_free$FUNC
+    );
+    static final FunctionDescriptor PKCS12_parse$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PKCS12_parse$MH = RuntimeHelper.downcallHandle(
+        "PKCS12_parse",
+        constants$24.PKCS12_parse$FUNC
+    );
+    static final FunctionDescriptor d2i_PKCS12_bio$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle d2i_PKCS12_bio$MH = RuntimeHelper.downcallHandle(
+        "d2i_PKCS12_bio",
+        constants$24.d2i_PKCS12_bio$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$25.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$25.java
new file mode 100644
index 0000000000..7eae50ad67
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$25.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$25 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$25() {}
+    static final FunctionDescriptor RAND_seed$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle RAND_seed$MH = RuntimeHelper.downcallHandle(
+        "RAND_seed",
+        constants$25.RAND_seed$FUNC
+    );
+    static final FunctionDescriptor RAND_load_file$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle RAND_load_file$MH = RuntimeHelper.downcallHandle(
+        "RAND_load_file",
+        constants$25.RAND_load_file$FUNC
+    );
+    static final FunctionDescriptor X509_check_issued$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_check_issued$MH = RuntimeHelper.downcallHandle(
+        "X509_check_issued",
+        constants$25.X509_check_issued$FUNC
+    );
+    static final FunctionDescriptor ENGINE_by_id$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ENGINE_by_id$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_by_id",
+        constants$25.ENGINE_by_id$FUNC
+    );
+    static final FunctionDescriptor ENGINE_register_all_complete$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT);
+    static final MethodHandle ENGINE_register_all_complete$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_register_all_complete",
+        constants$25.ENGINE_register_all_complete$FUNC
+    );
+    static final FunctionDescriptor ENGINE_ctrl_cmd_string$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle ENGINE_ctrl_cmd_string$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_ctrl_cmd_string",
+        constants$25.ENGINE_ctrl_cmd_string$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$26.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$26.java
new file mode 100644
index 0000000000..685a84ec1c
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$26.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$26 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$26() {}
+    static final FunctionDescriptor ENGINE_free$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ENGINE_free$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_free",
+        constants$26.ENGINE_free$FUNC
+    );
+    static final FunctionDescriptor ENGINE_load_private_key$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ENGINE_load_private_key$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_load_private_key",
+        constants$26.ENGINE_load_private_key$FUNC
+    );
+    static final FunctionDescriptor ENGINE_set_default$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle ENGINE_set_default$MH = RuntimeHelper.downcallHandle(
+        "ENGINE_set_default",
+        constants$26.ENGINE_set_default$FUNC
+    );
+    static final FunctionDescriptor OCSP_cert_to_id$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_cert_to_id$MH = RuntimeHelper.downcallHandle(
+        "OCSP_cert_to_id",
+        constants$26.OCSP_cert_to_id$FUNC
+    );
+    static final FunctionDescriptor OCSP_request_add0_id$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_request_add0_id$MH = RuntimeHelper.downcallHandle(
+        "OCSP_request_add0_id",
+        constants$26.OCSP_request_add0_id$FUNC
+    );
+    static final FunctionDescriptor OCSP_response_status$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_response_status$MH = RuntimeHelper.downcallHandle(
+        "OCSP_response_status",
+        constants$26.OCSP_response_status$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$27.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$27.java
new file mode 100644
index 0000000000..d6a8d897c9
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$27.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$27 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$27() {}
+    static final FunctionDescriptor OCSP_response_get1_basic$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_response_get1_basic$MH = RuntimeHelper.downcallHandle(
+        "OCSP_response_get1_basic",
+        constants$27.OCSP_response_get1_basic$FUNC
+    );
+    static final FunctionDescriptor OCSP_resp_get0$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle OCSP_resp_get0$MH = RuntimeHelper.downcallHandle(
+        "OCSP_resp_get0",
+        constants$27.OCSP_resp_get0$FUNC
+    );
+    static final FunctionDescriptor OCSP_resp_find$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle OCSP_resp_find$MH = RuntimeHelper.downcallHandle(
+        "OCSP_resp_find",
+        constants$27.OCSP_resp_find$FUNC
+    );
+    static final FunctionDescriptor OCSP_single_get0_status$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_single_get0_status$MH = RuntimeHelper.downcallHandle(
+        "OCSP_single_get0_status",
+        constants$27.OCSP_single_get0_status$FUNC
+    );
+    static final FunctionDescriptor OCSP_BASICRESP_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_BASICRESP_free$MH = RuntimeHelper.downcallHandle(
+        "OCSP_BASICRESP_free",
+        constants$27.OCSP_BASICRESP_free$FUNC
+    );
+    static final FunctionDescriptor OCSP_RESPONSE_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_RESPONSE_free$MH = RuntimeHelper.downcallHandle(
+        "OCSP_RESPONSE_free",
+        constants$27.OCSP_RESPONSE_free$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$28.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$28.java
new file mode 100644
index 0000000000..4df680b449
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$28.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$28 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$28() {}
+    static final FunctionDescriptor d2i_OCSP_RESPONSE$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle d2i_OCSP_RESPONSE$MH = RuntimeHelper.downcallHandle(
+        "d2i_OCSP_RESPONSE",
+        constants$28.d2i_OCSP_RESPONSE$FUNC
+    );
+    static final FunctionDescriptor OCSP_CERTID_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_CERTID_free$MH = RuntimeHelper.downcallHandle(
+        "OCSP_CERTID_free",
+        constants$28.OCSP_CERTID_free$FUNC
+    );
+    static final FunctionDescriptor OCSP_REQUEST_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle OCSP_REQUEST_new$MH = RuntimeHelper.downcallHandle(
+        "OCSP_REQUEST_new",
+        constants$28.OCSP_REQUEST_new$FUNC
+    );
+    static final FunctionDescriptor OCSP_REQUEST_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OCSP_REQUEST_free$MH = RuntimeHelper.downcallHandle(
+        "OCSP_REQUEST_free",
+        constants$28.OCSP_REQUEST_free$FUNC
+    );
+    static final FunctionDescriptor i2d_OCSP_REQUEST$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle i2d_OCSP_REQUEST$MH = RuntimeHelper.downcallHandle(
+        "i2d_OCSP_REQUEST",
+        constants$28.i2d_OCSP_REQUEST$FUNC
+    );
+    static final FunctionDescriptor OSSL_PROVIDER_get0_name$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle OSSL_PROVIDER_get0_name$MH = RuntimeHelper.downcallHandle(
+        "OSSL_PROVIDER_get0_name",
+        constants$28.OSSL_PROVIDER_get0_name$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$29.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$29.java
new file mode 100644
index 0000000000..71ff4b643e
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$29.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$29 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$29() {}
+    static final MemorySegment OPENSSL_FILE$SEGMENT = RuntimeHelper.CONSTANT_ALLOCATOR.allocateUtf8String("/tmp/jextract$5975327931591344605.h");
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$3.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$3.java
new file mode 100644
index 0000000000..cae0beeb17
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$3.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$3 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$3() {}
+    static final FunctionDescriptor BN_get_rfc2409_prime_768$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc2409_prime_768$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc2409_prime_768",
+        constants$3.BN_get_rfc2409_prime_768$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc2409_prime_1024$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc2409_prime_1024$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc2409_prime_1024",
+        constants$3.BN_get_rfc2409_prime_1024$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc3526_prime_1536$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_1536$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_1536",
+        constants$3.BN_get_rfc3526_prime_1536$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc3526_prime_2048$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_2048$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_2048",
+        constants$3.BN_get_rfc3526_prime_2048$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc3526_prime_3072$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_3072$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_3072",
+        constants$3.BN_get_rfc3526_prime_3072$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc3526_prime_4096$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_4096$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_4096",
+        constants$3.BN_get_rfc3526_prime_4096$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$4.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$4.java
new file mode 100644
index 0000000000..d4a247d5f7
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$4.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$4 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$4() {}
+    static final FunctionDescriptor BN_get_rfc3526_prime_6144$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_6144$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_6144",
+        constants$4.BN_get_rfc3526_prime_6144$FUNC
+    );
+    static final FunctionDescriptor BN_get_rfc3526_prime_8192$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle BN_get_rfc3526_prime_8192$MH = RuntimeHelper.downcallHandle(
+        "BN_get_rfc3526_prime_8192",
+        constants$4.BN_get_rfc3526_prime_8192$FUNC
+    );
+    static final FunctionDescriptor ASN1_STRING_length$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ASN1_STRING_length$MH = RuntimeHelper.downcallHandle(
+        "ASN1_STRING_length",
+        constants$4.ASN1_STRING_length$FUNC
+    );
+    static final FunctionDescriptor ASN1_STRING_get0_data$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle ASN1_STRING_get0_data$MH = RuntimeHelper.downcallHandle(
+        "ASN1_STRING_get0_data",
+        constants$4.ASN1_STRING_get0_data$FUNC
+    );
+    static final FunctionDescriptor EVP_MD_get0_provider$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EVP_MD_get0_provider$MH = RuntimeHelper.downcallHandle(
+        "EVP_MD_get0_provider",
+        constants$4.EVP_MD_get0_provider$FUNC
+    );
+    static final FunctionDescriptor EVP_MD_fetch$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EVP_MD_fetch$MH = RuntimeHelper.downcallHandle(
+        "EVP_MD_fetch",
+        constants$4.EVP_MD_fetch$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$5.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$5.java
new file mode 100644
index 0000000000..67fb9fc829
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$5.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$5 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$5() {}
+    static final FunctionDescriptor EVP_MD_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EVP_MD_free$MH = RuntimeHelper.downcallHandle(
+        "EVP_MD_free",
+        constants$5.EVP_MD_free$FUNC
+    );
+    static final FunctionDescriptor EVP_PKEY_get_base_id$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EVP_PKEY_get_base_id$MH = RuntimeHelper.downcallHandle(
+        "EVP_PKEY_get_base_id",
+        constants$5.EVP_PKEY_get_base_id$FUNC
+    );
+    static final FunctionDescriptor EVP_PKEY_get_bits$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EVP_PKEY_get_bits$MH = RuntimeHelper.downcallHandle(
+        "EVP_PKEY_get_bits",
+        constants$5.EVP_PKEY_get_bits$FUNC
+    );
+    static final FunctionDescriptor EC_GROUP_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EC_GROUP_free$MH = RuntimeHelper.downcallHandle(
+        "EC_GROUP_free",
+        constants$5.EC_GROUP_free$FUNC
+    );
+    static final FunctionDescriptor EC_GROUP_get_curve_name$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EC_GROUP_get_curve_name$MH = RuntimeHelper.downcallHandle(
+        "EC_GROUP_get_curve_name",
+        constants$5.EC_GROUP_get_curve_name$FUNC
+    );
+    static final FunctionDescriptor EC_KEY_new_by_curve_name$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle EC_KEY_new_by_curve_name$MH = RuntimeHelper.downcallHandle(
+        "EC_KEY_new_by_curve_name",
+        constants$5.EC_KEY_new_by_curve_name$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$6.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$6.java
new file mode 100644
index 0000000000..76f2e37454
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$6.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$6 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$6() {}
+    static final FunctionDescriptor EC_KEY_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle EC_KEY_free$MH = RuntimeHelper.downcallHandle(
+        "EC_KEY_free",
+        constants$6.EC_KEY_free$FUNC
+    );
+    static final FunctionDescriptor DH_new$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle DH_new$MH = RuntimeHelper.downcallHandle(
+        "DH_new",
+        constants$6.DH_new$FUNC
+    );
+    static final FunctionDescriptor DH_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle DH_free$MH = RuntimeHelper.downcallHandle(
+        "DH_free",
+        constants$6.DH_free$FUNC
+    );
+    static final FunctionDescriptor DH_set0_pqg$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle DH_set0_pqg$MH = RuntimeHelper.downcallHandle(
+        "DH_set0_pqg",
+        constants$6.DH_set0_pqg$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_set_flags$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle X509_STORE_set_flags$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_set_flags",
+        constants$6.X509_STORE_set_flags$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get0_untrusted$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get0_untrusted$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get0_untrusted",
+        constants$6.X509_STORE_CTX_get0_untrusted$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$7.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$7.java
new file mode 100644
index 0000000000..8fa95fdb6c
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$7.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$7 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$7() {}
+    static final FunctionDescriptor X509_STORE_add_lookup$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_add_lookup$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_add_lookup",
+        constants$7.X509_STORE_add_lookup$FUNC
+    );
+    static final FunctionDescriptor X509_LOOKUP_hash_dir$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle X509_LOOKUP_hash_dir$MH = RuntimeHelper.downcallHandle(
+        "X509_LOOKUP_hash_dir",
+        constants$7.X509_LOOKUP_hash_dir$FUNC
+    );
+    static final FunctionDescriptor X509_LOOKUP_file$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle X509_LOOKUP_file$MH = RuntimeHelper.downcallHandle(
+        "X509_LOOKUP_file",
+        constants$7.X509_LOOKUP_file$FUNC
+    );
+    static final FunctionDescriptor X509_LOOKUP_ctrl$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_LOOKUP_ctrl$MH = RuntimeHelper.downcallHandle(
+        "X509_LOOKUP_ctrl",
+        constants$7.X509_LOOKUP_ctrl$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get_ex_data$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get_ex_data$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get_ex_data",
+        constants$7.X509_STORE_CTX_get_ex_data$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get_error$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get_error$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get_error",
+        constants$7.X509_STORE_CTX_get_error$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$8.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$8.java
new file mode 100644
index 0000000000..fc018122d2
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$8.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$8 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$8() {}
+    static final FunctionDescriptor X509_STORE_CTX_set_error$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_set_error$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_set_error",
+        constants$8.X509_STORE_CTX_set_error$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get_error_depth$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get_error_depth$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get_error_depth",
+        constants$8.X509_STORE_CTX_get_error_depth$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get_current_cert$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get_current_cert$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get_current_cert",
+        constants$8.X509_STORE_CTX_get_current_cert$FUNC
+    );
+    static final FunctionDescriptor X509_STORE_CTX_get0_current_issuer$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_STORE_CTX_get0_current_issuer$MH = RuntimeHelper.downcallHandle(
+        "X509_STORE_CTX_get0_current_issuer",
+        constants$8.X509_STORE_CTX_get0_current_issuer$FUNC
+    );
+    static final FunctionDescriptor d2i_X509_bio$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle d2i_X509_bio$MH = RuntimeHelper.downcallHandle(
+        "d2i_X509_bio",
+        constants$8.d2i_X509_bio$FUNC
+    );
+    static final FunctionDescriptor X509_free$FUNC = FunctionDescriptor.ofVoid(
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_free$MH = RuntimeHelper.downcallHandle(
+        "X509_free",
+        constants$8.X509_free$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$9.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$9.java
new file mode 100644
index 0000000000..bc99139f11
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/constants$9.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+final class constants$9 {
+
+    // Suppresses default constructor, ensuring non-instantiability.
+    private constants$9() {}
+    static final FunctionDescriptor d2i_X509$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_LONG_LONG$LAYOUT
+    );
+    static final MethodHandle d2i_X509$MH = RuntimeHelper.downcallHandle(
+        "d2i_X509",
+        constants$9.d2i_X509$FUNC
+    );
+    static final FunctionDescriptor i2d_X509$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle i2d_X509$MH = RuntimeHelper.downcallHandle(
+        "i2d_X509",
+        constants$9.i2d_X509$FUNC
+    );
+    static final FunctionDescriptor X509_get_ext_by_NID$FUNC = FunctionDescriptor.of(Constants$root.C_INT$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle X509_get_ext_by_NID$MH = RuntimeHelper.downcallHandle(
+        "X509_get_ext_by_NID",
+        constants$9.X509_get_ext_by_NID$FUNC
+    );
+    static final FunctionDescriptor X509_get_ext$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_INT$LAYOUT
+    );
+    static final MethodHandle X509_get_ext$MH = RuntimeHelper.downcallHandle(
+        "X509_get_ext",
+        constants$9.X509_get_ext$FUNC
+    );
+    static final FunctionDescriptor X509_EXTENSION_get_data$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle X509_EXTENSION_get_data$MH = RuntimeHelper.downcallHandle(
+        "X509_EXTENSION_get_data",
+        constants$9.X509_EXTENSION_get_data$FUNC
+    );
+    static final FunctionDescriptor PEM_read_bio_X509_AUX$FUNC = FunctionDescriptor.of(Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT,
+        Constants$root.C_POINTER$LAYOUT
+    );
+    static final MethodHandle PEM_read_bio_X509_AUX$MH = RuntimeHelper.downcallHandle(
+        "PEM_read_bio_X509_AUX",
+        constants$9.PEM_read_bio_X509_AUX$FUNC
+    );
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_compat_h.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_compat_h.java
new file mode 100644
index 0000000000..499a15c77e
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_compat_h.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+
+/**
+ * Methods used present in older OpenSSL versions but not in the current major version.
+ */
+public class openssl_compat_h  {
+
+    // OpenSSL 1.1 FIPS_mode
+    static final FunctionDescriptor FIPS_mode$FUNC = FunctionDescriptor
+            .of(JAVA_INT);
+    static final MethodHandle FIPS_mode$MH = RuntimeHelper
+            .downcallHandle("FIPS_mode", FIPS_mode$FUNC);
+    public static MethodHandle FIPS_mode$MH() {
+        return RuntimeHelper.requireNonNull(FIPS_mode$MH, "FIPS_mode");
+    }
+    public static int FIPS_mode() {
+        var mh$ = RuntimeHelper.requireNonNull(FIPS_mode$MH, "FIPS_mode");
+        try {
+            return (int) mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+
+    // OpenSSL 1.1 FIPS_mode_set
+    static final FunctionDescriptor FIPS_mode_set$FUNC = FunctionDescriptor
+            .of(JAVA_INT, JAVA_INT);
+    static final MethodHandle FIPS_mode_set$MH = RuntimeHelper
+            .downcallHandle("FIPS_mode_set", FIPS_mode_set$FUNC);
+    public static MethodHandle FIPS_mode_set$MH() {
+        return RuntimeHelper.requireNonNull(FIPS_mode_set$MH, "FIPS_mode_set");
+    }
+    public static int FIPS_mode_set(int r) {
+        var mh$ = RuntimeHelper.requireNonNull(FIPS_mode_set$MH,
+                "FIPS_mode_set");
+        try {
+            return (int) mh$.invokeExact(r);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+
+    // OpenSSL 1.1 EVP_PKEY_base_id
+    static final FunctionDescriptor EVP_PKEY_base_id$FUNC = FunctionDescriptor
+            .of(Constants$root.C_INT$LAYOUT, Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle EVP_PKEY_base_id$MH = RuntimeHelper
+            .downcallHandle("EVP_PKEY_base_id", EVP_PKEY_base_id$FUNC);
+    public static MethodHandle EVP_PKEY_base_id$MH() {
+        return RuntimeHelper.requireNonNull(EVP_PKEY_base_id$MH,
+                "EVP_PKEY_base_id");
+    }
+    public static int EVP_PKEY_base_id(MemorySegment pkey) {
+        var mh$ = EVP_PKEY_base_id$MH();
+        try {
+            return (int) mh$.invokeExact(pkey);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+
+    // OpenSSL 1.1 EVP_PKEY_bits
+    static final FunctionDescriptor EVP_PKEY_bits$FUNC = FunctionDescriptor
+            .of(Constants$root.C_INT$LAYOUT, Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle EVP_PKEY_bits$MH = RuntimeHelper
+            .downcallHandle("EVP_PKEY_bits", EVP_PKEY_bits$FUNC);
+    public static MethodHandle EVP_PKEY_bits$MH() {
+        return RuntimeHelper.requireNonNull(EVP_PKEY_bits$MH, "EVP_PKEY_bits");
+    }
+    public static int EVP_PKEY_bits(MemorySegment pkey) {
+        var mh$ = EVP_PKEY_bits$MH();
+        try {
+            return (int) mh$.invokeExact(pkey);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+
+    // OpenSSL 1.1 SSL_get_peer_certificate
+    static final FunctionDescriptor SSL_get_peer_certificate$FUNC = FunctionDescriptor
+            .of(Constants$root.C_POINTER$LAYOUT,
+                    Constants$root.C_POINTER$LAYOUT);
+    static final MethodHandle SSL_get_peer_certificate$MH = RuntimeHelper
+            .downcallHandle("SSL_get_peer_certificate",
+                    SSL_get_peer_certificate$FUNC);
+    public static MethodHandle SSL_get_peer_certificate$MH() {
+        return RuntimeHelper.requireNonNull(SSL_get_peer_certificate$MH,
+                "SSL_get_peer_certificate");
+    }
+    public static MemorySegment SSL_get_peer_certificate(MemorySegment s) {
+        var mh$ = SSL_get_peer_certificate$MH();
+        try {
+            return (java.lang.foreign.MemorySegment) mh$.invokeExact(s);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+
+}
+
+
diff --git a/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_h.java b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_h.java
new file mode 100644
index 0000000000..ede1a779a5
--- /dev/null
+++ b/modules/openssl-java21/src/main/java/org/apache/tomcat/util/openssl/openssl_h.java
@@ -0,0 +1,3447 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Generated by jextract
+
+package org.apache.tomcat.util.openssl;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+import java.lang.foreign.*;
+import static java.lang.foreign.ValueLayout.*;
+public class openssl_h  {
+
+    public static final OfByte C_CHAR = Constants$root.C_CHAR$LAYOUT;
+    public static final OfShort C_SHORT = Constants$root.C_SHORT$LAYOUT;
+    public static final OfInt C_INT = Constants$root.C_INT$LAYOUT;
+    public static final OfLong C_LONG = Constants$root.C_LONG_LONG$LAYOUT;
+    public static final OfLong C_LONG_LONG = Constants$root.C_LONG_LONG$LAYOUT;
+    public static final OfFloat C_FLOAT = Constants$root.C_FLOAT$LAYOUT;
+    public static final OfDouble C_DOUBLE = Constants$root.C_DOUBLE$LAYOUT;
+    public static final AddressLayout C_POINTER = Constants$root.C_POINTER$LAYOUT;
+    /**
+     * {@snippet :
+     * #define BIO_CLOSE 1
+     * }
+     */
+    public static int BIO_CLOSE() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define BIO_CTRL_RESET 1
+     * }
+     */
+    public static int BIO_CTRL_RESET() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define BIO_FP_READ 2
+     * }
+     */
+    public static int BIO_FP_READ() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define BIO_C_SET_FILENAME 108
+     * }
+     */
+    public static int BIO_C_SET_FILENAME() {
+        return (int)108L;
+    }
+    /**
+     * {@snippet :
+     * #define NID_info_access 177
+     * }
+     */
+    public static int NID_info_access() {
+        return (int)177L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_FILETYPE_PEM 1
+     * }
+     */
+    public static int X509_FILETYPE_PEM() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_L_FILE_LOAD 1
+     * }
+     */
+    public static int X509_L_FILE_LOAD() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_L_ADD_DIR 2
+     * }
+     */
+    public static int X509_L_ADD_DIR() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_OK 0
+     * }
+     */
+    public static int X509_V_OK() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_CRL_HAS_EXPIRED 12
+     * }
+     */
+    public static int X509_V_ERR_CRL_HAS_EXPIRED() {
+        return (int)12L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18
+     * }
+     */
+    public static int X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT() {
+        return (int)18L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19
+     * }
+     */
+    public static int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN() {
+        return (int)19L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20
+     * }
+     */
+    public static int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY() {
+        return (int)20L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21
+     * }
+     */
+    public static int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE() {
+        return (int)21L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_CERT_UNTRUSTED 27
+     * }
+     */
+    public static int X509_V_ERR_CERT_UNTRUSTED() {
+        return (int)27L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_ERR_APPLICATION_VERIFICATION 50
+     * }
+     */
+    public static int X509_V_ERR_APPLICATION_VERIFICATION() {
+        return (int)50L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_FLAG_CRL_CHECK 4
+     * }
+     */
+    public static int X509_V_FLAG_CRL_CHECK() {
+        return (int)4L;
+    }
+    /**
+     * {@snippet :
+     * #define X509_V_FLAG_CRL_CHECK_ALL 8
+     * }
+     */
+    public static int X509_V_FLAG_CRL_CHECK_ALL() {
+        return (int)8L;
+    }
+    /**
+     * {@snippet :
+     * #define PEM_R_NO_START_LINE 108
+     * }
+     */
+    public static int PEM_R_NO_START_LINE() {
+        return (int)108L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL3_VERSION 768
+     * }
+     */
+    public static int SSL3_VERSION() {
+        return (int)768L;
+    }
+    /**
+     * {@snippet :
+     * #define TLS1_VERSION 769
+     * }
+     */
+    public static int TLS1_VERSION() {
+        return (int)769L;
+    }
+    /**
+     * {@snippet :
+     * #define TLS1_1_VERSION 770
+     * }
+     */
+    public static int TLS1_1_VERSION() {
+        return (int)770L;
+    }
+    /**
+     * {@snippet :
+     * #define TLS1_2_VERSION 771
+     * }
+     */
+    public static int TLS1_2_VERSION() {
+        return (int)771L;
+    }
+    /**
+     * {@snippet :
+     * #define TLS1_3_VERSION 772
+     * }
+     */
+    public static int TLS1_3_VERSION() {
+        return (int)772L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_SENT_SHUTDOWN 1
+     * }
+     */
+    public static int SSL_SENT_SHUTDOWN() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_RECEIVED_SHUTDOWN 2
+     * }
+     */
+    public static int SSL_RECEIVED_SHUTDOWN() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_OP_SINGLE_ECDH_USE 0
+     * }
+     */
+    public static int SSL_OP_SINGLE_ECDH_USE() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_OP_SINGLE_DH_USE 0
+     * }
+     */
+    public static int SSL_OP_SINGLE_DH_USE() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_OP_NO_SSLv2 0
+     * }
+     */
+    public static int SSL_OP_NO_SSLv2() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_FLAG_FILE 2
+     * }
+     */
+    public static int SSL_CONF_FLAG_FILE() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_FLAG_SERVER 8
+     * }
+     */
+    public static int SSL_CONF_FLAG_SERVER() {
+        return (int)8L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_FLAG_SHOW_ERRORS 16
+     * }
+     */
+    public static int SSL_CONF_FLAG_SHOW_ERRORS() {
+        return (int)16L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_FLAG_CERTIFICATE 32
+     * }
+     */
+    public static int SSL_CONF_FLAG_CERTIFICATE() {
+        return (int)32L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_TYPE_UNKNOWN 0
+     * }
+     */
+    public static int SSL_CONF_TYPE_UNKNOWN() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_TYPE_FILE 2
+     * }
+     */
+    public static int SSL_CONF_TYPE_FILE() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CONF_TYPE_DIR 3
+     * }
+     */
+    public static int SSL_CONF_TYPE_DIR() {
+        return (int)3L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_SESS_CACHE_OFF 0
+     * }
+     */
+    public static int SSL_SESS_CACHE_OFF() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_SESS_CACHE_SERVER 2
+     * }
+     */
+    public static int SSL_SESS_CACHE_SERVER() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL2_VERSION 2
+     * }
+     */
+    public static int SSL2_VERSION() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_TLSEXT_ERR_OK 0
+     * }
+     */
+    public static int SSL_TLSEXT_ERR_OK() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_TLSEXT_ERR_NOACK 3
+     * }
+     */
+    public static int SSL_TLSEXT_ERR_NOACK() {
+        return (int)3L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CB_HANDSHAKE_DONE 32
+     * }
+     */
+    public static int SSL_CB_HANDSHAKE_DONE() {
+        return (int)32L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_VERIFY_NONE 0
+     * }
+     */
+    public static int SSL_VERIFY_NONE() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_VERIFY_PEER 1
+     * }
+     */
+    public static int SSL_VERIFY_PEER() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2
+     * }
+     */
+    public static int SSL_VERIFY_FAIL_IF_NO_PEER_CERT() {
+        return (int)2L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_ERROR_NONE 0
+     * }
+     */
+    public static int SSL_ERROR_NONE() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_TMP_DH 3
+     * }
+     */
+    public static int SSL_CTRL_SET_TMP_DH() {
+        return (int)3L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_TMP_ECDH 4
+     * }
+     */
+    public static int SSL_CTRL_SET_TMP_ECDH() {
+        return (int)4L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_NUMBER 20
+     * }
+     */
+    public static int SSL_CTRL_SESS_NUMBER() {
+        return (int)20L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_CONNECT 21
+     * }
+     */
+    public static int SSL_CTRL_SESS_CONNECT() {
+        return (int)21L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_CONNECT_GOOD 22
+     * }
+     */
+    public static int SSL_CTRL_SESS_CONNECT_GOOD() {
+        return (int)22L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_CONNECT_RENEGOTIATE 23
+     * }
+     */
+    public static int SSL_CTRL_SESS_CONNECT_RENEGOTIATE() {
+        return (int)23L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_ACCEPT 24
+     * }
+     */
+    public static int SSL_CTRL_SESS_ACCEPT() {
+        return (int)24L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_ACCEPT_GOOD 25
+     * }
+     */
+    public static int SSL_CTRL_SESS_ACCEPT_GOOD() {
+        return (int)25L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_ACCEPT_RENEGOTIATE 26
+     * }
+     */
+    public static int SSL_CTRL_SESS_ACCEPT_RENEGOTIATE() {
+        return (int)26L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_HIT 27
+     * }
+     */
+    public static int SSL_CTRL_SESS_HIT() {
+        return (int)27L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_CB_HIT 28
+     * }
+     */
+    public static int SSL_CTRL_SESS_CB_HIT() {
+        return (int)28L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_MISSES 29
+     * }
+     */
+    public static int SSL_CTRL_SESS_MISSES() {
+        return (int)29L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_TIMEOUTS 30
+     * }
+     */
+    public static int SSL_CTRL_SESS_TIMEOUTS() {
+        return (int)30L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SESS_CACHE_FULL 31
+     * }
+     */
+    public static int SSL_CTRL_SESS_CACHE_FULL() {
+        return (int)31L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_SESS_CACHE_SIZE 42
+     * }
+     */
+    public static int SSL_CTRL_SET_SESS_CACHE_SIZE() {
+        return (int)42L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_GET_SESS_CACHE_SIZE 43
+     * }
+     */
+    public static int SSL_CTRL_GET_SESS_CACHE_SIZE() {
+        return (int)43L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_SESS_CACHE_MODE 44
+     * }
+     */
+    public static int SSL_CTRL_SET_SESS_CACHE_MODE() {
+        return (int)44L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_GET_SESS_CACHE_MODE 45
+     * }
+     */
+    public static int SSL_CTRL_GET_SESS_CACHE_MODE() {
+        return (int)45L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS 59
+     * }
+     */
+    public static int SSL_CTRL_SET_TLSEXT_TICKET_KEYS() {
+        return (int)59L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_CHAIN_CERT 89
+     * }
+     */
+    public static int SSL_CTRL_CHAIN_CERT() {
+        return (int)89L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_MIN_PROTO_VERSION 123
+     * }
+     */
+    public static int SSL_CTRL_SET_MIN_PROTO_VERSION() {
+        return (int)123L;
+    }
+    /**
+     * {@snippet :
+     * #define SSL_CTRL_SET_MAX_PROTO_VERSION 124
+     * }
+     */
+    public static int SSL_CTRL_SET_MAX_PROTO_VERSION() {
+        return (int)124L;
+    }
+    /**
+     * {@snippet :
+     * #define OCSP_RESPONSE_STATUS_SUCCESSFUL 0
+     * }
+     */
+    public static int OCSP_RESPONSE_STATUS_SUCCESSFUL() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define V_OCSP_CERTSTATUS_GOOD 0
+     * }
+     */
+    public static int V_OCSP_CERTSTATUS_GOOD() {
+        return (int)0L;
+    }
+    /**
+     * {@snippet :
+     * #define V_OCSP_CERTSTATUS_REVOKED 1
+     * }
+     */
+    public static int V_OCSP_CERTSTATUS_REVOKED() {
+        return (int)1L;
+    }
+    /**
+     * {@snippet :
+     * #define V_OCSP_CERTSTATUS_UNKNOWN 2
+     * }
+     */
+    public static int V_OCSP_CERTSTATUS_UNKNOWN() {
+        return (int)2L;
+    }
+    public static MethodHandle OPENSSL_sk_num$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.OPENSSL_sk_num$MH,"OPENSSL_sk_num");
+    }
+    /**
+     * {@snippet :
+     * int OPENSSL_sk_num(const OPENSSL_STACK*);
+     * }
+     */
+    public static int OPENSSL_sk_num(MemorySegment x0) {
+        var mh$ = OPENSSL_sk_num$MH();
+        try {
+            return (int)mh$.invokeExact(x0);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle OPENSSL_sk_value$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.OPENSSL_sk_value$MH,"OPENSSL_sk_value");
+    }
+    /**
+     * {@snippet :
+     * void* OPENSSL_sk_value(const OPENSSL_STACK*, int);
+     * }
+     */
+    public static MemorySegment OPENSSL_sk_value(MemorySegment x0, int x1) {
+        var mh$ = OPENSSL_sk_value$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(x0, x1);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle OpenSSL_version_num$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.OpenSSL_version_num$MH,"OpenSSL_version_num");
+    }
+    /**
+     * {@snippet :
+     * unsigned long OpenSSL_version_num();
+     * }
+     */
+    public static long OpenSSL_version_num() {
+        var mh$ = OpenSSL_version_num$MH();
+        try {
+            return (long)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle OpenSSL_version$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.OpenSSL_version$MH,"OpenSSL_version");
+    }
+    /**
+     * {@snippet :
+     * char* OpenSSL_version(int type);
+     * }
+     */
+    public static MemorySegment OpenSSL_version(int type) {
+        var mh$ = OpenSSL_version$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(type);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle CRYPTO_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.CRYPTO_free$MH,"CRYPTO_free");
+    }
+    /**
+     * {@snippet :
+     * void CRYPTO_free(void* ptr, char* file, int line);
+     * }
+     */
+    public static void CRYPTO_free(MemorySegment ptr, MemorySegment file, int line) {
+        var mh$ = CRYPTO_free$MH();
+        try {
+            mh$.invokeExact(ptr, file, line);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_ctrl_pending$MH() {
+        return RuntimeHelper.requireNonNull(constants$0.BIO_ctrl_pending$MH,"BIO_ctrl_pending");
+    }
+    /**
+     * {@snippet :
+     * size_t BIO_ctrl_pending(BIO* b);
+     * }
+     */
+    public static long BIO_ctrl_pending(MemorySegment b) {
+        var mh$ = BIO_ctrl_pending$MH();
+        try {
+            return (long)mh$.invokeExact(b);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_s_file$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_s_file$MH,"BIO_s_file");
+    }
+    /**
+     * {@snippet :
+     * const BIO_METHOD* BIO_s_file();
+     * }
+     */
+    public static MemorySegment BIO_s_file() {
+        var mh$ = BIO_s_file$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_new_file$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_new_file$MH,"BIO_new_file");
+    }
+    /**
+     * {@snippet :
+     * BIO* BIO_new_file(char* filename, char* mode);
+     * }
+     */
+    public static MemorySegment BIO_new_file(MemorySegment filename, MemorySegment mode) {
+        var mh$ = BIO_new_file$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(filename, mode);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_new$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_new$MH,"BIO_new");
+    }
+    /**
+     * {@snippet :
+     * BIO* BIO_new(const BIO_METHOD* type);
+     * }
+     */
+    public static MemorySegment BIO_new(MemorySegment type) {
+        var mh$ = BIO_new$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(type);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_free$MH,"BIO_free");
+    }
+    /**
+     * {@snippet :
+     * int BIO_free(BIO* a);
+     * }
+     */
+    public static int BIO_free(MemorySegment a) {
+        var mh$ = BIO_free$MH();
+        try {
+            return (int)mh$.invokeExact(a);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_read$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_read$MH,"BIO_read");
+    }
+    /**
+     * {@snippet :
+     * int BIO_read(BIO* b, void* data, int dlen);
+     * }
+     */
+    public static int BIO_read(MemorySegment b, MemorySegment data, int dlen) {
+        var mh$ = BIO_read$MH();
+        try {
+            return (int)mh$.invokeExact(b, data, dlen);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_write$MH() {
+        return RuntimeHelper.requireNonNull(constants$1.BIO_write$MH,"BIO_write");
+    }
+    /**
+     * {@snippet :
+     * int BIO_write(BIO* b, void* data, int dlen);
+     * }
+     */
+    public static int BIO_write(MemorySegment b, MemorySegment data, int dlen) {
+        var mh$ = BIO_write$MH();
+        try {
+            return (int)mh$.invokeExact(b, data, dlen);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_ctrl$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BIO_ctrl$MH,"BIO_ctrl");
+    }
+    /**
+     * {@snippet :
+     * long BIO_ctrl(BIO* bp, int cmd, long larg, void* parg);
+     * }
+     */
+    public static long BIO_ctrl(MemorySegment bp, int cmd, long larg, MemorySegment parg) {
+        var mh$ = BIO_ctrl$MH();
+        try {
+            return (long)mh$.invokeExact(bp, cmd, larg, parg);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_s_mem$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BIO_s_mem$MH,"BIO_s_mem");
+    }
+    /**
+     * {@snippet :
+     * const BIO_METHOD* BIO_s_mem();
+     * }
+     */
+    public static MemorySegment BIO_s_mem() {
+        var mh$ = BIO_s_mem$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_s_bio$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BIO_s_bio$MH,"BIO_s_bio");
+    }
+    /**
+     * {@snippet :
+     * const BIO_METHOD* BIO_s_bio();
+     * }
+     */
+    public static MemorySegment BIO_s_bio() {
+        var mh$ = BIO_s_bio$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BIO_new_bio_pair$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BIO_new_bio_pair$MH,"BIO_new_bio_pair");
+    }
+    /**
+     * {@snippet :
+     * int BIO_new_bio_pair(BIO** bio1, size_t writebuf1, BIO** bio2, size_t writebuf2);
+     * }
+     */
+    public static int BIO_new_bio_pair(MemorySegment bio1, long writebuf1, MemorySegment bio2, long writebuf2) {
+        var mh$ = BIO_new_bio_pair$MH();
+        try {
+            return (int)mh$.invokeExact(bio1, writebuf1, bio2, writebuf2);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_new$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BN_new$MH,"BN_new");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_new();
+     * }
+     */
+    public static MemorySegment BN_new() {
+        var mh$ = BN_new$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_set_word$MH() {
+        return RuntimeHelper.requireNonNull(constants$2.BN_set_word$MH,"BN_set_word");
+    }
+    /**
+     * {@snippet :
+     * int BN_set_word(BIGNUM* a, unsigned long w);
+     * }
+     */
+    public static int BN_set_word(MemorySegment a, long w) {
+        var mh$ = BN_set_word$MH();
+        try {
+            return (int)mh$.invokeExact(a, w);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc2409_prime_768$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc2409_prime_768$MH,"BN_get_rfc2409_prime_768");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc2409_prime_768(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc2409_prime_768(MemorySegment bn) {
+        var mh$ = BN_get_rfc2409_prime_768$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc2409_prime_1024$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc2409_prime_1024$MH,"BN_get_rfc2409_prime_1024");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc2409_prime_1024(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc2409_prime_1024(MemorySegment bn) {
+        var mh$ = BN_get_rfc2409_prime_1024$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_1536$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc3526_prime_1536$MH,"BN_get_rfc3526_prime_1536");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_1536(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_1536(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_1536$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_2048$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc3526_prime_2048$MH,"BN_get_rfc3526_prime_2048");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_2048(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_2048(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_2048$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_3072$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc3526_prime_3072$MH,"BN_get_rfc3526_prime_3072");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_3072(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_3072(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_3072$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_4096$MH() {
+        return RuntimeHelper.requireNonNull(constants$3.BN_get_rfc3526_prime_4096$MH,"BN_get_rfc3526_prime_4096");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_4096(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_4096(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_4096$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_6144$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.BN_get_rfc3526_prime_6144$MH,"BN_get_rfc3526_prime_6144");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_6144(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_6144(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_6144$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle BN_get_rfc3526_prime_8192$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.BN_get_rfc3526_prime_8192$MH,"BN_get_rfc3526_prime_8192");
+    }
+    /**
+     * {@snippet :
+     * BIGNUM* BN_get_rfc3526_prime_8192(BIGNUM* bn);
+     * }
+     */
+    public static MemorySegment BN_get_rfc3526_prime_8192(MemorySegment bn) {
+        var mh$ = BN_get_rfc3526_prime_8192$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bn);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle ASN1_STRING_length$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.ASN1_STRING_length$MH,"ASN1_STRING_length");
+    }
+    /**
+     * {@snippet :
+     * int ASN1_STRING_length(const ASN1_STRING* x);
+     * }
+     */
+    public static int ASN1_STRING_length(MemorySegment x) {
+        var mh$ = ASN1_STRING_length$MH();
+        try {
+            return (int)mh$.invokeExact(x);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle ASN1_STRING_get0_data$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.ASN1_STRING_get0_data$MH,"ASN1_STRING_get0_data");
+    }
+    /**
+     * {@snippet :
+     * unsigned char* ASN1_STRING_get0_data(const ASN1_STRING* x);
+     * }
+     */
+    public static MemorySegment ASN1_STRING_get0_data(MemorySegment x) {
+        var mh$ = ASN1_STRING_get0_data$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(x);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EVP_MD_get0_provider$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.EVP_MD_get0_provider$MH,"EVP_MD_get0_provider");
+    }
+    /**
+     * {@snippet :
+     * const OSSL_PROVIDER* EVP_MD_get0_provider(const EVP_MD* md);
+     * }
+     */
+    public static MemorySegment EVP_MD_get0_provider(MemorySegment md) {
+        var mh$ = EVP_MD_get0_provider$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(md);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EVP_MD_fetch$MH() {
+        return RuntimeHelper.requireNonNull(constants$4.EVP_MD_fetch$MH,"EVP_MD_fetch");
+    }
+    /**
+     * {@snippet :
+     * EVP_MD* EVP_MD_fetch(OSSL_LIB_CTX* ctx, char* algorithm, char* properties);
+     * }
+     */
+    public static MemorySegment EVP_MD_fetch(MemorySegment ctx, MemorySegment algorithm, MemorySegment properties) {
+        var mh$ = EVP_MD_fetch$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(ctx, algorithm, properties);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EVP_MD_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EVP_MD_free$MH,"EVP_MD_free");
+    }
+    /**
+     * {@snippet :
+     * void EVP_MD_free(EVP_MD* md);
+     * }
+     */
+    public static void EVP_MD_free(MemorySegment md) {
+        var mh$ = EVP_MD_free$MH();
+        try {
+            mh$.invokeExact(md);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EVP_PKEY_get_base_id$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EVP_PKEY_get_base_id$MH,"EVP_PKEY_get_base_id");
+    }
+    /**
+     * {@snippet :
+     * int EVP_PKEY_get_base_id(const EVP_PKEY* pkey);
+     * }
+     */
+    public static int EVP_PKEY_get_base_id(MemorySegment pkey) {
+        var mh$ = EVP_PKEY_get_base_id$MH();
+        try {
+            return (int)mh$.invokeExact(pkey);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EVP_PKEY_get_bits$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EVP_PKEY_get_bits$MH,"EVP_PKEY_get_bits");
+    }
+    /**
+     * {@snippet :
+     * int EVP_PKEY_get_bits(const EVP_PKEY* pkey);
+     * }
+     */
+    public static int EVP_PKEY_get_bits(MemorySegment pkey) {
+        var mh$ = EVP_PKEY_get_bits$MH();
+        try {
+            return (int)mh$.invokeExact(pkey);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EC_GROUP_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EC_GROUP_free$MH,"EC_GROUP_free");
+    }
+    /**
+     * {@snippet :
+     * void EC_GROUP_free(EC_GROUP* group);
+     * }
+     */
+    public static void EC_GROUP_free(MemorySegment group) {
+        var mh$ = EC_GROUP_free$MH();
+        try {
+            mh$.invokeExact(group);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EC_GROUP_get_curve_name$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EC_GROUP_get_curve_name$MH,"EC_GROUP_get_curve_name");
+    }
+    /**
+     * {@snippet :
+     * int EC_GROUP_get_curve_name(const EC_GROUP* group);
+     * }
+     */
+    public static int EC_GROUP_get_curve_name(MemorySegment group) {
+        var mh$ = EC_GROUP_get_curve_name$MH();
+        try {
+            return (int)mh$.invokeExact(group);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EC_KEY_new_by_curve_name$MH() {
+        return RuntimeHelper.requireNonNull(constants$5.EC_KEY_new_by_curve_name$MH,"EC_KEY_new_by_curve_name");
+    }
+    /**
+     * {@snippet :
+     * EC_KEY* EC_KEY_new_by_curve_name(int nid);
+     * }
+     */
+    public static MemorySegment EC_KEY_new_by_curve_name(int nid) {
+        var mh$ = EC_KEY_new_by_curve_name$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(nid);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle EC_KEY_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.EC_KEY_free$MH,"EC_KEY_free");
+    }
+    /**
+     * {@snippet :
+     * void EC_KEY_free(EC_KEY* key);
+     * }
+     */
+    public static void EC_KEY_free(MemorySegment key) {
+        var mh$ = EC_KEY_free$MH();
+        try {
+            mh$.invokeExact(key);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle DH_new$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.DH_new$MH,"DH_new");
+    }
+    /**
+     * {@snippet :
+     * DH* DH_new();
+     * }
+     */
+    public static MemorySegment DH_new() {
+        var mh$ = DH_new$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle DH_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.DH_free$MH,"DH_free");
+    }
+    /**
+     * {@snippet :
+     * void DH_free(DH* dh);
+     * }
+     */
+    public static void DH_free(MemorySegment dh) {
+        var mh$ = DH_free$MH();
+        try {
+            mh$.invokeExact(dh);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle DH_set0_pqg$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.DH_set0_pqg$MH,"DH_set0_pqg");
+    }
+    /**
+     * {@snippet :
+     * int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g);
+     * }
+     */
+    public static int DH_set0_pqg(MemorySegment dh, MemorySegment p, MemorySegment q, MemorySegment g) {
+        var mh$ = DH_set0_pqg$MH();
+        try {
+            return (int)mh$.invokeExact(dh, p, q, g);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_set_flags$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.X509_STORE_set_flags$MH,"X509_STORE_set_flags");
+    }
+    /**
+     * {@snippet :
+     * int X509_STORE_set_flags(X509_STORE* ctx, unsigned long flags);
+     * }
+     */
+    public static int X509_STORE_set_flags(MemorySegment ctx, long flags) {
+        var mh$ = X509_STORE_set_flags$MH();
+        try {
+            return (int)mh$.invokeExact(ctx, flags);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get0_untrusted$MH() {
+        return RuntimeHelper.requireNonNull(constants$6.X509_STORE_CTX_get0_untrusted$MH,"X509_STORE_CTX_get0_untrusted");
+    }
+    /**
+     * {@snippet :
+     * struct stack_st_X509* X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX* ctx);
+     * }
+     */
+    public static MemorySegment X509_STORE_CTX_get0_untrusted(MemorySegment ctx) {
+        var mh$ = X509_STORE_CTX_get0_untrusted$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(ctx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_add_lookup$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_STORE_add_lookup$MH,"X509_STORE_add_lookup");
+    }
+    /**
+     * {@snippet :
+     * X509_LOOKUP* X509_STORE_add_lookup(X509_STORE* v, X509_LOOKUP_METHOD* m);
+     * }
+     */
+    public static MemorySegment X509_STORE_add_lookup(MemorySegment v, MemorySegment m) {
+        var mh$ = X509_STORE_add_lookup$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(v, m);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_LOOKUP_hash_dir$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_LOOKUP_hash_dir$MH,"X509_LOOKUP_hash_dir");
+    }
+    /**
+     * {@snippet :
+     * X509_LOOKUP_METHOD* X509_LOOKUP_hash_dir();
+     * }
+     */
+    public static MemorySegment X509_LOOKUP_hash_dir() {
+        var mh$ = X509_LOOKUP_hash_dir$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_LOOKUP_file$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_LOOKUP_file$MH,"X509_LOOKUP_file");
+    }
+    /**
+     * {@snippet :
+     * X509_LOOKUP_METHOD* X509_LOOKUP_file();
+     * }
+     */
+    public static MemorySegment X509_LOOKUP_file() {
+        var mh$ = X509_LOOKUP_file$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact();
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_LOOKUP_ctrl$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_LOOKUP_ctrl$MH,"X509_LOOKUP_ctrl");
+    }
+    /**
+     * {@snippet :
+     * int X509_LOOKUP_ctrl(X509_LOOKUP* ctx, int cmd, char* argc, long argl, char** ret);
+     * }
+     */
+    public static int X509_LOOKUP_ctrl(MemorySegment ctx, int cmd, MemorySegment argc, long argl, MemorySegment ret) {
+        var mh$ = X509_LOOKUP_ctrl$MH();
+        try {
+            return (int)mh$.invokeExact(ctx, cmd, argc, argl, ret);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get_ex_data$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_STORE_CTX_get_ex_data$MH,"X509_STORE_CTX_get_ex_data");
+    }
+    /**
+     * {@snippet :
+     * void* X509_STORE_CTX_get_ex_data(const X509_STORE_CTX* ctx, int idx);
+     * }
+     */
+    public static MemorySegment X509_STORE_CTX_get_ex_data(MemorySegment ctx, int idx) {
+        var mh$ = X509_STORE_CTX_get_ex_data$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(ctx, idx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get_error$MH() {
+        return RuntimeHelper.requireNonNull(constants$7.X509_STORE_CTX_get_error$MH,"X509_STORE_CTX_get_error");
+    }
+    /**
+     * {@snippet :
+     * int X509_STORE_CTX_get_error(const X509_STORE_CTX* ctx);
+     * }
+     */
+    public static int X509_STORE_CTX_get_error(MemorySegment ctx) {
+        var mh$ = X509_STORE_CTX_get_error$MH();
+        try {
+            return (int)mh$.invokeExact(ctx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_set_error$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.X509_STORE_CTX_set_error$MH,"X509_STORE_CTX_set_error");
+    }
+    /**
+     * {@snippet :
+     * void X509_STORE_CTX_set_error(X509_STORE_CTX* ctx, int s);
+     * }
+     */
+    public static void X509_STORE_CTX_set_error(MemorySegment ctx, int s) {
+        var mh$ = X509_STORE_CTX_set_error$MH();
+        try {
+            mh$.invokeExact(ctx, s);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get_error_depth$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.X509_STORE_CTX_get_error_depth$MH,"X509_STORE_CTX_get_error_depth");
+    }
+    /**
+     * {@snippet :
+     * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX* ctx);
+     * }
+     */
+    public static int X509_STORE_CTX_get_error_depth(MemorySegment ctx) {
+        var mh$ = X509_STORE_CTX_get_error_depth$MH();
+        try {
+            return (int)mh$.invokeExact(ctx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get_current_cert$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.X509_STORE_CTX_get_current_cert$MH,"X509_STORE_CTX_get_current_cert");
+    }
+    /**
+     * {@snippet :
+     * X509* X509_STORE_CTX_get_current_cert(const X509_STORE_CTX* ctx);
+     * }
+     */
+    public static MemorySegment X509_STORE_CTX_get_current_cert(MemorySegment ctx) {
+        var mh$ = X509_STORE_CTX_get_current_cert$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(ctx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_STORE_CTX_get0_current_issuer$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.X509_STORE_CTX_get0_current_issuer$MH,"X509_STORE_CTX_get0_current_issuer");
+    }
+    /**
+     * {@snippet :
+     * X509* X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX* ctx);
+     * }
+     */
+    public static MemorySegment X509_STORE_CTX_get0_current_issuer(MemorySegment ctx) {
+        var mh$ = X509_STORE_CTX_get0_current_issuer$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(ctx);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle d2i_X509_bio$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.d2i_X509_bio$MH,"d2i_X509_bio");
+    }
+    /**
+     * {@snippet :
+     * X509* d2i_X509_bio(BIO* bp, X509** x509);
+     * }
+     */
+    public static MemorySegment d2i_X509_bio(MemorySegment bp, MemorySegment x509) {
+        var mh$ = d2i_X509_bio$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(bp, x509);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_free$MH() {
+        return RuntimeHelper.requireNonNull(constants$8.X509_free$MH,"X509_free");
+    }
+    /**
+     * {@snippet :
+     * void X509_free(X509* a);
+     * }
+     */
+    public static void X509_free(MemorySegment a) {
+        var mh$ = X509_free$MH();
+        try {
+            mh$.invokeExact(a);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle d2i_X509$MH() {
+        return RuntimeHelper.requireNonNull(constants$9.d2i_X509$MH,"d2i_X509");
+    }
+    /**
+     * {@snippet :
+     * X509* d2i_X509(X509** a, unsigned char** in, long len);
+     * }
+     */
+    public static MemorySegment d2i_X509(MemorySegment a, MemorySegment in, long len) {
+        var mh$ = d2i_X509$MH();
+        try {
+            return (java.lang.foreign.MemorySegment)mh$.invokeExact(a, in, len);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle i2d_X509$MH() {
+        return RuntimeHelper.requireNonNull(constants$9.i2d_X509$MH,"i2d_X509");
+    }
+    /**
+     * {@snippet :
+     * int i2d_X509(const X509* a, unsigned char** out);
+     * }
+     */
+    public static int i2d_X509(MemorySegment a, MemorySegment out) {
+        var mh$ = i2d_X509$MH();
+        try {
+            return (int)mh$.invokeExact(a, out);
+        } catch (Throwable ex$) {
+            throw new AssertionError("should not reach here", ex$);
+        }
+    }
+    public static MethodHandle X509_get_ext_by_NID$MH() {
+        return RuntimeHelper.requireNonNull(constants$9.X509_get_ext_by_NID$MH,"X509_get_ext_by_NID");
+    }
+    /**
+     * {@snippet :
+     * int X509_get_ext_by_NID(const X509* x, int nid, int lastpos);
+     * }
+     */
+    public static int X509_get_ext_by_NID(MemorySegment x, int nid, int lastpos) {
+        var mh$ = X509_get_ext_by_NID$MH();
+        try {
... 2031 lines suppressed ...


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org