private boolean addCertificate()

in java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java [882:1242]


    private boolean addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception {
        int index = getCertificateIndex(certificate);
        // Load Server key and certificate
        if (certificate.getCertificateFile() != null) {
            // Pick right key password
            String keyPassToUse;
            String keyPass = certificate.getCertificateKeyPassword();
            if (keyPass == null) {
                keyPass = certificate.getCertificateKeystorePassword();
            }
            String keyPassFile = certificate.getCertificateKeyPasswordFile();
            if (keyPassFile == null) {
                keyPassFile = certificate.getCertificateKeystorePasswordFile();
            }
            if (keyPassFile != null) {
                try (BufferedReader reader =
                        new BufferedReader(new InputStreamReader(
                                ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(),
                                StandardCharsets.UTF_8))) {
                    keyPassToUse = reader.readLine();
                } catch (IOException e) {
                    log.error(sm.getString("openssl.errorLoadingPassword", keyPassFile), e);
                    return false;
                }
            } else {
                keyPassToUse = keyPass;
            }
            // Set certificate
            byte[] certificateFileBytes;
            try (Resource resource = ConfigFileLoader.getSource().getResource(certificate.getCertificateFile())) {
                certificateFileBytes = resource.getInputStream().readAllBytes();
            } catch (IOException e) {
                log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateFile()), e);
                return false;
            }
            MemorySegment certificateFileBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateFileBytes);
            MemorySegment certificateBIO = BIO_new(BIO_s_mem());
            try {
                if (BIO_write(certificateBIO, certificateFileBytesNative, certificateFileBytes.length) <= 0) {
                    log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                            certificate.getCertificateFile(), OpenSSLLibrary.getLastError()));
                    return false;
                }
                MemorySegment cert;
                MemorySegment key;
                if (certificate.getCertificateFile().endsWith(".pkcs12")) {
                    // Load pkcs12
                    MemorySegment p12 = d2i_PKCS12_bio(certificateBIO, MemorySegment.NULL);
                    if (MemorySegment.NULL.equals(p12)) {
                        log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                certificate.getCertificateFile(), OpenSSLLibrary.getLastError()));
                        return false;
                    }
                    MemorySegment passwordAddress = MemorySegment.NULL;
                    int passwordLength = 0;
                    if (keyPassToUse != null && !keyPassToUse.isEmpty()) {
                        passwordAddress = localArena.allocateFrom(keyPassToUse);
                        passwordLength = (int) (passwordAddress.byteSize() - 1);
                    }
                    if (PKCS12_verify_mac(p12, passwordAddress, passwordLength) <= 0) {
                        // Bad password
                        log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                certificate.getCertificateFile(), OpenSSLLibrary.getLastError()));
                        PKCS12_free(p12);
                        return false;
                    }
                    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.errorLoadingCertificateWithError",
                                certificate.getCertificateFile(), OpenSSLLibrary.getLastError()));
                        PKCS12_free(p12);
                        return false;
                    }
                    PKCS12_free(p12);
                    cert = certPointer.get(ValueLayout.ADDRESS, 0);
                    key = keyPointer.get(ValueLayout.ADDRESS, 0);
                } else {
                    String certificateKeyFileName = (certificate.getCertificateKeyFile() == null)
                            ? certificate.getCertificateFile() : certificate.getCertificateKeyFile();
                    // Load key
                    byte[] certificateKeyFileBytes;
                    try (Resource resource = ConfigFileLoader.getSource().getResource(certificateKeyFileName)) {
                        certificateKeyFileBytes = resource.getInputStream().readAllBytes();
                    } catch (IOException e) {
                        log.error(sm.getString("openssl.errorLoadingCertificate", certificateKeyFileName), e);
                        return false;
                    }
                    MemorySegment certificateKeyFileBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateKeyFileBytes);
                    MemorySegment keyBIO = BIO_new(BIO_s_mem());
                    try {
                        if (BIO_write(keyBIO, certificateKeyFileBytesNative, certificateKeyFileBytes.length) <= 0) {
                            log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                    certificateKeyFileName, OpenSSLLibrary.getLastError()));
                            return false;
                        }
                        key = MemorySegment.NULL;
                        for (int i = 0; i < 3; i++) {
                            key = PEM_read_bio_PrivateKey(keyBIO, MemorySegment.NULL,
                                    pem_password_cb.allocate(new PasswordCallback(keyPassToUse), contextArena),
                                    MemorySegment.NULL);
                            if (!MemorySegment.NULL.equals(key)) {
                                break;
                            }
                            BIO_reset(keyBIO);
                        }
                    } finally {
                        BIO_free(keyBIO);
                    }
                    if (MemorySegment.NULL.equals(key)) {
                        if (!MemorySegment.NULL.equals(OpenSSLLibrary.enginePointer)) {
                            // This needs a real file
                            key = ENGINE_load_private_key(OpenSSLLibrary.enginePointer,
                                    localArena.allocateFrom(SSLHostConfig.adjustRelativePath(certificateKeyFileName)),
                                    MemorySegment.NULL, MemorySegment.NULL);
                        }
                    }
                    if (MemorySegment.NULL.equals(key)) {
                        log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                certificateKeyFileName, OpenSSLLibrary.getLastError()));
                        return false;
                    }
                    // Load certificate
                    cert = PEM_read_bio_X509_AUX(certificateBIO, MemorySegment.NULL,
                            pem_password_cb.allocate(new PasswordCallback(keyPassToUse), contextArena),
                            MemorySegment.NULL);
                    if (MemorySegment.NULL.equals(cert) &&
                            // EOF is accepted, then try again
                            ((ERR_peek_last_error() & ERR_REASON_MASK()) == PEM_R_NO_START_LINE())) {
                        ERR_clear_error();
                        BIO_reset(certificateBIO);
                        cert = d2i_X509_bio(certificateBIO, MemorySegment.NULL);
                    }
                    if (MemorySegment.NULL.equals(cert)) {
                        log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                certificate.getCertificateFile(), OpenSSLLibrary.getLastError()));
                        return false;
                    }
                }
                if (SSL_CTX_use_certificate(state.sslCtx, cert) <= 0) {
                    logLastError("openssl.errorLoadingCertificate");
                    return false;
                }
                if (SSL_CTX_use_PrivateKey(state.sslCtx, key) <= 0) {
                    logLastError("openssl.errorLoadingPrivateKey");
                    return false;
                }
                if (SSL_CTX_check_private_key(state.sslCtx) <= 0) {
                    logLastError("openssl.errorPrivateKeyCheck");
                    return false;
                }
                // Try to read DH parameters from the (first) SSLCertificateFile
                if (index == SSL_AIDX_RSA) {
                    BIO_reset(certificateBIO);
                    if (!openssl_h_Compatibility.BORINGSSL) {
                        if (!openssl_h_Compatibility.OPENSSL3) {
                            var dh = PEM_read_bio_DHparams(certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                            if (!MemorySegment.NULL.equals(dh)) {
                                SSL_CTX_set_tmp_dh(state.sslCtx, dh);
                                DH_free(dh);
                            }
                        } else {
                            var pkey = PEM_read_bio_Parameters(certificateBIO, MemorySegment.NULL);
                            if (!MemorySegment.NULL.equals(pkey)) {
                                int numBits = EVP_PKEY_get_bits(pkey);
                                if (SSL_CTX_set0_tmp_dh_pkey(state.sslCtx, pkey) <= 0) {
                                    EVP_PKEY_free(pkey);
                                } else {
                                    log.debug(sm.getString("openssl.setCustomDHParameters", Integer.valueOf(numBits), certificate.getCertificateFile()));
                                }
                            } else {
                                String errMessage = OpenSSLLibrary.getLastError();
                                if (errMessage != null) {
                                    log.debug(sm.getString("openssl.errorReadingPEMParameters", errMessage, certificate.getCertificateFile()));
                                }
                                SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_DH_AUTO(), 1, MemorySegment.NULL);
                            }
                        }
                    }
                }
                // Similarly, try to read the ECDH curve name from SSLCertificateFile...
                BIO_reset(certificateBIO);
                if (!openssl_h_Compatibility.BORINGSSL) {
                    if (!openssl_h_Compatibility.OPENSSL3) {
                        var ecparams = PEM_read_bio_ECPKParameters(certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                        if (!MemorySegment.NULL.equals(ecparams)) {
                            int nid = EC_GROUP_get_curve_name(ecparams);
                            var eckey = EC_KEY_new_by_curve_name(nid);
                            SSL_CTX_set_tmp_ecdh(state.sslCtx, eckey);
                            EC_KEY_free(eckey);
                            EC_GROUP_free(ecparams);
                        }
                        // Set callback for DH parameters
                        SSL_CTX_set_tmp_dh_callback(state.sslCtx, SSL_CTX_set_tmp_dh_callback$dh.allocate(new TmpDHCallback(), contextArena));
                    } else {
                        var ecparams = PEM_ASN1_read_bio(d2i_ECPKParameters$SYMBOL(),
                                PEM_STRING_ECPARAMETERS(), certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                        if (!MemorySegment.NULL.equals(ecparams)) {
                            int curveNid = EC_GROUP_get_curve_name(ecparams);
                            var curveNidAddress = localArena.allocateFrom(ValueLayout.JAVA_INT, curveNid);
                            if (SSL_CTX_set1_groups(state.sslCtx, curveNidAddress, 1) <= 0) {
                                curveNid = 0;
                            }
                            if (log.isDebugEnabled()) {
                                log.debug(sm.getString("openssl.setECDHCurve", Integer.valueOf(curveNid),
                                        certificate.getCertificateFile()));
                            }
                            EC_GROUP_free(ecparams);
                        }
                    }
                }
                // Set certificate chain file
                if (certificate.getCertificateChainFile() != null) {
                    byte[] certificateChainBytes;
                    try (Resource resource = ConfigFileLoader.getSource().getResource(certificate.getCertificateChainFile())) {
                        certificateChainBytes = resource.getInputStream().readAllBytes();
                    } catch (IOException e) {
                        log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateChainFile()), e);
                        return false;
                    }
                    MemorySegment certificateChainBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateChainBytes);
                    MemorySegment certificateChainBIO = BIO_new(BIO_s_mem());
                    try {
                        if (BIO_write(certificateChainBIO, certificateChainBytesNative, certificateChainBytes.length) <= 0) {
                            log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                    certificate.getCertificateChainFile(), OpenSSLLibrary.getLastError()));
                            return false;
                        }
                        MemorySegment certChainEntry =
                                PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                        while (!MemorySegment.NULL.equals(certChainEntry)) {
                            if (SSL_CTX_add0_chain_cert(state.sslCtx, certChainEntry) <= 0) {
                                log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                        certificate.getCertificateChainFile(), OpenSSLLibrary.getLastError()));
                            }
                            certChainEntry =
                                    PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                        }
                        // EOF is accepted, otherwise log an error
                        if ((ERR_peek_last_error() & ERR_REASON_MASK()) == PEM_R_NO_START_LINE()) {
                            ERR_clear_error();
                        } else {
                            log.error(sm.getString("openssl.errorLoadingCertificateWithError",
                                    certificate.getCertificateChainFile(), OpenSSLLibrary.getLastError()));
                        }
                    } finally {
                        BIO_free(certificateChainBIO);
                    }
                }
                // Set revocation
                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.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile()));
                    if (X509_LOOKUP_load_file(x509Lookup, certificateRevocationListFileNative,
                            X509_FILETYPE_PEM()) <= 0) {
                        log.error(sm.getString("openssl.errorLoadingCertificateRevocationListWithError",
                                sslHostConfig.getCertificateRevocationListFile(), OpenSSLLibrary.getLastError()));
                    }
                }
                if (sslHostConfig.getCertificateRevocationListPath() != null) {
                    MemorySegment x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_hash_dir());
                    var certificateRevocationListPathNative =
                            localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath()));
                    if (X509_LOOKUP_add_dir(x509Lookup, certificateRevocationListPathNative,
                            X509_FILETYPE_PEM()) <= 0) {
                        log.error(sm.getString("openssl.errorLoadingCertificateRevocationListWithError",
                                sslHostConfig.getCertificateRevocationListPath(), OpenSSLLibrary.getLastError()));
                    }
                }
                X509_STORE_set_flags(certificateStore, X509_V_FLAG_CRL_CHECK() | X509_V_FLAG_CRL_CHECK_ALL());
            } finally {
                BIO_free(certificateBIO);
            }
        } 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);
            }
            String encodedKey =
                BEGIN_KEY +
                Base64.getMimeEncoder(64, new byte[]{'\n'}).encodeToString(x509KeyManager.getPrivateKey(alias).getEncoded()) +
                END_KEY;
            var rawCertificate = localArena.allocateFrom(ValueLayout.JAVA_BYTE, chain[0].getEncoded());
            var rawCertificatePointer = localArena.allocateFrom(ValueLayout.ADDRESS, rawCertificate);
            var rawKey = localArena.allocateFrom(ValueLayout.JAVA_BYTE, encodedKey.getBytes(StandardCharsets.US_ASCII));
            var x509cert = d2i_X509(MemorySegment.NULL, rawCertificatePointer, rawCertificate.byteSize());
            if (MemorySegment.NULL.equals(x509cert)) {
                logLastError("openssl.errorLoadingCertificate");
                return false;
            }
            MemorySegment keyBIO = BIO_new(BIO_s_mem());
            try {
                BIO_write(keyBIO, rawKey, (int) rawKey.byteSize());
                MemorySegment privateKeyAddress = PEM_read_bio_PrivateKey(keyBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL);
                if (MemorySegment.NULL.equals(privateKeyAddress)) {
                    logLastError("openssl.errorLoadingPrivateKey");
                    return false;
                }
                if (SSL_CTX_use_certificate(state.sslCtx, x509cert) <= 0) {
                    logLastError("openssl.errorLoadingCertificate");
                    return false;
                }
                if (SSL_CTX_use_PrivateKey(state.sslCtx, privateKeyAddress) <= 0) {
                    logLastError("openssl.errorLoadingPrivateKey");
                    return false;
                }
                if (SSL_CTX_check_private_key(state.sslCtx) <= 0) {
                    logLastError("openssl.errorPrivateKeyCheck");
                    return false;
                }
                if (!openssl_h_Compatibility.OPENSSL3) {
                    // Set callback for DH parameters
                    SSL_CTX_set_tmp_dh_callback(state.sslCtx,
                            SSL_CTX_set_tmp_dh_callback$dh.allocate(new TmpDHCallback(), contextArena));
                } else {
                    BIO_reset(keyBIO);
                    var pkey = PEM_read_bio_Parameters(keyBIO, MemorySegment.NULL);
                    if (!MemorySegment.NULL.equals(pkey)) {
                        int numBits = EVP_PKEY_get_bits(pkey);
                        if (SSL_CTX_set0_tmp_dh_pkey(state.sslCtx, pkey) <= 0) {
                            EVP_PKEY_free(pkey);
                        } else {
                            log.debug(sm.getString("openssl.setCustomDHParameters", Integer.valueOf(numBits),
                                    x509KeyManager.toString()));
                        }
                    } else {
                        String errMessage = OpenSSLLibrary.getLastError();
                        if (errMessage != null) {
                            log.debug(sm.getString("openssl.errorReadingPEMParameters", errMessage,
                                    x509KeyManager.toString()));
                        }
                        SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_DH_AUTO(), 1, MemorySegment.NULL);
                    }
                }
                for (int i = 1; i < chain.length; i++) {
                    var rawCertificateChain = localArena.allocateFrom(ValueLayout.JAVA_BYTE, chain[i].getEncoded());
                    var rawCertificateChainPointer = localArena.allocateFrom(ValueLayout.ADDRESS, rawCertificateChain);
                    var x509certChain = d2i_X509(MemorySegment.NULL, rawCertificateChainPointer, rawCertificateChain.byteSize());
                    if (MemorySegment.NULL.equals(x509certChain)) {
                        logLastError("openssl.errorLoadingCertificate");
                        return false;
                    }
                    if (SSL_CTX_add0_chain_cert(state.sslCtx, x509certChain) <= 0) {
                        logLastError("openssl.errorAddingCertificate");
                        return false;
                    }
                }
            } finally {
                BIO_free(keyBIO);
            }
        }
        return true;
    }