Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export selected certificates #490

Merged
merged 5 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,79 @@ public static X509Certificate[] orderX509CertChain(X509Certificate[] certs) {
// Return longest path
return longestPath.toArray(new X509Certificate[0]);
}
/*
* Tries to sort the certificates according to their hierarchy,
* and adds at the end those that have no dependencies.
*/
public static X509Certificate[] orderX509CertsChain(X509Certificate[] certs) {
if (certs == null) {
return new X509Certificate[0];
}
if (certs.length <= 1) {
return certs;
}
ArrayList<ArrayList<X509Certificate>> paths = new ArrayList<>();
for (X509Certificate cert : certs) {
ArrayList<X509Certificate> path = new ArrayList<>();
path.add(cert);
for (X509Certificate issuerCert : certs) {
if (certificatesEquals(issuerCert, cert)) {
continue;
}
if (isIssuedBy(cert, issuerCert)) {
path.add(issuerCert);
}
}
if (path.size() > 1) {
paths.add(path);
}
}
List<X509Certificate> listCertificates = new ArrayList<>();
for (ArrayList<X509Certificate> path : paths) {
X509Certificate cert = path.get(0);
X509Certificate issuerCert = path.get(1);
int posIssuer = -1;
int posCert = -1;
for (int i = 0; i < listCertificates.size(); i++) {
X509Certificate cert2 = listCertificates.get(i);
if (certificatesEquals(issuerCert, cert2)) {
posIssuer = i;
}
if (certificatesEquals(cert, cert2)) {
posCert = i;
}
}
if (posIssuer == -1) {
if (posCert == -1) {
listCertificates.add(cert);
}
listCertificates.add(issuerCert);
} else {
listCertificates.add(posIssuer, cert);
}
}
if (listCertificates.size() != certs.length) {
for (X509Certificate cert1 : certs) {
boolean found = false;
for (X509Certificate cert2 : listCertificates) {
if (certificatesEquals(cert1, cert2)) {
found = true;
break;
}
}
if (!found) {
listCertificates.add(cert1);
}
}
}
return listCertificates.toArray(new X509Certificate[0]);
}

private static boolean certificatesEquals(X509Certificate cert1, X509Certificate cert2) {
return cert1.getSubjectX500Principal().equals(cert2.getSubjectX500Principal())
&& cert1.getIssuerX500Principal().equals(cert2.getIssuerX500Principal())
&& cert1.getSerialNumber().equals(cert2.getSerialNumber());
}

private static X509Certificate findIssuedCert(X509Certificate issuerCert, X509Certificate[] certs) {
// Find a certificate issued by the supplied certificate
Expand Down
9 changes: 9 additions & 0 deletions kse/src/main/java/org/kse/gui/KseFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
import org.kse.gui.actions.ExportKeyPairCertificateChainAction;
import org.kse.gui.actions.ExportKeyPairPrivateKeyAction;
import org.kse.gui.actions.ExportKeyPairPublicKeyAction;
import org.kse.gui.actions.ExportSelectedCertificatesAction;
import org.kse.gui.actions.ExportTrustedCertificateAction;
import org.kse.gui.actions.ExportTrustedCertificatePublicKeyAction;
import org.kse.gui.actions.FindAction;
Expand Down Expand Up @@ -400,6 +401,7 @@ public final class KseFrame implements StatusBar {
private JMenuItem jmiMultEntrySelCopy;
private JMenuItem jmiMultiEntrySelDelete;
private JMenuItem jmiMultiEntryCompare;
private JMenuItem jmiMultiEntryExport;

//
// Main display controls
Expand Down Expand Up @@ -530,6 +532,7 @@ public final class KseFrame implements StatusBar {
private final SetKeyPasswordAction setKeyPasswordAction = new SetKeyPasswordAction(this);
private final DeleteKeyAction deleteKeyAction = new DeleteKeyAction(this);
private final RenameKeyAction renameKeyAction = new RenameKeyAction(this);
private final ExportSelectedCertificatesAction exportSelectedCertificatesAction = new ExportSelectedCertificatesAction(this);

//
// Action map keys - map input to action
Expand Down Expand Up @@ -2197,11 +2200,17 @@ private void initKeyStoreEntryPopupMenus() {
new StatusBarChangeHandler(jmiMultiEntryCompare,
(String) compareCertificateAction.getValue(Action.LONG_DESCRIPTION), this);

jmiMultiEntryExport = new JMenuItem(exportSelectedCertificatesAction);
jmiMultiEntryExport.setToolTipText(null);
new StatusBarChangeHandler(jmiMultiEntryExport,
(String) exportSelectedCertificatesAction.getValue(Action.LONG_DESCRIPTION), this);

jpmMultiEntrySel = new JPopupMenu();
jpmMultiEntrySel.add(jmiMultiEntrySelCut);
jpmMultiEntrySel.add(jmiMultEntrySelCopy);
jpmMultiEntrySel.add(jmiMultiEntrySelDelete);
jpmMultiEntrySel.add(jmiMultiEntryCompare);
jpmMultiEntrySel.add(jmiMultiEntryExport);

}

Expand Down
20 changes: 10 additions & 10 deletions kse/src/main/java/org/kse/gui/actions/ExamineClipboardAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,19 +240,19 @@ private void downloadCrl(URL url) throws IOException, CryptoException {
}

private void downloadCert(URL url) throws IOException, CryptoException {
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
int status = urlConn.getResponseCode();
if (isRedirect(status)) {
String newUrl = urlConn.getHeaderField("Location");
url = new URL(newUrl);
urlConn = (HttpURLConnection) url.openConnection();
}
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
int status = urlConn.getResponseCode();
if (isRedirect(status)) {
String newUrl = urlConn.getHeaderField("Location");
url = new URL(newUrl);
urlConn = (HttpURLConnection) url.openConnection();
}
try (InputStream is = urlConn.getInputStream()) {
X509Certificate[] certs = X509CertUtil.loadCertificates(IOUtils.toByteArray(is));
if (certs != null && certs.length > 0) {
DViewCertificate dViewCertificate = new DViewCertificate(frame, MessageFormat.format(
resExt.getString("DViewExtensions.ViewCert.Title"), url.toString()), certs, null,
DViewCertificate.NONE);
DViewCertificate dViewCertificate = new DViewCertificate(frame,
MessageFormat.format(resExt.getString("DViewExtensions.ViewCert.Title"), url.toString()), certs,
this.kseFrame, DViewCertificate.NONE);
dViewCertificate.setLocationRelativeTo(frame);
dViewCertificate.setVisible(true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package org.kse.gui.actions;

import java.awt.Toolkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

import org.kse.crypto.keystore.KeyStoreUtil;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.KseFrame;
import org.kse.gui.dialogs.importexport.DExportCertificates;
import org.kse.gui.error.DError;
import org.kse.utilities.history.KeyStoreHistory;
import org.kse.utilities.history.KeyStoreState;
import org.kse.utilities.io.FileNameUtil;

/**
* Action to export selected certificates.
*/
public class ExportSelectedCertificatesAction extends KeyStoreExplorerAction {

private static final long serialVersionUID = 1L;

public ExportSelectedCertificatesAction(KseFrame kseFrame) {
super(kseFrame);
putValue(LONG_DESCRIPTION, res.getString("ExportSelectedCertificatesAction.statusbar"));
putValue(NAME, res.getString("ExportSelectedCertificatesAction.text"));
putValue(SHORT_DESCRIPTION, res.getString("ExportSelectedCertificatesAction.tooltip"));
putValue(SMALL_ICON, new ImageIcon(
Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/exportselectedcerts.png"))));
}

@Override
protected void doAction() {
List<X509Certificate> listCertificate = getCertificates();
File exportFile = null;
try {
if (listCertificate.size() > 0) {
KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
String fileName = FileNameUtil.removeExtension(history.getName());
DExportCertificates dExportCertificates = new DExportCertificates(frame, fileName, false, true);
dExportCertificates.setLocationRelativeTo(frame);
dExportCertificates.setVisible(true);

if (!dExportCertificates.exportSelected()) {
return;
}
X509Certificate[] certs = listCertificate.toArray(new X509Certificate[0]);
certs = X509CertUtil.orderX509CertsChain(certs);
exportFile = dExportCertificates.getExportFile();

boolean pemEncode = dExportCertificates.pemEncode();

byte[] encoded = null;

if (dExportCertificates.exportFormatX509()) {
encoded = X509CertUtil.getCertsEncodedX509Pem(certs).getBytes();
} else if (dExportCertificates.exportFormatPkcs7()) {
if (pemEncode) {
encoded = X509CertUtil.getCertsEncodedPkcs7Pem(certs).getBytes();
} else {
encoded = X509CertUtil.getCertsEncodedPkcs7(certs);
}
} else if (dExportCertificates.exportFormatPkiPath()) {
encoded = X509CertUtil.getCertsEncodedPkiPath(certs);
} else if (dExportCertificates.exportFormatSpc()) {
encoded = X509CertUtil.getCertsEncodedPkcs7(certs); // SPC is just DER PKCS #7
}
exportEncodedCertificate(encoded, exportFile);
JOptionPane.showMessageDialog(frame,
res.getString("ExportSelectedCertificatesAction.ExportCertificateSuccessful.message"),
res.getString("ExportSelectedCertificatesAction.ExportCertificate.Title"),
JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(frame, res.getString("ExportSelectedCertificatesAction.onemore.message"),
res.getString("ExportSelectedCertificatesAction.ExportCertificate.Title"),
JOptionPane.WARNING_MESSAGE);
}
} catch (FileNotFoundException ex) {
String message = MessageFormat.format(res.getString("ExportSelectedCertificatesAction.NoWriteFile.message"),
exportFile);

JOptionPane.showMessageDialog(frame, message,
res.getString("ExportSelectedCertificatesAction.ExportCertificate.Title"),
JOptionPane.WARNING_MESSAGE);
} catch (Exception ex) {
DError.displayError(frame, ex);
}
}

private List<X509Certificate> getCertificates() {
KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
KeyStoreState currentState = history.getCurrentState();

String[] aliases = kseFrame.getSelectedEntryAliases();
try {
List<X509Certificate> listCertificates = new ArrayList<>();
KeyStore keyStore = currentState.getKeyStore();
for (String alias : aliases) {
if (KeyStoreUtil.isTrustedCertificateEntry(alias, keyStore)) {
Certificate certificate = keyStore.getCertificate(alias);
listCertificates.add((X509Certificate) certificate);
} else if (KeyStoreUtil.isKeyPairEntry(alias, keyStore)) {
Certificate[] chain = keyStore.getCertificateChain(alias);
if (chain != null) {
for (Certificate certificate : chain) {
listCertificates.add((X509Certificate) certificate);
}
}
}
}
return listCertificates;
} catch (Exception ex) {
DError.displayError(frame, ex);
return Collections.emptyList();
}
}

private void exportEncodedCertificate(byte[] encoded, File exportFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(exportFile)) {
fos.write(encoded);
fos.flush();
}
}
}
3 changes: 2 additions & 1 deletion kse/src/main/java/org/kse/gui/dialogs/DViewCertificate.java
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ private void initComponents(X509Certificate[] certs) throws CryptoException {

jbVerify = new JButton(res.getString("DViewCertificate.jbVerify.text"));
jbVerify.setToolTipText(res.getString("DViewCertificate.jbVerify.tooltip"));
jbVerify.setVisible(importExport != NONE);
PlatformUtil.setMnemonic(jbVerify, res.getString("DViewCertificate.jbVerify.mnemonic").charAt(0));

Container pane = getContentPane();
Expand Down Expand Up @@ -302,7 +303,7 @@ private void initComponents(X509Certificate[] certs) throws CryptoException {
pane.add(jbExport, "hidemode 1");
pane.add(jbExtensions, "");
pane.add(jbPem, "");
pane.add(jbVerify, "");
pane.add(jbVerify, "hidemode 1");
pane.add(jbAsn1, "wrap");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbOK, "spanx, tag ok");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public class DExportCertificates extends JEscDialog {
private boolean formatPkiPath;
private boolean formatSpc;
private boolean pemEncode;
private boolean certificateSelected = false;

/**
* Creates a new DExportCertificate dialog.
Expand All @@ -106,6 +107,14 @@ public DExportCertificates(JFrame parent, String certificateAlias, boolean chain
initComponents();
}

public DExportCertificates(JFrame parent, String certificateAlias, boolean chain, boolean certificateSelected) {
super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
this.certificateAlias = certificateAlias;
this.chain = chain;
this.certificateSelected = certificateSelected;
initComponents();
}

private void initComponents() {
jlExportLength = new JLabel(res.getString("DExportCertificates.jlExportLength.text"));

Expand Down Expand Up @@ -155,7 +164,9 @@ private void initComponents() {
jcbExportPem = new JCheckBox();
jcbExportPem.setSelected(true);
jcbExportPem.setToolTipText(res.getString("DExportCertificates.jcbExportPem.tooltip"));

if (jrbExportChain.isSelected() && jrbExportX509.isSelected()) {
jcbExportPem.setEnabled(false);
}
jlExportFile = new JLabel(res.getString("DExportCertificates.jlExportFile.text"));

jtfExportFile = new JTextField(30);
Expand Down Expand Up @@ -282,12 +293,15 @@ public void windowClosing(WindowEvent evt) {
}
});

if (chain) {
setTitle(MessageFormat.format(res.getString("DExportCertificates.CertificateChain.Title"),
certificateAlias));
} else {
setTitle(MessageFormat.format(res.getString("DExportCertificates.Certificate.Title"), certificateAlias));
}
if (certificateSelected) {
setTitle(MessageFormat.format(res.getString("DExportCertificates.CertificateSelected.Title"),
certificateAlias));
} else if (chain) {
setTitle(MessageFormat.format(res.getString("DExportCertificates.CertificateChain.Title"),
certificateAlias));
} else {
setTitle(MessageFormat.format(res.getString("DExportCertificates.Certificate.Title"), certificateAlias));
}

setResizable(false);

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ ExportTrustedCertificateAction.statusbar = Export the
ExportTrustedCertificateAction.text = Export Certificate
ExportTrustedCertificateAction.tooltip = Export Trusted Certificate entry

ExportSelectedCertificatesAction.statusbar = Export the Selected Certificates entries as X.509 PKCS #7, PKI Path or SPC
ExportSelectedCertificatesAction.text = Export Certificates
ExportSelectedCertificatesAction.tooltip = Export Selected Certificates
ExportSelectedCertificatesAction.ExportCertificate.Title = Export Selected Certificates
ExportSelectedCertificatesAction.ExportCertificateSuccessful.message = Export Selected Certificates Successful.
ExportSelectedCertificatesAction.onemore.message = You must select one or more certificates
ExportSelectedCertificatesAction.NoWriteFile.message = Could not write to file ''{0}''.

ExportTrustedCertificatePublicKeyAction.ExportPublicKeyOpenSsl.Title = Export Public Key as OpenSSL
ExportTrustedCertificatePublicKeyAction.ExportPublicKeyOpenSslSuccessful.message = Export Public Key as OpenSSL Successful.
ExportTrustedCertificatePublicKeyAction.NoAccessEntry.message = Could not access KeyStore entry ''{0}''.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

DExportCertificates.Certificate.Simple.Title = Export Certificate
DExportCertificates.Certificate.Title = Export Certificate ''{0}''
DExportCertificates.CertificateSelected.Title = Export Selected Certificates ''{0}''
DExportCertificates.CertificateChain.Simple.Title = Export Certificate Chain
DExportCertificates.CertificateChain.Title = Export Certificate Chain ''{0}''
DExportCertificates.ChooseExportFile.Title = Choose Certificate Export File
Expand Down
Loading