GP-6155 Refactor of PKI framework support

This commit is contained in:
ghidra1
2025-12-09 18:56:51 -05:00
parent 52ecdc701a
commit 78729379e4
41 changed files with 1177 additions and 1293 deletions

View File

@@ -38,7 +38,7 @@ import ghidra.GhidraLaunchable;
import ghidra.features.bsim.query.ingest.BSimLaunchable;
import ghidra.framework.*;
import ghidra.framework.client.ClientUtil;
import ghidra.net.ApplicationKeyManagerUtils;
import ghidra.net.PKIUtils;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
@@ -390,7 +390,7 @@ public class BSimControlLaunchable implements GhidraLaunchable {
if (line == null) {
break;
}
if (line.startsWith(ApplicationKeyManagerUtils.BEGIN_CERT)) {
if (line.startsWith(PKIUtils.BEGIN_CERT)) {
return true;
}
}
@@ -557,11 +557,10 @@ public class BSimControlLaunchable implements GhidraLaunchable {
PasswordProtection pp = new PasswordProtection(password);
try {
// TODO: should subjectAlternativeNames be supported?
KeyStore keyStore = ApplicationKeyManagerUtils.createKeyStore(alias, "CN=BSimServer",
365 * 2, null, null, "JKS", null, password);
KeyStore keyStore = PKIUtils.createKeyStore(alias, "CN=BSimServer", 365 * 2, null,
null, "JKS", null, password);
ApplicationKeyManagerUtils.exportX509Certificates(keyStore.getCertificateChain(alias),
certFile);
PKIUtils.exportX509Certificates(keyStore.getCertificateChain(alias), certFile);
Key key = keyStore.getKey(alias, password);
try (FileOutputStream fout = new FileOutputStream(passFile);

View File

@@ -34,7 +34,7 @@ import ghidra.framework.*;
import ghidra.framework.client.ClientUtil;
import ghidra.framework.client.HeadlessClientAuthenticator;
import ghidra.framework.protocol.ghidra.GhidraURL;
import ghidra.net.SSLContextInitializer;
import ghidra.net.DefaultSSLContextInitializer;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
@@ -1111,7 +1111,7 @@ public class BSimLaunchable implements GhidraLaunchable {
Application.initializeApplication(layout, config);
SSLContextInitializer.initialize();
DefaultSSLContextInitializer.initialize();
ghidra.framework.protocol.ghidra.Handler.registerHandler();
ghidra.features.bsim.query.postgresql.Handler.registerHandler();

View File

@@ -4,9 +4,9 @@
* 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.
@@ -20,7 +20,7 @@ import java.util.List;
import generic.jar.ResourceFile;
import ghidra.framework.*;
import ghidra.net.ApplicationTrustManagerFactory;
import ghidra.net.DefaultTrustManagerFactory;
import ghidra.util.classfinder.ClassSearcher;
public class HeadlessBSimApplicationConfiguration extends ApplicationConfiguration {
@@ -46,7 +46,7 @@ public class HeadlessBSimApplicationConfiguration extends ApplicationConfigurati
for (ResourceFile appRoot : Application.getApplicationRootDirectories()) {
File cacertsFile = new File(appRoot.getAbsolutePath(), "cacerts");
if (cacertsFile.isFile()) {
System.setProperty(ApplicationTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
System.setProperty(DefaultTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
cacertsFile.getAbsolutePath());
break;
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -19,10 +19,10 @@ import java.io.*;
import java.util.Date;
import java.util.List;
import generic.hash.HashUtilities;
import generic.lsh.vector.*;
import ghidra.features.bsim.query.description.*;
import ghidra.features.bsim.query.protocol.SimilarityVectorResult;
import ghidra.util.HashUtilities;
import ghidra.xml.XmlPullParser;
public class TestNearestVectorResult extends SimilarityVectorResult {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -19,10 +19,10 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Date;
import generic.hash.HashUtilities;
import ghidra.features.bsim.query.description.ExecutableRecord;
import ghidra.features.bsim.query.description.FunctionDescription;
import ghidra.features.bsim.query.protocol.SimilarityResult;
import ghidra.util.HashUtilities;
public class TestSimilarityResult extends SimilarityResult {
// protected static Random random = new Random();

View File

@@ -401,8 +401,7 @@ public class LibreTranslateStringTranslationService implements StringTranslation
// if possible, unwrap the exception that happened inside the future
Throwable cause = e.getCause();
Msg.error(this, "Error during HTTP request [%s]".formatted(request.uri()), cause);
throw (cause instanceof IOException)
? (IOException) cause
throw (cause instanceof IOException) ? (IOException) cause
: new IOException("Error during HTTP request", cause);
}
finally {

View File

@@ -19,6 +19,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import generic.hash.HashUtilities;
import ghidra.app.plugin.processors.generic.MemoryBlockDefinition;
import ghidra.app.util.Option;
import ghidra.app.util.OptionUtils;
@@ -36,7 +37,6 @@ import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
import ghidra.program.util.DefaultLanguageService;
import ghidra.program.util.GhidraProgramUtilities;
import ghidra.util.HashUtilities;
import ghidra.util.MD5Utilities;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;

View File

@@ -31,6 +31,7 @@ import java.util.Map.Entry;
import org.apache.commons.io.FilenameUtils;
import docking.widgets.OptionDialog;
import generic.hash.HashUtilities;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.fileinfo.FileType;

View File

@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
import org.apache.commons.collections4.map.ReferenceMap;
import generic.hash.HashUtilities;
import ghidra.app.util.bin.*;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -21,7 +21,7 @@ import java.util.List;
import generic.jar.ResourceFile;
import ghidra.GhidraClassLoader;
import ghidra.framework.preferences.Preferences;
import ghidra.net.ApplicationTrustManagerFactory;
import ghidra.net.DefaultTrustManagerFactory;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException;
@@ -88,7 +88,7 @@ public class HeadlessGhidraApplicationConfiguration extends ApplicationConfigura
for (ResourceFile appRoot : Application.getApplicationRootDirectories()) {
File cacertsFile = new File(appRoot.getAbsolutePath(), "cacerts");
if (cacertsFile.isFile()) {
System.setProperty(ApplicationTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
System.setProperty(DefaultTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
cacertsFile.getAbsolutePath());
break;
}

View File

@@ -23,6 +23,7 @@ import org.apache.commons.text.StringEscapeUtils;
import com.google.gson.*;
import generic.hash.HashUtilities;
import ghidra.app.util.bin.ByteProvider;
import ghidra.app.util.bin.ByteProviderWrapper;
import ghidra.formats.gfilesystem.*;
@@ -30,7 +31,6 @@ import ghidra.formats.gfilesystem.annotations.FileSystemInfo;
import ghidra.formats.gfilesystem.crypto.CryptoSession;
import ghidra.formats.gfilesystem.fileinfo.*;
import ghidra.framework.generic.auth.Password;
import ghidra.util.HashUtilities;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -22,7 +22,7 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import ghidra.util.HashUtilities;
import generic.hash.HashUtilities;
/**
* Helper class from providing all the constants required for parsing a CaRT

View File

@@ -4,9 +4,9 @@
* 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.
@@ -22,9 +22,9 @@ import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import generic.hash.HashUtilities;
import ghidra.app.util.bin.BinaryReader;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.util.HashUtilities;
public class CartV1FileTest {
CartV1File cartFile;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -20,6 +20,7 @@ import java.util.*;
import javax.security.auth.x500.X500Principal;
import generic.hash.HashUtilities;
import generic.jar.ResourceFile;
import ghidra.GhidraApplicationLayout;
import ghidra.GhidraLaunchable;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -25,6 +25,7 @@ import javax.security.auth.x500.X500Principal;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import generic.hash.HashUtilities;
import ghidra.framework.remote.User;
import ghidra.framework.store.local.LocalFileSystem;
import ghidra.util.*;

View File

@@ -45,8 +45,8 @@ import generic.random.SecureRandomFactory;
import ghidra.framework.Application;
import ghidra.framework.ApplicationConfiguration;
import ghidra.framework.remote.*;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.SSLContextInitializer;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.net.DefaultSSLContextInitializer;
import ghidra.server.RepositoryManager;
import ghidra.server.UserManager;
import ghidra.server.security.*;
@@ -731,7 +731,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
Application.initializeLogging(serverLogFile, serverLogFile);
// In the absence of module initialization - we must invoke directly
SSLContextInitializer.initialize();
DefaultSSLContextInitializer.initialize();
log = LogManager.getLogger(GhidraServer.class); // init log *after* initializing log system
@@ -752,13 +752,12 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
// Ensure that remote access hostname is properly set for RMI registration
String hostname = initRemoteAccessHostname();
if (ApplicationKeyManagerFactory.getPreferredKeyStore() == null) {
if (DefaultKeyManagerFactory.getPreferredKeyStore() == null) {
// keystore has not been identified - use self-signed certificate
ApplicationKeyManagerFactory
.setDefaultIdentity(new X500Principal("CN=GhidraServer"));
ApplicationKeyManagerFactory.addSubjectAlternativeName(hostname);
DefaultKeyManagerFactory.setDefaultIdentity(new X500Principal("CN=GhidraServer"));
DefaultKeyManagerFactory.addSubjectAlternativeName(hostname);
}
if (!ApplicationKeyManagerFactory.initialize()) {
if (!DefaultKeyManagerFactory.initialize()) {
log.fatal("Failed to initialize PKI/SSL keystore");
System.exit(0);
return;

View File

@@ -50,7 +50,7 @@ public class PKIAuthenticationModule implements AuthenticationModule {
public PKIAuthenticationModule(boolean anonymousAllowed)
throws IOException, CertificateException {
this.anonymousAllowed = anonymousAllowed;
authorities = ApplicationKeyManagerUtils.getTrustedIssuers();
authorities = DefaultTrustManagerFactory.getTrustedIssuers();
if (authorities == null || authorities.length == 0) {
throw new IOException("trusted PKI Certificate Authorities have not been configured");
}
@@ -72,8 +72,8 @@ public class PKIAuthenticationModule implements AuthenticationModule {
try {
byte[] token = TokenGenerator.getNewToken(TOKEN_SIZE);
boolean usingSelfSignedCert =
ApplicationKeyManagerFactory.usingGeneratedSelfSignedCertificate();
SignedToken signedToken = ApplicationKeyManagerUtils
DefaultKeyManagerFactory.usingGeneratedSelfSignedCertificate();
SignedToken signedToken = DefaultKeyManagerFactory
.getSignedToken(usingSelfSignedCert ? null : authorities, token);
sigCb = new SignatureCallback(authorities, token, signedToken.signature);
}
@@ -127,8 +127,8 @@ public class PKIAuthenticationModule implements AuthenticationModule {
}
boolean usingSelfSignedCert =
ApplicationKeyManagerFactory.usingGeneratedSelfSignedCertificate();
if (!ApplicationKeyManagerUtils.isMySignature(usingSelfSignedCert ? null : authorities,
DefaultKeyManagerFactory.usingGeneratedSelfSignedCertificate();
if (!DefaultKeyManagerFactory.isMySignature(usingSelfSignedCert ? null : authorities,
token, sigCb.getServerSignature())) {
throw new FailedLoginException("Invalid Signature callback");
}
@@ -138,8 +138,7 @@ public class PKIAuthenticationModule implements AuthenticationModule {
throw new FailedLoginException("user certificate not provided");
}
ApplicationKeyManagerUtils.validateClient(certChain,
ApplicationKeyManagerUtils.RSA_TYPE);
DefaultTrustManagerFactory.validateClient(certChain, PKIUtils.RSA_TYPE);
byte[] sigBytes = sigCb.getSignature();
if (sigBytes != null) {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -75,9 +75,9 @@ public class SSHAuthenticationModule {
byte[] token = TokenGenerator.getNewToken(TOKEN_SIZE);
try {
boolean usingSelfSignedCert =
ApplicationKeyManagerFactory.usingGeneratedSelfSignedCertificate();
SignedToken signedToken = ApplicationKeyManagerUtils.getSignedToken(
usingSelfSignedCert ? null : ApplicationKeyManagerUtils.getTrustedIssuers(), token);
DefaultKeyManagerFactory.usingGeneratedSelfSignedCertificate();
SignedToken signedToken = DefaultKeyManagerFactory.getSignedToken(
usingSelfSignedCert ? null : DefaultTrustManagerFactory.getTrustedIssuers(), token);
list.add(new SSHSignatureCallback(token, signedToken.signature));
}
catch (Exception e) {
@@ -88,9 +88,9 @@ public class SSHAuthenticationModule {
public boolean hasSignedSSHCallback(Callback[] callbacks) {
if (callbacks != null) {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof SSHSignatureCallback) {
SSHSignatureCallback sshCb = (SSHSignatureCallback) callbacks[i];
for (Callback callback : callbacks) {
if (callback instanceof SSHSignatureCallback) {
SSHSignatureCallback sshCb = (SSHSignatureCallback) callback;
return sshCb.isSigned();
}
}
@@ -154,12 +154,12 @@ public class SSHAuthenticationModule {
NameCallback nameCb = null;
SSHSignatureCallback sshCb = null;
if (callbacks != null) {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
nameCb = (NameCallback) callbacks[i];
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
nameCb = (NameCallback) callback;
}
if (callbacks[i] instanceof SSHSignatureCallback) {
sshCb = (SSHSignatureCallback) callbacks[i];
if (callback instanceof SSHSignatureCallback) {
sshCb = (SSHSignatureCallback) callback;
}
}
}
@@ -197,9 +197,9 @@ public class SSHAuthenticationModule {
boolean isValid = false;
try {
boolean usingSelfSignedCert =
ApplicationKeyManagerFactory.usingGeneratedSelfSignedCertificate();
isValid = ApplicationKeyManagerUtils.isMySignature(
usingSelfSignedCert ? null : ApplicationKeyManagerUtils.getTrustedIssuers(), token,
DefaultKeyManagerFactory.usingGeneratedSelfSignedCertificate();
isValid = DefaultKeyManagerFactory.isMySignature(
usingSelfSignedCert ? null : DefaultTrustManagerFactory.getTrustedIssuers(), token,
sshCb.getServerSignature());
}
catch (Exception e) {

View File

@@ -155,10 +155,10 @@ public class HttpSymbolServer extends AbstractSymbolServer implements MutableTru
@Override
public boolean exists(String filename, TaskMonitor monitor) {
try {
HttpRequest request = request(filename)
.timeout(Duration.ofMillis(HTTP_REQUEST_TIMEOUT_MS))
.method("HEAD", BodyPublishers.noBody())
.build();
HttpRequest request =
request(filename).timeout(Duration.ofMillis(HTTP_REQUEST_TIMEOUT_MS))
.method("HEAD", BodyPublishers.noBody())
.build();
Msg.debug(this,
logPrefix() + ": Checking exist for [" + filename + "]: " + request.toString());
@@ -182,8 +182,7 @@ public class HttpSymbolServer extends AbstractSymbolServer implements MutableTru
monitor.setMessage("Connecting to " + serverURI);
HttpRequest request = request(filename).GET().build();
Msg.debug(this,
logPrefix() + ": Getting file [" + filename + "]: " + request.toString());
Msg.debug(this, logPrefix() + ": Getting file [" + filename + "]: " + request.toString());
CompletableFuture<HttpResponse<InputStream>> futureResponse =
HttpClients.getHttpClient().sendAsync(request, BodyHandlers.ofInputStream());
CancelledListener l = () -> futureResponse.cancel(true);
@@ -215,8 +214,7 @@ public class HttpSymbolServer extends AbstractSymbolServer implements MutableTru
// if possible, unwrap the exception that happened inside the future
Throwable cause = e.getCause();
Msg.error(this, "Error during HTTP get", cause);
throw (cause instanceof IOException)
? (IOException) cause
throw (cause instanceof IOException) ? (IOException) cause
: new IOException("Error during HTTP get", cause);
}
finally {

View File

@@ -26,6 +26,7 @@ import java.util.Hashtable;
import javax.security.auth.callback.*;
import javax.security.auth.login.LoginException;
import generic.hash.HashUtilities;
import ghidra.framework.model.ServerInfo;
import ghidra.framework.remote.*;
import ghidra.framework.remote.security.SSHKeyManager;
@@ -444,7 +445,7 @@ public class ClientUtil {
static void processSignatureCallback(String serverName, SignatureCallback sigCb)
throws IOException {
try {
SignedToken signedToken = ApplicationKeyManagerUtils
SignedToken signedToken = DefaultKeyManagerFactory
.getSignedToken(sigCb.getRecognizedAuthorities(), sigCb.getToken());
sigCb.sign(signedToken.certChain, signedToken.signature);
Msg.info(ClientUtil.class, "PKI Authenticating to " + serverName + " as user '" +

View File

@@ -27,7 +27,7 @@ import org.apache.commons.lang3.StringUtils;
import ghidra.framework.remote.AnonymousCallback;
import ghidra.framework.remote.SSHSignatureCallback;
import ghidra.framework.remote.security.SSHKeyManager;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.util.Msg;
/**
@@ -169,7 +169,7 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
}
catch (InvalidKeyException e) { // keyfile is not a valid SSH private key format
// does not appear to be an SSH private key - try PKI keystore parse
if (ApplicationKeyManagerFactory.setKeyStore(keystorePath, false)) {
if (DefaultKeyManagerFactory.setDefaultKeyStore(keystorePath, false)) {
success = true;
Msg.info(HeadlessClientAuthenticator.class,
"Loaded PKI keystore: " + keystorePath);

View File

@@ -23,7 +23,7 @@ import javax.security.auth.callback.*;
import ghidra.framework.remote.AnonymousCallback;
import ghidra.framework.remote.SSHSignatureCallback;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.DefaultKeyManagerFactory;
/**
* <code>PasswordClientAuthenticator</code> provides a fixed username/password
@@ -38,7 +38,7 @@ import ghidra.net.ApplicationKeyManagerFactory;
* for accessing SSH keys or server password authentication. In such headless situations,
* the PKI certificate path/password should be specified via a property since it is unlikely
* that the same password will apply.
* @see ApplicationKeyManagerFactory
* @see DefaultKeyManagerFactory
*/
public class PasswordClientAuthenticator implements ClientAuthenticator {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -486,7 +486,7 @@ public class RepositoryServerAdapter {
* @throws IOException if user data can't be written to file
* @throws NotConnectedException if server connection is down (user already informed)
* @see ghidra.framework.remote.RemoteRepositoryServerHandle#setPassword(char[])
* @see ghidra.util.HashUtilities#getSaltedHash(String, char[]) HashUtilities.getSaltedHash("SHA-256", char[])
* @see generic.hash.HashUtilities#getSaltedHash(String, char[]) HashUtilities.getSaltedHash("SHA-256", char[])
*/
public synchronized boolean setPassword(char[] saltedSHA256PasswordHash)
throws IOException, NotConnectedException {

View File

@@ -38,7 +38,7 @@ import javax.security.auth.login.LoginException;
import ghidra.framework.Application;
import ghidra.framework.model.ServerInfo;
import ghidra.framework.remote.*;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.*;
@@ -130,7 +130,7 @@ class ServerConnectTask extends Task {
private static boolean isSSLHandshakeCancelled(SSLHandshakeException e) throws IOException {
if (e.getMessage().indexOf("bad_certificate") > 0) {
if (ApplicationKeyManagerFactory.getPreferredKeyStore() == null) {
if (DefaultKeyManagerFactory.getPreferredKeyStore() == null) {
throw new IOException("User PKI Certificate not installed", e);
}
// assume user cancelled connect attempt when prompted for cert password
@@ -291,13 +291,12 @@ class ServerConnectTask extends Task {
// if anonymous access allowed, let server validate certificate
// first and assume anonymous access if user unknown but cert is valid
if (!ApplicationKeyManagerFactory.initialize()) {
if (!DefaultKeyManagerFactory.initialize()) {
throw new IOException(
"Client PKI certificate has not been installed");
}
if (ApplicationKeyManagerFactory
.usingGeneratedSelfSignedCertificate()) {
if (DefaultKeyManagerFactory.usingGeneratedSelfSignedCertificate()) {
Msg.warn(this,
"Server connect - client is using self-signed PKI certificate");
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -104,7 +104,7 @@ public interface RepositoryServerHandle {
* @param saltedSHA256PasswordHash SHA256 salted password hash
* @return true if password changed
* @throws IOException if an IO error occurs
* @see ghidra.util.HashUtilities#getSaltedHash(String, char[]) HashUtilities.getSaltedHash("SHA-256", char[])
* @see generic.hash.HashUtilities#getSaltedHash(String, char[]) HashUtilities.getSaltedHash("SHA-256", char[])
*/
boolean setPassword(char[] saltedSHA256PasswordHash) throws IOException;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -18,6 +18,8 @@ package ghidra.util;
import java.io.*;
import java.util.List;
import generic.hash.HashUtilities;
public class MD5Utilities {
public static final int SALT_LENGTH = HashUtilities.SALT_LENGTH;
@@ -54,11 +56,7 @@ public class MD5Utilities {
* returned as a prefix to the hash
*/
public static char[] getSaltedMD5Hash(char[] msg) {
char[] salt = new char[HashUtilities.SALT_LENGTH];
for (int i = 0; i < salt.length; i++) {
salt[i] = HashUtilities.getRandomLetterOrDigit();
}
return getSaltedMD5Hash(salt, msg);
return HashUtilities.getSaltedHash(HashUtilities.MD5_ALGORITHM, msg);
}
/**

View File

@@ -4,16 +4,16 @@
* 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.
*/
package ghidra.util;
package generic.hash;
import java.io.*;
import java.security.MessageDigest;
@@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.List;
import generic.random.SecureRandomFactory;
import ghidra.util.NumericUtilities;
public class HashUtilities {

View File

@@ -4,9 +4,9 @@
* 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.
@@ -15,74 +15,32 @@
*/
package ghidra.net;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ssl.*;
import javax.security.auth.x500.X500Principal;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.preferences.Preferences;
import generic.hash.HashUtilities;
import ghidra.security.KeyStorePasswordProvider;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
/**
* <code>ApplicationKeyManagerFactory</code> provides application keystore management
* functionality and the ability to generate X509KeyManager's for use with an SSLContext
* or other PKI related operations. Access to keystore data (other than keystore path)
* is restricted to package access. Certain public operations are exposed via the
* {@link ApplicationKeyManagerUtils} class.
* {@link ApplicationKeyManagerFactory} provides a factory for using and caching X509 keystores.
*/
public class ApplicationKeyManagerFactory {
/**
* Keystore path system property or user preference. Setting the system
* property will take precedence over the user preference.
*/
public static final String KEYSTORE_PATH_PROPERTY = "ghidra.keystore";
/**
* Password system property may be set. If set, this password will be used
* when accessing the keystore before attempting to use <code>customPasswordProvider</code>
* if it has been set.
*/
public static final String KEYSTORE_PASSWORD_PROPERTY = "ghidra.password";
public static final String DEFAULT_PASSWORD = "changeme";
private static final int SELF_SIGNED_DURATION_DAYS = 2 * 365; // 2-years
private static KeyStorePasswordProvider customPasswordProvider;
private static X500Principal defaultIdentity;
private static List<String> subjectAlternativeNames;
private static ApplicationKeyManagerFactory instance;
// X509KeyManager cached keyed by file hash concatenated with its canonical path
private static HashMap<String, X509KeyManager> keyStoreCache = new HashMap<>();
/**
* Get ApplicationKeyManager singleton
* @return application X509KeyManager
*/
static synchronized ApplicationKeyManagerFactory getInstance() {
if (instance == null) {
instance = new ApplicationKeyManagerFactory();
}
return instance;
}
/**
* get the single key manager instance associated with the factory.
* @return key manager instance
*/
private static ApplicationKeyManager getKeyManagerWrapper() {
return getInstance().keyManagerWrapper;
private ApplicationKeyManagerFactory() {
// no construct
}
/**
@@ -94,220 +52,49 @@ public class ApplicationKeyManagerFactory {
}
/**
* Prune path to trim leading and trailing white space. A null will be
* returned if the pruned path is null or the empty string.
*
* @param path
* @return pruned path or null if path was null or pruned path was the empty
* string
* Clear all cached key managers.
* NOTE: This is primarily intended for test use only.
*/
private static String prunePath(String path) {
if (path != null) {
path = path.trim();
if (path.length() == 0) {
path = null;
}
}
return path;
public static synchronized void clearKeyManagerCache() {
keyStoreCache.clear();
}
/**
* Set user keystore file path (e.g., certificate file with private key).
* This method will have no effect if the keystore had been set via the system
* property and an error will be displayed. Otherwise, the keystore will
* be updated and the key manager re-initialized. The user preference will be
* updated unless a failure occurred while attempting to open the keystore.
* This change will take immediate effect for the current executing application,
* however, it may still be superseded by a system property setting when running
* the application in the future. See {@link #getKeyStore()}.
* @param path keystore file path or null to clear current key store and preference.
* @param savePreference if true will be saved as user preference
* @return true if successful else false if error occured (see log).
*/
public static synchronized boolean setKeyStore(String path, boolean savePreference) {
if (System.getProperty(KEYSTORE_PATH_PROPERTY) != null) {
Msg.showError(ApplicationKeyManagerFactory.class, null, "Set KeyStore Failed",
"PKI KeyStore was set via system property and can not be changed");
return false;
}
path = prunePath(path);
try {
boolean keyInitialized = getKeyManagerWrapper().init(path);
if (savePreference && (path == null || keyInitialized)) {
Preferences.setProperty(KEYSTORE_PATH_PROPERTY, path);
Preferences.store();
}
return keyInitialized;
}
catch (CancelledException e) {
// ignore - keystore left unchanged
return false;
}
}
/**
* Get the keystore path associated with the active key manager or the
* preferred keystore path if not yet initialized.
*/
public static synchronized String getKeyStore() {
return getKeyManagerWrapper().getKeyStore();
}
/**
* If the system property <i>ghidra.keystore</i> takes precedence in establishing
* the keystore. If using a GUI and the system property has not been set, the
* user preference with the same name will be used.
* @return active keystore path or null if currently not running with a keystore or
* one has not been set.
*/
public static synchronized String getPreferredKeyStore() {
String path = prunePath(System.getProperty(KEYSTORE_PATH_PROPERTY));
if (path == null && !SystemUtilities.isInHeadlessMode()) {
path = prunePath(Preferences.getProperty(KEYSTORE_PATH_PROPERTY));
}
return path;
}
/**
* Determine if active key manager is utilizing a generated self-signed certificate.
* @return true if using self-signed certificate.
*/
public static synchronized boolean usingGeneratedSelfSignedCertificate() {
return getKeyManagerWrapper().usingGeneratedSelfSignedCertificate();
}
/**
* Set the default self-signed principal identity to be used during initialization
* if no keystore defined. Current application key manager will be invalidated.
* (NOTE: this is intended for server use only when client will not be performing
* CA validation).
* @param identity if not null and a KeyStore path has not be set, this
* identity will be used to generate a self-signed certificate and private key
*/
public synchronized static void setDefaultIdentity(X500Principal identity) {
defaultIdentity = identity;
getKeyManagerWrapper().invalidateKey();
}
/**
* Add the optional self-signed subject alternative name to be used during initialization
* if no keystore defined. Current application key manager will be invalidated.
* (NOTE: this is intended for server use only when client will not be performing
* CA validation).
* @param subjectAltName name to be added to the current list of alternative subject names.
* A null value will clear all names currently set.
* name will be used to generate a self-signed certificate and private key
*/
public synchronized static void addSubjectAlternativeName(String subjectAltName) {
if (subjectAltName == null) {
subjectAlternativeNames = null;
}
else {
if (subjectAlternativeNames == null) {
subjectAlternativeNames = new ArrayList<>();
}
subjectAlternativeNames.add(subjectAltName);
}
getKeyManagerWrapper().invalidateKey();
}
/**
* Get the current list of subject alternative names to be used for a self-signed certificate
* if no keystore defined.
* @return list of subject alternative names to be used for a self-signed certificate
* if no keystore defined.
*/
public synchronized static List<String> getSubjectAlternativeName() {
return Collections.unmodifiableList(subjectAlternativeNames);
}
/**
* Initialize key manager if needed. Doing this explicitly independent of an SSL connection
* allows application to bail before initiating connection. This will get handshake failure
* if user forgets keystore password or other keystore problem.
* @return true if key manager initialized, otherwise false
*/
public synchronized static boolean initialize() {
try {
return getKeyManagerWrapper().init();
}
catch (CancelledException e) {
return false;
}
}
/**
* Invalidate the key managers associated with this factory
*/
public synchronized static void invalidateKeyManagers() {
getKeyManagerWrapper().invalidateKey();
}
// Factory maintains a single X509 key manager
private ApplicationKeyManager keyManagerWrapper = new ApplicationKeyManager();
/**
* <code>ApplicationKeyManagerFactory</code> constructor
*/
private ApplicationKeyManagerFactory() {
}
/**
* Get key managers
* @return key managers
*/
KeyManager[] getKeyManagers() {
return new KeyManager[] { keyManagerWrapper };
}
/**
* <code>ProtectedKeyStoreData</code> provides a container for a keystore
* which has been successfully accessed using the specified password.
*/
private static class ProtectedKeyStoreData {
KeyStore keyStore;
char[] password;
ProtectedKeyStoreData(KeyStore keyStore, char[] password) {
this.keyStore = keyStore;
this.password = password != null ? password.clone() : null;
}
/**
* Dispose this keystore data wrapper and the retained password
*/
void dispose() {
if (password != null) {
Arrays.fill(password, ' ');
}
keyStore = null;
}
@Override
protected void finalize() throws Throwable {
dispose();
super.finalize();
}
}
/**
* Get protected keystore data for specified keystorePath. Caller is responsible for
* properly disposing returned object.
* Get key manager for specified JKS or PKCS12 keystore file path. The user may be prompted
* for a password if required which will block the invocation of this synchronized method.
* If successfully opened, the resulting key manager instance will be cached for subsequent
* re-use of the same keystore.
*
* @param keystorePath protected keystore path
* @return protected keystore data
* @param defaultPasswd default password (e.g., supplied by property) or null
* @return key manager
* @throws CancelledException password entry was cancelled by user
* @throws KeyStoreException error occurred opening/processing keystore
*/
private static ProtectedKeyStoreData getProtectedKeyStoreData(String keystorePath)
throws CancelledException, KeyStoreException {
public synchronized static X509KeyManager getKeyManager(String keystorePath,
String defaultPasswd) throws CancelledException, KeyStoreException {
Msg.info(ApplicationKeyManagerFactory.class, "Using certificate keystore: " + keystorePath);
String hashKey;
String keystorePwd = System.getProperty(KEYSTORE_PASSWORD_PROPERTY);
try {
if (PKIUtils.detectKeyStoreType(keystorePath) == null) {
throw new KeyStoreException("Unsupported PKI key store file type: " + keystorePath);
}
// Obtain canonical path for cache use
File keystoreFile = new File(keystorePath);
keystorePath = (keystoreFile).getCanonicalPath();
hashKey = HashUtilities.getHash(HashUtilities.MD5_ALGORITHM, keystoreFile) + "_" +
keystorePath;
X509KeyManager keyManager = keyStoreCache.get(hashKey);
if (keyManager != null) {
return keyManager;
}
}
catch (IOException e) {
throw new KeyStoreException("Failed to examine keystore: " + keystorePath, e);
}
int tryCount = 0;
@@ -319,20 +106,12 @@ public class ApplicationKeyManagerFactory {
// try no password first
}
else if (tryCount == 1) {
// try default password
Msg.debug(ApplicationKeyManagerFactory.class,
"Attempting to load keystore without password...");
password = DEFAULT_PASSWORD.toCharArray();
}
else if (tryCount == 2) {
// try specified password
if (keystorePwd != null) {
Msg.debug(ApplicationKeyManagerFactory.class,
"Attempting to load keystore with property-based password...");
password = keystorePwd.toCharArray();
if (defaultPasswd != null) {
password = defaultPasswd.toCharArray();
}
else {
// no system property password provided
// no default password was provided
++tryCount;
continue;
}
@@ -341,7 +120,7 @@ public class ApplicationKeyManagerFactory {
disposePassword(oldPassword);
oldPassword = password;
password =
customPasswordProvider.getKeyStorePassword(keystorePath, tryCount != 3);
customPasswordProvider.getKeyStorePassword(keystorePath, tryCount != 2);
if (password == null) {
throw new CancelledException();
}
@@ -358,24 +137,24 @@ public class ApplicationKeyManagerFactory {
}
++tryCount;
// we only support a single password for both keystore and private key
KeyStore keyStore = ApplicationKeyStore.getKeyStoreInstance(keystorePath, password);
return new ProtectedKeyStoreData(keyStore, password);
// NOTE: we only support a single password for both keystore and private key
KeyStore keyStore = PKIUtils.getKeyStoreInstance(keystorePath, password);
X509KeyManager keyManager = getKeyManagerFromKeyStore(keyStore, password);
keyStoreCache.put(hashKey, keyManager);
return keyManager;
}
catch (NoSuchAlgorithmException | CertificateException | FileNotFoundException e) {
catch (UnrecoverableKeyException | NoSuchAlgorithmException | CertificateException
| FileNotFoundException e) {
throw new KeyStoreException("Failed to process keystore: " + keystorePath, e);
}
catch (KeyStoreException | IOException e) {
// Java Bug: JDK-6974037 : PKCS12 Keystore load with invalid passwords generates different exceptions
Exception ioException = getIOException(e);
if (ioException != null) {
// Assume all IOExceptions are the result of improperly decrypted keystore
// since arbitrary and inconsistent errors are produced when attempting to
// load an encrypted keystore without a password or with the wrong password.
continue;
if (ioException != null &&
ioException.getCause() instanceof UnrecoverableKeyException) {
continue; // Assume incorrect password was specified
}
throw new KeyStoreException(
"Failed to process keystore (" + tryCount + "): " + keystorePath, e);
throw new KeyStoreException("Failed to open keystore: " + keystorePath, e);
}
finally {
disposePassword(oldPassword);
@@ -385,6 +164,47 @@ public class ApplicationKeyManagerFactory {
throw new KeyStoreException("Failed to unlock key storage: " + keystorePath);
}
/**
* Get key manager for specified JKS or PKCS12 keystore. The keystore must only contain a
* single X509 certificate and corresponding private key which has a usage of digital signing.
* @param keyStore JKS or PKCS12 keystore file
* @param password password for accessing keystore and private key.
* @return X509 key manager instance
* @throws NoSuchAlgorithmException if the specified algorithm is not available from the
* specified provider.
* @throws UnrecoverableKeyException if the key cannot be recovered
* (e.g. the given password is wrong).
* @throws KeyStoreException if a general failure occurs while accessing keystore
*/
static X509KeyManager getKeyManagerFromKeyStore(KeyStore keyStore, char[] password)
throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
// Assume cert of interest is the first one
Enumeration<String> aliases = keyStore.aliases();
if (!aliases.hasMoreElements()) {
throw new KeyStoreException("PKI key store is empty");
}
Certificate certificate = keyStore.getCertificate(aliases.nextElement());
if (!(certificate instanceof X509Certificate x509Cert)) {
throw new KeyStoreException("PKI X509 Certificate not found");
}
boolean[] keyUsage = x509Cert.getKeyUsage();
if (!keyUsage[0]) {
throw new KeyStoreException("PKI key store must contain Digital Signing certificate");
}
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, password);
// Assume that one keystore will produce at most one KeyManager
KeyManager[] keyManagers = kmf.getKeyManagers();
if (keyManagers.length == 1 && (keyManagers[0] instanceof X509KeyManager)) {
return (X509KeyManager) keyManagers[0];
}
throw new KeyStoreException("Unsupported keystore");
}
private static void disposePassword(char[] password) {
if (password != null) {
Arrays.fill(password, (char) 0);
@@ -403,237 +223,4 @@ public class ApplicationKeyManagerFactory {
return null;
}
/**
* <code>ApplicationKeyManager</code> provides a wrapper for the X509 wrappedKeyManager whose
* instantiation is delayed until needed. When a wrapper method is first invoked, the
* {@link ApplicationKeyManagerFactory#init()} method is called to open the keystore
* (which may require a password prompt) and establish the underlying X509KeyManager.
*/
private class ApplicationKeyManager extends X509ExtendedKeyManager {
private X509KeyManager wrappedKeyManager;
private String keystorePath;
private boolean isSelfSigned = false;
@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers,
SSLEngine engine) {
return super.chooseEngineServerAlias(keyType, issuers, engine);
}
@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
SSLEngine engine) {
return super.chooseEngineClientAlias(keyType, issuers, engine);
}
@Override
public synchronized String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.chooseClientAlias(keyType, issuers, socket);
}
@Override
public synchronized String chooseServerAlias(String keyType, Principal[] issuers,
Socket socket) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.chooseServerAlias(keyType, issuers, socket);
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getClientAliases(keyType, issuers);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getServerAliases(keyType, issuers);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getPrivateKey(alias);
}
/**
* Invalidate the active keystore and key manager
*/
private synchronized void invalidateKey() {
wrappedKeyManager = null;
keystorePath = null;
isSelfSigned = false;
}
/**
* Return active keystore path or preferred keystore path if not yet initialized.
* @return active keystore path or preferred keystore path if not yet initialized.
*/
private synchronized String getKeyStore() {
return wrappedKeyManager != null ? keystorePath : getPreferredKeyStore();
}
/**
* Determine if active key manager is utilizing a generated self-signed certificate.
* @return true if using self-signed certificate.
*/
private synchronized boolean usingGeneratedSelfSignedCertificate() {
return wrappedKeyManager != null && isSelfSigned;
}
/**
* Initialize the x509KeyManager associated with the active keystore setting.
* If the <code>x509KeyManager</code> already exists, this method has no affect. If the
* <code>keystorePath</code> has not already been set, the <code>getPreferredKeyStore()</code>
* method will be invoked to obtain the keystore which should be used in establishing the
* <code>wrappedKeyManager</code>. If no keystore has been identified and the Default Identity
* has been set, a self-signed certificate will be generated. If nothing has been set, the
* wrappedKeyManager will remain null and false will be returned. If an error occurs it
* will be logged and key managers will remain uninitialized.
* @return true if key manager initialized successfully or was previously initialized, else
* false if keystore path has not been set and default identity for self-signed certificate
* has not be established (see {@link ApplicationKeyManagerFactory#setDefaultIdentity(X500Principal)}).
* @throws CancelledException user cancelled keystore password entry request
*/
private synchronized boolean init() throws CancelledException {
if (wrappedKeyManager != null) {
return true;
}
return init(getPreferredKeyStore());
}
/**
* Initialize the x509KeyManager.
* If the <code>x509KeyManager</code> already exists for the specified keystore path,
* this method has no affect. If no keystore has been identified and the Default Identity
* has been set, a self-signed certificate will be generated. If nothing has been set, the
* wrappedKeyManager will remain null and false will be returned. If an error occurs it
* will be logged and key managers will remain uninitialized.
* @param newKeystorePath specifies the keystore to be opened or null for no keystore
* @return true if key manager initialized successfully or was previously initialized, else
* false if new keystore path was not specified and default identity for self-signed certificate
* has not be established (see {@link ApplicationKeyManagerFactory#setDefaultIdentity(X500Principal)}).
* @throws CancelledException user cancelled keystore password entry request
*/
private synchronized boolean init(String newKeystorePath) throws CancelledException {
if (wrappedKeyManager != null) {
if (StringUtils.equals(keystorePath, newKeystorePath)) {
return true;
}
invalidateKey();
}
ProtectedKeyStoreData keystoreData = null;
try {
if (newKeystorePath != null && newKeystorePath.length() != 0) {
keystoreData = getProtectedKeyStoreData(newKeystorePath);
}
else if (defaultIdentity != null) {
// use self-signed keystore as fallback (intended for server use only)
Msg.info(this, "Using self-signed certificate: " + defaultIdentity.getName());
char[] pwd = DEFAULT_PASSWORD.toCharArray();
KeyStore selfSignedKeyStore =
ApplicationKeyManagerUtils.createKeyStore("defaultSigKey",
defaultIdentity.getName(), SELF_SIGNED_DURATION_DAYS, null, null, "JKS",
subjectAlternativeNames, pwd);
keystoreData = new ProtectedKeyStoreData(selfSignedKeyStore, pwd);
isSelfSigned = true;
}
else {
return false;
}
ApplicationKeyStore.logCerts(keystoreData.keyStore);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystoreData.keyStore, keystoreData.password);
// Assume that one keystore will produce at most one KeyManager
KeyManager[] keyManagers = kmf.getKeyManagers();
if (keyManagers.length == 1 && (keyManagers[0] instanceof X509KeyManager)) {
wrappedKeyManager = (X509KeyManager) keyManagers[0];
keystorePath = newKeystorePath; // update current keystore path
return true;
}
isSelfSigned = false;
if (keyManagers.length == 0) {
Msg.showError(this, null, "PKI Keystore Failure",
"Failed to create PKI key manager: failed to process keystore (no keys processed)");
}
else if (keyManagers.length == 1) {
Msg.showError(this, null, "PKI Keystore Failure",
"Failed to create PKI key manager: failed to process keystore (expected X.509)");
}
else {
// Unexpected condition
Msg.showError(this, null, "PKI Keystore Failure",
"Failed to create PKI key manager: unsupported keystore produced multiple KeyManagers");
}
}
catch (CancelledException e) {
throw e;
}
catch (Exception e) {
Msg.showError(this, null, "PKI Keystore Failure",
"Failed to create PKI key manager: " + e.getMessage(), e);
}
finally {
if (keystoreData != null) {
keystoreData.dispose();
keystoreData = null;
}
}
return false;
}
}
}

View File

@@ -1,244 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.net;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.Date;
import java.util.Enumeration;
import javax.security.auth.x500.X500Principal;
import ghidra.util.Msg;
/**
* <code>ApplicationKeyStore</code> provides the ability to read X.509 certificates and
* keystores in various formats. Certificate files (e.g., cacerts) may be in a standard
* X.509 form (*.pem, *.crt, *.cer, *.der) or Java JKS (*.jks) form, while keystores
* for client/server may be in a PKCS12 form (*.p12, *.pks, *.pfx) or Java JKS (*.jks) form.
*/
public class ApplicationKeyStore {
private ApplicationKeyStore() {
// no instantiation - static methods only
}
/**
* Load the specified X.509 certificate authority store in a standard
* X.509 form (*.pem, *.crt, *.cer, *.der) or Java JKS (*.jks) form.
* @param cacertsPath certificate store file path
* @return KeyStore containing ingested certificates
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
public static KeyStore getCertificateStoreInstance(String cacertsPath)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
int certCount = 0;
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null);
InputStream fis = new FileInputStream(cacertsPath);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
try {
Certificate cert = cf.generateCertificate(bis);
if (cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) cert;
String name = getCommonName(x509Cert.getSubjectDN());
store.setCertificateEntry(name, cert);
++certCount;
}
}
catch (CertificateException e) {
// Must handle blank lines at bottom of file
Throwable cause = e.getCause();
if (cause != null && "Empty input".equals(cause.getMessage())) {
break; // end of file
}
throw e;
}
}
}
finally {
bis.close();
}
if (certCount == 0) {
// Processing JKS files above produce "Empty input", if no certs read
// try reading as keystore without password
return getKeyStoreInstance(cacertsPath, null);
}
return store;
}
/**
* Attempt to load a client/server keystore in a PKCS12 form (*.p12, *.pks, *.pfx) or
* Java JKS (*.jks) form.
* @param pwd keystore password
* @return keystore instance
* @throws IOException
* @throws KeyStoreException
* @throws NoSuchAlgorithmException
* @throws CertificateException
*/
public static KeyStore getKeyStoreInstance(String keystorePath, char[] pwd)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
File keystoreFile = new File(keystorePath);
boolean isPKCS12 = ApplicationKeyManagerUtils.PKCS_FILENAME_FILTER.accept(keystoreFile);
String type = isPKCS12 ? "PKCS12" : "JKS"; // JKS assumed if not PKCS
KeyStore ks = KeyStore.getInstance(type);
InputStream fis = new FileInputStream(keystorePath);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
ks.load(bis, pwd);
}
finally {
bis.close();
}
return ks;
}
/**
* Attempt to detect PKI KeyStore type ("JKS" or "PKCS12") for the specified file.
* @param keystorePath key store file path
* @return "JKS", "PKCS12" or null
* @throws IOException if file read error occurs
*/
public static String detectKeyStoreType(String keystorePath) throws IOException {
try (FileInputStream fis = new FileInputStream(keystorePath)) {
byte[] header = new byte[4];
int read = fis.read(header);
if (read < 4) {
return null;
}
// Check for JKS magic number: FEEDFEED
if ((header[0] & 0xFF) == 0xFE && (header[1] & 0xFF) == 0xED &&
(header[2] & 0xFF) == 0xFE && (header[3] & 0xFF) == 0xED) {
return "JKS";
}
// Check for PKCS12: starts with 0x30 0x82
if ((header[0] & 0xFF) == 0x30 && (header[1] & 0xFF) == 0x82) {
return "PKCS12";
}
return null;
}
}
/**
* Extract Common Name (CN) from specified principal subject Distinguished Name (DN)
* @param subject X.509 certificate subject
* @return Common Name or full subject name if unable to extract CN from DN
*/
private static String getCommonName(Principal subject) {
// Subject name should be distinguished-name (DN) which starts with common-name (CN)
String name = subject.getName();
int commaIndex = name.indexOf(',');
String firstElement = commaIndex < 0 ? name : name.substring(0, commaIndex);
int equalsIndex = firstElement.indexOf('=');
if (equalsIndex <= 0) {
return name; // bad common name
}
String fieldName = firstElement.substring(0, equalsIndex).trim();
String fieldValue = firstElement.substring(equalsIndex + 1).trim();
if (!fieldName.equalsIgnoreCase("CN")) {
return name; // bad common name
}
return fieldValue;
}
/**
* Log all X509 certificates contained within keystore
* @param keyStore certificate keystore
*/
static void logCerts(KeyStore keyStore) {
try {
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate certificate = keyStore.getCertificate(alias);
if (certificate == null) {
continue;
}
else if (certificate instanceof X509Certificate) {
logCert(alias, (X509Certificate) certificate);
}
else {
Msg.warn(ApplicationKeyStore.class, "Ignore unrecognized certificate: alias=" +
alias + ", type=" + certificate.getType());
}
}
}
catch (KeyStoreException e) {
Msg.error(ApplicationKeyStore.class, "KeyStore failure", e);
}
}
/**
* Log all X509 certificates contained within array
* @param x509Certs array of certificates
*/
public static void logCerts(X509Certificate[] x509Certs) {
for (X509Certificate x509Cert : x509Certs) {
logCert(null, x509Cert);
}
}
/**
* Log specified X509 certificate details
* @param alias certificate alias or null if not applicable
* @param x509Cert X509 certificate
*/
static void logCert(String alias, X509Certificate x509Cert) {
X500Principal subj = x509Cert.getSubjectX500Principal();
X500Principal issuer = x509Cert.getIssuerX500Principal();
Date now = new Date();
String label = alias != null ? (alias + ": ") : "";
if (now.compareTo(x509Cert.getNotAfter()) > 0) {
Msg.warn(ApplicationKeyStore.class,
" " + label + getCommonName(subj) + ", issued by " + getCommonName(issuer) +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expired " +
x509Cert.getNotAfter() + " **EXPIRED**");
}
else {
Msg.info(ApplicationKeyStore.class,
" " + label + getCommonName(subj) + ", issued by " + getCommonName(issuer) +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expires " +
x509Cert.getNotAfter());
}
}
}

View File

@@ -1,92 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.net;
import java.io.IOException;
import java.net.*;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import ghidra.util.Msg;
/**
* <code>ApplicationSSLSocketFactory</code> provides a replacement for the default
* <code>SSLSocketFactory</code> which utilizes the default SSLContext established
* by {@link SSLContextInitializer}.
*/
public class ApplicationSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory socketFactory;
/**
* ApplicationSSLSocketFactory constructor.
* SSLContext initialization will be performed using {@link SSLContextInitializer}.
*/
public ApplicationSSLSocketFactory() {
SSLSocketFactory factory = null;
try {
if (SSLContextInitializer.initialize()) {
factory = SSLContext.getDefault().getSocketFactory();
}
}
catch (NoSuchAlgorithmException e) {
Msg.error(this, "Failed to employ default SSLContext: " + e.toString(), e);
}
this.socketFactory =
factory != null ? factory : (SSLSocketFactory) SSLSocketFactory.getDefault();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
throws IOException {
return socketFactory.createSocket(s, host, port, autoClose);
}
@Override
public String[] getDefaultCipherSuites() {
return socketFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return socketFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return socketFactory.createSocket(host, port);
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return socketFactory.createSocket(host, port, localHost, localPort);
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
int localPort)
throws IOException {
return socketFactory.createSocket(address, port, localAddress, localPort);
}
}

View File

@@ -0,0 +1,512 @@
/* ###
* IP: GHIDRA
*
* 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.
*/
package ghidra.net;
import java.net.Socket;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ssl.*;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.security.auth.DestroyFailedException;
import javax.security.auth.x500.X500Principal;
import org.apache.commons.lang3.StringUtils;
import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException;
/**
* {@link DefaultKeyManagerFactory} provides access to the default application key manager
* associated with the preferred keystore file specified by the {@link #KEYSTORE_PATH_PROPERTY}
* system property or set with {@link #setDefaultKeyStore(String, boolean)}.
* <p>
* <p>
* NOTE: Since {@link SslRMIClientSocketFactory} and {@link SSLServerSocketFactory} employ a
* static cache of a default {@link SSLSocketFactory}, with its default {@link SSLContext}, we
* must utilize a wrapped implementation of the associated {@link X509ExtendedKeyManager} so that
* an updated keystore is used by the existing default {@link SSLSocketFactory}.
*/
public class DefaultKeyManagerFactory {
/**
* Keystore path system property or user preference. Setting the system
* property will take precedence over the user preference.
*/
public static final String KEYSTORE_PATH_PROPERTY = "ghidra.keystore";
/**
* Password system property may be set. If set, this password will be used
* when accessing the keystore before attempting to use <code>customPasswordProvider</code>
* if it has been set.
*/
public static final String KEYSTORE_PASSWORD_PROPERTY = "ghidra.password";
public static final String DEFAULT_PASSWORD = "changeme";
private static final int SELF_SIGNED_DURATION_DAYS = 2 * 365; // 2-years
// Certificate info when self-signed cert is used
private static X500Principal defaultIdentity;
private static List<String> defaultSubjectAlternativeNames;
// Factory maintains a single X509 key manager
private static final DefaultX509KeyManager keyManagerWrapper = new DefaultX509KeyManager();
/**
* Prune path to trim leading and trailing white space. A null will be
* returned if the pruned path is null or the empty string.
*
* @param path file path to be pruned (may be null)
* @return pruned path or null if path was null or pruned path was the empty
* string
*/
private static String prunePath(String path) {
if (path != null) {
path = path.trim();
if (path.length() == 0) {
path = null;
}
}
return path;
}
/**
* Set default user keystore file path (e.g., certificate file with private key).
* This method will have no effect if the keystore had been set via the system
* property and an error will be displayed. Otherwise, the keystore will
* be updated and the key manager re-initialized. The user preference will be
* updated unless a failure occurred while attempting to open the keystore.
* This change will take immediate effect for the current executing application,
* however, it may still be superseded by a system property setting when running
* the application in the future. See {@link #getKeyStore()}.
*
* @param path keystore file path or null to clear current key store and preference.
* @param savePreference if true will be saved as user preference
* @return true if successful else false if error occured (see log).
*/
public static synchronized boolean setDefaultKeyStore(String path, boolean savePreference) {
if (System.getProperty(KEYSTORE_PATH_PROPERTY) != null) {
Msg.showError(DefaultKeyManagerFactory.class, null, "Set KeyStore Failed",
"PKI KeyStore was set via system property and can not be changed");
return false;
}
path = prunePath(path);
try {
boolean keyInitialized = keyManagerWrapper.init(path);
if (savePreference && (path == null || keyInitialized)) {
Preferences.setProperty(KEYSTORE_PATH_PROPERTY, path);
Preferences.store();
}
return keyInitialized;
}
catch (CancelledException e) {
// ignore - keystore left unchanged
return false;
}
}
/**
* Determine if active key manager is utilizing a generated self-signed certificate.
*
* @return true if using self-signed certificate.
*/
public static synchronized boolean usingGeneratedSelfSignedCertificate() {
return keyManagerWrapper.usingGeneratedSelfSignedCertificate();
}
/**
* Set the default self-signed principal identity to be used during initialization
* if no keystore defined. Current application key manager will be invalidated.
* (NOTE: this is intended for server use only when client will not be performing
* CA validation).
*
* @param identity if not null and a KeyStore path has not be set, this
* identity will be used to generate a self-signed certificate and private key
*/
public synchronized static void setDefaultIdentity(X500Principal identity) {
defaultIdentity = identity;
keyManagerWrapper.invalidateKey();
}
/**
* Add the optional self-signed subject alternative name to be used during initialization
* if no keystore defined. Current application key manager will be invalidated.
* (NOTE: this is intended for server use only when client will not be performing
* CA validation).
* @param subjectAltName name to be added to the current list of alternative subject names.
* A null value will clear all names currently set.
* name will be used to generate a self-signed certificate and private key
*/
public synchronized static void addSubjectAlternativeName(String subjectAltName) {
if (subjectAltName == null) {
defaultSubjectAlternativeNames = null;
}
else {
if (defaultSubjectAlternativeNames == null) {
defaultSubjectAlternativeNames = new ArrayList<>();
}
defaultSubjectAlternativeNames.add(subjectAltName);
}
keyManagerWrapper.invalidateKey();
}
/**
* Initialize key manager if needed. Doing this explicitly independent of an SSL connection
* allows application to bail before initiating connection. This will get handshake failure
* if user forgets keystore password or other keystore problem.
* @return true if key manager initialized, otherwise false
*/
public synchronized static boolean initialize() {
try {
return keyManagerWrapper.init();
}
catch (CancelledException e) {
return false;
}
}
/**
* Invalidate the existing default key manager.
*/
public synchronized static void invalidateKeyManager() {
keyManagerWrapper.invalidateKey();
}
/**
* If the system property <i>ghidra.keystore</i> takes precedence in establishing
* the keystore. If using a GUI and the system property has not been set, the
* user preference with the same name will be used.
* @return active keystore path or null if currently not running with a keystore or
* one has not been set.
*/
public static synchronized String getPreferredKeyStore() {
String path = prunePath(System.getProperty(KEYSTORE_PATH_PROPERTY));
if (path == null && !SystemUtilities.isInHeadlessMode()) {
path = prunePath(Preferences.getProperty(KEYSTORE_PATH_PROPERTY));
}
return path;
}
/**
* Get the default/preferred key store path.
* @return default key store path or null if not set
*/
public static synchronized String getKeyStore() {
return keyManagerWrapper.getKeyStore();
}
/**
* Get the lazy default key manager associated with the preferred key store.
* @return default key manager or null if not initialized
*/
public static synchronized X509ExtendedKeyManager getKeyManager() {
return keyManagerWrapper;
}
/**
* <code>DefaultKeyManager</code> provides a wrapper for the X509 wrappedKeyManager whose
* instantiation is delayed until needed. When a wrapper method is first invoked, the
* {@link DefaultX509KeyManager#init()} method is called to open the keystore
* (which may require a password prompt) and establish the underlying X509KeyManager.
*/
private static class DefaultX509KeyManager extends X509ExtendedKeyManager {
private X509KeyManager wrappedKeyManager;
private String keystorePath;
private boolean isSelfSigned = false;
@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers,
SSLEngine engine) {
return super.chooseEngineServerAlias(keyType, issuers, engine);
}
@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers,
SSLEngine engine) {
return super.chooseEngineClientAlias(keyType, issuers, engine);
}
@Override
public synchronized String chooseClientAlias(String[] keyType, Principal[] issuers,
Socket socket) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.chooseClientAlias(keyType, issuers, socket);
}
@Override
public synchronized String chooseServerAlias(String keyType, Principal[] issuers,
Socket socket) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.chooseServerAlias(keyType, issuers, socket);
}
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getClientAliases(keyType, issuers);
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
try {
init();
}
catch (CancelledException e) {
// ignore
}
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getServerAliases(keyType, issuers);
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getCertificateChain(alias);
}
@Override
public PrivateKey getPrivateKey(String alias) {
if (wrappedKeyManager == null) {
return null;
}
return wrappedKeyManager.getPrivateKey(alias);
}
/**
* Invalidate the active keystore and key manager
*/
private synchronized void invalidateKey() {
wrappedKeyManager = null;
keystorePath = null;
isSelfSigned = false;
}
/**
* Return active keystore path or preferred keystore path if not yet initialized.
* @return active keystore path or preferred keystore path if not yet initialized.
*/
private synchronized String getKeyStore() {
return keystorePath != null ? keystorePath : getPreferredKeyStore();
}
/**
* Determine if active key manager is utilizing a generated self-signed certificate.
* @return true if using self-signed certificate.
*/
private synchronized boolean usingGeneratedSelfSignedCertificate() {
return wrappedKeyManager != null && isSelfSigned;
}
/**
* Initialize the default x509KeyManager singleton wrappedKeyManager associated with the
* preferred keystore path.
* If the <code>x509KeyManager</code> already exists, this method has no affect. If the
* <code>keystorePath</code> has not already been set, the <code>getPreferredKeyStore()</code>
* method will be invoked to obtain the keystore which should be used in establishing the
* <code>wrappedKeyManager</code>. If no keystore has been identified and the Default Identity
* has been set, a self-signed certificate will be generated. If nothing has been set, the
* wrappedKeyManager will remain null and false will be returned. If an error occurs it
* will be logged and key managers will remain uninitialized.
* @return true if key manager initialized successfully or was previously initialized, else
* false if keystore path has not been set and default identity for self-signed certificate
* has not be established (see {@link DefaultKeyManagerFactory#setDefaultIdentity(X500Principal)}).
* @throws CancelledException user cancelled keystore password entry request
*/
private synchronized boolean init() throws CancelledException {
if (wrappedKeyManager != null) {
return true;
}
return init(getPreferredKeyStore());
}
/**
* Initialize the default x509KeyManager singleton wrappedKeyManager using the specified path.
* If the <code>x509KeyManager</code> already exists for the specified keystore path,
* this method has no affect. If no keystore has been identified and the Default Identity
* has been set, a self-signed certificate will be generated. If nothing has been set, the
* wrappedKeyManager will remain null and false will be returned. If an error occurs it
* will be logged and key managers will remain uninitialized.
* @param newKeystorePath specifies the keystore to be opened or null for no keystore
* @return true if key manager initialized successfully or was previously initialized, else
* false if new keystore path was not specified and default identity for self-signed certificate
* has not be established (see {@link DefaultKeyManagerFactory#setDefaultIdentity(X500Principal)}).
* @throws CancelledException user cancelled keystore password entry request
*/
private synchronized boolean init(String newKeystorePath) throws CancelledException {
if (wrappedKeyManager != null) {
if (StringUtils.equals(keystorePath, newKeystorePath)) {
return true;
}
invalidateKey();
}
isSelfSigned = false;
try {
if (newKeystorePath != null && newKeystorePath.length() != 0) {
Msg.info(DefaultKeyManagerFactory.class,
"Using certificate keystore: " + newKeystorePath);
// Password optionally specified via property
String keystorePwd = System.getProperty(KEYSTORE_PASSWORD_PROPERTY);
wrappedKeyManager =
ApplicationKeyManagerFactory.getKeyManager(newKeystorePath, keystorePwd);
keystorePath = newKeystorePath; // update current keystore path
}
else if (defaultIdentity != null) {
// use self-signed keystore as fallback (intended for server use only)
Msg.info(this, "Using self-signed certificate: " + defaultIdentity.getName());
char[] pwd = DEFAULT_PASSWORD.toCharArray();
KeyStore selfSignedKeyStore = PKIUtils.createKeyStore("defaultSigKey",
defaultIdentity.getName(), SELF_SIGNED_DURATION_DAYS, null, null, "JKS",
defaultSubjectAlternativeNames, pwd);
wrappedKeyManager = ApplicationKeyManagerFactory
.getKeyManagerFromKeyStore(selfSignedKeyStore, pwd);
isSelfSigned = true;
}
else {
return false;
}
return true;
}
catch (CancelledException e) {
throw e;
}
catch (Exception e) {
Msg.showError(this, null, "PKI Keystore Failure",
"Failed to create PKI key manager: " + e.getMessage(), e);
}
return false;
}
}
/**
* Sign the supplied token byte array using an installed certificate from
* one of the specified authorities
* @param authorities trusted certificate authorities used to constrain client certificate
* (may be null or empty array if CA constraint does not matter).
* @param token token byte array
* @return signed token object
* @throws NoSuchAlgorithmException algorithm associated within signing certificate not found
* @throws SignatureException failed to generate SignedToken
* @throws CertificateException error associated with signing certificate
*/
public static SignedToken getSignedToken(Principal[] authorities, byte[] token)
throws NoSuchAlgorithmException, SignatureException, CertificateException {
PrivateKey privateKey = null;
X509Certificate[] certificateChain = null;
try {
X509ExtendedKeyManager x509KeyManager = getKeyManager();
if (x509KeyManager != null) {
String alias = x509KeyManager.chooseClientAlias(new String[] { PKIUtils.RSA_TYPE },
authorities, null);
if (alias != null) {
privateKey = x509KeyManager.getPrivateKey(alias);
certificateChain = x509KeyManager.getCertificateChain(alias);
}
}
if (privateKey == null || certificateChain == null) {
CertificateException e =
new CertificateException("suitable PKI certificate not found");
e.printStackTrace();
throw e;
}
//
// See JAVA Examples in a Nutshell (p.358) for use of Signer and
// IdentityScope classes
//
String algorithm = certificateChain[0].getSigAlgName();
Signature sig = Signature.getInstance(algorithm);
try {
sig.initSign(privateKey);
}
catch (InvalidKeyException e) {
throw new CertificateException("suitable PKI certificate not found", e);
}
sig.update(token);
return new SignedToken(token, sig.sign(), certificateChain, algorithm);
}
finally {
if (privateKey != null) {
// Note: Keystore destroy only supported in Java 1.8
try {
privateKey.destroy();
}
catch (DestroyFailedException e) {
// ignore - may not be supported by all keystores
}
}
}
}
/**
* Verify that the specified sigBytes reflect my signature of the specified token.
* @param authorities trusted certificate authorities used to constrain client certificate
* (may be null or empty array if CA constraint does not matter).
* @param token byte array token
* @param signature token signature
* @return true if signature is my signature
* @throws NoSuchAlgorithmException algorithym associated within signing certificate not found
* @throws SignatureException failed to generate SignedToken
* @throws CertificateException error associated with signing certificate
*/
public static boolean isMySignature(Principal[] authorities, byte[] token, byte[] signature)
throws NoSuchAlgorithmException, SignatureException, CertificateException {
SignedToken signedToken = getSignedToken(authorities, token);
return Arrays.equals(signature, signedToken.signature);
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -26,21 +26,17 @@ import ghidra.util.Msg;
/**
* Initialize the default SSLContext for use by all SSL connections (e.g., https).
* It is the responsibility of the Application to properly invoke this initializer
* to ensure that the default SSLContext is properly established. While HTTPS URL connections
* will make use of this default SSLContext, other SSL connections may need to
* specify the {@link ApplicationSSLSocketFactory} to leverage the applications
* default SSLContext.
* to ensure that the default SSLContext is properly established.
* <p>
* The property <code>jdk.tls.client.protocols</code> should be set to restrict secure
* client connections to a specific set of enabled TLS protocols (e.g., TLSv1.2,TLSv1.3).
* See <A href="https://java.com/en/configure_crypto.html">JDK and JRE Cryptographic Algorithms</A>
* for details.
*
* @see ApplicationTrustManagerFactory
* @see ApplicationKeyManagerFactory
* @see ApplicationKeyManagerUtils
* @see DefaultTrustManagerFactory
* @see DefaultKeyManagerFactory
*/
public class SSLContextInitializer implements ModuleInitializer {
public class DefaultSSLContextInitializer implements ModuleInitializer {
private static final String DEFAULT_SSL_PROTOCOL = "TLS";
@@ -56,8 +52,8 @@ public class SSLContextInitializer implements ModuleInitializer {
public static synchronized boolean initialize(boolean reset) {
if (reset) {
sslContext = null;
ApplicationTrustManagerFactory.invalidateTrustManagers();
ApplicationKeyManagerFactory.invalidateKeyManagers();
DefaultTrustManagerFactory.invalidateTrustManagers();
DefaultKeyManagerFactory.invalidateKeyManager();
}
return initialize();
}
@@ -73,15 +69,25 @@ public class SSLContextInitializer implements ModuleInitializer {
return true;
}
Msg.info(SSLContextInitializer.class, "Initializing SSL Context");
Msg.info(DefaultSSLContextInitializer.class, "Initializing SSL Context");
KeyManager[] keyManagers = ApplicationKeyManagerFactory.getInstance().getKeyManagers();
KeyManager keyManager = DefaultKeyManagerFactory.getKeyManager();
try {
// Use new instance of SSLContext to avoid adopting CA certs provided with Java
sslContext = SSLContext.getInstance(DEFAULT_SSL_PROTOCOL);
SecureRandom random = SecureRandomFactory.getSecureRandom();
sslContext.init(keyManagers, ApplicationTrustManagerFactory.getTrustManagers(), random);
sslContext.init(new KeyManager[] { keyManager },
DefaultTrustManagerFactory.getTrustManagers(), random);
//
// NOTE: The SslRMIClientSocketFactory and SslRMIServerSocketFactory both statically
// cache their 'defaultSocketFactory' which prevents it from utilizing a new
// default SSLContext. This requires us to employ a wrapped TrustManager and
// wrapped KeyManager to allow for them to possibly be revised after first use
// (e.g., JUnit testing, new user PKI Certificate, etc.).
//
SSLContext.setDefault(sslContext);
// Must install default HostnameVerifier - otherwise all traffic will fail
@@ -101,7 +107,7 @@ public class SSLContextInitializer implements ModuleInitializer {
}
catch (Exception e) {
Msg.error(SSLContextInitializer.class,
Msg.error(DefaultSSLContextInitializer.class,
"SSL Context initialization failed: " + e.getMessage(), e);
}
return false;

View File

@@ -20,16 +20,20 @@ import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;
import javax.net.ssl.*;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.security.auth.x500.X500Principal;
import ghidra.framework.preferences.Preferences;
import ghidra.util.Msg;
/**
* <code>ApplicationTrustManagerFactory</code> provides the ability to establish
* acceptable certificate authorities to be used with SSL connections and PKI
* authentication.
* <code>DefaultTrustManagerFactory</code> provides the ability to establish
* acceptable certificate authorities to be used with the default SSLContext
* as established by {@link DefaultSSLContextInitializer}.
* <p>
* The default behavior is for no trust authority to be established, in which case
* SSL peers will not be authenticated. If CA certificates have been set, all SSL
@@ -47,8 +51,13 @@ import ghidra.util.Msg;
* <p>
* The application may choose to set the file path automatically based upon the presence of
* a <i>cacerts</i> file at a predetermined location.
* <p>
* NOTE: Since {@link SslRMIClientSocketFactory} and {@link SSLServerSocketFactory} employ a
* static cache of a default {@link SSLSocketFactory}, with its default {@link SSLContext}, we
* must utilize a wrapped implementation of the associated {@link X509TrustManager} so that any
* changes are used by the existing default {@link SSLSocketFactory}.
*/
public class ApplicationTrustManagerFactory {
public class DefaultTrustManagerFactory {
/**
* The X509 cacerts file to be used when authenticating remote
@@ -57,22 +66,20 @@ public class ApplicationTrustManagerFactory {
*/
public static final String GHIDRA_CACERTS_PATH_PROPERTY = "ghidra.cacerts";
private static final X509Certificate[] NO_CERTS = new X509Certificate[0];
/**
* Use a singleton wrappedTrustManager so we can alter the true trustManager
* as needed. Once the installed trust manager is consumed by the SSL Engine,
* we are unable to get it to use a new one. Use of a wrapper solves this
* issue which occurs during testing.
*/
private static X509TrustManager trustManager;
private static TrustManager[] wrappedTrustManagers;
private static boolean hasCAs;
private static Exception caError;
private static final WrappedTrustManager wrappedTrustManager = new WrappedTrustManager();
/**
* <code>ApplicationTrustManagerFactory</code> constructor
*/
private ApplicationTrustManagerFactory() {
private DefaultTrustManagerFactory() {
// no instantiation - static methods only
}
@@ -83,138 +90,169 @@ public class ApplicationTrustManagerFactory {
*/
private static void init() {
if (wrappedTrustManagers == null) {
wrappedTrustManagers = new WrappedTrustManager[] { new WrappedTrustManager() };
}
String cacertsPath = System.getProperty(GHIDRA_CACERTS_PATH_PROPERTY);
if (cacertsPath == null || cacertsPath.length() == 0) {
// check user preferences if cacerts not set via system property
cacertsPath = Preferences.getProperty(GHIDRA_CACERTS_PATH_PROPERTY);
if (cacertsPath == null || cacertsPath.length() == 0) {
Msg.info(ApplicationTrustManagerFactory.class,
Msg.info(DefaultTrustManagerFactory.class,
"Trust manager disabled, cacerts have not been set");
trustManager = new OpenTrustManager();
wrappedTrustManager.setTrustManager(new OpenTrustManager());
return;
}
}
try {
Msg.info(ApplicationTrustManagerFactory.class,
Msg.info(DefaultTrustManagerFactory.class,
"Trust manager initializing with cacerts: " + cacertsPath);
KeyStore keyStore = ApplicationKeyStore.getCertificateStoreInstance(cacertsPath);
KeyStore keyStore = PKIUtils.loadCertificateStore(cacertsPath);
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
X509TrustManager x509TrustMgr = null;
TrustManager[] trustManagers = tmf.getTrustManagers();
for (TrustManager trustManager2 : trustManagers) {
if (trustManager2 instanceof X509TrustManager) {
X509TrustManager mgr = (X509TrustManager) trustManager2;
ApplicationKeyStore.logCerts(mgr.getAcceptedIssuers());
trustManager = mgr;
for (TrustManager trustMgr : trustManagers) {
if (trustMgr instanceof X509TrustManager) {
x509TrustMgr = (X509TrustManager) trustMgr;
wrappedTrustManager.setTrustManager(x509TrustMgr);
PKIUtils.logCerts(x509TrustMgr.getAcceptedIssuers());
break;
}
}
hasCAs = true;
if (x509TrustMgr == null) {
throw new CertificateException("Failed to load any X509 certificates");
}
}
catch (GeneralSecurityException | IOException e) {
caError = e;
wrappedTrustManager.setTrustManagerError(e);
String msg = e.getMessage();
if (msg == null) {
msg = e.toString();
}
Msg.error(ApplicationTrustManagerFactory.class,
Msg.error(DefaultTrustManagerFactory.class,
"Failed to process cacerts (" + cacertsPath + "): " + msg, e);
}
}
/**
* Determine if certificate authorities are in place. If no certificate authorities
* have been specified via the "ghidra.cacerts" property, all certificates will be
* trusted.
* @return true if certificate authorities are in place, else false.
*/
public static boolean hasCertificateAuthorities() {
return hasCAs;
}
/**
* Determine if a CA cert initialization error occurred
* @return true if error occurred (see {@link #getCertError()})
*/
static boolean hasCertError() {
return caError != null;
}
/**
* Get the CA cert initialization error which occurred
* during initialization
* @return error object or null if not applicable
*/
static Exception getCertError() {
return caError;
}
/**
* Get trust managers after performing any necessary initialization.
* Get trust manager after performing any necessary initialization.
*
* @return trust managers
*/
static synchronized TrustManager[] getTrustManagers() {
if (trustManager == null) {
public static synchronized TrustManager[] getTrustManagers() {
if (wrappedTrustManager.trustManager == null) {
init();
}
return wrappedTrustManagers.clone();
return new TrustManager[] { wrappedTrustManager };
}
/**
* Get trust manager after performing any necessary initialization.
* @return trust managers
*/
public static synchronized X509TrustManager getTrustManager() {
if (trustManager == null) {
init();
}
return trustManager;
}
/**
* Invalidate the active keystore and key manager
* Invalidate the active keystore and key manager.
*
* NOTE: This should only be invoked by {@link DefaultSSLContextInitializer}.
*/
static synchronized void invalidateTrustManagers() {
trustManager = null;
caError = null;
wrappedTrustManager.invalidate();
}
private static final X509Certificate[] NO_CERTS = new X509Certificate[0];
/**
* Returns a list of trusted issuers (i.e., CA certificates) as established
* by the {@link DefaultTrustManagerFactory}.
*
* @return array of trusted Certificate Authorities
* @throws CertificateException if failed to properly initialize trust manager
* due to CA certificate error(s).
*/
public static X500Principal[] getTrustedIssuers() throws CertificateException {
return wrappedTrustManager.getTrustedIssuers();
}
/**
* Validate a client certificate ensuring that it is not expired and is
* trusted based upon the active trust managers.
* @param certChain X509 certificate chain
* @param authType authentication type (i.e., "RSA")
* @throws CertificateException if certificate validation fails
*/
public static void validateClient(X509Certificate[] certChain, String authType)
throws CertificateException {
wrappedTrustManager.checkClientTrusted(certChain, authType);
}
private static class WrappedTrustManager implements X509TrustManager {
private X509TrustManager trustManager;
private Exception caError;
WrappedTrustManager() {
invalidate();
}
void invalidate() {
this.trustManager = null;
this.caError = null;
}
synchronized void setTrustManager(X509TrustManager trustManager) {
this.trustManager = trustManager;
this.caError = null;
}
synchronized void setTrustManagerError(Exception caError) {
this.trustManager = null;
this.caError = caError;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
public synchronized void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
if (trustManager == null) {
throw new CertificateException("Trust manager not properly initialized");
}
checkTrustManager();
trustManager.checkClientTrusted(chain, authType);
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
public synchronized void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
if (trustManager == null) {
throw new CertificateException("Trust manager not properly initialized");
}
checkTrustManager();
trustManager.checkServerTrusted(chain, authType);
}
@Override
public X509Certificate[] getAcceptedIssuers() {
public synchronized X509Certificate[] getAcceptedIssuers() {
if (trustManager == null) {
return NO_CERTS;
}
return trustManager.getAcceptedIssuers();
}
synchronized X500Principal[] getTrustedIssuers() throws CertificateException {
WrappedTrustManager trustMgr = wrappedTrustManager;
trustMgr.checkTrustManager();
X509Certificate[] acceptedIssuers = trustMgr.getAcceptedIssuers();
if (acceptedIssuers == null || acceptedIssuers.length == 0) {
return null; // trust all authorities
}
Set<X500Principal> set = new HashSet<>();
for (X509Certificate trustedCert : acceptedIssuers) {
set.add(trustedCert.getSubjectX500Principal());
}
X500Principal[] principals = new X500Principal[set.size()];
return set.toArray(principals);
}
private void checkTrustManager() throws CertificateException {
if (trustManager != null) {
return;
}
if (caError != null) {
throw new CertificateException("Failed to load CA certs", caError);
}
throw new CertificateException("Trust manager not properly initialized");
}
}
/**

View File

@@ -4,9 +4,9 @@
* 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.
@@ -47,8 +47,8 @@ public class HttpClients {
* @throws IOException if error in PKI settings or crypto configuration
*/
public static HttpClient.Builder newHttpClientBuilder() throws IOException {
if (!ApplicationKeyManagerFactory.initialize()) {
if (ApplicationKeyManagerFactory.getKeyStore() != null) {
if (!DefaultKeyManagerFactory.initialize()) {
if (DefaultKeyManagerFactory.getKeyStore() != null) {
throw new IOException("Failed to initialize PKI certificate keystore");
}
}

View File

@@ -42,15 +42,13 @@ import org.bouncycastle.util.IPAddress;
import generic.random.SecureRandomFactory;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* <code>ApplicationKeyManagerUtils</code> provides public methods for utilizing
* the application PKI key management, including access to trusted issuers
* (i.e., CA certificates), token signing and validation, and the ability to
* generate keystores for testing or when a self-signed certificate will
* suffice.
* {@link PKIUtils} provides supporting utilities for creating and accessing X509 certificate
* keystore files.
*/
public class ApplicationKeyManagerUtils {
public class PKIUtils {
public static final String RSA_TYPE = "RSA";
@@ -69,193 +67,61 @@ public class ApplicationKeyManagerUtils {
static {
/**
* Bouncy Castle uses its BCStyle for X500Names which reverses Distingushed Name ordering.
* Bouncy Castle uses its BCStyle for X500Names which reverses Distinguished Name ordering.
* This is resolved by setting the default to RFC4519 style to ensure compatibility with
* Java's internal implementation of X500Name.
* <p>
* Note that this could become an issue if this static default is adjusted elsewhere.
* It may be neccessary to set this at the start of all methods which rely on any of the
* It may be necessary to set this at the start of all methods which rely on any of the
* BC code for X500 certificate processing.
*
*/
X500Name.setDefaultStyle(RFC4519Style.INSTANCE);
}
private ApplicationKeyManagerUtils() {
// no instantiation - static methods only
}
/**
* Sign the supplied token byte array using an installed certificate from
* one of the specified authorities
* @param authorities trusted certificate authorities used to constrain client certificate
* (may be null or empty array if CA constraint does not matter).
* @param token token byte array
* @return signed token object
* @throws NoSuchAlgorithmException algorithym associated within signing certificate not found
* @throws SignatureException failed to generate SignedToken
* @throws CertificateException error associated with signing certificate
* Establish X509TrustManager for the specified CA certificate storage.
*
* @param caCertsFile CA certificates storage file
* @return X509TrustManager
* @throws CancelledException if password entry was cancelled
* @throws GeneralSecurityException if error occured during truststore initialization
* @throws IOException if file read error occurs
*/
public static SignedToken getSignedToken(Principal[] authorities, byte[] token)
throws NoSuchAlgorithmException, SignatureException, CertificateException {
public static X509TrustManager getTrustManager(File caCertsFile)
throws CancelledException, GeneralSecurityException, IOException {
PrivateKey privateKey = null;
X509Certificate[] certificateChain = null;
try {
ApplicationKeyManagerFactory keyManagerFactory =
ApplicationKeyManagerFactory.getInstance();
for (KeyManager keyManager : keyManagerFactory.getKeyManagers()) {
if (!(keyManager instanceof X509KeyManager)) {
continue;
}
X509KeyManager x509KeyManager = (X509KeyManager) keyManager;
String alias =
x509KeyManager.chooseClientAlias(new String[] { RSA_TYPE }, authorities, null);
if (alias != null) {
privateKey = x509KeyManager.getPrivateKey(alias);
certificateChain = x509KeyManager.getCertificateChain(alias);
break;
}
}
if (privateKey == null || certificateChain == null) {
CertificateException e =
new CertificateException("suitable PKI certificate not found");
e.printStackTrace();
throw e;
}
//
// See JAVA Examples in a Nutshell (p.358) for use of Signer and
// IdentityScope classes
//
String algorithm = certificateChain[0].getSigAlgName();
Signature sig = Signature.getInstance(algorithm);
try {
sig.initSign(privateKey);
}
catch (InvalidKeyException e) {
throw new CertificateException("suitable PKI certificate not found", e);
}
sig.update(token);
return new SignedToken(token, sig.sign(), certificateChain, algorithm);
}
finally {
if (privateKey != null) {
// Note: Keystore destroy only supported in Java 1.8
try {
privateKey.destroy();
}
catch (DestroyFailedException e) {
// ignore - may not be supported by all keystores
}
}
}
}
/**
* Verify that the specified sigBytes reflect my signature of the specified token.
* @param authorities trusted certificate authorities used to constrain client certificate
* (may be null or empty array if CA constraint does not matter).
* @param token byte array token
* @param signature token signature
* @return true if signature is my signature
* @throws NoSuchAlgorithmException algorithym associated within signing certificate not found
* @throws SignatureException failed to generate SignedToken
* @throws CertificateException error associated with signing certificate
*/
public static boolean isMySignature(Principal[] authorities, byte[] token, byte[] signature)
throws NoSuchAlgorithmException, SignatureException, CertificateException {
SignedToken signedToken = getSignedToken(authorities, token);
return Arrays.equals(signature, signedToken.signature);
}
/**
* Returns a list of trusted issuers (i.e., CA certificates) as established
* by the {@link ApplicationTrustManagerFactory}.
* @return array of trusted Certificate Authorities
* @throws CertificateException if failed to properly initialize trust manager
* due to CA certificate error(s).
*/
public static X500Principal[] getTrustedIssuers() throws CertificateException {
TrustManager[] trustManagers = ApplicationTrustManagerFactory.getTrustManagers();
if (ApplicationTrustManagerFactory.hasCertError()) {
throw new CertificateException("failed to load CA certs",
ApplicationTrustManagerFactory.getCertError());
if (!caCertsFile.isFile()) {
throw new FileNotFoundException(
"CA Certificates file not found: " + caCertsFile.getAbsolutePath());
}
Set<X500Principal> set = new HashSet<>();
KeyStore keyStore = PKIUtils.loadCertificateStore(caCertsFile.getAbsolutePath());
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
boolean openTrust = true;
for (TrustManager trustManager : trustManagers) {
if (!(trustManager instanceof X509TrustManager)) {
Msg.warn(ApplicationKeyManagerUtils.class,
"Unexpected trust manager implementation: " +
trustManager.getClass().getName());
openTrust = false;
continue;
}
X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
X509Certificate[] acceptedIssuers = x509TrustManager.getAcceptedIssuers();
if (acceptedIssuers != null && acceptedIssuers.length != 0) {
openTrust = false;
for (X509Certificate trustedCert : acceptedIssuers) {
set.add(trustedCert.getSubjectX500Principal());
}
}
}
if (openTrust) {
return null;// trust all authorities
}
X500Principal[] principals = new X500Principal[set.size()];
return set.toArray(principals);
}
/**
* Validate a client certificate ensuring that it is not expired and is
* trusted based upon the active trust managers.
* @param certChain X509 certificate chain
* @param authType authentication type (i.e., "RSA")
* @throws CertificateException if certificate validation fails
*/
public static void validateClient(X509Certificate[] certChain, String authType)
throws CertificateException {
CertificateException checkFailure = null;
TrustManager[] trustManagers = ApplicationTrustManagerFactory.getTrustManagers();
if (ApplicationTrustManagerFactory.hasCertError()) {
throw new CertificateException("failed to load CA certs",
ApplicationTrustManagerFactory.getCertError());
}
for (TrustManager trustManager : trustManagers) {
if (!(trustManager instanceof X509TrustManager)) {
continue;
}
X509TrustManager x509TrustManager = (X509TrustManager) trustManager;
try {
x509TrustManager.checkClientTrusted(certChain, authType);
checkFailure = null;
X509TrustManager trustManager = null;
TrustManager[] trustManagers = tmf.getTrustManagers();
for (TrustManager trustManager2 : trustManagers) {
if (trustManager2 instanceof X509TrustManager mgr) {
//ApplicationKeyStore.logCerts(mgr.getAcceptedIssuers());
trustManager = mgr;
break;
}
catch (CertificateException e) {
checkFailure = e;
}
}
if (checkFailure != null) {
throw checkFailure;// check failed - throw last failure
if (trustManager == null) {
throw new CertStoreException(
"Failed to load X509 TrustManager from " + caCertsFile.getAbsolutePath());
}
return trustManager;
}
/**
* Pack ordered list of certs to create a certificate chain array
*
* @param cert primary certificate
* @param caCerts CA certificate chain.
* @return ordered certificate chain
@@ -269,6 +135,7 @@ public class ApplicationKeyManagerUtils {
/**
* Export X.509 certificates to the specified outFile.
*
* @param certificates certificates to be stored
* @param outFile output file
* @throws IOException if error occurs writing to outFile
@@ -300,6 +167,7 @@ public class ApplicationKeyManagerUtils {
/**
* Generate a new {@link X509Certificate} with RSA {@link KeyPair} and create/update a {@link KeyStore}
* optionally backed by a keyFile.
*
* @param alias entry alias with keystore
* @param dn distinguished name (e.g., "CN=Ghidra Test, O=Ghidra, OU=Test, C=US" )
* @param durationDays number of days which generated certificate should remain valid
@@ -407,7 +275,7 @@ public class ApplicationKeyManagerUtils {
keyStore.store(out, protectedPassphrase);
out.flush();
out.getFD().sync();
Msg.debug(ApplicationKeyManagerUtils.class,
Msg.debug(PKIUtils.class,
out.getChannel().size() + " bytes written to key/cert file: " + keyFile);
}
catch (SyncFailedException e) {
@@ -420,8 +288,7 @@ public class ApplicationKeyManagerUtils {
keyFile.setWritable(false);
}
Msg.debug(ApplicationKeyManagerUtils.class,
"Certificate Generated (" + alias + "): " + dn);
Msg.debug(PKIUtils.class, "Certificate Generated (" + alias + "): " + dn);
return keyStore;
}
@@ -441,6 +308,7 @@ public class ApplicationKeyManagerUtils {
/**
* Generate a new {@link X509Certificate} with RSA {@link KeyPair} and create/update a {@link KeyStore}
* optionally backed by a keyFile.
*
* @param alias entry alias with keystore
* @param dn distinguished name (e.g., "CN=Ghidra Test, O=Ghidra, OU=Test, C=US" )
* @param durationDays number of days which generated certificate should remain valid
@@ -477,4 +345,224 @@ public class ApplicationKeyManagerUtils {
}
}
/**
* Load the all certificates from the specified certificate store in a standard
* X.509 form (e.g., concatenation of Base64 encoded certificates: *.pem, *.crt, *.cer, *.der)
* or Java JKS (*.jks) form.
*
* @param certsPath certificate(s) storage file path
* @return KeyStore containing certificates
* @throws IOException if failure occurred reading and processing keystore file.
* @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the
* keystore cannot be found
* @throws CertificateException if any of the certificates in the keystore could not be loaded
* @throws KeyStoreException if a general error occurred opening/processing keystore
*/
public static KeyStore loadCertificateStore(String certsPath)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
int certCount = 0;
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null);
// Attempt to read certificates in Base64 encoded form
InputStream fis = new FileInputStream(certsPath);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
try {
Certificate cert = cf.generateCertificate(bis);
if (cert instanceof X509Certificate) {
X509Certificate x509Cert = (X509Certificate) cert;
String name = getCommonName(x509Cert.getSubjectX500Principal());
store.setCertificateEntry(name, cert);
++certCount;
}
}
catch (CertificateException e) {
// Must handle blank lines at bottom of file
Throwable cause = e.getCause();
if (cause != null && "Empty input".equals(cause.getMessage())) {
break; // end of file
}
throw e;
}
}
}
finally {
bis.close();
}
if (certCount == 0) {
// Processing JKS files above produce "Empty input", if no certs read
// try reading as keystore without password
return getKeyStoreInstance(certsPath, null);
}
return store;
}
/**
* Attempt to load a client/server keystore in a PKCS12 form (*.p12, *.pks, *.pfx) or
* Java JKS (*.jks) form.
*
* @param keystorePath JKS or PKCS12 keystore file path
* @param password keystore password
* @return keystore instance
* @throws IOException if failure occurred reading and processing keystore file or if the
* given password was incorrect. If the error is due to a wrong password, the
* {@link Throwable#getCause cause} of the {@code IOException} should be an
* {@code UnrecoverableKeyException}
* @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the
* keystore cannot be found
* @throws CertificateException if any of the certificates in the keystore could not be loaded
* @throws KeyStoreException if a general error occurred opening/processing keystore
*/
public static synchronized KeyStore getKeyStoreInstance(String keystorePath, char[] password)
throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
String type = PKIUtils.detectKeyStoreType(keystorePath);
if (type == null) {
throw new KeyStoreException("Unsupported PKI key store file type: " + keystorePath);
}
KeyStore ks = KeyStore.getInstance(type);
InputStream fis = new FileInputStream(keystorePath);
BufferedInputStream bis = new BufferedInputStream(fis);
try {
ks.load(bis, password);
}
finally {
bis.close();
}
return ks;
}
/**
* Attempt to detect PKI KeyStore type ("JKS" or "PKCS12") for the specified file.
*
* @param keystorePath key store file path
* @return "JKS", "PKCS12" or null
* @throws IOException if file read error occurs
*/
public static String detectKeyStoreType(String keystorePath) throws IOException {
try (FileInputStream fis = new FileInputStream(keystorePath)) {
byte[] header = new byte[4];
int read = fis.read(header);
if (read < 4) {
return null;
}
// Check for JKS magic number: FEEDFEED
if ((header[0] & 0xFF) == 0xFE && (header[1] & 0xFF) == 0xED &&
(header[2] & 0xFF) == 0xFE && (header[3] & 0xFF) == 0xED) {
return "JKS";
}
// Check for PKCS12: starts with 0x30 0x82
if ((header[0] & 0xFF) == 0x30 && (header[1] & 0xFF) == 0x82) {
return "PKCS12";
}
return null;
}
}
/**
* Extract Common Name (CN) from specified principal subject Distinguished Name (DN)
*
* @param subject X.509 certificate subject
* @return Common Name or full subject name if unable to extract CN from DN
*/
private static String getCommonName(Principal subject) {
// Subject name should be distinguished-name (DN) which starts with common-name (CN)
String name = subject.getName();
int commaIndex = name.indexOf(',');
String firstElement = commaIndex < 0 ? name : name.substring(0, commaIndex);
int equalsIndex = firstElement.indexOf('=');
if (equalsIndex <= 0) {
return name; // bad common name
}
String fieldName = firstElement.substring(0, equalsIndex).trim();
String fieldValue = firstElement.substring(equalsIndex + 1).trim();
if (!fieldName.equalsIgnoreCase("CN")) {
return name; // bad common name
}
return fieldValue;
}
/**
* Log all X509 certificates contained within keystore
*
* @param keyStore certificate keystore
*/
static void logCerts(KeyStore keyStore) {
try {
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate certificate = keyStore.getCertificate(alias);
if (certificate == null) {
continue;
}
else if (certificate instanceof X509Certificate) {
logCert(alias, (X509Certificate) certificate);
}
else {
Msg.warn(PKIUtils.class, "Ignore unrecognized certificate: alias=" + alias +
", type=" + certificate.getType());
}
}
}
catch (KeyStoreException e) {
Msg.error(PKIUtils.class, "KeyStore failure", e);
}
}
/**
* Log all X509 certificates contained within array
*
* @param x509Certs array of certificates
*/
public static void logCerts(X509Certificate[] x509Certs) {
for (X509Certificate x509Cert : x509Certs) {
logCert(null, x509Cert);
}
}
/**
* Log specified X509 certificate details
*
* @param alias certificate alias or null if not applicable
* @param x509Cert X509 certificate
*/
static void logCert(String alias, X509Certificate x509Cert) {
X500Principal subj = x509Cert.getSubjectX500Principal();
X500Principal issuer = x509Cert.getIssuerX500Principal();
Date now = new Date();
String label = alias != null ? (alias + ": ") : "";
if (now.compareTo(x509Cert.getNotAfter()) > 0) {
Msg.warn(PKIUtils.class,
" " + label + getCommonName(subj) + ", issued by " + getCommonName(issuer) +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expired " +
x509Cert.getNotAfter() + " **EXPIRED**");
}
else {
Msg.info(PKIUtils.class,
" " + label + getCommonName(subj) + ", issued by " + getCommonName(issuer) +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expires " +
x509Cert.getNotAfter());
}
}
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -19,12 +19,11 @@ import java.io.*;
import java.net.*;
import java.util.Properties;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.util.Msg;
public class HttpUtil {
/**
* Execute an HTTP/HTTPS GET request and return the resulting HttpURLConnection.
* @param httpUrlString HTTP/HTTPS URL
@@ -35,16 +34,16 @@ public class HttpUtil {
* @throws IOException if an error occurs while executing request
*/
public static HttpURLConnection getContent(String httpUrlString,
Properties httpRequestProperties, boolean allowRedirect) throws MalformedURLException,
IOException {
Properties httpRequestProperties, boolean allowRedirect)
throws MalformedURLException, IOException {
URL url = new URL(httpUrlString);
String protocol = url.getProtocol();
if ("https".equals(protocol)) {
// force password prompt before connecting
if (!ApplicationKeyManagerFactory.initialize()) {
if (ApplicationKeyManagerFactory.getKeyStore() != null) {
if (!DefaultKeyManagerFactory.initialize()) {
if (DefaultKeyManagerFactory.getKeyStore() != null) {
// Report error condition?
throw new IOException("Failed to initialize PKI certificate keystore");
}

View File

@@ -4,9 +4,9 @@
* 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.
@@ -21,7 +21,6 @@ import java.io.File;
import java.security.*;
import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManager;
import javax.net.ssl.X509ExtendedKeyManager;
import org.junit.*;
@@ -73,8 +72,8 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
keystoreFile = createTempFile("test-key", ".p12");
keystoreFile.delete();
ApplicationKeyManagerUtils.createKeyStore(ALIAS, TEST_IDENTITY, 2, null, keystoreFile,
"PKCS12", null, TEST_PWD.toCharArray());
PKIUtils.createKeyStore(ALIAS, TEST_IDENTITY, 2, null, keystoreFile, "PKCS12", null,
TEST_PWD.toCharArray());
ApplicationKeyManagerFactory.setKeyStorePasswordProvider(passwordProvider);
}
@@ -89,12 +88,9 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
@Test
public void testCancelledPasswordOnSetCertificate() throws Exception {
assertNull(ApplicationKeyManagerFactory.getKeyStore());
ApplicationKeyManagerFactory instance = ApplicationKeyManagerFactory.getInstance();
KeyManager[] keyManagers = instance.getKeyManagers();
assertEquals(1, keyManagers.length);
assertTrue("", keyManagers[0] instanceof X509ExtendedKeyManager);
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
assertNull(DefaultKeyManagerFactory.getKeyStore());
X509ExtendedKeyManager keyManager = DefaultKeyManagerFactory.getKeyManager();
assertNotNull(keyManager);
// verify that no certs are installed
assertNull(keyManager.getCertificateChain(ALIAS));
@@ -102,10 +98,10 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
passwordProvider.cancelNextEntry();
ApplicationKeyManagerFactory.setKeyStore(keystoreFile.getAbsolutePath(), false);
DefaultKeyManagerFactory.setDefaultKeyStore(keystoreFile.getAbsolutePath(), false);
// verify that no certs are installed
assertEquals(null, ApplicationKeyManagerFactory.getKeyStore());
assertEquals(null, DefaultKeyManagerFactory.getKeyStore());
assertNull(keyManager.getCertificateChain(ALIAS));
assertNull(keyManager.getClientAliases("RSA", null));
}
@@ -113,21 +109,18 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
@Test
public void testSetClearCertificate() throws Exception {
assertNull(ApplicationKeyManagerFactory.getKeyStore());
ApplicationKeyManagerFactory instance = ApplicationKeyManagerFactory.getInstance();
KeyManager[] keyManagers = instance.getKeyManagers();
assertEquals(1, keyManagers.length);
assertTrue("", keyManagers[0] instanceof X509ExtendedKeyManager);
X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[0];
assertNull(DefaultKeyManagerFactory.getKeyStore());
X509ExtendedKeyManager keyManager = DefaultKeyManagerFactory.getKeyManager();
assertNotNull(keyManager);
// verify that no certs are installed
assertNull(keyManager.getCertificateChain(ALIAS));
assertNull(keyManager.getClientAliases("RSA", null));
ApplicationKeyManagerFactory.setKeyStore(keystoreFile.getAbsolutePath(), false);
DefaultKeyManagerFactory.setDefaultKeyStore(keystoreFile.getAbsolutePath(), false);
// verify that generated cert is installed
assertEquals(keystoreFile.getAbsolutePath(), ApplicationKeyManagerFactory.getKeyStore());
assertEquals(keystoreFile.getAbsolutePath(), DefaultKeyManagerFactory.getKeyStore());
X509Certificate[] chain = keyManager.getCertificateChain(ALIAS);
assertNotNull(chain);
String[] aliases = keyManager.getClientAliases("RSA", new Principal[0]); // any CA allowed
@@ -158,10 +151,10 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
}
// clear keystore
ApplicationKeyManagerFactory.setKeyStore(null, false);
DefaultKeyManagerFactory.setDefaultKeyStore(null, false);
// verify that no certs are installed
assertNull(ApplicationKeyManagerFactory.getKeyStore());
assertNull(DefaultKeyManagerFactory.getKeyStore());
assertNull(keyManager.getCertificateChain(ALIAS));
assertNull(keyManager.getClientAliases("RSA", null));

View File

@@ -4,9 +4,9 @@
* 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.
@@ -24,8 +24,8 @@ import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.ApplicationKeyManagerUtils;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.net.PKIUtils;
import ghidra.util.HelpLocation;
import ghidra.util.filechooser.ExtensionFileFilter;
import ghidra.util.filechooser.GhidraFileFilter;
@@ -38,7 +38,7 @@ class EditActionManager {
* PKCS Private Key/Certificate File Filter
*/
public static final GhidraFileFilter CERTIFICATE_FILE_FILTER =
new ExtensionFileFilter(ApplicationKeyManagerUtils.PKCS_FILE_EXTENSIONS, "PKCS Key File");
new ExtensionFileFilter(PKIUtils.PKCS_FILE_EXTENSIONS, "PKCS Key File");
private FrontEndPlugin plugin;
private FrontEndTool tool;
@@ -68,8 +68,8 @@ class EditActionManager {
// ACTIONS - auto generated
editPluginPathAction.setEnabled(true);
editPluginPathAction.setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_EDIT,
"Plugin Path..." }, "GEdit"));
editPluginPathAction.setMenuBarData(
new MenuData(new String[] { ToolConstants.MENU_EDIT, "Plugin Path..." }, "GEdit"));
editCertPathAction = new DockingAction("Set PKI Certificate", plugin.getName()) {
@Override
@@ -80,8 +80,8 @@ class EditActionManager {
// ACTIONS - auto generated
editCertPathAction.setEnabled(true);
editCertPathAction.setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_EDIT,
"Set PKI Certificate..." }, "PKI"));
editCertPathAction.setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_EDIT, "Set PKI Certificate..." }, "PKI"));
clearCertPathAction = new DockingAction("Clear PKI Certificate", plugin.getName()) {
@Override
@@ -90,13 +90,13 @@ class EditActionManager {
}
};
// ACTIONS - auto generated
clearCertPathAction.setEnabled(ApplicationKeyManagerFactory.getKeyStore() != null);
clearCertPathAction.setEnabled(DefaultKeyManagerFactory.getKeyStore() != null);
clearCertPathAction.setMenuBarData(new MenuData(new String[] { ToolConstants.MENU_EDIT,
"Clear PKI Certificate..." }, "PKI"));
clearCertPathAction.setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_EDIT, "Clear PKI Certificate..." }, "PKI"));
clearCertPathAction.setHelpLocation(new HelpLocation("FrontEndPlugin",
"Set_PKI_Certificate"));
clearCertPathAction
.setHelpLocation(new HelpLocation("FrontEndPlugin", "Set_PKI_Certificate"));
tool.addAction(editCertPathAction);
tool.addAction(clearCertPathAction);
tool.addAction(editPluginPathAction);
@@ -112,7 +112,7 @@ class EditActionManager {
private void clearCertPath() {
String path = ApplicationKeyManagerFactory.getKeyStore();
String path = DefaultKeyManagerFactory.getKeyStore();
if (path == null) {
// unexpected
clearCertPathAction.setEnabled(false);
@@ -124,7 +124,7 @@ class EditActionManager {
return;
}
ApplicationKeyManagerFactory.setKeyStore(null, true);
DefaultKeyManagerFactory.setDefaultKeyStore(null, true);
clearCertPathAction.setEnabled(false);
}
@@ -134,7 +134,7 @@ class EditActionManager {
File dir = null;
File oldFile = null;
String path = ApplicationKeyManagerFactory.getKeyStore();
String path = DefaultKeyManagerFactory.getKeyStore();
if (path != null) {
oldFile = new File(path);
dir = oldFile.getParentFile();
@@ -163,7 +163,7 @@ class EditActionManager {
if (file == null) {
return; // cancelled
}
ApplicationKeyManagerFactory.setKeyStore(file.getAbsolutePath(), true);
DefaultKeyManagerFactory.setDefaultKeyStore(file.getAbsolutePath(), true);
clearCertPathAction.setEnabled(true);
validInput = true;
}

View File

@@ -28,6 +28,7 @@ import docking.tool.ToolConstants;
import docking.widgets.OptionDialog;
import docking.widgets.PasswordChangeDialog;
import docking.widgets.filechooser.GhidraFileChooser;
import generic.hash.HashUtilities;
import ghidra.framework.client.ClientUtil;
import ghidra.framework.client.RepositoryAdapter;
import ghidra.framework.model.*;

View File

@@ -4,9 +4,9 @@
* 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.
@@ -33,7 +33,7 @@ import org.junit.experimental.categories.Category;
import generic.test.category.PortSensitiveCategory;
import ghidra.framework.model.ServerInfo;
import ghidra.framework.remote.GhidraServerHandle;
import ghidra.net.ApplicationKeyManagerFactory;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.server.remote.ServerTestUtil;
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
import ghidra.util.task.TaskMonitor;
@@ -46,7 +46,7 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
@Before
public void setUp() throws Exception {
System.clearProperty(ApplicationKeyManagerFactory.KEYSTORE_PATH_PROPERTY);
System.clearProperty(DefaultKeyManagerFactory.KEYSTORE_PATH_PROPERTY);
}
@After
@@ -79,7 +79,6 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
enableAnonymous);
}
static class BogusPrincipal implements Principal, java.io.Serializable {
private String username;
@@ -110,10 +109,10 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
startServer(-1, false, false, false);
ServerInfo server = new ServerInfo("localhost", ServerTestUtil.GHIDRA_TEST_SERVER_PORT);
GhidraServerHandle serverHandle =
ServerConnectTask.getGhidraServerHandle(server, TaskMonitor.DUMMY);
try {
serverHandle.getRepositoryServer(getBogusUserSubject(), new Callback[0]);
fail("serial filter rejection failed to perform");
@@ -125,7 +124,7 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
assertTrue("expected remote invalid class exceptionn",
cause instanceof InvalidClassException);
}
}
}

View File

@@ -29,6 +29,7 @@ import javax.rmi.ssl.SslRMIClientSocketFactory;
import org.apache.commons.lang3.RandomStringUtils;
import generic.hash.HashUtilities;
import generic.test.*;
import ghidra.framework.Application;
import ghidra.framework.client.*;
@@ -424,9 +425,9 @@ public class ServerTestUtil {
" Enable Anonymous Login: " + enableAnonymousAuthentication);
// Force client-side use of newly generated CA certificates
System.setProperty(ApplicationTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
System.setProperty(DefaultTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY,
getTestPkiCACertsPath());
SSLContextInitializer.initialize(true);
DefaultSSLContextInitializer.initialize(true);
ArrayList<String> argList = new ArrayList<>();
String javaCommand =
@@ -441,11 +442,11 @@ public class ServerTestUtil {
argList.add("-Xdebug");
argList.add("-Xnoagent");
argList.add("-Djava.compiler=NONE");
argList.add("-D" + ApplicationTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY + "=" +
argList.add("-D" + DefaultTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY + "=" +
getTestPkiCACertsPath());
argList.add("-D" + ApplicationKeyManagerFactory.KEYSTORE_PATH_PROPERTY + "=" +
argList.add("-D" + DefaultKeyManagerFactory.KEYSTORE_PATH_PROPERTY + "=" +
getTestPkiServerKeystorePath());
argList.add("-D" + ApplicationKeyManagerFactory.KEYSTORE_PASSWORD_PROPERTY + "=" +
argList.add("-D" + DefaultKeyManagerFactory.KEYSTORE_PASSWORD_PROPERTY + "=" +
TEST_PKI_SERVER_PASSPHRASE);
argList.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:18202"); // *: for remote debug support
argList.add("-DSystemUtilities.isTesting=true");
@@ -622,7 +623,7 @@ public class ServerTestUtil {
public static synchronized void disposeServer() {
System.setProperty(ApplicationTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY, "");
System.setProperty(DefaultTrustManagerFactory.GHIDRA_CACERTS_PATH_PROPERTY, "");
if (serverProcess != null) {
@@ -939,23 +940,22 @@ public class ServerTestUtil {
// Generate CA certificate and keystore
Msg.info(ServerTestUtil.class, "Generating self-signed CA cert: " + caPath);
PrivateKeyEntry caEntry =
ApplicationKeyManagerUtils.createKeyEntry("test-CA", TEST_PKI_CA_DN, 2, null, null,
"PKCS12", null, ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray());
ApplicationKeyManagerUtils.exportX509Certificates(caEntry.getCertificateChain(), caFile);
PrivateKeyEntry caEntry = PKIUtils.createKeyEntry("test-CA", TEST_PKI_CA_DN, 2, null, null,
"PKCS12", null, DefaultKeyManagerFactory.DEFAULT_PASSWORD.toCharArray());
PKIUtils.exportX509Certificates(caEntry.getCertificateChain(), caFile);
// Generate User/Client certificate and keystore
Msg.info(ServerTestUtil.class, "Generating test user key/cert (signed by test-CA, pwd: " +
TEST_PKI_USER_PASSPHRASE + "): " + userKeystorePath);
ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_USER_DN, 2, caEntry,
userKeystoreFile, "PKCS12", null, TEST_PKI_USER_PASSPHRASE.toCharArray());
PKIUtils.createKeyEntry("test-sig", TEST_PKI_USER_DN, 2, caEntry, userKeystoreFile,
"PKCS12", null, TEST_PKI_USER_PASSPHRASE.toCharArray());
// Generate Server certificate and keystore
Msg.info(ServerTestUtil.class, "Generating test server key/cert (signed by test-CA, pwd: " +
TEST_PKI_SERVER_PASSPHRASE + "): " + serverKeystorePath);
ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_SERVER_DN, 2, caEntry,
serverKeystoreFile, "PKCS12", null, TEST_PKI_SERVER_PASSPHRASE.toCharArray());
PKIUtils.createKeyEntry("test-sig", TEST_PKI_SERVER_DN, 2, caEntry, serverKeystoreFile,
"PKCS12", null, TEST_PKI_SERVER_PASSPHRASE.toCharArray());
}
/**