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:
ghidra1
2023-02-03 14:21:49 -05:00
parent 9014d10edf
commit 67677174c4
11 changed files with 139 additions and 91 deletions

View File

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

View File

@@ -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) {

View File

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