mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
GP-3050-2935 Eliminate client-side canonical hostname lookups. Add subject alternative name to self-signed server certs. Improved initial server connect check to use faster timeout.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user