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;
}