mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-08-28 05:20:21 +00:00
GP-1296 Eliminated use of sun.security package and refactored ApplicationKeyManagerUtils
This commit is contained in:
parent
8b2ea61e27
commit
9db26bc7f5
|
@ -30,5 +30,5 @@
|
|||
<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.PROJECT_ATTR" value="Framework Utility"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Xshare:off -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name="Ghidra" -Dvisualvm.display.name=Ghidra -Dpython.console.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.desktop/sun.awt.image=ALL-UNNAMED --add-opens java.base/sun.security.x509=ALL-UNNAMED --add-opens java.base/sun.security.util=ALL-UNNAMED --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+IgnoreUnrecognizedVMOptions -Djava.system.class.loader=ghidra.GhidraClassLoader -Xshare:off -Dfile.encoding=UTF8 -Duser.country=US -Duser.language=en -Dsun.java2d.pmoffscreen=false -Dsun.java2d.xrender=true -Dsun.java2d.d3d=false -Xdock:name="Ghidra" -Dvisualvm.display.name=Ghidra -Dpython.console.encoding=UTF-8 --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.desktop/sun.awt.image=ALL-UNNAMED --add-opens java.desktop/sun.awt.X11=ALL-UNNAMED"/>
|
||||
</launchConfiguration>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
])
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* <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
|
||||
* generate keystores for testing or when a self-signed certificate will
|
||||
* 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 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.
|
||||
* <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() {
|
||||
// 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<Certificate> 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<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
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue