mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 13:16:48 +08:00
Merge remote-tracking branch
'origin/GP-3050-2935-ghidra1_ServerAddressAndConnectTimeout--SQUASHED' (Closes #4924, Closes #4928)
This commit is contained in:
@@ -32,7 +32,6 @@ import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.dialog.ExtensionUtils;
|
||||
import ghidra.framework.project.DefaultProjectManager;
|
||||
import ghidra.framework.remote.InetNameLookup;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.util.*;
|
||||
@@ -94,9 +93,6 @@ public class GhidraRun implements GhidraLaunchable {
|
||||
});
|
||||
};
|
||||
|
||||
// Automatically disable reverse name lookup if failure occurs
|
||||
InetNameLookup.setDisableOnFailure(true);
|
||||
|
||||
// Start main thread in GhidraThreadGroup
|
||||
Thread mainThread = new Thread(new GhidraThreadGroup(), mainTask, "Ghidra");
|
||||
mainThread.start();
|
||||
|
||||
@@ -25,9 +25,9 @@ import java.util.*;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import ghidra.framework.remote.InetNameLookup;
|
||||
import ghidra.framework.store.local.IndexedLocalFileSystem;
|
||||
import ghidra.framework.store.local.LocalFileSystem;
|
||||
import ghidra.server.remote.InetNameLookup;
|
||||
import ghidra.server.remote.RepositoryServerHandleImpl;
|
||||
import ghidra.util.NamingUtilities;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
@@ -751,6 +751,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
// keystore has not been identified - use self-signed certificate
|
||||
ApplicationKeyManagerFactory.setDefaultIdentity(
|
||||
new X500Principal("CN=GhidraServer"));
|
||||
ApplicationKeyManagerFactory.addSubjectAlternativeName(hostname);
|
||||
}
|
||||
if (!ApplicationKeyManagerFactory.initialize()) {
|
||||
log.fatal("Failed to initialize PKI/SSL keystore");
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.framework.remote;
|
||||
package ghidra.server.remote;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -18,7 +18,6 @@ package ghidra.framework.client;
|
||||
import java.awt.Component;
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.UnknownHostException;
|
||||
import java.rmi.*;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Arrays;
|
||||
@@ -50,7 +49,7 @@ public class ClientUtil {
|
||||
|
||||
/**
|
||||
* Set client authenticator
|
||||
* @param authenticator
|
||||
* @param authenticator client authenticator instance
|
||||
*/
|
||||
public static synchronized void setClientAuthenticator(ClientAuthenticator authenticator) {
|
||||
clientAuthenticator = authenticator;
|
||||
@@ -106,14 +105,6 @@ public class ClientUtil {
|
||||
// ensure that default callback is setup if possible
|
||||
getClientAuthenticator();
|
||||
|
||||
host = host.trim().toLowerCase();
|
||||
try {
|
||||
host = InetNameLookup.getCanonicalHostName(host);
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
Msg.warn(ClientUtil.class, "Failed to resolve hostname for " + host);
|
||||
}
|
||||
|
||||
if (port <= 0) {
|
||||
port = GhidraServerHandle.DEFAULT_PORT;
|
||||
}
|
||||
@@ -145,22 +136,14 @@ public class ClientUtil {
|
||||
* Eliminate the specified repository server from the connection cache
|
||||
* @param host host name or IP address
|
||||
* @param port port (0: use default port)
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void clearRepositoryAdapter(String host, int port) throws IOException {
|
||||
host = host.trim().toLowerCase();
|
||||
String hostAddr = host;
|
||||
try {
|
||||
hostAddr = InetNameLookup.getCanonicalHostName(host);
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
throw new IOException("Repository server lookup failed: " + host);
|
||||
}
|
||||
public static void clearRepositoryAdapter(String host, int port) {
|
||||
|
||||
if (port == 0) {
|
||||
port = GhidraServerHandle.DEFAULT_PORT;
|
||||
}
|
||||
ServerInfo server = new ServerInfo(hostAddr, port);
|
||||
|
||||
ServerInfo server = new ServerInfo(host, port);
|
||||
RepositoryServerAdapter serverAdapter = serverHandles.remove(server);
|
||||
if (serverAdapter != null) {
|
||||
serverAdapter.disconnect();
|
||||
@@ -170,6 +153,7 @@ public class ClientUtil {
|
||||
/**
|
||||
* Returns default user login name. Actual user name used by repository
|
||||
* should be obtained from RepositoryServerAdapter.getUser
|
||||
* @return default user name
|
||||
*/
|
||||
public static String getUserName() {
|
||||
String name = SystemUtilities.getUserName();
|
||||
@@ -372,7 +356,7 @@ public class ClientUtil {
|
||||
* @param parent dialog parent
|
||||
* @param handle server handle
|
||||
* @param serverInfo server information
|
||||
* @throws IOException
|
||||
* @throws IOException if error occurs while updating password
|
||||
*/
|
||||
public static void changePassword(Component parent, RepositoryServerHandle handle,
|
||||
String serverInfo) throws IOException {
|
||||
@@ -451,7 +435,7 @@ public class ClientUtil {
|
||||
sigCb.getRecognizedAuthorities(), sigCb.getToken());
|
||||
sigCb.sign(signedToken.certChain, signedToken.signature);
|
||||
Msg.info(ClientUtil.class, "PKI Authenticating to " + serverName + " as user '" +
|
||||
signedToken.certChain[0].getSubjectDN() + "'");
|
||||
signedToken.certChain[0].getSubjectX500Principal() + "'");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String msg = e.getMessage();
|
||||
|
||||
+57
-36
@@ -18,10 +18,12 @@ package ghidra.framework.client;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.rmi.*;
|
||||
import java.rmi.registry.LocateRegistry;
|
||||
import java.rmi.registry.Registry;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.HashSet;
|
||||
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
@@ -46,6 +48,8 @@ import ghidra.util.task.*;
|
||||
*/
|
||||
class ServerConnectTask extends Task {
|
||||
|
||||
private static final int LIVENESS_CHECK_TIMEOUT_MS = 3000;
|
||||
|
||||
private ServerInfo server;
|
||||
//private String defaultUserID;
|
||||
private boolean allowLoginRetry;
|
||||
@@ -98,6 +102,7 @@ class ServerConnectTask extends Task {
|
||||
* if handle is null after running task. If both the exception
|
||||
* and handle are null, it implies the connection attempt was cancelled
|
||||
* by the user.
|
||||
* @return exception which occured during a failed connection attempt, or null
|
||||
*/
|
||||
Exception getException() {
|
||||
return exc;
|
||||
@@ -123,16 +128,6 @@ class ServerConnectTask extends Task {
|
||||
return subj;
|
||||
}
|
||||
|
||||
private static String getPreferredHostname(String name) {
|
||||
try {
|
||||
return InetNameLookup.getCanonicalHostName(name);
|
||||
}
|
||||
catch (UnknownHostException e) {
|
||||
Msg.warn(ServerConnectTask.class, "Failed to resolve hostname for " + name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static boolean isSSLHandshakeCancelled(SSLHandshakeException e) throws IOException {
|
||||
if (e.getMessage().indexOf("bad_certificate") > 0) {
|
||||
if (ApplicationKeyManagerFactory.getPreferredKeyStore() == null) {
|
||||
@@ -154,8 +149,8 @@ class ServerConnectTask extends Task {
|
||||
* @param server server information
|
||||
* @param monitor cancellable monitor
|
||||
* @return Ghidra Server Handle object
|
||||
* @throws IOException
|
||||
* @throws CancelledException
|
||||
* @throws IOException if a connection error occurs
|
||||
* @throws CancelledException if connection attempt was cancelled
|
||||
*/
|
||||
public static GhidraServerHandle getGhidraServerHandle(ServerInfo server, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
@@ -163,6 +158,7 @@ class ServerConnectTask extends Task {
|
||||
GhidraServerHandle gsh = null;
|
||||
boolean canCancel = monitor.isCancelEnabled(); // original state
|
||||
try {
|
||||
|
||||
// Test SSL Handshake to ensure that user is able to decrypt keystore.
|
||||
// This is intended to work around an RMI issue where a continuous
|
||||
// retry condition can occur when a user cancels the password entry
|
||||
@@ -172,17 +168,10 @@ class ServerConnectTask extends Task {
|
||||
monitor.setCancelEnabled(false);
|
||||
monitor.setMessage("Connecting...");
|
||||
|
||||
Registry reg;
|
||||
try {
|
||||
// attempt to connect with older Ghidra Server registry without using SSL/TLS
|
||||
reg = LocateRegistry.getRegistry(server.getServerName(), server.getPortNumber());
|
||||
checkServerBindNames(reg);
|
||||
}
|
||||
catch (IOException e) {
|
||||
reg = LocateRegistry.getRegistry(server.getServerName(), server.getPortNumber(),
|
||||
Registry reg =
|
||||
LocateRegistry.getRegistry(server.getServerName(), server.getPortNumber(),
|
||||
new SslRMIClientSocketFactory());
|
||||
checkServerBindNames(reg);
|
||||
}
|
||||
checkServerBindNames(reg);
|
||||
|
||||
gsh = (GhidraServerHandle) reg.lookup(GhidraServerHandle.BIND_NAME);
|
||||
gsh.checkCompatibility(GhidraServerHandle.INTERFACE_VERSION);
|
||||
@@ -241,20 +230,17 @@ class ServerConnectTask extends Task {
|
||||
|
||||
/**
|
||||
* Attempts server connection and completes any necessary authentication.
|
||||
* @param defaultUserID
|
||||
* @param defaultUserID default user ID (actual ID used established during authentication)
|
||||
* @param monitor task monitor for connection cancellation
|
||||
* @return server handle or null if authentication or connection attempt was cancelled by user
|
||||
* @throws IOException
|
||||
* @throws LoginException
|
||||
* @throws IOException if server connection fails
|
||||
* @throws LoginException login failure
|
||||
*/
|
||||
private RemoteRepositoryServerHandle getRepositoryServerHandle(String defaultUserID,
|
||||
TaskMonitor monitor)
|
||||
throws IOException, LoginException, CancelledException {
|
||||
|
||||
GhidraServerHandle gsh = getGhidraServerHandle(server, monitor);
|
||||
if (gsh == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Callback[] callbacks = null;
|
||||
try {
|
||||
@@ -275,7 +261,6 @@ class ServerConnectTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
String serverName = getPreferredHostname(server.getServerName());
|
||||
AnonymousCallback onlyAnonymousCb = null;
|
||||
while (true) {
|
||||
try {
|
||||
@@ -298,8 +283,8 @@ class ServerConnectTask extends Task {
|
||||
// SSH option only available in conjunction with password
|
||||
// based authentication which will be used if SSH attempt fails
|
||||
hasSSHSignatureCallback = false; // only try SSH once
|
||||
ClientUtil.processSSHSignatureCallback(callbacks, serverName,
|
||||
defaultUserID);
|
||||
ClientUtil.processSSHSignatureCallback(callbacks,
|
||||
server.getServerName(), defaultUserID);
|
||||
}
|
||||
else if (pkiSignatureCb != null) {
|
||||
// when using PKI - no other authentication callback will be used
|
||||
@@ -317,14 +302,15 @@ class ServerConnectTask extends Task {
|
||||
}
|
||||
|
||||
loopOK = false; // only try once
|
||||
ClientUtil.processSignatureCallback(serverName, pkiSignatureCb);
|
||||
ClientUtil.processSignatureCallback(server.getServerName(),
|
||||
pkiSignatureCb);
|
||||
}
|
||||
else {
|
||||
// assume all other callback scenarios are password based
|
||||
// anonymous option must be explicitly chosen over username/password
|
||||
// when processing password callback
|
||||
if (!ClientUtil.processPasswordCallbacks(callbacks, serverName,
|
||||
defaultUserID, loginError)) {
|
||||
if (!ClientUtil.processPasswordCallbacks(callbacks,
|
||||
server.getServerName(), defaultUserID, loginError)) {
|
||||
return null; // Cancelled by user
|
||||
}
|
||||
}
|
||||
@@ -336,7 +322,7 @@ class ServerConnectTask extends Task {
|
||||
gsh.getRepositoryServer(getLocalUserSubject(), callbacks);
|
||||
if (rsh.isReadOnly()) {
|
||||
Msg.showInfo(this, null, "Anonymous Server Login",
|
||||
"You have been logged-in anonymously to " + serverName +
|
||||
"You have been logged-in anonymously to " + server.getServerName() +
|
||||
"\nRead-only permission is granted to repositories which allow anonymous access");
|
||||
}
|
||||
return rsh;
|
||||
@@ -374,7 +360,30 @@ class ServerConnectTask extends Task {
|
||||
}
|
||||
}
|
||||
|
||||
private static void testServerSSLConnection(ServerInfo server, TaskMonitor monitor)
|
||||
/**
|
||||
* Socket implementation with very short connect timeout
|
||||
*/
|
||||
private static class FastConnectionFailSocket extends Socket {
|
||||
FastConnectionFailSocket(String host, int port) throws UnknownHostException, IOException {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
public void connect(SocketAddress endpoint) throws IOException {
|
||||
connect(endpoint, LIVENESS_CHECK_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an SSLSocket connection in order to ensure that any neccesary client/server
|
||||
* certificate validation is performed.
|
||||
* @param server server to which connection should be verified. For the Ghidra Server
|
||||
* this should correspond to the RMI Registry port {@link GhidraServerHandle#DEFAULT_PORT}.
|
||||
* @param monitor connection task monitor
|
||||
* @return certificate chain of server
|
||||
* @throws IOException if connection failure occurs
|
||||
* @throws CancelledException if connection attempt is cancelled
|
||||
*/
|
||||
private static Certificate[] testServerSSLConnection(ServerInfo server, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
RMIServerPortFactory portFactory = new RMIServerPortFactory(server.getPortNumber());
|
||||
@@ -384,7 +393,18 @@ class ServerConnectTask extends Task {
|
||||
|
||||
monitor.setCancelEnabled(true);
|
||||
monitor.setMessage("Checking Server Liveness...");
|
||||
|
||||
// Perform simple socket test connection with short timeout to verify connectivity.
|
||||
try (Socket socket = new FastConnectionFailSocket(serverName, sslRmiPort);
|
||||
ConnectCancelledListener cancelListener =
|
||||
new ConnectCancelledListener(monitor, () -> forceClose(socket))) {
|
||||
// do nothing - connect occurs during instantiation
|
||||
}
|
||||
finally {
|
||||
monitor.checkCanceled(); // circumvent any IOException which may have occured
|
||||
}
|
||||
|
||||
// Perform secure socket test connection to prime keystore use without RMI involvement
|
||||
try (SSLSocket socket = (SSLSocket) factory.createSocket(serverName, sslRmiPort);
|
||||
ConnectCancelledListener cancelListener =
|
||||
new ConnectCancelledListener(monitor, () -> forceClose(socket))) {
|
||||
@@ -392,6 +412,7 @@ class ServerConnectTask extends Task {
|
||||
// which will give user ability to cancel without involving RMI which
|
||||
// will avoid RMI reconnect attempts
|
||||
socket.startHandshake();
|
||||
return socket.getSession().getPeerCertificates();
|
||||
}
|
||||
finally {
|
||||
monitor.checkCanceled(); // circumvent any IOException which may have occured
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,7 +37,8 @@ public class ServerInfo implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server name.
|
||||
* Get the server hostname or IP address as originally specified.
|
||||
* @return hostname or IP address as originally specified
|
||||
*/
|
||||
public String getServerName() {
|
||||
return host;
|
||||
@@ -46,6 +46,7 @@ public class ServerInfo implements Serializable {
|
||||
|
||||
/**
|
||||
* Get the port number.
|
||||
* @return port number
|
||||
*/
|
||||
public int getPortNumber() {
|
||||
return portNumber;
|
||||
|
||||
+37
-4
@@ -21,7 +21,7 @@ import java.net.Socket;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
@@ -62,6 +62,7 @@ public class ApplicationKeyManagerFactory {
|
||||
|
||||
private static KeyStorePasswordProvider customPasswordProvider;
|
||||
private static X500Principal defaultIdentity;
|
||||
private static List<String> subjectAlternativeNames;
|
||||
|
||||
private static ApplicationKeyManagerFactory instance;
|
||||
|
||||
@@ -182,16 +183,48 @@ public class ApplicationKeyManagerFactory {
|
||||
/**
|
||||
* Set the default self-signed principal identity to be used during initialization
|
||||
* if no keystore defined. Current application key manager will be invalidated.
|
||||
* @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
|
||||
* (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
|
||||
@@ -548,7 +581,7 @@ public class ApplicationKeyManagerFactory {
|
||||
KeyStore selfSignedKeyStore =
|
||||
ApplicationKeyManagerUtils.createKeyStore("defaultSigKey",
|
||||
defaultIdentity.getName(), SELF_SIGNED_DURATION_DAYS, null, null, "JKS",
|
||||
pwd);
|
||||
subjectAlternativeNames, pwd);
|
||||
keystoreData = new ProtectedKeyStoreData(selfSignedKeyStore, pwd);
|
||||
isSelfSigned = true;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.OperatorException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.bouncycastle.util.IPAddress;
|
||||
|
||||
import generic.random.SecureRandomFactory;
|
||||
import ghidra.util.Msg;
|
||||
@@ -301,15 +302,19 @@ public class ApplicationKeyManagerUtils {
|
||||
* @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
|
||||
* @param caEntry optional CA private key entry. If null, a self-signed CA certificate will be generated.
|
||||
* @param caEntry optional CA private key entry. If null, a self-signed CA certificate will be
|
||||
* generated.
|
||||
* @param keyFile optional file to load/store resulting {@link KeyStore} (may be null)
|
||||
* @param keystoreType support keystore type (e.g., "JKS", "PKCS12")
|
||||
* @param subjectAlternativeNames an optional list of subject alternative names to be included
|
||||
* in certificate (may be null)
|
||||
* @param protectedPassphrase key and keystore protection password
|
||||
* @return keystore containing newly generated certification with key pair
|
||||
* @throws KeyStoreException if error occurs while updating keystore
|
||||
*/
|
||||
public static final KeyStore createKeyStore(String alias, String dn, int durationDays,
|
||||
PrivateKeyEntry caEntry, File keyFile, String keystoreType, char[] protectedPassphrase)
|
||||
PrivateKeyEntry caEntry, File keyFile, String keystoreType,
|
||||
Collection<String> subjectAlternativeNames, char[] protectedPassphrase)
|
||||
throws KeyStoreException {
|
||||
|
||||
PasswordProtection pp = new PasswordProtection(protectedPassphrase);
|
||||
@@ -352,9 +357,8 @@ public class ApplicationKeyManagerUtils {
|
||||
}
|
||||
X509Certificate caX509Cert = (X509Certificate) caCert;
|
||||
caX500Name =
|
||||
new X500Name(caX509Cert.getSubjectDN().getName());
|
||||
keyUsage = new KeyUsage(
|
||||
KeyUsage.digitalSignature | KeyUsage.keyEncipherment);
|
||||
new X500Name(caX509Cert.getSubjectX500Principal().getName());
|
||||
keyUsage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment);
|
||||
issuerKey = caEntry.getPrivateKey();
|
||||
}
|
||||
Date notBefore = new Date();
|
||||
@@ -362,18 +366,23 @@ public class ApplicationKeyManagerUtils {
|
||||
Date notAfter = new Date(notBefore.getTime() + durationMs);
|
||||
BigInteger serialNumber = new BigInteger(128, random);
|
||||
|
||||
// JcaX509ExtensionUtils x509Utils = new JcaX509ExtensionUtils();
|
||||
|
||||
X509v3CertificateBuilder certificateBuilder = new X509v3CertificateBuilder(caX500Name,
|
||||
serialNumber, notBefore, notAfter, x500Name, bcPk);
|
||||
certificateBuilder
|
||||
// .addExtension(Extension.subjectKeyIdentifier, true, x509Utils.createSubjectKeyIdentifier(bcPk))
|
||||
.addExtension(Extension.keyUsage, true, keyUsage);
|
||||
|
||||
certificateBuilder.addExtension(Extension.keyUsage, true, keyUsage);
|
||||
if (subjectAlternativeNames != null && !subjectAlternativeNames.isEmpty()) {
|
||||
List<GeneralName> nameList = new ArrayList<GeneralName>();
|
||||
for (String altName : subjectAlternativeNames) {
|
||||
int nameType =
|
||||
IPAddress.isValid(altName) ? GeneralName.iPAddress : GeneralName.dNSName;
|
||||
nameList.add(new GeneralName(nameType, altName));
|
||||
}
|
||||
GeneralName[] altNames = nameList.toArray(GeneralName[]::new);
|
||||
certificateBuilder.addExtension(Extension.subjectAlternativeName, false,
|
||||
new GeneralNames(altNames));
|
||||
}
|
||||
if (caEntry == null) {
|
||||
certificateBuilder
|
||||
.addExtension(Extension.basicConstraints, true, new BasicConstraints(1));
|
||||
// .addExtension(Extension.authorityKeyIdentifier, true, x509Utils.createAuthorityKeyIdentifier(bcPk));
|
||||
certificateBuilder.addExtension(Extension.basicConstraints, true,
|
||||
new BasicConstraints(1));
|
||||
}
|
||||
|
||||
ContentSigner contentSigner =
|
||||
@@ -438,18 +447,21 @@ public class ApplicationKeyManagerUtils {
|
||||
* @param caEntry optional CA private key entry. If null, a self-signed CA certificate will be generated.
|
||||
* @param keyFile optional file to load/store resulting {@link KeyStore} (may be null)
|
||||
* @param keystoreType support keystore type (e.g., "JKS", "PKCS12")
|
||||
* @param subjectAlternativeNames an optional list of subject alternative names to be included
|
||||
* in certificate (may be null)
|
||||
* @param protectedPassphrase key and keystore protection password
|
||||
* @return newly generated keystore entry with key pair
|
||||
* @throws KeyStoreException if error occurs while updating keystore
|
||||
*/
|
||||
public static final PrivateKeyEntry createKeyEntry(String alias, String dn, int durationDays,
|
||||
PrivateKeyEntry caEntry, File keyFile, String keystoreType, char[] protectedPassphrase)
|
||||
PrivateKeyEntry caEntry, File keyFile, String keystoreType,
|
||||
Collection<String> subjectAlternativeNames, char[] protectedPassphrase)
|
||||
throws KeyStoreException {
|
||||
|
||||
PasswordProtection pp = new PasswordProtection(protectedPassphrase);
|
||||
try {
|
||||
KeyStore keyStore = createKeyStore(alias, dn, durationDays, caEntry, keyFile,
|
||||
keystoreType, protectedPassphrase);
|
||||
keystoreType, subjectAlternativeNames, protectedPassphrase);
|
||||
return (PrivateKeyEntry) keyStore.getEntry(alias, pp);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | UnrecoverableEntryException e) {
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
|
||||
keystoreFile.delete();
|
||||
|
||||
ApplicationKeyManagerUtils.createKeyStore(ALIAS, TEST_IDENTITY, 2, null, keystoreFile,
|
||||
"PKCS12", TEST_PWD.toCharArray());
|
||||
"PKCS12", null, TEST_PWD.toCharArray());
|
||||
|
||||
ApplicationKeyManagerFactory.setKeyStorePasswordProvider(passwordProvider);
|
||||
}
|
||||
|
||||
+3
-3
@@ -943,21 +943,21 @@ public class ServerTestUtil {
|
||||
Msg.info(ServerTestUtil.class, "Generating self-signed CA cert: " + caPath);
|
||||
PrivateKeyEntry caEntry =
|
||||
ApplicationKeyManagerUtils.createKeyEntry("test-CA", TEST_PKI_CA_DN, 2, null, null,
|
||||
"PKCS12", ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray());
|
||||
"PKCS12", null, ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray());
|
||||
ApplicationKeyManagerUtils.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", TEST_PKI_USER_PASSPHRASE.toCharArray());
|
||||
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", TEST_PKI_SERVER_PASSPHRASE.toCharArray());
|
||||
serverKeystoreFile, "PKCS12", null, TEST_PKI_SERVER_PASSPHRASE.toCharArray());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user