Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions core/pva/src/main/java/org/epics/pva/common/SecureSockets.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.Principal;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -63,16 +65,18 @@
/** X509 certificates loaded from the keychain mapped by principal name of the certificate */
public static Map<String, X509Certificate> keychain_x509_certificates = new ConcurrentHashMap<>();

/** @param keychain_setting "/path/to/keychain;password"
/** @param keychain_setting "/path/to/keychain", "/path/to/keychain;password",
* or just "/path/to/keychain" with password in a separate *_PWD_FILE
* @param is_server true for server keychain (uses EPICS_PVAS_TLS_KEYCHAIN_PWD_FILE),
* false for client (uses EPICS_PVA_TLS_KEYCHAIN_PWD_FILE)
* @return {@link SSLContext} with 'keystore' and 'truststore' set to content of keystore
* @throws Exception on error
*/
private static SSLContext createContext(final String keychain_setting) throws Exception
private static SSLContext createContext(final String keychain_setting, final boolean is_server) throws Exception

Check warning on line 75 in core/pva/src/main/java/org/epics/pva/common/SecureSockets.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace generic exceptions with specific library exceptions or a custom exception.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RmaDmAfTdVnF5O6vE&open=AZ2RmaDmAfTdVnF5O6vE&pullRequest=3783
{
final String path;
final char[] pass;

// We support the default "" empty as well as actual passwords, but not null for no password
final int sep = keychain_setting.indexOf(';');
if (sep > 0)
{
Expand All @@ -82,7 +86,7 @@
else
{
path = keychain_setting;
pass = "".toCharArray();
pass = readKeychainPassword(is_server);
}

logger.log(Level.FINE, () -> "Loading keychain '" + path + "'");
Expand Down Expand Up @@ -131,20 +135,43 @@
return context;
}

private static char[] readKeychainPassword(final boolean is_server)
{
final String env_name = is_server ? "EPICS_PVAS_TLS_KEYCHAIN_PWD_FILE"
: "EPICS_PVA_TLS_KEYCHAIN_PWD_FILE";
final String pwd_file = PVASettings.get(env_name, "");
if (! pwd_file.isEmpty())
{
try
{
final String password = Files.readString(Path.of(pwd_file)).trim();
logger.log(Level.FINE, () -> "Read keychain password from " + pwd_file);
return password.toCharArray();
}
catch (Exception ex)
{
logger.log(Level.WARNING, "Error reading password file " + pwd_file, ex);

Check warning on line 153 in core/pva/src/main/java/org/epics/pva/common/SecureSockets.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Lambda should be used to defer string concatenation.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AZ2RmaDmAfTdVnF5O6vF&open=AZ2RmaDmAfTdVnF5O6vF&pullRequest=3783
}
}
// Java PKCS12: null skips encrypted sections (loses CA certs).
// Empty array attempts decryption with retry via NUL char fallback.
return new char[0];
}

private static synchronized void initialize() throws Exception
{
if (initialized)
return;

if (! PVASettings.EPICS_PVAS_TLS_KEYCHAIN.isBlank())
{
final SSLContext context = createContext(PVASettings.EPICS_PVAS_TLS_KEYCHAIN);
final SSLContext context = createContext(PVASettings.EPICS_PVAS_TLS_KEYCHAIN, true);
tls_server_sockets = context.getServerSocketFactory();
}

if (! PVASettings.EPICS_PVA_TLS_KEYCHAIN.isBlank())
{
final SSLContext context = createContext(PVASettings.EPICS_PVA_TLS_KEYCHAIN);
final SSLContext context = createContext(PVASettings.EPICS_PVA_TLS_KEYCHAIN, false);
tls_client_sockets = context.getSocketFactory();
}
initialized = true;
Expand Down