diff --git a/Ghidra/Features/Base/.launch/Ghidra.launch b/Ghidra/Features/Base/.launch/Ghidra.launch index 4b904e6635..c619ef5bd6 100644 --- a/Ghidra/Features/Base/.launch/Ghidra.launch +++ b/Ghidra/Features/Base/.launch/Ghidra.launch @@ -30,5 +30,5 @@ - + diff --git a/Ghidra/Features/FileFormats/Module.manifest b/Ghidra/Features/FileFormats/Module.manifest index ef87f38645..2d92d7d883 100644 --- a/Ghidra/Features/FileFormats/Module.manifest +++ b/Ghidra/Features/FileFormats/Module.manifest @@ -9,4 +9,4 @@ MODULE FILE LICENSE: lib/sevenzipjbinding-16.02-2.01.jar LGPL 2.1 MODULE FILE LICENSE: lib/sevenzipjbinding-all-platforms-16.02-2.01.jar LGPL 2.1 MODULE FILE LICENSE: lib/AXMLPrinter2.jar Apache License 2.0 MODULE FILE LICENSE: lib/util-1.4.0.jar BSD -MODULE FILE LICENSE: lib/bcprov-jdk15on-1.68.jar Bouncy Castle License + diff --git a/Ghidra/Features/FileFormats/build.gradle b/Ghidra/Features/FileFormats/build.gradle index edb20b7c7f..0b0681b2fe 100644 --- a/Ghidra/Features/FileFormats/build.gradle +++ b/Ghidra/Features/FileFormats/build.gradle @@ -33,7 +33,6 @@ dependencies { api ':dex-translator:2.0' api 'org.ow2.asm:asm-debug-all:4.1' - api 'org.bouncycastle:bcprov-jdk15on:1.68' api 'org.smali:baksmali:1.4.0' // TODO: upgrade to 2.2.6 api 'org.smali:dexlib:1.4.0' diff --git a/Ghidra/Features/GhidraServer/build.gradle b/Ghidra/Features/GhidraServer/build.gradle index 2c628c66ed..69970d80aa 100644 --- a/Ghidra/Features/GhidraServer/build.gradle +++ b/Ghidra/Features/GhidraServer/build.gradle @@ -35,10 +35,6 @@ dependencies { runGhidraServer project } -addExports([ - 'java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED' -]) - CopySpec yajswCopySpec = copySpec { File depsFile = file("${DEPS_DIR}/GhidraServer/${yajswRelease}.zip") File binRepoFile = file("${BIN_REPO}/Ghidra/Features/GhidraServer/${yajswRelease}.zip") diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/RepositoryManager.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/RepositoryManager.java index ca9adf4cf0..fbde511f14 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/RepositoryManager.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/RepositoryManager.java @@ -18,6 +18,7 @@ package ghidra.server; import java.io.File; import java.io.IOException; import java.net.UnknownHostException; +import java.rmi.server.RemoteServer; import java.rmi.server.ServerNotActiveException; import java.util.*; @@ -391,7 +392,7 @@ public class RepositoryManager { } } try { - host = sun.rmi.transport.tcp.TCPTransport.getClientHost(); + host = RemoteServer.getClientHost(); try { host = InetNameLookup.getCanonicalHostName(host); } diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/PKIAuthenticationModule.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/PKIAuthenticationModule.java index e7f0ac3f62..fe6532bc75 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/PKIAuthenticationModule.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/PKIAuthenticationModule.java @@ -139,7 +139,7 @@ public class PKIAuthenticationModule implements AuthenticationModule { } ApplicationKeyManagerUtils.validateClient(certChain, - ApplicationKeyManagerUtils.DEFAULT_AUTH_TYPE); + ApplicationKeyManagerUtils.RSA_TYPE); byte[] sigBytes = sigCb.getSignature(); if (sigBytes != null) { diff --git a/Ghidra/Framework/Generic/Module.manifest b/Ghidra/Framework/Generic/Module.manifest index 15fdbbff40..de2c2194e7 100644 --- a/Ghidra/Framework/Generic/Module.manifest +++ b/Ghidra/Framework/Generic/Module.manifest @@ -8,3 +8,6 @@ MODULE FILE LICENSE: lib/commons-lang3-3.9.jar Apache License 2.0 MODULE FILE LICENSE: lib/commons-io-2.6.jar Apache License 2.0 MODULE FILE LICENSE: lib/commons-text-1.6.jar Apache License 2.0 MODULE FILE LICENSE: lib/gson-2.8.6.jar Apache License 2.0 +MODULE FILE LICENSE: lib/bcpkix-jdk15on-1.69.jar Bouncy Castle License +MODULE FILE LICENSE: lib/bcprov-jdk15on-1.69.jar Bouncy Castle License +MODULE FILE LICENSE: lib/bcutil-jdk15on-1.69.jar Bouncy Castle License diff --git a/Ghidra/Framework/Generic/build.gradle b/Ghidra/Framework/Generic/build.gradle index 27ff746e1b..77daa8fc7a 100644 --- a/Ghidra/Framework/Generic/build.gradle +++ b/Ghidra/Framework/Generic/build.gradle @@ -36,14 +36,14 @@ dependencies { api "org.apache.commons:commons-text:1.6" api "commons-io:commons-io:2.6" api "com.google.code.gson:gson:2.8.6" + api 'org.bouncycastle:bcpkix-jdk15on:1.69' // requires bcutil and bcprov + api 'org.bouncycastle:bcprov-jdk15on:1.69' + api 'org.bouncycastle:bcutil-jdk15on:1.69' compileOnly "junit:junit:4.12" } ext.addExports([ - 'java.base/sun.security.x509=ALL-UNNAMED', - 'java.base/sun.security.provider=ALL-UNNAMED', - 'java.base/sun.security.util=ALL-UNNAMED', 'java.desktop/sun.awt=ALL-UNNAMED' ]) diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerFactory.java b/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerFactory.java index 67329fd58c..b9f13095db 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerFactory.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerFactory.java @@ -15,8 +15,7 @@ */ package ghidra.net; -import java.io.FileNotFoundException; -import java.io.IOException; +import java.io.*; import java.net.Socket; import java.security.*; import java.security.cert.CertificateException; @@ -549,8 +548,9 @@ public class ApplicationKeyManagerFactory { Msg.info(this, "Using self-signed certificate: " + defaultIdentity.getName()); char[] pwd = DEFAULT_PASSWORD.toCharArray(); KeyStore selfSignedKeyStore = - ApplicationKeyManagerUtils.createKeyStore(null, "JKS", pwd, "defaultSigKey", - null, defaultIdentity.getName(), null, SELF_SIGNED_DURATION_DAYS); + ApplicationKeyManagerUtils.createKeyStore("defaultSigKey", + defaultIdentity.getName(), SELF_SIGNED_DURATION_DAYS, null, + new File(newKeystorePath), "JKS", pwd); keystoreData = new ProtectedKeyStoreData(selfSignedKeyStore, pwd); isSelfSigned = true; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerUtils.java index 66dbdce181..9a6c1d4d96 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerUtils.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/net/ApplicationKeyManagerUtils.java @@ -16,8 +16,9 @@ package ghidra.net; import java.io.*; +import java.math.BigInteger; import java.security.*; -import java.security.KeyStore.PrivateKeyEntry; +import java.security.KeyStore.*; import java.security.cert.*; import java.security.cert.Certificate; import java.util.*; @@ -26,10 +27,19 @@ import javax.net.ssl.*; import javax.security.auth.DestroyFailedException; import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.style.RFC4519Style; +import org.bouncycastle.asn1.x509.*; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + import generic.random.SecureRandomFactory; import ghidra.util.Msg; -import sun.security.provider.X509Factory; -import sun.security.x509.*; +import ghidra.util.exception.AssertException; /** * ApplicationKeyManagerUtils provides public methods for utilizing @@ -37,20 +47,34 @@ import sun.security.x509.*; * (i.e., CA certificates), token signing and validation, and the ability to * generate keystores for testing or when a self-signed certificate will * suffice. - *

- * NOTE: This class makes direct use of classes within the - * sun.security.x509 package thus breaking portability. While this is - * not preferred, the ability to generate X.509 certificates and keystores - * appears to be absent from the standard java/javax packages. */ public class ApplicationKeyManagerUtils { - public static final String DEFAULT_SIGNING_ALGORITHM = "SHA1withRSA"; + public static final String RSA_TYPE = "RSA"; - public static final String DEFAULT_AUTH_TYPE = "RSA"; + private static final int KEY_SIZE = 4096; + + private static final String SIGNING_ALGORITHM = "SHA512withRSA"; private static final int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; + public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; + public static final String END_CERT = "-----END CERTIFICATE-----"; + + static { + /** + * Bouncy Castle uses its BCStyle for X500Names which reverses Distingushed Name ordering. + * This is resolved by setting the default to RFC4519 style to ensure compatibility with + * Java's internal implementation of X500Name. + *

+ * 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 + * BC code for X500 certificate processing. + * + */ + X500Name.setDefaultStyle(RFC4519Style.INSTANCE); + } + private ApplicationKeyManagerUtils() { // no instantiation - static methods only } @@ -61,9 +85,9 @@ public class ApplicationKeyManagerUtils { * @param authorities trusted certificate authorities * @param token token byte array * @return signed token object - * @throws NoSuchAlgorithmException - * @throws SignatureException - * @throws CertificateException + * @throws NoSuchAlgorithmException algorithym 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 { @@ -78,7 +102,7 @@ public class ApplicationKeyManagerUtils { continue; } X509KeyManager x509KeyManager = (X509KeyManager) keyManager; - String alias = x509KeyManager.chooseClientAlias(new String[] { DEFAULT_AUTH_TYPE }, + String alias = x509KeyManager.chooseClientAlias(new String[] { RSA_TYPE }, authorities, null); if (alias != null) { privateKey = x509KeyManager.getPrivateKey(alias); @@ -131,9 +155,9 @@ public class ApplicationKeyManagerUtils { * @param token byte array token * @param signature token signature * @return true if signature is my signature - * @throws NoSuchAlgorithmException - * @throws SignatureException - * @throws CertificateException + * @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 { @@ -144,7 +168,9 @@ public class ApplicationKeyManagerUtils { /** * Returns a list of trusted issuers (i.e., CA certificates) as established * by the {@link ApplicationTrustManagerFactory}. - * @throws CertificateException + * @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 { @@ -188,7 +214,7 @@ public class ApplicationKeyManagerUtils { * trusted based upon the active trust managers. * @param certChain X509 certificate chain * @param authType authentication type (i.e., "RSA") - * @throws CertificateException + * @throws CertificateException if certificate validation fails */ public static void validateClient(X509Certificate[] certChain, String authType) throws CertificateException { @@ -222,52 +248,144 @@ public class ApplicationKeyManagerUtils { } /** - * Pack order list of certs to create a certificate chain array - * @param certs certificates which makeup the ordered certificate chain. Null - * certificate elements will be skipped. - * @return array of certificates + * Pack ordered list of certs to create a certificate chain array + * @param cert primary certificate + * @param caCerts CA certificate chain. + * @return ordered certificate chain */ - private static Certificate[] getCertificateChain(Certificate... certs) { - List list = new ArrayList<>(); - for (Certificate cert : certs) { - if (cert != null) { - list.add(cert); - } - } - Certificate[] chain = new Certificate[list.size()]; - return list.toArray(chain); + private static Certificate[] makeCertificateChain(Certificate cert, Certificate... caCerts) { + Certificate[] chain = new Certificate[caCerts.length + 1]; + chain[0] = cert; + System.arraycopy(caCerts, 0, chain, 1, caCerts.length); + return chain; } /** - * Generate self-signed PKI X509 keystore containing both a signing key/cert - * and an encrypting key/cert. Default certificte extension specifies key usage of - * Signing which is appropriate for SSL DHE or ECDHE cipher suites. - * @param keyFile keystore file or null if not to be stored - * @param keystoreType keystore type (e.g., "JKS", "PKCS12") - * @param protectedPassphrase passphrase for protecting key and keystore - * @param alias for key/cert - * @param certExtensions specifies certificate extensions to be set or null for default - * key usage extension. Only a single alias may be specified when - * this argument is not null. - * @param dn distinguished name for principal key holder - * @param caSignerKeyEntry certificate issuer/authority (CA) private key entry or null - * for self-signed - * @param durationDays number of days from now when certificate shall expire - * @return newly generated keystore - * @throws KeyStoreException error occurred generating keystore + * 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 + * @throws CertificateEncodingException if error occurs while encoding certificate data */ - public static KeyStore createKeyStore(File keyFile, String keystoreType, - char[] protectedPassphrase, String alias, CertificateExtensions certExtensions, - String dn, PrivateKeyEntry caSignerKeyEntry, int durationDays) + public static void exportX509Certificates(Certificate[] certificates, File outFile) + throws IOException, CertificateEncodingException { + + try (FileOutputStream fout = new FileOutputStream(outFile); + PrintWriter writer = new PrintWriter(fout)) { + for (Certificate certificate : certificates) { + if (!(certificate instanceof X509Certificate)) { + continue; + } + writer.println(BEGIN_CERT); + String base64 = Base64.getEncoder().encodeToString(certificate.getEncoded()); + while (base64.length() != 0) { + int endIndex = Math.min(44, base64.length()); + String line = base64.substring(0, endIndex); + writer.println(line); + base64 = base64.substring(endIndex); + } + writer.println(END_CERT); + writer.println(); + } + } + } + + /** + * 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 + * @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 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) throws KeyStoreException { - KeyStore keyStore; - try { - keyStore = KeyStore.getInstance(keystoreType); - keyStore.load(null); + PasswordProtection pp = new PasswordProtection(protectedPassphrase); - addNewKeyPair(keyStore, alias, dn, certExtensions, protectedPassphrase, - caSignerKeyEntry, durationDays); + LoadStoreParameter loadStoreParameter = null; + if (keyFile != null && keyFile.exists()) { + loadStoreParameter = new LoadStoreParameter() { + @Override + public ProtectionParameter getProtectionParameter() { + return pp; + } + }; + } + + try { + KeyStore keyStore = KeyStore.getInstance(keystoreType); + keyStore.load(loadStoreParameter); + + SecureRandom random = SecureRandomFactory.getSecureRandom(); + + KeyPairGenerator rsa = KeyPairGenerator.getInstance(RSA_TYPE); + rsa.initialize(KEY_SIZE); + + KeyPair keyPair = rsa.generateKeyPair(); + PrivateKey issuerKey = keyPair.getPrivate(); + + byte[] encodedPublicKey = keyPair.getPublic().getEncoded(); + SubjectPublicKeyInfo bcPk = SubjectPublicKeyInfo.getInstance(encodedPublicKey); + + X500Name x500Name = new X500Name(dn); + X500Name caX500Name = x500Name; // self-signed CA if caEntry is null + KeyUsage keyUsage = new KeyUsage( + KeyUsage.digitalSignature | KeyUsage.keyEncipherment | KeyUsage.keyCertSign); + if (caEntry != null) { + // derive CA X500Name from caEntry + Certificate caCert = caEntry.getCertificate(); + if (!(caCert instanceof X509Certificate)) { + throw new CertificateException( + "Unsupported certificate type: " + caCert.getType()); + } + X509Certificate caX509Cert = (X509Certificate) caCert; + caX500Name = + new X500Name(caX509Cert.getSubjectDN().getName()); + keyUsage = new KeyUsage( + KeyUsage.digitalSignature | KeyUsage.keyEncipherment); + issuerKey = caEntry.getPrivateKey(); + } + Date notBefore = new Date(); + long durationMs = (long) durationDays * MILLISECONDS_PER_DAY; + 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); + + if (caEntry == null) { + certificateBuilder + .addExtension(Extension.basicConstraints, true, new BasicConstraints(1)); +// .addExtension(Extension.authorityKeyIdentifier, true, x509Utils.createAuthorityKeyIdentifier(bcPk)); + } + + ContentSigner contentSigner = + new JcaContentSignerBuilder(SIGNING_ALGORITHM).build(issuerKey); + + X509Certificate certificate = new JcaX509CertificateConverter() + .getCertificate(certificateBuilder.build(contentSigner)); + + Certificate[] chain; + if (caEntry == null) { + chain = new Certificate[] { certificate }; + } + else { + chain = makeCertificateChain(certificate, caEntry.getCertificateChain()); + } + + keyStore.setKeyEntry(alias, keyPair.getPrivate(), protectedPassphrase, chain); if (keyFile != null) { FileOutputStream out = new FileOutputStream(keyFile); @@ -288,155 +406,57 @@ public class ApplicationKeyManagerUtils { keyFile.setWritable(false); } + Msg.debug(ApplicationKeyManagerUtils.class, + "Certificate Generated (" + alias + "): " + dn); + + return keyStore; } - catch (GeneralSecurityException | IOException e) { - throw new KeyStoreException("failed to generate/store certificate (" + dn + ")", e); - } - - return keyStore; - } - - /** - * Generate a new keypair/certificate and add it to the specified keyStore. - * Default certificate extension specifies key usage of digital-signature which is appropriate - * for SSL (i.e., DHE or ECDHE cipher suites) and other authentication uses. - * @param keyStore key store - * @param generator key pair generator - * @param alias keypair/certificate alias - * @param dn principal distinguished name - * @param certExtensions certificate extensions with key usage - * @param protectedPassphrase key protection passphrase - * @param caSignerKeyEntry certificate issuer/authority (CA) private key - * entry or null for self-signed - * @param durationDays number of days from now when certificate shall expire - * @throws KeyStoreException error occurred generating keystore - */ - private static void addNewKeyPair(KeyStore keyStore, String alias, String dn, - CertificateExtensions certExtensions, char[] protectedPassphrase, - PrivateKeyEntry caSignerKeyEntry, int durationDays) - throws GeneralSecurityException, IOException { - - X509Certificate signerCert = null; - if (caSignerKeyEntry != null) { - Certificate cert = caSignerKeyEntry.getCertificate(); - if (!(cert instanceof X509Certificate)) { - throw new IllegalArgumentException( - "Unsupported caSignerKeyEntry - X509 certificate required"); - } - signerCert = (X509Certificate) cert; - } - - KeyPairGenerator generator = KeyPairGenerator.getInstance(DEFAULT_AUTH_TYPE); - SecureRandom random = SecureRandomFactory.getSecureRandom(); - generator.initialize(2048, random); - - X509CertInfo certInfo = new X509CertInfo(); - certInfo.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3)); - - certInfo.set(X509CertInfo.ALGORITHM_ID, - new CertificateAlgorithmId(AlgorithmId.get(DEFAULT_SIGNING_ALGORITHM))); - - certInfo.set(X509CertInfo.SUBJECT + "." + X509CertInfo.DN_NAME, new X500Name(dn)); // requires Java 1.8 - // certInfo.set(X509CertInfo.SUBJECT, new CertificateSubjectName(new X500Name(dn))); // requires Java 1.7 - - Date now = new Date(System.currentTimeMillis()); - long durationMs = (long) durationDays * (long) MILLISECONDS_PER_DAY; - Date end = new Date(now.getTime() + durationMs); - certInfo.set(X509CertInfo.VALIDITY, new CertificateValidity(now, end)); - String issuer = signerCert != null ? signerCert.getSubjectDN().getName() : dn; - - certInfo.set(X509CertInfo.ISSUER + "." + X509CertInfo.DN_NAME, new X500Name(issuer)); // requires Java 1.8 - // certInfo.set(X509CertInfo.ISSUER, new CertificateIssuerName(new X500Name(issuer))); // requires Java 1.7 - - certInfo.set(X509CertInfo.SERIAL_NUMBER, - new CertificateSerialNumber(random.nextInt() & 0x7fffffff)); - KeyPair keyPair = generator.generateKeyPair(); - certInfo.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic())); - - if (certExtensions == null) { - // If no extensions specified, set default key usage to digital-signature - // which is appropriate for SSL and other authentication uses - certExtensions = new CertificateExtensions(); - KeyUsageExtension keyUsage = new KeyUsageExtension(); - keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true); - certExtensions.set(PKIXExtensions.KeyUsage_Id.toString(), keyUsage); - } - certInfo.set(X509CertInfo.EXTENSIONS, certExtensions); - - X509CertImpl cert = new X509CertImpl(certInfo); - PrivateKey caSignerKey = - caSignerKeyEntry != null ? caSignerKeyEntry.getPrivateKey() : keyPair.getPrivate(); - cert.sign(caSignerKey, DEFAULT_SIGNING_ALGORITHM); - keyStore.setKeyEntry(alias, keyPair.getPrivate(), protectedPassphrase, - getCertificateChain(cert, signerCert)); - Msg.debug(ApplicationKeyManagerUtils.class, - "Certificate Generated (" + alias + "): " + cert.getSubjectDN()); - } - - /** - * Export all X.509 certificates contained within keystore to the specified outFile. - * @param keystore - * @param outFile output file - * @throws IOException - * @throws KeyStoreException - * @throws CertificateEncodingException - */ - public static void exportX509Certificates(KeyStore keystore, File outFile) - throws IOException, KeyStoreException, CertificateEncodingException { - FileOutputStream fout = new FileOutputStream(outFile); - PrintWriter writer = new PrintWriter(fout); - Enumeration aliases = keystore.aliases(); - while (aliases.hasMoreElements()) { - Certificate certificate = keystore.getCertificate(aliases.nextElement()); - if (!(certificate instanceof X509Certificate)) { - continue; - } - writer.println(X509Factory.BEGIN_CERT); - String base64 = Base64.getEncoder().encodeToString(certificate.getEncoded()); - while (base64.length() != 0) { - int endIndex = Math.min(44, base64.length()); - String line = base64.substring(0, endIndex); - writer.println(line); - base64 = base64.substring(endIndex); - } - writer.println(X509Factory.END_CERT); - writer.println(); - } - writer.flush(); - try { - fout.getFD().sync(); - } - catch (SyncFailedException e) { - // ignore - } - writer.close(); - } - - /** - * Export all X.509 certificates contained within keystore to the specified outFile. - * @param keystore - * @param outFile output file - * @param password keystore password - * @throws CertificateException - * @throws NoSuchAlgorithmException - * @throws FileNotFoundException - * @throws KeyStoreException - * @throws CertificateEncodingException - */ - public static void exportKeystore(KeyStore keystore, File outFile, char[] password) - throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException { - - FileOutputStream out = new FileOutputStream(outFile); - try { - keystore.store(out, password); - out.flush(); - out.getFD().sync(); - } - catch (SyncFailedException e) { - // ignore + catch (GeneralSecurityException | OperatorException | IOException e) { + throw new KeyStoreException("Failed to generate/store certificate (" + dn + ")", e); } finally { - out.close(); + try { + pp.destroy(); + } + catch (DestroyFailedException e) { + throw new AssertException(e); // unexpected for simple password clearing + } + } + } + + /** + * 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 + * @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 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) + throws KeyStoreException { + + PasswordProtection pp = new PasswordProtection(protectedPassphrase); + try { + KeyStore keyStore = createKeyStore(alias, dn, durationDays, caEntry, keyFile, + keystoreType, protectedPassphrase); + return (PrivateKeyEntry) keyStore.getEntry(alias, pp); + } + catch (NoSuchAlgorithmException | UnrecoverableEntryException e) { + throw new KeyStoreException("Failed to generate/store certificate (" + dn + ")", e); + } + finally { + try { + pp.destroy(); + } + catch (DestroyFailedException e) { + throw new AssertException(e); // unexpected for simple password clearing + } } } diff --git a/Ghidra/Framework/Generic/src/test/java/ghidra/net/ApplicationKeyManagerFactoryTest.java b/Ghidra/Framework/Generic/src/test/java/ghidra/net/ApplicationKeyManagerFactoryTest.java index 09df2dc0fb..7670ac47f3 100644 --- a/Ghidra/Framework/Generic/src/test/java/ghidra/net/ApplicationKeyManagerFactoryTest.java +++ b/Ghidra/Framework/Generic/src/test/java/ghidra/net/ApplicationKeyManagerFactoryTest.java @@ -70,13 +70,11 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest { @Before public void setUp() throws Exception { - KeyStore selfSignedKeyStore = ApplicationKeyManagerUtils.createKeyStore(null, "PKCS12", - TEST_PWD.toCharArray(), ALIAS, null, TEST_IDENTITY, null, 2); - keystoreFile = createTempFile("test-key", ".p12"); keystoreFile.delete(); - ApplicationKeyManagerUtils.exportKeystore(selfSignedKeyStore, keystoreFile, - TEST_PWD.toCharArray()); + + ApplicationKeyManagerUtils.createKeyStore(ALIAS, TEST_IDENTITY, 2, null, keystoreFile, + "PKCS12", TEST_PWD.toCharArray()); ApplicationKeyManagerFactory.setKeyStorePasswordProvider(passwordProvider); } diff --git a/Ghidra/RuntimeScripts/Common/support/launch.properties b/Ghidra/RuntimeScripts/Common/support/launch.properties index b427d091b0..735d476abb 100644 --- a/Ghidra/RuntimeScripts/Common/support/launch.properties +++ b/Ghidra/RuntimeScripts/Common/support/launch.properties @@ -74,8 +74,6 @@ VMARGS=--add-opens java.base/java.lang=ALL-UNNAMED VMARGS=--add-opens java.base/java.util=ALL-UNNAMED VMARGS=--add-opens java.base/java.net=ALL-UNNAMED VMARGS=--add-opens java.desktop/sun.awt.image=ALL-UNNAMED -VMARGS=--add-opens java.base/sun.security.x509=ALL-UNNAMED -VMARGS=--add-opens java.base/sun.security.util=ALL-UNNAMED VMARGS_LINUX=--add-opens java.desktop/sun.awt.X11=ALL-UNNAMED # Persistent cache directory used by the application. This directory will be used to store diff --git a/Ghidra/Test/IntegrationTest/build.gradle b/Ghidra/Test/IntegrationTest/build.gradle index be145a56bc..b2ba16df34 100644 --- a/Ghidra/Test/IntegrationTest/build.gradle +++ b/Ghidra/Test/IntegrationTest/build.gradle @@ -66,8 +66,6 @@ dependencies { // We export them to all "unnamed" modules, which are modules that don't define themselves // as a new Java 9 style module. Ghidra is currently using unnamed modules everywhere. ext.addExports([ - 'java.base/sun.security.x509=ALL-UNNAMED', - 'java.base/sun.security.util=ALL-UNNAMED', 'java.desktop/sun.awt=ALL-UNNAMED', 'java.desktop/sun.swing=ALL-UNNAMED', 'java.desktop/sun.java2d=ALL-UNNAMED' diff --git a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/remote/ServerTestUtil.java b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/remote/ServerTestUtil.java index 15774baf56..3d863fb830 100644 --- a/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/remote/ServerTestUtil.java +++ b/Ghidra/Test/IntegrationTest/src/test.slow/java/ghidra/server/remote/ServerTestUtil.java @@ -19,8 +19,6 @@ import java.io.*; import java.net.*; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; -import java.security.KeyStore; -import java.security.KeyStore.PasswordProtection; import java.security.KeyStore.PrivateKeyEntry; import java.util.ArrayList; import java.util.zip.ZipEntry; @@ -49,7 +47,6 @@ import ghidra.util.*; import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; import ghidra.util.timer.GTimer; -import sun.security.x509.*; import utilities.util.FileUtilities; /** @@ -902,38 +899,23 @@ public class ServerTestUtil { // Generate CA certificate and keystore Msg.info(ServerTestUtil.class, "Generating self-signed CA cert: " + caPath); - - CertificateExtensions caCertExtensions = new CertificateExtensions(); - BasicConstraintsExtension caBasicConstraints = new BasicConstraintsExtension(true, true, 1); - caCertExtensions.set(PKIXExtensions.BasicConstraints_Id.toString(), caBasicConstraints); - - KeyUsageExtension caKeyUsage = new KeyUsageExtension(); - caKeyUsage.set(KeyUsageExtension.KEY_CERTSIGN, true); - caCertExtensions.set(PKIXExtensions.KeyUsage_Id.toString(), caKeyUsage); - - KeyStore caKeystore = ApplicationKeyManagerUtils.createKeyStore(null, "PKCS12", - ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray(), "test-CA", - caCertExtensions, TEST_PKI_CA_DN, null, 2); - ApplicationKeyManagerUtils.exportX509Certificates(caKeystore, caFile); - - PasswordProtection caPass = - new PasswordProtection(ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray()); - PrivateKeyEntry caPrivateKeyEntry = - (PrivateKeyEntry) caKeystore.getEntry("test-CA", caPass); + PrivateKeyEntry caEntry = + ApplicationKeyManagerUtils.createKeyEntry("test-CA", TEST_PKI_CA_DN, 2, null, null, + "PKCS12", 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.createKeyStore(userKeystoreFile, "PKCS12", - TEST_PKI_USER_PASSPHRASE.toCharArray(), "test-sig", null, TEST_PKI_USER_DN, - caPrivateKeyEntry, 2); + ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_USER_DN, 2, caEntry, + userKeystoreFile, "PKCS12", 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.createKeyStore(serverKeystoreFile, "PKCS12", - TEST_PKI_SERVER_PASSPHRASE.toCharArray(), "test-sig", null, TEST_PKI_SERVER_DN, - caPrivateKeyEntry, 2); + + ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_SERVER_DN, 2, caEntry, + serverKeystoreFile, "PKCS12", TEST_PKI_SERVER_PASSPHRASE.toCharArray()); } /** diff --git a/gradle/root/distribution.gradle b/gradle/root/distribution.gradle index a8f88b0f16..1902df1225 100644 --- a/gradle/root/distribution.gradle +++ b/gradle/root/distribution.gradle @@ -135,10 +135,7 @@ task createJavadocs(type: Javadoc, description: 'Generate javadocs for all proje // Some internal packages are not public and need to be exported. options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED", - "java.desktop/sun.awt=ALL-UNNAMED", - "java.base/sun.security.x509=ALL-UNNAMED", - "java.base/sun.security.provider=ALL-UNNAMED", - "java.base/sun.security.util=ALL-UNNAMED"]) + "java.desktop/sun.awt=ALL-UNNAMED"]) } @@ -197,10 +194,7 @@ task createJsondocs(type: Javadoc, description: 'Generate JSON docs for all proj // Some internal packages are not public and need to be exported. options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED", - "java.desktop/sun.awt=ALL-UNNAMED", - "java.base/sun.security.x509=ALL-UNNAMED", - "java.base/sun.security.provider=ALL-UNNAMED", - "java.base/sun.security.util=ALL-UNNAMED"]) + "java.desktop/sun.awt=ALL-UNNAMED"]) options.doclet = "JsonDoclet" doFirst {