diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 053005c..a6ba91a 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,22 @@
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
@@ -30,7 +43,12 @@
-
+
+
+
+
+
+
@@ -73,12 +91,13 @@
"Gradle.Keytool [signPlugin].executor": "Run",
"Gradle.Run Plugin.executor": "Run",
"Gradle.keytoolj [setupDependencies].executor": "Run",
+ "Gradle.ssl-toolbox [setupDependencies].executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"UI_DESIGNER_EDITOR_MODE.PaletteManager.SHOW": "true",
"git-widget-placeholder": "main",
"kotlin-language-version-configured": "true",
- "last_opened_file_path": "/home/cortiz/repos/TuanisCloud/gollo/portal",
+ "last_opened_file_path": "/Users/cortiz/repos/jmpeax/ssl-toolbox",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
@@ -90,6 +109,8 @@
}]]>
+
+
@@ -161,8 +182,8 @@
-
-
+
+
@@ -189,6 +210,7 @@
+
@@ -270,7 +292,15 @@
1725419668125
-
+
+
+ 1725504767201
+
+
+
+ 1725504767201
+
+
@@ -300,7 +330,8 @@
-
+
+
diff --git a/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditor.java b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditor.java
new file mode 100644
index 0000000..f01a6e0
--- /dev/null
+++ b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditor.java
@@ -0,0 +1,45 @@
+package com.jmpeax.ssltoolbox.jks;
+
+import com.intellij.diff.util.FileEditorBase;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.jmpeax.ssltoolbox.svc.CertificateHelper;
+import com.jmpeax.ssltoolbox.utils.Prompts;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.io.IOException;
+
+public class JKSFileEditor extends FileEditorBase {
+
+ private final VirtualFile file;
+ private final JKSView jksView;
+
+ public JKSFileEditor(VirtualFile file) {
+ this.file = file;
+ this.jksView= new JKSView(file);
+ }
+
+ @Override
+ public @NotNull JComponent getComponent() {
+
+ return this.jksView;
+
+ }
+
+ @Override
+ public @Nullable JComponent getPreferredFocusedComponent() {
+ return this.jksView.getUnlockText();
+ }
+
+ @Override
+ public @Nls(capitalization = Nls.Capitalization.Title) @NotNull String getName() {
+ return "Keystore Viewer";
+ }
+
+ @Override
+ public VirtualFile getFile() {
+ return file;
+ }
+}
diff --git a/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditorProvider.java b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditorProvider.java
new file mode 100644
index 0000000..7e66cb3
--- /dev/null
+++ b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileEditorProvider.java
@@ -0,0 +1,37 @@
+package com.jmpeax.ssltoolbox.jks;
+
+import com.intellij.openapi.fileEditor.FileEditor;
+import com.intellij.openapi.fileEditor.FileEditorPolicy;
+import com.intellij.openapi.fileEditor.FileEditorProvider;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public class JKSFileEditorProvider implements FileEditorProvider {
+ @Override
+ public boolean accept(@NotNull Project project, @NotNull VirtualFile file) {
+ if (file.getExtension() != null) {
+ return file.getExtension().equalsIgnoreCase("jks") || file.getExtension().equalsIgnoreCase("p12");
+ }
+ return false;
+ }
+
+ @Override
+ public @NotNull FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) {
+
+ return new JKSFileEditor(file);
+
+
+ }
+
+ @Override
+ public @NotNull @NonNls String getEditorTypeId() {
+ return "jks-editor";
+ }
+
+ @Override
+ public @NotNull FileEditorPolicy getPolicy() {
+ return FileEditorPolicy.PLACE_BEFORE_DEFAULT_EDITOR;
+ }
+}
diff --git a/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileType.java b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileType.java
new file mode 100644
index 0000000..b2db063
--- /dev/null
+++ b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSFileType.java
@@ -0,0 +1,36 @@
+package com.jmpeax.ssltoolbox.jks;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.fileTypes.FileType;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class JKSFileType implements FileType {
+ public static final JKSFileType INSTANCE = new JKSFileType();
+
+ @Override
+ public @NotNull String getName() {
+ return "JKS file";
+ }
+
+ @Override
+ public @NotNull String getDescription() {
+ return "JKS files";
+ }
+
+ @Override
+ public @NotNull String getDefaultExtension() {
+ return "jks";
+ }
+
+ @Override
+ public Icon getIcon() {
+ return AllIcons.Diff.Lock;
+ }
+
+ @Override
+ public boolean isBinary() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/jmpeax/ssltoolbox/jks/JKSView.java b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSView.java
new file mode 100644
index 0000000..d6e1398
--- /dev/null
+++ b/src/main/java/com/jmpeax/ssltoolbox/jks/JKSView.java
@@ -0,0 +1,216 @@
+package com.jmpeax.ssltoolbox.jks;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.components.JBLabel;
+import com.intellij.ui.components.JBList;
+import com.intellij.ui.components.JBPasswordField;
+import com.intellij.util.ui.JBUI;
+import com.jmpeax.ssltoolbox.pem.PemView;
+import com.jmpeax.ssltoolbox.svc.CertificateHelper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class JKSView extends JPanel {
+ private JBList list;
+ private PemView pemView;
+ private final VirtualFile file;
+ private JPanel listPanel;
+ private JPanel pemViewPanel;
+ private JBPasswordField passwordField;
+
+ public JKSView(@NotNull VirtualFile file) {
+ super(new GridBagLayout());
+ this.file = file;
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = JBUI.insets(5);
+ gbc.fill = GridBagConstraints.BOTH;
+
+ // Add unlock button at the top
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 2;
+ gbc.weightx = 1.0;
+ gbc.weighty = 0.0;
+ add(createUnlockButton(), gbc);
+
+ // Add certificate list panel
+ gbc.gridx = 0;
+ gbc.gridy = 1;
+ gbc.gridwidth = 1;
+ gbc.weightx = 0.45;
+ gbc.weighty = 1.0;
+ listPanel = createListPanel();
+ add(listPanel, gbc);
+
+ // Add PEM view panel
+ gbc.gridx = 1;
+ gbc.gridy = 1;
+ gbc.weightx = 0.55;
+ pemViewPanel = createPemViewPanel();
+ add(pemViewPanel, gbc);
+ }
+
+ private void loadPemView(X509Certificate certificate) {
+ if (pemView != null) {
+ pemViewPanel.remove(pemView);
+ }
+ pemView = new PemView(certificate);
+ pemViewPanel.removeAll();
+ pemViewPanel.add(pemView, BorderLayout.CENTER);
+ revalidate();
+ repaint();
+ }
+
+ private JPanel createListPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ JLabel label = new JLabel("Certificate List", SwingConstants.CENTER);
+ JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+ labelPanel.add(label);
+ panel.add(labelPanel, BorderLayout.NORTH);
+ panel.add(new JScrollPane(list), BorderLayout.CENTER);
+ return panel;
+ }
+
+ private JPanel createPemViewPanel() {
+ JPanel panel = new JPanel(new BorderLayout());
+ JLabel label = new JLabel("Certificate Details");
+ panel.add(label, BorderLayout.NORTH);
+ panel.add(new JPanel(), BorderLayout.CENTER); // Placeholder for PemView
+ return panel;
+ }
+
+ private JPanel createUnlockButton() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = JBUI.insets(5);
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+
+ this.passwordField = new JBPasswordField();
+ panel.setBorder(JBUI.Borders.empty(10));
+ JButton unlockButton = getButton(passwordField, panel);
+ JLabel passwordLabel = new JBLabel("Enter password: ");
+ passwordField.requestFocusInWindow();
+
+ // Set focus traversal keys for the password field to move to the unlock button
+ Set forwardKeys = new HashSet<>(passwordField.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
+ forwardKeys.add(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0));
+ passwordField.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
+ unlockButton.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.emptySet());
+
+ // Add password label (20%)
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.weightx = 0.1;
+ panel.add(passwordLabel, gbc);
+
+ // Add password field (60%)
+ gbc.gridx = 1;
+ gbc.weightx = 0.8;
+ panel.add(passwordField, gbc);
+
+ // Add unlock button (20%)
+ gbc.gridx = 2;
+ gbc.weightx = 0.1;
+ panel.add(unlockButton, gbc);
+
+ return panel;
+ }
+
+ private @NotNull JButton getButton(JPasswordField passwordField, JPanel panel) {
+ JButton unlockButton = new JButton("Unlock Certificate Store");
+ unlockButton.addActionListener(e -> {
+ char[] password = passwordField.getPassword();
+ if (password != null) {
+ try {
+ var certs = CertificateHelper.getKeyStoreCerts(file.getInputStream(), password);
+ updateView(certs);
+ // Remove the unlock button
+ //panel.removeAll();
+ // panel.add(buildToolBar());
+ revalidate();
+ repaint();
+ } catch (IOException ex) {
+ throw new RuntimeException("Error reading JKS file", ex);
+ }
+ }
+ });
+ return unlockButton;
+ }
+
+ private JPanel buildToolBar() {
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ JButton openButton = new JButton(AllIcons.Actions.Menu_open);
+
+ openButton.setToolTipText("Open");
+ openButton.setPreferredSize(new Dimension(AllIcons.Actions.Menu_open.getIconWidth() + 10, AllIcons.Actions.Menu_open.getIconHeight() + 10));
+
+ openButton.addActionListener(e -> {
+ });
+ panel.add(openButton);
+ JButton saveButton = new JButton(AllIcons.Actions.Menu_saveall);
+ saveButton.setPreferredSize(new Dimension(AllIcons.Actions.Menu_saveall.getIconWidth() + 10, AllIcons.Actions.Menu_saveall.getIconHeight() + 10));
+
+ saveButton.setToolTipText("Save");
+ saveButton.addActionListener(e -> {
+ });
+ panel.add(saveButton);
+
+ JButton closeButton = new JButton(AllIcons.Actions.Cancel);
+ closeButton.setPreferredSize(new Dimension(AllIcons.Actions.Cancel.getIconWidth() + 10, AllIcons.Actions.Cancel.getIconHeight() + 10));
+
+ closeButton.setToolTipText("Close");
+ closeButton.addActionListener(e -> {
+ });
+ panel.add(closeButton);
+
+
+ return panel;
+ }
+
+ private void updateView(Map certs) {
+ DefaultListModel listModel = new DefaultListModel<>();
+ certs.keySet().forEach(listModel::addElement);
+ list = new JBList<>(listModel);
+ list.setCellRenderer(new IconListRenderer());
+ // Add a selection listener to handle selection changes
+ list.addListSelectionListener(e -> {
+ if (!e.getValueIsAdjusting()) {
+ var selectedIndex = list.getSelectedValue();
+ if (selectedIndex != null && !selectedIndex.isEmpty()) {
+ loadPemView(certs.get(selectedIndex));
+ }
+ }
+ });
+
+ listPanel.removeAll();
+
+ listPanel.add(new JScrollPane(list), BorderLayout.CENTER);
+ revalidate();
+ repaint();
+ }
+
+ public @Nullable JComponent getUnlockText() {
+ return this.passwordField;
+ }
+
+ private static class IconListRenderer extends DefaultListCellRenderer {
+ @Override
+ public Component getListCellRendererComponent(JList> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ label.setIcon(AllIcons.FileTypes.Any_type);
+ return label;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditor.java b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditor.java
similarity index 94%
rename from src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditor.java
rename to src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditor.java
index 041b961..ae9e865 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditor.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditor.java
@@ -1,11 +1,10 @@
-package com.jmpeax.ssltoolbox.ui;
+package com.jmpeax.ssltoolbox.pem;
import com.intellij.diff.util.FileEditorBase;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.components.JBTabbedPane;
import com.jmpeax.ssltoolbox.svc.CertificateHelper;
-import com.jmpeax.ssltoolbox.ui.pem.PemView;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -22,7 +21,6 @@ public class PEMFileEditor extends FileEditorBase {
public PEMFileEditor(@NotNull VirtualFile file)
throws CertificateException, IOException {
this.file = file;
-
this.panel = new JBTabbedPane();
var c = CertificateHelper.getCertificate(file.getInputStream());
if (c.isEmpty()){
@@ -54,7 +52,7 @@ private void buildTabbedPane(X509Certificate x509Certificate) {
@Override
public @Nls(capitalization = Nls.Capitalization.Title) @NotNull String getName() {
- return "Keytool";
+ return "Certificate Viewer";
}
@Override
diff --git a/src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditorProvider.java b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditorProvider.java
similarity index 97%
rename from src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditorProvider.java
rename to src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditorProvider.java
index a458567..dc923ec 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/ui/PEMFileEditorProvider.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileEditorProvider.java
@@ -1,4 +1,4 @@
-package com.jmpeax.ssltoolbox.ui;
+package com.jmpeax.ssltoolbox.pem;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorPolicy;
diff --git a/src/main/java/com/jmpeax/ssltoolbox/fileTypes/PEMFileType.java b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileType.java
similarity index 95%
rename from src/main/java/com/jmpeax/ssltoolbox/fileTypes/PEMFileType.java
rename to src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileType.java
index 8c7eb78..de4e78d 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/fileTypes/PEMFileType.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/pem/PEMFileType.java
@@ -1,4 +1,4 @@
-package com.jmpeax.ssltoolbox.fileTypes;
+package com.jmpeax.ssltoolbox.pem;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.fileTypes.FileType;
diff --git a/src/main/java/com/jmpeax/ssltoolbox/ui/pem/PemView.java b/src/main/java/com/jmpeax/ssltoolbox/pem/PemView.java
similarity index 98%
rename from src/main/java/com/jmpeax/ssltoolbox/ui/pem/PemView.java
rename to src/main/java/com/jmpeax/ssltoolbox/pem/PemView.java
index c73d94f..749cb7f 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/ui/pem/PemView.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/pem/PemView.java
@@ -1,8 +1,8 @@
-package com.jmpeax.ssltoolbox.ui.pem;
+package com.jmpeax.ssltoolbox.pem;
import com.intellij.ui.components.JBPanel;
import com.intellij.util.ui.JBUI;
-import com.jmpeax.ssltoolbox.ui.Messages;
+import com.jmpeax.ssltoolbox.utils.Messages;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
diff --git a/src/main/java/com/jmpeax/ssltoolbox/svc/CertificateHelper.java b/src/main/java/com/jmpeax/ssltoolbox/svc/CertificateHelper.java
index 39904b8..9f73c95 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/svc/CertificateHelper.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/svc/CertificateHelper.java
@@ -4,12 +4,14 @@
import org.jetbrains.annotations.NotNull;
import javax.security.auth.x500.X500Principal;
+import java.io.FileInputStream;
import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.LinkedHashSet;
-import java.util.Set;
+import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -84,4 +86,27 @@ public static boolean isValid(@NotNull X509Certificate certificate) {
}
return false;
}
+
+ public static Map getKeyStoreCerts(InputStream keystoreIS, char[] keystorePassword) {
+ Map certs = new LinkedHashMap<>();
+ try {
+ KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keystore.load(keystoreIS, keystorePassword);
+ Enumeration aliases = keystore.aliases();
+ aliases.asIterator().forEachRemaining(alias -> {
+ try {
+ Certificate cert = keystore.getCertificate(alias);
+ if (cert instanceof X509Certificate) {
+ certs.put(alias, (X509Certificate) cert);
+ }
+ } catch (Exception e) {
+ LOGGER.error("Error loading certificate", e);
+ }
+ });
+ } catch (Exception e) {
+ LOGGER.error("Error loading keystore", e);
+ }
+ return certs;
+ }
+
}
diff --git a/src/main/java/com/jmpeax/ssltoolbox/ui/Messages.java b/src/main/java/com/jmpeax/ssltoolbox/utils/Messages.java
similarity index 93%
rename from src/main/java/com/jmpeax/ssltoolbox/ui/Messages.java
rename to src/main/java/com/jmpeax/ssltoolbox/utils/Messages.java
index 250abfb..ef2f150 100644
--- a/src/main/java/com/jmpeax/ssltoolbox/ui/Messages.java
+++ b/src/main/java/com/jmpeax/ssltoolbox/utils/Messages.java
@@ -1,4 +1,4 @@
-package com.jmpeax.ssltoolbox.ui;
+package com.jmpeax.ssltoolbox.utils;
import java.util.ResourceBundle;
import org.jetbrains.annotations.PropertyKey;
diff --git a/src/main/java/com/jmpeax/ssltoolbox/utils/Prompts.java b/src/main/java/com/jmpeax/ssltoolbox/utils/Prompts.java
new file mode 100644
index 0000000..76363d9
--- /dev/null
+++ b/src/main/java/com/jmpeax/ssltoolbox/utils/Prompts.java
@@ -0,0 +1,18 @@
+package com.jmpeax.ssltoolbox.utils;
+
+import javax.swing.*;
+
+public class Prompts {
+
+
+ public static char[] askPassword(JPanel parent) {
+ JPasswordField passwordField = new JPasswordField();
+ passwordField.requestFocusInWindow();
+ int option = JOptionPane.showConfirmDialog(parent, passwordField, "Enter Password", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
+ if (option == JOptionPane.OK_OPTION) {
+ return passwordField.getPassword();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index f9f3056..6f5877c 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -28,12 +28,14 @@
-
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties
index 00442e7..ff32e44 100644
--- a/src/main/resources/messages_en.properties
+++ b/src/main/resources/messages_en.properties
@@ -1,7 +1,7 @@
x509.subject=Subject:
x509.issuer=Issuer:
x509.serial-number=Serial Number:
-x509.valid-from="Valid From:"
-x509.valid-to="Valid To:"
+x509.valid-from=Valid From:
+x509.valid-to=Valid To:
x509.public-key=Public Key:
-x509.signature-algorithm="Signature Algorithm:"
\ No newline at end of file
+x509.signature-algorithm=Signature Algorithm:
\ No newline at end of file
diff --git a/test.p12 b/test.p12
new file mode 100644
index 0000000..b0ddbed
Binary files /dev/null and b/test.p12 differ