jcbTimestampServerUrl;
+ private JPanel jpButtons;
+ private JButton jbOK;
+ private JButton jbCancel;
+
+ private PrivateKey signPrivateKey;
+ private KeyPairType signKeyPairType;
+ private File inputFile;
+ // TODO JW - get rid of outputFile. It will be set automatically.
+ private File outputFile;
+ private SignatureType signatureType;
+ private DigestType digestType;
+ private String tsaUrl;
+ private boolean successStatus = true;
+
+ /**
+ * Creates a new DSignFile dialog.
+ *
+ * @param parent The parent frame
+ * @param signPrivateKey Signing key pair's private key
+ * @param signKeyPairType Signing key pair's type
+ * @param signatureName Default signature name
+ */
+ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPairType, String signatureName) {
+ super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
+ this.signPrivateKey = signPrivateKey;
+ this.signKeyPairType = signKeyPairType;
+ setTitle(res.getString("DSignFile.Title"));
+ initComponents(signatureName);
+ }
+
+ /**
+ * Initializes the dialogue panel and associated elements
+ *
+ * @param signatureName String
+ */
+ private void initComponents(String signatureName) {
+ jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
+ jcbDetachedSignature = new JCheckBox();
+ jcbDetachedSignature.setSelected(true);
+ jcbDetachedSignature.setToolTipText(res.getString("DSignFile.jcbDetachedSignature.tooltip"));
+
+ jlInputFile = new JLabel(res.getString("DSignFile.jlInputFile.text"));
+ jtfInputFile = new JTextField(30);
+ jtfInputFile.setCaretPosition(0);
+ jtfInputFile.setToolTipText(res.getString("DSignFile.jtfInputFile.tooltip"));
+
+ jbInputFileBrowse = new JButton(res.getString("DSignFile.jbInputFileBrowse.text"));
+ PlatformUtil.setMnemonic(jbInputFileBrowse, res.getString("DSignFile.jbInputFileBrowse.mnemonic").charAt(0));
+ jbInputFileBrowse.setToolTipText(res.getString("DSignFile.jbInputFileBrowse.tooltip"));
+
+ // TODO JW - Remove default panel margin/insets.
+ jpInputFile = new JPanel();
+ jpInputFile.add(jtfInputFile);
+ jpInputFile.add(jbInputFileBrowse);
+
+ jlSignatureAlgorithm = new JLabel(res.getString("DSignFile.jlSignatureAlgorithm.text"));
+ jcbSignatureAlgorithm = new JComboBox<>();
+ DialogHelper.populateSigAlgs(signKeyPairType, this.signPrivateKey, jcbSignatureAlgorithm);
+ jcbSignatureAlgorithm.setToolTipText(res.getString("DSignFile.jcbSignatureAlgorithm.tooltip"));
+
+ jlDigestAlgorithm = new JLabel(res.getString("DSignFile.jlDigestAlgorithm.text"));
+ jcbDigestAlgorithm = new JComboBox<>();
+ populateDigestAlgs();
+ jcbDigestAlgorithm.setToolTipText(res.getString("DSignFile.jcbDigestAlgorithm.tooltip"));
+
+ jlAddTimestamp = new JLabel(res.getString("DSignFile.jlAddTimestamp.text"));
+ jcbAddTimestamp = new JCheckBox();
+ jcbAddTimestamp.setSelected(false);
+ jcbAddTimestamp.setToolTipText(res.getString("DSignFile.jcbAddTimestamp.tooltip"));
+
+ jlTimestampServerUrl = new JLabel(res.getString("DSignFile.jlTimestampServerUrl.text"));
+ jcbTimestampServerUrl = new JComboBox<>();
+ jcbTimestampServerUrl.setEditable(true);
+ jcbTimestampServerUrl.setEnabled(false);
+ jcbTimestampServerUrl.setToolTipText(res.getString("DSignFile.jcbTimestampServerUrl.tooltip"));
+ jcbTimestampServerUrl.setModel(new DefaultComboBoxModel<>(URLs.TSA_URLS));
+
+ jbOK = new JButton(res.getString("DSignFile.jbOK.text"));
+
+ jbCancel = new JButton(res.getString("DSignFile.jbCancel.text"));
+ jbCancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+ .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), CANCEL_KEY);
+
+ jpButtons = PlatformUtil.createDialogButtonPanel(jbOK, jbCancel, "insets 0");
+
+ // layout
+ Container pane = getContentPane();
+ pane.setLayout(new MigLayout("insets dialog, fill", "[para]unrel[right]unrel[]", "[]unrel[]"));
+ MiGUtil.addSeparator(pane, res.getString("DSignFile.jlFiles.text"));
+ pane.add(jlInputFile, "skip");
+ pane.add(jpInputFile, "wrap");
+ pane.add(jlDetachedSignature, "skip");
+ pane.add(jcbDetachedSignature, "wrap");
+ MiGUtil.addSeparator(pane, res.getString("DSignFile.jlSignature.text"));
+ pane.add(jlSignatureAlgorithm, "skip");
+ pane.add(jcbSignatureAlgorithm, "sgx, wrap");
+ pane.add(jlDigestAlgorithm, "skip");
+ pane.add(jcbDigestAlgorithm, "sgx, wrap para");
+ MiGUtil.addSeparator(pane, res.getString("DSignFile.jlTimestamp.text"));
+ pane.add(jlAddTimestamp, "skip");
+ pane.add(jcbAddTimestamp, "wrap");
+ pane.add(jlTimestampServerUrl, "skip");
+ pane.add(jcbTimestampServerUrl, "sgx, wrap para");
+ pane.add(new JSeparator(), "spanx, growx, wrap para");
+ pane.add(jpButtons, "right, spanx");
+
+ // actions
+ jbInputFileBrowse.addActionListener(evt -> {
+ try {
+ CursorUtil.setCursorBusy(DSignFile.this);
+ inputFileBrowsePressed();
+ } finally {
+ CursorUtil.setCursorFree(DSignFile.this);
+ }
+ });
+
+ jcbAddTimestamp.addItemListener(evt -> enableDisableElements());
+ jcbDetachedSignature.addItemListener(evt -> enableDisableElements());
+
+ jbOK.addActionListener(evt -> okPressed());
+
+ jbCancel.getActionMap().put(CANCEL_KEY, new AbstractAction() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public void actionPerformed(ActionEvent evt) {
+ cancelPressed();
+ }
+ });
+ jbCancel.addActionListener(evt -> cancelPressed());
+
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent evt) {
+ closeDialog();
+ }
+ });
+
+ setResizable(false);
+
+ getRootPane().setDefaultButton(jbOK);
+
+ pack();
+ setLocationRelativeTo(null);
+
+ }
+
+ /**
+ * This function enables and disables elements in the dialog
+ */
+ protected void enableDisableElements() {
+ jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
+ }
+
+ /**
+ * Populate combination box with items
+ */
+ private void populateDigestAlgs() {
+ jcbDigestAlgorithm.removeAllItems();
+
+ jcbDigestAlgorithm.addItem(DigestType.SHA1);
+ jcbDigestAlgorithm.addItem(DigestType.SHA224);
+ jcbDigestAlgorithm.addItem(DigestType.SHA256);
+ jcbDigestAlgorithm.addItem(DigestType.SHA384);
+ jcbDigestAlgorithm.addItem(DigestType.SHA512);
+
+ jcbDigestAlgorithm.setSelectedItem(DigestType.SHA256);
+ }
+
+ /**
+ * Get chosen input file.
+ *
+ * @return File input file
+ */
+ public File getInputFile() {
+ return inputFile;
+ }
+
+ /**
+ * Get chosen output file.
+ *
+ * @return File output file
+ */
+ public File getOutputFile() {
+ return outputFile;
+ }
+
+ /**
+ * Get chosen signature type.
+ *
+ * @return SignatureType or null if dialog cancelled
+ */
+ public SignatureType getSignatureType() {
+ return signatureType;
+ }
+
+ /**
+ * Get chosen digest type.
+ *
+ * @return DigestType or null if dialog cancelled
+ */
+ public DigestType getDigestType() {
+ return digestType;
+ }
+
+ /**
+ * Get chosen TSA URL.
+ *
+ * @return String TSA URL or null if dialog cancelled
+ */
+ public String getTimestampingServerUrl() {
+ return tsaUrl;
+ }
+
+ /**
+ * The function checks the following
+ *
+ * - dialog fields to ensure they are not empty
+ *
+ * - output file paths for overwriting files
+ */
+ private void okPressed() {
+ // check if any files selected
+ if (inputFile == null) {
+ JOptionPane.showMessageDialog(this, res.getString("DSignFile.InputFileRequired.message"), getTitle(),
+ JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ // check if time stamp URL is empty
+ if (jcbAddTimestamp.isSelected() && jcbTimestampServerUrl.getSelectedItem().toString().isEmpty()) {
+ JOptionPane.showMessageDialog(this, res.getString("DSignFile.EmptyTimestampUrl.message"), getTitle(),
+ JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+
+ signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
+ digestType = (DigestType) jcbDigestAlgorithm.getSelectedItem();
+
+ // check add time stamp is selected and assign value
+ if (jcbAddTimestamp.isSelected()) {
+ tsaUrl = jcbTimestampServerUrl.getSelectedItem().toString();
+ }
+
+// // set output Jar files and Overwrite File if selected
+// if (!setOutputJarFiles(inputJarFiles)) {
+// return;
+// }
+
+ closeDialog();
+ }
+
+// /**
+// * Set output JAR files and check files for overwrite
+// *
+// * @return Boolean true if successful false if no option chosen
+// */
+// private boolean setOutputJarFiles(File[] files) {
+// String outputJarPrefix = jtfPrefix.getText().trim();
+// String outputJarSuffix = jtfSuffix.getText().trim();
+// final String FILE_SUFFIX = ".jar";
+// JCheckBox checkbox = new JCheckBox(res.getString("DSignFile.OverwriteSkip.message"));
+//
+// // set input files array to output files list
+// this.outputJarFiles = new ArrayList<>(Arrays.asList(files));
+//
+// if (jrbOutputJarFixes.isSelected()) {
+// // loop through output JAR files
+// for (int i = 0; i < outputJarFiles.size(); i++) {
+// // set prefix and suffix to the file name
+// String fileBaseName = FileNameUtil.removeExtension(outputJarFiles.get(i).getName());
+// String outFileName =
+// outputJarFiles.get(i).getParent() + "\\" + outputJarPrefix + fileBaseName + outputJarSuffix +
+// FILE_SUFFIX;
+// // replace file object in arraylist
+// this.outputJarFiles.set(i, new File(outFileName));
+//
+// if (!checkbox.isSelected()) {
+// // check if file exists
+// if (outputJarFiles.get(i).isFile()) {
+// String message = MessageFormat.format(res.getString("DSignFile.OverWriteOutputJarFile.message"),
+// outputJarFiles.get(i));
+// Object[] params = { message, checkbox };
+//
+// // check if overwrite is allowed and present checkbox to skip overwrite message
+// int selected = JOptionPane.showConfirmDialog(this, params, getTitle(),
+// JOptionPane.YES_NO_OPTION);
+// if (selected != JOptionPane.YES_OPTION) {
+// this.outputJarFiles.clear();
+// return false;
+// }
+// }
+// }
+// }
+// }
+// return true;
+// }
+
+// /**
+// * Checks to overwrite an existing signature
+// *
+// * @return Boolean continues jar signing if true cancels process if false
+// */
+// private boolean checkSignature(File[] files) {
+// JCheckBox checkbox = new JCheckBox(res.getString("DSignFile.OverwriteSkip.message"));
+//
+// for (File file : files) {
+// try {
+// // check if the existing signature matches the current signature
+// if (JarSigner.hasSignature(file, this.signatureName)) {
+// String message = MessageFormat.format(res.getString("DSignFile.SignatureOverwrite.message"),
+// this.signatureName, file.getName());
+// Object[] params = {message, checkbox};
+// // check if overwrite is allowed and present checkbox to skip overwrite message
+// int selected = JOptionPane.showConfirmDialog(this, params, getTitle(), JOptionPane.YES_NO_OPTION);
+// if (selected != JOptionPane.YES_OPTION) {
+// return false;
+// }
+// }
+// } catch (IOException ex) {
+// DError.displayError(this, ex);
+// return false;
+// }
+// // check to skip overwrite alert message
+// if (checkbox.isSelected()) {
+// return true;
+// }
+// }
+// return true;
+// }
+
+ /**
+ * Get input file
+ */
+ private void inputFileBrowsePressed() {
+ JFileChooser chooser = FileChooserFactory.getAllFileChooser();
+ chooser.setCurrentDirectory(CurrentDirectory.get());
+ chooser.setDialogTitle(res.getString("DSignFile.ChooseInputFile.Title"));
+ chooser.setMultiSelectionEnabled(false);
+ chooser.setApproveButtonText(res.getString("DSignFile.InputFileChooser.button"));
+
+ int rtnValue = chooser.showOpenDialog(this);
+ if (rtnValue == JFileChooser.APPROVE_OPTION) {
+ File chosenFile = chooser.getSelectedFile();
+ CurrentDirectory.updateForFile(chosenFile);
+ inputFile = chosenFile;
+ // TODO JW - Is there a better location for this code?
+ jtfInputFile.setText(inputFile.getAbsolutePath());
+ jtfInputFile.setCaretPosition(0);
+ }
+ }
+
+ /**
+ * Returns the current success status
+ *
+ * @return successStatus true if successful false if not
+ */
+ public boolean isSuccessful() {
+ return successStatus;
+ }
+
+ /**
+ * Call the close dialog method
+ */
+ private void cancelPressed() {
+ successStatus = false;
+ closeDialog();
+ }
+
+ /**
+ * Close the dialog method
+ */
+ private void closeDialog() {
+ setVisible(false);
+ dispose();
+ }
+
+ // for quick UI testing
+ public static void main(String[] args) throws Exception {
+ DialogViewer.prepare();
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyPairType.RSA.jce(), KSE.BC);
+ kpg.initialize(1024, new SecureRandom());
+ KeyPair kp = kpg.generateKeyPair();
+
+ DSignFile dialog = new DSignFile(new JFrame(), kp.getPrivate(), KeyPairType.RSA, "signature name");
+ DialogViewer.run(dialog);
+ }
+}
\ No newline at end of file
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index b6afd3648..eafce52d0 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -592,6 +592,16 @@ SignJarAction.statusbar = Sign a Java Archive (JAR) using the Ke
SignJarAction.text = Sign JAR
SignJarAction.tooltip = Sign a JAR
+SignFileAction.ButtonOK.message = OK
+#SignFileAction.ButtonView.message = View
+SignFileAction.NoWriteFile.message = Could not write to file ''{0}''.
+SignFileAction.SignJar.Title = Sign File
+#SignFileAction.SignJarError.message = {0} of {1} file(s) have an error.
+#SignFileAction.SignJarSuccessful.message = {0} JAR file(s) successfully signed.
+SignFileAction.statusbar = Sign a file using the Key Pair entry
+SignFileAction.text = Sign File
+SignFileAction.tooltip = Sign a file
+
SignMidletAction.ReqRsaKeyPairMidletSigning.message = Only RSA key pairs can be used for MIDlet signing.
SignMidletAction.SignMidlet.Title = Sign MIDlet
SignMidletAction.SignMidletSuccessful.message = MIDlet Signing Successful.
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index ed213dad9..48ad09437 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -234,6 +234,34 @@ DSignJwt.jdtNotBefore.tooltip = Not before
DSignJwt.jlSignatureAlgorithm.text = Signature Algorithm:
DSignJwt.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign the JWT
+DSignFile.ChooseInputFile.Title = Choose Input File
+DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
+DSignFile.InputFileChooser.button = Choose
+DSignFile.InputFileRequired.message = Path to Input File required.
+DSignFile.NoOpenFile.Problem = Could not open Input File ''{0}''.
+DSignFile.ProblemOpeningFile.Title = Problem Opening Input File
+DSignFile.Title = Sign File
+DSignFile.jbCancel.text = Cancel
+DSignFile.jbInputFileBrowse.mnemonic = B
+DSignFile.jbInputFileBrowse.text = Browse
+DSignFile.jbInputFileBrowse.tooltip = Browse to Input File
+DSignFile.jbOK.text = OK
+DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
+DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
+DSignFile.jcbDigestAlgorithm.tooltip = Message digest algorithm used to digest the file content
+DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
+DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
+DSignFile.jlAddTimestamp.text = Add Time stamp:
+DSignFile.jlDetachedSignature.text = Generate Detached Signature:
+DSignFile.jlDigestAlgorithm.text = Digest Algorithm:
+DSignFile.jlFiles.text = Files
+DSignFile.jlInputFile.text = Input File:
+DSignFile.jlSignature.text = Signature
+DSignFile.jlSignatureAlgorithm.text = Signature Algorithm:
+DSignFile.jlTimestamp.text = Time stamp
+DSignFile.jlTimestampServerUrl.text = TSA URL:
+DSignFile.jtfInputFile.tooltip = File to sign
+
ExamineFileAction.ExamineFile.Title = Open File
ExamineFileAction.ExamineFile.button = Open
ExamineFileAction.NoCertsFound.message = No certificates found
From b6e5e9a42d28ad06ba3f6eb1fbe474829286e773 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 3 Dec 2024 11:07:07 -0500
Subject: [PATCH 22/69] Hooked up the digital signature code.
---
.../org/kse/gui/actions/SignFileAction.java | 47 ++++++++++++++--
.../org/kse/gui/dialogs/sign/DSignFile.java | 54 +++++--------------
.../kse/gui/dialogs/sign/resources.properties | 7 +--
3 files changed, 56 insertions(+), 52 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 6f4e9032e..649e2f0af 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -21,18 +21,31 @@
import java.awt.Toolkit;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
+import java.util.Arrays;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSProcessableFile;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.kse.KSE;
import org.kse.gui.passwordmanager.Password;
+import org.kse.crypto.CryptoException;
import org.kse.crypto.digest.DigestType;
import org.kse.crypto.keypair.KeyPairType;
import org.kse.crypto.keypair.KeyPairUtil;
@@ -101,9 +114,6 @@ protected void doAction() {
// set the key pair type
KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey);
- // set the signer
- String signer = KSE.getFullApplicationName();
-
// get the jars, signatures, and time stamp
DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, alias);
dSignFile.setLocationRelativeTo(frame);
@@ -114,11 +124,15 @@ protected void doAction() {
return;
}
+ // TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
+ boolean detachedSignature = dSignFile.isDetachedSignature();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
File outputFile = dSignFile.getOutputFile();
String tsaUrl = dSignFile.getTimestampingServerUrl();
- DigestType digestType = dSignFile.getDigestType();
+
+ // TODO JW - What if the dialog is cancelled?
+ sign(inputFile, outputFile, privateKey, certs, detachedSignature, signatureType, tsaUrl, provider);
// start jar signing process
// DSignJarSigning dSignJarSigning = new DSignJarSigning(frame, inputJarFile, outputJarFile, privateKey, certs,
@@ -167,4 +181,29 @@ protected void doAction() {
DError.displayError(frame, ex);
}
}
+
+ public static void sign(File inputFile, File outputFile, PrivateKey privateKey, X509Certificate[] certificateChain,
+ boolean detachedSignature, SignatureType signatureType, String tsaUrl, Provider provider)
+ throws CryptoException {
+ try {
+ CMSTypedData msg = new CMSProcessableFile(inputFile);
+
+ CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+ ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey);
+ generator.addSignerInfoGenerator(
+ new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
+ .build(contentSigner, certificateChain[0]));
+ generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
+
+ CMSSignedData sig = generator.generate(msg, !detachedSignature);
+
+ try (OutputStream os = new FileOutputStream(outputFile)) {
+ os.write(sig.getEncoded());
+ }
+ }
+ catch (Exception e) {
+ // TODO JW - Create exception message
+ throw new CryptoException("TODO");
+ }
+ }
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 85a88c5ec..f2a07425f 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -96,8 +96,6 @@ public class DSignFile extends JEscDialog {
private JCheckBox jcbDetachedSignature;
private JLabel jlSignatureAlgorithm;
private JComboBox jcbSignatureAlgorithm;
- private JLabel jlDigestAlgorithm;
- private JComboBox jcbDigestAlgorithm;
private JLabel jlAddTimestamp;
private JCheckBox jcbAddTimestamp;
private JLabel jlTimestampServerUrl;
@@ -111,8 +109,8 @@ public class DSignFile extends JEscDialog {
private File inputFile;
// TODO JW - get rid of outputFile. It will be set automatically.
private File outputFile;
+ private boolean detachedSignature;
private SignatureType signatureType;
- private DigestType digestType;
private String tsaUrl;
private boolean successStatus = true;
@@ -162,11 +160,6 @@ private void initComponents(String signatureName) {
DialogHelper.populateSigAlgs(signKeyPairType, this.signPrivateKey, jcbSignatureAlgorithm);
jcbSignatureAlgorithm.setToolTipText(res.getString("DSignFile.jcbSignatureAlgorithm.tooltip"));
- jlDigestAlgorithm = new JLabel(res.getString("DSignFile.jlDigestAlgorithm.text"));
- jcbDigestAlgorithm = new JComboBox<>();
- populateDigestAlgs();
- jcbDigestAlgorithm.setToolTipText(res.getString("DSignFile.jcbDigestAlgorithm.tooltip"));
-
jlAddTimestamp = new JLabel(res.getString("DSignFile.jlAddTimestamp.text"));
jcbAddTimestamp = new JCheckBox();
jcbAddTimestamp.setSelected(false);
@@ -190,17 +183,12 @@ private void initComponents(String signatureName) {
// layout
Container pane = getContentPane();
pane.setLayout(new MigLayout("insets dialog, fill", "[para]unrel[right]unrel[]", "[]unrel[]"));
- MiGUtil.addSeparator(pane, res.getString("DSignFile.jlFiles.text"));
pane.add(jlInputFile, "skip");
pane.add(jpInputFile, "wrap");
pane.add(jlDetachedSignature, "skip");
pane.add(jcbDetachedSignature, "wrap");
- MiGUtil.addSeparator(pane, res.getString("DSignFile.jlSignature.text"));
pane.add(jlSignatureAlgorithm, "skip");
pane.add(jcbSignatureAlgorithm, "sgx, wrap");
- pane.add(jlDigestAlgorithm, "skip");
- pane.add(jcbDigestAlgorithm, "sgx, wrap para");
- MiGUtil.addSeparator(pane, res.getString("DSignFile.jlTimestamp.text"));
pane.add(jlAddTimestamp, "skip");
pane.add(jcbAddTimestamp, "wrap");
pane.add(jlTimestampServerUrl, "skip");
@@ -256,21 +244,6 @@ protected void enableDisableElements() {
jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
}
- /**
- * Populate combination box with items
- */
- private void populateDigestAlgs() {
- jcbDigestAlgorithm.removeAllItems();
-
- jcbDigestAlgorithm.addItem(DigestType.SHA1);
- jcbDigestAlgorithm.addItem(DigestType.SHA224);
- jcbDigestAlgorithm.addItem(DigestType.SHA256);
- jcbDigestAlgorithm.addItem(DigestType.SHA384);
- jcbDigestAlgorithm.addItem(DigestType.SHA512);
-
- jcbDigestAlgorithm.setSelectedItem(DigestType.SHA256);
- }
-
/**
* Get chosen input file.
*
@@ -290,21 +263,21 @@ public File getOutputFile() {
}
/**
- * Get chosen signature type.
- *
- * @return SignatureType or null if dialog cancelled
+ * Get the chosen detached signature setting
+ *
+ * @return boolean detached signature setting
*/
- public SignatureType getSignatureType() {
- return signatureType;
+ public boolean isDetachedSignature() {
+ return detachedSignature;
}
/**
- * Get chosen digest type.
+ * Get chosen signature type.
*
- * @return DigestType or null if dialog cancelled
+ * @return SignatureType or null if dialog cancelled
*/
- public DigestType getDigestType() {
- return digestType;
+ public SignatureType getSignatureType() {
+ return signatureType;
}
/**
@@ -339,17 +312,14 @@ private void okPressed() {
}
signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
- digestType = (DigestType) jcbDigestAlgorithm.getSelectedItem();
+ detachedSignature = jcbDetachedSignature.isSelected();
// check add time stamp is selected and assign value
if (jcbAddTimestamp.isSelected()) {
tsaUrl = jcbTimestampServerUrl.getSelectedItem().toString();
}
-// // set output Jar files and Overwrite File if selected
-// if (!setOutputJarFiles(inputJarFiles)) {
-// return;
-// }
+ outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
closeDialog();
}
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 48ad09437..31d0b320b 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -248,17 +248,12 @@ DSignFile.jbInputFileBrowse.tooltip = Browse to Input File
DSignFile.jbOK.text = OK
DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
-DSignFile.jcbDigestAlgorithm.tooltip = Message digest algorithm used to digest the file content
DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
DSignFile.jlAddTimestamp.text = Add Time stamp:
DSignFile.jlDetachedSignature.text = Generate Detached Signature:
-DSignFile.jlDigestAlgorithm.text = Digest Algorithm:
-DSignFile.jlFiles.text = Files
-DSignFile.jlInputFile.text = Input File:
-DSignFile.jlSignature.text = Signature
+DSignFile.jlInputFile.text = Input File:
DSignFile.jlSignatureAlgorithm.text = Signature Algorithm:
-DSignFile.jlTimestamp.text = Time stamp
DSignFile.jlTimestampServerUrl.text = TSA URL:
DSignFile.jtfInputFile.tooltip = File to sign
From f01e9d488e60fa3c1526bb3b6a186b9b8636dfa2 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Fri, 6 Dec 2024 00:23:15 -0800
Subject: [PATCH 23/69] Added time stamping and viewing of the time stamp
signer.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 56 +++++++++++++++++++
.../org/kse/gui/actions/SignFileAction.java | 17 +++++-
.../gui/actions/VerifySignatureAction.java | 17 +++++-
.../org/kse/gui/dialogs/DViewSignature.java | 56 ++++++++++++++++---
.../org/kse/gui/dialogs/resources.properties | 3 +
.../kse/gui/dialogs/sign/resources.properties | 1 +
6 files changed, 138 insertions(+), 12 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index a570629e4..2172c8902 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -29,12 +29,20 @@
import org.bouncycastle.asn1.cms.Attribute;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TimeStampToken;
import org.kse.crypto.CryptoException;
import org.kse.utilities.pem.PemInfo;
import org.kse.utilities.pem.PemUtil;
+/**
+ * Provides utility methods relating to Cryptographic Message Syntax (CMS).
+ */
public class CmsUtil {
private static final String CMS_PEM_TYPE = "CMS";
private static final String PKCS7_PEM_TYPE = "PKCS7";
@@ -91,4 +99,52 @@ public static Date getSigningTime(SignerInformation signerInfo) throws CryptoExc
}
return signingTime;
}
+
+ /**
+ * Extracts the time stamp token, if present, from the signature's unsigned attributes.
+ *
+ * @param signerInfo The signer information.
+ * @return The time stamp token as TimeStampToken, if present, else null.
+ */
+ public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) throws CryptoException
+ {
+ TimeStampToken timeStampToken = null;
+ AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
+ if (unsignedAttributes != null) {
+ Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
+ if (tsTokenAttribute != null) {
+ try {
+ timeStampToken = new TimeStampToken(ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]));
+ } catch (TSPException | IOException e) {
+ // TODO JW Auto-generated catch block
+ throw new CryptoException(e);
+ }
+ }
+ }
+ return timeStampToken;
+ }
+
+ /**
+ * Extracts the time stamp token, if present, from the signature's unsigned attributes.
+ *
+ * @param signerInfo The signer information.
+ * @return The time stamp token as CMSSignedData, if present, else null.
+ */
+ public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) throws CryptoException
+ {
+ CMSSignedData timeStampToken = null;
+ AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
+ if (unsignedAttributes != null) {
+ Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
+ if (tsTokenAttribute != null) {
+ try {
+ timeStampToken = new CMSSignedData(ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]));
+ } catch (CMSException e) {
+ // TODO JW Auto-generated catch block
+ throw new CryptoException(e);
+ }
+ }
+ }
+ return timeStampToken;
+ }
}
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 649e2f0af..f6262ad89 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -34,21 +34,30 @@
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.tsp.TimeStampResponse;
+import org.bouncycastle.tsp.TimeStampToken;
import org.kse.KSE;
import org.kse.gui.passwordmanager.Password;
import org.kse.crypto.CryptoException;
import org.kse.crypto.digest.DigestType;
import org.kse.crypto.keypair.KeyPairType;
import org.kse.crypto.keypair.KeyPairUtil;
+import org.kse.crypto.signing.JarSigner;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.KseFrame;
@@ -195,10 +204,14 @@ public static void sign(File inputFile, File outputFile, PrivateKey privateKey,
.build(contentSigner, certificateChain[0]));
generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
- CMSSignedData sig = generator.generate(msg, !detachedSignature);
+ CMSSignedData signedData = generator.generate(msg, !detachedSignature);
+
+ if (tsaUrl != null) {
+ signedData = JarSigner.addTimestamp(tsaUrl, signedData);
+ }
try (OutputStream os = new FileOutputStream(outputFile)) {
- os.write(sig.getEncoded());
+ os.write(signedData.getEncoded());
}
}
catch (Exception e) {
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 11866b6a2..bb9b9be9c 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -83,10 +83,12 @@
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.Selector;
import org.bouncycastle.util.Store;
import org.kse.KSE;
import org.kse.crypto.CryptoException;
+import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.CurrentDirectory;
import org.kse.gui.FileChooserFactory;
@@ -242,13 +244,24 @@ protected void doAction() {
// }
// }
//
- @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector
+ // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
Collection matchedCerts = certStore.getMatches(signer.getSID());
- for (X509CertificateHolder cert : matchedCerts) {
+ if (!matchedCerts.isEmpty()) {
+ X509CertificateHolder cert = matchedCerts.iterator().next();
// TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
System.out.println("Verified by: " + cert.getSubject());
verified = true;
+
+ TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
+ if (tspToken != null) {
+ matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+ tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ System.out.println("Time stamped by: " + cert.getSubject());
+ }
+ }
}
}
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 50912bb3e..b72a49866 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -75,20 +75,21 @@
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1UTCTime;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TSPValidationException;
+import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.StoreException;
import org.bouncycastle.util.encoders.Hex;
import org.kse.KSE;
import org.kse.crypto.CryptoException;
@@ -154,6 +155,7 @@ public class DViewSignature extends JEscDialog {
private JTextField jtfContentDigest;
// TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes
private JButton jbCertificates;
+ private JButton jbTimeStamp;
private JButton jbCounterSigners;
private JButton jbExtensions;
private JButton jbPem;
@@ -161,7 +163,7 @@ public class DViewSignature extends JEscDialog {
private JButton jbOK;
private CMSSignedData signedData;
- private Collection signerInfos;
+ private CMSSignedData timeStampSigner;
/**
* Creates a new DViewCertificate dialog.
@@ -178,7 +180,6 @@ public DViewSignature(Window parent, String title, CMSSignedData signedData, Col
super(parent, title, Dialog.ModalityType.MODELESS);
this.kseFrame = kseFrame;
this.signedData = signedData;
- this.signerInfos = signers;
initComponents(signers);
}
@@ -240,6 +241,11 @@ private void initComponents(Collection signers) throws Crypto
jbCertificates.setToolTipText(res.getString("DViewSignature.jbCertificates.tooltip"));
PlatformUtil.setMnemonic(jbCertificates, res.getString("DViewSignature.jbCertificates.mnemonic").charAt(0));
+ jbTimeStamp = new JButton(res.getString("DViewSignature.jbTimeStamp.text"));
+ jbTimeStamp.setToolTipText(res.getString("DViewSignature.jbTimeStamp.tooltip"));
+ // TODO JW - Need mnemonic for time stamp button
+// PlatformUtil.setMnemonic(jbTimeStamp, res.getString("DViewSignature.jbTimeStamp.mnemonic").charAt(0));
+
jbCounterSigners = new JButton(res.getString("DViewSignature.jbCounterSigners.text"));
jbCounterSigners.setToolTipText(res.getString("DViewSignature.jbCounterSigners.tooltip"));
// TODO JW - Need mnemonic for counter signers button
@@ -278,6 +284,7 @@ private void initComponents(Collection signers) throws Crypto
pane.add(jlContentDigest, "");
pane.add(jtfContentDigest, "wrap");
pane.add(jbCertificates, "spanx, split");
+ pane.add(jbTimeStamp, "");
pane.add(jbCounterSigners, "");
// pane.add(jbExtensions, "");
pane.add(jbPem, "");
@@ -305,6 +312,15 @@ private void initComponents(Collection signers) throws Crypto
}
});
+ jbTimeStamp.addActionListener(evt -> {
+ try {
+ CursorUtil.setCursorBusy(DViewSignature.this);
+ timeStampPressed();
+ } finally {
+ CursorUtil.setCursorFree(DViewSignature.this);
+ }
+ });
+
jbCounterSigners.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewSignature.this);
@@ -378,6 +394,9 @@ private void populateDetails() {
if (signerInfo == null) {
jdnSubject.setEnabled(false);
jdnIssuer.setEnabled(false);
+ jbCertificates.setEnabled(false);
+ jbTimeStamp.setEnabled(false);
+ jbCounterSigners.setEnabled(false);
// jbExtensions.setEnabled(false);
jbPem.setEnabled(false);
jbAsn1.setEnabled(false);
@@ -442,8 +461,8 @@ private void populateDetails() {
jtfContentType.setCaretPosition(0);
// TODO JW - digest is only available after verify is called.
- jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':'));
- jtfContentDigest.setCaretPosition(0);
+// jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':'));
+// jtfContentDigest.setCaretPosition(0);
DigestType digestType = DigestType.resolveOid(signerInfo.getDigestAlgOID());
KeyInfo keyInfo = KeyPairUtil.getKeyInfo(cert.getPublicKey());
@@ -459,6 +478,15 @@ private void populateDetails() {
jtfSignatureAlgorithm.setCaretPosition(0);
}
+ timeStampSigner = CmsUtil.getTimeStampSignature(signerInfo);
+
+ if (timeStampSigner != null) {
+ jbTimeStamp.setEnabled(true);
+ } else {
+ jbTimeStamp.setEnabled(false);
+ }
+
+
if (signerInfo.getCounterSignatures().size() > 0) {
jbCounterSigners.setEnabled(true);
} else {
@@ -495,7 +523,7 @@ public boolean match(T obj) {
private void certificatesPressed() {
try {
List certs = X509CertUtil.convertCertificateHolders(
- signedData.getCertificates().getMatches(new SelectAll()));
+ signedData.getCertificates().getMatches(new SelectAll<>()));
DViewCertificate dViewCertificates = new DViewCertificate(this,
res.getString("DViewSignature.Certificates.Title"), certs.toArray(X509Certificate[]::new), kseFrame,
DViewCertificate.NONE);
@@ -506,6 +534,18 @@ private void certificatesPressed() {
}
}
+ private void timeStampPressed() {
+ try {
+ DViewSignature dViewSignature = new DViewSignature(this,
+ res.getString("DViewSignature.TimeStampSigner.Title"), timeStampSigner,
+ timeStampSigner.getSignerInfos().getSigners(), null);
+ dViewSignature.setLocationRelativeTo(this);
+ dViewSignature.setVisible(true);
+ } catch (CryptoException e) {
+ DError.displayError(this, e);
+ }
+ }
+
private void counterSignersPressed() {
SignerInformation signer = getSelectedSignerInfo();
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
index a449cc9e1..c6e9cd1ec 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
@@ -590,6 +590,7 @@ DViewSignature.Issuer.Title = Issuer
DViewSignature.NoGetEncodedCert.exception.message = Could not get the encoded form of the certificate.
DViewSignature.Pem.Title = Signature PEM
DViewSignature.Subject.Title = Subject
+DViewSignature.TimeStampSigner.Title = Time Stamp
DViewSignature.jbAsn1.mnemonic = A
DViewSignature.jbAsn1.text = ASN.1
DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signature
@@ -606,6 +607,8 @@ DViewSignature.jbOK.text = OK
DViewSignature.jbPem.mnemonic = P
DViewSignature.jbPem.text = PEM
DViewSignature.jbPem.tooltip = Display signature as PEM
+DViewSignature.jbTimeStamp.text = Time Stamp
+DViewSignature.jbTimeStamp.tooltip = Display the signature's time stamp signer
DViewSignature.jdnIssuer.tooltip = Signer's issuer certificate distinguished name
DViewSignature.jdnSubject.tooltip = Signer's subject certificate distinguished name
DViewSignature.jlContentDigest.text = Content Digest:
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 31d0b320b..83e386ec2 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -234,6 +234,7 @@ DSignJwt.jdtNotBefore.tooltip = Not before
DSignJwt.jlSignatureAlgorithm.text = Signature Algorithm:
DSignJwt.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign the JWT
+# TODO JW - Are all these strings in use?
DSignFile.ChooseInputFile.Title = Choose Input File
DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
DSignFile.InputFileChooser.button = Choose
From cdfbfdaea4e5506ac148b45e59ba6e96ac6a956f Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Mon, 9 Dec 2024 10:48:40 -0800
Subject: [PATCH 24/69] Added counter signing -- not fully complete (see
TODOs).
---
.../org/kse/crypto/signing/CmsSigner.java | 165 ++++++++++++++++++
.../java/org/kse/crypto/signing/CmsUtil.java | 69 ++++++++
.../org/kse/crypto/signing/JarSigner.java | 28 +--
kse/src/main/java/org/kse/gui/KseFrame.java | 8 +
.../kse/gui/actions/CounterSignAction.java | 138 +++++++++++++++
.../org/kse/gui/actions/SignFileAction.java | 70 +++-----
.../gui/actions/VerifySignatureAction.java | 106 +++--------
.../org/kse/gui/dialogs/DViewSignature.java | 2 +
.../org/kse/gui/dialogs/sign/DSignFile.java | 34 ++--
.../org/kse/gui/actions/resources.properties | 7 +
.../kse/gui/dialogs/sign/resources.properties | 46 ++---
11 files changed, 485 insertions(+), 188 deletions(-)
create mode 100644 kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
create mode 100644 kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
new file mode 100644
index 000000000..a55dc9898
--- /dev/null
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2004 - 2013 Wayne Grant
+ * 2013 - 2024 Kai Kramer
+ *
+ * This file is part of KeyStore Explorer.
+ *
+ * KeyStore Explorer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeyStore Explorer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeyStore Explorer. If not, see .
+ */
+package org.kse.crypto.signing;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableFile;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.kse.crypto.CryptoException;
+import org.kse.crypto.digest.DigestType;
+
+/**
+ * Class provides functionality to sign files using PKCS #7 Cryptographic
+ * Message Syntax (CMS).
+ */
+public class CmsSigner {
+
+ /**
+ * Signs a file using PKCS #7 CMS.
+ *
+ * @param inputFile The file to sign.
+ * @param outputFile The output file for the signature.
+ * @param privateKey The private key to use for signing.
+ * @param certificateChain The certificate chain for the private key.
+ * @param detachedSignature True if the signature is to be detached. False,
+ * encapsulate the file into the signature.
+ * @param signatureType The signature type to use for signing.
+ * @param tsaUrl An optional TSA URL for adding a time stamp token to
+ * the signature.
+ * @param provider
+ */
+ public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Certificate[] certificateChain,
+ boolean detachedSignature, SignatureType signatureType, String tsaUrl, Provider provider)
+ throws CryptoException {
+ try {
+ CMSTypedData msg = new CMSProcessableFile(inputFile);
+
+ CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+ ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey);
+ generator.addSignerInfoGenerator(
+ new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
+ .build(contentSigner, certificateChain[0]));
+ generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
+
+ CMSSignedData signedData = generator.generate(msg, !detachedSignature);
+
+ if (tsaUrl != null) {
+ signedData = addTimestamp(tsaUrl, signedData);
+ }
+
+ return signedData;
+ } catch (Exception e) {
+ // TODO JW - Create exception message
+ throw new CryptoException("TODO");
+ }
+ }
+
+ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey privateKey,
+ X509Certificate[] certificateChain, boolean detachedSignature, SignatureType signatureType, String tsaUrl,
+ Provider provider) throws CryptoException {
+ try {
+ CMSSignedDataGenerator counterSignerGen = new CMSSignedDataGenerator();
+ ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey);
+ counterSignerGen.addSignerInfoGenerator(
+ new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
+ .build(contentSigner, certificateChain[0]));
+
+ CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
+ for (SignerInformation signer : signedData.getSignerInfos()) {
+ SignerInformationStore counterSigners = counterSignerGen.generateCounterSigners(signer);
+ signer = SignerInformation.addCounterSigners(signer, counterSigners);
+
+ generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
+ generator.addSigners(new SignerInformationStore(signer));
+ }
+ generator.addCertificates(signedData.getCertificates());
+
+ CMSSignedData counterSignedData = generator.generate(signedData.getSignedContent(), !detachedSignature);
+
+ // TODO JW - is it possible to add a timestamp for a counter signature? The current logic drops the counter signer.
+// if (tsaUrl != null) {
+// counterSignedData = addTimestamp(tsaUrl, counterSignedData);
+// }
+
+ return counterSignedData;
+ } catch (CertificateEncodingException | OperatorCreationException | CMSException e) {
+ // TODO Auto-generated catch block
+ throw new CryptoException("TODO");
+ }
+ }
+
+ /**
+ * Adds a timestamp to a PKCS #7 signature.
+ *
+ * @param tsaUrl The URL of the time stamp authority
+ * @param signedData The signature to time stamp.
+ * @return CMSSignedData with time stamp.
+ */
+ public static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData) throws IOException {
+
+ Collection signerInfos = signedData.getSignerInfos().getSigners();
+
+ // get signature of first signer (should be the only one)
+ SignerInformation si = signerInfos.iterator().next();
+ byte[] signature = si.getSignature();
+
+ // send request to TSA
+ byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, DigestType.SHA256);
+
+ // create new SignerInformation with TS attribute
+ Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
+ new DERSet(ASN1Primitive.fromByteArray(token)));
+ ASN1EncodableVector timestampVector = new ASN1EncodableVector();
+ timestampVector.add(tokenAttr);
+ AttributeTable at = new AttributeTable(timestampVector);
+ si = SignerInformation.replaceUnsignedAttributes(si, at);
+ signerInfos.clear();
+ signerInfos.add(si);
+ SignerInformationStore newSignerStore = new SignerInformationStore(signerInfos);
+
+ // create new signed data
+ return CMSSignedData.replaceSigners(signedData, newSignerStore);
+ }
+}
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index 2172c8902..121bb09f7 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -19,10 +19,13 @@
*/
package org.kse.crypto.signing;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
import java.text.ParseException;
import java.util.Date;
import java.util.Enumeration;
+import java.util.function.Supplier;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1UTCTime;
@@ -32,6 +35,7 @@
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.tsp.TSPException;
@@ -50,6 +54,71 @@ public class CmsUtil {
private CmsUtil() {
}
+ public static CMSSignedData loadSignature(File signatureFile, Supplier chooser)
+ throws IOException, CryptoException, CMSException {
+
+ // TODO JW - What if the file cannot be opened?
+ byte[] signature = Files.readAllBytes(signatureFile.toPath());
+
+ if (PemUtil.isPemFormat(signature)) {
+ PemInfo signaturePem = PemUtil.decode(signature);
+ if (signaturePem == null) {
+ // TODO JW - What to throw if the signature is detected as PEM, but is not PEM?
+ throw new CryptoException("Not a PEM file, but has PEM header!");
+ }
+ // TODO JW - Do we even want to check the type? Should we just let the BC CMS
+ // class bomb out?
+ if (!"CMS".equals(signaturePem.getType()) && !"PKCS7".equals(signaturePem.getType())) {
+ // TODO JW - What to throw if the signature is not the correct type?
+ throw new CryptoException("PEM is not of type CMS or PKCS7");
+ }
+ signature = signaturePem.getContent();
+ }
+ CMSSignedData signedData = new CMSSignedData(signature);
+
+ if (signedData.isCertificateManagementMessage()) {
+ // TODO JW - Display a message indicating that the file doesn't have any
+ // signatures.
+ return null;
+ }
+
+ if (signedData.isDetachedSignature()) {
+ CMSProcessableFile content = loadDetachedContent(signatureFile, chooser);
+ if (content == null) {
+ return null;
+ }
+ signedData = new CMSSignedData(content, signature);
+ }
+ return signedData;
+ }
+
+ private static CMSProcessableFile loadDetachedContent(File signatureFile, Supplier chooser)
+ throws CMSException {
+ // Look for the content file. if not present, prompt for it.
+ File contentFile = null;
+
+ // First try file name with signature extension stripped.
+ int extensionIndex = signatureFile.getAbsolutePath().lastIndexOf('.');
+ if (extensionIndex > 0) {
+ // Turn file_name.txt.p7s into file_name.txt
+ String contentFileName = signatureFile.getAbsolutePath().substring(0, extensionIndex);
+ contentFile = new File(contentFileName);
+ if (!contentFile.exists()) {
+ contentFile = null;
+ }
+ }
+
+ // No file - ask for one (if choose is available)
+ if (contentFile == null && chooser != null) {
+ contentFile = chooser.get();
+ if (contentFile == null) {
+ return null;
+ }
+ }
+
+ return new CMSProcessableFile(contentFile);
+ }
+
/**
* PEM encode a CMS signature.
*
diff --git a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
index 6e07ceda1..6a92ed4ad 100644
--- a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
@@ -755,7 +755,7 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters
// now let TSA time-stamp the signature
if (tsaUrl != null && !tsaUrl.isEmpty()) {
- signedData = addTimestamp(tsaUrl, signedData);
+ signedData = CmsSigner.addTimestamp(tsaUrl, signedData);
}
return signedData.getEncoded();
@@ -764,32 +764,6 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters
}
}
- private static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData) throws IOException {
-
- Collection signerInfos = signedData.getSignerInfos().getSigners();
-
- // get signature of first signer (should be the only one)
- SignerInformation si = signerInfos.iterator().next();
- byte[] signature = si.getSignature();
-
- // send request to TSA
- byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, DigestType.SHA256);
-
- // create new SignerInformation with TS attribute
- Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
- new DERSet(ASN1Primitive.fromByteArray(token)));
- ASN1EncodableVector timestampVector = new ASN1EncodableVector();
- timestampVector.add(tokenAttr);
- AttributeTable at = new AttributeTable(timestampVector);
- si = SignerInformation.replaceUnsignedAttributes(si, at);
- signerInfos.clear();
- signerInfos.add(si);
- SignerInformationStore newSignerStore = new SignerInformationStore(signerInfos);
-
- // create new signed data
- return CMSSignedData.replaceSigners(signedData, newSignerStore);
- }
-
/*
* Convert the supplied signature name to make it valid for use with
* signing, ie any characters that are not 'a-z', 'A-Z', '0-9', '_' or
diff --git a/kse/src/main/java/org/kse/gui/KseFrame.java b/kse/src/main/java/org/kse/gui/KseFrame.java
index 5e7333ab9..8005f4599 100644
--- a/kse/src/main/java/org/kse/gui/KseFrame.java
+++ b/kse/src/main/java/org/kse/gui/KseFrame.java
@@ -105,6 +105,7 @@
import org.kse.gui.actions.CopyAction;
import org.kse.gui.actions.CopyKeyPairAction;
import org.kse.gui.actions.CopyTrustedCertificateAction;
+import org.kse.gui.actions.CounterSignAction;
import org.kse.gui.actions.CutAction;
import org.kse.gui.actions.CutKeyPairAction;
import org.kse.gui.actions.CutTrustedCertificateAction;
@@ -373,6 +374,7 @@ public final class KseFrame implements StatusBar {
private JMenuItem jmiKeyPairSignJwt;
private JMenuItem jmiKeyPairSignNewKeyPair;
private JMenuItem jmiKeyPairSignFile;
+ private JMenuItem jmiKeyPairSignSignature;
private JMenuItem jmiKeyPairUnlock;
private JMenuItem jmiKeyPairSetPassword;
private JMenuItem jmiKeyPairDelete;
@@ -510,6 +512,7 @@ public final class KseFrame implements StatusBar {
private final SignCrlAction signCrlAction = new SignCrlAction(this);
private final SignJwtAction signJwtAction = new SignJwtAction(this);
private final SignFileAction signFileAction = new SignFileAction(this);
+ private final CounterSignAction counterSignAction = new CounterSignAction(this);
private final SignNewKeyPairAction signNewKeyPairAction = new SignNewKeyPairAction(this);
private final UnlockKeyPairAction unlockKeyPairAction = new UnlockKeyPairAction(this);
private final SetKeyPairPasswordAction setKeyPairPasswordAction = new SetKeyPairPasswordAction(this);
@@ -2045,6 +2048,10 @@ private void initKeyStoreEntryPopupMenus() {
jmiKeyPairSignFile.setToolTipText(null);
new StatusBarChangeHandler(jmiKeyPairSignFile, (String) signFileAction.getValue(Action.LONG_DESCRIPTION), this);
+ jmiKeyPairSignSignature = new JMenuItem(counterSignAction);
+ jmiKeyPairSignSignature.setToolTipText(null);
+ new StatusBarChangeHandler(jmiKeyPairSignSignature, (String) counterSignAction.getValue(Action.LONG_DESCRIPTION), this);
+
jmiKeyPairSignNewKeyPair = new JMenuItem(signNewKeyPairAction);
jmiKeyPairSignNewKeyPair.setToolTipText(null);
new StatusBarChangeHandler(jmiKeyPairSignNewKeyPair,
@@ -2100,6 +2107,7 @@ private void initKeyStoreEntryPopupMenus() {
jmKeyPairSign.add(jmiKeyPairSignCrl);
jmKeyPairSign.add(jmiKeyPairSignJwt);
jmKeyPairSign.add(jmiKeyPairSignFile);
+ jmKeyPairSign.add(jmiKeyPairSignSignature);
jpmKeyPair.addSeparator();
jpmKeyPair.add(jmiKeyPairUnlock);
jpmKeyPair.add(jmiKeyPairSetPassword);
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
new file mode 100644
index 000000000..e0401d8c0
--- /dev/null
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2004 - 2013 Wayne Grant
+ * 2013 - 2024 Kai Kramer
+ *
+ * This file is part of KeyStore Explorer.
+ *
+ * KeyStore Explorer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeyStore Explorer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeyStore Explorer. If not, see .
+ */
+package org.kse.gui.actions;
+
+import java.awt.Toolkit;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.cert.X509Certificate;
+
+import javax.swing.ImageIcon;
+
+import org.bouncycastle.cms.CMSSignedData;
+import org.kse.crypto.keypair.KeyPairType;
+import org.kse.crypto.keypair.KeyPairUtil;
+import org.kse.crypto.signing.CmsSigner;
+import org.kse.crypto.signing.CmsUtil;
+import org.kse.crypto.signing.SignatureType;
+import org.kse.crypto.x509.X509CertUtil;
+import org.kse.gui.KseFrame;
+import org.kse.gui.dialogs.sign.DSignFile;
+import org.kse.gui.error.DError;
+import org.kse.gui.passwordmanager.Password;
+import org.kse.utilities.history.KeyStoreHistory;
+import org.kse.utilities.history.KeyStoreState;
+
+/**
+ * Action to counter sign a PKCS #7 signature using the selected key pair entry.
+ */
+public class CounterSignAction extends KeyStoreExplorerAction {
+ private static final long serialVersionUID = 6227240459189308322L;
+
+ /**
+ * Construct action.
+ *
+ * @param kseFrame KeyStore Explorer frame
+ */
+ public CounterSignAction(KseFrame kseFrame) {
+ super(kseFrame);
+
+ putValue(LONG_DESCRIPTION, res.getString("CounterSignAction.statusbar"));
+ putValue(NAME, res.getString("CounterSignAction.text"));
+ putValue(SHORT_DESCRIPTION, res.getString("CounterSignAction.tooltip"));
+ // TODO JW - Need icon for sign file.
+ putValue(SMALL_ICON,
+ new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signjar.png"))));
+ }
+
+ /**
+ * Do action.
+ */
+ @Override
+ protected void doAction() {
+ try {
+ KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
+ KeyStoreState currentState = history.getCurrentState();
+
+ String alias = kseFrame.getSelectedEntryAlias();
+
+ Password password = getEntryPassword(alias, currentState);
+
+ if (password == null) {
+ return;
+ }
+
+ // set the keystore state
+ KeyStore keyStore = currentState.getKeyStore();
+
+ // set the provider history
+ Provider provider = history.getExplicitProvider();
+
+ // set the private key
+ PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
+
+ // set the certificate
+ X509Certificate[] certs = X509CertUtil
+ .orderX509CertChain(X509CertUtil.convertCertificates(keyStore.getCertificateChain(alias)));
+
+ // set the key pair type
+ KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey);
+
+ // get the file, signatures, and time stamp
+ DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, true);
+ dSignFile.setLocationRelativeTo(frame);
+ dSignFile.setVisible(true);
+
+ // check if file sign dialog was successful
+ if (!dSignFile.isSuccessful()) {
+ return;
+ }
+
+ // TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
+ boolean detachedSignature = dSignFile.isDetachedSignature();
+ SignatureType signatureType = dSignFile.getSignatureType();
+ File inputFile = dSignFile.getInputFile();
+ File outputFile = dSignFile.getOutputFile();
+ String tsaUrl = dSignFile.getTimestampingServerUrl();
+
+ // TODO JW - Implement a chooser for finding the detached content.
+ CMSSignedData signature = CmsUtil.loadSignature(inputFile, null);
+ if (signature == null) {
+ // TODO JW - identify the error conditions.
+ return;
+ }
+
+ CMSSignedData signedData = CmsSigner.counterSign(signature, privateKey, certs, detachedSignature,
+ signatureType, tsaUrl, provider);
+
+ try (OutputStream os = new FileOutputStream(outputFile)) {
+ // TODO JW - What about generating a PEM encoded file?
+ os.write(signedData.getEncoded());
+ }
+
+ } catch (Exception ex) {
+ DError.displayError(frame, ex);
+ }
+ }
+}
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index f6262ad89..2f751b6e9 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -22,23 +22,23 @@
import java.awt.Toolkit;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.OutputStream;
+import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
-import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.ImageIcon;
-import javax.swing.JOptionPane;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaCertStore;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
@@ -47,29 +47,27 @@
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-import org.bouncycastle.tsp.TimeStampResponse;
-import org.bouncycastle.tsp.TimeStampToken;
-import org.kse.KSE;
-import org.kse.gui.passwordmanager.Password;
+import org.bouncycastle.util.Store;
import org.kse.crypto.CryptoException;
-import org.kse.crypto.digest.DigestType;
import org.kse.crypto.keypair.KeyPairType;
import org.kse.crypto.keypair.KeyPairUtil;
+import org.kse.crypto.signing.CmsSigner;
import org.kse.crypto.signing.JarSigner;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.KseFrame;
import org.kse.gui.dialogs.sign.DSignFile;
-import org.kse.gui.dialogs.sign.DSignJarSigning;
import org.kse.gui.error.DError;
-import org.kse.gui.error.DErrorCollection;
+import org.kse.gui.passwordmanager.Password;
import org.kse.utilities.history.KeyStoreHistory;
import org.kse.utilities.history.KeyStoreState;
/**
- * Action to sign a JAR using the selected key pair entry.
+ * Action to sign a file using the selected key pair entry.
*/
public class SignFileAction extends KeyStoreExplorerAction {
private static final long serialVersionUID = 6227240459189308322L;
@@ -123,12 +121,12 @@ protected void doAction() {
// set the key pair type
KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey);
- // get the jars, signatures, and time stamp
- DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, alias);
+ // get the file, signatures, and time stamp
+ DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, false);
dSignFile.setLocationRelativeTo(frame);
dSignFile.setVisible(true);
- // check if jar sign dialog was successful
+ // check if file sign dialog was successful
if (!dSignFile.isSuccessful()) {
return;
}
@@ -140,8 +138,13 @@ protected void doAction() {
File outputFile = dSignFile.getOutputFile();
String tsaUrl = dSignFile.getTimestampingServerUrl();
- // TODO JW - What if the dialog is cancelled?
- sign(inputFile, outputFile, privateKey, certs, detachedSignature, signatureType, tsaUrl, provider);
+ CMSSignedData signedData = CmsSigner.sign(inputFile, privateKey, certs, detachedSignature, signatureType,
+ tsaUrl, provider);
+
+ try (OutputStream os = new FileOutputStream(outputFile)) {
+ // TODO JW - What about generating a PEM encoded file?
+ os.write(signedData.getEncoded());
+ }
// start jar signing process
// DSignJarSigning dSignJarSigning = new DSignJarSigning(frame, inputJarFile, outputJarFile, privateKey, certs,
@@ -190,33 +193,4 @@ protected void doAction() {
DError.displayError(frame, ex);
}
}
-
- public static void sign(File inputFile, File outputFile, PrivateKey privateKey, X509Certificate[] certificateChain,
- boolean detachedSignature, SignatureType signatureType, String tsaUrl, Provider provider)
- throws CryptoException {
- try {
- CMSTypedData msg = new CMSProcessableFile(inputFile);
-
- CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
- ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey);
- generator.addSignerInfoGenerator(
- new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
- .build(contentSigner, certificateChain[0]));
- generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
-
- CMSSignedData signedData = generator.generate(msg, !detachedSignature);
-
- if (tsaUrl != null) {
- signedData = JarSigner.addTimestamp(tsaUrl, signedData);
- }
-
- try (OutputStream os = new FileOutputStream(outputFile)) {
- os.write(signedData.getEncoded());
- }
- }
- catch (Exception e) {
- // TODO JW - Create exception message
- throw new CryptoException("TODO");
- }
- }
}
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index bb9b9be9c..962861988 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -31,6 +31,7 @@
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.nio.file.Files;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
@@ -62,6 +63,7 @@
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
@@ -109,7 +111,6 @@
public class VerifySignatureAction extends AuthorityCertificatesAction {
private static final long serialVersionUID = 1L;
- private File signatureFile;
public VerifySignatureAction(KseFrame kseFrame) {
super(kseFrame);
@@ -147,38 +148,13 @@ protected void doAction() {
KeyStoreState currentState = history.getCurrentState();
KeyStore keyStore = currentState.getKeyStore();
- byte[] signature = showFileSelectionDialog();
- if (signature == null) {
+ File signatureFile = showFileSelectionDialog();
+ if (signatureFile == null) {
return;
}
- if (PemUtil.isPemFormat(signature)) {
- PemInfo signaturePem = PemUtil.decode(signature);
- if (signaturePem == null) {
- // TODO JW - What to throw if the signature is detected as PEM, but is not PEM?
- throw new CryptoException("Not a PEM file, but has PEM header!");
- }
- // TODO JW - Do we even want to check the type? Should we just let the BC CMS
- // class bomb out?
- if (!"CMS".equals(signaturePem.getType()) && !"PKCS7".equals(signaturePem.getType())) {
- // TODO JW - What to throw if the signature is not the correct type?
- throw new CryptoException("PEM is not of type CMS or PKCS7");
- }
- signature = signaturePem.getContent();
- }
- CMSSignedData signedData = new CMSSignedData(signature);
-
- if (signedData.isCertificateManagementMessage()) {
- // TODO JW - Display a message indicating that the file doesn't have any signatures.
- return;
- }
-
- if (signedData.isDetachedSignature()) {
- signedData = handleDetachedSignature(signature);
- if (signedData == null) {
- return;
- }
- }
+ // TODO JW - What about the logic in openSignature?
+ CMSSignedData signedData = CmsUtil.loadSignature(signatureFile, this::chooseContentFile);
// TODO JW - Add new option for using cacerts for signature verification.
if (preferences.getCaCertsSettings().isImportTrustedCertTrustCheckEnabled()) {
@@ -248,21 +224,22 @@ protected void doAction() {
Collection matchedCerts = certStore.getMatches(signer.getSID());
if (!matchedCerts.isEmpty()) {
X509CertificateHolder cert = matchedCerts.iterator().next();
+ // TODO JW- Counter signing breaks the signature...
// TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
- if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
- System.out.println("Verified by: " + cert.getSubject());
- verified = true;
-
- TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
- if (tspToken != null) {
- matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
- tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- System.out.println("Time stamped by: " + cert.getSubject());
- }
- }
- }
+// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+// System.out.println("Verified by: " + cert.getSubject());
+// verified = true;
+//
+// TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
+// if (tspToken != null) {
+// matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+// if (!matchedCerts.isEmpty()) {
+// cert = matchedCerts.iterator().next();
+// tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+// System.out.println("Time stamped by: " + cert.getSubject());
+// }
+// }
+// }
}
}
System.out.println("Verified: " + verified);
@@ -279,6 +256,7 @@ protected void doAction() {
Signer's serial: 30bc7fadef7fe924fa77a0f376f31d92b68fa33e
Signing time: Mon Feb 07 23:07:19 UTC 2022
Signature Algorithm: RSA-SHA256
+ Signature Algorithm: RSA-PSS-SHA256 certtool shows SHA-256, but KSE shows SHA-384, which is what was used. (CmsSigner.java.p7m)
Signed Attributes:
messageDigest: 0420ed6b93a0a57ff71b075d7628f807db2d6f9a8727557a02b2f6f9ff520eb4caaf
1.2.840.113549.1.9.52: 301c300b0609608648016503040201a10d06092a864886f70d01010b0500
@@ -297,32 +275,6 @@ protected void doAction() {
}
}
- private CMSSignedData handleDetachedSignature(byte[] signature) throws CMSException {
- // Look for the content file. if not present, prompt for it.
- File contentFile = null;
-
- // First try file name with signature extension stripped.
- int extensionIndex = signatureFile.getAbsolutePath().lastIndexOf('.');
- if (extensionIndex > 0) {
- // Turn file_name.txt.p7s into file_name.txt
- String contentFileName = signatureFile.getAbsolutePath().substring(0, extensionIndex);
- contentFile = new File(contentFileName);
- if (!contentFile.exists()) {
- contentFile = null;
- }
- }
-
- // No file - ask for one
- if (contentFile == null) {
- contentFile = chooseContentFile();
- if (contentFile == null) {
- return null;
- }
- }
-
- return new CMSSignedData(new CMSProcessableFile(contentFile), signature);
- }
-
private boolean isCA(X509Certificate cert) {
int basicConstraints = cert.getBasicConstraints();
if (basicConstraints != -1) {
@@ -403,7 +355,7 @@ private X509Certificate[] getCertificateChain(String alias) throws CryptoExcepti
*/
protected byte[] openSignature(File signatureFile) {
try {
- return FileUtils.readFileToByteArray(signatureFile);
+ return Files.readAllBytes(signatureFile.toPath());
} catch (IOException ex) {
JOptionPane.showMessageDialog(frame, MessageFormat.format(
res.getString("KeyStoreExplorerAction.NoReadFile.message"),
@@ -414,19 +366,13 @@ protected byte[] openSignature(File signatureFile) {
}
}
- private byte[] showFileSelectionDialog() {
- signatureFile = chooseSignatureFile();
+ private File showFileSelectionDialog() {
+ File signatureFile = chooseSignatureFile();
if (signatureFile == null) {
return null;
}
- byte[] sig = openSignature(signatureFile);
-
- if (sig == null) {
- return null;
- }
-
- return sig;
+ return signatureFile;
}
private File chooseSignatureFile() {
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index b72a49866..e6ae3c0bf 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -255,6 +255,8 @@ private void initComponents(Collection signers) throws Crypto
// jbExtensions.setToolTipText(res.getString("DViewSignature.jbExtensions.tooltip"));
// PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewSignature.jbExtensions.mnemonic").charAt(0));
+ // TODO JW - Display PEM and ASN1 buttons for counter signatures? What about for time stamps?
+ // TODO JW - Need to make certain these buttons work as expected.
jbPem = new JButton(res.getString("DViewSignature.jbPem.text"));
jbPem.setToolTipText(res.getString("DViewSignature.jbPem.tooltip"));
PlatformUtil.setMnemonic(jbPem, res.getString("DViewSignature.jbPem.mnemonic").charAt(0));
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index f2a07425f..a59241f97 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -120,22 +120,27 @@ public class DSignFile extends JEscDialog {
* @param parent The parent frame
* @param signPrivateKey Signing key pair's private key
* @param signKeyPairType Signing key pair's type
- * @param signatureName Default signature name
+ * @param isCounterSign True if the dialog should be counter signing a signature. False for signing a file.
*/
- public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPairType, String signatureName) {
+ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPairType, boolean isCounterSign) {
super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
this.signPrivateKey = signPrivateKey;
this.signKeyPairType = signKeyPairType;
- setTitle(res.getString("DSignFile.Title"));
- initComponents(signatureName);
+ // TODO JW - for counter signing only choose a signature file.
+ String title = "DSignFile.Sign.Title";;
+ if (isCounterSign) {
+ title = "DSignFile.CounterSign.Title";
+ }
+ setTitle(res.getString(title));
+ initComponents(isCounterSign);
}
/**
* Initializes the dialogue panel and associated elements
*
- * @param signatureName String
+ * @param isCounterSign True for counter signing. False for signing.
*/
- private void initComponents(String signatureName) {
+ private void initComponents(boolean isCounterSign) {
jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
jcbDetachedSignature = new JCheckBox();
jcbDetachedSignature.setSelected(true);
@@ -200,7 +205,7 @@ private void initComponents(String signatureName) {
jbInputFileBrowse.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DSignFile.this);
- inputFileBrowsePressed();
+ inputFileBrowsePressed(isCounterSign);
} finally {
CursorUtil.setCursorFree(DSignFile.this);
}
@@ -406,10 +411,17 @@ private void okPressed() {
/**
* Get input file
*/
- private void inputFileBrowsePressed() {
- JFileChooser chooser = FileChooserFactory.getAllFileChooser();
+ private void inputFileBrowsePressed(boolean isCounterSign) {
+ JFileChooser chooser;
+
+ if (!isCounterSign) {
+ chooser = FileChooserFactory.getAllFileChooser();
+ chooser.setDialogTitle(res.getString("DSignFile.ChooseInputFile.Sign.Title"));
+ } else {
+ chooser = FileChooserFactory.getSignatureFileChooser();
+ chooser.setDialogTitle(res.getString("DSignFile.ChooseInputFile.CounterSign.Title"));
+ }
chooser.setCurrentDirectory(CurrentDirectory.get());
- chooser.setDialogTitle(res.getString("DSignFile.ChooseInputFile.Title"));
chooser.setMultiSelectionEnabled(false);
chooser.setApproveButtonText(res.getString("DSignFile.InputFileChooser.button"));
@@ -456,7 +468,7 @@ public static void main(String[] args) throws Exception {
kpg.initialize(1024, new SecureRandom());
KeyPair kp = kpg.generateKeyPair();
- DSignFile dialog = new DSignFile(new JFrame(), kp.getPrivate(), KeyPairType.RSA, "signature name");
+ DSignFile dialog = new DSignFile(new JFrame(), kp.getPrivate(), KeyPairType.RSA, false);
DialogViewer.run(dialog);
}
}
\ No newline at end of file
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index eafce52d0..93eecb636 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -602,6 +602,13 @@ SignFileAction.statusbar = Sign a file using the Key Pair entry
SignFileAction.text = Sign File
SignFileAction.tooltip = Sign a file
+CounterSignAction.ButtonOK.message = OK
+#CounterSignAction.NoWriteFile.message = Could not write to file ''{0}''.
+CounterSignAction.SignJar.Title = Counter Sign Signature
+CounterSignAction.statusbar = Counter sign a signature using the Key Pair entry
+CounterSignAction.text = Counter Sign Signature
+CounterSignAction.tooltip = Counter sign a signature
+
SignMidletAction.ReqRsaKeyPairMidletSigning.message = Only RSA key pairs can be used for MIDlet signing.
SignMidletAction.SignMidlet.Title = Sign MIDlet
SignMidletAction.SignMidletSuccessful.message = MIDlet Signing Successful.
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 83e386ec2..35c0d79a0 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -235,28 +235,30 @@ DSignJwt.jlSignatureAlgorithm.text = Signature Algorithm:
DSignJwt.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign the JWT
# TODO JW - Are all these strings in use?
-DSignFile.ChooseInputFile.Title = Choose Input File
-DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
-DSignFile.InputFileChooser.button = Choose
-DSignFile.InputFileRequired.message = Path to Input File required.
-DSignFile.NoOpenFile.Problem = Could not open Input File ''{0}''.
-DSignFile.ProblemOpeningFile.Title = Problem Opening Input File
-DSignFile.Title = Sign File
-DSignFile.jbCancel.text = Cancel
-DSignFile.jbInputFileBrowse.mnemonic = B
-DSignFile.jbInputFileBrowse.text = Browse
-DSignFile.jbInputFileBrowse.tooltip = Browse to Input File
-DSignFile.jbOK.text = OK
-DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
-DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
-DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
-DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
-DSignFile.jlAddTimestamp.text = Add Time stamp:
-DSignFile.jlDetachedSignature.text = Generate Detached Signature:
-DSignFile.jlInputFile.text = Input File:
-DSignFile.jlSignatureAlgorithm.text = Signature Algorithm:
-DSignFile.jlTimestampServerUrl.text = TSA URL:
-DSignFile.jtfInputFile.tooltip = File to sign
+DSignFile.ChooseInputFile.CounterSign.Title = Choose Signature File
+DSignFile.ChooseInputFile.Sign.Title = Choose Input File
+DSignFile.CounterSign.Title = Counter Sign Signature
+DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
+DSignFile.InputFileChooser.button = Choose
+DSignFile.InputFileRequired.message = Path to Input File required.
+DSignFile.NoOpenFile.Problem = Could not open Input File ''{0}''.
+DSignFile.ProblemOpeningFile.Title = Problem Opening Input File
+DSignFile.Sign.Title = Sign File
+DSignFile.jbCancel.text = Cancel
+DSignFile.jbInputFileBrowse.mnemonic = B
+DSignFile.jbInputFileBrowse.text = Browse
+DSignFile.jbInputFileBrowse.tooltip = Browse to Input File
+DSignFile.jbOK.text = OK
+DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
+DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
+DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
+DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
+DSignFile.jlAddTimestamp.text = Add Time stamp:
+DSignFile.jlDetachedSignature.text = Generate Detached Signature:
+DSignFile.jlInputFile.text = Input File:
+DSignFile.jlSignatureAlgorithm.text = Signature Algorithm:
+DSignFile.jlTimestampServerUrl.text = TSA URL:
+DSignFile.jtfInputFile.tooltip = File to sign
ExamineFileAction.ExamineFile.Title = Open File
ExamineFileAction.ExamineFile.button = Open
From 9413e1b76b0cceebf6738d207ac44de0d917c7c5 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 06:48:42 -0800
Subject: [PATCH 25/69] Added file chooser for detached content for counter
signing.
---
.../kse/gui/actions/CounterSignAction.java | 22 +++++++++++++++++--
.../org/kse/gui/actions/resources.properties | 2 ++
2 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index e0401d8c0..c482fa988 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -29,6 +29,7 @@
import java.security.cert.X509Certificate;
import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
import org.bouncycastle.cms.CMSSignedData;
import org.kse.crypto.keypair.KeyPairType;
@@ -37,6 +38,8 @@
import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X509CertUtil;
+import org.kse.gui.CurrentDirectory;
+import org.kse.gui.FileChooserFactory;
import org.kse.gui.KseFrame;
import org.kse.gui.dialogs.sign.DSignFile;
import org.kse.gui.error.DError;
@@ -116,8 +119,7 @@ protected void doAction() {
File outputFile = dSignFile.getOutputFile();
String tsaUrl = dSignFile.getTimestampingServerUrl();
- // TODO JW - Implement a chooser for finding the detached content.
- CMSSignedData signature = CmsUtil.loadSignature(inputFile, null);
+ CMSSignedData signature = CmsUtil.loadSignature(inputFile, this::chooseContentFile);
if (signature == null) {
// TODO JW - identify the error conditions.
return;
@@ -135,4 +137,20 @@ protected void doAction() {
DError.displayError(frame, ex);
}
}
+
+ private File chooseContentFile() {
+ JFileChooser chooser = FileChooserFactory.getNoFileChooser();
+ chooser.setCurrentDirectory(CurrentDirectory.get());
+ chooser.setDialogTitle(res.getString("CounterSignAction.ChooseContent.Title"));
+ chooser.setMultiSelectionEnabled(false);
+ chooser.setApproveButtonText(res.getString("CounterSignAction.ChooseContent.button"));
+
+ int rtnValue = chooser.showOpenDialog(frame);
+ if (rtnValue == JFileChooser.APPROVE_OPTION) {
+ File importFile = chooser.getSelectedFile();
+ CurrentDirectory.updateForFile(importFile);
+ return importFile;
+ }
+ return null;
+ }
}
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index 93eecb636..551c57cca 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -603,6 +603,8 @@ SignFileAction.text = Sign File
SignFileAction.tooltip = Sign a file
CounterSignAction.ButtonOK.message = OK
+CounterSignAction.ChooseContent.Title = Choose File
+CounterSignAction.ChooseContent.button = Select
#CounterSignAction.NoWriteFile.message = Could not write to file ''{0}''.
CounterSignAction.SignJar.Title = Counter Sign Signature
CounterSignAction.statusbar = Counter sign a signature using the Key Pair entry
From 3322d04b9756c255ecd1a549baa079b4ff975d03 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 07:38:30 -0800
Subject: [PATCH 26/69] Clean up. Added icon files for signing. Enabled PEM
output.
---
.../kse/gui/actions/CounterSignAction.java | 13 +-
.../org/kse/gui/actions/SignFileAction.java | 80 +------
.../org/kse/gui/dialogs/sign/DSignFile.java | 204 ++++++++----------
.../org/kse/gui/actions/images/signfile.png | Bin 0 -> 805 bytes
.../kse/gui/actions/images/signsignature.png | Bin 0 -> 805 bytes
.../kse/gui/dialogs/sign/resources.properties | 12 ++
6 files changed, 124 insertions(+), 185 deletions(-)
create mode 100644 kse/src/main/resources/org/kse/gui/actions/images/signfile.png
create mode 100644 kse/src/main/resources/org/kse/gui/actions/images/signsignature.png
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index c482fa988..b7e9623f4 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -64,9 +64,8 @@ public CounterSignAction(KseFrame kseFrame) {
putValue(LONG_DESCRIPTION, res.getString("CounterSignAction.statusbar"));
putValue(NAME, res.getString("CounterSignAction.text"));
putValue(SHORT_DESCRIPTION, res.getString("CounterSignAction.tooltip"));
- // TODO JW - Need icon for sign file.
putValue(SMALL_ICON,
- new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signjar.png"))));
+ new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signsignature.png"))));
}
/**
@@ -114,6 +113,7 @@ protected void doAction() {
// TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
+ boolean outputPem = dSignFile.isOutputPem();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
File outputFile = dSignFile.getOutputFile();
@@ -129,8 +129,13 @@ protected void doAction() {
signatureType, tsaUrl, provider);
try (OutputStream os = new FileOutputStream(outputFile)) {
- // TODO JW - What about generating a PEM encoded file?
- os.write(signedData.getEncoded());
+ if (!outputPem) {
+ os.write(signedData.getEncoded());
+ } else {
+ // shouldn't need character encoding since PEM is plain ASCII.
+ // TODO JW - see out how other actions handle PEM output
+ os.write(CmsUtil.getPem(signedData).getBytes());
+ }
}
} catch (Exception ex) {
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 2f751b6e9..3dd89f5a5 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -22,41 +22,19 @@
import java.awt.Toolkit;
import java.io.File;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.OutputStream;
-import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import javax.swing.ImageIcon;
-import org.bouncycastle.cert.jcajce.JcaCertStore;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.CMSTypedData;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.DigestCalculatorProvider;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-import org.bouncycastle.util.Store;
-import org.kse.crypto.CryptoException;
import org.kse.crypto.keypair.KeyPairType;
import org.kse.crypto.keypair.KeyPairUtil;
import org.kse.crypto.signing.CmsSigner;
-import org.kse.crypto.signing.JarSigner;
+import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.KseFrame;
@@ -83,9 +61,8 @@ public SignFileAction(KseFrame kseFrame) {
putValue(LONG_DESCRIPTION, res.getString("SignFileAction.statusbar"));
putValue(NAME, res.getString("SignFileAction.text"));
putValue(SHORT_DESCRIPTION, res.getString("SignFileAction.tooltip"));
- // TODO JW - Need icon for sign file.
putValue(SMALL_ICON,
- new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signjar.png"))));
+ new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signfile.png"))));
}
/**
@@ -133,6 +110,7 @@ protected void doAction() {
// TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
+ boolean outputPem = dSignFile.isOutputPem();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
File outputFile = dSignFile.getOutputFile();
@@ -142,53 +120,15 @@ protected void doAction() {
tsaUrl, provider);
try (OutputStream os = new FileOutputStream(outputFile)) {
- // TODO JW - What about generating a PEM encoded file?
- os.write(signedData.getEncoded());
+ if (!outputPem) {
+ os.write(signedData.getEncoded());
+ } else {
+ // shouldn't need character encoding since PEM is plain ASCII.
+ // TODO JW - see out how other actions handle PEM output
+ os.write(CmsUtil.getPem(signedData).getBytes());
+ }
}
- // start jar signing process
-// DSignJarSigning dSignJarSigning = new DSignJarSigning(frame, inputJarFile, outputJarFile, privateKey, certs,
-// signatureType, signatureName, signer, digestType,
-// tsaUrl, provider);
-// dSignJarSigning.setLocationRelativeTo(frame);
-// dSignJarSigning.startDSignJarSigning();
-// dSignJarSigning.setVisible(true);
-//
-// // check if jar signing was successful
-// if (!dSignJarSigning.isSuccessful()) {
-// return;
-// }
-//
-// // check if exceptions were caught during jar signing
-// if (!dSignJarSigning.getFileExceptions().isEmpty()) {
-// Integer fileCount = inputJarFile.length;
-// Integer errorCount = dSignJarSigning.getFileExceptions().size();
-// String message = MessageFormat.format(res.getString("SignJarAction.SignJarError.message"),
-// errorCount,
-// fileCount);
-//
-// String viewButtonText = res.getString("SignJarAction.ButtonView.message");
-// String okButtonText = res.getString("SignJarAction.ButtonOK.message");
-// Object[] buttonTexts = { viewButtonText, okButtonText };
-//
-// int selected = JOptionPane.showOptionDialog(frame, message,
-// res.getString("SignJarAction.SignJar.Title"),
-// JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
-// null, buttonTexts, okButtonText);
-//
-// // if view button pressed show error collection
-// if (selected == 0) {
-// DErrorCollection dError = new DErrorCollection(frame, dSignJarSigning.getFileExceptions());
-// dError.setVisible(true);
-// }
-// } else {
-// Integer fileCount = inputJarFile.length;
-// String message = MessageFormat.format(res.getString("SignJarAction.SignJarSuccessful.message"),
-// fileCount);
-// JOptionPane.showMessageDialog(frame, message, res.getString("SignJarAction.SignJar.Title"),
-// JOptionPane.INFORMATION_MESSAGE);
-// }
-
} catch (Exception ex) {
DError.displayError(frame, ex);
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index a59241f97..23a9ed806 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -26,20 +26,13 @@
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
-import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.ResourceBundle;
-import java.util.jar.JarFile;
import javax.swing.AbstractAction;
-import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
@@ -50,29 +43,20 @@
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
-import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
-import org.apache.commons.io.IOUtils;
import org.kse.KSE;
-import org.kse.crypto.digest.DigestType;
import org.kse.crypto.keypair.KeyPairType;
-import org.kse.crypto.signing.JarSigner;
import org.kse.crypto.signing.SignatureType;
import org.kse.gui.CurrentDirectory;
import org.kse.gui.CursorUtil;
import org.kse.gui.FileChooserFactory;
-import org.kse.gui.components.JEscDialog;
-import org.kse.gui.MiGUtil;
import org.kse.gui.PlatformUtil;
+import org.kse.gui.components.JEscDialog;
import org.kse.gui.dialogs.DialogHelper;
-import org.kse.gui.error.DError;
-import org.kse.gui.error.DProblem;
-import org.kse.gui.error.Problem;
import org.kse.utilities.DialogViewer;
-import org.kse.utilities.io.FileNameUtil;
import org.kse.utilities.net.URLs;
import net.miginfocom.swing.MigLayout;
@@ -81,8 +65,7 @@
* Dialog that displays the presents JAR signing options.
*/
public class DSignFile extends JEscDialog {
- // TODO JW - update this after adjusting the fields
- private static final long serialVersionUID = -5095469699284737624L;
+ private static final long serialVersionUID = 9162242907697268949L;
private static ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/dialogs/sign/resources");
@@ -92,8 +75,14 @@ public class DSignFile extends JEscDialog {
private JPanel jpInputFile;
private JTextField jtfInputFile;
private JButton jbInputFileBrowse;
+ private JLabel jlOutputFile;
+ private JPanel jpOutputFile;
+ private JTextField jtfOutputFile;
+ private JButton jbOutputFileBrowse;
private JLabel jlDetachedSignature;
private JCheckBox jcbDetachedSignature;
+ private JLabel jlOutputPem;
+ private JCheckBox jcbOutputPem;
private JLabel jlSignatureAlgorithm;
private JComboBox jcbSignatureAlgorithm;
private JLabel jlAddTimestamp;
@@ -107,9 +96,9 @@ public class DSignFile extends JEscDialog {
private PrivateKey signPrivateKey;
private KeyPairType signKeyPairType;
private File inputFile;
- // TODO JW - get rid of outputFile. It will be set automatically.
private File outputFile;
- private boolean detachedSignature;
+ private boolean detachedSignature = true;
+ private boolean outputPem;
private SignatureType signatureType;
private String tsaUrl;
private boolean successStatus = true;
@@ -126,7 +115,6 @@ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPa
super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
this.signPrivateKey = signPrivateKey;
this.signKeyPairType = signKeyPairType;
- // TODO JW - for counter signing only choose a signature file.
String title = "DSignFile.Sign.Title";;
if (isCounterSign) {
title = "DSignFile.CounterSign.Title";
@@ -141,11 +129,6 @@ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPa
* @param isCounterSign True for counter signing. False for signing.
*/
private void initComponents(boolean isCounterSign) {
- jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
- jcbDetachedSignature = new JCheckBox();
- jcbDetachedSignature.setSelected(true);
- jcbDetachedSignature.setToolTipText(res.getString("DSignFile.jcbDetachedSignature.tooltip"));
-
jlInputFile = new JLabel(res.getString("DSignFile.jlInputFile.text"));
jtfInputFile = new JTextField(30);
jtfInputFile.setCaretPosition(0);
@@ -160,6 +143,30 @@ private void initComponents(boolean isCounterSign) {
jpInputFile.add(jtfInputFile);
jpInputFile.add(jbInputFileBrowse);
+ jlOutputFile = new JLabel(res.getString("DSignFile.jlOutputFile.text"));
+ jtfOutputFile = new JTextField(30);
+ jtfOutputFile.setCaretPosition(0);
+ jtfOutputFile.setToolTipText(res.getString("DSignFile.jtfOutputFile.tooltip"));
+
+ jbOutputFileBrowse = new JButton(res.getString("DSignFile.jbOutputFileBrowse.text"));
+ PlatformUtil.setMnemonic(jbOutputFileBrowse, res.getString("DSignFile.jbOutputFileBrowse.mnemonic").charAt(0));
+ jbOutputFileBrowse.setToolTipText(res.getString("DSignFile.jbOutputFileBrowse.tooltip"));
+
+ // TODO JW - Remove default panel margin/insets.
+ jpOutputFile = new JPanel();
+ jpOutputFile.add(jtfOutputFile);
+ jpOutputFile.add(jbOutputFileBrowse);
+
+ jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
+ jcbDetachedSignature = new JCheckBox();
+ jcbDetachedSignature.setSelected(true);
+ jcbDetachedSignature.setToolTipText(res.getString("DSignFile.jcbDetachedSignature.tooltip"));
+
+ jlOutputPem = new JLabel(res.getString("DSignFile.jlOutputPem.text"));
+ jcbOutputPem = new JCheckBox();
+ jcbOutputPem.setSelected(false);
+ jcbOutputPem.setToolTipText(res.getString("DSignFile.jcbOutputPem.tooltip"));
+
jlSignatureAlgorithm = new JLabel(res.getString("DSignFile.jlSignatureAlgorithm.text"));
jcbSignatureAlgorithm = new JComboBox<>();
DialogHelper.populateSigAlgs(signKeyPairType, this.signPrivateKey, jcbSignatureAlgorithm);
@@ -190,8 +197,12 @@ private void initComponents(boolean isCounterSign) {
pane.setLayout(new MigLayout("insets dialog, fill", "[para]unrel[right]unrel[]", "[]unrel[]"));
pane.add(jlInputFile, "skip");
pane.add(jpInputFile, "wrap");
+ pane.add(jlOutputFile, "skip");
+ pane.add(jpOutputFile, "wrap");
pane.add(jlDetachedSignature, "skip");
pane.add(jcbDetachedSignature, "wrap");
+ pane.add(jlOutputPem, "skip");
+ pane.add(jcbOutputPem, "wrap");
pane.add(jlSignatureAlgorithm, "skip");
pane.add(jcbSignatureAlgorithm, "sgx, wrap");
pane.add(jlAddTimestamp, "skip");
@@ -211,8 +222,17 @@ private void initComponents(boolean isCounterSign) {
}
});
- jcbAddTimestamp.addItemListener(evt -> enableDisableElements());
- jcbDetachedSignature.addItemListener(evt -> enableDisableElements());
+ jbOutputFileBrowse.addActionListener(evt -> {
+ try {
+ CursorUtil.setCursorBusy(DSignFile.this);
+ outputFileBrowsePressed();
+ } finally {
+ CursorUtil.setCursorFree(DSignFile.this);
+ }
+ });
+
+ jcbAddTimestamp.addItemListener(evt -> enableDisableTsaElements());
+ jcbDetachedSignature.addItemListener(evt -> detachedSignatureStateChange());
jbOK.addActionListener(evt -> okPressed());
@@ -245,10 +265,15 @@ public void windowClosing(WindowEvent evt) {
/**
* This function enables and disables elements in the dialog
*/
- protected void enableDisableElements() {
+ protected void enableDisableTsaElements() {
jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
}
+ protected void detachedSignatureStateChange() {
+ detachedSignature = jcbDetachedSignature.isSelected();
+ // TODO JW - implement output file name update -- even if user chooses a file?
+ }
+
/**
* Get chosen input file.
*
@@ -276,6 +301,15 @@ public boolean isDetachedSignature() {
return detachedSignature;
}
+ /**
+ * Gets the chosen output PEM setting
+ *
+ * @return booleans output PEM setting
+ */
+ public boolean isOutputPem() {
+ return outputPem;
+ }
+
/**
* Get chosen signature type.
*
@@ -318,96 +352,16 @@ private void okPressed() {
signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
detachedSignature = jcbDetachedSignature.isSelected();
+ outputPem = jcbOutputPem.isSelected();
// check add time stamp is selected and assign value
if (jcbAddTimestamp.isSelected()) {
tsaUrl = jcbTimestampServerUrl.getSelectedItem().toString();
}
- outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
-
closeDialog();
}
-// /**
-// * Set output JAR files and check files for overwrite
-// *
-// * @return Boolean true if successful false if no option chosen
-// */
-// private boolean setOutputJarFiles(File[] files) {
-// String outputJarPrefix = jtfPrefix.getText().trim();
-// String outputJarSuffix = jtfSuffix.getText().trim();
-// final String FILE_SUFFIX = ".jar";
-// JCheckBox checkbox = new JCheckBox(res.getString("DSignFile.OverwriteSkip.message"));
-//
-// // set input files array to output files list
-// this.outputJarFiles = new ArrayList<>(Arrays.asList(files));
-//
-// if (jrbOutputJarFixes.isSelected()) {
-// // loop through output JAR files
-// for (int i = 0; i < outputJarFiles.size(); i++) {
-// // set prefix and suffix to the file name
-// String fileBaseName = FileNameUtil.removeExtension(outputJarFiles.get(i).getName());
-// String outFileName =
-// outputJarFiles.get(i).getParent() + "\\" + outputJarPrefix + fileBaseName + outputJarSuffix +
-// FILE_SUFFIX;
-// // replace file object in arraylist
-// this.outputJarFiles.set(i, new File(outFileName));
-//
-// if (!checkbox.isSelected()) {
-// // check if file exists
-// if (outputJarFiles.get(i).isFile()) {
-// String message = MessageFormat.format(res.getString("DSignFile.OverWriteOutputJarFile.message"),
-// outputJarFiles.get(i));
-// Object[] params = { message, checkbox };
-//
-// // check if overwrite is allowed and present checkbox to skip overwrite message
-// int selected = JOptionPane.showConfirmDialog(this, params, getTitle(),
-// JOptionPane.YES_NO_OPTION);
-// if (selected != JOptionPane.YES_OPTION) {
-// this.outputJarFiles.clear();
-// return false;
-// }
-// }
-// }
-// }
-// }
-// return true;
-// }
-
-// /**
-// * Checks to overwrite an existing signature
-// *
-// * @return Boolean continues jar signing if true cancels process if false
-// */
-// private boolean checkSignature(File[] files) {
-// JCheckBox checkbox = new JCheckBox(res.getString("DSignFile.OverwriteSkip.message"));
-//
-// for (File file : files) {
-// try {
-// // check if the existing signature matches the current signature
-// if (JarSigner.hasSignature(file, this.signatureName)) {
-// String message = MessageFormat.format(res.getString("DSignFile.SignatureOverwrite.message"),
-// this.signatureName, file.getName());
-// Object[] params = {message, checkbox};
-// // check if overwrite is allowed and present checkbox to skip overwrite message
-// int selected = JOptionPane.showConfirmDialog(this, params, getTitle(), JOptionPane.YES_NO_OPTION);
-// if (selected != JOptionPane.YES_OPTION) {
-// return false;
-// }
-// }
-// } catch (IOException ex) {
-// DError.displayError(this, ex);
-// return false;
-// }
-// // check to skip overwrite alert message
-// if (checkbox.isSelected()) {
-// return true;
-// }
-// }
-// return true;
-// }
-
/**
* Get input file
*/
@@ -430,9 +384,37 @@ private void inputFileBrowsePressed(boolean isCounterSign) {
File chosenFile = chooser.getSelectedFile();
CurrentDirectory.updateForFile(chosenFile);
inputFile = chosenFile;
+ outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
+
// TODO JW - Is there a better location for this code?
jtfInputFile.setText(inputFile.getAbsolutePath());
jtfInputFile.setCaretPosition(0);
+
+ jtfOutputFile.setText(outputFile.getAbsolutePath());
+ jtfOutputFile.setCaretPosition(0);
+ }
+ }
+
+ /**
+ * Get input file
+ */
+ private void outputFileBrowsePressed() {
+ JFileChooser chooser = FileChooserFactory.getSignatureFileChooser();
+ chooser.setDialogTitle(res.getString("DSignFile.ChooseOutputFile.Title"));
+ chooser.setCurrentDirectory(CurrentDirectory.get());
+ chooser.setMultiSelectionEnabled(false);
+ chooser.setApproveButtonText(res.getString("DSignFile.OutputFileChooser.button"));
+
+ int rtnValue = chooser.showOpenDialog(this);
+ if (rtnValue == JFileChooser.APPROVE_OPTION) {
+ File chosenFile = chooser.getSelectedFile();
+ CurrentDirectory.updateForFile(chosenFile);
+ // TODO JW - Need to add extension to chosen file?
+ outputFile = chosenFile;
+
+ // TODO JW - Is there a better location for this code?
+ jtfOutputFile.setText(outputFile.getAbsolutePath());
+ jtfOutputFile.setCaretPosition(0);
}
}
diff --git a/kse/src/main/resources/org/kse/gui/actions/images/signfile.png b/kse/src/main/resources/org/kse/gui/actions/images/signfile.png
new file mode 100644
index 0000000000000000000000000000000000000000..443cac9d311bb1aea369b104e6fc14f1a433fa65
GIT binary patch
literal 805
zcmV+=1KRwFP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=h{=K~y+Tby8hO
zlTjFczT+m%5+TUQ$XuhsVc~Mfx=?D7GQ#LWh`_sUA_%(65*!IhK~Pc9F8V`|ZVVZ;
zY6%5_3Klx&bj~u|k2({k;&hwazSDb7-=c^fc+NTBci!hY@B5ykw03qvKv9$yn)b+#
z|Ko$Is^8Rn9-MReLswYPT+!ony0&ODE2yf-(_UfU=kuXwVBiuh72;RBOKxmSDYe7owT&kl&ABB!XZ#j95JWyRj?F%gAhQ9yU6i=azw+Iw95|d+_k0cq9rMEf$%{
zpIM|6Lzo;(!R2;4noXvA7dU^%3inQw8FY4Z$T|lpgQ;i~qcbxylR%&kk%{{_Yx@jK
zLxcBAI{m`c*7pAI5=eNfyIVG~v9=aRtEy06U5(0;2$ZFlsJ9-2=j|}cD_Gdr)Z`aB
zwnp963`hoCenQtp^Cx;F0Xlh{PM#XM4Xf1x&zm9C9B;>3apiZR5n73THX9B(93Avf
zB&c>ycgzz%f_(6`gQpg*^H)z#@L<5hH?vtTVuhvv1F?L!&DN#qC8%k9*yH3p8RGrV
zPIAA`%hS0W&ycx@6&j%x+3qA(7>vdyW-#ocwS=0mNK!J7eM4ETnxpNx4`$h00000NkvXXu0mjf=^}BS
literal 0
HcmV?d00001
diff --git a/kse/src/main/resources/org/kse/gui/actions/images/signsignature.png b/kse/src/main/resources/org/kse/gui/actions/images/signsignature.png
new file mode 100644
index 0000000000000000000000000000000000000000..443cac9d311bb1aea369b104e6fc14f1a433fa65
GIT binary patch
literal 805
zcmV+=1KRwFP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0=h{=K~y+Tby8hO
zlTjFczT+m%5+TUQ$XuhsVc~Mfx=?D7GQ#LWh`_sUA_%(65*!IhK~Pc9F8V`|ZVVZ;
zY6%5_3Klx&bj~u|k2({k;&hwazSDb7-=c^fc+NTBci!hY@B5ykw03qvKv9$yn)b+#
z|Ko$Is^8Rn9-MReLswYPT+!ony0&ODE2yf-(_UfU=kuXwVBiuh72;RBOKxmSDYe7owT&kl&ABB!XZ#j95JWyRj?F%gAhQ9yU6i=azw+Iw95|d+_k0cq9rMEf$%{
zpIM|6Lzo;(!R2;4noXvA7dU^%3inQw8FY4Z$T|lpgQ;i~qcbxylR%&kk%{{_Yx@jK
zLxcBAI{m`c*7pAI5=eNfyIVG~v9=aRtEy06U5(0;2$ZFlsJ9-2=j|}cD_Gdr)Z`aB
zwnp963`hoCenQtp^Cx;F0Xlh{PM#XM4Xf1x&zm9C9B;>3apiZR5n73THX9B(93Avf
zB&c>ycgzz%f_(6`gQpg*^H)z#@L<5hH?vtTVuhvv1F?L!&DN#qC8%k9*yH3p8RGrV
zPIAA`%hS0W&ycx@6&j%x+3qA(7>vdyW-#ocwS=0mNK!J7eM4ETnxpNx4`$h00000NkvXXu0mjf=^}BS
literal 0
HcmV?d00001
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 35c0d79a0..4828c0300 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -237,11 +237,16 @@ DSignJwt.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign the JW
# TODO JW - Are all these strings in use?
DSignFile.ChooseInputFile.CounterSign.Title = Choose Signature File
DSignFile.ChooseInputFile.Sign.Title = Choose Input File
+DSignFile.ChooseOutputFile.Title = Choose Output File
DSignFile.CounterSign.Title = Counter Sign Signature
DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
DSignFile.InputFileChooser.button = Choose
+# TODO JW - is this used?
DSignFile.InputFileRequired.message = Path to Input File required.
DSignFile.NoOpenFile.Problem = Could not open Input File ''{0}''.
+DSignFile.OutputFileChooser.button = Choose
+# TODO JW - is this used?
+DSignFile.OutputFileRequired.message = Path to Output File required.
DSignFile.ProblemOpeningFile.Title = Problem Opening Input File
DSignFile.Sign.Title = Sign File
DSignFile.jbCancel.text = Cancel
@@ -249,16 +254,23 @@ DSignFile.jbInputFileBrowse.mnemonic = B
DSignFile.jbInputFileBrowse.text = Browse
DSignFile.jbInputFileBrowse.tooltip = Browse to Input File
DSignFile.jbOK.text = OK
+DSignFile.jbOutputFileBrowse.mnemonic = O
+DSignFile.jbOutputFileBrowse.text = Browse
+DSignFile.jbOutputFileBrowse.tooltip = Browse to Output File
DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
+DSignFile.jcbOutputPem.tooltip = Output the signature in PEM encoding or DER encoding
DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
DSignFile.jlAddTimestamp.text = Add Time stamp:
DSignFile.jlDetachedSignature.text = Generate Detached Signature:
DSignFile.jlInputFile.text = Input File:
+DSignFile.jlOutputFile.text = Output File:
+DSignFile.jlOutputPem.text = PEM:
DSignFile.jlSignatureAlgorithm.text = Signature Algorithm:
DSignFile.jlTimestampServerUrl.text = TSA URL:
DSignFile.jtfInputFile.tooltip = File to sign
+DSignFile.jtfOutputFile.tooltip = File to store the signature
ExamineFileAction.ExamineFile.Title = Open File
ExamineFileAction.ExamineFile.button = Open
From 7e984c36a03af2ccede680aabd924d60d37221d1 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 09:36:49 -0800
Subject: [PATCH 27/69] Remove signature output PEM encoding TODO.
---
.../org/kse/gui/actions/CounterSignAction.java | 16 ++++++++--------
.../java/org/kse/gui/actions/SignFileAction.java | 16 ++++++++--------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index b7e9623f4..3bf235f2c 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -113,7 +113,6 @@ protected void doAction() {
// TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
- boolean outputPem = dSignFile.isOutputPem();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
File outputFile = dSignFile.getOutputFile();
@@ -128,14 +127,15 @@ protected void doAction() {
CMSSignedData signedData = CmsSigner.counterSign(signature, privateKey, certs, detachedSignature,
signatureType, tsaUrl, provider);
+ byte[] encoded;
+ if (!dSignFile.isOutputPem()) {
+ encoded = signedData.getEncoded();
+ } else {
+ encoded = CmsUtil.getPem(signedData).getBytes();
+ }
+
try (OutputStream os = new FileOutputStream(outputFile)) {
- if (!outputPem) {
- os.write(signedData.getEncoded());
- } else {
- // shouldn't need character encoding since PEM is plain ASCII.
- // TODO JW - see out how other actions handle PEM output
- os.write(CmsUtil.getPem(signedData).getBytes());
- }
+ os.write(encoded);
}
} catch (Exception ex) {
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 3dd89f5a5..0f489eff5 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -110,7 +110,6 @@ protected void doAction() {
// TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
- boolean outputPem = dSignFile.isOutputPem();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
File outputFile = dSignFile.getOutputFile();
@@ -119,14 +118,15 @@ protected void doAction() {
CMSSignedData signedData = CmsSigner.sign(inputFile, privateKey, certs, detachedSignature, signatureType,
tsaUrl, provider);
+ byte[] encoded;
+ if (!dSignFile.isOutputPem()) {
+ encoded = signedData.getEncoded();
+ } else {
+ encoded = CmsUtil.getPem(signedData).getBytes();
+ }
+
try (OutputStream os = new FileOutputStream(outputFile)) {
- if (!outputPem) {
- os.write(signedData.getEncoded());
- } else {
- // shouldn't need character encoding since PEM is plain ASCII.
- // TODO JW - see out how other actions handle PEM output
- os.write(CmsUtil.getPem(signedData).getBytes());
- }
+ os.write(encoded);
}
} catch (Exception ex) {
From 83e7a3c25b4ca85b1f1ea54ab119d0962ce9aa14 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 10:35:10 -0800
Subject: [PATCH 28/69] Fixed RSAPSS algorithm lookup TODO.
---
.../kse/gui/actions/CounterSignAction.java | 1 -
.../org/kse/gui/actions/SignFileAction.java | 1 -
.../org/kse/gui/dialogs/DViewSignature.java | 28 ++++++++++++-------
3 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index 3bf235f2c..15b3e2435 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -111,7 +111,6 @@ protected void doAction() {
return;
}
- // TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 0f489eff5..20f431a8d 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -108,7 +108,6 @@ protected void doAction() {
return;
}
- // TODO JW - When using RSA and MFG1, the MFG1 is not displayed when viewing the signature.
boolean detachedSignature = dSignFile.isDetachedSignature();
SignatureType signatureType = dSignFile.getSignatureType();
File inputFile = dSignFile.getInputFile();
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index e6ae3c0bf..b8a74c01f 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -466,19 +466,13 @@ private void populateDetails() {
// jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':'));
// jtfContentDigest.setCaretPosition(0);
- DigestType digestType = DigestType.resolveOid(signerInfo.getDigestAlgOID());
- KeyInfo keyInfo = KeyPairUtil.getKeyInfo(cert.getPublicKey());
- String algorithm = keyInfo.getAlgorithm();
- // TODO JW - Is there a better method for getting signature algorithm name from cert algorithm?
- if (algorithm.equals("EC")) {
- algorithm = "ECDSA";
- }
- String signatureAlgorithm = digestType.friendly().replace("-", "") + "with" + algorithm;
- SignatureType signatureType = SignatureType.resolveJce(signatureAlgorithm);
+ SignatureType signatureType = lookupSignatureType(signerInfo);
if (signatureType != null ) {
jtfSignatureAlgorithm.setText(signatureType.friendly());
- jtfSignatureAlgorithm.setCaretPosition(0);
+ } else {
+ jtfSignatureAlgorithm.setText("");
}
+ jtfSignatureAlgorithm.setCaretPosition(0);
timeStampSigner = CmsUtil.getTimeStampSignature(signerInfo);
@@ -510,6 +504,20 @@ private void populateDetails() {
}
}
+ private SignatureType lookupSignatureType(SignerInformation signerInfo) {
+ SignatureType signatureType = null;
+ if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(signerInfo.getEncryptionAlgOID())) {
+ // Lookup by JCE name for RSA
+ DigestType digestType = DigestType.resolveOid(signerInfo.getDigestAlgOID());
+ String signatureAlgorithm = digestType.friendly().replace("-", "") + "withRSA";
+ signatureType = SignatureType.resolveJce(signatureAlgorithm);
+ } else {
+ signatureType = SignatureType.resolveOid(signerInfo.getEncryptionAlgOID(),
+ signerInfo.getEncryptionAlgParams());
+ }
+ return signatureType;
+ }
+
private static class SelectAll implements Selector {
@Override
public Object clone() {
From ccbbf79169df04bba0462c0a2cde729afa5a4e44 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 11:05:15 -0800
Subject: [PATCH 29/69] Fixed DSignFile layout.
---
.../org/kse/gui/dialogs/sign/DSignFile.java | 38 +++++++------------
1 file changed, 14 insertions(+), 24 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 23a9ed806..d8078afb6 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -72,11 +72,9 @@ public class DSignFile extends JEscDialog {
private static final String CANCEL_KEY = "CANCEL_KEY";
private JLabel jlInputFile;
- private JPanel jpInputFile;
private JTextField jtfInputFile;
private JButton jbInputFileBrowse;
private JLabel jlOutputFile;
- private JPanel jpOutputFile;
private JTextField jtfOutputFile;
private JButton jbOutputFileBrowse;
private JLabel jlDetachedSignature;
@@ -138,11 +136,6 @@ private void initComponents(boolean isCounterSign) {
PlatformUtil.setMnemonic(jbInputFileBrowse, res.getString("DSignFile.jbInputFileBrowse.mnemonic").charAt(0));
jbInputFileBrowse.setToolTipText(res.getString("DSignFile.jbInputFileBrowse.tooltip"));
- // TODO JW - Remove default panel margin/insets.
- jpInputFile = new JPanel();
- jpInputFile.add(jtfInputFile);
- jpInputFile.add(jbInputFileBrowse);
-
jlOutputFile = new JLabel(res.getString("DSignFile.jlOutputFile.text"));
jtfOutputFile = new JTextField(30);
jtfOutputFile.setCaretPosition(0);
@@ -152,11 +145,6 @@ private void initComponents(boolean isCounterSign) {
PlatformUtil.setMnemonic(jbOutputFileBrowse, res.getString("DSignFile.jbOutputFileBrowse.mnemonic").charAt(0));
jbOutputFileBrowse.setToolTipText(res.getString("DSignFile.jbOutputFileBrowse.tooltip"));
- // TODO JW - Remove default panel margin/insets.
- jpOutputFile = new JPanel();
- jpOutputFile.add(jtfOutputFile);
- jpOutputFile.add(jbOutputFileBrowse);
-
jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
jcbDetachedSignature = new JCheckBox();
jcbDetachedSignature.setSelected(true);
@@ -194,21 +182,23 @@ private void initComponents(boolean isCounterSign) {
// layout
Container pane = getContentPane();
- pane.setLayout(new MigLayout("insets dialog, fill", "[para]unrel[right]unrel[]", "[]unrel[]"));
- pane.add(jlInputFile, "skip");
- pane.add(jpInputFile, "wrap");
- pane.add(jlOutputFile, "skip");
- pane.add(jpOutputFile, "wrap");
- pane.add(jlDetachedSignature, "skip");
+ pane.setLayout(new MigLayout("insets dialog, fill", "[right]rel[]", "[]unrel[]"));
+ pane.add(jlInputFile, "");
+ pane.add(jtfInputFile, "");
+ pane.add(jbInputFileBrowse, "wrap");
+ pane.add(jlOutputFile, "");
+ pane.add(jtfOutputFile, "");
+ pane.add(jbOutputFileBrowse, "wrap");
+ pane.add(jlDetachedSignature, "");
pane.add(jcbDetachedSignature, "wrap");
- pane.add(jlOutputPem, "skip");
+ pane.add(jlOutputPem, "");
pane.add(jcbOutputPem, "wrap");
- pane.add(jlSignatureAlgorithm, "skip");
- pane.add(jcbSignatureAlgorithm, "sgx, wrap");
- pane.add(jlAddTimestamp, "skip");
+ pane.add(jlSignatureAlgorithm, "");
+ pane.add(jcbSignatureAlgorithm, "wrap");
+ pane.add(jlAddTimestamp, "");
pane.add(jcbAddTimestamp, "wrap");
- pane.add(jlTimestampServerUrl, "skip");
- pane.add(jcbTimestampServerUrl, "sgx, wrap para");
+ pane.add(jlTimestampServerUrl, "");
+ pane.add(jcbTimestampServerUrl, "wrap para");
pane.add(new JSeparator(), "spanx, growx, wrap para");
pane.add(jpButtons, "right, spanx");
From 28545d49f039f6705e7dcb04f07b9939dbd20ed1 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 13:24:42 -0800
Subject: [PATCH 30/69] Fixed TODO for CmsSigner exception. Parameterized
DigestType for time stamp.
---
.../java/org/kse/crypto/signing/CmsSigner.java | 15 +++++++++------
.../java/org/kse/crypto/signing/JarSigner.java | 2 +-
.../java/org/kse/gui/dialogs/sign/DSignFile.java | 1 +
.../org/kse/crypto/signing/resources.properties | 1 +
4 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index a55dc9898..5615aa352 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -27,6 +27,7 @@
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collection;
+import java.util.ResourceBundle;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Primitive;
@@ -55,6 +56,7 @@
* Message Syntax (CMS).
*/
public class CmsSigner {
+ private static ResourceBundle res = ResourceBundle.getBundle("org/kse/crypto/signing/resources");
/**
* Signs a file using PKCS #7 CMS.
@@ -86,13 +88,12 @@ public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Cert
CMSSignedData signedData = generator.generate(msg, !detachedSignature);
if (tsaUrl != null) {
- signedData = addTimestamp(tsaUrl, signedData);
+ signedData = addTimestamp(tsaUrl, signedData, signatureType.digestType());
}
return signedData;
} catch (Exception e) {
- // TODO JW - Create exception message
- throw new CryptoException("TODO");
+ throw new CryptoException(res.getString("CmsSignatureFailed.exception.message"), e);
}
}
@@ -133,11 +134,13 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
/**
* Adds a timestamp to a PKCS #7 signature.
*
- * @param tsaUrl The URL of the time stamp authority
+ * @param tsaUrl The URL of the time stamp authority.
* @param signedData The signature to time stamp.
+ * @param digestType The digest type to use for the time stamp.
* @return CMSSignedData with time stamp.
*/
- public static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData) throws IOException {
+ public static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData, DigestType digestType)
+ throws IOException {
Collection signerInfos = signedData.getSignerInfos().getSigners();
@@ -146,7 +149,7 @@ public static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData
byte[] signature = si.getSignature();
// send request to TSA
- byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, DigestType.SHA256);
+ byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, digestType);
// create new SignerInformation with TS attribute
Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
diff --git a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
index 6a92ed4ad..a4feabf6d 100644
--- a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
@@ -755,7 +755,7 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters
// now let TSA time-stamp the signature
if (tsaUrl != null && !tsaUrl.isEmpty()) {
- signedData = CmsSigner.addTimestamp(tsaUrl, signedData);
+ signedData = CmsSigner.addTimestamp(tsaUrl, signedData, DigestType.SHA256);
}
return signedData.getEncoded();
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index d8078afb6..56076a49d 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -91,6 +91,7 @@ public class DSignFile extends JEscDialog {
private JButton jbOK;
private JButton jbCancel;
+ // TODO store settings (e.g., signature algorithm, TS setting and URL) in preferences.
private PrivateKey signPrivateKey;
private KeyPairType signKeyPairType;
private File inputFile;
diff --git a/kse/src/main/resources/org/kse/crypto/signing/resources.properties b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
index 860c42d25..3f31b18e2 100644
--- a/kse/src/main/resources/org/kse/crypto/signing/resources.properties
+++ b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
@@ -1,6 +1,7 @@
SignatureBlockCreationFailed.exception.message=Block signing failed.
JarDigestSignatureFailed.exception.message=JAR digest signing failed.
+CmsSignatureFailed.exception.message=File signing failed.
NoReadJadCorrupt.exception.message=Could not read JAD file, may be corrupt.
Base64CertificateFailed.exception.message=Could not get Base-64 encoding for certificate.
From d33b10917b6822f2be08f035a1cbb0a907661b2c Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 13:32:21 -0800
Subject: [PATCH 31/69] Fixed some warnings.
---
kse/src/main/java/org/kse/crypto/signing/CmsUtil.java | 2 +-
kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index 121bb09f7..b86d478a2 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -68,7 +68,7 @@ public static CMSSignedData loadSignature(File signatureFile, Supplier cho
}
// TODO JW - Do we even want to check the type? Should we just let the BC CMS
// class bomb out?
- if (!"CMS".equals(signaturePem.getType()) && !"PKCS7".equals(signaturePem.getType())) {
+ if (!CMS_PEM_TYPE.equals(signaturePem.getType()) && !PKCS7_PEM_TYPE.equals(signaturePem.getType())) {
// TODO JW - What to throw if the signature is not the correct type?
throw new CryptoException("PEM is not of type CMS or PKCS7");
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 56076a49d..4b80482cb 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -114,7 +114,7 @@ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPa
super(parent, Dialog.ModalityType.DOCUMENT_MODAL);
this.signPrivateKey = signPrivateKey;
this.signKeyPairType = signKeyPairType;
- String title = "DSignFile.Sign.Title";;
+ String title = "DSignFile.Sign.Title";
if (isCounterSign) {
title = "DSignFile.CounterSign.Title";
}
From 67283a6d1d1f4200c355459b3df456299f623407 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 13:38:56 -0800
Subject: [PATCH 32/69] Fixed more warnings and a TODO.
---
.../org/kse/crypto/signing/CmsSigner.java | 19 ++++++++++++++++---
.../kse/crypto/signing/resources.properties | 1 +
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index 5615aa352..8814462dd 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -62,7 +62,6 @@ public class CmsSigner {
* Signs a file using PKCS #7 CMS.
*
* @param inputFile The file to sign.
- * @param outputFile The output file for the signature.
* @param privateKey The private key to use for signing.
* @param certificateChain The certificate chain for the private key.
* @param detachedSignature True if the signature is to be detached. False,
@@ -71,6 +70,7 @@ public class CmsSigner {
* @param tsaUrl An optional TSA URL for adding a time stamp token to
* the signature.
* @param provider
+ * @return The signature in a CMSSignedData object.
*/
public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Certificate[] certificateChain,
boolean detachedSignature, SignatureType signatureType, String tsaUrl, Provider provider)
@@ -97,6 +97,20 @@ public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Cert
}
}
+ /**
+ * Counter signs a signature using PKCS #7 CMS.
+ *
+ * @param signedData The signature to counter sign.
+ * @param privateKey The private key to use for signing.
+ * @param certificateChain The certificate chain for the private key.
+ * @param detachedSignature True if the signature is to be detached. False,
+ * encapsulate the file into the signature.
+ * @param signatureType The signature type to use for signing.
+ * @param tsaUrl An optional TSA URL for adding a time stamp token to
+ * the signature.
+ * @param provider
+ * @return The counter signed signature in a CMSSignedData object.
+ */
public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey privateKey,
X509Certificate[] certificateChain, boolean detachedSignature, SignatureType signatureType, String tsaUrl,
Provider provider) throws CryptoException {
@@ -126,8 +140,7 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
return counterSignedData;
} catch (CertificateEncodingException | OperatorCreationException | CMSException e) {
- // TODO Auto-generated catch block
- throw new CryptoException("TODO");
+ throw new CryptoException(res.getString("CmsCounterSignatureFailed.exception.message"), e);
}
}
diff --git a/kse/src/main/resources/org/kse/crypto/signing/resources.properties b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
index 3f31b18e2..cc98dc9eb 100644
--- a/kse/src/main/resources/org/kse/crypto/signing/resources.properties
+++ b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
@@ -2,6 +2,7 @@
SignatureBlockCreationFailed.exception.message=Block signing failed.
JarDigestSignatureFailed.exception.message=JAR digest signing failed.
CmsSignatureFailed.exception.message=File signing failed.
+CmsCounterSignatureFailed.exception.message=Counter signing failed.
NoReadJadCorrupt.exception.message=Could not read JAD file, may be corrupt.
Base64CertificateFailed.exception.message=Could not get Base-64 encoding for certificate.
From c593166a4e02d425b7fe802ccd3c6c377f173045 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 10 Dec 2024 23:52:57 -0800
Subject: [PATCH 33/69] Added support for an email address to
SignerListCellRend.
---
.../org/kse/crypto/x509/X500NameUtils.java | 30 ++++++++++++++-----
.../kse/gui/dialogs/SignerListCellRend.java | 9 ++++++
2 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java b/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java
index a24678592..db99574b9 100644
--- a/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java
+++ b/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java
@@ -83,17 +83,11 @@ public static String getRdn(X500Name dn, ASN1ObjectIdentifier rdnOid) {
return value;
}
- /**
- * Return CN of an X.500 name
- *
- * @param name X.500 name object
- * @return CN from Name or an empty string if no CN found
- */
- public static String extractCN(X500Name name) {
+ private static String extractComponent(X500Name name, ASN1ObjectIdentifier objectIdentifier) {
for (RDN rdn : name.getRDNs()) {
AttributeTypeAndValue atav = rdn.getFirst();
- if (atav.getType().equals(BCStyle.CN)) {
+ if (atav.getType().equals(objectIdentifier)) {
return atav.getValue().toString();
}
}
@@ -101,6 +95,26 @@ public static String extractCN(X500Name name) {
return "";
}
+ /**
+ * Return Email Address of an X.500 name
+ *
+ * @param name X.500 name object
+ * @return Email Address from Name or an empty string if no Email Address found
+ */
+ public static String extractEmailAddress(X500Name name) {
+ return extractComponent(name, BCStyle.E);
+ }
+
+ /**
+ * Return CN of an X.500 name
+ *
+ * @param name X.500 name object
+ * @return CN from Name or an empty string if no CN found
+ */
+ public static String extractCN(X500Name name) {
+ return extractComponent(name, BCStyle.CN);
+ }
+
/**
* Return CN of an X.500 principal
*
diff --git a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
index 170672554..a9c51efc0 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
@@ -75,6 +75,15 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
X500Name subject = cert.getSubject();
String shortName = X500NameUtils.extractCN(subject);
+ String emailAddress = X500NameUtils.extractEmailAddress(subject);
+
+ if (!StringUtils.isBlank(emailAddress)) {
+ if (StringUtils.isBlank(shortName)) {
+ shortName = emailAddress;
+ } else {
+ shortName += " <" + emailAddress + ">";
+ }
+ }
if (StringUtils.isBlank(shortName)) {
shortName = subject.toString();
From 3bf2ce26b80ab58eccadaa1c034ffbe56d4d75a4 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Wed, 11 Dec 2024 10:17:22 -0800
Subject: [PATCH 34/69] Removed cert import pop-up dialog. Hid fields not
hooked up in DViewSignature.
---
.../org/kse/gui/actions/VerifySignatureAction.java | 7 +------
.../java/org/kse/gui/dialogs/DViewSignature.java | 13 +++++++------
2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 962861988..b1381a005 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -251,12 +251,12 @@ protected void doAction() {
dViewSignature.setVisible(true);
/*
+ Information from GnuTLS certtool
Signers:
Signer's issuer DN: O=ICU Medical,CN=ICU Medical Dev Issuing
Signer's serial: 30bc7fadef7fe924fa77a0f376f31d92b68fa33e
Signing time: Mon Feb 07 23:07:19 UTC 2022
Signature Algorithm: RSA-SHA256
- Signature Algorithm: RSA-PSS-SHA256 certtool shows SHA-256, but KSE shows SHA-384, which is what was used. (CmsSigner.java.p7m)
Signed Attributes:
messageDigest: 0420ed6b93a0a57ff71b075d7628f807db2d6f9a8727557a02b2f6f9ff520eb4caaf
1.2.840.113549.1.9.52: 301c300b0609608648016503040201a10d06092a864886f70d01010b0500
@@ -265,11 +265,6 @@ protected void doAction() {
*/
kseFrame.updateControls(true);
-
- JOptionPane.showMessageDialog(frame, res.getString(
- "ImportTrustedCertificateAction.ImportTrustCertSuccessful.message"),
- res.getString("ImportTrustedCertificateAction.ImportTrustCert.Title"),
- JOptionPane.INFORMATION_MESSAGE);
} catch (Exception ex) {
DError.displayError(frame, ex);
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index b8a74c01f..139b5919b 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -153,10 +153,10 @@ public class DViewSignature extends JEscDialog {
private JTextField jtfContentType;
private JLabel jlContentDigest;
private JTextField jtfContentDigest;
- // TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes
private JButton jbCertificates;
private JButton jbTimeStamp;
private JButton jbCounterSigners;
+ // TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes
private JButton jbExtensions;
private JButton jbPem;
private JButton jbAsn1;
@@ -281,10 +281,11 @@ private void initComponents(Collection signers) throws Crypto
pane.add(jtfSigningTime, "wrap");
pane.add(jlSignatureAlgorithm, "");
pane.add(jtfSignatureAlgorithm, "wrap");
- pane.add(jlContentType, "");
- pane.add(jtfContentType, "wrap");
- pane.add(jlContentDigest, "");
- pane.add(jtfContentDigest, "wrap");
+ // TODO JW - clean up the dialog
+// pane.add(jlContentType, "");
+// pane.add(jtfContentType, "wrap");
+// pane.add(jlContentDigest, "");
+// pane.add(jtfContentDigest, "wrap");
pane.add(jbCertificates, "spanx, split");
pane.add(jbTimeStamp, "");
pane.add(jbCounterSigners, "");
@@ -482,7 +483,6 @@ private void populateDetails() {
jbTimeStamp.setEnabled(false);
}
-
if (signerInfo.getCounterSignatures().size() > 0) {
jbCounterSigners.setEnabled(true);
} else {
@@ -506,6 +506,7 @@ private void populateDetails() {
private SignatureType lookupSignatureType(SignerInformation signerInfo) {
SignatureType signatureType = null;
+
if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(signerInfo.getEncryptionAlgOID())) {
// Lookup by JCE name for RSA
DigestType digestType = DigestType.resolveOid(signerInfo.getDigestAlgOID());
From d8e8f7f134280ac4bd78454292d8444b0b5f40c6 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 00:24:34 -0800
Subject: [PATCH 35/69] Re-enabled signature verification. Update output file
based on detached output.
---
.../gui/actions/VerifySignatureAction.java | 29 +++++++++----------
.../org/kse/gui/dialogs/sign/DSignFile.java | 25 ++++++++++------
2 files changed, 30 insertions(+), 24 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index b1381a005..90d3b669a 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -224,22 +224,21 @@ protected void doAction() {
Collection matchedCerts = certStore.getMatches(signer.getSID());
if (!matchedCerts.isEmpty()) {
X509CertificateHolder cert = matchedCerts.iterator().next();
- // TODO JW- Counter signing breaks the signature...
// TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
-// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
-// System.out.println("Verified by: " + cert.getSubject());
-// verified = true;
-//
-// TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
-// if (tspToken != null) {
-// matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
-// if (!matchedCerts.isEmpty()) {
-// cert = matchedCerts.iterator().next();
-// tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
-// System.out.println("Time stamped by: " + cert.getSubject());
-// }
-// }
-// }
+ if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+ System.out.println("Verified by: " + cert.getSubject());
+ verified = true;
+
+ TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
+ if (tspToken != null) {
+ matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+ tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ System.out.println("Time stamped by: " + cert.getSubject());
+ }
+ }
+ }
}
}
System.out.println("Verified: " + verified);
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 4b80482cb..2ed932523 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -96,6 +96,7 @@ public class DSignFile extends JEscDialog {
private KeyPairType signKeyPairType;
private File inputFile;
private File outputFile;
+ private boolean outputFileChosen;
private boolean detachedSignature = true;
private boolean outputPem;
private SignatureType signatureType;
@@ -187,6 +188,7 @@ private void initComponents(boolean isCounterSign) {
pane.add(jlInputFile, "");
pane.add(jtfInputFile, "");
pane.add(jbInputFileBrowse, "wrap");
+ // TODO JW - Hide the output file for counter signing (re-use the same cert file). Prompt for overwriting.
pane.add(jlOutputFile, "");
pane.add(jtfOutputFile, "");
pane.add(jbOutputFileBrowse, "wrap");
@@ -262,7 +264,7 @@ protected void enableDisableTsaElements() {
protected void detachedSignatureStateChange() {
detachedSignature = jcbDetachedSignature.isSelected();
- // TODO JW - implement output file name update -- even if user chooses a file?
+ updateOutputFile();
}
/**
@@ -375,14 +377,11 @@ private void inputFileBrowsePressed(boolean isCounterSign) {
File chosenFile = chooser.getSelectedFile();
CurrentDirectory.updateForFile(chosenFile);
inputFile = chosenFile;
- outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
- // TODO JW - Is there a better location for this code?
jtfInputFile.setText(inputFile.getAbsolutePath());
jtfInputFile.setCaretPosition(0);
- jtfOutputFile.setText(outputFile.getAbsolutePath());
- jtfOutputFile.setCaretPosition(0);
+ updateOutputFile();
}
}
@@ -400,15 +399,23 @@ private void outputFileBrowsePressed() {
if (rtnValue == JFileChooser.APPROVE_OPTION) {
File chosenFile = chooser.getSelectedFile();
CurrentDirectory.updateForFile(chosenFile);
- // TODO JW - Need to add extension to chosen file?
outputFile = chosenFile;
+ outputFileChosen = true;
- // TODO JW - Is there a better location for this code?
- jtfOutputFile.setText(outputFile.getAbsolutePath());
- jtfOutputFile.setCaretPosition(0);
+ updateOutputFile();
}
}
+ private void updateOutputFile()
+ {
+ if (!outputFileChosen) {
+ outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
+ }
+
+ jtfOutputFile.setText(outputFile.getAbsolutePath());
+ jtfOutputFile.setCaretPosition(0);
+ }
+
/**
* Returns the current success status
*
From 4fd282c51915682793728b9c478449d614603eb9 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 01:02:40 -0800
Subject: [PATCH 36/69] Rearranged the buttons on DViewSignature.
---
.../main/java/org/kse/gui/dialogs/DViewSignature.java | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 139b5919b..cde77f909 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -286,10 +286,13 @@ private void initComponents(Collection signers) throws Crypto
// pane.add(jtfContentType, "wrap");
// pane.add(jlContentDigest, "");
// pane.add(jtfContentDigest, "wrap");
- pane.add(jbCertificates, "spanx, split");
- pane.add(jbTimeStamp, "");
- pane.add(jbCounterSigners, "");
+ pane.add(jbTimeStamp, "spanx, split");
+ pane.add(jbCounterSigners, "wrap");
// pane.add(jbExtensions, "");
+ pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
+ pane.add(jbCertificates, "spanx, split");
+ // TODO JW - Hide PEM and ASN.1 buttons for Counter Signers.
+ // Bouncy Castle CMS does not expose the ASN1 encoding for SignerInfos or other aspects of the CMS data.
pane.add(jbPem, "");
pane.add(jbAsn1, "wrap");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
From e5b67335e1e6252578f11a75004fdab78ead04dc Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 01:14:41 -0800
Subject: [PATCH 37/69] Verify counter signers.
---
.../gui/actions/VerifySignatureAction.java | 91 +++++++++++--------
1 file changed, 51 insertions(+), 40 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 90d3b669a..b369c5ee0 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -85,6 +85,8 @@
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TSPValidationException;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.Selector;
import org.bouncycastle.util.Store;
@@ -200,48 +202,9 @@ protected void doAction() {
// TODO JW - Verify the signature using the keystore
// TODO JW build Store using certs from the truststore. If a cert cannot be found
// then the signature should not be trusted (even if valid)
- boolean verified = false;
Store certStore = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
- for (SignerInformation signer : signers.getSigners()) {
-// AttributeTable signedAttributes = signer.getSignedAttributes();
-// AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
-//
-// if (signedAttributes != null) {
-// Hashtable ht = signedAttributes.toHashtable();
-// for (Object k : ht.keySet()) {
-// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
-// }
-// }
-// if (unsignedAttributes != null) {
-// Hashtable ht = unsignedAttributes.toHashtable();
-// for (Object k : ht.keySet()) {
-// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
-// }
-// }
-//
- // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
- Collection matchedCerts = certStore.getMatches(signer.getSID());
- if (!matchedCerts.isEmpty()) {
- X509CertificateHolder cert = matchedCerts.iterator().next();
- // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
- if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
- System.out.println("Verified by: " + cert.getSubject());
- verified = true;
-
- TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
- if (tspToken != null) {
- matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
- tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- System.out.println("Time stamped by: " + cert.getSubject());
- }
- }
- }
- }
- }
- System.out.println("Verified: " + verified);
+ verify(certStore, signers);
DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat
.format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()),
@@ -269,6 +232,54 @@ protected void doAction() {
}
}
+ private void verify(Store certStore, SignerInformationStore signers) throws CMSException,
+ OperatorCreationException, CertificateException, CryptoException, TSPException, TSPValidationException {
+ boolean verified = false;
+ for (SignerInformation signer : signers.getSigners()) {
+// AttributeTable signedAttributes = signer.getSignedAttributes();
+// AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
+//
+// if (signedAttributes != null) {
+// Hashtable ht = signedAttributes.toHashtable();
+// for (Object k : ht.keySet()) {
+// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
+// }
+// }
+// if (unsignedAttributes != null) {
+// Hashtable ht = unsignedAttributes.toHashtable();
+// for (Object k : ht.keySet()) {
+// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
+// }
+// }
+//
+ // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
+ Collection matchedCerts = certStore.getMatches(signer.getSID());
+ if (!matchedCerts.isEmpty()) {
+ X509CertificateHolder cert = matchedCerts.iterator().next();
+ // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
+ if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+ System.out.println("Verified by: " + cert.getSubject());
+ verified = true;
+
+ TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
+ if (tspToken != null) {
+ matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+ tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ System.out.println("Time stamped by: " + cert.getSubject());
+ }
+ }
+
+ if (signer.getCounterSignatures().size() > 0) {
+ verify(certStore, signer.getCounterSignatures());
+ }
+ }
+ }
+ }
+ System.out.println("Verified: " + verified);
+ }
+
private boolean isCA(X509Certificate cert) {
int basicConstraints = cert.getBasicConstraints();
if (basicConstraints != -1) {
From 3e17c6af62be1fd613483b9d6feef0b0d28ebbfe Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 13:43:36 -0800
Subject: [PATCH 38/69] Implement time stamps for counter signers.
---
.../org/kse/crypto/signing/CmsSigner.java | 68 ++++++++++---------
.../org/kse/crypto/signing/JarSigner.java | 28 +++++++-
.../kse/gui/actions/CounterSignAction.java | 2 +
.../org/kse/gui/dialogs/sign/DSignFile.java | 1 +
4 files changed, 66 insertions(+), 33 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index 8814462dd..87bb4261c 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -25,6 +25,7 @@
import java.security.Provider;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ResourceBundle;
@@ -88,7 +89,9 @@ public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Cert
CMSSignedData signedData = generator.generate(msg, !detachedSignature);
if (tsaUrl != null) {
- signedData = addTimestamp(tsaUrl, signedData, signatureType.digestType());
+ SignerInformationStore signerInfos = addTimestamp(tsaUrl, signedData.getSignerInfos(),
+ signatureType.digestType());
+ signedData = CMSSignedData.replaceSigners(signedData, signerInfos);
}
return signedData;
@@ -121,9 +124,16 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
.build(contentSigner, certificateChain[0]));
+ // Counter signs all existing signatures.
+ // TODO JW - Should there be a dialog for choosing the signature to counter sign?
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
for (SignerInformation signer : signedData.getSignerInfos()) {
SignerInformationStore counterSigners = counterSignerGen.generateCounterSigners(signer);
+
+ if (tsaUrl != null) {
+ counterSigners = addTimestamp(tsaUrl, counterSigners, signatureType.digestType());
+ }
+
signer = SignerInformation.addCounterSigners(signer, counterSigners);
generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
@@ -133,13 +143,9 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
CMSSignedData counterSignedData = generator.generate(signedData.getSignedContent(), !detachedSignature);
- // TODO JW - is it possible to add a timestamp for a counter signature? The current logic drops the counter signer.
-// if (tsaUrl != null) {
-// counterSignedData = addTimestamp(tsaUrl, counterSignedData);
-// }
return counterSignedData;
- } catch (CertificateEncodingException | OperatorCreationException | CMSException e) {
+ } catch (CertificateEncodingException | OperatorCreationException | CMSException | IOException e) {
throw new CryptoException(res.getString("CmsCounterSignatureFailed.exception.message"), e);
}
}
@@ -147,35 +153,33 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
/**
* Adds a timestamp to a PKCS #7 signature.
*
- * @param tsaUrl The URL of the time stamp authority.
- * @param signedData The signature to time stamp.
- * @param digestType The digest type to use for the time stamp.
- * @return CMSSignedData with time stamp.
+ * @param tsaUrl The URL of the time stamp authority.
+ * @param signerInfos The signer information to time stamp.
+ * @param digestType The digest type to use for the time stamp.
+ * @return SignerInformation with time stamp token.
*/
- public static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData, DigestType digestType)
- throws IOException {
+ public static SignerInformationStore addTimestamp(String tsaUrl, SignerInformationStore signerInfos,
+ DigestType digestType) throws IOException {
- Collection signerInfos = signedData.getSignerInfos().getSigners();
+ Collection newSignerInfos = new ArrayList<>();
// get signature of first signer (should be the only one)
- SignerInformation si = signerInfos.iterator().next();
- byte[] signature = si.getSignature();
-
- // send request to TSA
- byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, digestType);
-
- // create new SignerInformation with TS attribute
- Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
- new DERSet(ASN1Primitive.fromByteArray(token)));
- ASN1EncodableVector timestampVector = new ASN1EncodableVector();
- timestampVector.add(tokenAttr);
- AttributeTable at = new AttributeTable(timestampVector);
- si = SignerInformation.replaceUnsignedAttributes(si, at);
- signerInfos.clear();
- signerInfos.add(si);
- SignerInformationStore newSignerStore = new SignerInformationStore(signerInfos);
-
- // create new signed data
- return CMSSignedData.replaceSigners(signedData, newSignerStore);
+ for (SignerInformation si : signerInfos.getSigners()) {
+ byte[] signature = si.getSignature();
+
+ // send request to TSA
+ byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, digestType);
+
+ // create new SignerInformation with TS attribute
+ Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
+ new DERSet(ASN1Primitive.fromByteArray(token)));
+ ASN1EncodableVector timestampVector = new ASN1EncodableVector();
+ timestampVector.add(tokenAttr);
+ AttributeTable at = new AttributeTable(timestampVector);
+
+ newSignerInfos.add(SignerInformation.replaceUnsignedAttributes(si, at));
+ }
+
+ return new SignerInformationStore(newSignerInfos);
}
}
diff --git a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
index a4feabf6d..6e07ceda1 100644
--- a/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/JarSigner.java
@@ -755,7 +755,7 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters
// now let TSA time-stamp the signature
if (tsaUrl != null && !tsaUrl.isEmpty()) {
- signedData = CmsSigner.addTimestamp(tsaUrl, signedData, DigestType.SHA256);
+ signedData = addTimestamp(tsaUrl, signedData);
}
return signedData.getEncoded();
@@ -764,6 +764,32 @@ public AttributeTable getAttributes(@SuppressWarnings("rawtypes") Map parameters
}
}
+ private static CMSSignedData addTimestamp(String tsaUrl, CMSSignedData signedData) throws IOException {
+
+ Collection signerInfos = signedData.getSignerInfos().getSigners();
+
+ // get signature of first signer (should be the only one)
+ SignerInformation si = signerInfos.iterator().next();
+ byte[] signature = si.getSignature();
+
+ // send request to TSA
+ byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, DigestType.SHA256);
+
+ // create new SignerInformation with TS attribute
+ Attribute tokenAttr = new Attribute(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken,
+ new DERSet(ASN1Primitive.fromByteArray(token)));
+ ASN1EncodableVector timestampVector = new ASN1EncodableVector();
+ timestampVector.add(tokenAttr);
+ AttributeTable at = new AttributeTable(timestampVector);
+ si = SignerInformation.replaceUnsignedAttributes(si, at);
+ signerInfos.clear();
+ signerInfos.add(si);
+ SignerInformationStore newSignerStore = new SignerInformationStore(signerInfos);
+
+ // create new signed data
+ return CMSSignedData.replaceSigners(signedData, newSignerStore);
+ }
+
/*
* Convert the supplied signature name to make it valid for use with
* signing, ie any characters that are not 'a-z', 'A-Z', '0-9', '_' or
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index 15b3e2435..dd00c3e06 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -143,6 +143,8 @@ protected void doAction() {
}
private File chooseContentFile() {
+ // TODO JW - This dialog just pops up and the user doesn't know what file to choose.
+ // Need to provide more information.
JFileChooser chooser = FileChooserFactory.getNoFileChooser();
chooser.setCurrentDirectory(CurrentDirectory.get());
chooser.setDialogTitle(res.getString("CounterSignAction.ChooseContent.Title"));
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 2ed932523..73975e380 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -282,6 +282,7 @@ public File getInputFile() {
* @return File output file
*/
public File getOutputFile() {
+ // TODO JW - output file is not updated if the user manually changes the value (i.e., didn't use the file chooser)
return outputFile;
}
From 4bb49445e01586b9c22159e06e46ebd32b4e1907 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 14:37:44 -0800
Subject: [PATCH 39/69] Added context to the view Time Stamp and Counter
Signers dialogs.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 45 +++++++++++++++++++
.../org/kse/gui/dialogs/DViewSignature.java | 26 ++++++-----
.../kse/gui/dialogs/SignerListCellRend.java | 40 ++---------------
.../org/kse/gui/dialogs/resources.properties | 4 +-
4 files changed, 65 insertions(+), 50 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index b86d478a2..f6f3ecb47 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.text.ParseException;
+import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.function.Supplier;
@@ -34,6 +35,8 @@
import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
@@ -41,6 +44,8 @@
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.kse.crypto.CryptoException;
+import org.kse.crypto.x509.X500NameUtils;
+import org.kse.utilities.StringUtils;
import org.kse.utilities.pem.PemInfo;
import org.kse.utilities.pem.PemUtil;
@@ -216,4 +221,44 @@ public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo)
}
return timeStampToken;
}
+
+ public static X509CertificateHolder getSignerCert(CMSSignedData signedData, SignerInformation signer) {
+ X509CertificateHolder cert = null;
+
+ Collection matchedCerts = signedData.getCertificates().getMatches(signer.getSID());
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+ }
+
+ if (cert == null) {
+ // TODO JW - what type of error handling
+ }
+ return cert;
+ }
+
+ public static String getShortName(X509CertificateHolder cert) {
+ X500Name subject = cert.getSubject();
+
+ String shortName = X500NameUtils.extractCN(subject);
+ String emailAddress = X500NameUtils.extractEmailAddress(subject);
+
+ if (!StringUtils.isBlank(emailAddress)) {
+ if (StringUtils.isBlank(shortName)) {
+ shortName = emailAddress;
+ } else {
+ shortName += " <" + emailAddress + ">";
+ }
+ }
+
+ if (StringUtils.isBlank(shortName)) {
+ shortName = subject.toString();
+ }
+
+ // subject DN can be empty in some cases
+ if (StringUtils.isBlank(shortName)) {
+ shortName = cert.getSerialNumber().toString();
+ }
+
+ return shortName;
+ }
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index cde77f909..093363915 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -423,15 +423,9 @@ private void populateDetails() {
try {
Date signingTime = CmsUtil.getSigningTime(signerInfo);
- X509Certificate cert = null;
+ X509Certificate cert = X509CertUtil.convertCertificate(CmsUtil.getSignerCert(signedData, signerInfo));
- @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector
- Collection matchedCerts = signedData.getCertificates()
- .getMatches(signerInfo.getSID());
- if (!matchedCerts.isEmpty()) {
- cert = X509CertUtil.convertCertificate(matchedCerts.iterator().next());
- }
- // TODO JW - Need to check for null certificate
+ // TODO JW - Need to check for null certificate (see CmsUtil.getSignerCert)
jtfVersion.setText(Integer.toString(signerInfo.getVersion()));
jtfVersion.setCaretPosition(0);
@@ -440,9 +434,11 @@ private void populateDetails() {
jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal()));
- // TODO JW - What to do if the signature doesn't have a signing time?
if (signingTime != null) {
jtfSigningTime.setText(StringUtils.formatDate(signingTime));
+ } else {
+ // TODO JW - What to do if the signature doesn't have a signing time?
+ jtfSigningTime.setText("");
}
// if (noLongerValid) {
@@ -549,10 +545,14 @@ private void certificatesPressed() {
}
private void timeStampPressed() {
+ SignerInformation signer = getSelectedSignerInfo();
+
try {
+ String shortName = CmsUtil.getShortName(CmsUtil.getSignerCert(signedData, signer));
+
DViewSignature dViewSignature = new DViewSignature(this,
- res.getString("DViewSignature.TimeStampSigner.Title"), timeStampSigner,
- timeStampSigner.getSignerInfos().getSigners(), null);
+ MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName),
+ timeStampSigner, timeStampSigner.getSignerInfos().getSigners(), null);
dViewSignature.setLocationRelativeTo(this);
dViewSignature.setVisible(true);
} catch (CryptoException e) {
@@ -564,8 +564,10 @@ private void counterSignersPressed() {
SignerInformation signer = getSelectedSignerInfo();
try {
+ String shortName = CmsUtil.getShortName(CmsUtil.getSignerCert(signedData, signer));
+
DViewSignature dViewSignature = new DViewSignature(this,
- res.getString("DViewSignature.CounterSigners.Title"), signedData,
+ MessageFormat.format(res.getString("DViewSignature.CounterSigners.Title"), shortName), signedData,
signer.getCounterSignatures().getSigners(), null);
dViewSignature.setLocationRelativeTo(this);
dViewSignature.setVisible(true);
diff --git a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
index a9c51efc0..9cef3b6c9 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
@@ -20,18 +20,15 @@
package org.kse.gui.dialogs;
import java.awt.Component;
-import java.util.Collection;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;
-import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
-import org.kse.crypto.x509.X500NameUtils;
-import org.kse.utilities.StringUtils;
+import org.kse.crypto.signing.CmsUtil;
/**
* Custom cell renderer for the cells of the DViewSignature list.
@@ -61,38 +58,9 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
if (value instanceof SignerInformation) {
SignerInformation signer = (SignerInformation) value;
- // TODO JW Need to move cert lookup to a utility class.
- X509CertificateHolder cert = null;
- Collection matchedCerts = signedData.getCertificates().getMatches(signer.getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
- }
- if (cert == null) {
- // TODO JW - what type of error handling
- }
-
- X500Name subject = cert.getSubject();
-
- String shortName = X500NameUtils.extractCN(subject);
- String emailAddress = X500NameUtils.extractEmailAddress(subject);
-
- if (!StringUtils.isBlank(emailAddress)) {
- if (StringUtils.isBlank(shortName)) {
- shortName = emailAddress;
- } else {
- shortName += " <" + emailAddress + ">";
- }
- }
-
- if (StringUtils.isBlank(shortName)) {
- shortName = subject.toString();
- }
-
- // subject DN can be empty in some cases
- if (StringUtils.isBlank(shortName)) {
- shortName = cert.getSerialNumber().toString();
- }
+ X509CertificateHolder cert = CmsUtil.getSignerCert(signedData, signer);
+ String shortName = CmsUtil.getShortName(cert);
cell.setText(shortName);
@@ -100,7 +68,7 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
// ImageIcon icon = new ImageIcon(getClass().getResource("images/certificate_node.png"));
// cell.setIcon(icon);
- cell.setToolTipText(subject.toString());
+ cell.setToolTipText(cert.getSubject().toString());
}
return cell;
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
index c6e9cd1ec..16b71865f 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
@@ -584,13 +584,13 @@ DViewSecretKey.jtfKeySize.text = {0} bits
DViewSecretKey.jtfKeySize.tooltip = Secret key's size
DViewSignature.Certificates.Title = Signature Certificates
-DViewSignature.CounterSigners.Title = Counter Signers
+DViewSignature.CounterSigners.Title = Counter Signers for ''{0}''
DViewSignature.Extensions.Title = Certificate Extensions
DViewSignature.Issuer.Title = Issuer
DViewSignature.NoGetEncodedCert.exception.message = Could not get the encoded form of the certificate.
DViewSignature.Pem.Title = Signature PEM
DViewSignature.Subject.Title = Subject
-DViewSignature.TimeStampSigner.Title = Time Stamp
+DViewSignature.TimeStampSigner.Title = Time Stamp for ''{0}''
DViewSignature.jbAsn1.mnemonic = A
DViewSignature.jbAsn1.text = ASN.1
DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signature
From 075a52e2888e8e508662e6a21a297a0b69056204 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 12 Dec 2024 15:14:21 -0800
Subject: [PATCH 40/69] Formatted CmsUtil.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 47 ++++++++++---------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index f6f3ecb47..469cb83f3 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -180,19 +180,16 @@ public static Date getSigningTime(SignerInformation signerInfo) throws CryptoExc
* @param signerInfo The signer information.
* @return The time stamp token as TimeStampToken, if present, else null.
*/
- public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) throws CryptoException
- {
+ public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) throws CryptoException {
TimeStampToken timeStampToken = null;
- AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
- if (unsignedAttributes != null) {
- Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
- if (tsTokenAttribute != null) {
- try {
- timeStampToken = new TimeStampToken(ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]));
- } catch (TSPException | IOException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
- }
+
+ ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
+ if (timeStamp != null) {
+ try {
+ timeStampToken = new TimeStampToken(timeStamp);
+ } catch (TSPException | IOException e) {
+ // TODO JW Auto-generated catch block
+ throw new CryptoException(e);
}
}
return timeStampToken;
@@ -204,22 +201,30 @@ public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) thr
* @param signerInfo The signer information.
* @return The time stamp token as CMSSignedData, if present, else null.
*/
- public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) throws CryptoException
- {
+ public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) throws CryptoException {
CMSSignedData timeStampToken = null;
+
+ ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
+ if (timeStamp != null) {
+ try {
+ timeStampToken = new CMSSignedData(timeStamp);
+ } catch (CMSException e) {
+ // TODO JW Auto-generated catch block
+ throw new CryptoException(e);
+ }
+ }
+ return timeStampToken;
+ }
+
+ private static ContentInfo getTimeStampContentInfo(SignerInformation signerInfo) {
AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
if (unsignedAttributes != null) {
Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
if (tsTokenAttribute != null) {
- try {
- timeStampToken = new CMSSignedData(ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]));
- } catch (CMSException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
- }
+ return ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]);
}
}
- return timeStampToken;
+ return null;
}
public static X509CertificateHolder getSignerCert(CMSSignedData signedData, SignerInformation signer) {
From 82b1595af6eb9fbac988ddee0646769cf1dadacc Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Fri, 13 Dec 2024 00:22:25 -0800
Subject: [PATCH 41/69] Restructured signature loading flows. Created TODOs for
invalid conditions.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 72 ++++++++++---------
.../kse/gui/actions/CounterSignAction.java | 12 +++-
.../gui/actions/VerifySignatureAction.java | 52 +++++---------
.../org/kse/gui/dialogs/DViewSignature.java | 1 -
.../org/kse/gui/actions/resources.properties | 4 +-
5 files changed, 66 insertions(+), 75 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index 469cb83f3..7a0c08f20 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -22,6 +22,7 @@
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.Paths;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
@@ -59,46 +60,44 @@ public class CmsUtil {
private CmsUtil() {
}
+ /**
+ * Loads a signature. If it is a detached signature, attempts to find and load the content.
+ * Verification and counter signing require the content.
+ *
+ * @param signatureFile The signature file.
+ * @param chooser The file chooser to use for choosing the content file.
+ * @return
+ * @throws IOException
+ * @throws CMSException
+ */
public static CMSSignedData loadSignature(File signatureFile, Supplier chooser)
- throws IOException, CryptoException, CMSException {
+ throws IOException, CMSException {
// TODO JW - What if the file cannot be opened?
byte[] signature = Files.readAllBytes(signatureFile.toPath());
if (PemUtil.isPemFormat(signature)) {
PemInfo signaturePem = PemUtil.decode(signature);
- if (signaturePem == null) {
- // TODO JW - What to throw if the signature is detected as PEM, but is not PEM?
- throw new CryptoException("Not a PEM file, but has PEM header!");
- }
- // TODO JW - Do we even want to check the type? Should we just let the BC CMS
- // class bomb out?
- if (!CMS_PEM_TYPE.equals(signaturePem.getType()) && !PKCS7_PEM_TYPE.equals(signaturePem.getType())) {
- // TODO JW - What to throw if the signature is not the correct type?
- throw new CryptoException("PEM is not of type CMS or PKCS7");
+ if (signaturePem != null) {
+ signature = signaturePem.getContent();
}
- signature = signaturePem.getContent();
}
- CMSSignedData signedData = new CMSSignedData(signature);
- if (signedData.isCertificateManagementMessage()) {
- // TODO JW - Display a message indicating that the file doesn't have any
- // signatures.
- return null;
- }
+ CMSSignedData signedData = new CMSSignedData(signature);
if (signedData.isDetachedSignature()) {
CMSProcessableFile content = loadDetachedContent(signatureFile, chooser);
- if (content == null) {
- return null;
+ if (content != null) {
+ signedData = new CMSSignedData(content, signature);
}
- signedData = new CMSSignedData(content, signature);
}
+
return signedData;
}
private static CMSProcessableFile loadDetachedContent(File signatureFile, Supplier chooser)
throws CMSException {
+
// Look for the content file. if not present, prompt for it.
File contentFile = null;
@@ -107,9 +106,8 @@ private static CMSProcessableFile loadDetachedContent(File signatureFile, Suppli
if (extensionIndex > 0) {
// Turn file_name.txt.p7s into file_name.txt
String contentFileName = signatureFile.getAbsolutePath().substring(0, extensionIndex);
- contentFile = new File(contentFileName);
- if (!contentFile.exists()) {
- contentFile = null;
+ if (Files.exists(Paths.get(contentFileName))) {
+ contentFile = new File(contentFileName);
}
}
@@ -130,16 +128,11 @@ private static CMSProcessableFile loadDetachedContent(File signatureFile, Suppli
* @param cms The CMS signature
* @return The PEM'd encoding
*/
- public static String getPem(CMSSignedData cms) throws CryptoException {
+ public static String getPem(CMSSignedData cms) throws CryptoException, IOException {
// Use PKCS7 PEM header since it can be verified using GnuTLS certtool and OpenSSL.
- // GnuTLS certtool cannot verify signatures with the CMS PEM header.
- try {
- PemInfo pemInfo = new PemInfo(PKCS7_PEM_TYPE, null, cms.getEncoded());
- return PemUtil.encode(pemInfo);
- } catch (IOException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
- }
+ // GnuTLS certtool will not verify signatures with the CMS PEM header.
+ PemInfo pemInfo = new PemInfo(PKCS7_PEM_TYPE, null, cms.getEncoded());
+ return PemUtil.encode(pemInfo);
}
/**
@@ -181,9 +174,10 @@ public static Date getSigningTime(SignerInformation signerInfo) throws CryptoExc
* @return The time stamp token as TimeStampToken, if present, else null.
*/
public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) throws CryptoException {
- TimeStampToken timeStampToken = null;
+ TimeStampToken timeStampToken = null;
ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
+
if (timeStamp != null) {
try {
timeStampToken = new TimeStampToken(timeStamp);
@@ -192,6 +186,7 @@ public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) thr
throw new CryptoException(e);
}
}
+
return timeStampToken;
}
@@ -202,9 +197,10 @@ public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) thr
* @return The time stamp token as CMSSignedData, if present, else null.
*/
public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) throws CryptoException {
- CMSSignedData timeStampToken = null;
+ CMSSignedData timeStampToken = null;
ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
+
if (timeStamp != null) {
try {
timeStampToken = new CMSSignedData(timeStamp);
@@ -213,24 +209,29 @@ public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo)
throw new CryptoException(e);
}
}
+
return timeStampToken;
}
private static ContentInfo getTimeStampContentInfo(SignerInformation signerInfo) {
+
AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
+
if (unsignedAttributes != null) {
Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
if (tsTokenAttribute != null) {
return ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]);
}
}
+
return null;
}
public static X509CertificateHolder getSignerCert(CMSSignedData signedData, SignerInformation signer) {
- X509CertificateHolder cert = null;
+ X509CertificateHolder cert = null;
Collection matchedCerts = signedData.getCertificates().getMatches(signer.getSID());
+
if (!matchedCerts.isEmpty()) {
cert = matchedCerts.iterator().next();
}
@@ -238,6 +239,7 @@ public static X509CertificateHolder getSignerCert(CMSSignedData signedData, Sign
if (cert == null) {
// TODO JW - what type of error handling
}
+
return cert;
}
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index dd00c3e06..6ac64a07c 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -118,8 +118,16 @@ protected void doAction() {
String tsaUrl = dSignFile.getTimestampingServerUrl();
CMSSignedData signature = CmsUtil.loadSignature(inputFile, this::chooseContentFile);
- if (signature == null) {
- // TODO JW - identify the error conditions.
+ if (signature.isDetachedSignature()) {
+ // loadSignature tried to find and load the content but could not.
+ // TODO JW - Display a message stating that the content file has to be chosen
+ // for counter signing a detached signature.
+ return;
+ }
+
+ if (signature.isCertificateManagementMessage()) {
+ // TODO JW - Display a message indicating that the file doesn't have any
+ // signatures.
return;
}
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index b369c5ee0..168c8cee5 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -157,6 +157,11 @@ protected void doAction() {
// TODO JW - What about the logic in openSignature?
CMSSignedData signedData = CmsUtil.loadSignature(signatureFile, this::chooseContentFile);
+ if (signedData.isCertificateManagementMessage()) {
+ // TODO JW - Display a message indicating that the file doesn't have any
+ // signatures.
+ return;
+ }
// TODO JW - Add new option for using cacerts for signature verification.
if (preferences.getCaCertsSettings().isImportTrustedCertTrustCheckEnabled()) {
@@ -199,12 +204,19 @@ protected void doAction() {
return;
}
- // TODO JW - Verify the signature using the keystore
- // TODO JW build Store using certs from the truststore. If a cert cannot be found
- // then the signature should not be trusted (even if valid)
- Store certStore = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
- verify(certStore, signers);
+
+ // TODO JW - It is not possible to verify a detached signature. loadSignature
+ // already tried to find and load the detached content. Provide some indication
+ // to the user that the signature cannot be verified, but still allow the signature
+ // info to be viewed.
+ if (!signedData.isDetachedSignature()) {
+ // TODO JW - Verify the signature using the keystore
+ // TODO JW build Store using certs from the truststore. If a cert cannot be found
+ // then the signature should not be trusted (even if valid)
+ Store certStore = signedData.getCertificates();
+ verify(certStore, signers);
+ }
DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat
.format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()),
@@ -212,20 +224,6 @@ protected void doAction() {
dViewSignature.setLocationRelativeTo(frame);
dViewSignature.setVisible(true);
- /*
- Information from GnuTLS certtool
-Signers:
- Signer's issuer DN: O=ICU Medical,CN=ICU Medical Dev Issuing
- Signer's serial: 30bc7fadef7fe924fa77a0f376f31d92b68fa33e
- Signing time: Mon Feb 07 23:07:19 UTC 2022
- Signature Algorithm: RSA-SHA256
- Signed Attributes:
- messageDigest: 0420ed6b93a0a57ff71b075d7628f807db2d6f9a8727557a02b2f6f9ff520eb4caaf
- 1.2.840.113549.1.9.52: 301c300b0609608648016503040201a10d06092a864886f70d01010b0500
- signingTime: 170d3232303230373233303731395a
- contentType: 06092a864886f70d010701
- */
-
kseFrame.updateControls(true);
} catch (Exception ex) {
DError.displayError(frame, ex);
@@ -236,22 +234,6 @@ private void verify(Store certStore, SignerInformationSto
OperatorCreationException, CertificateException, CryptoException, TSPException, TSPValidationException {
boolean verified = false;
for (SignerInformation signer : signers.getSigners()) {
-// AttributeTable signedAttributes = signer.getSignedAttributes();
-// AttributeTable unsignedAttributes = signer.getUnsignedAttributes();
-//
-// if (signedAttributes != null) {
-// Hashtable ht = signedAttributes.toHashtable();
-// for (Object k : ht.keySet()) {
-// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
-// }
-// }
-// if (unsignedAttributes != null) {
-// Hashtable ht = unsignedAttributes.toHashtable();
-// for (Object k : ht.keySet()) {
-// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues());
-// }
-// }
-//
// TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
Collection matchedCerts = certStore.getMatches(signer.getSID());
if (!matchedCerts.isEmpty()) {
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 093363915..6631b404f 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -437,7 +437,6 @@ private void populateDetails() {
if (signingTime != null) {
jtfSigningTime.setText(StringUtils.formatDate(signingTime));
} else {
- // TODO JW - What to do if the signature doesn't have a signing time?
jtfSigningTime.setText("");
}
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index 551c57cca..19386afba 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -603,7 +603,7 @@ SignFileAction.text = Sign File
SignFileAction.tooltip = Sign a file
CounterSignAction.ButtonOK.message = OK
-CounterSignAction.ChooseContent.Title = Choose File
+CounterSignAction.ChooseContent.Title = Choose Content File
CounterSignAction.ChooseContent.button = Select
#CounterSignAction.NoWriteFile.message = Could not write to file ''{0}''.
CounterSignAction.SignJar.Title = Counter Sign Signature
@@ -700,7 +700,7 @@ VerifyCertificateAction.unknownStatus.message = Unknown status {0}
#VerifySignatureAction.ChainSuccessful.message = CHAIN check successful, certificate valid
VerifySignatureAction.SignatureDetailsFile.Title = Signature Details for File ''{0}''
-VerifySignatureAction.ChooseContent.Title = Choose File
+VerifySignatureAction.ChooseContent.Title = Choose Content File
VerifySignatureAction.ChooseContent.button = Select
VerifySignatureAction.ChooseSignature.Title = Choose Signature File
VerifySignatureAction.ChooseSignature.button = Verify
From 2570749093ff3d719084e0a6a2fd5bb1d080667c Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Fri, 13 Dec 2024 00:41:53 -0800
Subject: [PATCH 42/69] Fixed compile error in DViewPem.
---
.../java/org/kse/crypto/signing/CmsSigner.java | 3 +++
.../java/org/kse/crypto/signing/CmsUtil.java | 17 ++++++++++++-----
.../main/java/org/kse/gui/dialogs/DViewPem.java | 2 +-
.../org/kse/crypto/signing/resources.properties | 1 +
4 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index 87bb4261c..088597c7c 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -59,6 +59,9 @@
public class CmsSigner {
private static ResourceBundle res = ResourceBundle.getBundle("org/kse/crypto/signing/resources");
+ private CmsSigner() {
+ }
+
/**
* Signs a file using PKCS #7 CMS.
*
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index 7a0c08f20..a7cc99669 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -27,6 +27,7 @@
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
+import java.util.ResourceBundle;
import java.util.function.Supplier;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
@@ -54,6 +55,8 @@
* Provides utility methods relating to Cryptographic Message Syntax (CMS).
*/
public class CmsUtil {
+ private static ResourceBundle res = ResourceBundle.getBundle("org/kse/crypto/signing/resources");
+
private static final String CMS_PEM_TYPE = "CMS";
private static final String PKCS7_PEM_TYPE = "PKCS7";
@@ -128,11 +131,15 @@ private static CMSProcessableFile loadDetachedContent(File signatureFile, Suppli
* @param cms The CMS signature
* @return The PEM'd encoding
*/
- public static String getPem(CMSSignedData cms) throws CryptoException, IOException {
- // Use PKCS7 PEM header since it can be verified using GnuTLS certtool and OpenSSL.
- // GnuTLS certtool will not verify signatures with the CMS PEM header.
- PemInfo pemInfo = new PemInfo(PKCS7_PEM_TYPE, null, cms.getEncoded());
- return PemUtil.encode(pemInfo);
+ public static String getPem(CMSSignedData cms) throws CryptoException {
+ try {
+ // Use PKCS7 PEM header since it can be verified using GnuTLS certtool and OpenSSL.
+ // GnuTLS certtool will not verify signatures with the CMS PEM header.
+ PemInfo pemInfo = new PemInfo(PKCS7_PEM_TYPE, null, cms.getEncoded());
+ return PemUtil.encode(pemInfo);
+ } catch (IOException e) {
+ throw new CryptoException(res.getString("CmsGetPemFailed.exception.message"), e);
+ }
}
/**
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java b/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java
index dfba6760c..0251138e7 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java
@@ -295,7 +295,7 @@ private String getPemString() throws CryptoException {
} else if (pubKey != null) {
return OpenSslPubUtil.getPem(pubKey);
} else if (cms != null) {
- return CmsUtil.getPem(cms);
+ return CmsUtil.getPem(cms);
}
return "";
diff --git a/kse/src/main/resources/org/kse/crypto/signing/resources.properties b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
index cc98dc9eb..f2c8a3cff 100644
--- a/kse/src/main/resources/org/kse/crypto/signing/resources.properties
+++ b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
@@ -1,6 +1,7 @@
SignatureBlockCreationFailed.exception.message=Block signing failed.
JarDigestSignatureFailed.exception.message=JAR digest signing failed.
+CmsGetPemFailed.exception.message=CMS PEM encoding failed.
CmsSignatureFailed.exception.message=File signing failed.
CmsCounterSignatureFailed.exception.message=Counter signing failed.
NoReadJadCorrupt.exception.message=Could not read JAD file, may be corrupt.
From dfd20c2365edee7fab33cf3cc5f04af227095e2d Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Fri, 13 Dec 2024 11:57:38 -0800
Subject: [PATCH 43/69] Added messages for missing content and no signatures.
---
.../kse/gui/actions/CounterSignAction.java | 25 ++++++++++++-------
.../gui/actions/VerifySignatureAction.java | 17 +++++++------
.../org/kse/gui/actions/resources.properties | 8 +++++-
3 files changed, 33 insertions(+), 17 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
index 6ac64a07c..ce43c0407 100644
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
@@ -27,9 +27,11 @@
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
import org.bouncycastle.cms.CMSSignedData;
import org.kse.crypto.keypair.KeyPairType;
@@ -118,16 +120,23 @@ protected void doAction() {
String tsaUrl = dSignFile.getTimestampingServerUrl();
CMSSignedData signature = CmsUtil.loadSignature(inputFile, this::chooseContentFile);
- if (signature.isDetachedSignature()) {
- // loadSignature tried to find and load the content but could not.
- // TODO JW - Display a message stating that the content file has to be chosen
- // for counter signing a detached signature.
+
+ if (signature.isCertificateManagementMessage()) {
+ JOptionPane.showMessageDialog(frame,
+ MessageFormat.format(res.getString("CounterSignAction.NoSignatures.message"),
+ inputFile.getName()),
+ res.getString("CounterSignAction.CounterSign.Title"), JOptionPane.INFORMATION_MESSAGE);
+
return;
}
- if (signature.isCertificateManagementMessage()) {
- // TODO JW - Display a message indicating that the file doesn't have any
- // signatures.
+ if (signature.getSignedContent() == null) {
+ // loadSignature tried to find and load the content but could not.
+ // TODO JW - Is there a way to counter sign a signature without having the original content? Like providing the original hashes.
+ JOptionPane.showMessageDialog(frame,
+ MessageFormat.format(res.getString("CounterSignAction.NoContent.message"), inputFile.getName()),
+ res.getString("CounterSignAction.CounterSign.Title"), JOptionPane.ERROR_MESSAGE);
+
return;
}
@@ -151,8 +160,6 @@ protected void doAction() {
}
private File chooseContentFile() {
- // TODO JW - This dialog just pops up and the user doesn't know what file to choose.
- // Need to provide more information.
JFileChooser chooser = FileChooserFactory.getNoFileChooser();
chooser.setCurrentDirectory(CurrentDirectory.get());
chooser.setDialogTitle(res.getString("CounterSignAction.ChooseContent.Title"));
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 168c8cee5..da8c53fe4 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -158,8 +158,11 @@ protected void doAction() {
// TODO JW - What about the logic in openSignature?
CMSSignedData signedData = CmsUtil.loadSignature(signatureFile, this::chooseContentFile);
if (signedData.isCertificateManagementMessage()) {
- // TODO JW - Display a message indicating that the file doesn't have any
- // signatures.
+ JOptionPane.showMessageDialog(frame,
+ MessageFormat.format(res.getString("VerifySignatureAction.NoSignatures.message"),
+ signatureFile.getName()),
+ res.getString("VerifySignatureAction.VerifySignature.Title"), JOptionPane.INFORMATION_MESSAGE);
+
return;
}
@@ -206,11 +209,11 @@ protected void doAction() {
SignerInformationStore signers = signedData.getSignerInfos();
- // TODO JW - It is not possible to verify a detached signature. loadSignature
- // already tried to find and load the detached content. Provide some indication
- // to the user that the signature cannot be verified, but still allow the signature
- // info to be viewed.
- if (!signedData.isDetachedSignature()) {
+ // TODO JW - On DViewSignature, provide signature status: verified, unverified, invalid, verified - no trust
+ // Don't verify the signature if there is no signed content, but the signature details
+ // can still be displayed. loadSignature already tried to find and load the detachted
+ // content.
+ if (signedData.getSignedContent() != null) {
// TODO JW - Verify the signature using the keystore
// TODO JW build Store using certs from the truststore. If a cert cannot be found
// then the signature should not be trusted (even if valid)
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index 19386afba..c759f1486 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -603,8 +603,11 @@ SignFileAction.text = Sign File
SignFileAction.tooltip = Sign a file
CounterSignAction.ButtonOK.message = OK
-CounterSignAction.ChooseContent.Title = Choose Content File
+CounterSignAction.ChooseContent.Title = Choose Content File for Counter Signature
CounterSignAction.ChooseContent.button = Select
+CounterSignAction.CounterSign.Title = Counter Sign
+CounterSignAction.NoContent.message = Counter signing requires the original content. The original file counld not be detect, and it was not provided. Counter signing ''{0}'' is not possible.
+CounterSignAction.NoSignatures.message = ''{0}'' does not have any signatures to counter sign
#CounterSignAction.NoWriteFile.message = Could not write to file ''{0}''.
CounterSignAction.SignJar.Title = Counter Sign Signature
CounterSignAction.statusbar = Counter sign a signature using the Key Pair entry
@@ -710,9 +713,12 @@ VerifySignatureAction.ChooseSignature.button = Verify
#VerifySignatureAction.Exception.Title = Could not load KeyStore.
#VerifySignatureAction.FileNotFoundException.message = File not found
#VerifySignatureAction.NoAccessEntry.message = No access entry
+# TODO JW - Better message for no signatures.
+VerifySignatureAction.NoSignatures.message = ''{0}'' does not have any signatures to verify
#VerifySignatureAction.NotTypeKeyStore.message = File not keystore type
#VerifySignatureAction.OcspSuccessful.message = OCSP check successful, certificate valid
#VerifySignatureAction.Verify.Title = Verify ''{0}''
+VerifySignatureAction.VerifySignature.Title = Verify Signature
#VerifySignatureAction.badSerials.message = OCSP Bad serials {0} vs {1}
#VerifySignatureAction.certExpired.message = The certificate is expired it should not be evaluated
#VerifySignatureAction.certStatus.message = OCSP Certificate status {0}
From 025e668ec2d3da9f74faf0eef0dd02f67c49bf6d Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Fri, 13 Dec 2024 13:50:07 -0800
Subject: [PATCH 44/69] Fixed outputFile TODO. Added more context for ASN.1
TODO.
---
kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java | 8 +++-----
kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java | 5 ++++-
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 6631b404f..ebfb9f826 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -255,8 +255,6 @@ private void initComponents(Collection signers) throws Crypto
// jbExtensions.setToolTipText(res.getString("DViewSignature.jbExtensions.tooltip"));
// PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewSignature.jbExtensions.mnemonic").charAt(0));
- // TODO JW - Display PEM and ASN1 buttons for counter signatures? What about for time stamps?
- // TODO JW - Need to make certain these buttons work as expected.
jbPem = new JButton(res.getString("DViewSignature.jbPem.text"));
jbPem.setToolTipText(res.getString("DViewSignature.jbPem.tooltip"));
PlatformUtil.setMnemonic(jbPem, res.getString("DViewSignature.jbPem.mnemonic").charAt(0));
@@ -291,8 +289,8 @@ private void initComponents(Collection signers) throws Crypto
// pane.add(jbExtensions, "");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbCertificates, "spanx, split");
- // TODO JW - Hide PEM and ASN.1 buttons for Counter Signers.
- // Bouncy Castle CMS does not expose the ASN1 encoding for SignerInfos or other aspects of the CMS data.
+ // TODO JW - Hide PEM button for Counter Signers.
+ // Use SignerInformation.toASN1Structure for displaying signer info ASN.1.
pane.add(jbPem, "");
pane.add(jbAsn1, "wrap");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
@@ -595,7 +593,7 @@ private void pemEncodingPressed() {
}
private void asn1DumpPressed() {
- // TODO JW - Should this show only the ASN.1 for the selected signer?
+ // TODO JW - Should this show only the ASN.1 for the selected signer? See signerInfo.toASN1Structure().
try {
DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, signedData);
dViewAsn1Dump.setLocationRelativeTo(this);
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 73975e380..3c83242d5 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -282,7 +282,6 @@ public File getInputFile() {
* @return File output file
*/
public File getOutputFile() {
- // TODO JW - output file is not updated if the user manually changes the value (i.e., didn't use the file chooser)
return outputFile;
}
@@ -348,6 +347,10 @@ private void okPressed() {
detachedSignature = jcbDetachedSignature.isSelected();
outputPem = jcbOutputPem.isSelected();
+ if (!outputFileChosen) {
+ outputFile = new File(jtfOutputFile.getText());
+ }
+
// check add time stamp is selected and assign value
if (jcbAddTimestamp.isSelected()) {
tsaUrl = jcbTimestampServerUrl.getSelectedItem().toString();
From 0e8e60a4dd217b1649cd50d27490fb48a4981608 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 17 Dec 2024 09:52:33 -0800
Subject: [PATCH 45/69] Updated DSignFile for counter signing. Merged counter
signing into SignFileAction.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 8 +-
kse/src/main/java/org/kse/gui/KseFrame.java | 8 -
.../kse/gui/actions/CounterSignAction.java | 177 ------------------
.../org/kse/gui/actions/SignFileAction.java | 53 +++++-
.../org/kse/gui/dialogs/sign/DSignFile.java | 108 +++++++++--
.../org/kse/gui/actions/resources.properties | 21 +--
.../kse/gui/dialogs/sign/resources.properties | 4 +-
7 files changed, 164 insertions(+), 215 deletions(-)
delete mode 100644 kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index a7cc99669..2c51d07fb 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -68,7 +68,7 @@ private CmsUtil() {
* Verification and counter signing require the content.
*
* @param signatureFile The signature file.
- * @param chooser The file chooser to use for choosing the content file.
+ * @param chooser The file chooser to use for choosing the content file.
* @return
* @throws IOException
* @throws CMSException
@@ -114,7 +114,7 @@ private static CMSProcessableFile loadDetachedContent(File signatureFile, Suppli
}
}
- // No file - ask for one (if choose is available)
+ // No file - ask for one (if chooser is available)
if (contentFile == null && chooser != null) {
contentFile = chooser.get();
if (contentFile == null) {
@@ -125,6 +125,10 @@ private static CMSProcessableFile loadDetachedContent(File signatureFile, Suppli
return new CMSProcessableFile(contentFile);
}
+ public static boolean isCmsPemType(PemInfo pemInfo) {
+ return pemInfo != null && (PKCS7_PEM_TYPE.equals(pemInfo.getType()) || CMS_PEM_TYPE.equals(pemInfo.getType()));
+ }
+
/**
* PEM encode a CMS signature.
*
diff --git a/kse/src/main/java/org/kse/gui/KseFrame.java b/kse/src/main/java/org/kse/gui/KseFrame.java
index 8005f4599..5e7333ab9 100644
--- a/kse/src/main/java/org/kse/gui/KseFrame.java
+++ b/kse/src/main/java/org/kse/gui/KseFrame.java
@@ -105,7 +105,6 @@
import org.kse.gui.actions.CopyAction;
import org.kse.gui.actions.CopyKeyPairAction;
import org.kse.gui.actions.CopyTrustedCertificateAction;
-import org.kse.gui.actions.CounterSignAction;
import org.kse.gui.actions.CutAction;
import org.kse.gui.actions.CutKeyPairAction;
import org.kse.gui.actions.CutTrustedCertificateAction;
@@ -374,7 +373,6 @@ public final class KseFrame implements StatusBar {
private JMenuItem jmiKeyPairSignJwt;
private JMenuItem jmiKeyPairSignNewKeyPair;
private JMenuItem jmiKeyPairSignFile;
- private JMenuItem jmiKeyPairSignSignature;
private JMenuItem jmiKeyPairUnlock;
private JMenuItem jmiKeyPairSetPassword;
private JMenuItem jmiKeyPairDelete;
@@ -512,7 +510,6 @@ public final class KseFrame implements StatusBar {
private final SignCrlAction signCrlAction = new SignCrlAction(this);
private final SignJwtAction signJwtAction = new SignJwtAction(this);
private final SignFileAction signFileAction = new SignFileAction(this);
- private final CounterSignAction counterSignAction = new CounterSignAction(this);
private final SignNewKeyPairAction signNewKeyPairAction = new SignNewKeyPairAction(this);
private final UnlockKeyPairAction unlockKeyPairAction = new UnlockKeyPairAction(this);
private final SetKeyPairPasswordAction setKeyPairPasswordAction = new SetKeyPairPasswordAction(this);
@@ -2048,10 +2045,6 @@ private void initKeyStoreEntryPopupMenus() {
jmiKeyPairSignFile.setToolTipText(null);
new StatusBarChangeHandler(jmiKeyPairSignFile, (String) signFileAction.getValue(Action.LONG_DESCRIPTION), this);
- jmiKeyPairSignSignature = new JMenuItem(counterSignAction);
- jmiKeyPairSignSignature.setToolTipText(null);
- new StatusBarChangeHandler(jmiKeyPairSignSignature, (String) counterSignAction.getValue(Action.LONG_DESCRIPTION), this);
-
jmiKeyPairSignNewKeyPair = new JMenuItem(signNewKeyPairAction);
jmiKeyPairSignNewKeyPair.setToolTipText(null);
new StatusBarChangeHandler(jmiKeyPairSignNewKeyPair,
@@ -2107,7 +2100,6 @@ private void initKeyStoreEntryPopupMenus() {
jmKeyPairSign.add(jmiKeyPairSignCrl);
jmKeyPairSign.add(jmiKeyPairSignJwt);
jmKeyPairSign.add(jmiKeyPairSignFile);
- jmKeyPairSign.add(jmiKeyPairSignSignature);
jpmKeyPair.addSeparator();
jpmKeyPair.add(jmiKeyPairUnlock);
jpmKeyPair.add(jmiKeyPairSetPassword);
diff --git a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java b/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
deleted file mode 100644
index ce43c0407..000000000
--- a/kse/src/main/java/org/kse/gui/actions/CounterSignAction.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2004 - 2013 Wayne Grant
- * 2013 - 2024 Kai Kramer
- *
- * This file is part of KeyStore Explorer.
- *
- * KeyStore Explorer is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * KeyStore Explorer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with KeyStore Explorer. If not, see .
- */
-package org.kse.gui.actions;
-
-import java.awt.Toolkit;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.Provider;
-import java.security.cert.X509Certificate;
-import java.text.MessageFormat;
-
-import javax.swing.ImageIcon;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-
-import org.bouncycastle.cms.CMSSignedData;
-import org.kse.crypto.keypair.KeyPairType;
-import org.kse.crypto.keypair.KeyPairUtil;
-import org.kse.crypto.signing.CmsSigner;
-import org.kse.crypto.signing.CmsUtil;
-import org.kse.crypto.signing.SignatureType;
-import org.kse.crypto.x509.X509CertUtil;
-import org.kse.gui.CurrentDirectory;
-import org.kse.gui.FileChooserFactory;
-import org.kse.gui.KseFrame;
-import org.kse.gui.dialogs.sign.DSignFile;
-import org.kse.gui.error.DError;
-import org.kse.gui.passwordmanager.Password;
-import org.kse.utilities.history.KeyStoreHistory;
-import org.kse.utilities.history.KeyStoreState;
-
-/**
- * Action to counter sign a PKCS #7 signature using the selected key pair entry.
- */
-public class CounterSignAction extends KeyStoreExplorerAction {
- private static final long serialVersionUID = 6227240459189308322L;
-
- /**
- * Construct action.
- *
- * @param kseFrame KeyStore Explorer frame
- */
- public CounterSignAction(KseFrame kseFrame) {
- super(kseFrame);
-
- putValue(LONG_DESCRIPTION, res.getString("CounterSignAction.statusbar"));
- putValue(NAME, res.getString("CounterSignAction.text"));
- putValue(SHORT_DESCRIPTION, res.getString("CounterSignAction.tooltip"));
- putValue(SMALL_ICON,
- new ImageIcon(Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/signsignature.png"))));
- }
-
- /**
- * Do action.
- */
- @Override
- protected void doAction() {
- try {
- KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
- KeyStoreState currentState = history.getCurrentState();
-
- String alias = kseFrame.getSelectedEntryAlias();
-
- Password password = getEntryPassword(alias, currentState);
-
- if (password == null) {
- return;
- }
-
- // set the keystore state
- KeyStore keyStore = currentState.getKeyStore();
-
- // set the provider history
- Provider provider = history.getExplicitProvider();
-
- // set the private key
- PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password.toCharArray());
-
- // set the certificate
- X509Certificate[] certs = X509CertUtil
- .orderX509CertChain(X509CertUtil.convertCertificates(keyStore.getCertificateChain(alias)));
-
- // set the key pair type
- KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey);
-
- // get the file, signatures, and time stamp
- DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, true);
- dSignFile.setLocationRelativeTo(frame);
- dSignFile.setVisible(true);
-
- // check if file sign dialog was successful
- if (!dSignFile.isSuccessful()) {
- return;
- }
-
- boolean detachedSignature = dSignFile.isDetachedSignature();
- SignatureType signatureType = dSignFile.getSignatureType();
- File inputFile = dSignFile.getInputFile();
- File outputFile = dSignFile.getOutputFile();
- String tsaUrl = dSignFile.getTimestampingServerUrl();
-
- CMSSignedData signature = CmsUtil.loadSignature(inputFile, this::chooseContentFile);
-
- if (signature.isCertificateManagementMessage()) {
- JOptionPane.showMessageDialog(frame,
- MessageFormat.format(res.getString("CounterSignAction.NoSignatures.message"),
- inputFile.getName()),
- res.getString("CounterSignAction.CounterSign.Title"), JOptionPane.INFORMATION_MESSAGE);
-
- return;
- }
-
- if (signature.getSignedContent() == null) {
- // loadSignature tried to find and load the content but could not.
- // TODO JW - Is there a way to counter sign a signature without having the original content? Like providing the original hashes.
- JOptionPane.showMessageDialog(frame,
- MessageFormat.format(res.getString("CounterSignAction.NoContent.message"), inputFile.getName()),
- res.getString("CounterSignAction.CounterSign.Title"), JOptionPane.ERROR_MESSAGE);
-
- return;
- }
-
- CMSSignedData signedData = CmsSigner.counterSign(signature, privateKey, certs, detachedSignature,
- signatureType, tsaUrl, provider);
-
- byte[] encoded;
- if (!dSignFile.isOutputPem()) {
- encoded = signedData.getEncoded();
- } else {
- encoded = CmsUtil.getPem(signedData).getBytes();
- }
-
- try (OutputStream os = new FileOutputStream(outputFile)) {
- os.write(encoded);
- }
-
- } catch (Exception ex) {
- DError.displayError(frame, ex);
- }
- }
-
- private File chooseContentFile() {
- JFileChooser chooser = FileChooserFactory.getNoFileChooser();
- chooser.setCurrentDirectory(CurrentDirectory.get());
- chooser.setDialogTitle(res.getString("CounterSignAction.ChooseContent.Title"));
- chooser.setMultiSelectionEnabled(false);
- chooser.setApproveButtonText(res.getString("CounterSignAction.ChooseContent.button"));
-
- int rtnValue = chooser.showOpenDialog(frame);
- if (rtnValue == JFileChooser.APPROVE_OPTION) {
- File importFile = chooser.getSelectedFile();
- CurrentDirectory.updateForFile(importFile);
- return importFile;
- }
- return null;
- }
-}
diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
index 20f431a8d..7cfa88dea 100644
--- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java
@@ -27,8 +27,11 @@
import java.security.PrivateKey;
import java.security.Provider;
import java.security.cert.X509Certificate;
+import java.text.MessageFormat;
import javax.swing.ImageIcon;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
import org.bouncycastle.cms.CMSSignedData;
import org.kse.crypto.keypair.KeyPairType;
@@ -37,6 +40,8 @@
import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X509CertUtil;
+import org.kse.gui.CurrentDirectory;
+import org.kse.gui.FileChooserFactory;
import org.kse.gui.KseFrame;
import org.kse.gui.dialogs.sign.DSignFile;
import org.kse.gui.error.DError;
@@ -114,8 +119,36 @@ protected void doAction() {
File outputFile = dSignFile.getOutputFile();
String tsaUrl = dSignFile.getTimestampingServerUrl();
- CMSSignedData signedData = CmsSigner.sign(inputFile, privateKey, certs, detachedSignature, signatureType,
- tsaUrl, provider);
+ CMSSignedData signedData;
+ if (!dSignFile.isCounterSign()) {
+ signedData = CmsSigner.sign(inputFile, privateKey, certs, detachedSignature, signatureType, tsaUrl,
+ provider);
+ } else {
+ CMSSignedData signature = CmsUtil.loadSignature(inputFile, this::chooseContentFile);
+
+ if (signature.isCertificateManagementMessage()) {
+ JOptionPane.showMessageDialog(frame,
+ MessageFormat.format(res.getString("SignFileAction.NoSignatures.message"),
+ inputFile.getName()),
+ res.getString("SignFileAction.CounterSign.Title"), JOptionPane.INFORMATION_MESSAGE);
+
+ return;
+ }
+
+ if (signature.getSignedContent() == null) {
+ // loadSignature tried to find and load the content but could not.
+ // TODO JW - Is there a way to counter sign a signature without having the original content? Like providing the original hashes.
+ JOptionPane.showMessageDialog(frame,
+ MessageFormat.format(res.getString("SignFileAction.NoContent.message"),
+ inputFile.getName()),
+ res.getString("SignFileAction.CounterSign.Title"), JOptionPane.ERROR_MESSAGE);
+
+ return;
+ }
+
+ signedData = CmsSigner.counterSign(signature, privateKey, certs, detachedSignature, signatureType,
+ tsaUrl, provider);
+ }
byte[] encoded;
if (!dSignFile.isOutputPem()) {
@@ -132,4 +165,20 @@ protected void doAction() {
DError.displayError(frame, ex);
}
}
+
+ private File chooseContentFile() {
+ JFileChooser chooser = FileChooserFactory.getNoFileChooser();
+ chooser.setCurrentDirectory(CurrentDirectory.get());
+ chooser.setDialogTitle(res.getString("SignFileAction.ChooseContent.Title"));
+ chooser.setMultiSelectionEnabled(false);
+ chooser.setApproveButtonText(res.getString("SignFileAction.ChooseContent.button"));
+
+ int rtnValue = chooser.showOpenDialog(frame);
+ if (rtnValue == JFileChooser.APPROVE_OPTION) {
+ File importFile = chooser.getSelectedFile();
+ CurrentDirectory.updateForFile(importFile);
+ return importFile;
+ }
+ return null;
+ }
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 3c83242d5..7afa5509e 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -26,6 +26,8 @@
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
@@ -47,8 +49,11 @@
import javax.swing.JTextField;
import javax.swing.KeyStroke;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedData;
import org.kse.KSE;
import org.kse.crypto.keypair.KeyPairType;
+import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.signing.SignatureType;
import org.kse.gui.CurrentDirectory;
import org.kse.gui.CursorUtil;
@@ -58,6 +63,8 @@
import org.kse.gui.dialogs.DialogHelper;
import org.kse.utilities.DialogViewer;
import org.kse.utilities.net.URLs;
+import org.kse.utilities.pem.PemInfo;
+import org.kse.utilities.pem.PemUtil;
import net.miginfocom.swing.MigLayout;
@@ -77,6 +84,8 @@ public class DSignFile extends JEscDialog {
private JLabel jlOutputFile;
private JTextField jtfOutputFile;
private JButton jbOutputFileBrowse;
+ private JLabel jlCounterSign;
+ private JCheckBox jcbCounterSign;
private JLabel jlDetachedSignature;
private JCheckBox jcbDetachedSignature;
private JLabel jlOutputPem;
@@ -96,9 +105,11 @@ public class DSignFile extends JEscDialog {
private KeyPairType signKeyPairType;
private File inputFile;
private File outputFile;
+ private CMSSignedData inputSignature;
private boolean outputFileChosen;
private boolean detachedSignature = true;
private boolean outputPem;
+ private boolean enableCounterSign;
private SignatureType signatureType;
private String tsaUrl;
private boolean successStatus = true;
@@ -129,6 +140,8 @@ public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPa
* @param isCounterSign True for counter signing. False for signing.
*/
private void initComponents(boolean isCounterSign) {
+ resetToDefault();
+
jlInputFile = new JLabel(res.getString("DSignFile.jlInputFile.text"));
jtfInputFile = new JTextField(30);
jtfInputFile.setCaretPosition(0);
@@ -147,14 +160,21 @@ private void initComponents(boolean isCounterSign) {
PlatformUtil.setMnemonic(jbOutputFileBrowse, res.getString("DSignFile.jbOutputFileBrowse.mnemonic").charAt(0));
jbOutputFileBrowse.setToolTipText(res.getString("DSignFile.jbOutputFileBrowse.tooltip"));
+ jlCounterSign = new JLabel(res.getString("DSignFile.jlCounterSign.text"));
+ jlCounterSign.setEnabled(enableCounterSign);
+ jcbCounterSign = new JCheckBox();
+ jcbCounterSign.setSelected(enableCounterSign);
+ jcbCounterSign.setEnabled(enableCounterSign);
+ jcbCounterSign.setToolTipText(res.getString("DSignFile.jcbCounterSign.tooltip"));
+
jlDetachedSignature = new JLabel(res.getString("DSignFile.jlDetachedSignature.text"));
jcbDetachedSignature = new JCheckBox();
- jcbDetachedSignature.setSelected(true);
+ jcbDetachedSignature.setSelected(detachedSignature);
jcbDetachedSignature.setToolTipText(res.getString("DSignFile.jcbDetachedSignature.tooltip"));
jlOutputPem = new JLabel(res.getString("DSignFile.jlOutputPem.text"));
jcbOutputPem = new JCheckBox();
- jcbOutputPem.setSelected(false);
+ jcbOutputPem.setSelected(outputPem);
jcbOutputPem.setToolTipText(res.getString("DSignFile.jcbOutputPem.tooltip"));
jlSignatureAlgorithm = new JLabel(res.getString("DSignFile.jlSignatureAlgorithm.text"));
@@ -192,6 +212,8 @@ private void initComponents(boolean isCounterSign) {
pane.add(jlOutputFile, "");
pane.add(jtfOutputFile, "");
pane.add(jbOutputFileBrowse, "wrap");
+ pane.add(jlCounterSign, "");
+ pane.add(jcbCounterSign, "wrap");
pane.add(jlDetachedSignature, "");
pane.add(jcbDetachedSignature, "wrap");
pane.add(jlOutputPem, "");
@@ -224,8 +246,9 @@ private void initComponents(boolean isCounterSign) {
}
});
- jcbAddTimestamp.addItemListener(evt -> enableDisableTsaElements());
+ jcbCounterSign.addActionListener(evt -> counterSignStateChange());
jcbDetachedSignature.addItemListener(evt -> detachedSignatureStateChange());
+ jcbAddTimestamp.addItemListener(evt -> enableDisableTsaElements());
jbOK.addActionListener(evt -> okPressed());
@@ -256,10 +279,20 @@ public void windowClosing(WindowEvent evt) {
}
/**
- * This function enables and disables elements in the dialog
+ * This function enables and disables elements in the dialog.
*/
- protected void enableDisableTsaElements() {
- jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
+ protected void enableDisableControls() {
+ jlCounterSign.setEnabled(enableCounterSign);
+ jcbCounterSign.setEnabled(enableCounterSign);
+ jcbCounterSign.setSelected(enableCounterSign);
+
+ jcbDetachedSignature.setSelected(detachedSignature);
+ jcbOutputPem.setSelected(outputPem);
+ updateOutputFile();
+ }
+
+ protected void counterSignStateChange() {
+ updateOutputFile();
}
protected void detachedSignatureStateChange() {
@@ -267,6 +300,13 @@ protected void detachedSignatureStateChange() {
updateOutputFile();
}
+ /**
+ * This function enables and disables the TSA elements in the dialog
+ */
+ protected void enableDisableTsaElements() {
+ jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
+ }
+
/**
* Get chosen input file.
*
@@ -303,6 +343,16 @@ public boolean isOutputPem() {
return outputPem;
}
+ /**
+ * Gets the counter sign setting.
+ *
+ * @return booleans output counterSign setting
+ */
+ public boolean isCounterSign() {
+ // TODO JW - Use a field for tracking this state?
+ return jcbCounterSign.isSelected();
+ }
+
/**
* Get chosen signature type.
*
@@ -343,6 +393,7 @@ private void okPressed() {
return;
}
+ // TODO JW - Review these assignments to see if they're really necessary. I think they are.
signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
detachedSignature = jcbDetachedSignature.isSelected();
outputPem = jcbOutputPem.isSelected();
@@ -382,13 +433,43 @@ private void inputFileBrowsePressed(boolean isCounterSign) {
CurrentDirectory.updateForFile(chosenFile);
inputFile = chosenFile;
+ // reset to defaults
+ resetToDefault();
+ try {
+ if (inputFile.exists()) {
+ byte[] signature = Files.readAllBytes(inputFile.toPath());
+
+ if (PemUtil.isPemFormat(signature)) {
+ PemInfo signaturePem = PemUtil.decode(signature);
+ if (signaturePem != null) {
+ signature = signaturePem.getContent();
+ }
+ outputPem = CmsUtil.isCmsPemType(signaturePem);
+ }
+
+ inputSignature = new CMSSignedData(signature);
+ enableCounterSign = !inputSignature.isCertificateManagementMessage();
+ detachedSignature = inputSignature.isDetachedSignature();
+ }
+ } catch (IOException | CMSException e) {
+ // Eat the exception.
+ // For IOException - don't know what failed, assume the file is not PKCS#7.
+ // For CMSException - know for certain that the file is not PKCS#7.
+ }
+
jtfInputFile.setText(inputFile.getAbsolutePath());
jtfInputFile.setCaretPosition(0);
-
- updateOutputFile();
+ enableDisableControls();
}
}
+ private void resetToDefault() {
+ inputSignature = null;
+ enableCounterSign = false;
+ detachedSignature = true;
+ outputPem = false;
+ }
+
/**
* Get input file
*/
@@ -410,10 +491,15 @@ private void outputFileBrowsePressed() {
}
}
- private void updateOutputFile()
- {
+ private void updateOutputFile() {
if (!outputFileChosen) {
- outputFile = new File(inputFile.getAbsolutePath() + (detachedSignature ? ".p7s" : ".p7m"));
+ String addedExtension = "";
+
+ if (!enableCounterSign || !jcbCounterSign.isSelected()) {
+ addedExtension = detachedSignature ? ".p7s" : ".p7m";
+ }
+ // TODO JW - Need to prompt if overwrite is ok.
+ outputFile = new File(inputFile.getAbsolutePath() + addedExtension);
}
jtfOutputFile.setText(outputFile.getAbsolutePath());
diff --git a/kse/src/main/resources/org/kse/gui/actions/resources.properties b/kse/src/main/resources/org/kse/gui/actions/resources.properties
index c759f1486..5fa96b2c1 100644
--- a/kse/src/main/resources/org/kse/gui/actions/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties
@@ -593,26 +593,19 @@ SignJarAction.text = Sign JAR
SignJarAction.tooltip = Sign a JAR
SignFileAction.ButtonOK.message = OK
+SignFileAction.ChooseContent.Title = Choose Content File for Counter Signature
+SignFileAction.ChooseContent.button = Select
+SignFileAction.CounterSign.Title = Counter Sign
#SignFileAction.ButtonView.message = View
+SignFileAction.NoContent.message = Counter signing requires the original content. The original file could not be detect, and it was not provided. Counter signing ''{0}'' is not possible.
+SignFileAction.NoSignatures.message = ''{0}'' does not have any signatures to counter sign
SignFileAction.NoWriteFile.message = Could not write to file ''{0}''.
SignFileAction.SignJar.Title = Sign File
#SignFileAction.SignJarError.message = {0} of {1} file(s) have an error.
#SignFileAction.SignJarSuccessful.message = {0} JAR file(s) successfully signed.
-SignFileAction.statusbar = Sign a file using the Key Pair entry
+SignFileAction.statusbar = Sign a file or counter sign a PKCS#7 signature using the Key Pair entry
SignFileAction.text = Sign File
-SignFileAction.tooltip = Sign a file
-
-CounterSignAction.ButtonOK.message = OK
-CounterSignAction.ChooseContent.Title = Choose Content File for Counter Signature
-CounterSignAction.ChooseContent.button = Select
-CounterSignAction.CounterSign.Title = Counter Sign
-CounterSignAction.NoContent.message = Counter signing requires the original content. The original file counld not be detect, and it was not provided. Counter signing ''{0}'' is not possible.
-CounterSignAction.NoSignatures.message = ''{0}'' does not have any signatures to counter sign
-#CounterSignAction.NoWriteFile.message = Could not write to file ''{0}''.
-CounterSignAction.SignJar.Title = Counter Sign Signature
-CounterSignAction.statusbar = Counter sign a signature using the Key Pair entry
-CounterSignAction.text = Counter Sign Signature
-CounterSignAction.tooltip = Counter sign a signature
+SignFileAction.tooltip = Sign a file or counter sign a PKCS#7 signature
SignMidletAction.ReqRsaKeyPairMidletSigning.message = Only RSA key pairs can be used for MIDlet signing.
SignMidletAction.SignMidlet.Title = Sign MIDlet
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 4828c0300..10ab11fd6 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -258,12 +258,14 @@ DSignFile.jbOutputFileBrowse.mnemonic = O
DSignFile.jbOutputFileBrowse.text = Browse
DSignFile.jbOutputFileBrowse.tooltip = Browse to Output File
DSignFile.jcbAddTimestamp.tooltip = Include a time stamp counter signature
+DSignFile.jcbCounterSign.tooltip = Counter sign the input file.
DSignFile.jcbDetachedSignature.tooltip = Generate a detached signature or include the file with the signature
DSignFile.jcbOutputPem.tooltip = Output the signature in PEM encoding or DER encoding
DSignFile.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign file
DSignFile.jcbTimestampServerUrl.tooltip = Location of the Time Stamping Authority (TSA)
DSignFile.jlAddTimestamp.text = Add Time stamp:
-DSignFile.jlDetachedSignature.text = Generate Detached Signature:
+DSignFile.jlCounterSign.text = Counter Sign
+DSignFile.jlDetachedSignature.text = Detached Signature:
DSignFile.jlInputFile.text = Input File:
DSignFile.jlOutputFile.text = Output File:
DSignFile.jlOutputPem.text = PEM:
From 5947caa91f2a483ace1906e6d003796783b72315 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 17 Dec 2024 23:29:41 -0800
Subject: [PATCH 46/69] Wrapped up DSignDialog.
---
.../org/kse/crypto/signing/CmsSigner.java | 3 +-
.../org/kse/gui/dialogs/sign/DSignFile.java | 121 ++++++++++--------
.../kse/gui/dialogs/sign/resources.properties | 3 +-
3 files changed, 74 insertions(+), 53 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index 088597c7c..5dd51b26e 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -137,6 +137,8 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
counterSigners = addTimestamp(tsaUrl, counterSigners, signatureType.digestType());
}
+ // This does not replace existing counter signers. It creates a new counter signer vector
+ // if it does not already exist, and then it adds the counter signer.
signer = SignerInformation.addCounterSigners(signer, counterSigners);
generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
@@ -146,7 +148,6 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri
CMSSignedData counterSignedData = generator.generate(signedData.getSignedContent(), !detachedSignature);
-
return counterSignedData;
} catch (CertificateEncodingException | OperatorCreationException | CMSException | IOException e) {
throw new CryptoException(res.getString("CmsCounterSignatureFailed.exception.message"), e);
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index 7afa5509e..fff69004e 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -32,6 +32,7 @@
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.SecureRandom;
+import java.text.MessageFormat;
import java.util.ResourceBundle;
import javax.swing.AbstractAction;
@@ -100,16 +101,16 @@ public class DSignFile extends JEscDialog {
private JButton jbOK;
private JButton jbCancel;
- // TODO store settings (e.g., signature algorithm, TS setting and URL) in preferences.
+ // TODO JW Store settings (e.g., signature algorithm, TS setting and URL) in preferences.
private PrivateKey signPrivateKey;
private KeyPairType signKeyPairType;
private File inputFile;
private File outputFile;
private CMSSignedData inputSignature;
private boolean outputFileChosen;
+ private boolean enableCounterSign;
private boolean detachedSignature = true;
private boolean outputPem;
- private boolean enableCounterSign;
private SignatureType signatureType;
private String tsaUrl;
private boolean successStatus = true;
@@ -208,7 +209,6 @@ private void initComponents(boolean isCounterSign) {
pane.add(jlInputFile, "");
pane.add(jtfInputFile, "");
pane.add(jbInputFileBrowse, "wrap");
- // TODO JW - Hide the output file for counter signing (re-use the same cert file). Prompt for overwriting.
pane.add(jlOutputFile, "");
pane.add(jtfOutputFile, "");
pane.add(jbOutputFileBrowse, "wrap");
@@ -246,7 +246,7 @@ private void initComponents(boolean isCounterSign) {
}
});
- jcbCounterSign.addActionListener(evt -> counterSignStateChange());
+ jcbCounterSign.addActionListener(evt -> updateOutputFile());
jcbDetachedSignature.addItemListener(evt -> detachedSignatureStateChange());
jcbAddTimestamp.addItemListener(evt -> enableDisableTsaElements());
@@ -281,7 +281,7 @@ public void windowClosing(WindowEvent evt) {
/**
* This function enables and disables elements in the dialog.
*/
- protected void enableDisableControls() {
+ protected void updateControls() {
jlCounterSign.setEnabled(enableCounterSign);
jcbCounterSign.setEnabled(enableCounterSign);
jcbCounterSign.setSelected(enableCounterSign);
@@ -291,10 +291,6 @@ protected void enableDisableControls() {
updateOutputFile();
}
- protected void counterSignStateChange() {
- updateOutputFile();
- }
-
protected void detachedSignatureStateChange() {
detachedSignature = jcbDetachedSignature.isSelected();
updateOutputFile();
@@ -307,6 +303,13 @@ protected void enableDisableTsaElements() {
jcbTimestampServerUrl.setEnabled(jcbAddTimestamp.isSelected());
}
+ private void resetToDefault() {
+ inputSignature = null;
+ enableCounterSign = false;
+ detachedSignature = true;
+ outputPem = false;
+ }
+
/**
* Get chosen input file.
*
@@ -349,7 +352,6 @@ public boolean isOutputPem() {
* @return booleans output counterSign setting
*/
public boolean isCounterSign() {
- // TODO JW - Use a field for tracking this state?
return jcbCounterSign.isSelected();
}
@@ -393,15 +395,35 @@ private void okPressed() {
return;
}
- // TODO JW - Review these assignments to see if they're really necessary. I think they are.
- signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
- detachedSignature = jcbDetachedSignature.isSelected();
- outputPem = jcbOutputPem.isSelected();
-
if (!outputFileChosen) {
outputFile = new File(jtfOutputFile.getText());
}
+ // warn if overwriting a file when not counter signing
+ if (outputFile.exists() && (!enableCounterSign || !jcbCounterSign.isSelected())) {
+ int option = JOptionPane.showConfirmDialog(this,
+ MessageFormat.format(res.getString("DSignFile.OverWriteOutput.message"), outputFile.getName()),
+ getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+
+ if (option == JOptionPane.NO_OPTION) {
+ return;
+ }
+ }
+
+ // TODO JW Removed if not used.
+// if (inputFile.equals(outputFile)) {
+// int option = JOptionPane.showConfirmDialog(this, res.getString("DSignFile.OverWriteInput.message"),
+// getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
+//
+// if (option == JOptionPane.NO_OPTION) {
+// return;
+// }
+// }
+
+ detachedSignature = jcbDetachedSignature.isSelected();
+ outputPem = jcbOutputPem.isSelected();
+ signatureType = (SignatureType) jcbSignatureAlgorithm.getSelectedItem();
+
// check add time stamp is selected and assign value
if (jcbAddTimestamp.isSelected()) {
tsaUrl = jcbTimestampServerUrl.getSelectedItem().toString();
@@ -433,45 +455,12 @@ private void inputFileBrowsePressed(boolean isCounterSign) {
CurrentDirectory.updateForFile(chosenFile);
inputFile = chosenFile;
- // reset to defaults
- resetToDefault();
- try {
- if (inputFile.exists()) {
- byte[] signature = Files.readAllBytes(inputFile.toPath());
-
- if (PemUtil.isPemFormat(signature)) {
- PemInfo signaturePem = PemUtil.decode(signature);
- if (signaturePem != null) {
- signature = signaturePem.getContent();
- }
- outputPem = CmsUtil.isCmsPemType(signaturePem);
- }
-
- inputSignature = new CMSSignedData(signature);
- enableCounterSign = !inputSignature.isCertificateManagementMessage();
- detachedSignature = inputSignature.isDetachedSignature();
- }
- } catch (IOException | CMSException e) {
- // Eat the exception.
- // For IOException - don't know what failed, assume the file is not PKCS#7.
- // For CMSException - know for certain that the file is not PKCS#7.
- }
-
- jtfInputFile.setText(inputFile.getAbsolutePath());
- jtfInputFile.setCaretPosition(0);
- enableDisableControls();
+ inputFileUpdated();
}
}
- private void resetToDefault() {
- inputSignature = null;
- enableCounterSign = false;
- detachedSignature = true;
- outputPem = false;
- }
-
/**
- * Get input file
+ * Get output file
*/
private void outputFileBrowsePressed() {
JFileChooser chooser = FileChooserFactory.getSignatureFileChooser();
@@ -491,6 +480,37 @@ private void outputFileBrowsePressed() {
}
}
+ private void inputFileUpdated() {
+ if (inputFile.exists()) {
+ // reset to defaults
+ resetToDefault();
+
+ try {
+ byte[] signature = Files.readAllBytes(inputFile.toPath());
+
+ if (PemUtil.isPemFormat(signature)) {
+ PemInfo signaturePem = PemUtil.decode(signature);
+ if (signaturePem != null) {
+ signature = signaturePem.getContent();
+ }
+ outputPem = CmsUtil.isCmsPemType(signaturePem);
+ }
+
+ inputSignature = new CMSSignedData(signature);
+ enableCounterSign = !inputSignature.isCertificateManagementMessage();
+ detachedSignature = inputSignature.isDetachedSignature();
+ } catch (IOException | CMSException e) {
+ // Eat the exception.
+ // For IOException - don't know what failed, assume the file is not PKCS#7.
+ // For CMSException - know for certain that the file is not PKCS#7.
+ }
+ }
+
+ jtfInputFile.setText(inputFile.getAbsolutePath());
+ jtfInputFile.setCaretPosition(0);
+ updateControls();
+ }
+
private void updateOutputFile() {
if (!outputFileChosen) {
String addedExtension = "";
@@ -498,7 +518,6 @@ private void updateOutputFile() {
if (!enableCounterSign || !jcbCounterSign.isSelected()) {
addedExtension = detachedSignature ? ".p7s" : ".p7m";
}
- // TODO JW - Need to prompt if overwrite is ok.
outputFile = new File(inputFile.getAbsolutePath() + addedExtension);
}
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
index 10ab11fd6..03e696297 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/sign/resources.properties
@@ -241,12 +241,13 @@ DSignFile.ChooseOutputFile.Title = Choose Output File
DSignFile.CounterSign.Title = Counter Sign Signature
DSignFile.EmptyTimestampUrl.message = Time stamping is selected, but TSA URL is empty.
DSignFile.InputFileChooser.button = Choose
-# TODO JW - is this used?
DSignFile.InputFileRequired.message = Path to Input File required.
DSignFile.NoOpenFile.Problem = Could not open Input File ''{0}''.
DSignFile.OutputFileChooser.button = Choose
# TODO JW - is this used?
DSignFile.OutputFileRequired.message = Path to Output File required.
+#DSignFile.OverWriteInput.message = Overwrite input signature file with counter signature?
+DSignFile.OverWriteOutput.message = The file ''{0}'' already exists. Overwrite it with a new signature?
DSignFile.ProblemOpeningFile.Title = Problem Opening Input File
DSignFile.Sign.Title = Sign File
DSignFile.jbCancel.text = Cancel
From ca3f38f10cef233dd22e01839488bfc617604d4f Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Wed, 18 Dec 2024 11:21:12 -0800
Subject: [PATCH 47/69] Resolved some TODOs in CmsUtil.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 53 +++----------------
.../gui/actions/VerifySignatureAction.java | 10 ++--
.../org/kse/gui/dialogs/DViewSignature.java | 33 +++++++++++-
.../org/kse/gui/dialogs/sign/DSignFile.java | 4 +-
4 files changed, 46 insertions(+), 54 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index 2c51d07fb..d3f1e5a57 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -43,8 +43,6 @@
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.tsp.TSPException;
-import org.bouncycastle.tsp.TimeStampToken;
import org.kse.crypto.CryptoException;
import org.kse.crypto.x509.X500NameUtils;
import org.kse.utilities.StringUtils;
@@ -169,8 +167,8 @@ public static Date getSigningTime(SignerInformation signerInfo) throws CryptoExc
signingTime = ((ASN1GeneralizedTime) o).getDate();
}
} catch (ParseException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
+ // Users are not going to know what to do about invalid ASN.1 date structures.
+ // So ignore the exception.
}
}
}
@@ -179,52 +177,13 @@ public static Date getSigningTime(SignerInformation signerInfo) throws CryptoExc
}
/**
- * Extracts the time stamp token, if present, from the signature's unsigned attributes.
+ * Extracts the time stamp token, if present, from the signature's unsigned
+ * attributes.
*
* @param signerInfo The signer information.
- * @return The time stamp token as TimeStampToken, if present, else null.
+ * @return The time stamp token as ContentInfo, if present, else null.
*/
- public static TimeStampToken getTimeStampToken(SignerInformation signerInfo) throws CryptoException {
-
- TimeStampToken timeStampToken = null;
- ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
-
- if (timeStamp != null) {
- try {
- timeStampToken = new TimeStampToken(timeStamp);
- } catch (TSPException | IOException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
- }
- }
-
- return timeStampToken;
- }
-
- /**
- * Extracts the time stamp token, if present, from the signature's unsigned attributes.
- *
- * @param signerInfo The signer information.
- * @return The time stamp token as CMSSignedData, if present, else null.
- */
- public static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) throws CryptoException {
-
- CMSSignedData timeStampToken = null;
- ContentInfo timeStamp = getTimeStampContentInfo(signerInfo);
-
- if (timeStamp != null) {
- try {
- timeStampToken = new CMSSignedData(timeStamp);
- } catch (CMSException e) {
- // TODO JW Auto-generated catch block
- throw new CryptoException(e);
- }
- }
-
- return timeStampToken;
- }
-
- private static ContentInfo getTimeStampContentInfo(SignerInformation signerInfo) {
+ public static ContentInfo getTimeStamp(SignerInformation signerInfo) {
AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index da8c53fe4..390711a6a 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -233,8 +233,9 @@ protected void doAction() {
}
}
+ // TODO JW Display verification errors for the signature.
private void verify(Store certStore, SignerInformationStore signers) throws CMSException,
- OperatorCreationException, CertificateException, CryptoException, TSPException, TSPValidationException {
+ IOException, OperatorCreationException, CertificateException, TSPException, TSPValidationException {
boolean verified = false;
for (SignerInformation signer : signers.getSigners()) {
// TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
@@ -246,8 +247,11 @@ private void verify(Store certStore, SignerInformationSto
System.out.println("Verified by: " + cert.getSubject());
verified = true;
- TimeStampToken tspToken = CmsUtil.getTimeStampToken(signer);
- if (tspToken != null) {
+ ContentInfo timeStamp = CmsUtil.getTimeStamp(signer);
+
+ if (timeStamp != null) {
+ TimeStampToken tspToken = new TimeStampToken(timeStamp);
+
matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
if (!matchedCerts.isEmpty()) {
cert = matchedCerts.iterator().next();
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index ebfb9f826..8c593c260 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -76,11 +76,13 @@
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1UTCTime;
import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
@@ -307,6 +309,7 @@ private void initComponents(Collection signers) throws Crypto
jbOK.addActionListener(evt -> okPressed());
+ // TODO JW What to do if the signature doesn't have any certs at all?
jbCertificates.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewSignature.this);
@@ -471,7 +474,7 @@ private void populateDetails() {
}
jtfSignatureAlgorithm.setCaretPosition(0);
- timeStampSigner = CmsUtil.getTimeStampSignature(signerInfo);
+ timeStampSigner = getTimeStampSignature(signerInfo);
if (timeStampSigner != null) {
jbTimeStamp.setEnabled(true);
@@ -500,7 +503,7 @@ private void populateDetails() {
}
}
- private SignatureType lookupSignatureType(SignerInformation signerInfo) {
+ private static SignatureType lookupSignatureType(SignerInformation signerInfo) {
SignatureType signatureType = null;
if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(signerInfo.getEncryptionAlgOID())) {
@@ -515,6 +518,32 @@ private SignatureType lookupSignatureType(SignerInformation signerInfo) {
return signatureType;
}
+ /**
+ * Extracts the time stamp token, if present, from the signature's unsigned
+ * attributes.
+ *
+ * @param signerInfo The signer information.
+ * @return The time stamp token as CMSSignedData, if present, else null.
+ */
+ private static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) {
+
+ CMSSignedData timeStampToken = null;
+ ContentInfo timeStamp = CmsUtil.getTimeStamp(signerInfo);
+
+ if (timeStamp != null) {
+ try {
+ timeStampToken = new CMSSignedData(timeStamp);
+ } catch (CMSException e) {
+ // Users are not going to know what to do about an invalid time stamp token. The
+ // entire signature file has already been loaded so it is unlikely that this
+ // token is invalid. The signature verification logic will indicate if the time
+ // could not be verified.
+ }
+ }
+
+ return timeStampToken;
+ }
+
private static class SelectAll implements Selector {
@Override
public Object clone() {
diff --git a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
index fff69004e..83860217d 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java
@@ -401,11 +401,11 @@ private void okPressed() {
// warn if overwriting a file when not counter signing
if (outputFile.exists() && (!enableCounterSign || !jcbCounterSign.isSelected())) {
- int option = JOptionPane.showConfirmDialog(this,
+ int selected = JOptionPane.showConfirmDialog(this,
MessageFormat.format(res.getString("DSignFile.OverWriteOutput.message"), outputFile.getName()),
getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
- if (option == JOptionPane.NO_OPTION) {
+ if (selected != JOptionPane.YES_OPTION) {
return;
}
}
From 775d9ea8e1f6cbcda73d879476398bb9243d59ab Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 19 Dec 2024 12:49:38 -0800
Subject: [PATCH 48/69] Refactoring for signer information management.
Helps Handle the case when the signature does not include any certs.
---
.../java/org/kse/crypto/signing/CmsUtil.java | 116 +-------
.../crypto/signing/KseSignerInformation.java | 266 +++++++++++++++++
.../org/kse/crypto/x509/X509CertUtil.java | 2 +-
.../gui/actions/VerifySignatureAction.java | 278 ++++++------------
.../org/kse/gui/dialogs/DViewSignature.java | 88 +++---
.../kse/gui/dialogs/SignerListCellRend.java | 29 +-
6 files changed, 421 insertions(+), 358 deletions(-)
create mode 100644 kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index d3f1e5a57..a8528cf3d 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -23,29 +23,19 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.text.ParseException;
import java.util.Collection;
-import java.util.Date;
-import java.util.Enumeration;
+import java.util.List;
import java.util.ResourceBundle;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
-import org.bouncycastle.asn1.ASN1GeneralizedTime;
-import org.bouncycastle.asn1.ASN1UTCTime;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.cms.CMSAttributes;
-import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSProcessableFile;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.util.Store;
import org.kse.crypto.CryptoException;
-import org.kse.crypto.x509.X500NameUtils;
-import org.kse.utilities.StringUtils;
import org.kse.utilities.pem.PemInfo;
import org.kse.utilities.pem.PemUtil;
@@ -96,8 +86,7 @@ public static CMSSignedData loadSignature(File signatureFile, Supplier cho
return signedData;
}
- private static CMSProcessableFile loadDetachedContent(File signatureFile, Supplier chooser)
- throws CMSException {
+ private static CMSProcessableFile loadDetachedContent(File signatureFile, Supplier chooser) {
// Look for the content file. if not present, prompt for it.
File contentFile = null;
@@ -144,98 +133,9 @@ public static String getPem(CMSSignedData cms) throws CryptoException {
}
}
- /**
- * Extracts the signature signing time, if present, from the signature's signed attributes.
- *
- * @param signerInfo The signer information.
- * @return The signing time, if present, else null.
- */
- public static Date getSigningTime(SignerInformation signerInfo) throws CryptoException {
- Date signingTime = null;
- AttributeTable signedAttributes = signerInfo.getSignedAttributes();
-
- if (signedAttributes != null) {
- Attribute signingTimeAttribute = signedAttributes.get(CMSAttributes.signingTime);
- if (signingTimeAttribute != null) {
- Enumeration> element = signingTimeAttribute.getAttrValues().getObjects();
- if (element.hasMoreElements()) {
- Object o = element.nextElement();
- try {
- if (o instanceof ASN1UTCTime) {
- signingTime = ((ASN1UTCTime) o).getAdjustedDate();
- } else if (o instanceof ASN1GeneralizedTime) {
- signingTime = ((ASN1GeneralizedTime) o).getDate();
- }
- } catch (ParseException e) {
- // Users are not going to know what to do about invalid ASN.1 date structures.
- // So ignore the exception.
- }
- }
- }
- }
- return signingTime;
- }
-
- /**
- * Extracts the time stamp token, if present, from the signature's unsigned
- * attributes.
- *
- * @param signerInfo The signer information.
- * @return The time stamp token as ContentInfo, if present, else null.
- */
- public static ContentInfo getTimeStamp(SignerInformation signerInfo) {
-
- AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes();
-
- if (unsignedAttributes != null) {
- Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
- if (tsTokenAttribute != null) {
- return ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]);
- }
- }
-
- return null;
- }
-
- public static X509CertificateHolder getSignerCert(CMSSignedData signedData, SignerInformation signer) {
-
- X509CertificateHolder cert = null;
- Collection matchedCerts = signedData.getCertificates().getMatches(signer.getSID());
-
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
- }
-
- if (cert == null) {
- // TODO JW - what type of error handling
- }
-
- return cert;
- }
-
- public static String getShortName(X509CertificateHolder cert) {
- X500Name subject = cert.getSubject();
-
- String shortName = X500NameUtils.extractCN(subject);
- String emailAddress = X500NameUtils.extractEmailAddress(subject);
-
- if (!StringUtils.isBlank(emailAddress)) {
- if (StringUtils.isBlank(shortName)) {
- shortName = emailAddress;
- } else {
- shortName += " <" + emailAddress + ">";
- }
- }
-
- if (StringUtils.isBlank(shortName)) {
- shortName = subject.toString();
- }
-
- // subject DN can be empty in some cases
- if (StringUtils.isBlank(shortName)) {
- shortName = cert.getSerialNumber().toString();
- }
-
- return shortName;
+ public static List convertSignerInformations(Collection signerInfos,
+ Store trustedCerts, Store signatureCerts) {
+ return signerInfos.stream().map(s -> new KseSignerInformation(s, trustedCerts, signatureCerts))
+ .collect(Collectors.toList());
}
}
diff --git a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
new file mode 100644
index 000000000..9f8cb0dbc
--- /dev/null
+++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2004 - 2013 Wayne Grant
+ * 2013 - 2024 Kai Kramer
+ *
+ * This file is part of KeyStore Explorer.
+ *
+ * KeyStore Explorer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeyStore Explorer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeyStore Explorer. If not, see .
+ */
+package org.kse.crypto.signing;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+
+import org.bouncycastle.asn1.ASN1GeneralizedTime;
+import org.bouncycastle.asn1.ASN1UTCTime;
+import org.bouncycastle.asn1.cms.Attribute;
+import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.asn1.cms.CMSAttributes;
+import org.bouncycastle.asn1.cms.ContentInfo;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.tsp.TSPException;
+import org.bouncycastle.tsp.TSPValidationException;
+import org.bouncycastle.tsp.TimeStampToken;
+import org.bouncycastle.util.Store;
+import org.kse.crypto.x509.X500NameUtils;
+import org.kse.utilities.StringUtils;
+import org.kse.utilities.io.HexUtil;
+
+/**
+ * A SignerInformation extension for UI / verification needs.
+ */
+public class KseSignerInformation extends SignerInformation {
+
+ private Store trustedCerts;
+ private Store signatureCerts;
+ private X509CertificateHolder cert;
+ // TODO JW trustedCert is not used.
+ private boolean trustedCert;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param signerInfo The SignerInformation to extend.
+ * @param trustedCerts The trusted certs for lookup and verification.
+ * @param signatureCerts The signature certs for lookup and verification.
+ */
+ public KseSignerInformation(SignerInformation signerInfo, Store trustedCerts,
+ Store signatureCerts) {
+ super(signerInfo);
+ this.trustedCerts = trustedCerts;
+ this.signatureCerts = signatureCerts;
+ lookupCert();
+ }
+
+ /**
+ * @return the trustedCerts
+ */
+ public Store getTrustedCerts() {
+ return trustedCerts;
+ }
+
+ /**
+ * @return the signatureCerts
+ */
+ public Store getSignatureCerts() {
+ return signatureCerts;
+ }
+
+ /**
+ * @return the cert
+ */
+ public X509CertificateHolder getCertificate() {
+ return cert;
+ }
+
+ private void lookupCert() {
+ Collection matchedCerts = trustedCerts.getMatches(getSID());
+
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+
+ // TODO JW Need to trace the signature cert to a self-signed or CA cert, check basic constraints.
+ trustedCert = true;
+ } else {
+ matchedCerts = signatureCerts.getMatches(getSID());
+
+ if (!matchedCerts.isEmpty()) {
+ cert = matchedCerts.iterator().next();
+ }
+ }
+ }
+
+ /**
+ *
+ * @return The short name for user interfaces.
+ */
+ public String getShortName() {
+ String shortName;
+
+ if (cert != null) {
+ shortName = getShortName(cert);
+ } else {
+ shortName = HexUtil.getHexString(getSID().getSerialNumber(), "0x", 0, 0);
+ }
+
+ return shortName;
+ }
+
+ private static String getShortName(X509CertificateHolder cert) {
+ X500Name subject = cert.getSubject();
+
+ String shortName = X500NameUtils.extractCN(subject);
+ String emailAddress = X500NameUtils.extractEmailAddress(subject);
+
+ if (!StringUtils.isBlank(emailAddress)) {
+ if (StringUtils.isBlank(shortName)) {
+ shortName = emailAddress;
+ } else {
+ shortName += " <" + emailAddress + ">";
+ }
+ }
+
+ if (StringUtils.isBlank(shortName)) {
+ shortName = subject.toString();
+ }
+
+ // subject DN can be empty in some cases
+ if (StringUtils.isBlank(shortName)) {
+ shortName = cert.getSerialNumber().toString();
+ }
+
+ return shortName;
+ }
+
+ /**
+ * Extracts the signature signing time, if present, from the signature's signed attributes.
+ *
+ * @return The signing time, if present, else null.
+ */
+ public Date getSigningTime() {
+ Date signingTime = null;
+ AttributeTable signedAttributes = getSignedAttributes();
+
+ if (signedAttributes != null) {
+ Attribute signingTimeAttribute = signedAttributes.get(CMSAttributes.signingTime);
+ if (signingTimeAttribute != null) {
+ Enumeration> element = signingTimeAttribute.getAttrValues().getObjects();
+ if (element.hasMoreElements()) {
+ Object o = element.nextElement();
+ try {
+ if (o instanceof ASN1UTCTime) {
+ signingTime = ((ASN1UTCTime) o).getAdjustedDate();
+ } else if (o instanceof ASN1GeneralizedTime) {
+ signingTime = ((ASN1GeneralizedTime) o).getDate();
+ }
+ } catch (ParseException e) {
+ // Users are not going to know what to do about invalid ASN.1 date structures.
+ // So ignore the exception.
+ }
+ }
+ }
+ }
+ return signingTime;
+ }
+
+ /**
+ * Extracts the time stamp token, if present, from the signature's unsigned
+ * attributes.
+ *
+ * @return The time stamp token as ContentInfo, if present, else null.
+ */
+ public ContentInfo getTimeStamp() {
+
+ AttributeTable unsignedAttributes = getUnsignedAttributes();
+
+ if (unsignedAttributes != null) {
+ Attribute tsTokenAttribute = unsignedAttributes.get(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
+ if (tsTokenAttribute != null) {
+ return ContentInfo.getInstance(tsTokenAttribute.getAttributeValues()[0]);
+ }
+ }
+
+ return null;
+ }
+
+ // TODO JW Design verification flags/information for display. Add javadoc.
+ public boolean verify() throws CMSException, IOException, OperatorCreationException, CertificateException,
+ TSPException, TSPValidationException {
+
+ boolean verified = false;
+
+ if (cert != null) {
+ // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
+ if (verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+ System.out.println("Verified by: " + cert.getSubject());
+ verified = true;
+
+ // TODO JW Convert TS validation from exception into return value.
+ // TODO JW Display TS validation status on form.
+ verifyTimeStamp();
+
+ if (getCounterSignatures().size() > 0) {
+
+ Collection counterSigners = CmsUtil.convertSignerInformations(
+ getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
+
+ verified &= verify(counterSigners);
+ }
+ }
+ }
+
+ // TODO JW Remove the System.out.println() calls.
+ System.out.println("Verified: " + verified);
+ return verified;
+ }
+
+ private boolean verify(Collection signers) throws OperatorCreationException,
+ CertificateException, TSPValidationException, CMSException, IOException, TSPException {
+
+ boolean verified = true;
+
+ for (KseSignerInformation signer : signers) {
+ verified &= signer.verify();
+ }
+
+ return verified;
+ }
+
+ private void verifyTimeStamp()
+ throws TSPException, IOException, TSPValidationException, OperatorCreationException, CertificateException {
+
+ ContentInfo timeStamp = getTimeStamp();
+
+ if (timeStamp != null) {
+ TimeStampToken tspToken = new TimeStampToken(timeStamp);
+
+ Collection matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+ if (!matchedCerts.isEmpty()) {
+ X509CertificateHolder cert = matchedCerts.iterator().next();
+ tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+ System.out.println("Time stamped by: " + cert.getSubject());
+ }
+ }
+ }
+}
diff --git a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
index bff92edde..b1d5cde70 100644
--- a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
+++ b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
@@ -704,7 +704,7 @@ private static X509Certificate[] establishTrust(X509Certificate cert, List extractCertificates(KeyStore keyStore) throws CryptoException {
+ public static List extractCertificates(KeyStore keyStore) throws CryptoException {
try {
List certs = new ArrayList<>();
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 390711a6a..67b3e5a0a 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -20,96 +20,42 @@
package org.kse.gui.actions;
-import java.awt.HeadlessException;
import java.awt.Toolkit;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.nio.file.Files;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Security;
-import java.security.SignatureException;
-import java.security.cert.CertPath;
-import java.security.cert.CertPathValidator;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStore;
import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.CollectionCertStoreParameters;
-import java.security.cert.PKIXCertPathChecker;
-import java.security.cert.PKIXParameters;
-import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
-import java.util.Hashtable;
import java.util.List;
import java.util.Set;
-import java.util.function.Supplier;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
-import javax.swing.KeyStroke;
-import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
-import org.bouncycastle.asn1.cms.Attribute;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessable;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSProcessableFile;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.tsp.TSPException;
-import org.bouncycastle.tsp.TSPValidationException;
-import org.bouncycastle.tsp.TimeStampToken;
-import org.bouncycastle.util.Selector;
import org.bouncycastle.util.Store;
-import org.kse.KSE;
import org.kse.crypto.CryptoException;
import org.kse.crypto.signing.CmsUtil;
+import org.kse.crypto.signing.KseSignerInformation;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.CurrentDirectory;
import org.kse.gui.FileChooserFactory;
import org.kse.gui.KseFrame;
-import org.kse.gui.dialogs.DGetAlias;
-import org.kse.gui.dialogs.DVerifyCertificate;
-import org.kse.gui.dialogs.DViewCertificate;
import org.kse.gui.dialogs.DViewSignature;
-import org.kse.gui.dialogs.DVerifyCertificate.VerifyOptions;
import org.kse.gui.error.DError;
-import org.kse.gui.error.DProblem;
-import org.kse.gui.error.Problem;
-import org.kse.utilities.StringUtils;
import org.kse.utilities.history.KeyStoreHistory;
import org.kse.utilities.history.KeyStoreState;
-import org.kse.utilities.pem.PemInfo;
-import org.kse.utilities.pem.PemUtil;
public class VerifySignatureAction extends AuthorityCertificatesAction {
private static final long serialVersionUID = 1L;
@@ -146,7 +92,6 @@ protected void doAction() {
// }
// }
- // TODO JW - Remove these?
KeyStoreState currentState = history.getCurrentState();
KeyStore keyStore = currentState.getKeyStore();
@@ -166,64 +111,43 @@ protected void doAction() {
return;
}
- // TODO JW - Add new option for using cacerts for signature verification.
- if (preferences.getCaCertsSettings().isImportTrustedCertTrustCheckEnabled()) {
-// String matchAlias = X509CertUtil.matchCertificate(keyStore, trustCert);
-// if (matchAlias != null) {
-// int selected = JOptionPane.showConfirmDialog(frame, MessageFormat.format(
-// res.getString(
-// "ImportTrustedCertificateAction" +
-// ".TrustCertExistsConfirm.message"),
-// matchAlias),
-// res.getString(
-// "ImportTrustedCertificateAction" +
-// ".ImportTrustCert.Title"),
-// JOptionPane.YES_NO_OPTION);
-// if (selected != JOptionPane.YES_OPTION) {
-// return;
-// }
-// }
-
- KeyStore caCertificates = getCaCertificates();
- KeyStore windowsTrustedRootCertificates = getWindowsTrustedRootCertificates();
-
- // Establish against current KeyStore
- ArrayList compKeyStores = new ArrayList<>();
- compKeyStores.add(keyStore);
-
- if (caCertificates != null) {
- // Establish trust against CA Certificates KeyStore
- compKeyStores.add(caCertificates);
- }
+ KeyStore caCertificates = getCaCertificates();
+ KeyStore windowsTrustedRootCertificates = getWindowsTrustedRootCertificates();
- if (windowsTrustedRootCertificates != null) {
- // Establish trust against Windows Trusted Root Certificates KeyStore
- compKeyStores.add(windowsTrustedRootCertificates);
- }
+ // Perform cert lookup against current KeyStore
+ Set compCerts = new HashSet<>();
+ compCerts.addAll(extractCertificates(keyStore));
- // TODO JW - Verify the signature using the CA certs
- // TODO JW - Display dialog with option to see signature details.
+ if (caCertificates != null) {
+ // Perform cert lookup against CA Certificates KeyStore
+ compCerts.addAll(extractCertificates(caCertificates));
+ }
- return;
+ if (windowsTrustedRootCertificates != null) {
+ // Perform cert lookup against Windows Trusted Root Certificates KeyStore
+ compCerts.addAll(extractCertificates(windowsTrustedRootCertificates));
}
- SignerInformationStore signers = signedData.getSignerInfos();
+ @SuppressWarnings("unchecked")
+ Store trustedCerts = new JcaCertStore(compCerts);
+
+ SignerInformationStore signerInfos = signedData.getSignerInfos();
+ List signers = CmsUtil.convertSignerInformations(signerInfos.getSigners(),
+ trustedCerts, signedData.getCertificates());
// TODO JW - On DViewSignature, provide signature status: verified, unverified, invalid, verified - no trust
// Don't verify the signature if there is no signed content, but the signature details
// can still be displayed. loadSignature already tried to find and load the detachted
// content.
if (signedData.getSignedContent() != null) {
- // TODO JW - Verify the signature using the keystore
- // TODO JW build Store using certs from the truststore. If a cert cannot be found
- // then the signature should not be trusted (even if valid)
- Store certStore = signedData.getCertificates();
- verify(certStore, signers);
+ for (KseSignerInformation signer : signers) {
+ signer.verify();
+ }
}
DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat
.format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()),
- signedData, signedData.getSignerInfos().getSigners(), null);
+ signedData, signers, null);
dViewSignature.setLocationRelativeTo(frame);
dViewSignature.setVisible(true);
@@ -233,114 +157,76 @@ protected void doAction() {
}
}
- // TODO JW Display verification errors for the signature.
- private void verify(Store certStore, SignerInformationStore signers) throws CMSException,
- IOException, OperatorCreationException, CertificateException, TSPException, TSPValidationException {
- boolean verified = false;
- for (SignerInformation signer : signers.getSigners()) {
- // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
- Collection matchedCerts = certStore.getMatches(signer.getSID());
- if (!matchedCerts.isEmpty()) {
- X509CertificateHolder cert = matchedCerts.iterator().next();
- // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
- if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
- System.out.println("Verified by: " + cert.getSubject());
- verified = true;
-
- ContentInfo timeStamp = CmsUtil.getTimeStamp(signer);
-
- if (timeStamp != null) {
- TimeStampToken tspToken = new TimeStampToken(timeStamp);
-
- matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
- tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- System.out.println("Time stamped by: " + cert.getSubject());
- }
- }
+ private Collection extractCertificates(KeyStore keystore) {
+
+ List certs = new ArrayList<>();
- if (signer.getCounterSignatures().size() > 0) {
- verify(certStore, signer.getCounterSignatures());
+ try {
+ Enumeration aliases = keystore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+
+ Certificate[] certChain = keystore.getCertificateChain(alias);
+ if (certChain != null) {
+ for (Certificate cert : certChain) {
+ certs.add(X509CertUtil.convertCertificate(cert));
}
}
- }
- }
- System.out.println("Verified: " + verified);
- }
- private boolean isCA(X509Certificate cert) {
- int basicConstraints = cert.getBasicConstraints();
- if (basicConstraints != -1) {
- boolean[] keyUsage = cert.getKeyUsage();
- if (keyUsage != null && keyUsage[5]) {
- return true;
- }
- }
- return false;
- }
-
- private KeyStore getKeyStore(KeyStoreHistory keyStoreHistory)
- throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
-
- KeyStore trustStore = null;
- trustStore = KeyStore.getInstance("JCEKS");
- trustStore.load(null, null);
- if (keyStoreHistory != null) {
-
- KeyStore tempTrustStore = keyStoreHistory.getCurrentState().getKeyStore();
- Enumeration enumeration = tempTrustStore.aliases();
- while (enumeration.hasMoreElements()) {
- String alias = enumeration.nextElement();
- if (tempTrustStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class) ||
- tempTrustStore.entryInstanceOf(alias, KeyStore.TrustedCertificateEntry.class)) {
- X509Certificate cert = (X509Certificate) tempTrustStore.getCertificate(alias);
- if (isCA(cert)) {
- trustStore.setCertificateEntry(alias, cert);
- }
+ Certificate cert = keystore.getCertificate(alias);
+ if (cert != null) {
+ certs.add(X509CertUtil.convertCertificate(cert));
}
}
}
- if (trustStore.size() == 0) {
-// if (keyCertChain != null) {
-// for (int i = 0; i < keyCertChain.length; i++) {
-// X509Certificate cert = keyCertChain[i];
-// if (isCA(cert)) {
-// String entry = "entry" + i;
-// trustStore.setCertificateEntry(entry, cert);
-// }
-// }
-// }
+ catch (KeyStoreException e) {
+ // TODO JW Auto-generated catch block
+ e.printStackTrace();
}
- return trustStore;
- }
-
- private X509Certificate getCertificate(String alias) throws CryptoException {
- try {
- KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
- KeyStore keyStore = history.getCurrentState().getKeyStore();
-
- return X509CertUtil.convertCertificate(keyStore.getCertificate(alias));
- } catch (KeyStoreException ex) {
- String message = MessageFormat.format(res.getString("VerifySignatureAction.NoAccessEntry.message"),
- alias);
- throw new CryptoException(message, ex);
+ catch (CryptoException e) {
+ // TODO JW Auto-generated catch block
+ e.printStackTrace();
}
- }
- private X509Certificate[] getCertificateChain(String alias) throws CryptoException {
- try {
- KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
- KeyStore keyStore = history.getCurrentState().getKeyStore();
- X509Certificate[] certs = X509CertUtil.convertCertificates(keyStore.getCertificateChain(alias));
- return certs;
- } catch (KeyStoreException ex) {
- String message = MessageFormat.format(res.getString("VerifySignatureAction.NoAccessEntry.message"),
- alias);
- throw new CryptoException(message, ex);
- }
+ return certs;
}
+ // TODO JW Display verification errors for the signature.
+// private void verify(Store certStore, SignerInformationStore signers) throws CMSException,
+// IOException, OperatorCreationException, CertificateException, TSPException, TSPValidationException {
+// boolean verified = false;
+// for (SignerInformation signer : signers.getSigners()) {
+// // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
+// Collection matchedCerts = certStore.getMatches(signer.getSID());
+// if (!matchedCerts.isEmpty()) {
+// X509CertificateHolder cert = matchedCerts.iterator().next();
+// // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
+// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+// System.out.println("Verified by: " + cert.getSubject());
+// verified = true;
+//
+// ContentInfo timeStamp = CmsUtil.getTimeStamp(signer);
+//
+// if (timeStamp != null) {
+// TimeStampToken tspToken = new TimeStampToken(timeStamp);
+//
+// matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
+// if (!matchedCerts.isEmpty()) {
+// cert = matchedCerts.iterator().next();
+// tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
+// System.out.println("Time stamped by: " + cert.getSubject());
+// }
+// }
+//
+// if (signer.getCounterSignatures().size() > 0) {
+// verify(certStore, signer.getCounterSignatures());
+// }
+// }
+// }
+// }
+// System.out.println("Verified: " + verified);
+// }
+
/**
* Open a signature file.
*
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 8c593c260..92b8cbbdc 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -31,6 +31,7 @@
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.KeyStore;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -99,6 +100,7 @@
import org.kse.crypto.digest.DigestType;
import org.kse.crypto.keypair.KeyPairUtil;
import org.kse.crypto.signing.CmsUtil;
+import org.kse.crypto.signing.KseSignerInformation;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X500NameUtils;
import org.kse.crypto.x509.X509CertUtil;
@@ -139,7 +141,7 @@ public class DViewSignature extends JEscDialog {
private KseFrame kseFrame;
private JLabel jlSigners;
- private JList jlbSigners;
+ private JList jlbSigners;
private JScrollPane jspSigners;
private JLabel jlVersion;
private JTextField jtfVersion;
@@ -177,7 +179,7 @@ public class DViewSignature extends JEscDialog {
* @param kseFrame Reference to main class with currently opened keystores and their contents
* @throws CryptoException A problem was encountered getting the certificates' details
*/
- public DViewSignature(Window parent, String title, CMSSignedData signedData, Collection signers,
+ public DViewSignature(Window parent, String title, CMSSignedData signedData, Collection signers,
KseFrame kseFrame) throws CryptoException {
super(parent, title, Dialog.ModalityType.MODELESS);
this.kseFrame = kseFrame;
@@ -185,7 +187,7 @@ public DViewSignature(Window parent, String title, CMSSignedData signedData, Col
initComponents(signers);
}
- private void initComponents(Collection signers) throws CryptoException {
+ private void initComponents(Collection signers) throws CryptoException {
jlSigners = new JLabel(res.getString("DViewSignature.jlSigners.text"));
jlbSigners = new JList<>(createSignerList(signers));
@@ -193,7 +195,7 @@ private void initComponents(Collection signers) throws Crypto
// jlbSigners.setRowHeight(Math.max(18, jlbSigners.getRowHeight()));
jlbSigners.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
ToolTipManager.sharedInstance().registerComponent(jlbSigners);
- jlbSigners.setCellRenderer(new SignerListCellRend(signedData));
+ jlbSigners.setCellRenderer(new SignerListCellRend());
jspSigners = PlatformUtil.createScrollPane(jlbSigners, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
@@ -383,20 +385,20 @@ public void windowClosing(WindowEvent evt) {
SwingUtilities.invokeLater(() -> jbOK.requestFocus());
}
- private ListModel createSignerList(Collection signers) {
- DefaultListModel signerList = new DefaultListModel<>();
-
+ private ListModel createSignerList(Collection signers) {
+ DefaultListModel signerList = new DefaultListModel<>();
+
signerList.addAll(signers);
return signerList;
}
- private SignerInformation getSelectedSignerInfo() {
+ private KseSignerInformation getSelectedSignerInfo() {
return jlbSigners.getSelectedValue();
}
private void populateDetails() {
- SignerInformation signerInfo = getSelectedSignerInfo();
+ KseSignerInformation signerInfo = getSelectedSignerInfo();
if (signerInfo == null) {
jdnSubject.setEnabled(false);
@@ -423,17 +425,18 @@ private void populateDetails() {
jbAsn1.setEnabled(true);
try {
- Date signingTime = CmsUtil.getSigningTime(signerInfo);
- X509Certificate cert = X509CertUtil.convertCertificate(CmsUtil.getSignerCert(signedData, signerInfo));
-
- // TODO JW - Need to check for null certificate (see CmsUtil.getSignerCert)
+ Date signingTime = signerInfo.getSigningTime();
+ X509CertificateHolder cert = signerInfo.getCertificate();
jtfVersion.setText(Integer.toString(signerInfo.getVersion()));
jtfVersion.setCaretPosition(0);
- jdnSubject.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getSubjectX500Principal()));
+ // TODO JW - Need to check for null certificate (see CmsUtil.getSignerCert)
+ if (cert != null) {
+ jdnSubject.setDistinguishedName(cert.getSubject());
+ }
- jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal()));
+ jdnIssuer.setDistinguishedName(signerInfo.getSID().getIssuer());
if (signingTime != null) {
jtfSigningTime.setText(StringUtils.formatDate(signingTime));
@@ -496,14 +499,15 @@ private void populateDetails() {
// } else {
// jbExtensions.setEnabled(false);
// }
- } catch (CryptoException e) {
+ // TODO JW Remove the try/catch (or fix exception list)
+ } catch (Exception e) {
DError.displayError(this, e);
dispose();
}
}
}
- private static SignatureType lookupSignatureType(SignerInformation signerInfo) {
+ private static SignatureType lookupSignatureType(KseSignerInformation signerInfo) {
SignatureType signatureType = null;
if (PKCSObjectIdentifiers.rsaEncryption.getId().equals(signerInfo.getEncryptionAlgOID())) {
@@ -525,10 +529,10 @@ private static SignatureType lookupSignatureType(SignerInformation signerInfo) {
* @param signerInfo The signer information.
* @return The time stamp token as CMSSignedData, if present, else null.
*/
- private static CMSSignedData getTimeStampSignature(SignerInformation signerInfo) {
+ private static CMSSignedData getTimeStampSignature(KseSignerInformation signerInfo) {
CMSSignedData timeStampToken = null;
- ContentInfo timeStamp = CmsUtil.getTimeStamp(signerInfo);
+ ContentInfo timeStamp = signerInfo.getTimeStamp();
if (timeStamp != null) {
try {
@@ -543,23 +547,23 @@ private static CMSSignedData getTimeStampSignature(SignerInformation signerInfo)
return timeStampToken;
}
-
- private static class SelectAll implements Selector {
- @Override
- public Object clone() {
- return null;
- }
-
- @Override
- public boolean match(T obj) {
- return true;
- }
- }
+//
+// private static class SelectAll implements Selector {
+// @Override
+// public Object clone() {
+// return null;
+// }
+//
+// @Override
+// public boolean match(T obj) {
+// return true;
+// }
+// }
private void certificatesPressed() {
try {
List certs = X509CertUtil.convertCertificateHolders(
- signedData.getCertificates().getMatches(new SelectAll<>()));
+ signedData.getCertificates().getMatches(null));
DViewCertificate dViewCertificates = new DViewCertificate(this,
res.getString("DViewSignature.Certificates.Title"), certs.toArray(X509Certificate[]::new), kseFrame,
DViewCertificate.NONE);
@@ -571,14 +575,18 @@ private void certificatesPressed() {
}
private void timeStampPressed() {
- SignerInformation signer = getSelectedSignerInfo();
+ KseSignerInformation signer = getSelectedSignerInfo();
try {
- String shortName = CmsUtil.getShortName(CmsUtil.getSignerCert(signedData, signer));
+ String shortName = signer.getShortName();
+
+ List timeStampSigners = CmsUtil.convertSignerInformations(
+ timeStampSigner.getSignerInfos().getSigners(), signer.getTrustedCerts(),
+ signer.getSignatureCerts());
DViewSignature dViewSignature = new DViewSignature(this,
MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName),
- timeStampSigner, timeStampSigner.getSignerInfos().getSigners(), null);
+ timeStampSigner, timeStampSigners, null);
dViewSignature.setLocationRelativeTo(this);
dViewSignature.setVisible(true);
} catch (CryptoException e) {
@@ -587,14 +595,18 @@ private void timeStampPressed() {
}
private void counterSignersPressed() {
- SignerInformation signer = getSelectedSignerInfo();
+ KseSignerInformation signer = getSelectedSignerInfo();
try {
- String shortName = CmsUtil.getShortName(CmsUtil.getSignerCert(signedData, signer));
+ String shortName = signer.getShortName();
+
+ // TODO JW Have KseSignerInformation provide list of KseSignerInfos for counter signatures?
+ List counterSigners = CmsUtil.convertSignerInformations(
+ signer.getCounterSignatures().getSigners(), signer.getTrustedCerts(), signer.getSignatureCerts());
DViewSignature dViewSignature = new DViewSignature(this,
MessageFormat.format(res.getString("DViewSignature.CounterSigners.Title"), shortName), signedData,
- signer.getCounterSignatures().getSigners(), null);
+ counterSigners, null);
dViewSignature.setLocationRelativeTo(this);
dViewSignature.setVisible(true);
} catch (CryptoException e) {
diff --git a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
index 9cef3b6c9..5ba13a857 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
@@ -26,20 +26,13 @@
import javax.swing.JList;
import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.SignerInformation;
-import org.kse.crypto.signing.CmsUtil;
+import org.kse.crypto.signing.KseSignerInformation;
/**
* Custom cell renderer for the cells of the DViewSignature list.
*/
public class SignerListCellRend extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;
- private CMSSignedData signedData;
-
- public SignerListCellRend(CMSSignedData signedData) {
- this.signedData = signedData;
- }
/**
* Returns the rendered cell for the supplied value.
@@ -56,19 +49,25 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
boolean cellHasFocus) {
JLabel cell = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
- if (value instanceof SignerInformation) {
- SignerInformation signer = (SignerInformation) value;
+ if (value instanceof KseSignerInformation) {
- X509CertificateHolder cert = CmsUtil.getSignerCert(signedData, signer);
- String shortName = CmsUtil.getShortName(cert);
+ KseSignerInformation signer = (KseSignerInformation) value;
+ X509CertificateHolder cert = signer.getCertificate();
- cell.setText(shortName);
+ cell.setText(signer.getShortName());
- // TODO JW - need icon for signer list cell renderer
+ // TODO JW Is an icon for signer list cell renderer desired?
// ImageIcon icon = new ImageIcon(getClass().getResource("images/certificate_node.png"));
// cell.setIcon(icon);
- cell.setToolTipText(cert.getSubject().toString());
+ String tooltip;
+ if (cert != null) {
+ tooltip = cert.getSubject().toString();
+ } else {
+ tooltip = signer.getSID().getIssuer() + " / " + signer.getShortName();
+ }
+
+ cell.setToolTipText(tooltip);
}
return cell;
From 3326abe5529bde9f75eb8f79c3dde83e31e2a6d7 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 19 Dec 2024 12:54:51 -0800
Subject: [PATCH 49/69] Revert scope change to X509CertUtil.extractCertificates
---
kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
index b1d5cde70..bff92edde 100644
--- a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
+++ b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java
@@ -704,7 +704,7 @@ private static X509Certificate[] establishTrust(X509Certificate cert, List extractCertificates(KeyStore keyStore) throws CryptoException {
+ private static List extractCertificates(KeyStore keyStore) throws CryptoException {
try {
List certs = new ArrayList<>();
From de76dcbbe3d61237f8c1beec2d8cc6596c43a7c6 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 19 Dec 2024 15:27:57 -0800
Subject: [PATCH 50/69] Removed a couple more TODOs.
---
kse/src/main/java/org/kse/gui/KseFrame.java | 1 -
.../kse/gui/actions/VerifySignatureAction.java | 14 +-------------
.../kse/gui/actions/images/verifysignature.png | Bin 0 -> 865 bytes
3 files changed, 1 insertion(+), 14 deletions(-)
create mode 100644 kse/src/main/resources/org/kse/gui/actions/images/verifysignature.png
diff --git a/kse/src/main/java/org/kse/gui/KseFrame.java b/kse/src/main/java/org/kse/gui/KseFrame.java
index 5e7333ab9..8822555ce 100644
--- a/kse/src/main/java/org/kse/gui/KseFrame.java
+++ b/kse/src/main/java/org/kse/gui/KseFrame.java
@@ -1830,7 +1830,6 @@ private void initKeyStorePopupMenu() {
jpmKeyStore.addSeparator();
- // TODO JW - Need to figure out how to disable this until a key store is loaded
jmiVerifySignature = new JMenuItem(verifySignatureAction);
PlatformUtil.setMnemonic(jmiVerifySignature, res.getString("KseFrame.jmiVerifySignature.mnemonic").charAt(0));
jmiVerifySignature.setToolTipText(null);
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 67b3e5a0a..2c3a12ca5 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -72,7 +72,7 @@ public VerifySignatureAction(KseFrame kseFrame) {
putValue(SHORT_DESCRIPTION, res.getString("VerifySignatureAction.tooltip"));
// TODO JW - Need image for verify signature.
putValue(SMALL_ICON, new ImageIcon(
- Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/verifycert.png"))));
+ Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/verifysignature.png"))));
}
@Override
@@ -80,18 +80,6 @@ protected void doAction() {
try {
KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory();
- // TODO JW - Use cacerts truststore if no keystore is currently opened
- // handle case that no keystore is currently opened (-> create new keystore)
-// if (history == null) {
-// new NewAction(kseFrame).actionPerformed(null);
-// history = kseFrame.getActiveKeyStoreHistory();
-//
-// // cancel pressed => abort
-// if (history == null) {
-// return;
-// }
-// }
-
KeyStoreState currentState = history.getCurrentState();
KeyStore keyStore = currentState.getKeyStore();
diff --git a/kse/src/main/resources/org/kse/gui/actions/images/verifysignature.png b/kse/src/main/resources/org/kse/gui/actions/images/verifysignature.png
new file mode 100644
index 0000000000000000000000000000000000000000..c208f60b85413d381f3afc1b6867817def4da87e
GIT binary patch
literal 865
zcmV-n1D^beP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0`^HnK~y+TT~kX;
zQ&A8-ukZG?wX~MDl%Eue=t2_{NfaRxK@(AGFsO+Oqb_9Of&}AE7k&~yaYJ;$#u$wn
zO-RJ(!niWQ4Pg945>N?hYgH^%O8R_
zaJb;&E@7hJ7G#_-ND#gg`7bGvT#G>Y`qQUd`Qr4YOXCh;iQEOkKgn3W=oIBhMPFa9
zSW!`>PEA46G~{I&ndasluC6ZRLZM*j#tnYraspk56bCOQ0n}Ipi?Y7S93rHqc;|3)uzyBG`3)m<(4
zCDB(UlLpg|ua*xFZl}M@yjx17lkse1G5dP>p5*a*SA|WchC3}SRvx^}9V%cK;Z>x+
zU;E(oYA21x&o0JJIT*Wq%xDaZw6|-6ot@f~;fG3ZW8+|JYb!O1Zom~?rHRai?fdsP
z=xJaSSq{?(-?6FcWrwPsvdHqyHIfuQNE11F@tksU=vKcz8a>|D)>g&mnuy)&PIAngf_K=ldWyhuOU81UvXW5UGSPd!JE?z_kn&}70)Siyq{yvZ7Y
r3voc5{C$Y2XeEL}lB7*Go9+ECXq_wa5tGV`00000NkvXXu0mjfk@$#-
literal 0
HcmV?d00001
From 6458262317c8f34fbc668527f9da533d8a7f38ed Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 19 Dec 2024 17:56:51 -0800
Subject: [PATCH 51/69] Fixed TODO in CmsUtil.
---
.../org/kse/crypto/signing/CmsSigner.java | 1 -
.../java/org/kse/crypto/signing/CmsUtil.java | 36 ++++++++++---------
.../gui/actions/VerifySignatureAction.java | 20 -----------
.../kse/crypto/signing/resources.properties | 1 +
4 files changed, 20 insertions(+), 38 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
index 5dd51b26e..7d2b94078 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java
@@ -167,7 +167,6 @@ public static SignerInformationStore addTimestamp(String tsaUrl, SignerInformati
Collection newSignerInfos = new ArrayList<>();
- // get signature of first signer (should be the only one)
for (SignerInformation si : signerInfos.getSigners()) {
byte[] signature = si.getSignature();
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
index a8528cf3d..280dcdcf3 100644
--- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java
@@ -58,32 +58,34 @@ private CmsUtil() {
* @param signatureFile The signature file.
* @param chooser The file chooser to use for choosing the content file.
* @return
- * @throws IOException
- * @throws CMSException
+ * @throws CryptoException
*/
public static CMSSignedData loadSignature(File signatureFile, Supplier chooser)
- throws IOException, CMSException {
+ throws CryptoException {
- // TODO JW - What if the file cannot be opened?
- byte[] signature = Files.readAllBytes(signatureFile.toPath());
+ try {
+ byte[] signature = Files.readAllBytes(signatureFile.toPath());
- if (PemUtil.isPemFormat(signature)) {
- PemInfo signaturePem = PemUtil.decode(signature);
- if (signaturePem != null) {
- signature = signaturePem.getContent();
+ if (PemUtil.isPemFormat(signature)) {
+ PemInfo signaturePem = PemUtil.decode(signature);
+ if (signaturePem != null) {
+ signature = signaturePem.getContent();
+ }
}
- }
- CMSSignedData signedData = new CMSSignedData(signature);
+ CMSSignedData signedData = new CMSSignedData(signature);
- if (signedData.isDetachedSignature()) {
- CMSProcessableFile content = loadDetachedContent(signatureFile, chooser);
- if (content != null) {
- signedData = new CMSSignedData(content, signature);
+ if (signedData.isDetachedSignature()) {
+ CMSProcessableFile content = loadDetachedContent(signatureFile, chooser);
+ if (content != null) {
+ signedData = new CMSSignedData(content, signature);
+ }
}
- }
- return signedData;
+ return signedData;
+ } catch (IOException | CMSException e) {
+ throw new CryptoException(res.getString("NoReadCms.exception.message"), e);
+ }
}
private static CMSProcessableFile loadDetachedContent(File signatureFile, Supplier chooser) {
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 2c3a12ca5..08ff5cf04 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -88,7 +88,6 @@ protected void doAction() {
return;
}
- // TODO JW - What about the logic in openSignature?
CMSSignedData signedData = CmsUtil.loadSignature(signatureFile, this::chooseContentFile);
if (signedData.isCertificateManagementMessage()) {
JOptionPane.showMessageDialog(frame,
@@ -215,25 +214,6 @@ private Collection extractCertificates(KeyStore keystore) {
// System.out.println("Verified: " + verified);
// }
- /**
- * Open a signature file.
- *
- * @param signatureFile The signature file
- * @return The signature found in the file or null if open failed
- */
- protected byte[] openSignature(File signatureFile) {
- try {
- return Files.readAllBytes(signatureFile.toPath());
- } catch (IOException ex) {
- JOptionPane.showMessageDialog(frame, MessageFormat.format(
- res.getString("KeyStoreExplorerAction.NoReadFile.message"),
- signatureFile),
- res.getString("KeyStoreExplorerAction.OpenSignature.Title"),
- JOptionPane.WARNING_MESSAGE);
- return null;
- }
- }
-
private File showFileSelectionDialog() {
File signatureFile = chooseSignatureFile();
if (signatureFile == null) {
diff --git a/kse/src/main/resources/org/kse/crypto/signing/resources.properties b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
index f2c8a3cff..588dd001d 100644
--- a/kse/src/main/resources/org/kse/crypto/signing/resources.properties
+++ b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
@@ -5,6 +5,7 @@ CmsGetPemFailed.exception.message=CMS PEM encoding failed.
CmsSignatureFailed.exception.message=File signing failed.
CmsCounterSignatureFailed.exception.message=Counter signing failed.
NoReadJadCorrupt.exception.message=Could not read JAD file, may be corrupt.
+NoReadCms.exception.message=Could not read signature file, may be corrupt.
Base64CertificateFailed.exception.message=Could not get Base-64 encoding for certificate.
SignatureType.Sha1WithDsa=SHA-1 with DSA
From 33a6ca24fdac74a647bac909da22109905c2aa6d Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Thu, 19 Dec 2024 23:24:24 -0800
Subject: [PATCH 52/69] DViewSignature improvements:
View ASN.1 button for signer info.
Fixed button enabling/disabling.
---
.../crypto/signing/KseSignerInformation.java | 23 +++-
.../gui/actions/VerifySignatureAction.java | 2 -
.../org/kse/gui/dialogs/DViewAsn1Dump.java | 19 ++++
.../org/kse/gui/dialogs/DViewSignature.java | 104 +++++++++++-------
.../org/kse/gui/dialogs/resources.properties | 6 +
5 files changed, 109 insertions(+), 45 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
index 9f8cb0dbc..c38e1a56e 100644
--- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
+++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
@@ -94,19 +94,30 @@ public X509CertificateHolder getCertificate() {
return cert;
}
+ /**
+ * @return the signature status
+ */
+ public String getStatus() {
+ // TDOO JW Determine the ignature status.
+ return "";
+ }
+
private void lookupCert() {
- Collection matchedCerts = trustedCerts.getMatches(getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
+ @SuppressWarnings("unchecked")
+ Collection matchedCerts1 = trustedCerts.getMatches(getSID());
+
+ if (!matchedCerts1.isEmpty()) {
+ cert = matchedCerts1.iterator().next();
// TODO JW Need to trace the signature cert to a self-signed or CA cert, check basic constraints.
trustedCert = true;
} else {
- matchedCerts = signatureCerts.getMatches(getSID());
+ @SuppressWarnings("unchecked")
+ Collection matchedCerts2 = signatureCerts.getMatches(getSID());
- if (!matchedCerts.isEmpty()) {
- cert = matchedCerts.iterator().next();
+ if (!matchedCerts2.isEmpty()) {
+ cert = matchedCerts2.iterator().next();
}
}
}
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index 08ff5cf04..ceafbc603 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -22,8 +22,6 @@
import java.awt.Toolkit;
import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.Certificate;
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewAsn1Dump.java b/kse/src/main/java/org/kse/gui/dialogs/DViewAsn1Dump.java
index ce9c6f546..9bfbfaf58 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewAsn1Dump.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewAsn1Dump.java
@@ -43,6 +43,7 @@
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
+import org.bouncycastle.asn1.cms.SignerInfo;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.kse.crypto.csr.spkac.Spkac;
@@ -78,6 +79,7 @@ public class DViewAsn1Dump extends JEscFrame {
private PKCS10CertificationRequest pkcs10Csr;
private Spkac spkac;
private CMSSignedData cms;
+ private SignerInfo signerInfo;
/**
* Creates a new DViewAsn1Dump dialog.
@@ -200,6 +202,21 @@ public DViewAsn1Dump(JDialog parent, CMSSignedData cms) throws Asn1Exception, IO
initComponents();
}
+ /**
+ * Creates a new DViewAsn1Dump dialog.
+ *
+ * @param parent Parent frame
+ * @param signerInfo CMS signature to display dump for
+ * @throws Asn1Exception A problem was encountered getting the signer info ASN.1 dump
+ * @throws IOException If an I/O problem occurred
+ */
+ public DViewAsn1Dump(JDialog parent, SignerInfo signerInfo) throws Asn1Exception, IOException {
+ super(res.getString("DViewAsn1Dump.SignerInfo.Title"));
+ this.signerInfo = signerInfo;
+ this.setIconImages(parent.getOwner().getIconImages());
+ initComponents();
+ }
+
private void initComponents() throws Asn1Exception, IOException {
jbCopy = new JButton(res.getString("DViewAsn1Dump.jbCopy.text"));
@@ -239,6 +256,8 @@ private void initComponents() throws Asn1Exception, IOException {
jtaAsn1Dump = new JTextArea(asn1Dump.dump(publicKey));
} else if (pkcs10Csr != null) {
jtaAsn1Dump = new JTextArea(asn1Dump.dump(pkcs10Csr.getEncoded()));
+ } else if (signerInfo != null) {
+ jtaAsn1Dump = new JTextArea(asn1Dump.dump(signerInfo.getEncoded()));
} else {
jtaAsn1Dump = new JTextArea(asn1Dump.dump(spkac.getEncoded()));
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 92b8cbbdc..6a92dc854 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -143,6 +143,8 @@ public class DViewSignature extends JEscDialog {
private JLabel jlSigners;
private JList jlbSigners;
private JScrollPane jspSigners;
+ private JLabel jlStatus;
+ private JTextField jtfStatus;
private JLabel jlVersion;
private JTextField jtfVersion;
private JLabel jlSubject;
@@ -157,11 +159,12 @@ public class DViewSignature extends JEscDialog {
private JTextField jtfContentType;
private JLabel jlContentDigest;
private JTextField jtfContentDigest;
- private JButton jbCertificates;
private JButton jbTimeStamp;
private JButton jbCounterSigners;
+ private JButton jbSignerAsn1;
// TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes
private JButton jbExtensions;
+ private JButton jbCertificates;
private JButton jbPem;
private JButton jbAsn1;
private JButton jbOK;
@@ -201,6 +204,12 @@ private void initComponents(Collection signers) throws Cry
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jspSigners.setPreferredSize(new Dimension(100, 75));
+ jlStatus = new JLabel(res.getString("DViewSignature.jlStatus.text"));
+
+ jtfStatus = new JTextField(40);
+ jtfStatus.setEditable(false);
+ jtfStatus.setToolTipText(res.getString("DViewSignature.jtfStatus.tooltip"));
+
jlVersion = new JLabel(res.getString("DViewSignature.jlVersion.text"));
jtfVersion = new JTextField(40);
@@ -228,7 +237,7 @@ private void initComponents(Collection signers) throws Cry
jtfSignatureAlgorithm = new JTextField(40);
jtfSignatureAlgorithm.setEditable(false);
jtfSignatureAlgorithm.setToolTipText(res.getString("DViewSignature.jtfSignatureAlgorithm.tooltip"));
-
+
jlContentType = new JLabel(res.getString("DViewSignature.jlContentType.text"));
jtfContentType = new JTextField(40);
@@ -236,15 +245,11 @@ private void initComponents(Collection signers) throws Cry
jtfContentType.setToolTipText(res.getString("DViewSignature.jtfContentType.tooltip"));
jlContentDigest = new JLabel(res.getString("DViewSignature.jlContentDigest.text"));
-
+
jtfContentDigest = new JTextField(40);
jtfContentDigest.setEditable(false);
jtfContentDigest.setToolTipText(res.getString("DViewSignature.jtfContentDigest.tooltip"));
- jbCertificates = new JButton(res.getString("DViewSignature.jbCertificates.text"));
- jbCertificates.setToolTipText(res.getString("DViewSignature.jbCertificates.tooltip"));
- PlatformUtil.setMnemonic(jbCertificates, res.getString("DViewSignature.jbCertificates.mnemonic").charAt(0));
-
jbTimeStamp = new JButton(res.getString("DViewSignature.jbTimeStamp.text"));
jbTimeStamp.setToolTipText(res.getString("DViewSignature.jbTimeStamp.tooltip"));
// TODO JW - Need mnemonic for time stamp button
@@ -255,10 +260,20 @@ private void initComponents(Collection signers) throws Cry
// TODO JW - Need mnemonic for counter signers button
PlatformUtil.setMnemonic(jbCounterSigners, res.getString("DViewSignature.jbCounterSigners.mnemonic").charAt(0));
+ jbSignerAsn1 = new JButton(res.getString("DViewSignature.jbSignerAsn1.text"));
+ jbSignerAsn1.setToolTipText(res.getString("DViewSignature.jbSignerAsn1.tooltip"));
+ // TODO JW - Need mnemonic for signer asn1 button
+// PlatformUtil.setMnemonic(jbPem, res.getString("DViewSignature.jbSignerAsn1.mnemonic").charAt(0));
+
// jbExtensions = new JButton(res.getString("DViewSignature.jbExtensions.text"));
// jbExtensions.setToolTipText(res.getString("DViewSignature.jbExtensions.tooltip"));
// PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewSignature.jbExtensions.mnemonic").charAt(0));
+ jbCertificates = new JButton(res.getString("DViewSignature.jbCertificates.text"));
+ jbCertificates.setToolTipText(res.getString("DViewSignature.jbCertificates.tooltip"));
+ PlatformUtil.setMnemonic(jbCertificates, res.getString("DViewSignature.jbCertificates.mnemonic").charAt(0));
+ jbCertificates.setEnabled(!signedData.getCertificates().getMatches(null).isEmpty());
+
jbPem = new JButton(res.getString("DViewSignature.jbPem.text"));
jbPem.setToolTipText(res.getString("DViewSignature.jbPem.tooltip"));
PlatformUtil.setMnemonic(jbPem, res.getString("DViewSignature.jbPem.mnemonic").charAt(0));
@@ -273,6 +288,9 @@ private void initComponents(Collection signers) throws Cry
pane.setLayout(new MigLayout("insets dialog, fill", "[right]unrel[]", "[]unrel[]"));
pane.add(jlSigners, "");
pane.add(jspSigners, "sgx, wrap");
+ // TODO JW Disable certs button when no certs.
+ pane.add(jlStatus, "");
+ pane.add(jtfStatus, "sgx, wrap");
pane.add(jlVersion, "");
pane.add(jtfVersion, "sgx, wrap");
pane.add(jlSubject, "");
@@ -289,7 +307,8 @@ private void initComponents(Collection signers) throws Cry
// pane.add(jlContentDigest, "");
// pane.add(jtfContentDigest, "wrap");
pane.add(jbTimeStamp, "spanx, split");
- pane.add(jbCounterSigners, "wrap");
+ pane.add(jbCounterSigners, "");
+ pane.add(jbSignerAsn1, "wrap");
// pane.add(jbExtensions, "");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbCertificates, "spanx, split");
@@ -339,6 +358,15 @@ private void initComponents(Collection signers) throws Cry
}
});
+ jbSignerAsn1.addActionListener(evt -> {
+ try {
+ CursorUtil.setCursorBusy(DViewSignature.this);
+ signerAsn1DumpPressed();
+ } finally {
+ CursorUtil.setCursorFree(DViewSignature.this);
+ }
+ });
+
// jbExtensions.addActionListener(evt -> {
// try {
// CursorUtil.setCursorBusy(DViewSignature.this);
@@ -403,12 +431,10 @@ private void populateDetails() {
if (signerInfo == null) {
jdnSubject.setEnabled(false);
jdnIssuer.setEnabled(false);
- jbCertificates.setEnabled(false);
jbTimeStamp.setEnabled(false);
jbCounterSigners.setEnabled(false);
+ jbSignerAsn1.setEnabled(false);
// jbExtensions.setEnabled(false);
- jbPem.setEnabled(false);
- jbAsn1.setEnabled(false);
jtfVersion.setText("");
jdnSubject.setDistinguishedName(null);
@@ -421,19 +447,23 @@ private void populateDetails() {
jdnSubject.setEnabled(true);
jdnIssuer.setEnabled(true);
// jbExtensions.setEnabled(true);
- jbPem.setEnabled(true);
- jbAsn1.setEnabled(true);
+ jbSignerAsn1.setEnabled(true);
try {
Date signingTime = signerInfo.getSigningTime();
X509CertificateHolder cert = signerInfo.getCertificate();
+ jtfStatus.setText(res.getString(signerInfo.getStatus()));
+ jtfStatus.setCaretPosition(0);
+
jtfVersion.setText(Integer.toString(signerInfo.getVersion()));
jtfVersion.setCaretPosition(0);
- // TODO JW - Need to check for null certificate (see CmsUtil.getSignerCert)
if (cert != null) {
+ jdnSubject.setEnabled(true);
jdnSubject.setDistinguishedName(cert.getSubject());
+ } else {
+ jdnSubject.setEnabled(false);
}
jdnIssuer.setDistinguishedName(signerInfo.getSID().getIssuer());
@@ -455,15 +485,15 @@ private void populateDetails() {
jtfSigningTime.setCaretPosition(0);
// TODO JW - These map strings need to be moved to a resource bundle.
- Map CONTENT_TYPES = new HashMap<>();
- CONTENT_TYPES.put(PKCSObjectIdentifiers.data, "Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.signedData, "Signed Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.envelopedData, "Enveloped Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.signedAndEnvelopedData, "Signed and Enveloped Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data");
- jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
- jtfContentType.setCaretPosition(0);
+// Map CONTENT_TYPES = new HashMap<>();
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.data, "Data");
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.signedData, "Signed Data");
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.envelopedData, "Enveloped Data");
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.signedAndEnvelopedData, "Signed and Enveloped Data");
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data");
+// CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data");
+// jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
+// jtfContentType.setCaretPosition(0);
// TODO JW - digest is only available after verify is called.
// jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':'));
@@ -519,6 +549,7 @@ private static SignatureType lookupSignatureType(KseSignerInformation signerInfo
signatureType = SignatureType.resolveOid(signerInfo.getEncryptionAlgOID(),
signerInfo.getEncryptionAlgParams());
}
+
return signatureType;
}
@@ -547,18 +578,6 @@ private static CMSSignedData getTimeStampSignature(KseSignerInformation signerIn
return timeStampToken;
}
-//
-// private static class SelectAll implements Selector {
-// @Override
-// public Object clone() {
-// return null;
-// }
-//
-// @Override
-// public boolean match(T obj) {
-// return true;
-// }
-// }
private void certificatesPressed() {
try {
@@ -582,7 +601,7 @@ private void timeStampPressed() {
List timeStampSigners = CmsUtil.convertSignerInformations(
timeStampSigner.getSignerInfos().getSigners(), signer.getTrustedCerts(),
- signer.getSignatureCerts());
+ timeStampSigner.getCertificates());
DViewSignature dViewSignature = new DViewSignature(this,
MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName),
@@ -614,6 +633,18 @@ private void counterSignersPressed() {
}
}
+ private void signerAsn1DumpPressed() {
+ KseSignerInformation signer = getSelectedSignerInfo();
+
+ try {
+ DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, signer.toASN1Structure());
+ dViewAsn1Dump.setLocationRelativeTo(this);
+ dViewAsn1Dump.setVisible(true);
+ } catch (Asn1Exception | IOException e) {
+ DError.displayError(this, e);
+ }
+ }
+
// private void extensionsPressed() {
// X509Certificate cert = getSelectedSignerInfo();
//
@@ -634,7 +665,6 @@ private void pemEncodingPressed() {
}
private void asn1DumpPressed() {
- // TODO JW - Should this show only the ASN.1 for the selected signer? See signerInfo.toASN1Structure().
try {
DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, signedData);
dViewAsn1Dump.setLocationRelativeTo(this);
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
index 16b71865f..52139cfef 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
@@ -311,6 +311,7 @@ DViewAsn1Dump.Csr.Title = PKCS#10 Request ASN.1 Dump
DViewAsn1Dump.Extension.Title = Extension ASN.1 Dump
DViewAsn1Dump.PrivateKey.Title = Private Key ASN.1 Dump
DViewAsn1Dump.PublicKey.Title = Public Key ASN.1 Dump
+DViewAsn1Dump.SignerInfo.Title = Signer Info ASN.1 Dump
DViewAsn1Dump.jbCopy.mnemonic = C
DViewAsn1Dump.jbCopy.text = Copy
DViewAsn1Dump.jbCopy.tooltip = Copy ASN.1 dump to the clipboard
@@ -607,6 +608,9 @@ DViewSignature.jbOK.text = OK
DViewSignature.jbPem.mnemonic = P
DViewSignature.jbPem.text = PEM
DViewSignature.jbPem.tooltip = Display signature as PEM
+DViewSignature.jbSignerAsn1.mnemonic = A
+DViewSignature.jbSignerAsn1.text = ASN.1
+DViewSignature.jbSignerAsn1.tooltip = Display ASN.1 dump for signature
DViewSignature.jbTimeStamp.text = Time Stamp
DViewSignature.jbTimeStamp.tooltip = Display the signature's time stamp signer
DViewSignature.jdnIssuer.tooltip = Signer's issuer certificate distinguished name
@@ -617,6 +621,7 @@ DViewSignature.jlIssuer.text = Issuer:
DViewSignature.jlSigners.text = Signers:
DViewSignature.jlSignatureAlgorithm.text = Signature Algorithm:
DViewSignature.jlSigningTime.text = Signing Time:
+DViewSignature.jlStatus.text = Status:
DViewSignature.jlSubject.text = Subject:
DViewSignature.jlVersion.text = Version:
DViewSignature.jtfContentDigest.tooltip = The message digest of the signed content
@@ -624,6 +629,7 @@ DViewSignature.jtfContentType.tooltip = PKCS #7 content type
DViewSignature.jtfSignatureAlgorithm.tooltip = Signature algorithm used to sign the certificate
#DViewSignature.jtfValidUntil.expired.text = {0} (EXPIRED)
DViewSignature.jtfSigningTime.tooltip = The signing time of the document
+DViewSignature.jtfStatus.tooltip = Signer's signature status
DViewSignature.jtfVersion.tooltip = Signer's version number
PasswordCallbackHandler.Title = PIN Login
From 74105fb25807692f156f2118632eb22c10c97620 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Mon, 23 Dec 2024 00:31:09 -0800
Subject: [PATCH 53/69] Display signature status.
---
.../crypto/signing/CmsSignatureStatus.java | 48 ++++++++++++++
.../crypto/signing/KseSignerInformation.java | 65 ++++++++++++-------
.../gui/actions/VerifySignatureAction.java | 57 ++++------------
.../org/kse/gui/dialogs/DViewSignature.java | 24 ++-----
.../kse/crypto/signing/resources.properties | 12 +++-
.../org/kse/gui/dialogs/resources.properties | 2 -
6 files changed, 115 insertions(+), 93 deletions(-)
create mode 100644 kse/src/main/java/org/kse/crypto/signing/CmsSignatureStatus.java
diff --git a/kse/src/main/java/org/kse/crypto/signing/CmsSignatureStatus.java b/kse/src/main/java/org/kse/crypto/signing/CmsSignatureStatus.java
new file mode 100644
index 000000000..ce54b58e0
--- /dev/null
+++ b/kse/src/main/java/org/kse/crypto/signing/CmsSignatureStatus.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2004 - 2013 Wayne Grant
+ * 2013 - 2024 Kai Kramer
+ *
+ * This file is part of KeyStore Explorer.
+ *
+ * KeyStore Explorer is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * KeyStore Explorer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with KeyStore Explorer. If not, see .
+ */
+package org.kse.crypto.signing;
+
+import java.util.ResourceBundle;
+
+/**
+ * Enumeration of signature statuses for verifying a CMS signature.
+ */
+public enum CmsSignatureStatus {
+
+ // @formatter:off
+
+ NOT_VERIFIED,
+ INVALID,
+ VALID_NOT_TRUSTED,
+ VALID_TRUSTED;
+
+ // @formatter:on
+
+ private static ResourceBundle res = ResourceBundle.getBundle("org/kse/crypto/signing/resources");
+
+ public String getText() {
+ return res.getString("CmsSignatureStatus." + name() + ".text");
+ }
+
+ public String getToolTip() {
+ return res.getString("CmsSignatureStatus." + name() + ".tooltip");
+ }
+
+}
diff --git a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
index c38e1a56e..ae17a65fa 100644
--- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
+++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
@@ -55,7 +55,7 @@ public class KseSignerInformation extends SignerInformation {
private Store trustedCerts;
private Store signatureCerts;
private X509CertificateHolder cert;
- // TODO JW trustedCert is not used.
+ private CmsSignatureStatus status;
private boolean trustedCert;
/**
@@ -97,9 +97,11 @@ public X509CertificateHolder getCertificate() {
/**
* @return the signature status
*/
- public String getStatus() {
- // TDOO JW Determine the ignature status.
- return "";
+ public CmsSignatureStatus getStatus() {
+ if (status == null) {
+ verify();
+ }
+ return status;
}
private void lookupCert() {
@@ -215,35 +217,45 @@ public ContentInfo getTimeStamp() {
return null;
}
- // TODO JW Design verification flags/information for display. Add javadoc.
- public boolean verify() throws CMSException, IOException, OperatorCreationException, CertificateException,
- TSPException, TSPValidationException {
+ private void verify() {
- boolean verified = false;
+ status = CmsSignatureStatus.NOT_VERIFIED;
if (cert != null) {
- // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
- if (verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
- System.out.println("Verified by: " + cert.getSubject());
- verified = true;
- // TODO JW Convert TS validation from exception into return value.
- // TODO JW Display TS validation status on form.
- verifyTimeStamp();
+ boolean verified = false;
+
+ try {
+ // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
+ if (verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
+ verified = true;
- if (getCounterSignatures().size() > 0) {
+ // TODO JW Display TS validation status on form.
+ verifyTimeStamp();
- Collection counterSigners = CmsUtil.convertSignerInformations(
- getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
+ if (getCounterSignatures().size() > 0) {
- verified &= verify(counterSigners);
+ Collection counterSigners = CmsUtil.convertSignerInformations(
+ getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
+
+ verified &= verify(counterSigners);
+ }
}
+ } catch (Exception e) {
+ verified = false;
}
- }
- // TODO JW Remove the System.out.println() calls.
- System.out.println("Verified: " + verified);
- return verified;
+ if (verified) {
+ if (trustedCert) {
+ status = CmsSignatureStatus.VALID_TRUSTED;
+ } else {
+ status = CmsSignatureStatus.VALID_NOT_TRUSTED;
+ }
+ } else {
+ status = CmsSignatureStatus.INVALID;
+ }
+
+ }
}
private boolean verify(Collection signers) throws OperatorCreationException,
@@ -252,7 +264,10 @@ private boolean verify(Collection signers) throws Operator
boolean verified = true;
for (KseSignerInformation signer : signers) {
- verified &= signer.verify();
+ signer.verify();
+
+ verified &= (signer.getStatus() == CmsSignatureStatus.VALID_NOT_TRUSTED
+ || signer.getStatus() == CmsSignatureStatus.VALID_TRUSTED);
}
return verified;
@@ -266,11 +281,11 @@ private void verifyTimeStamp()
if (timeStamp != null) {
TimeStampToken tspToken = new TimeStampToken(timeStamp);
+ @SuppressWarnings("unchecked")
Collection matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
if (!matchedCerts.isEmpty()) {
X509CertificateHolder cert = matchedCerts.iterator().next();
tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
- System.out.println("Time stamped by: " + cert.getSubject());
}
}
}
diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
index ceafbc603..9311d32c3 100644
--- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
+++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java
@@ -68,7 +68,6 @@ public VerifySignatureAction(KseFrame kseFrame) {
putValue(LONG_DESCRIPTION, res.getString("VerifySignatureAction.statusbar"));
putValue(NAME, res.getString("VerifySignatureAction.text"));
putValue(SHORT_DESCRIPTION, res.getString("VerifySignatureAction.tooltip"));
- // TODO JW - Need image for verify signature.
putValue(SMALL_ICON, new ImageIcon(
Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/verifysignature.png"))));
}
@@ -120,15 +119,17 @@ protected void doAction() {
List signers = CmsUtil.convertSignerInformations(signerInfos.getSigners(),
trustedCerts, signedData.getCertificates());
- // TODO JW - On DViewSignature, provide signature status: verified, unverified, invalid, verified - no trust
- // Don't verify the signature if there is no signed content, but the signature details
- // can still be displayed. loadSignature already tried to find and load the detachted
- // content.
- if (signedData.getSignedContent() != null) {
- for (KseSignerInformation signer : signers) {
- signer.verify();
- }
- }
+ // TODO JW Signature verification happens while getting the signer info status. Not loading
+ // the content will display as invalid when really it cannot be verified.
+
+// // Don't verify the signature if there is no signed content, but the signature details
+// // can still be displayed. loadSignature already tried to find and load the detachted
+// // content.
+// if (signedData.getSignedContent() != null) {
+// for (KseSignerInformation signer : signers) {
+// signer.verify();
+// }
+// }
DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat
.format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()),
@@ -176,42 +177,6 @@ private Collection extractCertificates(KeyStore keystore) {
return certs;
}
- // TODO JW Display verification errors for the signature.
-// private void verify(Store certStore, SignerInformationStore signers) throws CMSException,
-// IOException, OperatorCreationException, CertificateException, TSPException, TSPValidationException {
-// boolean verified = false;
-// for (SignerInformation signer : signers.getSigners()) {
-// // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder?
-// Collection matchedCerts = certStore.getMatches(signer.getSID());
-// if (!matchedCerts.isEmpty()) {
-// X509CertificateHolder cert = matchedCerts.iterator().next();
-// // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain.
-// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
-// System.out.println("Verified by: " + cert.getSubject());
-// verified = true;
-//
-// ContentInfo timeStamp = CmsUtil.getTimeStamp(signer);
-//
-// if (timeStamp != null) {
-// TimeStampToken tspToken = new TimeStampToken(timeStamp);
-//
-// matchedCerts = tspToken.getCertificates().getMatches(tspToken.getSID());
-// if (!matchedCerts.isEmpty()) {
-// cert = matchedCerts.iterator().next();
-// tspToken.validate(new JcaSimpleSignerInfoVerifierBuilder().build(cert));
-// System.out.println("Time stamped by: " + cert.getSubject());
-// }
-// }
-//
-// if (signer.getCounterSignatures().size() > 0) {
-// verify(certStore, signer.getCounterSignatures());
-// }
-// }
-// }
-// }
-// System.out.println("Verified: " + verified);
-// }
-
private File showFileSelectionDialog() {
File signatureFile = chooseSignatureFile();
if (signatureFile == null) {
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 6a92dc854..12ca48566 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -157,8 +157,6 @@ public class DViewSignature extends JEscDialog {
private JTextField jtfSignatureAlgorithm;
private JLabel jlContentType;
private JTextField jtfContentType;
- private JLabel jlContentDigest;
- private JTextField jtfContentDigest;
private JButton jbTimeStamp;
private JButton jbCounterSigners;
private JButton jbSignerAsn1;
@@ -244,12 +242,6 @@ private void initComponents(Collection signers) throws Cry
jtfContentType.setEditable(false);
jtfContentType.setToolTipText(res.getString("DViewSignature.jtfContentType.tooltip"));
- jlContentDigest = new JLabel(res.getString("DViewSignature.jlContentDigest.text"));
-
- jtfContentDigest = new JTextField(40);
- jtfContentDigest.setEditable(false);
- jtfContentDigest.setToolTipText(res.getString("DViewSignature.jtfContentDigest.tooltip"));
-
jbTimeStamp = new JButton(res.getString("DViewSignature.jbTimeStamp.text"));
jbTimeStamp.setToolTipText(res.getString("DViewSignature.jbTimeStamp.tooltip"));
// TODO JW - Need mnemonic for time stamp button
@@ -288,7 +280,6 @@ private void initComponents(Collection signers) throws Cry
pane.setLayout(new MigLayout("insets dialog, fill", "[right]unrel[]", "[]unrel[]"));
pane.add(jlSigners, "");
pane.add(jspSigners, "sgx, wrap");
- // TODO JW Disable certs button when no certs.
pane.add(jlStatus, "");
pane.add(jtfStatus, "sgx, wrap");
pane.add(jlVersion, "");
@@ -304,8 +295,6 @@ private void initComponents(Collection signers) throws Cry
// TODO JW - clean up the dialog
// pane.add(jlContentType, "");
// pane.add(jtfContentType, "wrap");
-// pane.add(jlContentDigest, "");
-// pane.add(jtfContentDigest, "wrap");
pane.add(jbTimeStamp, "spanx, split");
pane.add(jbCounterSigners, "");
pane.add(jbSignerAsn1, "wrap");
@@ -313,7 +302,7 @@ private void initComponents(Collection signers) throws Cry
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbCertificates, "spanx, split");
// TODO JW - Hide PEM button for Counter Signers.
- // Use SignerInformation.toASN1Structure for displaying signer info ASN.1.
+ // TODO JW Use OpenSSL to view PEM structure of SignerInfo
pane.add(jbPem, "");
pane.add(jbAsn1, "wrap");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
@@ -330,7 +319,6 @@ private void initComponents(Collection signers) throws Cry
jbOK.addActionListener(evt -> okPressed());
- // TODO JW What to do if the signature doesn't have any certs at all?
jbCertificates.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewSignature.this);
@@ -436,13 +424,14 @@ private void populateDetails() {
jbSignerAsn1.setEnabled(false);
// jbExtensions.setEnabled(false);
+ jtfStatus.setText("");
+ jtfStatus.setToolTipText("");
jtfVersion.setText("");
jdnSubject.setDistinguishedName(null);
jdnIssuer.setDistinguishedName(null);
jtfSigningTime.setText("");
jtfSignatureAlgorithm.setText("");
jtfContentType.setText("");
- jtfContentDigest.setText("");
} else {
jdnSubject.setEnabled(true);
jdnIssuer.setEnabled(true);
@@ -453,8 +442,9 @@ private void populateDetails() {
Date signingTime = signerInfo.getSigningTime();
X509CertificateHolder cert = signerInfo.getCertificate();
- jtfStatus.setText(res.getString(signerInfo.getStatus()));
+ jtfStatus.setText(signerInfo.getStatus().getText());
jtfStatus.setCaretPosition(0);
+ jtfStatus.setToolTipText(signerInfo.getStatus().getToolTip());
jtfVersion.setText(Integer.toString(signerInfo.getVersion()));
jtfVersion.setCaretPosition(0);
@@ -495,10 +485,6 @@ private void populateDetails() {
// jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
// jtfContentType.setCaretPosition(0);
- // TODO JW - digest is only available after verify is called.
-// jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':'));
-// jtfContentDigest.setCaretPosition(0);
-
SignatureType signatureType = lookupSignatureType(signerInfo);
if (signatureType != null ) {
jtfSignatureAlgorithm.setText(signatureType.friendly());
diff --git a/kse/src/main/resources/org/kse/crypto/signing/resources.properties b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
index 588dd001d..c4b6eecac 100644
--- a/kse/src/main/resources/org/kse/crypto/signing/resources.properties
+++ b/kse/src/main/resources/org/kse/crypto/signing/resources.properties
@@ -42,4 +42,14 @@ SignatureType.Sha3_384WithRsaAndMGF1=SHA3-384 with RSA and MGF1
SignatureType.Sha3_512WithRsaAndMGF1=SHA3-512 with RSA and MGF1
SignatureType.Ed25519=Ed25519
-SignatureType.Ed448=Ed448
\ No newline at end of file
+SignatureType.Ed448=Ed448
+
+
+CmsSignatureStatus.NOT_VERIFIED.text=Not Verified
+CmsSignatureStatus.NOT_VERIFIED.tooltip=The signature could not be verified
+CmsSignatureStatus.INVALID.text=Invalid
+CmsSignatureStatus.INVALID.tooltip=The signature is invalid
+CmsSignatureStatus.VALID_NOT_TRUSTED.text=Valid - Not Trusted
+CmsSignatureStatus.VALID_NOT_TRUSTED.tooltip=The signature is valid, but signer certificate is not trusted.
+CmsSignatureStatus.VALID_TRUSTED.text=Valid
+CmsSignatureStatus.VALID_TRUSTED.tooltip=The signature valid
diff --git a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
index 52139cfef..3c50d2aca 100644
--- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
+++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties
@@ -615,7 +615,6 @@ DViewSignature.jbTimeStamp.text = Time Stamp
DViewSignature.jbTimeStamp.tooltip = Display the signature's time stamp signer
DViewSignature.jdnIssuer.tooltip = Signer's issuer certificate distinguished name
DViewSignature.jdnSubject.tooltip = Signer's subject certificate distinguished name
-DViewSignature.jlContentDigest.text = Content Digest:
DViewSignature.jlContentType.text = Content Type:
DViewSignature.jlIssuer.text = Issuer:
DViewSignature.jlSigners.text = Signers:
@@ -624,7 +623,6 @@ DViewSignature.jlSigningTime.text = Signing Time:
DViewSignature.jlStatus.text = Status:
DViewSignature.jlSubject.text = Subject:
DViewSignature.jlVersion.text = Version:
-DViewSignature.jtfContentDigest.tooltip = The message digest of the signed content
DViewSignature.jtfContentType.tooltip = PKCS #7 content type
DViewSignature.jtfSignatureAlgorithm.tooltip = Signature algorithm used to sign the certificate
#DViewSignature.jtfValidUntil.expired.text = {0} (EXPIRED)
From 90bc87b54ddf4fc6f13e45d067740c397f191eb7 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Mon, 23 Dec 2024 22:56:09 -0800
Subject: [PATCH 54/69] Show content type.
---
.../org/kse/gui/dialogs/DViewSignature.java | 23 +++++++++----------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 12ca48566..e27eb3d67 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -292,9 +292,8 @@ private void initComponents(Collection signers) throws Cry
pane.add(jtfSigningTime, "wrap");
pane.add(jlSignatureAlgorithm, "");
pane.add(jtfSignatureAlgorithm, "wrap");
- // TODO JW - clean up the dialog
-// pane.add(jlContentType, "");
-// pane.add(jtfContentType, "wrap");
+ pane.add(jlContentType, "");
+ pane.add(jtfContentType, "wrap");
pane.add(jbTimeStamp, "spanx, split");
pane.add(jbCounterSigners, "");
pane.add(jbSignerAsn1, "wrap");
@@ -475,15 +474,15 @@ private void populateDetails() {
jtfSigningTime.setCaretPosition(0);
// TODO JW - These map strings need to be moved to a resource bundle.
-// Map CONTENT_TYPES = new HashMap<>();
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.data, "Data");
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.signedData, "Signed Data");
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.envelopedData, "Enveloped Data");
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.signedAndEnvelopedData, "Signed and Enveloped Data");
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data");
-// CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data");
-// jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
-// jtfContentType.setCaretPosition(0);
+ Map CONTENT_TYPES = new HashMap<>();
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.data, "Data");
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.signedData, "Signed Data");
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.envelopedData, "Enveloped Data");
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.signedAndEnvelopedData, "Signed and Enveloped Data");
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data");
+ CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data");
+ jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
+ jtfContentType.setCaretPosition(0);
SignatureType signatureType = lookupSignatureType(signerInfo);
if (signatureType != null ) {
From 8b86856d90c03cb2627c8c0f49cb295550c51598 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 24 Dec 2024 06:09:59 -0800
Subject: [PATCH 55/69] Moved signatures to a JTree. Counter signatures are
children of signatures.
---
.../crypto/signing/KseSignerInformation.java | 26 ++-
.../org/kse/gui/dialogs/DViewSignature.java | 176 +++++-------------
...tCellRend.java => SignerTreeCellRend.java} | 40 ++--
kse/src/si.pem | 125 +++++++++++++
4 files changed, 221 insertions(+), 146 deletions(-)
rename kse/src/main/java/org/kse/gui/dialogs/{SignerListCellRend.java => SignerTreeCellRend.java} (58%)
create mode 100644 kse/src/si.pem
diff --git a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
index ae17a65fa..c25766a9c 100644
--- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
+++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
@@ -22,9 +22,11 @@
import java.io.IOException;
import java.security.cert.CertificateException;
import java.text.ParseException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
+import java.util.List;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
import org.bouncycastle.asn1.ASN1UTCTime;
@@ -35,8 +37,8 @@
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.tsp.TSPException;
@@ -235,10 +237,7 @@ private void verify() {
if (getCounterSignatures().size() > 0) {
- Collection counterSigners = CmsUtil.convertSignerInformations(
- getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
-
- verified &= verify(counterSigners);
+ verified &= verifyCounterSignatures();
}
}
} catch (Exception e) {
@@ -258,12 +257,15 @@ private void verify() {
}
}
- private boolean verify(Collection signers) throws OperatorCreationException,
- CertificateException, TSPValidationException, CMSException, IOException, TSPException {
+ private boolean verifyCounterSignatures() {
boolean verified = true;
- for (KseSignerInformation signer : signers) {
+ // TODO JW Don't use super. Just use the overridden method.
+ Collection counterSigners = CmsUtil.convertSignerInformations(
+ super.getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
+
+ for (KseSignerInformation signer : counterSigners) {
signer.verify();
verified &= (signer.getStatus() == CmsSignatureStatus.VALID_NOT_TRUSTED
@@ -289,4 +291,12 @@ private void verifyTimeStamp()
}
}
}
+
+ @Override
+ public SignerInformationStore getCounterSignatures() {
+ List counterSigners = CmsUtil.convertSignerInformations(
+ super.getCounterSignatures().getSigners(), trustedCerts, signatureCerts);
+ // Load into an ArrayList for mapping between generic types.
+ return new SignerInformationStore(new ArrayList<>(counterSigners));
+ }
}
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index e27eb3d67..27a104a1f 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -23,7 +23,6 @@
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
-import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -31,40 +30,22 @@
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
-import java.security.interfaces.ECPublicKey;
import java.text.MessageFormat;
-import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.ResourceBundle;
-import java.util.Set;
-import java.util.TreeSet;
-import javax.swing.DefaultListModel;
-import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
-import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JTree;
-import javax.swing.ListModel;
-import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
@@ -73,32 +54,17 @@
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
-import org.bouncycastle.asn1.ASN1GeneralizedTime;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1UTCTime;
-import org.bouncycastle.asn1.cms.CMSAttributes;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.cert.X509AttributeCertificateHolder;
import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.tsp.TSPException;
-import org.bouncycastle.tsp.TSPValidationException;
-import org.bouncycastle.tsp.TimeStampToken;
-import org.bouncycastle.util.Selector;
-import org.bouncycastle.util.StoreException;
import org.bouncycastle.util.encoders.Hex;
import org.kse.KSE;
import org.kse.crypto.CryptoException;
-import org.kse.crypto.KeyInfo;
import org.kse.crypto.digest.DigestType;
-import org.kse.crypto.keypair.KeyPairUtil;
import org.kse.crypto.signing.CmsUtil;
import org.kse.crypto.signing.KseSignerInformation;
import org.kse.crypto.signing.SignatureType;
@@ -107,22 +73,16 @@
import org.kse.crypto.x509.X509CertificateGenerator;
import org.kse.crypto.x509.X509CertificateVersion;
import org.kse.gui.CursorUtil;
-import org.kse.gui.components.JEscDialog;
import org.kse.gui.KseFrame;
import org.kse.gui.PlatformUtil;
-import org.kse.gui.actions.ExportTrustedCertificateAction;
-import org.kse.gui.actions.ImportTrustedCertificateAction;
-import org.kse.gui.actions.VerifyCertificateAction;
-import org.kse.gui.crypto.JCertificateFingerprint;
+import org.kse.gui.components.JEscDialog;
import org.kse.gui.crypto.JDistinguishedName;
-import org.kse.gui.dialogs.extensions.DViewExtensions;
import org.kse.gui.error.DError;
import org.kse.gui.preferences.PreferencesManager;
import org.kse.gui.preferences.data.KsePreferences;
import org.kse.utilities.DialogViewer;
import org.kse.utilities.StringUtils;
import org.kse.utilities.asn1.Asn1Exception;
-import org.kse.utilities.io.HexUtil;
import net.miginfocom.swing.MigLayout;
@@ -140,8 +100,9 @@ public class DViewSignature extends JEscDialog {
private KseFrame kseFrame;
+ // TODO JW Add control to display if the signature is a counter signature
private JLabel jlSigners;
- private JList jlbSigners;
+ private JTree jtrSigners;
private JScrollPane jspSigners;
private JLabel jlStatus;
private JTextField jtfStatus;
@@ -155,10 +116,7 @@ public class DViewSignature extends JEscDialog {
private JTextField jtfSigningTime;
private JLabel jlSignatureAlgorithm;
private JTextField jtfSignatureAlgorithm;
- private JLabel jlContentType;
- private JTextField jtfContentType;
private JButton jbTimeStamp;
- private JButton jbCounterSigners;
private JButton jbSignerAsn1;
// TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes
private JButton jbExtensions;
@@ -191,14 +149,17 @@ public DViewSignature(Window parent, String title, CMSSignedData signedData, Col
private void initComponents(Collection signers) throws CryptoException {
jlSigners = new JLabel(res.getString("DViewSignature.jlSigners.text"));
- jlbSigners = new JList<>(createSignerList(signers));
- // TODO JW - Signer list row height?
-// jlbSigners.setRowHeight(Math.max(18, jlbSigners.getRowHeight()));
- jlbSigners.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- ToolTipManager.sharedInstance().registerComponent(jlbSigners);
- jlbSigners.setCellRenderer(new SignerListCellRend());
+ jtrSigners = new JTree(createSignerNodes(signers));
+ jtrSigners.setRowHeight(Math.max(18, jtrSigners.getRowHeight()));
+ jtrSigners.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+ ToolTipManager.sharedInstance().registerComponent(jtrSigners);
+ jtrSigners.setCellRenderer(new SignerTreeCellRend());
+ jtrSigners.setRootVisible(false);
+
+ TreeNode topNode = (TreeNode) jtrSigners.getModel().getRoot();
+ expandTree(jtrSigners, new TreePath(topNode));
- jspSigners = PlatformUtil.createScrollPane(jlbSigners, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
+ jspSigners = PlatformUtil.createScrollPane(jtrSigners, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jspSigners.setPreferredSize(new Dimension(100, 75));
@@ -236,22 +197,11 @@ private void initComponents(Collection signers) throws Cry
jtfSignatureAlgorithm.setEditable(false);
jtfSignatureAlgorithm.setToolTipText(res.getString("DViewSignature.jtfSignatureAlgorithm.tooltip"));
- jlContentType = new JLabel(res.getString("DViewSignature.jlContentType.text"));
-
- jtfContentType = new JTextField(40);
- jtfContentType.setEditable(false);
- jtfContentType.setToolTipText(res.getString("DViewSignature.jtfContentType.tooltip"));
-
jbTimeStamp = new JButton(res.getString("DViewSignature.jbTimeStamp.text"));
jbTimeStamp.setToolTipText(res.getString("DViewSignature.jbTimeStamp.tooltip"));
// TODO JW - Need mnemonic for time stamp button
// PlatformUtil.setMnemonic(jbTimeStamp, res.getString("DViewSignature.jbTimeStamp.mnemonic").charAt(0));
- jbCounterSigners = new JButton(res.getString("DViewSignature.jbCounterSigners.text"));
- jbCounterSigners.setToolTipText(res.getString("DViewSignature.jbCounterSigners.tooltip"));
- // TODO JW - Need mnemonic for counter signers button
- PlatformUtil.setMnemonic(jbCounterSigners, res.getString("DViewSignature.jbCounterSigners.mnemonic").charAt(0));
-
jbSignerAsn1 = new JButton(res.getString("DViewSignature.jbSignerAsn1.text"));
jbSignerAsn1.setToolTipText(res.getString("DViewSignature.jbSignerAsn1.tooltip"));
// TODO JW - Need mnemonic for signer asn1 button
@@ -292,22 +242,17 @@ private void initComponents(Collection signers) throws Cry
pane.add(jtfSigningTime, "wrap");
pane.add(jlSignatureAlgorithm, "");
pane.add(jtfSignatureAlgorithm, "wrap");
- pane.add(jlContentType, "");
- pane.add(jtfContentType, "wrap");
pane.add(jbTimeStamp, "spanx, split");
- pane.add(jbCounterSigners, "");
pane.add(jbSignerAsn1, "wrap");
// pane.add(jbExtensions, "");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbCertificates, "spanx, split");
- // TODO JW - Hide PEM button for Counter Signers.
- // TODO JW Use OpenSSL to view PEM structure of SignerInfo
pane.add(jbPem, "");
pane.add(jbAsn1, "wrap");
pane.add(new JSeparator(), "spanx, growx, wrap 15:push");
pane.add(jbOK, "spanx, tag ok");
- jlbSigners.addListSelectionListener(evt -> {
+ jtrSigners.addTreeSelectionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewSignature.this);
populateDetails();
@@ -336,15 +281,6 @@ private void initComponents(Collection signers) throws Cry
}
});
- jbCounterSigners.addActionListener(evt -> {
- try {
- CursorUtil.setCursorBusy(DViewSignature.this);
- counterSignersPressed();
- } finally {
- CursorUtil.setCursorFree(DViewSignature.this);
- }
- });
-
jbSignerAsn1.addActionListener(evt -> {
try {
CursorUtil.setCursorBusy(DViewSignature.this);
@@ -390,8 +326,10 @@ public void windowClosing(WindowEvent evt) {
setResizable(false);
- // select first signer in signer list
- jlbSigners.setSelectedIndex(0);
+ // TODO JW fix the selection -- this does not work.
+ // select (first) child in signers tree
+ TreeNode firstChild = ((DefaultMutableTreeNode) topNode).getFirstChild();
+ jtrSigners.setSelectionPath(new TreePath(firstChild));
getRootPane().setDefaultButton(jbOK);
@@ -400,16 +338,43 @@ public void windowClosing(WindowEvent evt) {
SwingUtilities.invokeLater(() -> jbOK.requestFocus());
}
- private ListModel createSignerList(Collection signers) {
- DefaultListModel signerList = new DefaultListModel<>();
+ private DefaultMutableTreeNode createSignerNodes(Collection signers) {
+ DefaultMutableTreeNode signersNode = new DefaultMutableTreeNode();
+
+ for (SignerInformation signerInfo : signers) {
+ DefaultMutableTreeNode signerNode = new DefaultMutableTreeNode(signerInfo);
- signerList.addAll(signers);
+ signersNode.add(signerNode);
+
+ for (SignerInformation counterSignerInfo : signerInfo.getCounterSignatures().getSigners()) {
+ signerNode.add(new DefaultMutableTreeNode(counterSignerInfo));
+ }
+ }
+
+ return signersNode;
+ }
+
+ private void expandTree(JTree tree, TreePath parent) {
+ TreeNode node = (TreeNode) parent.getLastPathComponent();
+ if (node.getChildCount() >= 0) {
+ for (Enumeration> enumNodes = node.children(); enumNodes.hasMoreElements(); ) {
+ TreeNode subNode = (TreeNode) enumNodes.nextElement();
+ TreePath path = parent.pathByAddingChild(subNode);
+ expandTree(tree, path);
+ }
+ }
- return signerList;
+ tree.expandPath(parent);
}
private KseSignerInformation getSelectedSignerInfo() {
- return jlbSigners.getSelectedValue();
+ TreePath[] selections = jtrSigners.getSelectionPaths();
+
+ if (selections == null) {
+ return null;
+ }
+
+ return (KseSignerInformation) ((DefaultMutableTreeNode) selections[0].getLastPathComponent()).getUserObject();
}
private void populateDetails() {
@@ -419,7 +384,6 @@ private void populateDetails() {
jdnSubject.setEnabled(false);
jdnIssuer.setEnabled(false);
jbTimeStamp.setEnabled(false);
- jbCounterSigners.setEnabled(false);
jbSignerAsn1.setEnabled(false);
// jbExtensions.setEnabled(false);
@@ -430,7 +394,6 @@ private void populateDetails() {
jdnIssuer.setDistinguishedName(null);
jtfSigningTime.setText("");
jtfSignatureAlgorithm.setText("");
- jtfContentType.setText("");
} else {
jdnSubject.setEnabled(true);
jdnIssuer.setEnabled(true);
@@ -463,7 +426,7 @@ private void populateDetails() {
jtfSigningTime.setText("");
}
-// if (noLongerValid) {
+// if (true) {
// jtfSigningTime.setText(
// MessageFormat.format(res.getString("DViewCertificate.jtfSigningTime.expired.text"),
// jtfSigningTime.getText()));
@@ -473,17 +436,6 @@ private void populateDetails() {
// }
jtfSigningTime.setCaretPosition(0);
- // TODO JW - These map strings need to be moved to a resource bundle.
- Map CONTENT_TYPES = new HashMap<>();
- CONTENT_TYPES.put(PKCSObjectIdentifiers.data, "Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.signedData, "Signed Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.envelopedData, "Enveloped Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.signedAndEnvelopedData, "Signed and Enveloped Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data");
- CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data");
- jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType()));
- jtfContentType.setCaretPosition(0);
-
SignatureType signatureType = lookupSignatureType(signerInfo);
if (signatureType != null ) {
jtfSignatureAlgorithm.setText(signatureType.friendly());
@@ -500,12 +452,6 @@ private void populateDetails() {
jbTimeStamp.setEnabled(false);
}
- if (signerInfo.getCounterSignatures().size() > 0) {
- jbCounterSigners.setEnabled(true);
- } else {
- jbCounterSigners.setEnabled(false);
- }
-
// Set> critExts = signerInfo.getCriticalExtensionOIDs();
// Set> nonCritExts = signerInfo.getNonCriticalExtensionOIDs();
//
@@ -598,26 +544,6 @@ private void timeStampPressed() {
}
}
- private void counterSignersPressed() {
- KseSignerInformation signer = getSelectedSignerInfo();
-
- try {
- String shortName = signer.getShortName();
-
- // TODO JW Have KseSignerInformation provide list of KseSignerInfos for counter signatures?
- List counterSigners = CmsUtil.convertSignerInformations(
- signer.getCounterSignatures().getSigners(), signer.getTrustedCerts(), signer.getSignatureCerts());
-
- DViewSignature dViewSignature = new DViewSignature(this,
- MessageFormat.format(res.getString("DViewSignature.CounterSigners.Title"), shortName), signedData,
- counterSigners, null);
- dViewSignature.setLocationRelativeTo(this);
- dViewSignature.setVisible(true);
- } catch (CryptoException e) {
- DError.displayError(this, e);
- }
- }
-
private void signerAsn1DumpPressed() {
KseSignerInformation signer = getSelectedSignerInfo();
diff --git a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java b/kse/src/main/java/org/kse/gui/dialogs/SignerTreeCellRend.java
similarity index 58%
rename from kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
rename to kse/src/main/java/org/kse/gui/dialogs/SignerTreeCellRend.java
index 5ba13a857..263517e1d 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/SignerTreeCellRend.java
@@ -21,9 +21,10 @@
import java.awt.Component;
-import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
-import javax.swing.JList;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
import org.bouncycastle.cert.X509CertificateHolder;
import org.kse.crypto.signing.KseSignerInformation;
@@ -31,33 +32,46 @@
/**
* Custom cell renderer for the cells of the DViewSignature list.
*/
-public class SignerListCellRend extends DefaultListCellRenderer {
+public class SignerTreeCellRend extends DefaultTreeCellRenderer {
private static final long serialVersionUID = 1L;
/**
* Returns the rendered cell for the supplied value.
*
- * @param list The JList
+ * @param jtrHSigners The JTree
* @param value The value to assign to the cell
- * @param index The row index of the cell to render
* @param isSelected True if cell is selected
- * @param cellHasFocus If true, render cell appropriately
+ * @param isExpanded True if cell is expanded
+ * @param leaf True if cell is a leaf
+ * @param row The row of the cell to render
+ * @param hasFocus If true, render cell appropriately
* @return The rendered cell
*/
@Override
- public Component getListCellRendererComponent(JList> list, Object value, int index, boolean isSelected,
- boolean cellHasFocus) {
- JLabel cell = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ public Component getTreeCellRendererComponent(JTree jtrHSigners, Object value, boolean isSelected,
+ boolean isExpanded, boolean leaf, int row, boolean hasFocus) {
+ JLabel cell = (JLabel) super.getTreeCellRendererComponent(jtrHSigners, value, isSelected, isExpanded, leaf,
+ row, hasFocus);
- if (value instanceof KseSignerInformation) {
+ DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) value;
- KseSignerInformation signer = (KseSignerInformation) value;
+ Object userObject = treeNode.getUserObject();
+
+ if (userObject instanceof KseSignerInformation) {
+
+ KseSignerInformation signer = (KseSignerInformation) userObject;
X509CertificateHolder cert = signer.getCertificate();
cell.setText(signer.getShortName());
- // TODO JW Is an icon for signer list cell renderer desired?
-// ImageIcon icon = new ImageIcon(getClass().getResource("images/certificate_node.png"));
+ // TODO JW Is an icon for signer tree cell renderer desired?
+// String iconResource;
+// if (signer.isCounterSignature()) {
+// iconResource = "images/counter_signature_node.png";
+// } else {
+// iconResource = "images/signature_node.png";
+// }
+// ImageIcon icon = new ImageIcon(getClass().getResource(iconResource));
// cell.setIcon(icon);
String tooltip;
diff --git a/kse/src/si.pem b/kse/src/si.pem
new file mode 100644
index 000000000..9f90e2d3f
--- /dev/null
+++ b/kse/src/si.pem
@@ -0,0 +1,125 @@
+-----BEGIN PKCS7-----
+MIIW2AYJKoZIhvcNAQcCoIIWyTCCFsUCAQMxDTALBglghkgBZQMEAgEwggELBgsq
+hkiG9w0BCRABBKCB+wSB+DCB9QIBAQYJKwYBBAGgMgIDME8wCwYJYIZIAWUDBAID
+BEA3wY2MMoNyf4UW9mWa46Opxm3M/qT/wGoJiRDCILfBlKq8jbYqTy2AsCytAyzw
+rnTlEnTL0xzY5v+4LYdXbOqQAhQVqrt/Wi37OdGn7oE8VTxD9eqBvRgPMjAyNDEy
+MjQwNzE0MDZaMAMCAQECBgGT94QLk6BgpF4wXDELMAkGA1UEBhMCQkUxGTAXBgNV
+BAoMEEdsb2JhbFNpZ24gbnYtc2ExMjAwBgNVBAMMKUdsb2JhbHNpZ24gVFNBIGZv
+ciBBZHZhbmNlZCAtIEc0IC0gMjAyMzExoIISUzCCBmswggRToAMCAQICEAEZdXRx
+yZLXRN+lluu5cBUwDQYJKoZIhvcNAQELBQAwWzELMAkGA1UEBhMCQkUxGTAXBgNV
+BAoTEEdsb2JhbFNpZ24gbnYtc2ExMTAvBgNVBAMTKEdsb2JhbFNpZ24gVGltZXN0
+YW1waW5nIENBIC0gU0hBMzg0IC0gRzQwHhcNMjMxMTAyMTAzMDAyWhcNMzQxMjA0
+MTAzMDAyWjBcMQswCQYDVQQGEwJCRTEZMBcGA1UECgwQR2xvYmFsU2lnbiBudi1z
+YTEyMDAGA1UEAwwpR2xvYmFsc2lnbiBUU0EgZm9yIEFkdmFuY2VkIC0gRzQgLSAy
+MDIzMTEwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCyNUZ0qoON1Zan
+PEjVxcqo31S+CKuh31zpSdBgXrWlGvdDWEOXPPRnYwgyPBl/K9lVRtXUjMBcz6TF
+pRq6pyvOJkIhPOW7oaOV3WDqElWu787cMoTto7XgP3PRNbibu8VE3eG46/NZrYn2
+cY9aCvoKkgWEDZcBvwW7/FgBs43J1AWFp5ArbqzT2U7apyQ1lm+qs6BBO+D55xGO
+1WYCgC09zM8epJaLF4DcTDkaJHUsxXcW2ZGDJn/nE4uiRVTmtkp359ItLuewPEjZ
+xo37evQrvKYiSKLX3q14R4gMX5v0kUoGHPoDnmpWHisw4/OOWbC0Hx5hOIZ5+YOD
+lI8JMEIztA63iIIYLT/XgYsnoGnx0wWuxkWjwh+brenAyE/X58anQTJo/1nKVFz7
+v9kfFvBS0s+4NZWlkc6jHfV2UpjskWGLCaGtmZnorJQolziMCa48nPh+UaI3ashx
+uh1PDSYBVn5Xw3VC2FPgY2Pdfp4dqGLozv6ZWVP28wCK/ZOVz9ECAwEAAaOCAagw
+ggGkMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAdBgNV
+HQ4EFgQUxL7uhzyJdA7es+4ZG4UMzkFOf50wVgYDVR0gBE8wTTAIBgZngQwBBAIw
+QQYJKwYBBAGgMgEeMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNp
+Z24uY29tL3JlcG9zaXRvcnkvMAwGA1UdEwEB/wQCMAAwgZAGCCsGAQUFBwEBBIGD
+MIGAMDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9jYS9n
+c3RzYWNhc2hhMzg0ZzQwQwYIKwYBBQUHMAKGN2h0dHA6Ly9zZWN1cmUuZ2xvYmFs
+c2lnbi5jb20vY2FjZXJ0L2dzdHNhY2FzaGEzODRnNC5jcnQwHwYDVR0jBBgwFoAU
+6hbGaefjy1dFOTOk8EC+0MO9ZZYwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2Ny
+bC5nbG9iYWxzaWduLmNvbS9jYS9nc3RzYWNhc2hhMzg0ZzQuY3JsMA0GCSqGSIb3
+DQEBCwUAA4ICAQCzMtHqZ//b36e0N0Rd7R6+diPJzgPtTdRq5zOMPF8gYtvu6Ww4
+OeWZcfsmkR8nsXNcAxnPaDLQ1eZ2eEqqPJcy0hXuehwyPGCnQcq5PvFB6sPT8cfl
+vt4axsGOIt/WgOWP8qyyIY14tsSJjJS9MnO42JdEPNdmbA0cEFxeqIhAvaCuTlot
+ZE8GJaWExjhwx1RzFI1XFqkwHKgJSd+lAQYDvxOzdJSbB4GvDUGQVSmwYKlU+jgg
+M84Jug5MZ1iBhqntiIapmOO25UaXJEdsSNEQaspxsj5dwz0tIYJrg2Nvl8CR/vt9
+lrmqwBzNpa2QeIDWfW2JKkCOrCX664g2I36G8vu1Bu0ogyyz2pp6b0gRFpQ2tUVA
+nYE1DcWxjJs75jzpehhQ+TmKkne7kSJuoLlbKgFAKOTRSKkwjqKGEjdNyVmZx6YD
+f+GRCn0K+AtCDnGu9s+65TH4+R8t8OAKjISMpTmjO7DzNtlD1ZuYJA/QwuMmPq3h
++/seq94G9vtoQewx36nJHowZ9j72Hpgu0WCBWyZ09FROQATftV7U9+7wDYdvQECn
+aeooyKGpT3cSiTFq6ZqDd4upxUQz7rdpTiy0p7SVeJvWqkAsNhqnREOzUthgxnNX
+v3zWNdMjo2BCItYWFc4TGunO9eXPWr6sP3Pp+nO/Gc2il2bKHGANor1UzDCCBlkw
+ggRBoAMCAQICDQHsHJJA3v0uQF18R3QwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UE
+CxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24x
+EzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTgwNjIwMDAwMDAwWhcNMzQxMjEwMDAw
+MDAwWjBbMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEx
+MC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEzODQgLSBH
+NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPAC4jAj+uAb4Zp0s691
+g1+pR1LHYTpjfDkjeW10/DHkdBIZlvrOJ2JbrgeKJ+5Xo8Q17bM0x6zDDOuAZm3R
+KErBLLu5cPJyroz3mVpddq6/RKh8QSSOj7rFT/82QaunLf14TkOI/pMZF9nuMc+8
+ijtuasSI8O6X9tzzGKBLmRwOh6cm4YjJoOWZ4p70nEw/XVvstu/SZc9FC1Q9sVRT
+B4uZbrhUmYqoMZI78np9/A5Y34Fq4bBsHmWCKtQhx5T+QpY78Quxf39GmA6HPXpl
+69FWqS69+1g9tYX6U5lNW3TtckuiDYI3GQzQq+pawe8P1Zm5P/RPNfGcD9M3E1LZ
+JTTtlu/4Z+oIvo9Jev+QsdT3KRXX+Q1d1odDHnTEcCi0gHu9Kpu7hOEOrG8NubX2
+bVb+ih0JPiQOZybH/LINoJSwspTMe+Zn/qZYstTYQRLBVf1ukcW7sUwIS57UQgZv
+GxjVNupkrs799QXm4mbQDgUhrLERBiMZ5PsFNETqCK6dSWcRi4LlrVqGp2b9MwMB
+3pkl+XFu6ZxdAkxgPM8CjwH9cu6S8acS3kISTeypJuV3AqwOVwwJ0WGeJoj8yLJN
+22TwRZ+6wT9Uo9h2ApVsao3KIlz2DATjKfpLsBzTN3SE2R1mqzRzjx59fF6W1j0Z
+sJfqjFCRba9Xhn4QNx1rGhTfAgMBAAGjggEpMIIBJTAOBgNVHQ8BAf8EBAMCAYYw
+EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6hbGaefjy1dFOTOk8EC+0MO9
+ZZYwHwYDVR0jBBgwFoAUrmwFo5MT4qLn4tcc1sfwf8hnU6AwPgYIKwYBBQUHAQEE
+MjAwMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vcm9v
+dHI2MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20v
+cm9vdC1yNi5jcmwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0
+dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEB
+DAUAA4ICAQB/4ojZV2crQl+BpwkLusS7KBhW1ky/2xsHcMb7CwmtADpgMx85xhZr
+GUBJJQge5Jv31qQNjx6W8oaiF95Bv0/hvKvN7sAjjMaF/ksVJPkYROwfwqSs0LLP
+7MJWZR29f/begsi3n2HTtUZImJcCZ3oWlUrbYsbQswLMNEhFVd3s6UqfXhTtchBx
+dnDSD5bz6jdXlJEYr9yNmTgZWMKpoX6ibhUm6rT5fyrn50hkaS/SmqFy9vckS3Ra
+fXKGNbMCVx+LnPy7rEze+t5TTIP9ErG2SVVPdZ2sb0rILmq5yojDEjBOsghzn16h
+1pnO6X1LlizMFmsYzeRZN4YJLOJF1rLNboJ1pdqNHrdbL4guPX3x8pEwBZzOe3yg
+xayvUQbwEccdMMVRVmDofJU9IuPVCiRTJ5eA+kiJJyx54jzlmx7jqoSCiT7ASvUh
+/mIQ7R0w/PbM6kgnfIt1Qn9ry/Ola5UfBFg0ContglDk0Xuoyea+SKorVdmNtyUg
+DhtRoNRjqoPqbHJhSsn6Q8TGV8Wdtjywi7C5HDHvve8U2BRAbCAdwi3oC8aNbYy2
+ce1SIf4+9p+fORqurNIveiCx9KyqHeItFJ36lmodxjzK89kcv1NNpEdZfJXEQ0H5
+JeIsEH6B+Q2Up33ytQn12GByQFCVINRDRL76oJXnIFm2eMakaqoimzCCBYMwggNr
+oAMCAQICDkXmuwODM8OFZUjm/0VRMA0GCSqGSIb3DQEBDAUAMEwxIDAeBgNVBAsT
+F0dsb2JhbFNpZ24gUm9vdCBDQSAtIFI2MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMw
+EQYDVQQDEwpHbG9iYWxTaWduMB4XDTE0MTIxMDAwMDAwMFoXDTM0MTIxMDAwMDAw
+MFowTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoT
+Ckdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCVB+hzymb57BTKezz3DQjxtEULLIK0SMbrWzyug7hB
+kjMUpG9/6SrMxrCIa8W2idHGsv8UzlEUIexK3RtaxtaH7k06FQbtZGYLkoDKRN5z
+lE7zp4l/T3hjCMgSUG1CZi9NuXkoTVIaihqAtxmBDn7EirxkTCEcQ2jXPTyKxbJm
+1ZCatzEGxb7ibTIGph75ueuqo7i/voJjUNDwGInf5A959eqiHyrScC5757yTu21T
+4kh8jBAHOP9msndhfuDqjDyqtKT285VKEgdt/Yyyic/QoGF3yFh0sNQjOvddOsqi
+250J3l1ELZDxgc1Xkvp+vFAEYzTfa5MYvms2sjnkrCQ2t/DvthwTV5O23rL44oW3
+c6K4NapF8uCdNqFvVIrxclZuLojFUUJEFZTuo8U4lptOTloLR/MGNkl3MLxxN+Wm
+7CEIdfzmYRY/d9XZkZeECmzUAk10wBTt/Tn7g/JeFKEEsAvp/u6P4W4LsgizYWYJ
+arEGOmWWWcDwNf3J2iiNGhGHcIEKqJp1HZ46hgUAntuA1iX53AWeJ1lMdjlb6vml
+odiDD9H/3zAR+YXPM0j1ym1kFCx6WE/TSwhJxZVkGmMOeT31s4zKWK2cQkV5bg6H
+GVxUsWW2v4yb3BPpDW+4LtxnbsmLEbWEFIoAGXCDeZGXkdQaJ783HjIH2BRjPChM
+rwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUrmwFo5MT4qLn4tcc1sfwf8hnU6AwHwYDVR0jBBgwFoAUrmwFo5MT4qLn
+4tcc1sfwf8hnU6AwDQYJKoZIhvcNAQEMBQADggIBAIMl7ejR/ZVSzZ7ABKCRaeZc
+0ITe3K2iT+hHeNZlmKlbqDyHfAKK0W63FnPmX8BUmNV0vsHN4hGRrSMYPd3hckSW
+tJVewHuOmXgWQxNWV7Oiszu1d9xAcqyj65s1PrEIIaHnxEM3eTK+teecLEy8QymZ
+jjDTrCHg4x362AczdlQAIiq5TSAucGja5VP8g1zTnfL/RAxEZvLS471GABptArol
+XY2hMVHdVEYcTduZlu8aHARcphXveOB5/l3bPqpMVf2aFalv4ab733Aw6cPuQkbt
+wpMFifp9Y3s/0HGBfADomK4OeDTDJfuvCp8ga907E48SjOJBGkh6c6B3ace2XH+C
+yB7+WBsoK6hsrV5twAXSe7frgP4lN/4Cm2isQl3D7vXM3PBQddI2aZzmewTfbgZp
+tt4KCUhZh+t7FGB6ZKppQ++Rx0zsGN1s71MtjJnhXvJyPs9UyL1n7KQPTEX/07kw
+IwdMjxC/hpbZmVq0mVccpMy7FYlTuiwFD+TEnhmxGDTVTJ267fcfrySVBHioA7vu
+geXaX3yLSqGQdCWnsz5LyCxWvcfI7zjiXJLwefechLp0LWEBIH5+0fJPB1lfiy1D
+UutGDJTh9WZHeXfVVFsfrSQ3y0VaTqBESMjYsJnFFYQJ9tZJScBluOYacW6gqPGC
+6EU+bNYC1wpngwVayaQQMYIDSTCCA0UCAQEwbzBbMQswCQYDVQQGEwJCRTEZMBcG
+A1UEChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1l
+c3RhbXBpbmcgQ0EgLSBTSEEzODQgLSBHNAIQARl1dHHJktdE36WW67lwFTALBglg
+hkgBZQMEAgGgggEtMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDArBgkqhkiG
+9w0BCTQxHjAcMAsGCWCGSAFlAwQCAaENBgkqhkiG9w0BAQsFADAvBgkqhkiG9w0B
+CQQxIgQgpIpFy9JJnkXEHGlnv5D5GjBkFsza/q50u8UrTL/cicEwgbAGCyqGSIb3
+DQEJEAIvMYGgMIGdMIGaMIGXBCALeaI5rkIQje9Ws1QFv4/NjlmnS4Tu4t7D2XHB
+6hc07DBzMF+kXTBbMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBu
+di1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEz
+ODQgLSBHNAIQARl1dHHJktdE36WW67lwFTANBgkqhkiG9w0BAQsFAASCAYBXobHF
+kM1g0aJ82UC/4EENczbXSfEyM0MvtJNo3cr0I+r1NhVzqYYGEQA8ep6O8i3t1rlY
+1pJUwXVA9z8kQ6dENTxVbJs/h9h0wGWaaQKiUfaq+MMmlHPWbXETUw/mY5o7DgLY
+luBluv3Sk86WJVAiuUwWZDpKsN2sG8q0Z9nSAUzGxoyLf+NwTsxPJxH2k2D6vW7e
+DWxVIlj+BU/WzPjreQda+BY78f/6jKdqk4/KweSNUraMZdxdyIg852DeX0S0g6cW
+F/H4evnSOtaUnhMWHmgZqjOdp2uiFXCcXq0IhjaP5m+LX4No1ocnRziNZzhY3cHP
+RWA2VBz1iSrSWQOsL/GOuUsOXD7b00XAKWBFEZ9Mp9CcIb1qrtjh4Vz5NY0aDalm
+FBwRtKsF8XZvkYQkCk3msDhRrbBaMYpsx1jl81laOQ1zyXB3mafBITYRKpbS3v1o
++VPYxY3No74X/tx5sUp6yPjZqsTwHALdPk0W4bfE+266nP6YQn/r/twb+S4=
+-----END PKCS7-----
+
From 765eaa0d726d9f36d295b910068468209fbbbe8a Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:28:31 -0800
Subject: [PATCH 56/69] Removed inadvertently added file.
---
kse/src/si.pem | 125 -------------------------------------------------
1 file changed, 125 deletions(-)
delete mode 100644 kse/src/si.pem
diff --git a/kse/src/si.pem b/kse/src/si.pem
deleted file mode 100644
index 9f90e2d3f..000000000
--- a/kse/src/si.pem
+++ /dev/null
@@ -1,125 +0,0 @@
------BEGIN PKCS7-----
-MIIW2AYJKoZIhvcNAQcCoIIWyTCCFsUCAQMxDTALBglghkgBZQMEAgEwggELBgsq
-hkiG9w0BCRABBKCB+wSB+DCB9QIBAQYJKwYBBAGgMgIDME8wCwYJYIZIAWUDBAID
-BEA3wY2MMoNyf4UW9mWa46Opxm3M/qT/wGoJiRDCILfBlKq8jbYqTy2AsCytAyzw
-rnTlEnTL0xzY5v+4LYdXbOqQAhQVqrt/Wi37OdGn7oE8VTxD9eqBvRgPMjAyNDEy
-MjQwNzE0MDZaMAMCAQECBgGT94QLk6BgpF4wXDELMAkGA1UEBhMCQkUxGTAXBgNV
-BAoMEEdsb2JhbFNpZ24gbnYtc2ExMjAwBgNVBAMMKUdsb2JhbHNpZ24gVFNBIGZv
-ciBBZHZhbmNlZCAtIEc0IC0gMjAyMzExoIISUzCCBmswggRToAMCAQICEAEZdXRx
-yZLXRN+lluu5cBUwDQYJKoZIhvcNAQELBQAwWzELMAkGA1UEBhMCQkUxGTAXBgNV
-BAoTEEdsb2JhbFNpZ24gbnYtc2ExMTAvBgNVBAMTKEdsb2JhbFNpZ24gVGltZXN0
-YW1waW5nIENBIC0gU0hBMzg0IC0gRzQwHhcNMjMxMTAyMTAzMDAyWhcNMzQxMjA0
-MTAzMDAyWjBcMQswCQYDVQQGEwJCRTEZMBcGA1UECgwQR2xvYmFsU2lnbiBudi1z
-YTEyMDAGA1UEAwwpR2xvYmFsc2lnbiBUU0EgZm9yIEFkdmFuY2VkIC0gRzQgLSAy
-MDIzMTEwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCyNUZ0qoON1Zan
-PEjVxcqo31S+CKuh31zpSdBgXrWlGvdDWEOXPPRnYwgyPBl/K9lVRtXUjMBcz6TF
-pRq6pyvOJkIhPOW7oaOV3WDqElWu787cMoTto7XgP3PRNbibu8VE3eG46/NZrYn2
-cY9aCvoKkgWEDZcBvwW7/FgBs43J1AWFp5ArbqzT2U7apyQ1lm+qs6BBO+D55xGO
-1WYCgC09zM8epJaLF4DcTDkaJHUsxXcW2ZGDJn/nE4uiRVTmtkp359ItLuewPEjZ
-xo37evQrvKYiSKLX3q14R4gMX5v0kUoGHPoDnmpWHisw4/OOWbC0Hx5hOIZ5+YOD
-lI8JMEIztA63iIIYLT/XgYsnoGnx0wWuxkWjwh+brenAyE/X58anQTJo/1nKVFz7
-v9kfFvBS0s+4NZWlkc6jHfV2UpjskWGLCaGtmZnorJQolziMCa48nPh+UaI3ashx
-uh1PDSYBVn5Xw3VC2FPgY2Pdfp4dqGLozv6ZWVP28wCK/ZOVz9ECAwEAAaOCAagw
-ggGkMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAdBgNV
-HQ4EFgQUxL7uhzyJdA7es+4ZG4UMzkFOf50wVgYDVR0gBE8wTTAIBgZngQwBBAIw
-QQYJKwYBBAGgMgEeMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNp
-Z24uY29tL3JlcG9zaXRvcnkvMAwGA1UdEwEB/wQCMAAwgZAGCCsGAQUFBwEBBIGD
-MIGAMDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9jYS9n
-c3RzYWNhc2hhMzg0ZzQwQwYIKwYBBQUHMAKGN2h0dHA6Ly9zZWN1cmUuZ2xvYmFs
-c2lnbi5jb20vY2FjZXJ0L2dzdHNhY2FzaGEzODRnNC5jcnQwHwYDVR0jBBgwFoAU
-6hbGaefjy1dFOTOk8EC+0MO9ZZYwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2Ny
-bC5nbG9iYWxzaWduLmNvbS9jYS9nc3RzYWNhc2hhMzg0ZzQuY3JsMA0GCSqGSIb3
-DQEBCwUAA4ICAQCzMtHqZ//b36e0N0Rd7R6+diPJzgPtTdRq5zOMPF8gYtvu6Ww4
-OeWZcfsmkR8nsXNcAxnPaDLQ1eZ2eEqqPJcy0hXuehwyPGCnQcq5PvFB6sPT8cfl
-vt4axsGOIt/WgOWP8qyyIY14tsSJjJS9MnO42JdEPNdmbA0cEFxeqIhAvaCuTlot
-ZE8GJaWExjhwx1RzFI1XFqkwHKgJSd+lAQYDvxOzdJSbB4GvDUGQVSmwYKlU+jgg
-M84Jug5MZ1iBhqntiIapmOO25UaXJEdsSNEQaspxsj5dwz0tIYJrg2Nvl8CR/vt9
-lrmqwBzNpa2QeIDWfW2JKkCOrCX664g2I36G8vu1Bu0ogyyz2pp6b0gRFpQ2tUVA
-nYE1DcWxjJs75jzpehhQ+TmKkne7kSJuoLlbKgFAKOTRSKkwjqKGEjdNyVmZx6YD
-f+GRCn0K+AtCDnGu9s+65TH4+R8t8OAKjISMpTmjO7DzNtlD1ZuYJA/QwuMmPq3h
-+/seq94G9vtoQewx36nJHowZ9j72Hpgu0WCBWyZ09FROQATftV7U9+7wDYdvQECn
-aeooyKGpT3cSiTFq6ZqDd4upxUQz7rdpTiy0p7SVeJvWqkAsNhqnREOzUthgxnNX
-v3zWNdMjo2BCItYWFc4TGunO9eXPWr6sP3Pp+nO/Gc2il2bKHGANor1UzDCCBlkw
-ggRBoAMCAQICDQHsHJJA3v0uQF18R3QwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UE
-CxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24x
-EzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTgwNjIwMDAwMDAwWhcNMzQxMjEwMDAw
-MDAwWjBbMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEx
-MC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEzODQgLSBH
-NDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPAC4jAj+uAb4Zp0s691
-g1+pR1LHYTpjfDkjeW10/DHkdBIZlvrOJ2JbrgeKJ+5Xo8Q17bM0x6zDDOuAZm3R
-KErBLLu5cPJyroz3mVpddq6/RKh8QSSOj7rFT/82QaunLf14TkOI/pMZF9nuMc+8
-ijtuasSI8O6X9tzzGKBLmRwOh6cm4YjJoOWZ4p70nEw/XVvstu/SZc9FC1Q9sVRT
-B4uZbrhUmYqoMZI78np9/A5Y34Fq4bBsHmWCKtQhx5T+QpY78Quxf39GmA6HPXpl
-69FWqS69+1g9tYX6U5lNW3TtckuiDYI3GQzQq+pawe8P1Zm5P/RPNfGcD9M3E1LZ
-JTTtlu/4Z+oIvo9Jev+QsdT3KRXX+Q1d1odDHnTEcCi0gHu9Kpu7hOEOrG8NubX2
-bVb+ih0JPiQOZybH/LINoJSwspTMe+Zn/qZYstTYQRLBVf1ukcW7sUwIS57UQgZv
-GxjVNupkrs799QXm4mbQDgUhrLERBiMZ5PsFNETqCK6dSWcRi4LlrVqGp2b9MwMB
-3pkl+XFu6ZxdAkxgPM8CjwH9cu6S8acS3kISTeypJuV3AqwOVwwJ0WGeJoj8yLJN
-22TwRZ+6wT9Uo9h2ApVsao3KIlz2DATjKfpLsBzTN3SE2R1mqzRzjx59fF6W1j0Z
-sJfqjFCRba9Xhn4QNx1rGhTfAgMBAAGjggEpMIIBJTAOBgNVHQ8BAf8EBAMCAYYw
-EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU6hbGaefjy1dFOTOk8EC+0MO9
-ZZYwHwYDVR0jBBgwFoAUrmwFo5MT4qLn4tcc1sfwf8hnU6AwPgYIKwYBBQUHAQEE
-MjAwMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vcm9v
-dHI2MDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20v
-cm9vdC1yNi5jcmwwRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0
-dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMA0GCSqGSIb3DQEB
-DAUAA4ICAQB/4ojZV2crQl+BpwkLusS7KBhW1ky/2xsHcMb7CwmtADpgMx85xhZr
-GUBJJQge5Jv31qQNjx6W8oaiF95Bv0/hvKvN7sAjjMaF/ksVJPkYROwfwqSs0LLP
-7MJWZR29f/begsi3n2HTtUZImJcCZ3oWlUrbYsbQswLMNEhFVd3s6UqfXhTtchBx
-dnDSD5bz6jdXlJEYr9yNmTgZWMKpoX6ibhUm6rT5fyrn50hkaS/SmqFy9vckS3Ra
-fXKGNbMCVx+LnPy7rEze+t5TTIP9ErG2SVVPdZ2sb0rILmq5yojDEjBOsghzn16h
-1pnO6X1LlizMFmsYzeRZN4YJLOJF1rLNboJ1pdqNHrdbL4guPX3x8pEwBZzOe3yg
-xayvUQbwEccdMMVRVmDofJU9IuPVCiRTJ5eA+kiJJyx54jzlmx7jqoSCiT7ASvUh
-/mIQ7R0w/PbM6kgnfIt1Qn9ry/Ola5UfBFg0ContglDk0Xuoyea+SKorVdmNtyUg
-DhtRoNRjqoPqbHJhSsn6Q8TGV8Wdtjywi7C5HDHvve8U2BRAbCAdwi3oC8aNbYy2
-ce1SIf4+9p+fORqurNIveiCx9KyqHeItFJ36lmodxjzK89kcv1NNpEdZfJXEQ0H5
-JeIsEH6B+Q2Up33ytQn12GByQFCVINRDRL76oJXnIFm2eMakaqoimzCCBYMwggNr
-oAMCAQICDkXmuwODM8OFZUjm/0VRMA0GCSqGSIb3DQEBDAUAMEwxIDAeBgNVBAsT
-F0dsb2JhbFNpZ24gUm9vdCBDQSAtIFI2MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMw
-EQYDVQQDEwpHbG9iYWxTaWduMB4XDTE0MTIxMDAwMDAwMFoXDTM0MTIxMDAwMDAw
-MFowTDEgMB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoT
-Ckdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQCVB+hzymb57BTKezz3DQjxtEULLIK0SMbrWzyug7hB
-kjMUpG9/6SrMxrCIa8W2idHGsv8UzlEUIexK3RtaxtaH7k06FQbtZGYLkoDKRN5z
-lE7zp4l/T3hjCMgSUG1CZi9NuXkoTVIaihqAtxmBDn7EirxkTCEcQ2jXPTyKxbJm
-1ZCatzEGxb7ibTIGph75ueuqo7i/voJjUNDwGInf5A959eqiHyrScC5757yTu21T
-4kh8jBAHOP9msndhfuDqjDyqtKT285VKEgdt/Yyyic/QoGF3yFh0sNQjOvddOsqi
-250J3l1ELZDxgc1Xkvp+vFAEYzTfa5MYvms2sjnkrCQ2t/DvthwTV5O23rL44oW3
-c6K4NapF8uCdNqFvVIrxclZuLojFUUJEFZTuo8U4lptOTloLR/MGNkl3MLxxN+Wm
-7CEIdfzmYRY/d9XZkZeECmzUAk10wBTt/Tn7g/JeFKEEsAvp/u6P4W4LsgizYWYJ
-arEGOmWWWcDwNf3J2iiNGhGHcIEKqJp1HZ46hgUAntuA1iX53AWeJ1lMdjlb6vml
-odiDD9H/3zAR+YXPM0j1ym1kFCx6WE/TSwhJxZVkGmMOeT31s4zKWK2cQkV5bg6H
-GVxUsWW2v4yb3BPpDW+4LtxnbsmLEbWEFIoAGXCDeZGXkdQaJ783HjIH2BRjPChM
-rwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
-HQ4EFgQUrmwFo5MT4qLn4tcc1sfwf8hnU6AwHwYDVR0jBBgwFoAUrmwFo5MT4qLn
-4tcc1sfwf8hnU6AwDQYJKoZIhvcNAQEMBQADggIBAIMl7ejR/ZVSzZ7ABKCRaeZc
-0ITe3K2iT+hHeNZlmKlbqDyHfAKK0W63FnPmX8BUmNV0vsHN4hGRrSMYPd3hckSW
-tJVewHuOmXgWQxNWV7Oiszu1d9xAcqyj65s1PrEIIaHnxEM3eTK+teecLEy8QymZ
-jjDTrCHg4x362AczdlQAIiq5TSAucGja5VP8g1zTnfL/RAxEZvLS471GABptArol
-XY2hMVHdVEYcTduZlu8aHARcphXveOB5/l3bPqpMVf2aFalv4ab733Aw6cPuQkbt
-wpMFifp9Y3s/0HGBfADomK4OeDTDJfuvCp8ga907E48SjOJBGkh6c6B3ace2XH+C
-yB7+WBsoK6hsrV5twAXSe7frgP4lN/4Cm2isQl3D7vXM3PBQddI2aZzmewTfbgZp
-tt4KCUhZh+t7FGB6ZKppQ++Rx0zsGN1s71MtjJnhXvJyPs9UyL1n7KQPTEX/07kw
-IwdMjxC/hpbZmVq0mVccpMy7FYlTuiwFD+TEnhmxGDTVTJ267fcfrySVBHioA7vu
-geXaX3yLSqGQdCWnsz5LyCxWvcfI7zjiXJLwefechLp0LWEBIH5+0fJPB1lfiy1D
-UutGDJTh9WZHeXfVVFsfrSQ3y0VaTqBESMjYsJnFFYQJ9tZJScBluOYacW6gqPGC
-6EU+bNYC1wpngwVayaQQMYIDSTCCA0UCAQEwbzBbMQswCQYDVQQGEwJCRTEZMBcG
-A1UEChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1l
-c3RhbXBpbmcgQ0EgLSBTSEEzODQgLSBHNAIQARl1dHHJktdE36WW67lwFTALBglg
-hkgBZQMEAgGgggEtMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDArBgkqhkiG
-9w0BCTQxHjAcMAsGCWCGSAFlAwQCAaENBgkqhkiG9w0BAQsFADAvBgkqhkiG9w0B
-CQQxIgQgpIpFy9JJnkXEHGlnv5D5GjBkFsza/q50u8UrTL/cicEwgbAGCyqGSIb3
-DQEJEAIvMYGgMIGdMIGaMIGXBCALeaI5rkIQje9Ws1QFv4/NjlmnS4Tu4t7D2XHB
-6hc07DBzMF+kXTBbMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBu
-di1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEz
-ODQgLSBHNAIQARl1dHHJktdE36WW67lwFTANBgkqhkiG9w0BAQsFAASCAYBXobHF
-kM1g0aJ82UC/4EENczbXSfEyM0MvtJNo3cr0I+r1NhVzqYYGEQA8ep6O8i3t1rlY
-1pJUwXVA9z8kQ6dENTxVbJs/h9h0wGWaaQKiUfaq+MMmlHPWbXETUw/mY5o7DgLY
-luBluv3Sk86WJVAiuUwWZDpKsN2sG8q0Z9nSAUzGxoyLf+NwTsxPJxH2k2D6vW7e
-DWxVIlj+BU/WzPjreQda+BY78f/6jKdqk4/KweSNUraMZdxdyIg852DeX0S0g6cW
-F/H4evnSOtaUnhMWHmgZqjOdp2uiFXCcXq0IhjaP5m+LX4No1ocnRziNZzhY3cHP
-RWA2VBz1iSrSWQOsL/GOuUsOXD7b00XAKWBFEZ9Mp9CcIb1qrtjh4Vz5NY0aDalm
-FBwRtKsF8XZvkYQkCk3msDhRrbBaMYpsx1jl81laOQ1zyXB3mafBITYRKpbS3v1o
-+VPYxY3No74X/tx5sUp6yPjZqsTwHALdPk0W4bfE+266nP6YQn/r/twb+S4=
------END PKCS7-----
-
From 80ccb7e037ea662787fa7e9999580f21fc84d419 Mon Sep 17 00:00:00 2001
From: jonwltn <86822083+jonwltn@users.noreply.github.com>
Date: Tue, 24 Dec 2024 21:40:16 -0800
Subject: [PATCH 57/69] Cleaned up warnings. Fixed selection of first tree
node.
---
.../crypto/signing/KseSignerInformation.java | 3 +-
.../org/kse/gui/dialogs/DViewSignature.java | 35 ++++++++-----------
2 files changed, 16 insertions(+), 22 deletions(-)
diff --git a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
index c25766a9c..30f37e9cd 100644
--- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
+++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java
@@ -232,6 +232,7 @@ private void verify() {
if (verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
verified = true;
+ // TODO JW always use the cacerts store for validating the timestamp trust.
// TODO JW Display TS validation status on form.
verifyTimeStamp();
@@ -276,7 +277,7 @@ private boolean verifyCounterSignatures() {
}
private void verifyTimeStamp()
- throws TSPException, IOException, TSPValidationException, OperatorCreationException, CertificateException {
+ throws IOException, TSPException, TSPValidationException, OperatorCreationException, CertificateException {
ContentInfo timeStamp = getTimeStamp();
diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
index 27a104a1f..7e6767141 100644
--- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
+++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java
@@ -136,17 +136,16 @@ public class DViewSignature extends JEscDialog {
* @param signedData Signature to display
* @param signers Signature(s) to display
* @param kseFrame Reference to main class with currently opened keystores and their contents
- * @throws CryptoException A problem was encountered getting the certificates' details
*/
public DViewSignature(Window parent, String title, CMSSignedData signedData, Collection signers,
- KseFrame kseFrame) throws CryptoException {
+ KseFrame kseFrame) {
super(parent, title, Dialog.ModalityType.MODELESS);
this.kseFrame = kseFrame;
this.signedData = signedData;
initComponents(signers);
}
- private void initComponents(Collection