GP-1296 Eliminated use of sun.security package and refactored ApplicationKeyManagerUtils

This commit is contained in:
ghidra1 2021-09-15 13:55:20 -04:00
parent 8b2ea61e27
commit 9db26bc7f5
15 changed files with 252 additions and 263 deletions

View file

@ -30,5 +30,5 @@
<stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="Framework Utility"/> <stringAttribute key="org.eclipse.jdt.launching.MODULE_NAME" value="Framework Utility"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.GhidraRun"/> <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="ghidra.GhidraRun"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework Utility"/> <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="Framework Utility"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions&#13;&#10;-Djava.system.class.loader=ghidra.GhidraClassLoader&#13;&#10;-Xshare:off&#13;&#10;-Dfile.encoding=UTF8&#13;&#10;-Duser.country=US&#13;&#10;-Duser.language=en&#13;&#10;-Dsun.java2d.pmoffscreen=false&#13;&#10;-Dsun.java2d.xrender=true&#13;&#10;-Dsun.java2d.d3d=false&#13;&#10;-Xdock:name=&quot;Ghidra&quot;&#13;&#10;-Dvisualvm.display.name=Ghidra&#13;&#10;-Dpython.console.encoding=UTF-8&#13;&#10;--add-opens java.base/java.lang=ALL-UNNAMED&#13;&#10;--add-opens java.base/java.util=ALL-UNNAMED&#13;&#10;--add-opens java.base/java.net=ALL-UNNAMED&#13;&#10;--add-opens java.desktop/sun.awt.image=ALL-UNNAMED&#13;&#10;--add-opens java.base/sun.security.x509=ALL-UNNAMED&#13;&#10;--add-opens java.base/sun.security.util=ALL-UNNAMED&#13;&#10;--add-opens java.desktop/sun.awt.X11=ALL-UNNAMED"/> <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions&#13;&#10;-Djava.system.class.loader=ghidra.GhidraClassLoader&#13;&#10;-Xshare:off&#13;&#10;-Dfile.encoding=UTF8&#13;&#10;-Duser.country=US&#13;&#10;-Duser.language=en&#13;&#10;-Dsun.java2d.pmoffscreen=false&#13;&#10;-Dsun.java2d.xrender=true&#13;&#10;-Dsun.java2d.d3d=false&#13;&#10;-Xdock:name=&quot;Ghidra&quot;&#13;&#10;-Dvisualvm.display.name=Ghidra&#13;&#10;-Dpython.console.encoding=UTF-8&#13;&#10;--add-opens java.base/java.lang=ALL-UNNAMED&#13;&#10;--add-opens java.base/java.util=ALL-UNNAMED&#13;&#10;--add-opens java.base/java.net=ALL-UNNAMED&#13;&#10;--add-opens java.desktop/sun.awt.image=ALL-UNNAMED&#13;&#10;--add-opens java.desktop/sun.awt.X11=ALL-UNNAMED"/>
</launchConfiguration> </launchConfiguration>

View file

@ -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/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/AXMLPrinter2.jar Apache License 2.0
MODULE FILE LICENSE: lib/util-1.4.0.jar BSD MODULE FILE LICENSE: lib/util-1.4.0.jar BSD
MODULE FILE LICENSE: lib/bcprov-jdk15on-1.68.jar Bouncy Castle License

View file

@ -33,7 +33,6 @@ dependencies {
api ':dex-translator:2.0' api ':dex-translator:2.0'
api 'org.ow2.asm:asm-debug-all:4.1' 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:baksmali:1.4.0' // TODO: upgrade to 2.2.6
api 'org.smali:dexlib:1.4.0' api 'org.smali:dexlib:1.4.0'

View file

@ -35,10 +35,6 @@ dependencies {
runGhidraServer project runGhidraServer project
} }
addExports([
'java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED'
])
CopySpec yajswCopySpec = copySpec { CopySpec yajswCopySpec = copySpec {
File depsFile = file("${DEPS_DIR}/GhidraServer/${yajswRelease}.zip") File depsFile = file("${DEPS_DIR}/GhidraServer/${yajswRelease}.zip")
File binRepoFile = file("${BIN_REPO}/Ghidra/Features/GhidraServer/${yajswRelease}.zip") File binRepoFile = file("${BIN_REPO}/Ghidra/Features/GhidraServer/${yajswRelease}.zip")

View file

@ -18,6 +18,7 @@ package ghidra.server;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.rmi.server.RemoteServer;
import java.rmi.server.ServerNotActiveException; import java.rmi.server.ServerNotActiveException;
import java.util.*; import java.util.*;
@ -391,7 +392,7 @@ public class RepositoryManager {
} }
} }
try { try {
host = sun.rmi.transport.tcp.TCPTransport.getClientHost(); host = RemoteServer.getClientHost();
try { try {
host = InetNameLookup.getCanonicalHostName(host); host = InetNameLookup.getCanonicalHostName(host);
} }

View file

@ -139,7 +139,7 @@ public class PKIAuthenticationModule implements AuthenticationModule {
} }
ApplicationKeyManagerUtils.validateClient(certChain, ApplicationKeyManagerUtils.validateClient(certChain,
ApplicationKeyManagerUtils.DEFAULT_AUTH_TYPE); ApplicationKeyManagerUtils.RSA_TYPE);
byte[] sigBytes = sigCb.getSignature(); byte[] sigBytes = sigCb.getSignature();
if (sigBytes != null) { if (sigBytes != null) {

View file

@ -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-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/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/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

View file

@ -36,14 +36,14 @@ dependencies {
api "org.apache.commons:commons-text:1.6" api "org.apache.commons:commons-text:1.6"
api "commons-io:commons-io:2.6" api "commons-io:commons-io:2.6"
api "com.google.code.gson:gson:2.8.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" compileOnly "junit:junit:4.12"
} }
ext.addExports([ 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' 'java.desktop/sun.awt=ALL-UNNAMED'
]) ])

View file

@ -15,8 +15,7 @@
*/ */
package ghidra.net; package ghidra.net;
import java.io.FileNotFoundException; import java.io.*;
import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.security.*; import java.security.*;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
@ -549,8 +548,9 @@ public class ApplicationKeyManagerFactory {
Msg.info(this, "Using self-signed certificate: " + defaultIdentity.getName()); Msg.info(this, "Using self-signed certificate: " + defaultIdentity.getName());
char[] pwd = DEFAULT_PASSWORD.toCharArray(); char[] pwd = DEFAULT_PASSWORD.toCharArray();
KeyStore selfSignedKeyStore = KeyStore selfSignedKeyStore =
ApplicationKeyManagerUtils.createKeyStore(null, "JKS", pwd, "defaultSigKey", ApplicationKeyManagerUtils.createKeyStore("defaultSigKey",
null, defaultIdentity.getName(), null, SELF_SIGNED_DURATION_DAYS); defaultIdentity.getName(), SELF_SIGNED_DURATION_DAYS, null,
new File(newKeystorePath), "JKS", pwd);
keystoreData = new ProtectedKeyStoreData(selfSignedKeyStore, pwd); keystoreData = new ProtectedKeyStoreData(selfSignedKeyStore, pwd);
isSelfSigned = true; isSelfSigned = true;
} }

View file

@ -16,8 +16,9 @@
package ghidra.net; package ghidra.net;
import java.io.*; import java.io.*;
import java.math.BigInteger;
import java.security.*; import java.security.*;
import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.*;
import java.security.cert.*; import java.security.cert.*;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.util.*; import java.util.*;
@ -26,10 +27,19 @@ import javax.net.ssl.*;
import javax.security.auth.DestroyFailedException; import javax.security.auth.DestroyFailedException;
import javax.security.auth.x500.X500Principal; 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 generic.random.SecureRandomFactory;
import ghidra.util.Msg; import ghidra.util.Msg;
import sun.security.provider.X509Factory; import ghidra.util.exception.AssertException;
import sun.security.x509.*;
/** /**
* <code>ApplicationKeyManagerUtils</code> provides public methods for utilizing * <code>ApplicationKeyManagerUtils</code> 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 * (i.e., CA certificates), token signing and validation, and the ability to
* generate keystores for testing or when a self-signed certificate will * generate keystores for testing or when a self-signed certificate will
* suffice. * suffice.
* <p>
* <b>NOTE:</b> This class makes direct use of classes within the
* <code>sun.security.x509</code> 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 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; 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.
* <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
* BC code for X500 certificate processing.
*
*/
X500Name.setDefaultStyle(RFC4519Style.INSTANCE);
}
private ApplicationKeyManagerUtils() { private ApplicationKeyManagerUtils() {
// no instantiation - static methods only // no instantiation - static methods only
} }
@ -61,9 +85,9 @@ public class ApplicationKeyManagerUtils {
* @param authorities trusted certificate authorities * @param authorities trusted certificate authorities
* @param token token byte array * @param token token byte array
* @return signed token object * @return signed token object
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException algorithym associated within signing certificate not found
* @throws SignatureException * @throws SignatureException failed to generate SignedToken
* @throws CertificateException * @throws CertificateException error associated with signing certificate
*/ */
public static SignedToken getSignedToken(Principal[] authorities, byte[] token) public static SignedToken getSignedToken(Principal[] authorities, byte[] token)
throws NoSuchAlgorithmException, SignatureException, CertificateException { throws NoSuchAlgorithmException, SignatureException, CertificateException {
@ -78,7 +102,7 @@ public class ApplicationKeyManagerUtils {
continue; continue;
} }
X509KeyManager x509KeyManager = (X509KeyManager) keyManager; X509KeyManager x509KeyManager = (X509KeyManager) keyManager;
String alias = x509KeyManager.chooseClientAlias(new String[] { DEFAULT_AUTH_TYPE }, String alias = x509KeyManager.chooseClientAlias(new String[] { RSA_TYPE },
authorities, null); authorities, null);
if (alias != null) { if (alias != null) {
privateKey = x509KeyManager.getPrivateKey(alias); privateKey = x509KeyManager.getPrivateKey(alias);
@ -131,9 +155,9 @@ public class ApplicationKeyManagerUtils {
* @param token byte array token * @param token byte array token
* @param signature token signature * @param signature token signature
* @return true if signature is my signature * @return true if signature is my signature
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException algorithym associated within signing certificate not found
* @throws SignatureException * @throws SignatureException failed to generate SignedToken
* @throws CertificateException * @throws CertificateException error associated with signing certificate
*/ */
public static boolean isMySignature(Principal[] authorities, byte[] token, byte[] signature) public static boolean isMySignature(Principal[] authorities, byte[] token, byte[] signature)
throws NoSuchAlgorithmException, SignatureException, CertificateException { throws NoSuchAlgorithmException, SignatureException, CertificateException {
@ -144,7 +168,9 @@ public class ApplicationKeyManagerUtils {
/** /**
* Returns a list of trusted issuers (i.e., CA certificates) as established * Returns a list of trusted issuers (i.e., CA certificates) as established
* by the {@link ApplicationTrustManagerFactory}. * 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 { public static X500Principal[] getTrustedIssuers() throws CertificateException {
@ -188,7 +214,7 @@ public class ApplicationKeyManagerUtils {
* trusted based upon the active trust managers. * trusted based upon the active trust managers.
* @param certChain X509 certificate chain * @param certChain X509 certificate chain
* @param authType authentication type (i.e., "RSA") * @param authType authentication type (i.e., "RSA")
* @throws CertificateException * @throws CertificateException if certificate validation fails
*/ */
public static void validateClient(X509Certificate[] certChain, String authType) public static void validateClient(X509Certificate[] certChain, String authType)
throws CertificateException { throws CertificateException {
@ -222,52 +248,144 @@ public class ApplicationKeyManagerUtils {
} }
/** /**
* Pack order list of certs to create a certificate chain array * Pack ordered list of certs to create a certificate chain array
* @param certs certificates which makeup the ordered certificate chain. Null * @param cert primary certificate
* certificate elements will be skipped. * @param caCerts CA certificate chain.
* @return array of certificates * @return ordered certificate chain
*/ */
private static Certificate[] getCertificateChain(Certificate... certs) { private static Certificate[] makeCertificateChain(Certificate cert, Certificate... caCerts) {
List<Certificate> list = new ArrayList<>(); Certificate[] chain = new Certificate[caCerts.length + 1];
for (Certificate cert : certs) { chain[0] = cert;
if (cert != null) { System.arraycopy(caCerts, 0, chain, 1, caCerts.length);
list.add(cert); return chain;
}
}
Certificate[] chain = new Certificate[list.size()];
return list.toArray(chain);
} }
/** /**
* Generate self-signed PKI X509 keystore containing both a signing key/cert * Export X.509 certificates to the specified outFile.
* and an encrypting key/cert. Default certificte extension specifies key usage of * @param certificates certificates to be stored
* Signing which is appropriate for SSL DHE or ECDHE cipher suites. * @param outFile output file
* @param keyFile keystore file or null if not to be stored * @throws IOException if error occurs writing to outFile
* @param keystoreType keystore type (e.g., "JKS", "PKCS12") * @throws CertificateEncodingException if error occurs while encoding certificate data
* @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
*/ */
public static KeyStore createKeyStore(File keyFile, String keystoreType, public static void exportX509Certificates(Certificate[] certificates, File outFile)
char[] protectedPassphrase, String alias, CertificateExtensions certExtensions, throws IOException, CertificateEncodingException {
String dn, PrivateKeyEntry caSignerKeyEntry, int durationDays)
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 { throws KeyStoreException {
KeyStore keyStore; PasswordProtection pp = new PasswordProtection(protectedPassphrase);
try {
keyStore = KeyStore.getInstance(keystoreType);
keyStore.load(null);
addNewKeyPair(keyStore, alias, dn, certExtensions, protectedPassphrase, LoadStoreParameter loadStoreParameter = null;
caSignerKeyEntry, durationDays); 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) { if (keyFile != null) {
FileOutputStream out = new FileOutputStream(keyFile); FileOutputStream out = new FileOutputStream(keyFile);
@ -288,155 +406,57 @@ public class ApplicationKeyManagerUtils {
keyFile.setWritable(false); keyFile.setWritable(false);
} }
} Msg.debug(ApplicationKeyManagerUtils.class,
catch (GeneralSecurityException | IOException e) { "Certificate Generated (" + alias + "): " + dn);
throw new KeyStoreException("failed to generate/store certificate (" + dn + ")", e);
}
return keyStore; return keyStore;
} }
catch (GeneralSecurityException | OperatorException | IOException e) {
/** throw new KeyStoreException("Failed to generate/store certificate (" + dn + ")", e);
* 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<String> 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
} }
finally { 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
}
} }
} }

View file

@ -70,13 +70,11 @@ public class ApplicationKeyManagerFactoryTest extends AbstractGenericTest {
@Before @Before
public void setUp() throws Exception { 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 = createTempFile("test-key", ".p12");
keystoreFile.delete(); keystoreFile.delete();
ApplicationKeyManagerUtils.exportKeystore(selfSignedKeyStore, keystoreFile,
TEST_PWD.toCharArray()); ApplicationKeyManagerUtils.createKeyStore(ALIAS, TEST_IDENTITY, 2, null, keystoreFile,
"PKCS12", TEST_PWD.toCharArray());
ApplicationKeyManagerFactory.setKeyStorePasswordProvider(passwordProvider); ApplicationKeyManagerFactory.setKeyStorePasswordProvider(passwordProvider);
} }

View file

@ -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.util=ALL-UNNAMED
VMARGS=--add-opens java.base/java.net=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.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 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 # Persistent cache directory used by the application. This directory will be used to store

View file

@ -66,8 +66,6 @@ dependencies {
// We export them to all "unnamed" modules, which are modules that don't define themselves // 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. // as a new Java 9 style module. Ghidra is currently using unnamed modules everywhere.
ext.addExports([ 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.awt=ALL-UNNAMED',
'java.desktop/sun.swing=ALL-UNNAMED', 'java.desktop/sun.swing=ALL-UNNAMED',
'java.desktop/sun.java2d=ALL-UNNAMED' 'java.desktop/sun.java2d=ALL-UNNAMED'

View file

@ -19,8 +19,6 @@ import java.io.*;
import java.net.*; import java.net.*;
import java.rmi.registry.LocateRegistry; import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry; import java.rmi.registry.Registry;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.PrivateKeyEntry;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
@ -49,7 +47,6 @@ import ghidra.util.*;
import ghidra.util.exception.*; import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import ghidra.util.timer.GTimer; import ghidra.util.timer.GTimer;
import sun.security.x509.*;
import utilities.util.FileUtilities; import utilities.util.FileUtilities;
/** /**
@ -902,38 +899,23 @@ public class ServerTestUtil {
// Generate CA certificate and keystore // Generate CA certificate and keystore
Msg.info(ServerTestUtil.class, "Generating self-signed CA cert: " + caPath); Msg.info(ServerTestUtil.class, "Generating self-signed CA cert: " + caPath);
PrivateKeyEntry caEntry =
CertificateExtensions caCertExtensions = new CertificateExtensions(); ApplicationKeyManagerUtils.createKeyEntry("test-CA", TEST_PKI_CA_DN, 2, null, null,
BasicConstraintsExtension caBasicConstraints = new BasicConstraintsExtension(true, true, 1); "PKCS12", ApplicationKeyManagerFactory.DEFAULT_PASSWORD.toCharArray());
caCertExtensions.set(PKIXExtensions.BasicConstraints_Id.toString(), caBasicConstraints); ApplicationKeyManagerUtils.exportX509Certificates(caEntry.getCertificateChain(), caFile);
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);
// Generate User/Client certificate and keystore // Generate User/Client certificate and keystore
Msg.info(ServerTestUtil.class, "Generating test user key/cert (signed by test-CA, pwd: " + Msg.info(ServerTestUtil.class, "Generating test user key/cert (signed by test-CA, pwd: " +
TEST_PKI_USER_PASSPHRASE + "): " + userKeystorePath); TEST_PKI_USER_PASSPHRASE + "): " + userKeystorePath);
ApplicationKeyManagerUtils.createKeyStore(userKeystoreFile, "PKCS12", ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_USER_DN, 2, caEntry,
TEST_PKI_USER_PASSPHRASE.toCharArray(), "test-sig", null, TEST_PKI_USER_DN, userKeystoreFile, "PKCS12", TEST_PKI_USER_PASSPHRASE.toCharArray());
caPrivateKeyEntry, 2);
// Generate Server certificate and keystore // Generate Server certificate and keystore
Msg.info(ServerTestUtil.class, "Generating test server key/cert (signed by test-CA, pwd: " + Msg.info(ServerTestUtil.class, "Generating test server key/cert (signed by test-CA, pwd: " +
TEST_PKI_SERVER_PASSPHRASE + "): " + serverKeystorePath); TEST_PKI_SERVER_PASSPHRASE + "): " + serverKeystorePath);
ApplicationKeyManagerUtils.createKeyStore(serverKeystoreFile, "PKCS12",
TEST_PKI_SERVER_PASSPHRASE.toCharArray(), "test-sig", null, TEST_PKI_SERVER_DN, ApplicationKeyManagerUtils.createKeyEntry("test-sig", TEST_PKI_SERVER_DN, 2, caEntry,
caPrivateKeyEntry, 2); serverKeystoreFile, "PKCS12", TEST_PKI_SERVER_PASSPHRASE.toCharArray());
} }
/** /**

View file

@ -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. // Some internal packages are not public and need to be exported.
options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED", options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED",
"java.desktop/sun.awt=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"])
} }
@ -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. // Some internal packages are not public and need to be exported.
options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED", options.addMultilineStringsOption("-add-exports").setValue(["java.desktop/sun.awt.image=ALL-UNNAMED",
"java.desktop/sun.awt=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"])
options.doclet = "JsonDoclet" options.doclet = "JsonDoclet"
doFirst { doFirst {