From ca39bf633990a4abe7ea135d0f8e2a215ae2d6c0 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:53:57 -0800 Subject: [PATCH 01/69] Initial buttons and menu items for verify signature action. --- kse/src/main/java/org/kse/gui/KseFrame.java | 43 ++ .../gui/actions/VerifySignatureAction.java | 514 ++++++++++++++++++ .../org/kse/gui/actions/resources.properties | 25 + .../org/kse/gui/resources.properties | 1 + 4 files changed, 583 insertions(+) create mode 100644 kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java diff --git a/kse/src/main/java/org/kse/gui/KseFrame.java b/kse/src/main/java/org/kse/gui/KseFrame.java index 646ddc4dd..a7d1ce4e8 100644 --- a/kse/src/main/java/org/kse/gui/KseFrame.java +++ b/kse/src/main/java/org/kse/gui/KseFrame.java @@ -178,6 +178,7 @@ import org.kse.gui.actions.UnlockKeyAction; import org.kse.gui.actions.UnlockKeyPairAction; import org.kse.gui.actions.VerifyCertificateAction; +import org.kse.gui.actions.VerifySignatureAction; import org.kse.gui.actions.WebsiteAction; import org.kse.gui.dnd.DragEntry; import org.kse.gui.dnd.DragKeyPairEntry; @@ -258,6 +259,7 @@ public final class KseFrame implements StatusBar { private JMenuItem jmiGenerateSecretKey; private JMenuItem jmiImportTrustedCertificate; private JMenuItem jmiImportKeyPair; + private JMenuItem jmiVerifySignature; private JMenu jmChangeType; private JRadioButtonMenuItem jrbmiChangeTypeJks; private JRadioButtonMenuItem jrbmiChangeTypeJceks; @@ -306,6 +308,7 @@ public final class KseFrame implements StatusBar { private JButton jbGenerateSecretKey; private JButton jbImportTrustedCertificate; private JButton jbImportKeyPair; + private JButton jbVerifySignature; private JButton jbSetPassword; private JButton jbProperties; private JButton jbExamineFile; @@ -489,6 +492,7 @@ public final class KseFrame implements StatusBar { private final ExportKeyPairPublicKeyAction exportKeyPairPublicKeyAction = new ExportKeyPairPublicKeyAction(this); private final GenerateCsrAction generateCsrAction = new GenerateCsrAction(this); private final VerifyCertificateAction verifyCertificateAction = new VerifyCertificateAction(this); + private final VerifySignatureAction verifySignatureAction = new VerifySignatureAction(this); private final ImportCaReplyFromFileAction importCaReplyFromFileAction = new ImportCaReplyFromFileAction(this); private final ImportCaReplyFromClipboardAction importCaReplyFromClipboardAction = new ImportCaReplyFromClipboardAction( @@ -900,6 +904,15 @@ private void initMenu() { jmTools.addSeparator(); + jmiVerifySignature = new JMenuItem(verifySignatureAction); + PlatformUtil.setMnemonic(jmiVerifySignature, res.getString("KseFrame.jmiVerifySignature.mnemonic").charAt(0)); + jmiVerifySignature.setToolTipText(null); + new StatusBarChangeHandler(jmiVerifySignature, (String) verifySignatureAction.getValue(Action.LONG_DESCRIPTION), + this); + jmTools.add(jmiVerifySignature); + + jmTools.addSeparator(); + jmiSetPassword = new JMenuItem(setPasswordAction); PlatformUtil.setMnemonic(jmiSetPassword, res.getString("KseFrame.jmiSetPassword.mnemonic").charAt(0)); jmiSetPassword.setToolTipText(null); @@ -1329,6 +1342,23 @@ public void mouseExited(MouseEvent evt) { } }); + jbVerifySignature = new JButton(); + jbVerifySignature.setAction(verifySignatureAction); + jbVerifySignature.setText(null); + PlatformUtil.setMnemonic(jbVerifySignature, 0); + jbVerifySignature.setFocusable(false); + jbVerifySignature.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent evt) { + setStatusBarText((String) verifySignatureAction.getValue(Action.LONG_DESCRIPTION)); + } + + @Override + public void mouseExited(MouseEvent evt) { + setDefaultStatusBarText(); + } + }); + jbSetPassword = new JButton(); jbSetPassword.setAction(setPasswordAction); jbSetPassword.setText(null); @@ -1464,6 +1494,7 @@ public void mouseExited(MouseEvent evt) { jtbToolBar.add(jbGenerateSecretKey); jtbToolBar.add(jbImportTrustedCertificate); jtbToolBar.add(jbImportKeyPair); + jtbToolBar.add(jbVerifySignature); jtbToolBar.add(jbSetPassword); jtbToolBar.add(jbProperties); @@ -1796,6 +1827,16 @@ 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); + new StatusBarChangeHandler(jmiVerifySignature, (String) verifySignatureAction.getValue(Action.LONG_DESCRIPTION), + this); + jpmKeyStore.add(jmiVerifySignature); + + jpmKeyStore.addSeparator(); + jmiKeyStoreSetPassword = new JMenuItem(setPasswordAction); PlatformUtil.setMnemonic(jmiKeyStoreSetPassword, res.getString("KseFrame.jmiSetPassword.mnemonic").charAt(0)); jmiKeyStoreSetPassword.setToolTipText(null); @@ -2778,6 +2819,7 @@ public void updateControls(boolean keyStoreContentsChanged) { generateSecretKeyAction.setEnabled(type.supportsKeyEntries()); importTrustedCertificateAction.setEnabled(true); importKeyPairAction.setEnabled(true); + verifySignatureAction.setEnabled(true); propertiesAction.setEnabled(true); if (type.isFileBased()) { setPasswordAction.setEnabled(true); @@ -2904,6 +2946,7 @@ private void updateControlsNoKeyStoresOpen() { generateSecretKeyAction.setEnabled(false); importTrustedCertificateAction.setEnabled(false); importKeyPairAction.setEnabled(false); + verifySignatureAction.setEnabled(false); setPasswordAction.setEnabled(false); jmChangeType.setEnabled(false); propertiesAction.setEnabled(false); diff --git a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java new file mode 100644 index 000000000..51bd77215 --- /dev/null +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -0,0 +1,514 @@ +/* + * 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.HeadlessException; +import java.awt.Toolkit; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.URL; +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.List; +import java.util.Set; + +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.bouncycastle.cert.ocsp.BasicOCSPResp; +import org.bouncycastle.cert.ocsp.CertificateID; +import org.bouncycastle.cert.ocsp.CertificateStatus; +import org.bouncycastle.cert.ocsp.OCSPException; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPReqBuilder; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.ocsp.RevokedStatus; +import org.bouncycastle.cert.ocsp.SingleResp; +import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; +import org.bouncycastle.operator.DigestCalculatorProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.kse.KSE; +import org.kse.crypto.CryptoException; +import org.kse.crypto.x509.X509CertUtil; +import org.kse.gui.KseFrame; +import org.kse.gui.dialogs.DVerifyCertificate; +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; + +public class VerifySignatureAction extends KeyStoreExplorerAction { + + private static final long serialVersionUID = 1L; + private X509Certificate certificateEval; + private X509Certificate[] keyCertChain; + + public VerifySignatureAction(KseFrame kseFrame) { + super(kseFrame); + + // TODO JW - Add an accelerator? +// putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('K', +// Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); + // TODO JW - Need a good description. + 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/verifycert.png")))); + } + + public VerifySignatureAction(KseFrame kseFrame, X509Certificate cert, X509Certificate[] keyCertChain) { + super(kseFrame); + this.certificateEval = cert; + this.keyCertChain = keyCertChain; + } + + @Override + protected void doAction() { + + String alias = ""; + try { + // TODO JW - how to get all certs in keystore and remove this specific cert selection. + if (certificateEval == null) { + alias = kseFrame.getSelectedEntryAlias(); + certificateEval = getCertificate(alias); + keyCertChain = getCertificateChain(alias); + } else { + alias = X509CertUtil.getCertificateAlias(certificateEval); + } + // TODO JW - create dialog for verify + DVerifyCertificate dVerifyCertificate = new DVerifyCertificate(frame, alias, kseFrame); + dVerifyCertificate.setLocationRelativeTo(frame); + dVerifyCertificate.setVisible(true); + if (dVerifyCertificate.isVerifySelected()) { + + VerifyOptions verifyOptions = dVerifyCertificate.getVerifyOption(); + KeyStoreHistory keyStoreHistory = dVerifyCertificate.getKeyStore(); + if (verifyOptions == VerifyOptions.CRL_DIST) { + verifyStatusCrl(keyStoreHistory, alias); + } else if (verifyOptions == VerifyOptions.CRL_FILE) { + verifyStatusCrlFile(keyStoreHistory, alias, dVerifyCertificate.getCrlFile()); + } else if (verifyOptions == VerifyOptions.OCSP_AIA) { + verifyStatusOCSP(keyStoreHistory, alias); + } else if (verifyOptions == VerifyOptions.OCSP_URL) { + verifyStatusOcspUrl(keyStoreHistory, alias, dVerifyCertificate.getOcspUrl()); + } else { + verifyChain(keyStoreHistory, alias); + } + } + } catch (CertPathValidatorException cex) { + DError.displayError(frame, cex); + } catch (Exception ex) { + DError.displayError(frame, ex); + } finally { + certificateEval = null; + } + } + + private void verifyChain(KeyStoreHistory keyStoreHistory, String alias) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, + InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, + CryptoException { + if (verify("false", "false", false, keyStoreHistory, null, alias)) { + JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.ChainSuccessful.message"), + MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), + alias), JOptionPane.INFORMATION_MESSAGE); + } + } + + private void verifyStatusOCSP(KeyStoreHistory keyStoreHistory, String alias) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, + InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, + CryptoException { + if (verify("false", "true", true, keyStoreHistory, null, alias)) { + JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.OcspSuccessful.message"), + MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), + alias), JOptionPane.INFORMATION_MESSAGE); + } + } + + private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias, String ocspUrl) + throws OperatorCreationException, OCSPException, IOException, HeadlessException, CertPathValidatorException, + KeyStoreException, NoSuchAlgorithmException, CertificateException, + InvalidAlgorithmParameterException, IllegalStateException, CryptoException { + + if (verify("false", "false", false, keyStoreHistory, null, alias)) { + // search issuer + X509Certificate issuer = null; + + if (keyCertChain == null || keyCertChain.length == 1) { + KeyStore trustStore = getKeyStore(keyStoreHistory); + Enumeration enumeration = trustStore.aliases(); + while (enumeration.hasMoreElements()) { + String tempAlias = enumeration.nextElement(); + X509Certificate cert = (X509Certificate) trustStore.getCertificate(tempAlias); + try { + certificateEval.verify(cert.getPublicKey()); + issuer = cert; + break; + } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException | SignatureException e) { + // ignore + } + } + } else { + issuer = keyCertChain[1]; + } + if (issuer == null) { + throw new CertPathValidatorException(res.getString("VerifySignatureAction.trustStoreEmpty.message")); + } + OCSPReq request = makeOcspRequest(issuer, certificateEval); + OCSPResp response = requestOCSPResponse(ocspUrl, request); + if (isGoodCertificate(response)) { + JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.OcspSuccessful.message"), + MessageFormat.format( + res.getString("VerifySignatureAction.Verify.Title"), alias), + JOptionPane.INFORMATION_MESSAGE); + } + } + } + + private static OCSPReq makeOcspRequest(X509Certificate caCert, X509Certificate certToCheck) + throws OCSPException, OperatorCreationException, CertificateEncodingException { + DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(KSE.BC) + .build(); + + CertificateID certId = new JcaCertificateID(digCalcProv.get(CertificateID.HASH_SHA1), caCert, + certToCheck.getSerialNumber()); + + OCSPReqBuilder gen = new OCSPReqBuilder(); + gen.addRequest(certId); + return gen.build(); + } + + private OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException { + byte[] ocspReqData = ocspReq.getEncoded(); + + HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); + try { + con.setRequestProperty("Content-Type", "application/ocsp-request"); + con.setRequestProperty("Accept", "application/ocsp-response"); + con.setDoInput(true); + con.setDoOutput(true); + con.setUseCaches(false); + try (OutputStream out = con.getOutputStream()) { + IOUtils.write(ocspReqData, out); + out.flush(); + } + byte[] responseBytes = IOUtils.toByteArray(con.getInputStream()); + OCSPResp ocspResp = new OCSPResp(responseBytes); + return ocspResp; + } finally { + if (con != null) { + con.disconnect(); + } + } + } + + private boolean isGoodCertificate(OCSPResp ocspResp) throws OCSPException, CertPathValidatorException { + + if (ocspResp.getStatus() != OCSPResp.SUCCESSFUL) { + throw new CertPathValidatorException(getMessageStatus(ocspResp.getStatus())); + } + BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResp.getResponseObject(); + SingleResp first = basicResponse.getResponses()[0]; + + CertificateStatus certStatus = first.getCertStatus(); + + if (certStatus != CertificateStatus.GOOD) { + if (certStatus instanceof RevokedStatus) { + RevokedStatus status = (RevokedStatus) certStatus; + int reason; + try { + reason = status.getRevocationReason(); + } catch (IllegalStateException ex) { + reason = -1; + } + Date revocationTime = status.getRevocationTime(); + throw new CertPathValidatorException( + MessageFormat.format(res.getString("VerifySignatureAction.revokedStatus.message"), reason, + StringUtils.formatDate(revocationTime))); + } else { + throw new CertPathValidatorException( + MessageFormat.format(res.getString("VerifySignatureAction.certStatus.message"), certStatus)); + } + } + BigInteger certSerial = certificateEval.getSerialNumber(); + BigInteger ocspSerial = first.getCertID().getSerialNumber(); + if (!certSerial.equals(ocspSerial)) { + throw new CertPathValidatorException( + MessageFormat.format(res.getString("VerifySignatureAction.badSerials.message"), certSerial, + ocspSerial)); + } + return true; + } + + private String getMessageStatus(int status) { + if (status == OCSPResp.MALFORMED_REQUEST) { + return res.getString("VerifySignatureAction.malformedRequest.message"); + } + if (status == OCSPResp.INTERNAL_ERROR) { + return res.getString("VerifySignatureAction.internalError.message"); + } + if (status == OCSPResp.TRY_LATER) { + return res.getString("VerifySignatureAction.tryLater.message"); + } + if (status == OCSPResp.SIG_REQUIRED) { + return res.getString("VerifySignatureAction.sigRequired.message"); + } + if (status == OCSPResp.UNAUTHORIZED) { + return res.getString("VerifySignatureAction.unauthorized.message"); + } + return MessageFormat.format(res.getString("VerifySignatureAction.unknownStatus.message"), status); + } + + private void verifyStatusCrlFile(KeyStoreHistory keyStoreHistory, String alias, String crlFile) + throws HeadlessException, KeyStoreException, NoSuchAlgorithmException, CertificateException, + InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, IOException, + CryptoException { + File file = new File(crlFile); + X509CRL crl = null; + try { + byte[] data = FileUtils.readFileToByteArray(file); + crl = X509CertUtil.loadCRL(data); + } catch (Exception ex) { + String problemStr = MessageFormat.format(res.getString("ExamineFileAction.NoOpenCrl.Problem"), + file.getName()); + + String[] causes = new String[] { res.getString("ExamineFileAction.NotCrl.Cause"), + res.getString("ExamineFileAction.CorruptedCrl.Cause") }; + + Problem problem = new Problem(problemStr, causes, ex); + + DProblem dProblem = new DProblem(frame, res.getString("ExamineFileAction.ProblemOpeningCrl.Title"), + problem); + dProblem.setLocationRelativeTo(frame); + dProblem.setVisible(true); + return; + } + if (verify("true", "false", true, keyStoreHistory, crl, alias)) { + JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.CrlSuccessful.message"), + MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), + alias), JOptionPane.INFORMATION_MESSAGE); + } + } + + private void verifyStatusCrl(KeyStoreHistory keyStoreHistory, String alias) + throws CertificateException, KeyStoreException, NoSuchAlgorithmException, IOException, + InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, + CryptoException { + if (verify("true", "false", true, keyStoreHistory, null, alias)) { + JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.CrlSuccessful.message"), + MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), + alias), JOptionPane.INFORMATION_MESSAGE); + } + } + + private boolean verify(String crl, String ocsp, boolean revocationEnabled, KeyStoreHistory keyStoreHistory, + X509CRL xCrl, String alias) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, + InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, + CryptoException { + + KeyStore trustStore = getKeyStore(keyStoreHistory); + + if (trustStore == null) { + return false; + } + if (trustStore.size() == 0) { + throw new CertPathValidatorException(res.getString("VerifySignatureAction.trustStoreEmpty.message")); + } + System.setProperty("com.sun.net.ssl.checkRevocation", crl); + System.setProperty("com.sun.security.enableCRLDP", crl); + Security.setProperty("ocsp.enable", ocsp); + + List listCertificates = new ArrayList<>(); + if (revocationEnabled) { + listCertificates.add(certificateEval); + } else { + if (keyCertChain != null) { + for (int i = keyCertChain.length - 1; i >= 0; i--) { + X509Certificate cert = keyCertChain[i]; + listCertificates.add(0, cert); + if (cert.equals(certificateEval)) { + break; + } + } + } + } + + CertPathValidator validator = CertPathValidator.getInstance("PKIX"); + CertificateFactory factory = CertificateFactory.getInstance("X509"); + CertPath certPath = factory.generateCertPath(listCertificates); + PKIXParameters params = new PKIXParameters(trustStore); + if (xCrl != null) { + params.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters( + Collections.singletonList(xCrl)))); + } + + // remove some critical extensions that are private to companies and would + // otherwise cause a validation failure + PKIXCertPathChecker certPathChecker = new ExtensionRemovingCertPathChecker(); + params.addCertPathChecker(certPathChecker); + + Date now = new Date(System.currentTimeMillis()); + params.setDate(now); + params.setRevocationEnabled(revocationEnabled); + validator.validate(certPath, params); + return true; + } + + 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); + } + } + } + } + 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); + } + } + } + } + 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); + } + } + + 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); + } + } + + static class ExtensionRemovingCertPathChecker extends PKIXCertPathChecker { + @Override + public void init(boolean forward) throws CertPathValidatorException { + // nothing to do here + } + + @Override + public boolean isForwardCheckingSupported() { + return false; + } + + @Override + public Set getSupportedExtensions() { + HashSet hashSet = new HashSet<>(); + + // appleCertificateExtensionCodeSigning + hashSet.add("1.2.840.113635.100.6.1.13"); + + return hashSet; + } + + @Override + public void check(Certificate cert, Collection unresolvedCritExts) throws CertPathValidatorException { + // remove critical Apple private extension that causes certificate validation to + // fail + unresolvedCritExts.remove("1.2.840.113635.100.6.1.13"); + } + } +} 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 9dcb2973d..dcbe59823 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -678,6 +678,31 @@ VerifyCertificateAction.tryLater.message = Try again later VerifyCertificateAction.unauthorized.message = Request unauthorized VerifyCertificateAction.unknownStatus.message = Unknown status {0} +#VerifySignatureAction.ChainSuccessful.message = CHAIN check successful, certificate valid +#VerifySignatureAction.CrlSuccessful.message = CRL check successful, certificate valid +#VerifySignatureAction.EnterPassword.Title = Enter Password for ''{0}'' +#VerifySignatureAction.ExamineFile.Title = Examine File +#VerifySignatureAction.Exception.Title = Could not load KeyStore. +#VerifySignatureAction.FileNotFoundException.message = File not found +#VerifySignatureAction.NoAccessEntry.message = No access entry +#VerifySignatureAction.NotTypeKeyStore.message = File not keystore type +#VerifySignatureAction.OcspSuccessful.message = OCSP check successful, certificate valid +#VerifySignatureAction.Verify.Title = Verify ''{0}'' +#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} +#VerifySignatureAction.internalError.message = Internal error in issuer +#VerifySignatureAction.malformedRequest.message = Illegal confirmation request +#VerifySignatureAction.revokedStatus.message = The certificate has been revoked, reason: {0}, revocation date: {1} +#VerifySignatureAction.sigRequired.message = Must sign the request +VerifySignatureAction.statusbar = Verify signature +VerifySignatureAction.text = Verify Signature +VerifySignatureAction.tooltip = Verify signature +#VerifySignatureAction.trustStoreEmpty.message = Path does not chain with any of the trust anchors +#VerifySignatureAction.tryLater.message = Try again later +#VerifySignatureAction.unauthorized.message = Request unauthorized +#VerifySignatureAction.unknownStatus.message = Unknown status {0} + WebsiteAction.GitHubIssueTracker.statusbar = Create a bug report or feature request WebsiteAction.GitHubIssueTracker.text = Bug Reports / Feature Requests WebsiteAction.GitHubIssueTracker.tooltip = Bug Reports / Feature Requests diff --git a/kse/src/main/resources/org/kse/gui/resources.properties b/kse/src/main/resources/org/kse/gui/resources.properties index e790c576f..6490e6145 100644 --- a/kse/src/main/resources/org/kse/gui/resources.properties +++ b/kse/src/main/resources/org/kse/gui/resources.properties @@ -172,6 +172,7 @@ KseFrame.jmiSourceforge.mnemonic = g KseFrame.jmiSystemInformation.mnemonic = i KseFrame.jmiTipOfTheDay.mnemonic = t KseFrame.jmiUndo.mnemonic = u +KseFrame.jmiVerifySignature.mnemonic = v KseFrame.jmiWebsite.mnemonic = w KseFrame.jmrfRecentFiles.mnemonic = r KseFrame.jmrfRecentFiles.text = Recent Files From c5819265a78494a11b15d92dff3e2cd139a77a8a Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:56:38 -0800 Subject: [PATCH 02/69] Moved appbundler dependency spec to better location. --- kse/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kse/build.gradle b/kse/build.gradle index c36663525..77e3171ab 100644 --- a/kse/build.gradle +++ b/kse/build.gradle @@ -118,7 +118,6 @@ dependencies { implementation('org.openjdk.nashorn:nashorn-core:15.4') implementation('com.fasterxml.jackson.jr:jackson-jr-objects:2.17.0') implementation('com.fasterxml.jackson.jr:jackson-jr-annotation-support:2.17.0') - appbundler('com.evolvedbinary.appbundler:appbundler:1.3.1') if (gradle.startParameter.taskNames.any { ['innosetup', 'zip'].contains(it) }) { implementation('org.openjfx:javafx-base:17.0.9:win') @@ -136,6 +135,8 @@ dependencies { implementation('org.openjfx:javafx-swing:17.0.9:mac') } + appbundler('com.evolvedbinary.appbundler:appbundler:1.3.1') + testImplementation('org.assertj:assertj-core:3.25.1') testImplementation('org.junit.jupiter:junit-jupiter-api:5.10.1') testImplementation('org.junit.jupiter:junit-jupiter-params:5.10.1') From 74adc62cf9eda963186d83d3e68474268238aac0 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Thu, 14 Nov 2024 09:41:30 -0800 Subject: [PATCH 03/69] Added verify signature file chooser. --- .../java/org/kse/gui/FileChooserFactory.java | 17 +- .../gui/actions/VerifySignatureAction.java | 441 +++++------------- .../org/kse/gui/actions/resources.properties | 2 + .../org/kse/gui/resources.properties | 1 + 4 files changed, 131 insertions(+), 330 deletions(-) diff --git a/kse/src/main/java/org/kse/gui/FileChooserFactory.java b/kse/src/main/java/org/kse/gui/FileChooserFactory.java index 41eb163a6..e26d6f6f0 100644 --- a/kse/src/main/java/org/kse/gui/FileChooserFactory.java +++ b/kse/src/main/java/org/kse/gui/FileChooserFactory.java @@ -73,6 +73,7 @@ public class FileChooserFactory { public static final String LIB_DYLIB_EXT = "dylib"; public static final String PEM_EXT = "pem"; public static final String JWK_EXT = "json"; + public static final String SIG_EXT = "sig"; private static final String KEYSTORE_FILE_DESC = format(res.getString("FileChooserFactory.KeyStoreFiles"), PKCS12_KEYSTORE_EXT_1, PKCS12_KEYSTORE_EXT_2, @@ -132,6 +133,9 @@ public class FileChooserFactory { private static final String PEM_FILE_DESC = format(res.getString("FileChooserFactory.PemFiles"), PEM_EXT); + private static final String SIG_FILE_DESC = + format(res.getString("FileChooserFactory.SignatureFiles"), SIG_EXT); + private FileChooserFactory() { } @@ -425,7 +429,18 @@ public static JFileChooser getLibFileChooser() { return chooser; } - private static JFileChooser getFileChooser() { + /** + * Get a JFileChooser filtered for signature files. + * + * @return JFileChooser object + */ + public static JFileChooser getSignatureFileChooser() { + JFileChooser chooser = getFileChooser(); + chooser.setFileFilter(new FileNameExtensionFilter(SIG_FILE_DESC, SIG_EXT)); + return chooser; + } + + private static JFileChooser getFileChooser() { JFileChooser fileChooser = JavaFXFileChooser.isFxAvailable() ? new JavaFXFileChooser() : new JFileChooser(); // show/hide hidden files 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 51bd77215..c8609ab38 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -60,41 +60,32 @@ import java.util.Set; 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.cert.ocsp.BasicOCSPResp; -import org.bouncycastle.cert.ocsp.CertificateID; -import org.bouncycastle.cert.ocsp.CertificateStatus; -import org.bouncycastle.cert.ocsp.OCSPException; -import org.bouncycastle.cert.ocsp.OCSPReq; -import org.bouncycastle.cert.ocsp.OCSPReqBuilder; -import org.bouncycastle.cert.ocsp.OCSPResp; -import org.bouncycastle.cert.ocsp.RevokedStatus; -import org.bouncycastle.cert.ocsp.SingleResp; -import org.bouncycastle.cert.ocsp.jcajce.JcaCertificateID; -import org.bouncycastle.operator.DigestCalculatorProvider; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.kse.KSE; import org.kse.crypto.CryptoException; 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.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; -public class VerifySignatureAction extends KeyStoreExplorerAction { - +public class VerifySignatureAction extends AuthorityCertificatesAction { private static final long serialVersionUID = 1L; - private X509Certificate certificateEval; - private X509Certificate[] keyCertChain; + private File signatureFile; public VerifySignatureAction(KseFrame kseFrame) { super(kseFrame); @@ -111,304 +102,84 @@ public VerifySignatureAction(KseFrame kseFrame) { Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/verifycert.png")))); } - public VerifySignatureAction(KseFrame kseFrame, X509Certificate cert, X509Certificate[] keyCertChain) { - super(kseFrame); - this.certificateEval = cert; - this.keyCertChain = keyCertChain; - } - @Override protected void doAction() { - - String alias = ""; try { - // TODO JW - how to get all certs in keystore and remove this specific cert selection. - if (certificateEval == null) { - alias = kseFrame.getSelectedEntryAlias(); - certificateEval = getCertificate(alias); - keyCertChain = getCertificateChain(alias); - } else { - alias = X509CertUtil.getCertificateAlias(certificateEval); - } - // TODO JW - create dialog for verify - DVerifyCertificate dVerifyCertificate = new DVerifyCertificate(frame, alias, kseFrame); - dVerifyCertificate.setLocationRelativeTo(frame); - dVerifyCertificate.setVisible(true); - if (dVerifyCertificate.isVerifySelected()) { - - VerifyOptions verifyOptions = dVerifyCertificate.getVerifyOption(); - KeyStoreHistory keyStoreHistory = dVerifyCertificate.getKeyStore(); - if (verifyOptions == VerifyOptions.CRL_DIST) { - verifyStatusCrl(keyStoreHistory, alias); - } else if (verifyOptions == VerifyOptions.CRL_FILE) { - verifyStatusCrlFile(keyStoreHistory, alias, dVerifyCertificate.getCrlFile()); - } else if (verifyOptions == VerifyOptions.OCSP_AIA) { - verifyStatusOCSP(keyStoreHistory, alias); - } else if (verifyOptions == VerifyOptions.OCSP_URL) { - verifyStatusOcspUrl(keyStoreHistory, alias, dVerifyCertificate.getOcspUrl()); - } else { - verifyChain(keyStoreHistory, alias); - } - } - } catch (CertPathValidatorException cex) { - DError.displayError(frame, cex); - } catch (Exception ex) { - DError.displayError(frame, ex); - } finally { - certificateEval = null; - } - } - - private void verifyChain(KeyStoreHistory keyStoreHistory, String alias) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, - InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, - CryptoException { - if (verify("false", "false", false, keyStoreHistory, null, alias)) { - JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.ChainSuccessful.message"), - MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), - alias), JOptionPane.INFORMATION_MESSAGE); - } - } - - private void verifyStatusOCSP(KeyStoreHistory keyStoreHistory, String alias) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, - InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, - CryptoException { - if (verify("false", "true", true, keyStoreHistory, null, alias)) { - JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.OcspSuccessful.message"), - MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), - alias), JOptionPane.INFORMATION_MESSAGE); - } - } + KeyStoreHistory history = kseFrame.getActiveKeyStoreHistory(); - private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias, String ocspUrl) - throws OperatorCreationException, OCSPException, IOException, HeadlessException, CertPathValidatorException, - KeyStoreException, NoSuchAlgorithmException, CertificateException, - InvalidAlgorithmParameterException, IllegalStateException, CryptoException { - - if (verify("false", "false", false, keyStoreHistory, null, alias)) { - // search issuer - X509Certificate issuer = null; - - if (keyCertChain == null || keyCertChain.length == 1) { - KeyStore trustStore = getKeyStore(keyStoreHistory); - Enumeration enumeration = trustStore.aliases(); - while (enumeration.hasMoreElements()) { - String tempAlias = enumeration.nextElement(); - X509Certificate cert = (X509Certificate) trustStore.getCertificate(tempAlias); - try { - certificateEval.verify(cert.getPublicKey()); - issuer = cert; - break; - } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException | SignatureException e) { - // ignore - } - } - } else { - issuer = keyCertChain[1]; - } - if (issuer == null) { - throw new CertPathValidatorException(res.getString("VerifySignatureAction.trustStoreEmpty.message")); - } - OCSPReq request = makeOcspRequest(issuer, certificateEval); - OCSPResp response = requestOCSPResponse(ocspUrl, request); - if (isGoodCertificate(response)) { - JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.OcspSuccessful.message"), - MessageFormat.format( - res.getString("VerifySignatureAction.Verify.Title"), alias), - JOptionPane.INFORMATION_MESSAGE); + // 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(); + + X509Certificate signature = showFileSelectionDialog(); + if (signature == null) { + return; } - } - } - private static OCSPReq makeOcspRequest(X509Certificate caCert, X509Certificate certToCheck) - throws OCSPException, OperatorCreationException, CertificateEncodingException { - DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(KSE.BC) - .build(); - - CertificateID certId = new JcaCertificateID(digCalcProv.get(CertificateID.HASH_SHA1), caCert, - certToCheck.getSerialNumber()); + // 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); + } - OCSPReqBuilder gen = new OCSPReqBuilder(); - gen.addRequest(certId); - return gen.build(); - } + if (windowsTrustedRootCertificates != null) { + // Establish trust against Windows Trusted Root Certificates KeyStore + compKeyStores.add(windowsTrustedRootCertificates); + } - private OCSPResp requestOCSPResponse(String url, OCSPReq ocspReq) throws IOException { - byte[] ocspReqData = ocspReq.getEncoded(); + // TODO JW - Verify the signature using the CA certs + // TODO JW - Display dialog with option to see signature details. - HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(); - try { - con.setRequestProperty("Content-Type", "application/ocsp-request"); - con.setRequestProperty("Accept", "application/ocsp-response"); - con.setDoInput(true); - con.setDoOutput(true); - con.setUseCaches(false); - try (OutputStream out = con.getOutputStream()) { - IOUtils.write(ocspReqData, out); - out.flush(); + return; } - byte[] responseBytes = IOUtils.toByteArray(con.getInputStream()); - OCSPResp ocspResp = new OCSPResp(responseBytes); - return ocspResp; - } finally { - if (con != null) { - con.disconnect(); - } - } - } - private boolean isGoodCertificate(OCSPResp ocspResp) throws OCSPException, CertPathValidatorException { + // TODO JW - Verify the signature using the keystore + // TODO JW - Display dialog with option to see signature details. - if (ocspResp.getStatus() != OCSPResp.SUCCESSFUL) { - throw new CertPathValidatorException(getMessageStatus(ocspResp.getStatus())); - } - BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResp.getResponseObject(); - SingleResp first = basicResponse.getResponses()[0]; - - CertificateStatus certStatus = first.getCertStatus(); - - if (certStatus != CertificateStatus.GOOD) { - if (certStatus instanceof RevokedStatus) { - RevokedStatus status = (RevokedStatus) certStatus; - int reason; - try { - reason = status.getRevocationReason(); - } catch (IllegalStateException ex) { - reason = -1; - } - Date revocationTime = status.getRevocationTime(); - throw new CertPathValidatorException( - MessageFormat.format(res.getString("VerifySignatureAction.revokedStatus.message"), reason, - StringUtils.formatDate(revocationTime))); - } else { - throw new CertPathValidatorException( - MessageFormat.format(res.getString("VerifySignatureAction.certStatus.message"), certStatus)); - } - } - BigInteger certSerial = certificateEval.getSerialNumber(); - BigInteger ocspSerial = first.getCertID().getSerialNumber(); - if (!certSerial.equals(ocspSerial)) { - throw new CertPathValidatorException( - MessageFormat.format(res.getString("VerifySignatureAction.badSerials.message"), certSerial, - ocspSerial)); - } - return true; - } + kseFrame.updateControls(true); - private String getMessageStatus(int status) { - if (status == OCSPResp.MALFORMED_REQUEST) { - return res.getString("VerifySignatureAction.malformedRequest.message"); - } - if (status == OCSPResp.INTERNAL_ERROR) { - return res.getString("VerifySignatureAction.internalError.message"); - } - if (status == OCSPResp.TRY_LATER) { - return res.getString("VerifySignatureAction.tryLater.message"); - } - if (status == OCSPResp.SIG_REQUIRED) { - return res.getString("VerifySignatureAction.sigRequired.message"); - } - if (status == OCSPResp.UNAUTHORIZED) { - return res.getString("VerifySignatureAction.unauthorized.message"); - } - return MessageFormat.format(res.getString("VerifySignatureAction.unknownStatus.message"), status); - } - - private void verifyStatusCrlFile(KeyStoreHistory keyStoreHistory, String alias, String crlFile) - throws HeadlessException, KeyStoreException, NoSuchAlgorithmException, CertificateException, - InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, IOException, - CryptoException { - File file = new File(crlFile); - X509CRL crl = null; - try { - byte[] data = FileUtils.readFileToByteArray(file); - crl = X509CertUtil.loadCRL(data); + JOptionPane.showMessageDialog(frame, res.getString( + "ImportTrustedCertificateAction.ImportTrustCertSuccessful.message"), + res.getString("ImportTrustedCertificateAction.ImportTrustCert.Title"), + JOptionPane.INFORMATION_MESSAGE); } catch (Exception ex) { - String problemStr = MessageFormat.format(res.getString("ExamineFileAction.NoOpenCrl.Problem"), - file.getName()); - - String[] causes = new String[] { res.getString("ExamineFileAction.NotCrl.Cause"), - res.getString("ExamineFileAction.CorruptedCrl.Cause") }; - - Problem problem = new Problem(problemStr, causes, ex); - - DProblem dProblem = new DProblem(frame, res.getString("ExamineFileAction.ProblemOpeningCrl.Title"), - problem); - dProblem.setLocationRelativeTo(frame); - dProblem.setVisible(true); - return; - } - if (verify("true", "false", true, keyStoreHistory, crl, alias)) { - JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.CrlSuccessful.message"), - MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), - alias), JOptionPane.INFORMATION_MESSAGE); - } - } - - private void verifyStatusCrl(KeyStoreHistory keyStoreHistory, String alias) - throws CertificateException, KeyStoreException, NoSuchAlgorithmException, IOException, - InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, - CryptoException { - if (verify("true", "false", true, keyStoreHistory, null, alias)) { - JOptionPane.showMessageDialog(frame, res.getString("VerifySignatureAction.CrlSuccessful.message"), - MessageFormat.format(res.getString("VerifySignatureAction.Verify.Title"), - alias), JOptionPane.INFORMATION_MESSAGE); - } - } - - private boolean verify(String crl, String ocsp, boolean revocationEnabled, KeyStoreHistory keyStoreHistory, - X509CRL xCrl, String alias) - throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, - InvalidAlgorithmParameterException, CertPathValidatorException, IllegalStateException, - CryptoException { - - KeyStore trustStore = getKeyStore(keyStoreHistory); - - if (trustStore == null) { - return false; - } - if (trustStore.size() == 0) { - throw new CertPathValidatorException(res.getString("VerifySignatureAction.trustStoreEmpty.message")); - } - System.setProperty("com.sun.net.ssl.checkRevocation", crl); - System.setProperty("com.sun.security.enableCRLDP", crl); - Security.setProperty("ocsp.enable", ocsp); - - List listCertificates = new ArrayList<>(); - if (revocationEnabled) { - listCertificates.add(certificateEval); - } else { - if (keyCertChain != null) { - for (int i = keyCertChain.length - 1; i >= 0; i--) { - X509Certificate cert = keyCertChain[i]; - listCertificates.add(0, cert); - if (cert.equals(certificateEval)) { - break; - } - } - } - } - - CertPathValidator validator = CertPathValidator.getInstance("PKIX"); - CertificateFactory factory = CertificateFactory.getInstance("X509"); - CertPath certPath = factory.generateCertPath(listCertificates); - PKIXParameters params = new PKIXParameters(trustStore); - if (xCrl != null) { - params.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters( - Collections.singletonList(xCrl)))); + DError.displayError(frame, ex); } - - // remove some critical extensions that are private to companies and would - // otherwise cause a validation failure - PKIXCertPathChecker certPathChecker = new ExtensionRemovingCertPathChecker(); - params.addCertPathChecker(certPathChecker); - - Date now = new Date(System.currentTimeMillis()); - params.setDate(now); - params.setRevocationEnabled(revocationEnabled); - validator.validate(certPath, params); - return true; } private boolean isCA(X509Certificate cert) { @@ -444,15 +215,15 @@ private KeyStore getKeyStore(KeyStoreHistory keyStoreHistory) } } 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); - } - } - } +// 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); +// } +// } +// } } return trustStore; } @@ -483,32 +254,44 @@ private X509Certificate[] getCertificateChain(String alias) throws CryptoExcepti } } - static class ExtensionRemovingCertPathChecker extends PKIXCertPathChecker { - @Override - public void init(boolean forward) throws CertPathValidatorException { - // nothing to do here + private X509Certificate showFileSelectionDialog() { + signatureFile = chooseSignatureFile(); + if (signatureFile == null) { + return null; } - @Override - public boolean isForwardCheckingSupported() { - return false; - } + // TODO JW ... + X509Certificate[] certs = openCertificate(signatureFile); - @Override - public Set getSupportedExtensions() { - HashSet hashSet = new HashSet<>(); - - // appleCertificateExtensionCodeSigning - hashSet.add("1.2.840.113635.100.6.1.13"); + if ((certs == null) || (certs.length == 0)) { + return null; + } - return hashSet; + if (certs.length > 1) { + // TODO JW - fix resource strings. + JOptionPane.showMessageDialog(frame, res.getString( + "ImportTrustedCertificateAction.NoMultipleTrustCertImport.message"), + res.getString("ImportTrustedCertificateAction.ImportTrustCert.Title"), + JOptionPane.WARNING_MESSAGE); + return null; } - @Override - public void check(Certificate cert, Collection unresolvedCritExts) throws CertPathValidatorException { - // remove critical Apple private extension that causes certificate validation to - // fail - unresolvedCritExts.remove("1.2.840.113635.100.6.1.13"); + return certs[0]; + } + + private File chooseSignatureFile() { + JFileChooser chooser = FileChooserFactory.getSignatureFileChooser(); + chooser.setCurrentDirectory(CurrentDirectory.get()); + chooser.setDialogTitle(res.getString("VerifySignatureAction.ChooseSignature.Title")); + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText(res.getString("VerifySignatureAction.ChooseSignature.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 dcbe59823..5a9a0a363 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -679,6 +679,8 @@ VerifyCertificateAction.unauthorized.message = Request unauthorized VerifyCertificateAction.unknownStatus.message = Unknown status {0} #VerifySignatureAction.ChainSuccessful.message = CHAIN check successful, certificate valid +VerifySignatureAction.ChooseSignature.Title = Choose Signature File +VerifySignatureAction.ChooseSignature.button = Verify #VerifySignatureAction.CrlSuccessful.message = CRL check successful, certificate valid #VerifySignatureAction.EnterPassword.Title = Enter Password for ''{0}'' #VerifySignatureAction.ExamineFile.Title = Examine File diff --git a/kse/src/main/resources/org/kse/gui/resources.properties b/kse/src/main/resources/org/kse/gui/resources.properties index 6490e6145..f175b510f 100644 --- a/kse/src/main/resources/org/kse/gui/resources.properties +++ b/kse/src/main/resources/org/kse/gui/resources.properties @@ -30,6 +30,7 @@ FileChooserFactory.Pkcs8Files = PKCS #8 Private Key Files (*.{0};*.{1};*.{ FileChooserFactory.PkiPathFiles = PKI Path Certificate Files (*.{0}) FileChooserFactory.PublicKeyFiles = Public Key Files (*.{0}) FileChooserFactory.PvkFiles = PVK Private Key Files (*.{0}) +FileChooserFactory.SignatureFiles = Signature Files (*.{0}) FileChooserFactory.SpcFiles = Software Publisher Certificate Files (*.{0}) FileChooserFactory.SpkacCsrFiles = SPKAC CSR Files (*.{0}) FileChooserFactory.ZipFiles = ZIP Files (*.{0}) From c299030d874980c52adf5300fdc32a5135ca2c7e Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:40:30 -0800 Subject: [PATCH 04/69] Added basic signed data/signature verification. --- .../gui/actions/VerifySignatureAction.java | 103 +++++++++++++++--- 1 file changed, 88 insertions(+), 15 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 c8609ab38..b9f651ff7 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -23,7 +23,10 @@ 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; @@ -66,6 +69,19 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +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.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.util.Selector; +import org.bouncycastle.util.Store; import org.kse.KSE; import org.kse.crypto.CryptoException; import org.kse.crypto.x509.X509CertUtil; @@ -122,11 +138,19 @@ protected void doAction() { KeyStoreState currentState = history.getCurrentState(); KeyStore keyStore = currentState.getKeyStore(); - X509Certificate signature = showFileSelectionDialog(); + byte[] signature = showFileSelectionDialog(); if (signature == null) { return; } + int extensionIndex = signatureFile.getAbsolutePath().lastIndexOf('.'); + if (extensionIndex < 0) { + // TODO JW - signature doesn't have a file extension + return; + } + String contentFileName = signatureFile.getAbsolutePath().substring(0, extensionIndex); + File contentFile = new File(contentFileName); + // TODO JW - Add new option for using cacerts for signature verification. if (preferences.getCaCertsSettings().isImportTrustedCertTrustCheckEnabled()) { // String matchAlias = X509CertUtil.matchCertificate(keyStore, trustCert); @@ -169,6 +193,25 @@ protected void doAction() { } // TODO JW - Verify the signature using the keystore + CMSProcessable data = new CMSProcessableFile(contentFile); + CMSSignedData signedData = new CMSSignedData(data, signature); + + // TODO 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(); + printStore(certStore); + SignerInformationStore signers = signedData.getSignerInfos(); + for (SignerInformation signer : signers.getSigners()) { + Collection matchedCerts = certStore.getMatches(signer.getSID()); + for (X509CertificateHolder cert : matchedCerts) { + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { + System.out.println("Verified by: " + cert.getSubject()); + verified = true; + } + } + } + System.out.println("Verified: " + verified); // TODO JW - Display dialog with option to see signature details. kseFrame.updateControls(true); @@ -182,6 +225,26 @@ protected void doAction() { } } + private static void printStore(Store store) { + for (T obj : store.getMatches(new Selector() { + @Override + public Object clone() { + return null; + } + + @Override + public boolean match(T obj) { + return true; + } + })) { + if (obj instanceof X509CertificateHolder) { + System.out.println(((X509CertificateHolder) obj).getSubject()); + } else { + System.out.println(obj); + } + } + } + private boolean isCA(X509Certificate cert) { int basicConstraints = cert.getBasicConstraints(); if (basicConstraints != -1) { @@ -254,29 +317,39 @@ private X509Certificate[] getCertificateChain(String alias) throws CryptoExcepti } } - private X509Certificate showFileSelectionDialog() { - signatureFile = chooseSignatureFile(); - if (signatureFile == null) { + /** + * 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 FileUtils.readFileToByteArray(signatureFile); + } catch (IOException ex) { + JOptionPane.showMessageDialog(frame, MessageFormat.format( + res.getString("KeyStoreExplorerAction.NoReadFile.message"), + signatureFile), + // TODO JW - Create a new resource for open signature. + res.getString("KeyStoreExplorerAction.OpenCertificate.Title"), + JOptionPane.WARNING_MESSAGE); return null; } + } - // TODO JW ... - X509Certificate[] certs = openCertificate(signatureFile); - - if ((certs == null) || (certs.length == 0)) { + private byte[] showFileSelectionDialog() { + signatureFile = chooseSignatureFile(); + if (signatureFile == null) { return null; } - if (certs.length > 1) { - // TODO JW - fix resource strings. - JOptionPane.showMessageDialog(frame, res.getString( - "ImportTrustedCertificateAction.NoMultipleTrustCertImport.message"), - res.getString("ImportTrustedCertificateAction.ImportTrustCert.Title"), - JOptionPane.WARNING_MESSAGE); + byte[] sig = openSignature(signatureFile); + + if (sig == null) { return null; } - return certs[0]; + return sig; } private File chooseSignatureFile() { From 46b49c74ae9f7a06133ff10d4b28490e4aada832 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sun, 17 Nov 2024 00:07:00 -0800 Subject: [PATCH 05/69] Hooked up view signature dialog showing only the issuer DN. --- .../gui/actions/VerifySignatureAction.java | 62 +- .../org/kse/gui/dialogs/DViewSignature.java | 683 ++++++++++++++++++ .../kse/gui/dialogs/SignerListCellRend.java | 85 +++ 3 files changed, 815 insertions(+), 15 deletions(-) create mode 100644 kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java create mode 100644 kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java 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 b9f651ff7..336eb22b1 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -59,6 +59,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.HashSet; +import java.util.Hashtable; import java.util.List; import java.util.Set; @@ -69,6 +70,8 @@ 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; @@ -91,6 +94,7 @@ 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; @@ -198,21 +202,49 @@ protected void doAction() { // TODO 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(); - printStore(certStore); - SignerInformationStore signers = signedData.getSignerInfos(); - for (SignerInformation signer : signers.getSigners()) { - Collection matchedCerts = certStore.getMatches(signer.getSID()); - for (X509CertificateHolder cert : matchedCerts) { - if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { - System.out.println("Verified by: " + cert.getSubject()); - verified = true; - } - } - } - System.out.println("Verified: " + verified); - // TODO JW - Display dialog with option to see signature details. +// boolean verified = false; +// Store certStore = signedData.getCertificates(); +// printStore(certStore); +// SignerInformationStore signers = signedData.getSignerInfos(); +// for (SignerInformation signer : signers.getSigners()) { +// signer.getCounterSignatures(); +// AttributeTable signedAttributes = signer.getSignedAttributes(); +// AttributeTable unsignedAttributes = signer.getUnsignedAttributes(); +// +// Hashtable ht = signedAttributes.toHashtable(); +// for (Object k : ht.keySet()) { +// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues()); +// } +// +// Collection matchedCerts = certStore.getMatches(signer.getSID()); +// for (X509CertificateHolder cert : matchedCerts) { +// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { +// System.out.println("Verified by: " + cert.getSubject()); +// verified = true; +// } +// } +// } +// System.out.println("Verified: " + verified); + // TODO JW - Display signature details dialog with option to see signature details. + DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat.format( + // TODO JW - view signature resource string + res.getString("ImportTrustedCertificateAction.CertDetailsFile.Title"), + signatureFile.getName()), signedData, null, DViewSignature.NONE); + dViewSignature.setLocationRelativeTo(frame); + dViewSignature.setVisible(true); + + /* +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); diff --git a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java new file mode 100644 index 000000000..4baf7203f --- /dev/null +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -0,0 +1,683 @@ +/* + * 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.dialogs; + +import java.awt.Color; +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; +import java.io.IOException; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.text.MessageFormat; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +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; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.util.encoders.Hex; +import org.kse.KSE; +import org.kse.crypto.CryptoException; +import org.kse.crypto.KeyInfo; +import org.kse.crypto.keypair.KeyPairUtil; +import org.kse.crypto.signing.SignatureType; +import org.kse.crypto.x509.X500NameUtils; +import org.kse.crypto.x509.X509CertUtil; +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.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 net.miginfocom.swing.MigLayout; + +/** + * Displays the details of a PKCS #7 signature. The details of one + * signer are displayed at a time with selector buttons allowing the + * movement to another of the signers. + */ +public class DViewSignature extends JEscDialog { + private static final long serialVersionUID = 1L; + + private static ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/dialogs/resources"); + + private KsePreferences preferences = PreferencesManager.getPreferences(); + + public static final int NONE = 0; + public static final int IMPORT = 1; + public static final int EXPORT = 2; + public static final int IMPORT_EXPORT = 3; + private int importExport = 0; + + private KseFrame kseFrame; + + private JLabel jlSigners; + private JList jlbSigners; + private JScrollPane jspSigners; + private JLabel jlSignerDN; + private JTextField jtfSignerDN; + private JLabel jlSerial; + private JDistinguishedName jdnSerial; + private JLabel jlIssuer; + private JDistinguishedName jdnIssuer; + private JLabel jlSerialNumberHex; + private JTextField jtfSerialNumberHex; + private JLabel jlSerialNumberDec; + private JTextField jtfSerialNumberDec; + private JLabel jlValidFrom; + private JTextField jtfValidFrom; + private JLabel jlValidUntil; + private JTextField jtfValidUntil; + private JLabel jlPublicKey; + private JTextField jtfPublicKey; + private JButton jbViewPublicKeyDetails; + private JLabel jlSignatureAlgorithm; + private JTextField jtfSignatureAlgorithm; + private JLabel jlFingerprint; + private JCertificateFingerprint jcfFingerprint; + private JButton jbExtensions; + private JButton jbPem; + private JButton jbAsn1; + private JButton jbImport; + private JButton jbExport; + private JButton jbOK; + private JButton jbVerify; + + private CMSSignedData signedData; + + /** + * Creates a new DViewCertificate dialog. + * + * @param parent Parent frame + * @param title The dialog title + * @param signedData Signature to display + * @param kseFrame Reference to main class with currently opened keystores and their contents + * @param importExport Show import button/export button/no extra button? + * @throws CryptoException A problem was encountered getting the certificates' details + */ + public DViewSignature(Window parent, String title, CMSSignedData signedData, KseFrame kseFrame, int importExport) + throws CryptoException { + super(parent, title, Dialog.ModalityType.MODELESS); + this.kseFrame = kseFrame; + this.importExport = importExport; + this.signedData = signedData; + initComponents(signedData); + } + + private void initComponents(CMSSignedData signedData) throws CryptoException { + jlSigners = new JLabel(res.getString("DViewCertificate.jlHierarchy.text")); + + jlbSigners = new JList<>(createSignerList(signedData)); + // 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()); + + jspSigners = PlatformUtil.createScrollPane(jlbSigners, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jspSigners.setPreferredSize(new Dimension(100, 75)); + + jlSignerDN = new JLabel(res.getString("DViewCertificate.jlVersion.text")); + + jtfSignerDN = new JTextField(40); + jtfSignerDN.setEditable(false); + jtfSignerDN.setToolTipText(res.getString("DViewCertificate.jtfVersion.tooltip")); + + jlSerial = new JLabel(res.getString("DViewCertificate.jlSubject.text")); + + jdnSerial = new JDistinguishedName(res.getString("DViewCertificate.Subject.Title"), 40, false); + jdnSerial.setToolTipText(res.getString("DViewCertificate.jdnSubject.tooltip")); + + jlIssuer = new JLabel(res.getString("DViewCertificate.jlIssuer.text")); + + jdnIssuer = new JDistinguishedName(res.getString("DViewCertificate.Issuer.Title"), 40, false); + jdnIssuer.setToolTipText(res.getString("DViewCertificate.jdnIssuer.tooltip")); + + jlSerialNumberHex = new JLabel(res.getString("DViewCertificate.jlSerialNumberHex.text")); + + jtfSerialNumberHex = new JTextField(40); + jtfSerialNumberHex.setEditable(false); + jtfSerialNumberHex.setToolTipText(res.getString("DViewCertificate.jtfSerialNumberHex.tooltip")); + jtfSerialNumberHex.setCaretPosition(0); + + jlSerialNumberDec = new JLabel(res.getString("DViewCertificate.jlSerialNumberDec.text")); + + jtfSerialNumberDec = new JTextField(40); + jtfSerialNumberDec.setEditable(false); + jtfSerialNumberDec.setToolTipText(res.getString("DViewCertificate.jtfSerialNumberDec.tooltip")); + jtfSerialNumberDec.setCaretPosition(0); + + jlValidFrom = new JLabel(res.getString("DViewCertificate.jlValidFrom.text")); + + jtfValidFrom = new JTextField(40); + jtfValidFrom.setEditable(false); + jtfValidFrom.setToolTipText(res.getString("DViewCertificate.jtfValidFrom.tooltip")); + + jlValidUntil = new JLabel(res.getString("DViewCertificate.jlValidUntil.text")); + + jtfValidUntil = new JTextField(40); + jtfValidUntil.setEditable(false); + jtfValidUntil.setToolTipText(res.getString("DViewCertificate.jtfValidUntil.tooltip")); + + jlPublicKey = new JLabel(res.getString("DViewCertificate.jlPublicKey.text")); + + jtfPublicKey = new JTextField(40); + jtfPublicKey.setEditable(false); + jtfPublicKey.setToolTipText(res.getString("DViewCertificate.jtfPublicKey.tooltip")); + + jbViewPublicKeyDetails = new JButton(); + jbViewPublicKeyDetails.setToolTipText(res.getString("DViewCertificate.jbViewPublicKeyDetails.tooltip")); + jbViewPublicKeyDetails.setIcon(new ImageIcon( + Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/viewpubkey.png")))); + + jlSignatureAlgorithm = new JLabel(res.getString("DViewCertificate.jlSignatureAlgorithm.text")); + + jtfSignatureAlgorithm = new JTextField(40); + jtfSignatureAlgorithm.setEditable(false); + jtfSignatureAlgorithm.setToolTipText(res.getString("DViewCertificate.jtfSignatureAlgorithm.tooltip")); + + jlFingerprint = new JLabel(res.getString("DViewCertificate.jlFingerprint.text")); + + jcfFingerprint = new JCertificateFingerprint(30); + + jbExtensions = new JButton(res.getString("DViewCertificate.jbExtensions.text")); + jbExtensions.setToolTipText(res.getString("DViewCertificate.jbExtensions.tooltip")); + PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewCertificate.jbExtensions.mnemonic").charAt(0)); + + jbPem = new JButton(res.getString("DViewCertificate.jbPem.text")); + jbPem.setToolTipText(res.getString("DViewCertificate.jbPem.tooltip")); + PlatformUtil.setMnemonic(jbPem, res.getString("DViewCertificate.jbPem.mnemonic").charAt(0)); + + jbAsn1 = new JButton(res.getString("DViewCertificate.jbAsn1.text")); + jbAsn1.setToolTipText(res.getString("DViewCertificate.jbAsn1.tooltip")); + PlatformUtil.setMnemonic(jbAsn1, res.getString("DViewCertificate.jbAsn1.mnemonic").charAt(0)); + + jbImport = new JButton(res.getString("DViewCertificate.jbImportExport.import.text")); + jbImport.setToolTipText(res.getString("DViewCertificate.jbImportExport.import.tooltip")); + jbImport.setVisible(importExport == IMPORT || importExport == IMPORT_EXPORT); + PlatformUtil.setMnemonic(jbImport, res.getString("DViewCertificate.jbImport.mnemonic").charAt(0)); + + jbExport = new JButton(res.getString("DViewCertificate.jbImportExport.export.text")); + jbExport.setToolTipText(res.getString("DViewCertificate.jbImportExport.export.tooltip")); + jbExport.setVisible(importExport == EXPORT || importExport == IMPORT_EXPORT); + PlatformUtil.setMnemonic(jbExport, res.getString("DViewCertificate.jbExport.mnemonic").charAt(0)); + + jbOK = new JButton(res.getString("DViewCertificate.jbOK.text")); + + jbVerify = new JButton(res.getString("DViewCertificate.jbVerify.text")); + jbVerify.setToolTipText(res.getString("DViewCertificate.jbVerify.tooltip")); + jbVerify.setVisible(importExport != NONE); + PlatformUtil.setMnemonic(jbVerify, res.getString("DViewCertificate.jbVerify.mnemonic").charAt(0)); + + Container pane = getContentPane(); + pane.setLayout(new MigLayout("insets dialog, fill", "[right]unrel[]", "[]unrel[]")); + pane.add(jlSigners, ""); + pane.add(jspSigners, "sgx, wrap"); + pane.add(jlSignerDN, ""); + pane.add(jtfSignerDN, "sgx, wrap"); + pane.add(jlSerial, ""); + pane.add(jdnSerial, "wrap"); + pane.add(jlIssuer, ""); + pane.add(jdnIssuer, "wrap"); + pane.add(jlSerialNumberHex, ""); + pane.add(jtfSerialNumberHex, "wrap"); + pane.add(jlSerialNumberDec, ""); + pane.add(jtfSerialNumberDec, "wrap"); + pane.add(jlValidFrom, ""); + pane.add(jtfValidFrom, "wrap"); + pane.add(jlValidUntil, ""); + pane.add(jtfValidUntil, "wrap"); + pane.add(jlPublicKey, ""); + pane.add(jtfPublicKey, "spanx, split"); + pane.add(jbViewPublicKeyDetails, "wrap"); + pane.add(jlSignatureAlgorithm, ""); + pane.add(jtfSignatureAlgorithm, "wrap"); + pane.add(jlFingerprint, ""); + pane.add(jcfFingerprint, "spanx, growx, wrap"); + pane.add(jbImport, "hidemode 1, spanx, split"); + pane.add(jbExport, "hidemode 1"); + pane.add(jbExtensions, ""); + pane.add(jbPem, ""); + pane.add(jbVerify, "hidemode 1"); + pane.add(jbAsn1, "wrap"); + pane.add(new JSeparator(), "spanx, growx, wrap 15:push"); + pane.add(jbOK, "spanx, tag ok"); + + // TODO JW - list selection listener +// jlbSigners.addTreeSelectionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// populateDetails(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); + + jbOK.addActionListener(evt -> okPressed()); + +// jbExport.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// exportPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbExtensions.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// extensionsPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbImport.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// importPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbViewPublicKeyDetails.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// pubKeyDetailsPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbPem.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// pemEncodingPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbAsn1.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// asn1DumpPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); +// +// jbVerify.addActionListener(evt -> { +// try { +// CursorUtil.setCursorBusy(DViewSignature.this); +// verifyPressed(); +// } finally { +// CursorUtil.setCursorFree(DViewSignature.this); +// } +// }); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent evt) { + closeDialog(); + } + }); + + setResizable(false); + + // TODO JW - select first entry in list + // select (first) leaf in certificate tree +// DefaultMutableTreeNode firstLeaf = ((DefaultMutableTreeNode) topNode).getFirstLeaf(); +// jlbSigners.setSelectionPath(new TreePath(firstLeaf.getPath())); + + getRootPane().setDefaultButton(jbOK); + + pack(); + + SwingUtilities.invokeLater(() -> jbOK.requestFocus()); + } + +// private void verifyPressed() { +// +// X509Certificate cert = getSelectedCertificate(); +// new VerifyCertificateAction(kseFrame, cert, chain).actionPerformed(null); +// } + + private ListModel createSignerList(CMSSignedData signedData) { + DefaultListModel signerList = new DefaultListModel<>(); + + signerList.addAll(signedData.getSignerInfos().getSigners()); + + return signerList; + } + +// private X509Certificate getSelectedCertificate() { +// TreePath[] selections = jlbSigners.getSelectionPaths(); +// +// if (selections == null) { +// return null; +// } +// +// return (X509Certificate) ((DefaultMutableTreeNode) selections[0].getLastPathComponent()).getUserObject(); +// } + +// private void populateDetails() { +// X509Certificate cert = getSelectedCertificate(); +// +// if (cert == null) { +// jdnSerial.setEnabled(false); +// jdnIssuer.setEnabled(false); +// jbViewPublicKeyDetails.setEnabled(false); +// jcfFingerprint.setEnabled(false); +// jbExtensions.setEnabled(false); +// jbPem.setEnabled(false); +// jbAsn1.setEnabled(false); +// +// jtfSignerDN.setText(""); +// jdnSerial.setDistinguishedName(null); +// jdnIssuer.setDistinguishedName(null); +// jtfSerialNumberHex.setText(""); +// jtfSerialNumberDec.setText(""); +// jtfValidFrom.setText(""); +// jtfValidUntil.setText(""); +// jtfPublicKey.setText(""); +// jtfSignatureAlgorithm.setText(""); +// jcfFingerprint.setEncodedCertificate(null); +// } else { +// jdnSerial.setEnabled(true); +// jdnIssuer.setEnabled(true); +// jbViewPublicKeyDetails.setEnabled(true); +// jbExtensions.setEnabled(true); +// jbPem.setEnabled(true); +// jbAsn1.setEnabled(true); +// +// try { +// Date currentDate = new Date(); +// +// Date startDate = cert.getNotBefore(); +// Date endDate = cert.getNotAfter(); +// +// boolean notYetValid = currentDate.before(startDate); +// boolean noLongerValid = currentDate.after(endDate); +// +// jtfSignerDN.setText(Integer.toString(cert.getVersion())); +// jtfSignerDN.setCaretPosition(0); +// +// jdnSerial.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getSubjectX500Principal())); +// +// jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal())); +// +// jtfSerialNumberHex.setText(X509CertUtil.getSerialNumberAsHex(cert)); +// jtfSerialNumberHex.setCaretPosition(0); +// +// jtfSerialNumberDec.setText(X509CertUtil.getSerialNumberAsDec(cert)); +// jtfSerialNumberDec.setCaretPosition(0); +// +// jtfValidFrom.setText(StringUtils.formatDate(startDate)); +// +// if (notYetValid) { +// jtfValidFrom.setText( +// MessageFormat.format(res.getString("DViewCertificate.jtfValidFrom.notyetvalid.text"), +// jtfValidFrom.getText())); +// jtfValidFrom.setForeground(Color.red); +// } else { +// jtfValidFrom.setForeground(jtfSignerDN.getForeground()); +// } +// jtfValidFrom.setCaretPosition(0); +// +// jtfValidUntil.setText(StringUtils.formatDate(endDate)); +// +// if (noLongerValid) { +// jtfValidUntil.setText( +// MessageFormat.format(res.getString("DViewCertificate.jtfValidUntil.expired.text"), +// jtfValidUntil.getText())); +// jtfValidUntil.setForeground(Color.red); +// } else { +// jtfValidUntil.setForeground(jtfSignerDN.getForeground()); +// } +// jtfValidUntil.setCaretPosition(0); +// +// KeyInfo keyInfo = KeyPairUtil.getKeyInfo(cert.getPublicKey()); +// jtfPublicKey.setText(keyInfo.getAlgorithm()); +// Integer keySize = keyInfo.getSize(); +// +// if (keySize != null) { +// jtfPublicKey.setText(MessageFormat.format(res.getString("DViewCertificate.jtfPublicKey.text"), +// jtfPublicKey.getText(), "" + keySize)); +// } else { +// jtfPublicKey.setText(MessageFormat.format(res.getString("DViewCertificate.jtfPublicKey.text"), +// jtfPublicKey.getText(), "?")); +// } +// if (cert.getPublicKey() instanceof ECPublicKey) { +// jtfPublicKey.setText(jtfPublicKey.getText() + " (" + keyInfo.getDetailedAlgorithm() + ")"); +// } +// jtfPublicKey.setCaretPosition(0); +// +// jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(cert)); +// jtfSignatureAlgorithm.setCaretPosition(0); +// +// byte[] encodedCertificate; +// try { +// encodedCertificate = cert.getEncoded(); +// } catch (CertificateEncodingException ex) { +// throw new CryptoException(res.getString("DViewCertificate.NoGetEncodedCert.exception.message"), ex); +// } +// +// jcfFingerprint.setEncodedCertificate(encodedCertificate); +// +// jcfFingerprint.setFingerprintAlg(preferences.getCertificateFingerprintAlgorithm()); +// +// Set critExts = cert.getCriticalExtensionOIDs(); +// Set nonCritExts = cert.getNonCriticalExtensionOIDs(); +// +// if ((critExts != null && !critExts.isEmpty()) || (nonCritExts != null && !nonCritExts.isEmpty())) { +// jbExtensions.setEnabled(true); +// } else { +// jbExtensions.setEnabled(false); +// } +// } catch (CryptoException e) { +// DError.displayError(this, e); +// dispose(); +// } +// } +// } +// +// private void pubKeyDetailsPressed() { +// try { +// X509Certificate cert = getSelectedCertificate(); +// +// DViewPublicKey dViewPublicKey = new DViewPublicKey(this, +// res.getString("DViewCertificate.PubKeyDetails.Title"), +// cert.getPublicKey()); +// dViewPublicKey.setLocationRelativeTo(this); +// dViewPublicKey.setVisible(true); +// } catch (CryptoException e) { +// DError.displayError(this, e); +// } +// } +// +// private void extensionsPressed() { +// X509Certificate cert = getSelectedCertificate(); +// +// DViewExtensions dViewExtensions = new DViewExtensions(this, res.getString("DViewCertificate.Extensions.Title"), +// cert, kseFrame); +// dViewExtensions.setLocationRelativeTo(this); +// dViewExtensions.setVisible(true); +// } +// +// private void pemEncodingPressed() { +// X509Certificate cert = getSelectedCertificate(); +// +// try { +// DViewPem dViewCertPem = new DViewPem(this, res.getString("DViewCertificate.Pem.Title"), cert); +// dViewCertPem.setLocationRelativeTo(this); +// dViewCertPem.setVisible(true); +// } catch (CryptoException e) { +// DError.displayError(this, e); +// } +// } +// +// private void asn1DumpPressed() { +// X509Certificate cert = getSelectedCertificate(); +// +// try { +// DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, cert); +// dViewAsn1Dump.setLocationRelativeTo(this); +// dViewAsn1Dump.setVisible(true); +// } catch (Asn1Exception | IOException e) { +// DError.displayError(this, e); +// } +// } +// +// private void importPressed() { +// X509Certificate cert = getSelectedCertificate(); +// new ImportTrustedCertificateAction(kseFrame, cert).actionPerformed(null); +// } +// +// private void exportPressed() { +// X509Certificate cert = getSelectedCertificate(); +// new ExportTrustedCertificateAction(kseFrame, cert).actionPerformed(null); +// } + + private void okPressed() { + preferences.setCertificateFingerprintAlgorithm(jcfFingerprint.getSelectedFingerprintAlg()); + closeDialog(); + } + + private void closeDialog() { + setVisible(false); + dispose(); + } + + private class X509CertificateComparator implements Comparator { + + /* (non-Javadoc) + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + @Override + public int compare(X509Certificate cert1, X509Certificate cert2) { + + // Compare certificates for equality. Where all we care about is if + // the certificates are equal or not - the order is unimportant + if (cert1.equals(cert2)) { + return 0; + } + + // Compare on subject DN + int i = cert1.getSubjectX500Principal().toString().compareTo(cert2.getSubjectX500Principal().toString()); + + if (i != 0) { + return i; + } + + // Compare on issuer DN + i = cert1.getIssuerX500Principal().toString().compareTo(cert2.getIssuerX500Principal().toString()); + + if (i != 0) { + return i; + } + + // If all else fails then compare serial numbers - if this is the + // same and the DNs are the same then it is probably the same certificate anyway + return cert1.getSerialNumber().subtract(cert2.getSerialNumber()).intValue(); + } + } + + public static void main(String[] args) throws Exception { + DialogViewer.prepare(); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", KSE.BC); + + KeyPair caKeyPair = keyGen.genKeyPair(); + X509CertificateGenerator certGen = new X509CertificateGenerator(X509CertificateVersion.VERSION3); + X509Certificate caCert = certGen.generateSelfSigned(new X500Name("cn=CA"), Date.from(Instant.now()), + Date.from(Instant.now().plus(3650, ChronoUnit.DAYS)), + caKeyPair.getPublic(), caKeyPair.getPrivate(), + SignatureType.SHA224WITHRSAANDMGF1, new BigInteger( + Hex.decode("1122334455667788990011223344556677889900"))); + + KeyPair eeKeyPair = keyGen.genKeyPair(); + X509Certificate eeCert = certGen.generate(new X500Name("cn=EE"), X500NameUtils.x500PrincipalToX500Name( + caCert.getSubjectX500Principal()), Date.from(Instant.now()), + Date.from(Instant.now().plus(365, ChronoUnit.DAYS)), + eeKeyPair.getPublic(), eeKeyPair.getPrivate(), + SignatureType.SHA224WITHRSAANDMGF1, new BigInteger( + Hex.decode("0011223344556677889900112233445566778899"))); + + X509Certificate[] certs = new X509Certificate[] { eeCert, caCert }; + + // TODO JW - fix main method +// DViewSignature dialog = new DViewSignature(new javax.swing.JFrame(), "Title", certs, new KseFrame(), +// IMPORT_EXPORT); +// DialogViewer.run(dialog); + } +} diff --git a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java new file mode 100644 index 000000000..df6cb0ea6 --- /dev/null +++ b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java @@ -0,0 +1,85 @@ +/* + * 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.dialogs; + +import java.awt.Component; + +import javax.swing.DefaultListCellRenderer; +import javax.swing.JLabel; +import javax.swing.JList; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cms.SignerInformation; +import org.kse.crypto.x509.X500NameUtils; +import org.kse.utilities.StringUtils; + +/** + * 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. + * + * @param list The JList + * @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 + * @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); + + if (value instanceof SignerInformation) { + SignerInformation signer = (SignerInformation) value; + X500Name issuer = signer.getSID().getIssuer(); + + String shortName = X500NameUtils.extractCN(issuer); + + if (StringUtils.isBlank(shortName)) { + shortName = issuer.toString(); + } + + // subject DN can be empty in some cases + if (StringUtils.isBlank(shortName)) { + shortName = signer.getSID().getSerialNumber().toString(); + } + + cell.setText(shortName); + + // TODO JW - need icon for signer list cell renderer +// ImageIcon icon = new ImageIcon(getClass().getResource("images/certificate_node.png")); +// cell.setIcon(icon); + + cell.setToolTipText(issuer.toString()); + } + + return cell; + } +} From 4280243cd0354e3ab3146d884044f56c221c1e74 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:10:03 -0800 Subject: [PATCH 06/69] Initial population of the form content. --- .../org/kse/gui/dialogs/DViewSignature.java | 425 ++++++------------ 1 file changed, 130 insertions(+), 295 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 4baf7203f..9d1aa5e6e 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -35,6 +35,7 @@ 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; @@ -66,6 +67,10 @@ import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; +import org.bouncycastle.asn1.ASN1GeneralizedTime; +import org.bouncycastle.asn1.ASN1UTCTime; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.cms.CMSAttributes; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; @@ -95,6 +100,7 @@ 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; @@ -110,45 +116,30 @@ public class DViewSignature extends JEscDialog { private KsePreferences preferences = PreferencesManager.getPreferences(); - public static final int NONE = 0; - public static final int IMPORT = 1; - public static final int EXPORT = 2; - public static final int IMPORT_EXPORT = 3; - private int importExport = 0; - private KseFrame kseFrame; private JLabel jlSigners; private JList jlbSigners; private JScrollPane jspSigners; + private JLabel jlVersion; + private JTextField jtfVersion; private JLabel jlSignerDN; - private JTextField jtfSignerDN; - private JLabel jlSerial; - private JDistinguishedName jdnSerial; - private JLabel jlIssuer; - private JDistinguishedName jdnIssuer; + private JDistinguishedName jdnSignerDN; private JLabel jlSerialNumberHex; private JTextField jtfSerialNumberHex; private JLabel jlSerialNumberDec; private JTextField jtfSerialNumberDec; - private JLabel jlValidFrom; - private JTextField jtfValidFrom; - private JLabel jlValidUntil; - private JTextField jtfValidUntil; - private JLabel jlPublicKey; - private JTextField jtfPublicKey; - private JButton jbViewPublicKeyDetails; + private JLabel jlSigningTime; + private JTextField jtfSigningTime; private JLabel jlSignatureAlgorithm; private JTextField jtfSignatureAlgorithm; - private JLabel jlFingerprint; - private JCertificateFingerprint jcfFingerprint; + // TODO JW - Add content type + // TODO JW - Add content digest + // TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes private JButton jbExtensions; private JButton jbPem; private JButton jbAsn1; - private JButton jbImport; - private JButton jbExport; private JButton jbOK; - private JButton jbVerify; private CMSSignedData signedData; @@ -159,14 +150,12 @@ public class DViewSignature extends JEscDialog { * @param title The dialog title * @param signedData Signature to display * @param kseFrame Reference to main class with currently opened keystores and their contents - * @param importExport Show import button/export button/no extra button? * @throws CryptoException A problem was encountered getting the certificates' details */ - public DViewSignature(Window parent, String title, CMSSignedData signedData, KseFrame kseFrame, int importExport) + public DViewSignature(Window parent, String title, CMSSignedData signedData, KseFrame kseFrame) throws CryptoException { super(parent, title, Dialog.ModalityType.MODELESS); this.kseFrame = kseFrame; - this.importExport = importExport; this.signedData = signedData; initComponents(signedData); } @@ -185,21 +174,16 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jspSigners.setPreferredSize(new Dimension(100, 75)); - jlSignerDN = new JLabel(res.getString("DViewCertificate.jlVersion.text")); - - jtfSignerDN = new JTextField(40); - jtfSignerDN.setEditable(false); - jtfSignerDN.setToolTipText(res.getString("DViewCertificate.jtfVersion.tooltip")); + jlVersion = new JLabel(res.getString("DViewCertificate.jlVersion.text")); - jlSerial = new JLabel(res.getString("DViewCertificate.jlSubject.text")); + jtfVersion = new JTextField(40); + jtfVersion.setEditable(false); + jtfVersion.setToolTipText(res.getString("DViewCertificate.jtfVersion.tooltip")); - jdnSerial = new JDistinguishedName(res.getString("DViewCertificate.Subject.Title"), 40, false); - jdnSerial.setToolTipText(res.getString("DViewCertificate.jdnSubject.tooltip")); + jlSignerDN = new JLabel(res.getString("DViewCertificate.jlSubject.text")); - jlIssuer = new JLabel(res.getString("DViewCertificate.jlIssuer.text")); - - jdnIssuer = new JDistinguishedName(res.getString("DViewCertificate.Issuer.Title"), 40, false); - jdnIssuer.setToolTipText(res.getString("DViewCertificate.jdnIssuer.tooltip")); + jdnSignerDN = new JDistinguishedName(res.getString("DViewCertificate.Subject.Title"), 40, false); + jdnSignerDN.setToolTipText(res.getString("DViewCertificate.jdnSubject.tooltip")); jlSerialNumberHex = new JLabel(res.getString("DViewCertificate.jlSerialNumberHex.text")); @@ -215,28 +199,11 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jtfSerialNumberDec.setToolTipText(res.getString("DViewCertificate.jtfSerialNumberDec.tooltip")); jtfSerialNumberDec.setCaretPosition(0); - jlValidFrom = new JLabel(res.getString("DViewCertificate.jlValidFrom.text")); - - jtfValidFrom = new JTextField(40); - jtfValidFrom.setEditable(false); - jtfValidFrom.setToolTipText(res.getString("DViewCertificate.jtfValidFrom.tooltip")); - - jlValidUntil = new JLabel(res.getString("DViewCertificate.jlValidUntil.text")); - - jtfValidUntil = new JTextField(40); - jtfValidUntil.setEditable(false); - jtfValidUntil.setToolTipText(res.getString("DViewCertificate.jtfValidUntil.tooltip")); - - jlPublicKey = new JLabel(res.getString("DViewCertificate.jlPublicKey.text")); + jlSigningTime = new JLabel(res.getString("DViewCertificate.jlValidFrom.text")); - jtfPublicKey = new JTextField(40); - jtfPublicKey.setEditable(false); - jtfPublicKey.setToolTipText(res.getString("DViewCertificate.jtfPublicKey.tooltip")); - - jbViewPublicKeyDetails = new JButton(); - jbViewPublicKeyDetails.setToolTipText(res.getString("DViewCertificate.jbViewPublicKeyDetails.tooltip")); - jbViewPublicKeyDetails.setIcon(new ImageIcon( - Toolkit.getDefaultToolkit().createImage(getClass().getResource("images/viewpubkey.png")))); + jtfSigningTime = new JTextField(40); + jtfSigningTime.setEditable(false); + jtfSigningTime.setToolTipText(res.getString("DViewCertificate.jtfValidFrom.tooltip")); jlSignatureAlgorithm = new JLabel(res.getString("DViewCertificate.jlSignatureAlgorithm.text")); @@ -244,10 +211,6 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jtfSignatureAlgorithm.setEditable(false); jtfSignatureAlgorithm.setToolTipText(res.getString("DViewCertificate.jtfSignatureAlgorithm.tooltip")); - jlFingerprint = new JLabel(res.getString("DViewCertificate.jlFingerprint.text")); - - jcfFingerprint = new JCertificateFingerprint(30); - jbExtensions = new JButton(res.getString("DViewCertificate.jbExtensions.text")); jbExtensions.setToolTipText(res.getString("DViewCertificate.jbExtensions.tooltip")); PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewCertificate.jbExtensions.mnemonic").charAt(0)); @@ -260,78 +223,42 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jbAsn1.setToolTipText(res.getString("DViewCertificate.jbAsn1.tooltip")); PlatformUtil.setMnemonic(jbAsn1, res.getString("DViewCertificate.jbAsn1.mnemonic").charAt(0)); - jbImport = new JButton(res.getString("DViewCertificate.jbImportExport.import.text")); - jbImport.setToolTipText(res.getString("DViewCertificate.jbImportExport.import.tooltip")); - jbImport.setVisible(importExport == IMPORT || importExport == IMPORT_EXPORT); - PlatformUtil.setMnemonic(jbImport, res.getString("DViewCertificate.jbImport.mnemonic").charAt(0)); - - jbExport = new JButton(res.getString("DViewCertificate.jbImportExport.export.text")); - jbExport.setToolTipText(res.getString("DViewCertificate.jbImportExport.export.tooltip")); - jbExport.setVisible(importExport == EXPORT || importExport == IMPORT_EXPORT); - PlatformUtil.setMnemonic(jbExport, res.getString("DViewCertificate.jbExport.mnemonic").charAt(0)); - jbOK = new JButton(res.getString("DViewCertificate.jbOK.text")); - jbVerify = new JButton(res.getString("DViewCertificate.jbVerify.text")); - jbVerify.setToolTipText(res.getString("DViewCertificate.jbVerify.tooltip")); - jbVerify.setVisible(importExport != NONE); - PlatformUtil.setMnemonic(jbVerify, res.getString("DViewCertificate.jbVerify.mnemonic").charAt(0)); - Container pane = getContentPane(); pane.setLayout(new MigLayout("insets dialog, fill", "[right]unrel[]", "[]unrel[]")); pane.add(jlSigners, ""); pane.add(jspSigners, "sgx, wrap"); + pane.add(jlVersion, ""); + pane.add(jtfVersion, "sgx, wrap"); pane.add(jlSignerDN, ""); - pane.add(jtfSignerDN, "sgx, wrap"); - pane.add(jlSerial, ""); - pane.add(jdnSerial, "wrap"); - pane.add(jlIssuer, ""); - pane.add(jdnIssuer, "wrap"); + pane.add(jdnSignerDN, "wrap"); pane.add(jlSerialNumberHex, ""); pane.add(jtfSerialNumberHex, "wrap"); pane.add(jlSerialNumberDec, ""); pane.add(jtfSerialNumberDec, "wrap"); - pane.add(jlValidFrom, ""); - pane.add(jtfValidFrom, "wrap"); - pane.add(jlValidUntil, ""); - pane.add(jtfValidUntil, "wrap"); - pane.add(jlPublicKey, ""); - pane.add(jtfPublicKey, "spanx, split"); - pane.add(jbViewPublicKeyDetails, "wrap"); + pane.add(jlSigningTime, ""); + pane.add(jtfSigningTime, "wrap"); pane.add(jlSignatureAlgorithm, ""); pane.add(jtfSignatureAlgorithm, "wrap"); - pane.add(jlFingerprint, ""); - pane.add(jcfFingerprint, "spanx, growx, wrap"); - pane.add(jbImport, "hidemode 1, spanx, split"); - pane.add(jbExport, "hidemode 1"); + // TODO JW - buttons are not aligned correctly. pane.add(jbExtensions, ""); pane.add(jbPem, ""); - pane.add(jbVerify, "hidemode 1"); pane.add(jbAsn1, "wrap"); pane.add(new JSeparator(), "spanx, growx, wrap 15:push"); pane.add(jbOK, "spanx, tag ok"); - // TODO JW - list selection listener -// jlbSigners.addTreeSelectionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// populateDetails(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); + jlbSigners.addListSelectionListener(evt -> { + try { + CursorUtil.setCursorBusy(DViewSignature.this); + populateDetails(); + } finally { + CursorUtil.setCursorFree(DViewSignature.this); + } + }); jbOK.addActionListener(evt -> okPressed()); -// jbExport.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// exportPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); -// // jbExtensions.addActionListener(evt -> { // try { // CursorUtil.setCursorBusy(DViewSignature.this); @@ -341,24 +268,6 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { // } // }); // -// jbImport.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// importPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); -// -// jbViewPublicKeyDetails.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// pubKeyDetailsPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); -// // jbPem.addActionListener(evt -> { // try { // CursorUtil.setCursorBusy(DViewSignature.this); @@ -375,15 +284,6 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { // } finally { // CursorUtil.setCursorFree(DViewSignature.this); // } -// }); -// -// jbVerify.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// verifyPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } // }); addWindowListener(new WindowAdapter() { @@ -395,10 +295,8 @@ public void windowClosing(WindowEvent evt) { setResizable(false); - // TODO JW - select first entry in list - // select (first) leaf in certificate tree -// DefaultMutableTreeNode firstLeaf = ((DefaultMutableTreeNode) topNode).getFirstLeaf(); -// jlbSigners.setSelectionPath(new TreePath(firstLeaf.getPath())); + // select first signer in signer list + jlbSigners.setSelectedIndex(0); getRootPane().setDefaultButton(jbOK); @@ -407,12 +305,6 @@ public void windowClosing(WindowEvent evt) { SwingUtilities.invokeLater(() -> jbOK.requestFocus()); } -// private void verifyPressed() { -// -// X509Certificate cert = getSelectedCertificate(); -// new VerifyCertificateAction(kseFrame, cert, chain).actionPerformed(null); -// } - private ListModel createSignerList(CMSSignedData signedData) { DefaultListModel signerList = new DefaultListModel<>(); @@ -421,124 +313,89 @@ private ListModel createSignerList(CMSSignedData signedData) return signerList; } -// private X509Certificate getSelectedCertificate() { -// TreePath[] selections = jlbSigners.getSelectionPaths(); -// -// if (selections == null) { -// return null; -// } -// -// return (X509Certificate) ((DefaultMutableTreeNode) selections[0].getLastPathComponent()).getUserObject(); -// } + private SignerInformation getSelectedSignerInfo() { + return jlbSigners.getSelectedValue(); + } + + private void populateDetails() { + SignerInformation signerInfo = getSelectedSignerInfo(); + + if (signerInfo == null) { + jdnSignerDN.setEnabled(false); + jbExtensions.setEnabled(false); + jbPem.setEnabled(false); + jbAsn1.setEnabled(false); + + jtfVersion.setText(""); + jdnSignerDN.setDistinguishedName(null); + jtfSerialNumberHex.setText(""); + jtfSerialNumberDec.setText(""); + jtfSigningTime.setText(""); + jtfSignatureAlgorithm.setText(""); + } else { + jdnSignerDN.setEnabled(true); + jbExtensions.setEnabled(true); + jbPem.setEnabled(true); + jbAsn1.setEnabled(true); -// private void populateDetails() { -// X509Certificate cert = getSelectedCertificate(); -// -// if (cert == null) { -// jdnSerial.setEnabled(false); -// jdnIssuer.setEnabled(false); -// jbViewPublicKeyDetails.setEnabled(false); -// jcfFingerprint.setEnabled(false); -// jbExtensions.setEnabled(false); -// jbPem.setEnabled(false); -// jbAsn1.setEnabled(false); -// -// jtfSignerDN.setText(""); -// jdnSerial.setDistinguishedName(null); -// jdnIssuer.setDistinguishedName(null); -// jtfSerialNumberHex.setText(""); -// jtfSerialNumberDec.setText(""); -// jtfValidFrom.setText(""); -// jtfValidUntil.setText(""); -// jtfPublicKey.setText(""); -// jtfSignatureAlgorithm.setText(""); -// jcfFingerprint.setEncodedCertificate(null); -// } else { -// jdnSerial.setEnabled(true); -// jdnIssuer.setEnabled(true); -// jbViewPublicKeyDetails.setEnabled(true); -// jbExtensions.setEnabled(true); -// jbPem.setEnabled(true); -// jbAsn1.setEnabled(true); -// // try { -// Date currentDate = new Date(); -// -// Date startDate = cert.getNotBefore(); -// Date endDate = cert.getNotAfter(); -// -// boolean notYetValid = currentDate.before(startDate); + Date currentDate = new Date(); + Date signingTime = null; + + // TODO JW - Make the signing time extraction logic a utility method. + Attribute signingTimeAttribute = signerInfo.getSignedAttributes().get(CMSAttributes.signingTime); + if (signingTimeAttribute != null) { + Enumeration e = signingTimeAttribute.getAttrValues().getObjects(); + if (e.hasMoreElements()) { + Object o = e.nextElement(); + try { + if (o instanceof ASN1UTCTime) { + signingTime = ((ASN1UTCTime) o).getAdjustedDate(); + } else if (o instanceof ASN1GeneralizedTime) { + signingTime = ((ASN1GeneralizedTime) o).getDate(); + } + } catch (ParseException e1) { + // TODO JW Auto-generated catch block + e1.printStackTrace(); + } + } + } + + + // TODO JW - Need to determine valid date? + boolean noLongerValid = false; // boolean noLongerValid = currentDate.after(endDate); -// -// jtfSignerDN.setText(Integer.toString(cert.getVersion())); -// jtfSignerDN.setCaretPosition(0); -// -// jdnSerial.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getSubjectX500Principal())); -// -// jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal())); -// -// jtfSerialNumberHex.setText(X509CertUtil.getSerialNumberAsHex(cert)); -// jtfSerialNumberHex.setCaretPosition(0); -// -// jtfSerialNumberDec.setText(X509CertUtil.getSerialNumberAsDec(cert)); -// jtfSerialNumberDec.setCaretPosition(0); -// -// jtfValidFrom.setText(StringUtils.formatDate(startDate)); -// -// if (notYetValid) { -// jtfValidFrom.setText( -// MessageFormat.format(res.getString("DViewCertificate.jtfValidFrom.notyetvalid.text"), -// jtfValidFrom.getText())); -// jtfValidFrom.setForeground(Color.red); -// } else { -// jtfValidFrom.setForeground(jtfSignerDN.getForeground()); -// } -// jtfValidFrom.setCaretPosition(0); -// -// jtfValidUntil.setText(StringUtils.formatDate(endDate)); -// -// if (noLongerValid) { -// jtfValidUntil.setText( -// MessageFormat.format(res.getString("DViewCertificate.jtfValidUntil.expired.text"), -// jtfValidUntil.getText())); -// jtfValidUntil.setForeground(Color.red); -// } else { -// jtfValidUntil.setForeground(jtfSignerDN.getForeground()); -// } -// jtfValidUntil.setCaretPosition(0); -// -// KeyInfo keyInfo = KeyPairUtil.getKeyInfo(cert.getPublicKey()); -// jtfPublicKey.setText(keyInfo.getAlgorithm()); -// Integer keySize = keyInfo.getSize(); -// -// if (keySize != null) { -// jtfPublicKey.setText(MessageFormat.format(res.getString("DViewCertificate.jtfPublicKey.text"), -// jtfPublicKey.getText(), "" + keySize)); -// } else { -// jtfPublicKey.setText(MessageFormat.format(res.getString("DViewCertificate.jtfPublicKey.text"), -// jtfPublicKey.getText(), "?")); -// } -// if (cert.getPublicKey() instanceof ECPublicKey) { -// jtfPublicKey.setText(jtfPublicKey.getText() + " (" + keyInfo.getDetailedAlgorithm() + ")"); -// } -// jtfPublicKey.setCaretPosition(0); -// -// jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(cert)); + + jtfVersion.setText(Integer.toString(signerInfo.getVersion())); + jtfVersion.setCaretPosition(0); + + jdnSignerDN.setDistinguishedName(signerInfo.getSID().getIssuer()); + + // TODO JW - Make the HexUtil call generic? It was copied from X509CertUtil. + jtfSerialNumberHex.setText(HexUtil.getHexString(signerInfo.getSID().getSerialNumber(), "0x", 0, 0)); + jtfSerialNumberHex.setCaretPosition(0); + + // TODO JW - Make the BigInteger call generic? It was copied from X509CertUtil. + jtfSerialNumberDec.setText(new BigInteger(1, signerInfo.getSID().getSerialNumber().toByteArray()).toString(10)); + jtfSerialNumberDec.setCaretPosition(0); + + jtfSigningTime.setText(StringUtils.formatDate(signingTime)); + + if (noLongerValid) { + jtfSigningTime.setText( + MessageFormat.format(res.getString("DViewCertificate.jtfSigningTime.expired.text"), + jtfSigningTime.getText())); + jtfSigningTime.setForeground(Color.red); + } else { + jtfSigningTime.setForeground(jtfVersion.getForeground()); + } + jtfSigningTime.setCaretPosition(0); + +// jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(signerInfo)); // jtfSignatureAlgorithm.setCaretPosition(0); -// -// byte[] encodedCertificate; -// try { -// encodedCertificate = cert.getEncoded(); -// } catch (CertificateEncodingException ex) { -// throw new CryptoException(res.getString("DViewCertificate.NoGetEncodedCert.exception.message"), ex); -// } -// -// jcfFingerprint.setEncodedCertificate(encodedCertificate); -// -// jcfFingerprint.setFingerprintAlg(preferences.getCertificateFingerprintAlgorithm()); -// -// Set critExts = cert.getCriticalExtensionOIDs(); -// Set nonCritExts = cert.getNonCriticalExtensionOIDs(); + +// Set critExts = signerInfo.getCriticalExtensionOIDs(); +// Set nonCritExts = signerInfo.getNonCriticalExtensionOIDs(); // // if ((critExts != null && !critExts.isEmpty()) || (nonCritExts != null && !nonCritExts.isEmpty())) { // jbExtensions.setEnabled(true); @@ -549,25 +406,11 @@ private ListModel createSignerList(CMSSignedData signedData) // DError.displayError(this, e); // dispose(); // } -// } -// } -// -// private void pubKeyDetailsPressed() { -// try { -// X509Certificate cert = getSelectedCertificate(); -// -// DViewPublicKey dViewPublicKey = new DViewPublicKey(this, -// res.getString("DViewCertificate.PubKeyDetails.Title"), -// cert.getPublicKey()); -// dViewPublicKey.setLocationRelativeTo(this); -// dViewPublicKey.setVisible(true); -// } catch (CryptoException e) { -// DError.displayError(this, e); -// } -// } -// + } + } + // private void extensionsPressed() { -// X509Certificate cert = getSelectedCertificate(); +// X509Certificate cert = getSelectedSignerInfo(); // // DViewExtensions dViewExtensions = new DViewExtensions(this, res.getString("DViewCertificate.Extensions.Title"), // cert, kseFrame); @@ -576,7 +419,8 @@ private ListModel createSignerList(CMSSignedData signedData) // } // // private void pemEncodingPressed() { -// X509Certificate cert = getSelectedCertificate(); +// TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 strusture) +// X509Certificate cert = getSelectedSignerInfo(); // // try { // DViewPem dViewCertPem = new DViewPem(this, res.getString("DViewCertificate.Pem.Title"), cert); @@ -588,7 +432,8 @@ private ListModel createSignerList(CMSSignedData signedData) // } // // private void asn1DumpPressed() { -// X509Certificate cert = getSelectedCertificate(); +// TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 strusture) +// X509Certificate cert = getSelectedSignerInfo(); // // try { // DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, cert); @@ -597,20 +442,10 @@ private ListModel createSignerList(CMSSignedData signedData) // } catch (Asn1Exception | IOException e) { // DError.displayError(this, e); // } -// } -// -// private void importPressed() { -// X509Certificate cert = getSelectedCertificate(); -// new ImportTrustedCertificateAction(kseFrame, cert).actionPerformed(null); -// } -// -// private void exportPressed() { -// X509Certificate cert = getSelectedCertificate(); -// new ExportTrustedCertificateAction(kseFrame, cert).actionPerformed(null); // } private void okPressed() { - preferences.setCertificateFingerprintAlgorithm(jcfFingerprint.getSelectedFingerprintAlg()); + // TODO JW - set any preferences here (e.g., chosen fingerprint algorithm) closeDialog(); } From e163bb941730927caf8d04f0815840ceaed55787 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Mon, 18 Nov 2024 09:12:46 -0800 Subject: [PATCH 07/69] Initial update to resource files for DViewSignature. Added content type and digest. --- .../org/kse/gui/dialogs/DViewSignature.java | 92 +++++++++++++------ .../org/kse/gui/dialogs/resources.properties | 34 +++++++ 2 files changed, 99 insertions(+), 27 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 9d1aa5e6e..bd4d0192a 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -43,7 +43,9 @@ 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; @@ -68,9 +70,11 @@ 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.Attribute; import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; @@ -134,6 +138,10 @@ public class DViewSignature extends JEscDialog { private JLabel jlSignatureAlgorithm; private JTextField jtfSignatureAlgorithm; // TODO JW - Add content type + private JLabel jlContentType; + private JTextField jtfContentType; + private JLabel jlContentDigest; + private JTextField jtfContentDigest; // TODO JW - Add content digest // TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes private JButton jbExtensions; @@ -161,7 +169,7 @@ public DViewSignature(Window parent, String title, CMSSignedData signedData, Kse } private void initComponents(CMSSignedData signedData) throws CryptoException { - jlSigners = new JLabel(res.getString("DViewCertificate.jlHierarchy.text")); + jlSigners = new JLabel(res.getString("DViewSignature.jlSigners.text")); jlbSigners = new JList<>(createSignerList(signedData)); // TODO JW - Signer list row height? @@ -174,56 +182,68 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); jspSigners.setPreferredSize(new Dimension(100, 75)); - jlVersion = new JLabel(res.getString("DViewCertificate.jlVersion.text")); + jlVersion = new JLabel(res.getString("DViewSignature.jlVersion.text")); jtfVersion = new JTextField(40); jtfVersion.setEditable(false); - jtfVersion.setToolTipText(res.getString("DViewCertificate.jtfVersion.tooltip")); + jtfVersion.setToolTipText(res.getString("DViewSignature.jtfVersion.tooltip")); - jlSignerDN = new JLabel(res.getString("DViewCertificate.jlSubject.text")); + jlSignerDN = new JLabel(res.getString("DViewSignature.jlSignerDN.text")); - jdnSignerDN = new JDistinguishedName(res.getString("DViewCertificate.Subject.Title"), 40, false); - jdnSignerDN.setToolTipText(res.getString("DViewCertificate.jdnSubject.tooltip")); + jdnSignerDN = new JDistinguishedName(res.getString("DViewSignature.SignerDN.Title"), 40, false); + jdnSignerDN.setToolTipText(res.getString("DViewSignature.jdnSignerDN.tooltip")); - jlSerialNumberHex = new JLabel(res.getString("DViewCertificate.jlSerialNumberHex.text")); + jlSerialNumberHex = new JLabel(res.getString("DViewSignature.jlSerialNumberHex.text")); jtfSerialNumberHex = new JTextField(40); jtfSerialNumberHex.setEditable(false); - jtfSerialNumberHex.setToolTipText(res.getString("DViewCertificate.jtfSerialNumberHex.tooltip")); + jtfSerialNumberHex.setToolTipText(res.getString("DViewSignature.jtfSerialNumberHex.tooltip")); jtfSerialNumberHex.setCaretPosition(0); - jlSerialNumberDec = new JLabel(res.getString("DViewCertificate.jlSerialNumberDec.text")); + jlSerialNumberDec = new JLabel(res.getString("DViewSignature.jlSerialNumberDec.text")); jtfSerialNumberDec = new JTextField(40); jtfSerialNumberDec.setEditable(false); - jtfSerialNumberDec.setToolTipText(res.getString("DViewCertificate.jtfSerialNumberDec.tooltip")); + jtfSerialNumberDec.setToolTipText(res.getString("DViewSignature.jtfSerialNumberDec.tooltip")); jtfSerialNumberDec.setCaretPosition(0); - jlSigningTime = new JLabel(res.getString("DViewCertificate.jlValidFrom.text")); + jlSigningTime = new JLabel(res.getString("DViewSignature.jlSigningTime.text")); jtfSigningTime = new JTextField(40); jtfSigningTime.setEditable(false); - jtfSigningTime.setToolTipText(res.getString("DViewCertificate.jtfValidFrom.tooltip")); + jtfSigningTime.setToolTipText(res.getString("DViewSignature.jtfSigningTime.tooltip")); - jlSignatureAlgorithm = new JLabel(res.getString("DViewCertificate.jlSignatureAlgorithm.text")); + jlSignatureAlgorithm = new JLabel(res.getString("DViewSignature.jlSignatureAlgorithm.text")); jtfSignatureAlgorithm = new JTextField(40); jtfSignatureAlgorithm.setEditable(false); - jtfSignatureAlgorithm.setToolTipText(res.getString("DViewCertificate.jtfSignatureAlgorithm.tooltip")); + 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")); + + jlContentDigest = new JLabel(res.getString("DViewSignature.jlContentDigest.text")); + + jtfContentDigest = new JTextField(40); + jtfContentDigest.setEditable(false); + jtfContentDigest.setToolTipText(res.getString("DViewSignature.jtfContentDigest.tooltip")); - jbExtensions = new JButton(res.getString("DViewCertificate.jbExtensions.text")); - jbExtensions.setToolTipText(res.getString("DViewCertificate.jbExtensions.tooltip")); - PlatformUtil.setMnemonic(jbExtensions, res.getString("DViewCertificate.jbExtensions.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)); - jbPem = new JButton(res.getString("DViewCertificate.jbPem.text")); - jbPem.setToolTipText(res.getString("DViewCertificate.jbPem.tooltip")); - PlatformUtil.setMnemonic(jbPem, res.getString("DViewCertificate.jbPem.mnemonic").charAt(0)); + 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)); - jbAsn1 = new JButton(res.getString("DViewCertificate.jbAsn1.text")); - jbAsn1.setToolTipText(res.getString("DViewCertificate.jbAsn1.tooltip")); - PlatformUtil.setMnemonic(jbAsn1, res.getString("DViewCertificate.jbAsn1.mnemonic").charAt(0)); + jbAsn1 = new JButton(res.getString("DViewSignature.jbAsn1.text")); + jbAsn1.setToolTipText(res.getString("DViewSignature.jbAsn1.tooltip")); + PlatformUtil.setMnemonic(jbAsn1, res.getString("DViewSignature.jbAsn1.mnemonic").charAt(0)); - jbOK = new JButton(res.getString("DViewCertificate.jbOK.text")); + jbOK = new JButton(res.getString("DViewSignature.jbOK.text")); Container pane = getContentPane(); pane.setLayout(new MigLayout("insets dialog, fill", "[right]unrel[]", "[]unrel[]")); @@ -241,8 +261,12 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { 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 - buttons are not aligned correctly. - pane.add(jbExtensions, ""); +// pane.add(jbExtensions, ""); pane.add(jbPem, ""); pane.add(jbAsn1, "wrap"); pane.add(new JSeparator(), "spanx, growx, wrap 15:push"); @@ -322,7 +346,7 @@ private void populateDetails() { if (signerInfo == null) { jdnSignerDN.setEnabled(false); - jbExtensions.setEnabled(false); +// jbExtensions.setEnabled(false); jbPem.setEnabled(false); jbAsn1.setEnabled(false); @@ -332,9 +356,11 @@ private void populateDetails() { jtfSerialNumberDec.setText(""); jtfSigningTime.setText(""); jtfSignatureAlgorithm.setText(""); + jtfContentType.setText(""); + jtfContentDigest.setText(""); } else { jdnSignerDN.setEnabled(true); - jbExtensions.setEnabled(true); +// jbExtensions.setEnabled(true); jbPem.setEnabled(true); jbAsn1.setEnabled(true); @@ -391,6 +417,18 @@ 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())); + + jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':')); + // jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(signerInfo)); // jtfSignatureAlgorithm.setCaretPosition(0); 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 4faaa3227..689dd0c1d 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -582,6 +582,40 @@ DViewSecretKey.jtfFormat.tooltip = Secret key's encoded format DViewSecretKey.jtfKeySize.text = {0} bits DViewSecretKey.jtfKeySize.tooltip = Secret key's size +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.SignerDN.Title = Signer +DViewSignature.jbAsn1.mnemonic = A +DViewSignature.jbAsn1.text = ASN.1 +DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signature +#DViewSignature.jbExtensions.mnemonic = E +#DViewSignature.jbExtensions.text = Extensions +#DViewSignature.jbExtensions.tooltip = Display the certificate's extensions +DViewSignature.jbOK.text = OK +DViewSignature.jbPem.mnemonic = P +DViewSignature.jbPem.text = PEM +DViewSignature.jbPem.tooltip = Display signature as PEM +DViewSignature.jdnSignerDN.tooltip = Signer's issuer certificate distinguished name +DViewSignature.jlContentDigest.text = Content Digest: +DViewSignature.jlContentType.text = Content Type: +DViewSignature.jlSigners.text = Signers: +DViewSignature.jlSignerDN.text = Signer: +DViewSignature.jlSerialNumberDec.text = Serial Number (dec.): +DViewSignature.jlSerialNumberHex.text = Serial Number (hex.): +DViewSignature.jlSignatureAlgorithm.text = Signature Algorithm: +DViewSignature.jlSigningTime.text = Signing Time: +DViewSignature.jlVersion.text = Version: +DViewSignature.jtfContentDigest.tooltip = The message digest of the signed content +DViewSignature.jtfContentType.tooltip = PKCS #7 content type +DViewSignature.jtfSerialNumberDec.tooltip = Signer's issuer certificate serial number assigned by issuer (decimal format) +DViewSignature.jtfSerialNumberHex.tooltip = Signer's issuer certificate serial number assigned by issuer (hexadecimal format) +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.jtfVersion.tooltip = Signer's version number + PasswordCallbackHandler.Title = PIN Login RevokedCertsTableHeadRend.RevocationDateColumn.tooltip = Revocation date/time of revoked certificate From eb73723d71f384ba237457cf96c099efd731b90e Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:18:44 -0800 Subject: [PATCH 08/69] Cleaned up view signature dialog and resources. Displayed subject name. --- .../org/kse/gui/dialogs/DViewSignature.java | 74 +++++++++---------- .../kse/gui/dialogs/SignerListCellRend.java | 34 ++++++--- .../org/kse/gui/actions/resources.properties | 1 + .../org/kse/gui/dialogs/resources.properties | 12 ++- 4 files changed, 63 insertions(+), 58 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 bd4d0192a..aa0a0412a 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -39,6 +39,7 @@ import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -76,6 +77,8 @@ 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.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.util.encoders.Hex; @@ -127,12 +130,10 @@ public class DViewSignature extends JEscDialog { private JScrollPane jspSigners; private JLabel jlVersion; private JTextField jtfVersion; - private JLabel jlSignerDN; - private JDistinguishedName jdnSignerDN; - private JLabel jlSerialNumberHex; - private JTextField jtfSerialNumberHex; - private JLabel jlSerialNumberDec; - private JTextField jtfSerialNumberDec; + private JLabel jlSubject; + private JDistinguishedName jdnSubject; + private JLabel jlIssuer; + private JDistinguishedName jdnIssuer; private JLabel jlSigningTime; private JTextField jtfSigningTime; private JLabel jlSignatureAlgorithm; @@ -176,7 +177,7 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { // jlbSigners.setRowHeight(Math.max(18, jlbSigners.getRowHeight())); jlbSigners.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ToolTipManager.sharedInstance().registerComponent(jlbSigners); - jlbSigners.setCellRenderer(new SignerListCellRend()); + jlbSigners.setCellRenderer(new SignerListCellRend(signedData)); jspSigners = PlatformUtil.createScrollPane(jlbSigners, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); @@ -188,24 +189,15 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jtfVersion.setEditable(false); jtfVersion.setToolTipText(res.getString("DViewSignature.jtfVersion.tooltip")); - jlSignerDN = new JLabel(res.getString("DViewSignature.jlSignerDN.text")); + jlSubject = new JLabel(res.getString("DViewSignature.jlSubject.text")); - jdnSignerDN = new JDistinguishedName(res.getString("DViewSignature.SignerDN.Title"), 40, false); - jdnSignerDN.setToolTipText(res.getString("DViewSignature.jdnSignerDN.tooltip")); + jdnSubject = new JDistinguishedName(res.getString("DViewSignature.Subject.Title"), 40, false); + jdnSubject.setToolTipText(res.getString("DViewSignature.jdnSubject.tooltip")); - jlSerialNumberHex = new JLabel(res.getString("DViewSignature.jlSerialNumberHex.text")); + jlIssuer = new JLabel(res.getString("DViewSignature.jlIssuer.text")); - jtfSerialNumberHex = new JTextField(40); - jtfSerialNumberHex.setEditable(false); - jtfSerialNumberHex.setToolTipText(res.getString("DViewSignature.jtfSerialNumberHex.tooltip")); - jtfSerialNumberHex.setCaretPosition(0); - - jlSerialNumberDec = new JLabel(res.getString("DViewSignature.jlSerialNumberDec.text")); - - jtfSerialNumberDec = new JTextField(40); - jtfSerialNumberDec.setEditable(false); - jtfSerialNumberDec.setToolTipText(res.getString("DViewSignature.jtfSerialNumberDec.tooltip")); - jtfSerialNumberDec.setCaretPosition(0); + jdnIssuer = new JDistinguishedName(res.getString("DViewSignature.Issuer.Title"), 40, false); + jdnIssuer.setToolTipText(res.getString("DViewSignature.jdnIssuer.tooltip")); jlSigningTime = new JLabel(res.getString("DViewSignature.jlSigningTime.text")); @@ -251,12 +243,10 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { pane.add(jspSigners, "sgx, wrap"); pane.add(jlVersion, ""); pane.add(jtfVersion, "sgx, wrap"); - pane.add(jlSignerDN, ""); - pane.add(jdnSignerDN, "wrap"); - pane.add(jlSerialNumberHex, ""); - pane.add(jtfSerialNumberHex, "wrap"); - pane.add(jlSerialNumberDec, ""); - pane.add(jtfSerialNumberDec, "wrap"); + pane.add(jlSubject, ""); + pane.add(jdnSubject, "wrap"); + pane.add(jlIssuer, ""); + pane.add(jdnIssuer, "wrap"); pane.add(jlSigningTime, ""); pane.add(jtfSigningTime, "wrap"); pane.add(jlSignatureAlgorithm, ""); @@ -343,23 +333,29 @@ private SignerInformation getSelectedSignerInfo() { private void populateDetails() { SignerInformation signerInfo = getSelectedSignerInfo(); + X509CertificateHolder cert = null; + Collection matchedCerts = signedData.getCertificates().getMatches(signerInfo.getSID()); + if (!matchedCerts.isEmpty()) { + cert = matchedCerts.iterator().next(); + } if (signerInfo == null) { - jdnSignerDN.setEnabled(false); + jdnSubject.setEnabled(false); + jdnIssuer.setEnabled(false); // jbExtensions.setEnabled(false); jbPem.setEnabled(false); jbAsn1.setEnabled(false); jtfVersion.setText(""); - jdnSignerDN.setDistinguishedName(null); - jtfSerialNumberHex.setText(""); - jtfSerialNumberDec.setText(""); + jdnSubject.setDistinguishedName(null); + jdnIssuer.setDistinguishedName(null); jtfSigningTime.setText(""); jtfSignatureAlgorithm.setText(""); jtfContentType.setText(""); jtfContentDigest.setText(""); } else { - jdnSignerDN.setEnabled(true); + jdnSubject.setEnabled(true); + jdnIssuer.setEnabled(true); // jbExtensions.setEnabled(true); jbPem.setEnabled(true); jbAsn1.setEnabled(true); @@ -395,15 +391,9 @@ private void populateDetails() { jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); - jdnSignerDN.setDistinguishedName(signerInfo.getSID().getIssuer()); - - // TODO JW - Make the HexUtil call generic? It was copied from X509CertUtil. - jtfSerialNumberHex.setText(HexUtil.getHexString(signerInfo.getSID().getSerialNumber(), "0x", 0, 0)); - jtfSerialNumberHex.setCaretPosition(0); + jdnSubject.setDistinguishedName(cert.getSubject()); - // TODO JW - Make the BigInteger call generic? It was copied from X509CertUtil. - jtfSerialNumberDec.setText(new BigInteger(1, signerInfo.getSID().getSerialNumber().toByteArray()).toString(10)); - jtfSerialNumberDec.setCaretPosition(0); + jdnIssuer.setDistinguishedName(cert.getIssuer()); jtfSigningTime.setText(StringUtils.formatDate(signingTime)); @@ -426,8 +416,10 @@ private void populateDetails() { CONTENT_TYPES.put(PKCSObjectIdentifiers.digestedData, "Digested Data"); CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data"); jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType())); + jtfContentType.setCaretPosition(0); jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':')); + jtfContentDigest.setCaretPosition(0); // jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(signerInfo)); // jtfSignatureAlgorithm.setCaretPosition(0); 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 df6cb0ea6..170672554 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java +++ b/kse/src/main/java/org/kse/gui/dialogs/SignerListCellRend.java @@ -20,12 +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; @@ -35,11 +38,11 @@ */ public class SignerListCellRend extends DefaultListCellRenderer { private static final long serialVersionUID = 1L; -// private CMSSignedData signedData; -// -// public SignerListCellRend(CMSSignedData signedData) { -// this.signedData = signedData; -// } + private CMSSignedData signedData; + + public SignerListCellRend(CMSSignedData signedData) { + this.signedData = signedData; + } /** * Returns the rendered cell for the supplied value. @@ -58,17 +61,28 @@ public Component getListCellRendererComponent(JList list, Object value, int i if (value instanceof SignerInformation) { SignerInformation signer = (SignerInformation) value; - X500Name issuer = signer.getSID().getIssuer(); + // 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(issuer); + String shortName = X500NameUtils.extractCN(subject); if (StringUtils.isBlank(shortName)) { - shortName = issuer.toString(); + shortName = subject.toString(); } // subject DN can be empty in some cases if (StringUtils.isBlank(shortName)) { - shortName = signer.getSID().getSerialNumber().toString(); + shortName = cert.getSerialNumber().toString(); } cell.setText(shortName); @@ -77,7 +91,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(issuer.toString()); + cell.setToolTipText(subject.toString()); } return cell; 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 5a9a0a363..b697f6e6f 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -679,6 +679,7 @@ VerifyCertificateAction.unauthorized.message = Request unauthorized VerifyCertificateAction.unknownStatus.message = Unknown status {0} #VerifySignatureAction.ChainSuccessful.message = CHAIN check successful, certificate valid +VerifySignatureAction.SignatureDetailsFile.Title = Signature Details for File ''{0}'' VerifySignatureAction.ChooseSignature.Title = Choose Signature File VerifySignatureAction.ChooseSignature.button = Verify #VerifySignatureAction.CrlSuccessful.message = CRL check successful, certificate 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 689dd0c1d..bbf3cf471 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -586,7 +586,7 @@ 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.SignerDN.Title = Signer +DViewSignature.Subject.Title = Subject DViewSignature.jbAsn1.mnemonic = A DViewSignature.jbAsn1.text = ASN.1 DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signature @@ -597,20 +597,18 @@ DViewSignature.jbOK.text = OK DViewSignature.jbPem.mnemonic = P DViewSignature.jbPem.text = PEM DViewSignature.jbPem.tooltip = Display signature as PEM -DViewSignature.jdnSignerDN.tooltip = Signer's issuer certificate distinguished name +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: -DViewSignature.jlSignerDN.text = Signer: -DViewSignature.jlSerialNumberDec.text = Serial Number (dec.): -DViewSignature.jlSerialNumberHex.text = Serial Number (hex.): DViewSignature.jlSignatureAlgorithm.text = Signature Algorithm: DViewSignature.jlSigningTime.text = Signing Time: +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.jtfSerialNumberDec.tooltip = Signer's issuer certificate serial number assigned by issuer (decimal format) -DViewSignature.jtfSerialNumberHex.tooltip = Signer's issuer certificate serial number assigned by issuer (hexadecimal format) 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 From ce1f38249a50779855e865f271929d981ba266d3 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Tue, 19 Nov 2024 11:43:26 -0800 Subject: [PATCH 09/69] Added view certificates button. --- .../org/kse/crypto/x509/X509CertUtil.java | 41 +++++++++++++ .../org/kse/gui/dialogs/DViewSignature.java | 58 +++++++++++++++++-- .../org/kse/gui/dialogs/resources.properties | 4 ++ 3 files changed, 97 insertions(+), 6 deletions(-) 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 aa48f07ff..e62acdf38 100644 --- a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java +++ b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java @@ -277,6 +277,47 @@ public static X509Certificate convertCertificate(Certificate certIn) throws Cryp } } + /** + * Convert the supplied array of X509CertificateHolder objects into X509Certificate + * objects. + * + * @param certs The X509CertificateHolder objects + * @return The converted X509Certificate objects + * @throws CryptoException A problem occurred during the conversion + */ + public static List convertCertificateHolders(Collection certs) + throws CryptoException { + + ArrayList convertedCerts = new ArrayList<>(); + + if (certs == null) { + return convertedCerts; + } + + for (X509CertificateHolder cert : certs) { + convertedCerts.add(convertCertificate(cert)); + } + + return convertedCerts; + } + + /** + * Convert the supplied X509CertificateHolder object into an X509Certificate object. + * + * @param certIn The X509CertificateHolder object + * @return The converted X509Certificate object + * @throws CryptoException A problem occurred during the conversion + */ + public static X509Certificate convertCertificate(X509CertificateHolder certIn) throws CryptoException { + try { + CertificateFactory cf = CertificateFactory.getInstance(X509_CERT_TYPE, KSE.BC); + ByteArrayInputStream bais = new ByteArrayInputStream(certIn.getEncoded()); + return (X509Certificate) cf.generateCertificate(bais); + } catch (CertificateException | IOException e) { + throw new CryptoException(res.getString("NoConvertCertificate.exception.message"), e); + } + } + /** * Order the supplied array of X.509 certificates in issued to issuer order. * 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 aa0a0412a..97eb1808d 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -32,6 +32,7 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.ECPublicKey; import java.text.MessageFormat; @@ -79,8 +80,10 @@ 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.CMSSignedData; import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.util.Selector; import org.bouncycastle.util.encoders.Hex; import org.kse.KSE; import org.kse.crypto.CryptoException; @@ -138,13 +141,12 @@ public class DViewSignature extends JEscDialog { private JTextField jtfSigningTime; private JLabel jlSignatureAlgorithm; private JTextField jtfSignatureAlgorithm; - // TODO JW - Add content type private JLabel jlContentType; private JTextField jtfContentType; private JLabel jlContentDigest; private JTextField jtfContentDigest; - // TODO JW - Add content digest // TODO JW - Convert extensions into dialog for displaying the signed/unsigned attributes + private JButton jbCertificates; private JButton jbExtensions; private JButton jbPem; private JButton jbAsn1; @@ -223,6 +225,10 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { 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)); + // 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)); @@ -255,8 +261,8 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { pane.add(jtfContentType, "wrap"); pane.add(jlContentDigest, ""); pane.add(jtfContentDigest, "wrap"); - // TODO JW - buttons are not aligned correctly. -// pane.add(jbExtensions, ""); + pane.add(jbCertificates, "spanx, split"); +// pane.add(jbExtensions, ""); pane.add(jbPem, ""); pane.add(jbAsn1, "wrap"); pane.add(new JSeparator(), "spanx, growx, wrap 15:push"); @@ -273,6 +279,15 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jbOK.addActionListener(evt -> okPressed()); + jbCertificates.addActionListener(evt -> { + try { + CursorUtil.setCursorBusy(DViewSignature.this); + certificatesPressed(); + } finally { + CursorUtil.setCursorFree(DViewSignature.this); + } + }); + // jbExtensions.addActionListener(evt -> { // try { // CursorUtil.setCursorBusy(DViewSignature.this); @@ -439,6 +454,37 @@ private void populateDetails() { } } + 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())); +// JcaX509CertificateConverter converter = new JcaX509CertificateConverter(); +// for (X509CertificateHolder certHolder : signedData.getCertificates() +// .getMatches(new SelectAll())) { +// certs.add(converter.getCertificate(certHolder)); +// } + DViewCertificate dViewCertificates = new DViewCertificate(this, + res.getString("DViewSignature.Certificates.Title"), certs.toArray(X509Certificate[]::new), kseFrame, + DViewCertificate.NONE); + dViewCertificates.setLocationRelativeTo(this); + dViewCertificates.setVisible(true); + } catch (CryptoException e) { + DError.displayError(this, e); + } + } + // private void extensionsPressed() { // X509Certificate cert = getSelectedSignerInfo(); // @@ -449,7 +495,7 @@ private void populateDetails() { // } // // private void pemEncodingPressed() { -// TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 strusture) +// TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 structure) // X509Certificate cert = getSelectedSignerInfo(); // // try { @@ -462,7 +508,7 @@ private void populateDetails() { // } // // private void asn1DumpPressed() { -// TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 strusture) +// TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 structure) // X509Certificate cert = getSelectedSignerInfo(); // // try { 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 bbf3cf471..ea683fea8 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -582,6 +582,7 @@ DViewSecretKey.jtfFormat.tooltip = Secret key's encoded format DViewSecretKey.jtfKeySize.text = {0} bits DViewSecretKey.jtfKeySize.tooltip = Secret key's size +DViewSignature.Certificates.Title = Signature Certificates DViewSignature.Extensions.Title = Certificate Extensions DViewSignature.Issuer.Title = Issuer DViewSignature.NoGetEncodedCert.exception.message = Could not get the encoded form of the certificate. @@ -590,6 +591,9 @@ DViewSignature.Subject.Title = Subject DViewSignature.jbAsn1.mnemonic = A DViewSignature.jbAsn1.text = ASN.1 DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signature +DViewSignature.jbCertificates.mnemonic = C +DViewSignature.jbCertificates.text = Certificates +DViewSignature.jbCertificates.tooltip = Display the signature's certificates #DViewSignature.jbExtensions.mnemonic = E #DViewSignature.jbExtensions.text = Extensions #DViewSignature.jbExtensions.tooltip = Display the certificate's extensions From 806d0f6d992a551bfaf7ba3f179fb88f52c84f36 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:21:01 -0800 Subject: [PATCH 10/69] Added counter signers button. --- .../gui/actions/VerifySignatureAction.java | 24 +++--- .../org/kse/gui/dialogs/DViewSignature.java | 75 ++++++++++++++----- .../org/kse/gui/dialogs/resources.properties | 6 +- 3 files changed, 75 insertions(+), 30 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 336eb22b1..ffde73aae 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -207,13 +207,20 @@ protected void doAction() { // printStore(certStore); // SignerInformationStore signers = signedData.getSignerInfos(); // for (SignerInformation signer : signers.getSigners()) { -// signer.getCounterSignatures(); // AttributeTable signedAttributes = signer.getSignedAttributes(); // AttributeTable unsignedAttributes = signer.getUnsignedAttributes(); // -// Hashtable ht = signedAttributes.toHashtable(); -// for (Object k : ht.keySet()) { -// System.out.println(k + " :: " + ((Attribute) ht.get(k)).getAttrValues()); +// 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()); +// } // } // // Collection matchedCerts = certStore.getMatches(signer.getSID()); @@ -225,11 +232,10 @@ protected void doAction() { // } // } // System.out.println("Verified: " + verified); - // TODO JW - Display signature details dialog with option to see signature details. - DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat.format( - // TODO JW - view signature resource string - res.getString("ImportTrustedCertificateAction.CertDetailsFile.Title"), - signatureFile.getName()), signedData, null, DViewSignature.NONE); + + DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat + .format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()), + signedData, signedData.getSignerInfos().getSigners(), null); dViewSignature.setLocationRelativeTo(frame); dViewSignature.setVisible(true); 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 97eb1808d..3a8de7de3 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -40,6 +40,7 @@ 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; @@ -147,12 +148,14 @@ 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 jbCounterSigners; private JButton jbExtensions; private JButton jbPem; private JButton jbAsn1; private JButton jbOK; private CMSSignedData signedData; + private Collection signerInfos; /** * Creates a new DViewCertificate dialog. @@ -160,21 +163,23 @@ public class DViewSignature extends JEscDialog { * @param parent Parent frame * @param title The dialog title * @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, KseFrame kseFrame) - throws CryptoException { + public DViewSignature(Window parent, String title, CMSSignedData signedData, Collection signers, + KseFrame kseFrame) throws CryptoException { super(parent, title, Dialog.ModalityType.MODELESS); this.kseFrame = kseFrame; this.signedData = signedData; - initComponents(signedData); + this.signerInfos = signers; + initComponents(signers); } - private void initComponents(CMSSignedData signedData) throws CryptoException { + private void initComponents(Collection signers) throws CryptoException { jlSigners = new JLabel(res.getString("DViewSignature.jlSigners.text")); - jlbSigners = new JList<>(createSignerList(signedData)); + jlbSigners = new JList<>(createSignerList(signers)); // TODO JW - Signer list row height? // jlbSigners.setRowHeight(Math.max(18, jlbSigners.getRowHeight())); jlbSigners.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -229,6 +234,11 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { jbCertificates.setToolTipText(res.getString("DViewSignature.jbCertificates.tooltip")); PlatformUtil.setMnemonic(jbCertificates, res.getString("DViewSignature.jbCertificates.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)); + // 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)); @@ -262,6 +272,7 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { pane.add(jlContentDigest, ""); pane.add(jtfContentDigest, "wrap"); pane.add(jbCertificates, "spanx, split"); + pane.add(jbCounterSigners, ""); // pane.add(jbExtensions, ""); pane.add(jbPem, ""); pane.add(jbAsn1, "wrap"); @@ -288,6 +299,15 @@ private void initComponents(CMSSignedData signedData) throws CryptoException { } }); + jbCounterSigners.addActionListener(evt -> { + try { + CursorUtil.setCursorBusy(DViewSignature.this); + counterSignersPressed(); + } finally { + CursorUtil.setCursorFree(DViewSignature.this); + } + }); + // jbExtensions.addActionListener(evt -> { // try { // CursorUtil.setCursorBusy(DViewSignature.this); @@ -334,10 +354,10 @@ public void windowClosing(WindowEvent evt) { SwingUtilities.invokeLater(() -> jbOK.requestFocus()); } - private ListModel createSignerList(CMSSignedData signedData) { + private ListModel createSignerList(Collection signers) { DefaultListModel signerList = new DefaultListModel<>(); - signerList.addAll(signedData.getSignerInfos().getSigners()); + signerList.addAll(signers); return signerList; } @@ -376,7 +396,6 @@ private void populateDetails() { jbAsn1.setEnabled(true); // try { - Date currentDate = new Date(); Date signingTime = null; // TODO JW - Make the signing time extraction logic a utility method. @@ -399,10 +418,6 @@ private void populateDetails() { } - // TODO JW - Need to determine valid date? - boolean noLongerValid = false; -// boolean noLongerValid = currentDate.after(endDate); - jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); @@ -412,14 +427,14 @@ private void populateDetails() { jtfSigningTime.setText(StringUtils.formatDate(signingTime)); - if (noLongerValid) { - jtfSigningTime.setText( - MessageFormat.format(res.getString("DViewCertificate.jtfSigningTime.expired.text"), - jtfSigningTime.getText())); - jtfSigningTime.setForeground(Color.red); - } else { - jtfSigningTime.setForeground(jtfVersion.getForeground()); - } +// if (noLongerValid) { +// jtfSigningTime.setText( +// MessageFormat.format(res.getString("DViewCertificate.jtfSigningTime.expired.text"), +// jtfSigningTime.getText())); +// jtfSigningTime.setForeground(Color.red); +// } else { +// jtfSigningTime.setForeground(jtfVersion.getForeground()); +// } jtfSigningTime.setCaretPosition(0); // TODO JW - These map strings need to be moved to a resource bundle. @@ -439,6 +454,12 @@ private void populateDetails() { // jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(signerInfo)); // jtfSignatureAlgorithm.setCaretPosition(0); + if (signerInfo.getCounterSignatures().size() > 0) { + jbCounterSigners.setEnabled(true); + } else { + jbCounterSigners.setEnabled(false); + } + // Set critExts = signerInfo.getCriticalExtensionOIDs(); // Set nonCritExts = signerInfo.getNonCriticalExtensionOIDs(); // @@ -485,6 +506,20 @@ private void certificatesPressed() { } } + private void counterSignersPressed() { + SignerInformation signer = getSelectedSignerInfo(); + + try { + DViewSignature dViewSignature = new DViewSignature(this, + res.getString("DViewSignature.CounterSigners.Title"), signedData, + signer.getCounterSignatures().getSigners(), null); + dViewSignature.setLocationRelativeTo(this); + dViewSignature.setVisible(true); + } catch (CryptoException e) { + DError.displayError(this, e); + } + } + // private void extensionsPressed() { // X509Certificate cert = 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 ea683fea8..39f884785 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -582,7 +582,8 @@ DViewSecretKey.jtfFormat.tooltip = Secret key's encoded format DViewSecretKey.jtfKeySize.text = {0} bits DViewSecretKey.jtfKeySize.tooltip = Secret key's size -DViewSignature.Certificates.Title = Signature Certificates +DViewSignature.Certificates.Title = Signature Certificates +DViewSignature.CounterSigners.Title = Counter Signers DViewSignature.Extensions.Title = Certificate Extensions DViewSignature.Issuer.Title = Issuer DViewSignature.NoGetEncodedCert.exception.message = Could not get the encoded form of the certificate. @@ -594,6 +595,9 @@ DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signa DViewSignature.jbCertificates.mnemonic = C DViewSignature.jbCertificates.text = Certificates DViewSignature.jbCertificates.tooltip = Display the signature's certificates +DViewSignature.jbCounterSigners.mnemonic = S +DViewSignature.jbCounterSigners.text = Counter Signers +DViewSignature.jbCounterSigners.tooltip = Display the signature's counter signers #DViewSignature.jbExtensions.mnemonic = E #DViewSignature.jbExtensions.text = Extensions #DViewSignature.jbExtensions.tooltip = Display the certificate's extensions From 0499a5586cb9706e51bf336ab36b5ba2c5ebcdb7 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:22:01 -0800 Subject: [PATCH 11/69] Fixed warning about PEMParser not being closed. --- kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 e62acdf38..bff92edde 100644 --- a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java +++ b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java @@ -159,12 +159,11 @@ private static X509Certificate[] loadCertificatesPkiPath(InputStream is) throws private static List loadAsPEM(byte[] bytes, CertificateFactory cf) { - PEMParser pemParser = new PEMParser(new StringReader(new String(bytes))); JcaX509CertificateConverter jcaX509CertConverter = new JcaX509CertificateConverter(); List certs = new ArrayList<>(); - try { + try (PEMParser pemParser = new PEMParser(new StringReader(new String(bytes)))) { Object pemObject = pemParser.readObject(); while (pemObject != null) { // check for all possible certificate classes From ec9b50fc7e324cbc3e2a07c0c06e9618570ab2de Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 20 Nov 2024 23:30:21 -0800 Subject: [PATCH 12/69] Fixed potential NPE when a signer is not selected. --- .../java/org/kse/gui/dialogs/DViewSignature.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 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 3a8de7de3..986b13170 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -368,11 +368,6 @@ private SignerInformation getSelectedSignerInfo() { private void populateDetails() { SignerInformation signerInfo = getSelectedSignerInfo(); - X509CertificateHolder cert = null; - Collection matchedCerts = signedData.getCertificates().getMatches(signerInfo.getSID()); - if (!matchedCerts.isEmpty()) { - cert = matchedCerts.iterator().next(); - } if (signerInfo == null) { jdnSubject.setEnabled(false); @@ -395,6 +390,14 @@ private void populateDetails() { jbPem.setEnabled(true); jbAsn1.setEnabled(true); + X509CertificateHolder cert = null; + @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector + Collection matchedCerts = signedData.getCertificates() + .getMatches(signerInfo.getSID()); + if (!matchedCerts.isEmpty()) { + cert = matchedCerts.iterator().next(); + } + // try { Date signingTime = null; From 8c7a12b5a9c4ca637389dd6373658bbbffdf4947 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sat, 23 Nov 2024 23:38:30 -0800 Subject: [PATCH 13/69] Populated signature algorithm. Verified the signature. --- .../org/kse/gui/dialogs/DViewSignature.java | 70 ++++++++++++++----- 1 file changed, 51 insertions(+), 19 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 986b13170..7e8b85bea 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -82,13 +82,17 @@ 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.util.Selector; 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.SignatureType; import org.kse.crypto.x509.X500NameUtils; @@ -390,17 +394,34 @@ private void populateDetails() { jbPem.setEnabled(true); jbAsn1.setEnabled(true); - X509CertificateHolder cert = null; - @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector - Collection matchedCerts = signedData.getCertificates() - .getMatches(signerInfo.getSID()); - if (!matchedCerts.isEmpty()) { - cert = matchedCerts.iterator().next(); - } - -// try { + try { Date signingTime = null; - + X509Certificate cert = null; + + @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector + Collection matchedCerts = signedData.getCertificates() + .getMatches(signerInfo.getSID()); + // TODO JW - What to do when there isn't a matched certificate? + // A matched certificate is needed for content digest + if (!matchedCerts.isEmpty()) { + X509CertificateHolder certHolder = matchedCerts.iterator().next(); + try { + // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain. + signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certHolder)); + } catch (OperatorCreationException e) { + // TODO JW Auto-generated catch block + e.printStackTrace(); + } catch (CMSException e) { + // TODO JW Auto-generated catch block + e.printStackTrace(); + } catch (CertificateException e) { + // TODO JW Auto-generated catch block + e.printStackTrace(); + } + cert = X509CertUtil.convertCertificate(certHolder); + } + // TODO JW - Need to check for null certificate + // TODO JW - Make the signing time extraction logic a utility method. Attribute signingTimeAttribute = signerInfo.getSignedAttributes().get(CMSAttributes.signingTime); if (signingTimeAttribute != null) { @@ -424,9 +445,9 @@ private void populateDetails() { jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); - jdnSubject.setDistinguishedName(cert.getSubject()); + jdnSubject.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getSubjectX500Principal())); - jdnIssuer.setDistinguishedName(cert.getIssuer()); + jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal())); jtfSigningTime.setText(StringUtils.formatDate(signingTime)); @@ -450,12 +471,23 @@ private void populateDetails() { CONTENT_TYPES.put(PKCSObjectIdentifiers.encryptedData, "Encrypted Data"); jtfContentType.setText(CONTENT_TYPES.get(signerInfo.getContentType())); jtfContentType.setCaretPosition(0); - + jtfContentDigest.setText(HexUtil.getHexStringWithSep(signerInfo.getContentDigest(), ':')); jtfContentDigest.setCaretPosition(0); -// jtfSignatureAlgorithm.setText(X509CertUtil.getCertificateSignatureAlgorithm(signerInfo)); -// jtfSignatureAlgorithm.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); + if (signatureType != null ) { + jtfSignatureAlgorithm.setText(signatureType.friendly()); + jtfSignatureAlgorithm.setCaretPosition(0); + } if (signerInfo.getCounterSignatures().size() > 0) { jbCounterSigners.setEnabled(true); @@ -471,10 +503,10 @@ private void populateDetails() { // } else { // jbExtensions.setEnabled(false); // } -// } catch (CryptoException e) { -// DError.displayError(this, e); -// dispose(); -// } + } catch (CryptoException e) { + DError.displayError(this, e); + dispose(); + } } } From 2694b659b91f4cb1af9f949223000ab7ccf154b2 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:08:12 -0800 Subject: [PATCH 14/69] Signed attribute table can be null. --- .../org/kse/gui/dialogs/DViewSignature.java | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 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 7e8b85bea..385a434a3 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -76,6 +76,7 @@ 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; @@ -408,13 +409,7 @@ private void populateDetails() { try { // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain. signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certHolder)); - } catch (OperatorCreationException e) { - // TODO JW Auto-generated catch block - e.printStackTrace(); - } catch (CMSException e) { - // TODO JW Auto-generated catch block - e.printStackTrace(); - } catch (CertificateException e) { + } catch (OperatorCreationException | CMSException | CertificateException e) { // TODO JW Auto-generated catch block e.printStackTrace(); } @@ -423,24 +418,26 @@ private void populateDetails() { // TODO JW - Need to check for null certificate // TODO JW - Make the signing time extraction logic a utility method. - Attribute signingTimeAttribute = signerInfo.getSignedAttributes().get(CMSAttributes.signingTime); - if (signingTimeAttribute != null) { - Enumeration e = signingTimeAttribute.getAttrValues().getObjects(); - if (e.hasMoreElements()) { - Object o = e.nextElement(); - try { - if (o instanceof ASN1UTCTime) { - signingTime = ((ASN1UTCTime) o).getAdjustedDate(); - } else if (o instanceof ASN1GeneralizedTime) { - signingTime = ((ASN1GeneralizedTime) o).getDate(); + AttributeTable signedAttributes = signerInfo.getSignedAttributes(); + if (signedAttributes != null) { + Attribute signingTimeAttribute = signedAttributes.get(CMSAttributes.signingTime); + if (signingTimeAttribute != null) { + Enumeration e = signingTimeAttribute.getAttrValues().getObjects(); + if (e.hasMoreElements()) { + Object o = e.nextElement(); + try { + if (o instanceof ASN1UTCTime) { + signingTime = ((ASN1UTCTime) o).getAdjustedDate(); + } else if (o instanceof ASN1GeneralizedTime) { + signingTime = ((ASN1GeneralizedTime) o).getDate(); + } + } catch (ParseException e1) { + // TODO JW Auto-generated catch block + e1.printStackTrace(); } - } catch (ParseException e1) { - // TODO JW Auto-generated catch block - e1.printStackTrace(); } } } - jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); @@ -449,7 +446,10 @@ private void populateDetails() { jdnIssuer.setDistinguishedName(X500NameUtils.x500PrincipalToX500Name(cert.getIssuerX500Principal())); - jtfSigningTime.setText(StringUtils.formatDate(signingTime)); + // TODO JW - What to do if the signature doesn't have a signing time? + if (signingTime != null) { + jtfSigningTime.setText(StringUtils.formatDate(signingTime)); + } // if (noLongerValid) { // jtfSigningTime.setText( @@ -565,7 +565,7 @@ private void counterSignersPressed() { // } // // private void pemEncodingPressed() { -// TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 structure) +// // TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 structure) // X509Certificate cert = getSelectedSignerInfo(); // // try { @@ -578,7 +578,7 @@ private void counterSignersPressed() { // } // // private void asn1DumpPressed() { -// TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 structure) +// // TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 structure) // X509Certificate cert = getSelectedSignerInfo(); // // try { From 3bc633a36b66798e9adb5a1e4e649c367cec3e18 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:09:45 -0800 Subject: [PATCH 15/69] Handle PEM encoded signatures. --- .../kse/gui/actions/VerifySignatureAction.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 ffde73aae..377b9895e 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -102,6 +102,8 @@ 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; @@ -197,6 +199,19 @@ protected void doAction() { } // TODO JW - Verify the signature using the keystore + 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 Exception("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 Exception("PEM is not of type CMS or PKCS7"); + } + signature = signaturePem.getContent(); + } CMSProcessable data = new CMSProcessableFile(contentFile); CMSSignedData signedData = new CMSSignedData(data, signature); From 85273e2761be583fc27a5b8442af9be2750fa2c5 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:32:39 -0800 Subject: [PATCH 16/69] Hooked up the PEM button. --- .../java/org/kse/crypto/signing/CmsUtil.java | 52 +++++++++++++++++++ .../java/org/kse/gui/dialogs/DViewPem.java | 19 +++++++ .../org/kse/gui/dialogs/DViewSignature.java | 45 ++++++++-------- 3 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 kse/src/main/java/org/kse/crypto/signing/CmsUtil.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 new file mode 100644 index 000000000..5e04c90a9 --- /dev/null +++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java @@ -0,0 +1,52 @@ +/* + * 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 org.bouncycastle.cms.CMSSignedData; +import org.kse.crypto.CryptoException; +import org.kse.utilities.pem.PemInfo; +import org.kse.utilities.pem.PemUtil; + +public class CmsUtil { + private static final String CMS_PEM_TYPE = "CMS"; + private static final String PKCS7_PEM_TYPE = "PKCS7"; + + private CmsUtil() { + } + + /** + * PEM encode a CMS signature. + * + * @param cms The CMS signature + * @return The PEM'd encoding + */ + public static String getPem(CMSSignedData cms) throws CryptoException { + // Use PKCS7 PEM header since it can be verified using GnuTLS certtool and OpenSSL. + 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); + } + } +} 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 4364edcd2..dfba6760c 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewPem.java @@ -53,6 +53,7 @@ import org.apache.commons.io.IOUtils; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; @@ -61,6 +62,7 @@ import org.kse.crypto.csr.pkcs10.Pkcs10Util; import org.kse.crypto.privatekey.Pkcs8Util; import org.kse.crypto.publickey.OpenSslPubUtil; +import org.kse.crypto.signing.CmsUtil; import org.kse.crypto.x509.X509CertUtil; import org.kse.gui.CurrentDirectory; import org.kse.gui.CursorUtil; @@ -93,6 +95,7 @@ public class DViewPem extends JEscDialog { private PKCS10CertificationRequest pkcs10Csr; private PrivateKey privKey; private PublicKey pubKey; + private CMSSignedData cms; /** * Creates a new DViewPem dialog. @@ -200,6 +203,20 @@ public DViewPem(JDialog parent, String title, PublicKey publicKey) throws Crypto initComponents(); } + /** + * Creates new DViewPem dialog where the parent is a dialog. + * + * @param parent Parent dialog + * @param title The dialog title + * @param cms CMS data to display encoding for + * @throws CryptoException + */ + public DViewPem(JDialog parent, String title, CMSSignedData cms) throws CryptoException { + super(parent, title, ModalityType.DOCUMENT_MODAL); + this.cms = cms; + initComponents(); + } + private void initComponents() throws CryptoException { jbOK = new JButton(res.getString("DViewPem.jbOK.text")); @@ -277,6 +294,8 @@ private String getPemString() throws CryptoException { return Pkcs8Util.getPem(privKey); } else if (pubKey != null) { return OpenSslPubUtil.getPem(pubKey); + } else if (cms != null) { + return CmsUtil.getPem(cms); } return ""; 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 385a434a3..87ec3cf7f 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -321,16 +321,16 @@ private void initComponents(Collection signers) throws Crypto // CursorUtil.setCursorFree(DViewSignature.this); // } // }); -// -// jbPem.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// pemEncodingPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); -// + + jbPem.addActionListener(evt -> { + try { + CursorUtil.setCursorBusy(DViewSignature.this); + pemEncodingPressed(); + } finally { + CursorUtil.setCursorFree(DViewSignature.this); + } + }); + // jbAsn1.addActionListener(evt -> { // try { // CursorUtil.setCursorBusy(DViewSignature.this); @@ -563,20 +563,17 @@ private void counterSignersPressed() { // dViewExtensions.setLocationRelativeTo(this); // dViewExtensions.setVisible(true); // } -// -// private void pemEncodingPressed() { -// // TODO JW - PEM encoding needs to be for the entire signature (PKCS #7 structure) -// X509Certificate cert = getSelectedSignerInfo(); -// -// try { -// DViewPem dViewCertPem = new DViewPem(this, res.getString("DViewCertificate.Pem.Title"), cert); -// dViewCertPem.setLocationRelativeTo(this); -// dViewCertPem.setVisible(true); -// } catch (CryptoException e) { -// DError.displayError(this, e); -// } -// } -// + + private void pemEncodingPressed() { + try { + DViewPem dViewCertPem = new DViewPem(this, res.getString("DViewSignature.Pem.Title"), signedData); + dViewCertPem.setLocationRelativeTo(this); + dViewCertPem.setVisible(true); + } catch (CryptoException e) { + DError.displayError(this, e); + } + } + // private void asn1DumpPressed() { // // TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 structure) // X509Certificate cert = getSelectedSignerInfo(); From 4c3e444afb07baf00f73531ad11b5a48b956bd71 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:49:59 -0800 Subject: [PATCH 17/69] Hooked up the ANS1 button. --- .../org/kse/gui/dialogs/DViewAsn1Dump.java | 21 +++++++++- .../org/kse/gui/dialogs/DViewSignature.java | 38 +++++++++---------- .../org/kse/gui/dialogs/resources.properties | 1 + 3 files changed, 39 insertions(+), 21 deletions(-) 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 844700668..ce9c6f546 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.cms.CMSSignedData; import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.kse.crypto.csr.spkac.Spkac; import org.kse.crypto.x509.X509Ext; @@ -55,7 +56,7 @@ /** * Displays an ASN.1 dump of the supplied object: an X.509 certificate, private - * key, public key, CRL or Extension. + * key, public key, CRL, Extension, or CMS. */ public class DViewAsn1Dump extends JEscFrame { private static final long serialVersionUID = 1L; @@ -76,6 +77,7 @@ public class DViewAsn1Dump extends JEscFrame { private PublicKey publicKey; private PKCS10CertificationRequest pkcs10Csr; private Spkac spkac; + private CMSSignedData cms; /** * Creates a new DViewAsn1Dump dialog. @@ -183,6 +185,21 @@ public DViewAsn1Dump(JDialog parent, Spkac spkac) throws Asn1Exception, IOExcept initComponents(); } + /** + * Creates a new DViewAsn1Dump dialog. + * + * @param parent Parent frame + * @param cms CMS signature to display dump for + * @throws Asn1Exception A problem was encountered getting the CMS signature ASN.1 dump + * @throws IOException If an I/O problem occurred + */ + public DViewAsn1Dump(JDialog parent, CMSSignedData cms) throws Asn1Exception, IOException { + super(res.getString("DViewAsn1Dump.Cms.Title")); + this.cms = cms; + this.setIconImages(parent.getOwner().getIconImages()); + initComponents(); + } + private void initComponents() throws Asn1Exception, IOException { jbCopy = new JButton(res.getString("DViewAsn1Dump.jbCopy.text")); @@ -210,6 +227,8 @@ private void initComponents() throws Asn1Exception, IOException { if (certificate != null) { jtaAsn1Dump = new JTextArea(asn1Dump.dump(certificate)); + } else if (cms != null) { + jtaAsn1Dump = new JTextArea(asn1Dump.dump(cms.getEncoded())); } else if (crl != null) { jtaAsn1Dump = new JTextArea(asn1Dump.dump(crl)); } else if (extension != 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 87ec3cf7f..05215f8eb 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -331,14 +331,14 @@ private void initComponents(Collection signers) throws Crypto } }); -// jbAsn1.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// asn1DumpPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); + jbAsn1.addActionListener(evt -> { + try { + CursorUtil.setCursorBusy(DViewSignature.this); + asn1DumpPressed(); + } finally { + CursorUtil.setCursorFree(DViewSignature.this); + } + }); addWindowListener(new WindowAdapter() { @Override @@ -574,18 +574,16 @@ private void pemEncodingPressed() { } } -// private void asn1DumpPressed() { -// // TODO JW - ASN.1 dump needs to be for the entire signature (PKCS #7 structure) -// X509Certificate cert = getSelectedSignerInfo(); -// -// try { -// DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, cert); -// dViewAsn1Dump.setLocationRelativeTo(this); -// dViewAsn1Dump.setVisible(true); -// } catch (Asn1Exception | IOException e) { -// DError.displayError(this, e); -// } -// } + private void asn1DumpPressed() { + // TODO JW - Should this show only the ASN.1 for the selected signer? + try { + DViewAsn1Dump dViewAsn1Dump = new DViewAsn1Dump(this, signedData); + dViewAsn1Dump.setLocationRelativeTo(this); + dViewAsn1Dump.setVisible(true); + } catch (Asn1Exception | IOException e) { + DError.displayError(this, e); + } + } private void okPressed() { // TODO JW - set any preferences here (e.g., chosen fingerprint algorithm) 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 39f884785..a449cc9e1 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -306,6 +306,7 @@ DVerifyCertificate.jtfOcspUrl.tooltip = URL DViewAsn1Dump.Certificate.Title = Certificate ASN.1 Dump DViewAsn1Dump.Crl.Title = CRL ASN.1 Dump +DViewAsn1Dump.Cms.Title = CMS ASN.1 Dump DViewAsn1Dump.Csr.Title = PKCS#10 Request ASN.1 Dump DViewAsn1Dump.Extension.Title = Extension ASN.1 Dump DViewAsn1Dump.PrivateKey.Title = Private Key ASN.1 Dump From b216d6e4aa39e9f453e5d1ac56988f08c3420b93 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:10:21 -0800 Subject: [PATCH 18/69] Refactoring and cleanup. --- .../java/org/kse/crypto/signing/CmsUtil.java | 42 +++++++++++ .../java/org/kse/gui/FileChooserFactory.java | 8 ++- .../gui/actions/VerifySignatureAction.java | 50 ++++--------- .../org/kse/gui/dialogs/DViewSignature.java | 72 ++----------------- .../org/kse/gui/actions/resources.properties | 1 + .../org/kse/gui/resources.properties | 1 + 6 files changed, 70 insertions(+), 104 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 5e04c90a9..a570629e4 100644 --- a/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java +++ b/kse/src/main/java/org/kse/crypto/signing/CmsUtil.java @@ -20,8 +20,17 @@ package org.kse.crypto.signing; import java.io.IOException; +import java.text.ParseException; +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.cms.CMSSignedData; +import org.bouncycastle.cms.SignerInformation; import org.kse.crypto.CryptoException; import org.kse.utilities.pem.PemInfo; import org.kse.utilities.pem.PemUtil; @@ -41,6 +50,7 @@ private CmsUtil() { */ public static String getPem(CMSSignedData cms) throws CryptoException { // 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); @@ -49,4 +59,36 @@ public static String getPem(CMSSignedData cms) throws CryptoException { throw new CryptoException(e); } } + + /** + * 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) { + // TODO JW Auto-generated catch block + throw new CryptoException(e); + } + } + } + } + return signingTime; + } } diff --git a/kse/src/main/java/org/kse/gui/FileChooserFactory.java b/kse/src/main/java/org/kse/gui/FileChooserFactory.java index e26d6f6f0..882678b34 100644 --- a/kse/src/main/java/org/kse/gui/FileChooserFactory.java +++ b/kse/src/main/java/org/kse/gui/FileChooserFactory.java @@ -73,6 +73,8 @@ public class FileChooserFactory { public static final String LIB_DYLIB_EXT = "dylib"; public static final String PEM_EXT = "pem"; public static final String JWK_EXT = "json"; + public static final String CMS_EXT_1 = "p7s"; + public static final String CMS_EXT_2 = "p7m"; public static final String SIG_EXT = "sig"; private static final String KEYSTORE_FILE_DESC = @@ -133,6 +135,9 @@ public class FileChooserFactory { private static final String PEM_FILE_DESC = format(res.getString("FileChooserFactory.PemFiles"), PEM_EXT); + private static final String CMS_FILE_DESC = + format(res.getString("FileChooserFactory.CmsSigFiles"), CMS_EXT_1, CMS_EXT_2); + private static final String SIG_FILE_DESC = format(res.getString("FileChooserFactory.SignatureFiles"), SIG_EXT); @@ -436,7 +441,8 @@ public static JFileChooser getLibFileChooser() { */ public static JFileChooser getSignatureFileChooser() { JFileChooser chooser = getFileChooser(); - chooser.setFileFilter(new FileNameExtensionFilter(SIG_FILE_DESC, SIG_EXT)); + chooser.setFileFilter(new FileNameExtensionFilter(CMS_FILE_DESC, CMS_EXT_1, CMS_EXT_2)); + chooser.addChoosableFileFilter(new FileNameExtensionFilter(SIG_FILE_DESC, SIG_EXT)); return 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 377b9895e..7b464d981 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -217,11 +217,10 @@ protected void doAction() { // TODO 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(); -// printStore(certStore); -// SignerInformationStore signers = signedData.getSignerInfos(); -// for (SignerInformation signer : signers.getSigners()) { + boolean verified = false; + Store certStore = signedData.getCertificates(); + SignerInformationStore signers = signedData.getSignerInfos(); + for (SignerInformation signer : signers.getSigners()) { // AttributeTable signedAttributes = signer.getSignedAttributes(); // AttributeTable unsignedAttributes = signer.getUnsignedAttributes(); // @@ -238,14 +237,16 @@ protected void doAction() { // } // } // -// Collection matchedCerts = certStore.getMatches(signer.getSID()); -// for (X509CertificateHolder cert : matchedCerts) { -// if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { -// System.out.println("Verified by: " + cert.getSubject()); -// verified = true; -// } -// } -// } + @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector + Collection matchedCerts = certStore.getMatches(signer.getSID()); + for (X509CertificateHolder cert : matchedCerts) { + // 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; + } + } + } // System.out.println("Verified: " + verified); DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat @@ -278,26 +279,6 @@ protected void doAction() { } } - private static void printStore(Store store) { - for (T obj : store.getMatches(new Selector() { - @Override - public Object clone() { - return null; - } - - @Override - public boolean match(T obj) { - return true; - } - })) { - if (obj instanceof X509CertificateHolder) { - System.out.println(((X509CertificateHolder) obj).getSubject()); - } else { - System.out.println(obj); - } - } - } - private boolean isCA(X509Certificate cert) { int basicConstraints = cert.getBasicConstraints(); if (basicConstraints != -1) { @@ -383,8 +364,7 @@ protected byte[] openSignature(File signatureFile) { JOptionPane.showMessageDialog(frame, MessageFormat.format( res.getString("KeyStoreExplorerAction.NoReadFile.message"), signatureFile), - // TODO JW - Create a new resource for open signature. - res.getString("KeyStoreExplorerAction.OpenCertificate.Title"), + res.getString("KeyStoreExplorerAction.OpenSignature.Title"), JOptionPane.WARNING_MESSAGE); return 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 05215f8eb..599c62ca6 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -95,6 +95,7 @@ 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.SignatureType; import org.kse.crypto.x509.X500NameUtils; import org.kse.crypto.x509.X509CertUtil; @@ -396,49 +397,17 @@ private void populateDetails() { jbAsn1.setEnabled(true); try { - Date signingTime = null; + Date signingTime = CmsUtil.getSigningTime(signerInfo); X509Certificate cert = null; @SuppressWarnings("unchecked") // SignerId does not specify a type when extending Selector Collection matchedCerts = signedData.getCertificates() .getMatches(signerInfo.getSID()); - // TODO JW - What to do when there isn't a matched certificate? - // A matched certificate is needed for content digest if (!matchedCerts.isEmpty()) { - X509CertificateHolder certHolder = matchedCerts.iterator().next(); - try { - // TODO JW - this verifies using the attached certs. Need to link certs to keystore to validate the chain. - signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().build(certHolder)); - } catch (OperatorCreationException | CMSException | CertificateException e) { - // TODO JW Auto-generated catch block - e.printStackTrace(); - } - cert = X509CertUtil.convertCertificate(certHolder); + cert = X509CertUtil.convertCertificate(matchedCerts.iterator().next()); } // TODO JW - Need to check for null certificate - // TODO JW - Make the signing time extraction logic a utility method. - AttributeTable signedAttributes = signerInfo.getSignedAttributes(); - if (signedAttributes != null) { - Attribute signingTimeAttribute = signedAttributes.get(CMSAttributes.signingTime); - if (signingTimeAttribute != null) { - Enumeration e = signingTimeAttribute.getAttrValues().getObjects(); - if (e.hasMoreElements()) { - Object o = e.nextElement(); - try { - if (o instanceof ASN1UTCTime) { - signingTime = ((ASN1UTCTime) o).getAdjustedDate(); - } else if (o instanceof ASN1GeneralizedTime) { - signingTime = ((ASN1GeneralizedTime) o).getDate(); - } - } catch (ParseException e1) { - // TODO JW Auto-generated catch block - e1.printStackTrace(); - } - } - } - } - jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); @@ -472,6 +441,7 @@ 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); @@ -595,40 +565,6 @@ private void closeDialog() { dispose(); } - private class X509CertificateComparator implements Comparator { - - /* (non-Javadoc) - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - @Override - public int compare(X509Certificate cert1, X509Certificate cert2) { - - // Compare certificates for equality. Where all we care about is if - // the certificates are equal or not - the order is unimportant - if (cert1.equals(cert2)) { - return 0; - } - - // Compare on subject DN - int i = cert1.getSubjectX500Principal().toString().compareTo(cert2.getSubjectX500Principal().toString()); - - if (i != 0) { - return i; - } - - // Compare on issuer DN - i = cert1.getIssuerX500Principal().toString().compareTo(cert2.getIssuerX500Principal().toString()); - - if (i != 0) { - return i; - } - - // If all else fails then compare serial numbers - if this is the - // same and the DNs are the same then it is probably the same certificate anyway - return cert1.getSerialNumber().subtract(cert2.getSerialNumber()).intValue(); - } - } - public static void main(String[] args) throws Exception { DialogViewer.prepare(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", KSE.BC); 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 b697f6e6f..8d351fd20 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -387,6 +387,7 @@ KeyStoreExplorerAction.NoReadFile.message = Could not read from file ' KeyStoreExplorerAction.NoUnlockEntry.Problem = Could not unlock entry ''{0}''. KeyStoreExplorerAction.NotCert.Cause = The file is not in a supported format: X.509, PKCS #7, PKI Path or SPC. KeyStoreExplorerAction.OpenCertificate.Title = Open Certificate +KeyStoreExplorerAction.OpenSignature.Title = Open Signature KeyStoreExplorerAction.PasswordIncorrectEntry.Cause = The supplied password is not correct. KeyStoreExplorerAction.ProblemOpeningCert.Title = Problem Opening Certificate KeyStoreExplorerAction.ProblemUnlockingEntry.Title = Problem Unlocking Entry diff --git a/kse/src/main/resources/org/kse/gui/resources.properties b/kse/src/main/resources/org/kse/gui/resources.properties index f175b510f..416fd7857 100644 --- a/kse/src/main/resources/org/kse/gui/resources.properties +++ b/kse/src/main/resources/org/kse/gui/resources.properties @@ -15,6 +15,7 @@ FileChooserFactory.CaReplyFiles = PKCS #7 CA Reply Files (*.{0}) FileChooserFactory.CertificateFiles = Certificate Files (*.{0};*.{1}) FileChooserFactory.CetFiles = Certificate Extensions Template Files (*.{0}) FileChooserFactory.CrlFiles = Certificate Revocation List Files (*.{0}) +FileChooserFactory.CmsSigFiles = CMS Signature Files (*.{0}, *.{1}) FileChooserFactory.JadFiles = Java Application Descriptor Files (*.{0}) FileChooserFactory.JarFiles = Java Archive Files (*.{0}) FileChooserFactory.KeyStoreFiles = KeyStore Files (*.{0};*.{1};*.{2};*.{3};*.{4};*.{5};*.{6}) From 90fadb288aa299b98038663b11c2491405453eaa Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 27 Nov 2024 00:11:40 -0800 Subject: [PATCH 19/69] Removed embedded CertHolder to X509Certificate conversion loigc. --- kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java | 5 ----- 1 file changed, 5 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 599c62ca6..50912bb3e 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -496,11 +496,6 @@ private void certificatesPressed() { try { List certs = X509CertUtil.convertCertificateHolders( signedData.getCertificates().getMatches(new SelectAll())); -// JcaX509CertificateConverter converter = new JcaX509CertificateConverter(); -// for (X509CertificateHolder certHolder : signedData.getCertificates() -// .getMatches(new SelectAll())) { -// certs.add(converter.getCertificate(certHolder)); -// } DViewCertificate dViewCertificates = new DViewCertificate(this, res.getString("DViewSignature.Certificates.Title"), certs.toArray(X509Certificate[]::new), kseFrame, DViewCertificate.NONE); From 2a92e6c35a0ae5067e76a3e43ab9d2cea9276249 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Fri, 29 Nov 2024 23:59:10 -0800 Subject: [PATCH 20/69] Work with both attached and detached signatures. Prompt for a file when the content for a detached signature cannot be automatically identified. --- .../gui/actions/VerifySignatureAction.java | 93 ++++++++++++++----- .../org/kse/gui/actions/resources.properties | 2 + 2 files changed, 72 insertions(+), 23 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 7b464d981..11866b6a2 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -141,6 +141,7 @@ protected void doAction() { // } // } + // TODO JW - Remove these? KeyStoreState currentState = history.getCurrentState(); KeyStore keyStore = currentState.getKeyStore(); @@ -149,13 +150,33 @@ protected void doAction() { return; } - int extensionIndex = signatureFile.getAbsolutePath().lastIndexOf('.'); - if (extensionIndex < 0) { - // TODO JW - signature doesn't have a file extension + 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; } - String contentFileName = signatureFile.getAbsolutePath().substring(0, extensionIndex); - File contentFile = new File(contentFileName); + + if (signedData.isDetachedSignature()) { + signedData = handleDetachedSignature(signature); + if (signedData == null) { + return; + } + } // TODO JW - Add new option for using cacerts for signature verification. if (preferences.getCaCertsSettings().isImportTrustedCertTrustCheckEnabled()) { @@ -199,23 +220,7 @@ protected void doAction() { } // TODO JW - Verify the signature using the keystore - 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 Exception("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 Exception("PEM is not of type CMS or PKCS7"); - } - signature = signaturePem.getContent(); - } - CMSProcessable data = new CMSProcessableFile(contentFile); - CMSSignedData signedData = new CMSSignedData(data, signature); - - // TODO build Store using certs from the truststore. If a cert cannot be found + // 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(); @@ -247,7 +252,7 @@ protected void doAction() { } } } -// System.out.println("Verified: " + verified); + System.out.println("Verified: " + verified); DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat .format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()), @@ -279,6 +284,32 @@ 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) { @@ -400,4 +431,20 @@ private File chooseSignatureFile() { } return null; } + + private File chooseContentFile() { + JFileChooser chooser = FileChooserFactory.getNoFileChooser(); + chooser.setCurrentDirectory(CurrentDirectory.get()); + chooser.setDialogTitle(res.getString("VerifySignatureAction.ChooseContent.Title")); + chooser.setMultiSelectionEnabled(false); + chooser.setApproveButtonText(res.getString("VerifySignatureAction.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 8d351fd20..b6afd3648 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -681,6 +681,8 @@ 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.button = Select VerifySignatureAction.ChooseSignature.Title = Choose Signature File VerifySignatureAction.ChooseSignature.button = Verify #VerifySignatureAction.CrlSuccessful.message = CRL check successful, certificate valid From c63a67188539211455c68a1fcc6ef9bd919ea127 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:54:20 -0500 Subject: [PATCH 21/69] Added sign file menu item and dialogs. --- kse/src/main/java/org/kse/gui/KseFrame.java | 8 + .../org/kse/gui/actions/SignFileAction.java | 170 ++++++ .../org/kse/gui/dialogs/sign/DSignFile.java | 492 ++++++++++++++++++ .../org/kse/gui/actions/resources.properties | 10 + .../kse/gui/dialogs/sign/resources.properties | 28 + 5 files changed, 708 insertions(+) create mode 100644 kse/src/main/java/org/kse/gui/actions/SignFileAction.java create mode 100644 kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java diff --git a/kse/src/main/java/org/kse/gui/KseFrame.java b/kse/src/main/java/org/kse/gui/KseFrame.java index a7d1ce4e8..5e7333ab9 100644 --- a/kse/src/main/java/org/kse/gui/KseFrame.java +++ b/kse/src/main/java/org/kse/gui/KseFrame.java @@ -164,6 +164,7 @@ import org.kse.gui.actions.ShowHideToolBarAction; import org.kse.gui.actions.SignCrlAction; import org.kse.gui.actions.SignCsrAction; +import org.kse.gui.actions.SignFileAction; import org.kse.gui.actions.SignJarAction; import org.kse.gui.actions.SignJwtAction; import org.kse.gui.actions.SignMidletAction; @@ -371,6 +372,7 @@ public final class KseFrame implements StatusBar { private JMenuItem jmiKeyPairSignCrl; private JMenuItem jmiKeyPairSignJwt; private JMenuItem jmiKeyPairSignNewKeyPair; + private JMenuItem jmiKeyPairSignFile; private JMenuItem jmiKeyPairUnlock; private JMenuItem jmiKeyPairSetPassword; private JMenuItem jmiKeyPairDelete; @@ -507,6 +509,7 @@ public final class KseFrame implements StatusBar { private final SignMidletAction signMidletAction = new SignMidletAction(this); private final SignCrlAction signCrlAction = new SignCrlAction(this); private final SignJwtAction signJwtAction = new SignJwtAction(this); + private final SignFileAction signFileAction = new SignFileAction(this); private final SignNewKeyPairAction signNewKeyPairAction = new SignNewKeyPairAction(this); private final UnlockKeyPairAction unlockKeyPairAction = new UnlockKeyPairAction(this); private final SetKeyPairPasswordAction setKeyPairPasswordAction = new SetKeyPairPasswordAction(this); @@ -2038,6 +2041,10 @@ private void initKeyStoreEntryPopupMenus() { jmiKeyPairSignJwt.setToolTipText(null); new StatusBarChangeHandler(jmiKeyPairSignJwt, (String) signJwtAction.getValue(Action.LONG_DESCRIPTION), this); + jmiKeyPairSignFile = new JMenuItem(signFileAction); + jmiKeyPairSignFile.setToolTipText(null); + new StatusBarChangeHandler(jmiKeyPairSignFile, (String) signFileAction.getValue(Action.LONG_DESCRIPTION), this); + jmiKeyPairSignNewKeyPair = new JMenuItem(signNewKeyPairAction); jmiKeyPairSignNewKeyPair.setToolTipText(null); new StatusBarChangeHandler(jmiKeyPairSignNewKeyPair, @@ -2092,6 +2099,7 @@ private void initKeyStoreEntryPopupMenus() { jmKeyPairSign.add(jmiKeyPairSignMidlet); jmKeyPairSign.add(jmiKeyPairSignCrl); jmKeyPairSign.add(jmiKeyPairSignJwt); + jmKeyPairSign.add(jmiKeyPairSignFile); jpmKeyPair.addSeparator(); jpmKeyPair.add(jmiKeyPairUnlock); jpmKeyPair.add(jmiKeyPairSetPassword); diff --git a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java new file mode 100644 index 000000000..6f4e9032e --- /dev/null +++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java @@ -0,0 +1,170 @@ +/* + * 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.security.KeyStore; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; + +import org.kse.KSE; +import org.kse.gui.passwordmanager.Password; +import org.kse.crypto.digest.DigestType; +import org.kse.crypto.keypair.KeyPairType; +import org.kse.crypto.keypair.KeyPairUtil; +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.utilities.history.KeyStoreHistory; +import org.kse.utilities.history.KeyStoreState; + +/** + * Action to sign a JAR using the selected key pair entry. + */ +public class SignFileAction extends KeyStoreExplorerAction { + private static final long serialVersionUID = 6227240459189308322L; + + /** + * Construct action. + * + * @param kseFrame KeyStore Explorer frame + */ + public SignFileAction(KseFrame kseFrame) { + super(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")))); + } + + /** + * 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); + + // 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); + dSignFile.setVisible(true); + + // check if jar sign dialog was successful + if (!dSignFile.isSuccessful()) { + return; + } + + SignatureType signatureType = dSignFile.getSignatureType(); + File inputFile = dSignFile.getInputFile(); + File outputFile = dSignFile.getOutputFile(); + String tsaUrl = dSignFile.getTimestampingServerUrl(); + DigestType digestType = dSignFile.getDigestType(); + + // 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 new file mode 100644 index 000000000..85a88c5ec --- /dev/null +++ b/kse/src/main/java/org/kse/gui/dialogs/sign/DSignFile.java @@ -0,0 +1,492 @@ +/* + * 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.dialogs.sign; + +import java.awt.Container; +import java.awt.Dialog; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +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; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +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.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; + +/** + * 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 ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/dialogs/sign/resources"); + + private static final String CANCEL_KEY = "CANCEL_KEY"; + + private JLabel jlInputFile; + private JPanel jpInputFile; + private JTextField jtfInputFile; + private JButton jbInputFileBrowse; + private JLabel jlDetachedSignature; + private JCheckBox jcbDetachedSignature; + private JLabel jlSignatureAlgorithm; + private JComboBox jcbSignatureAlgorithm; + private JLabel jlDigestAlgorithm; + private JComboBox jcbDigestAlgorithm; + private JLabel jlAddTimestamp; + private JCheckBox jcbAddTimestamp; + private JLabel jlTimestampServerUrl; + private JComboBox 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 signers) throws CryptoException { + private void initComponents(Collection signers) { jlSigners = new JLabel(res.getString("DViewSignature.jlSigners.text")); jtrSigners = new JTree(createSignerNodes(signers)); @@ -326,10 +325,9 @@ public void windowClosing(WindowEvent evt) { setResizable(false); - // 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)); + jtrSigners.setSelectionPath(new TreePath(new TreeNode[] {topNode, firstChild})); getRootPane().setDefaultButton(jbOK); @@ -357,8 +355,8 @@ private DefaultMutableTreeNode createSignerNodes(Collection= 0) { - for (Enumeration enumNodes = node.children(); enumNodes.hasMoreElements(); ) { - TreeNode subNode = (TreeNode) enumNodes.nextElement(); + for (Enumeration enumNodes = node.children(); enumNodes.hasMoreElements(); ) { + TreeNode subNode = enumNodes.nextElement(); TreePath path = parent.pathByAddingChild(subNode); expandTree(tree, path); } @@ -526,22 +524,17 @@ private void certificatesPressed() { private void timeStampPressed() { KseSignerInformation signer = getSelectedSignerInfo(); + String shortName = signer.getShortName(); - try { - String shortName = signer.getShortName(); - - List timeStampSigners = CmsUtil.convertSignerInformations( - timeStampSigner.getSignerInfos().getSigners(), signer.getTrustedCerts(), - timeStampSigner.getCertificates()); + List timeStampSigners = CmsUtil.convertSignerInformations( + timeStampSigner.getSignerInfos().getSigners(), signer.getTrustedCerts(), + timeStampSigner.getCertificates()); - DViewSignature dViewSignature = new DViewSignature(this, - MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName), - timeStampSigner, timeStampSigners, null); - dViewSignature.setLocationRelativeTo(this); - dViewSignature.setVisible(true); - } catch (CryptoException e) { - DError.displayError(this, e); - } + DViewSignature dViewSignature = new DViewSignature(this, + MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName), + timeStampSigner, timeStampSigners, null); + dViewSignature.setLocationRelativeTo(this); + dViewSignature.setVisible(true); } private void signerAsn1DumpPressed() { From aaeff1d9fd73c88c15db173f874d8061240d9754 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Tue, 24 Dec 2024 22:10:32 -0800 Subject: [PATCH 58/69] Added TODO about supported digest types for time stamp servers. --- kse/src/main/java/org/kse/crypto/signing/CmsSigner.java | 3 +++ 1 file changed, 3 insertions(+) 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 7d2b94078..c4868f17a 100644 --- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java +++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java @@ -170,6 +170,9 @@ public static SignerInformationStore addTimestamp(String tsaUrl, SignerInformati for (SignerInformation si : signerInfos.getSigners()) { byte[] signature = si.getSignature(); + // TODO JW Ed448 uses digest type of SHAKE256-512, which is not readily supported by many, + // if not any, time stamp servers. Convert to something more widely recognized? + // send request to TSA byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, digestType); From 8666cdccaf44cbdf77fe22c2f7c3f59ab3879fa7 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 25 Dec 2024 11:28:49 -0800 Subject: [PATCH 59/69] Remove content type resource strings. Updated TODOs.T --- .../main/java/org/kse/crypto/signing/KseSignerInformation.java | 1 + kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java | 2 +- kse/src/main/resources/org/kse/gui/dialogs/resources.properties | 2 -- 3 files changed, 2 insertions(+), 3 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 30f37e9cd..92d4aa113 100644 --- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java +++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java @@ -242,6 +242,7 @@ private void verify() { } } } catch (Exception e) { + // TODO JW Display verification failure reason. verified = false; } 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 7e6767141..ab57951d5 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -41,6 +41,7 @@ import java.util.ResourceBundle; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JSeparator; @@ -100,7 +101,6 @@ 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 JTree jtrSigners; private JScrollPane jspSigners; 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 3c50d2aca..91854debd 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.jlContentType.text = Content Type: DViewSignature.jlIssuer.text = Issuer: DViewSignature.jlSigners.text = Signers: DViewSignature.jlSignatureAlgorithm.text = Signature Algorithm: @@ -623,7 +622,6 @@ DViewSignature.jlSigningTime.text = Signing Time: DViewSignature.jlStatus.text = Status: DViewSignature.jlSubject.text = Subject: DViewSignature.jlVersion.text = Version: -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 From 2b3325310c2fd5db6ccc777a20f0b4f571d2a011 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Thu, 26 Dec 2024 11:06:13 -0800 Subject: [PATCH 60/69] Removed unusd resource strings. --- .../org/kse/gui/dialogs/DViewSignature.java | 40 +------------------ .../org/kse/gui/actions/resources.properties | 6 --- .../org/kse/gui/dialogs/resources.properties | 13 +----- .../kse/gui/dialogs/sign/resources.properties | 7 +--- 4 files changed, 4 insertions(+), 62 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 ab57951d5..d4016d2bd 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -41,7 +41,6 @@ import java.util.ResourceBundle; import javax.swing.JButton; -import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JSeparator; @@ -118,8 +117,6 @@ public class DViewSignature extends JEscDialog { private JTextField jtfSignatureAlgorithm; private JButton jbTimeStamp; 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; @@ -198,18 +195,13 @@ private void initComponents(Collection signers) { 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)); + PlatformUtil.setMnemonic(jbTimeStamp, res.getString("DViewSignature.jbTimeStamp.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)); @@ -243,7 +235,6 @@ private void initComponents(Collection signers) { pane.add(jtfSignatureAlgorithm, "wrap"); pane.add(jbTimeStamp, "spanx, split"); pane.add(jbSignerAsn1, "wrap"); -// pane.add(jbExtensions, ""); pane.add(new JSeparator(), "spanx, growx, wrap 15:push"); pane.add(jbCertificates, "spanx, split"); pane.add(jbPem, ""); @@ -289,15 +280,6 @@ private void initComponents(Collection signers) { } }); -// jbExtensions.addActionListener(evt -> { -// try { -// CursorUtil.setCursorBusy(DViewSignature.this); -// extensionsPressed(); -// } finally { -// CursorUtil.setCursorFree(DViewSignature.this); -// } -// }); - jbPem.addActionListener(evt -> { try { CursorUtil.setCursorBusy(DViewSignature.this); @@ -383,7 +365,6 @@ private void populateDetails() { jdnIssuer.setEnabled(false); jbTimeStamp.setEnabled(false); jbSignerAsn1.setEnabled(false); -// jbExtensions.setEnabled(false); jtfStatus.setText(""); jtfStatus.setToolTipText(""); @@ -395,7 +376,6 @@ private void populateDetails() { } else { jdnSubject.setEnabled(true); jdnIssuer.setEnabled(true); -// jbExtensions.setEnabled(true); jbSignerAsn1.setEnabled(true); try { @@ -449,15 +429,6 @@ private void populateDetails() { } else { jbTimeStamp.setEnabled(false); } - -// Set critExts = signerInfo.getCriticalExtensionOIDs(); -// Set nonCritExts = signerInfo.getNonCriticalExtensionOIDs(); -// -// if ((critExts != null && !critExts.isEmpty()) || (nonCritExts != null && !nonCritExts.isEmpty())) { -// jbExtensions.setEnabled(true); -// } else { -// jbExtensions.setEnabled(false); -// } // TODO JW Remove the try/catch (or fix exception list) } catch (Exception e) { DError.displayError(this, e); @@ -549,15 +520,6 @@ private void signerAsn1DumpPressed() { } } -// private void extensionsPressed() { -// X509Certificate cert = getSelectedSignerInfo(); -// -// DViewExtensions dViewExtensions = new DViewExtensions(this, res.getString("DViewCertificate.Extensions.Title"), -// cert, kseFrame); -// dViewExtensions.setLocationRelativeTo(this); -// dViewExtensions.setVisible(true); -// } - private void pemEncodingPressed() { try { DViewPem dViewCertPem = new DViewPem(this, res.getString("DViewSignature.Pem.Title"), signedData); 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 5fa96b2c1..657b8816d 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -592,17 +592,11 @@ SignJarAction.statusbar = Sign a Java Archive (JAR) using the Ke 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 or counter sign a PKCS#7 signature using the Key Pair entry SignFileAction.text = Sign File SignFileAction.tooltip = Sign a file or counter sign a PKCS#7 signature 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 91854debd..12616e5b7 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -585,10 +585,7 @@ DViewSecretKey.jtfKeySize.text = {0} bits DViewSecretKey.jtfKeySize.tooltip = Secret key's size DViewSignature.Certificates.Title = Signature Certificates -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 for ''{0}'' @@ -598,19 +595,14 @@ DViewSignature.jbAsn1.tooltip = Display ASN.1 dump for signa DViewSignature.jbCertificates.mnemonic = C DViewSignature.jbCertificates.text = Certificates DViewSignature.jbCertificates.tooltip = Display the signature's certificates -DViewSignature.jbCounterSigners.mnemonic = S -DViewSignature.jbCounterSigners.text = Counter Signers -DViewSignature.jbCounterSigners.tooltip = Display the signature's counter signers -#DViewSignature.jbExtensions.mnemonic = E -#DViewSignature.jbExtensions.text = Extensions -#DViewSignature.jbExtensions.tooltip = Display the certificate's extensions 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.jbSignerAsn1.tooltip = Display ASN.1 dump for the signer information +DViewSignature.jbTimeStamp.mnemonic = T DViewSignature.jbTimeStamp.text = Time Stamp DViewSignature.jbTimeStamp.tooltip = Display the signature's time stamp signer DViewSignature.jdnIssuer.tooltip = Signer's issuer certificate distinguished name @@ -623,7 +615,6 @@ DViewSignature.jlStatus.text = Status: DViewSignature.jlSubject.text = Subject: DViewSignature.jlVersion.text = Version: 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 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 03e696297..d3c21cae7 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,7 +234,6 @@ 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.CounterSign.Title = Choose Signature File DSignFile.ChooseInputFile.Sign.Title = Choose Input File DSignFile.ChooseOutputFile.Title = Choose Output File @@ -242,13 +241,9 @@ 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.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 DSignFile.jbInputFileBrowse.mnemonic = B From 7cbd87a6ba52479c6b842be6ba4855fc4f490cfa Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sat, 28 Dec 2024 10:28:29 -0800 Subject: [PATCH 61/69] Removed more unused resource strings. Resolved some TODOs. Fixed TSA with Ed448 signature algorithm. --- .../org/kse/crypto/signing/CmsSigner.java | 11 +++---- .../gui/actions/VerifySignatureAction.java | 11 +++---- .../org/kse/gui/dialogs/sign/DSignFile.java | 10 ------- .../org/kse/gui/actions/resources.properties | 30 +++---------------- 4 files changed, 16 insertions(+), 46 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 c4868f17a..40d5a27d5 100644 --- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java +++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java @@ -128,7 +128,6 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri .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); @@ -137,8 +136,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. + // addCounterSigners 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))); @@ -170,8 +169,10 @@ public static SignerInformationStore addTimestamp(String tsaUrl, SignerInformati for (SignerInformation si : signerInfos.getSigners()) { byte[] signature = si.getSignature(); - // TODO JW Ed448 uses digest type of SHAKE256-512, which is not readily supported by many, - // if not any, time stamp servers. Convert to something more widely recognized? + // Ed448 uses digest type of SHAKE256-512, which is not currently supported by the TSAs. + if (DigestType.SHAKE256 == digestType) { + digestType = DigestType.SHA512; + } // send request to TSA byte[] token = TimeStampingClient.getTimeStampToken(tsaUrl, signature, digestType); 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 9311d32c3..5bcd10a67 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -21,6 +21,7 @@ package org.kse.gui.actions; import java.awt.Toolkit; +import java.awt.event.InputEvent; import java.io.File; import java.security.KeyStore; import java.security.KeyStoreException; @@ -37,6 +38,7 @@ import javax.swing.ImageIcon; import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.KeyStroke; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaCertStore; @@ -61,10 +63,8 @@ public class VerifySignatureAction extends AuthorityCertificatesAction { public VerifySignatureAction(KseFrame kseFrame) { super(kseFrame); - // TODO JW - Add an accelerator? -// putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('K', -// Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); - // TODO JW - Need a good description. + putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke('V', + Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() + InputEvent.ALT_DOWN_MASK)); putValue(LONG_DESCRIPTION, res.getString("VerifySignatureAction.statusbar")); putValue(NAME, res.getString("VerifySignatureAction.text")); putValue(SHORT_DESCRIPTION, res.getString("VerifySignatureAction.tooltip")); @@ -102,6 +102,7 @@ protected void doAction() { Set compCerts = new HashSet<>(); compCerts.addAll(extractCertificates(keyStore)); + // TODO JW make X509CertUtil.extractCertificates public and use it for cacerts and windowstrustedrootcerts if (caCertificates != null) { // Perform cert lookup against CA Certificates KeyStore compCerts.addAll(extractCertificates(caCertificates)); @@ -123,7 +124,7 @@ protected void doAction() { // 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 +// // can still be displayed. loadSignature already tried to find and load the detached // // content. // if (signedData.getSignedContent() != null) { // for (KseSignerInformation signer : signers) { 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 83860217d..70df522e6 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 @@ -410,16 +410,6 @@ private void okPressed() { } } - // 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(); 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 657b8816d..f49405a65 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -688,38 +688,16 @@ VerifyCertificateAction.tryLater.message = Try again later VerifyCertificateAction.unauthorized.message = Request unauthorized 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 Content File VerifySignatureAction.ChooseContent.button = Select VerifySignatureAction.ChooseSignature.Title = Choose Signature File VerifySignatureAction.ChooseSignature.button = Verify -#VerifySignatureAction.CrlSuccessful.message = CRL check successful, certificate valid -#VerifySignatureAction.EnterPassword.Title = Enter Password for ''{0}'' -#VerifySignatureAction.ExamineFile.Title = Examine File -#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} -#VerifySignatureAction.internalError.message = Internal error in issuer -#VerifySignatureAction.malformedRequest.message = Illegal confirmation request -#VerifySignatureAction.revokedStatus.message = The certificate has been revoked, reason: {0}, revocation date: {1} -#VerifySignatureAction.sigRequired.message = Must sign the request -VerifySignatureAction.statusbar = Verify signature -VerifySignatureAction.text = Verify Signature -VerifySignatureAction.tooltip = Verify signature -#VerifySignatureAction.trustStoreEmpty.message = Path does not chain with any of the trust anchors -#VerifySignatureAction.tryLater.message = Try again later -#VerifySignatureAction.unauthorized.message = Request unauthorized -#VerifySignatureAction.unknownStatus.message = Unknown status {0} +VerifySignatureAction.VerifySignature.Title = Verify PKCS#7/CMS Signature +VerifySignatureAction.statusbar = Verify a PKCS#7/CMS signature +VerifySignatureAction.text = Verify PKCS#7/CMS Signature +VerifySignatureAction.tooltip = Verify PKCS#7/CMS Signature WebsiteAction.GitHubIssueTracker.statusbar = Create a bug report or feature request WebsiteAction.GitHubIssueTracker.text = Bug Reports / Feature Requests From 0189dfc07dd27f9f9e43eb77e6edfecf60c7b2f0 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Sat, 28 Dec 2024 15:56:09 -0800 Subject: [PATCH 62/69] Use the explicit provider if one is present for signing. --- .../org/kse/crypto/signing/CmsSigner.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 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 40d5a27d5..0e72a3172 100644 --- a/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java +++ b/kse/src/main/java/org/kse/crypto/signing/CmsSigner.java @@ -45,7 +45,6 @@ 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; @@ -82,11 +81,16 @@ public static CMSSignedData sign(File inputFile, PrivateKey privateKey, X509Cert try { CMSTypedData msg = new CMSProcessableFile(inputFile); + JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(signatureType.jce()); + JcaDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder(); + if (provider != null) { + contentSignerBuilder.setProvider(provider); + digestCalculatorProviderBuilder.setProvider(provider); + } + CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); - ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey); - generator.addSignerInfoGenerator( - new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) - .build(contentSigner, certificateChain[0])); + generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digestCalculatorProviderBuilder.build()) + .build(contentSignerBuilder.build(privateKey), certificateChain[0])); generator.addCertificates(new JcaCertStore(Arrays.asList(certificateChain))); CMSSignedData signedData = generator.generate(msg, !detachedSignature); @@ -121,11 +125,17 @@ public static CMSSignedData counterSign(CMSSignedData signedData, PrivateKey pri X509Certificate[] certificateChain, boolean detachedSignature, SignatureType signatureType, String tsaUrl, Provider provider) throws CryptoException { try { + JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(signatureType.jce()); + JcaDigestCalculatorProviderBuilder digestCalculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder(); + if (provider != null) { + contentSignerBuilder.setProvider(provider); + digestCalculatorProviderBuilder.setProvider(provider); + } + CMSSignedDataGenerator counterSignerGen = new CMSSignedDataGenerator(); - ContentSigner contentSigner = new JcaContentSignerBuilder(signatureType.jce()).build(privateKey); counterSignerGen.addSignerInfoGenerator( - new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) - .build(contentSigner, certificateChain[0])); + new JcaSignerInfoGeneratorBuilder(digestCalculatorProviderBuilder.build()) + .build(contentSignerBuilder.build(privateKey), certificateChain[0])); // Counter signs all existing signatures. CMSSignedDataGenerator generator = new CMSSignedDataGenerator(); From 7b26dc83a683d9bdb849249d5d2f3985fc27b38a Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Wed, 1 Jan 2025 12:11:11 -0800 Subject: [PATCH 63/69] Resolved more TODOs. Addressed comments from the PR. --- .../crypto/signing/KseSignerInformation.java | 63 +++++++++++++++---- .../org/kse/crypto/x509/X500NameUtils.java | 30 +++------ .../org/kse/gui/dialogs/DViewSignature.java | 3 +- .../org/kse/gui/dialogs/resources.properties | 4 +- 4 files changed, 62 insertions(+), 38 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 92d4aa113..8438d5228 100644 --- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java +++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -36,6 +37,9 @@ import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; @@ -45,7 +49,10 @@ import org.bouncycastle.tsp.TSPValidationException; import org.bouncycastle.tsp.TimeStampToken; import org.bouncycastle.util.Store; +import org.kse.KSE; +import org.kse.crypto.CryptoException; import org.kse.crypto.x509.X500NameUtils; +import org.kse.crypto.x509.X509CertUtil; import org.kse.utilities.StringUtils; import org.kse.utilities.io.HexUtil; @@ -73,6 +80,7 @@ public KseSignerInformation(SignerInformation signerInfo, Store matchedCerts1 = trustedCerts.getMatches(getSID()); + Collection matchedCerts1 = signatureCerts.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 { @SuppressWarnings("unchecked") - Collection matchedCerts2 = signatureCerts.getMatches(getSID()); + Collection matchedCerts2 = trustedCerts.getMatches(getSID()); if (!matchedCerts2.isEmpty()) { cert = matchedCerts2.iterator().next(); @@ -126,6 +131,28 @@ private void lookupCert() { } } + private void establishTrust() { + + try { + List certs = new ArrayList<>(); + + // TODO JW Don't want to use signature certs for establishing trust, but need to use + // them for building a chain that can potentially lead to trust. + for (X509CertificateHolder certHolder : signatureCerts.getMatches(null)) { + certs.add(X509CertUtil.convertCertificate(certHolder)); + } + for (X509CertificateHolder certHolder : trustedCerts.getMatches(null)) { + certs.add(X509CertUtil.convertCertificate(certHolder)); + } + + X509Certificate[] trustChain = null; //X509CertUtil.establishTrust(X509CertUtil.convertCertificate(cert), certs); + trustedCert = trustChain != null; + } catch (CryptoException e) { + // TODO JW Auto-generated catch block + e.printStackTrace(); + } + } + /** * * @return The short name for user interfaces. @@ -146,9 +173,9 @@ private static String getShortName(X509CertificateHolder cert) { X500Name subject = cert.getSubject(); String shortName = X500NameUtils.extractCN(subject); - String emailAddress = X500NameUtils.extractEmailAddress(subject); + String emailAddress = extractEmailAddress(cert); - if (!StringUtils.isBlank(emailAddress)) { + if (!StringUtils.isBlank(emailAddress) && !shortName.equals(emailAddress)) { if (StringUtils.isBlank(shortName)) { shortName = emailAddress; } else { @@ -168,6 +195,20 @@ private static String getShortName(X509CertificateHolder cert) { return shortName; } + private static String extractEmailAddress(X509CertificateHolder cert) { + GeneralNames names = GeneralNames.fromExtensions(cert.getExtensions(), Extension.subjectAlternativeName); + + if (names != null) { + for (GeneralName name : names.getNames()) { + if (name.getTagNo() == GeneralName.rfc822Name) { + return name.getName().toString(); + } + } + } + + return ""; + } + /** * Extracts the signature signing time, if present, from the signature's signed attributes. * @@ -228,8 +269,7 @@ private void verify() { boolean verified = false; try { - // TODO JW - Should a provider be specified for the JcaSimpleSingerInfoVerifierBuilder? - if (verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { + if (verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(KSE.BC).build(cert))) { verified = true; // TODO JW always use the cacerts store for validating the timestamp trust. @@ -263,9 +303,8 @@ private boolean verifyCounterSignatures() { boolean verified = true; - // TODO JW Don't use super. Just use the overridden method. - Collection counterSigners = CmsUtil.convertSignerInformations( - super.getCounterSignatures().getSigners(), trustedCerts, signatureCerts); + Collection counterSigners = CmsUtil + .convertSignerInformations(super.getCounterSignatures().getSigners(), trustedCerts, signatureCerts); for (KseSignerInformation signer : counterSigners) { signer.verify(); 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 db99574b9..a24678592 100644 --- a/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java +++ b/kse/src/main/java/org/kse/crypto/x509/X500NameUtils.java @@ -83,11 +83,17 @@ public static String getRdn(X500Name dn, ASN1ObjectIdentifier rdnOid) { return value; } - private static String extractComponent(X500Name name, ASN1ObjectIdentifier objectIdentifier) { + /** + * 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) { for (RDN rdn : name.getRDNs()) { AttributeTypeAndValue atav = rdn.getFirst(); - if (atav.getType().equals(objectIdentifier)) { + if (atav.getType().equals(BCStyle.CN)) { return atav.getValue().toString(); } } @@ -95,26 +101,6 @@ private static String extractComponent(X500Name name, ASN1ObjectIdentifier objec 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/DViewSignature.java b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java index d4016d2bd..04d7b7fcc 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -199,8 +199,7 @@ private void initComponents(Collection signers) { 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)); + PlatformUtil.setMnemonic(jbSignerAsn1, res.getString("DViewSignature.jbSignerAsn1.mnemonic").charAt(0)); jbCertificates = new JButton(res.getString("DViewSignature.jbCertificates.text")); jbCertificates.setToolTipText(res.getString("DViewSignature.jbCertificates.tooltip")); 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 12616e5b7..fd010c5f5 100644 --- a/kse/src/main/resources/org/kse/gui/dialogs/resources.properties +++ b/kse/src/main/resources/org/kse/gui/dialogs/resources.properties @@ -599,8 +599,8 @@ 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.mnemonic = S +DViewSignature.jbSignerAsn1.text = Signer ASN.1 DViewSignature.jbSignerAsn1.tooltip = Display ASN.1 dump for the signer information DViewSignature.jbTimeStamp.mnemonic = T DViewSignature.jbTimeStamp.text = Time Stamp From cb684ce5ac728edb8c5a44f066ec2b2f4a248e1d Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:18:56 -0800 Subject: [PATCH 64/69] Removed feature idea TODOs. Removed unused parameters in DSignFile. --- .../org/kse/gui/actions/SignFileAction.java | 3 +- .../org/kse/gui/dialogs/sign/DSignFile.java | 31 ++++++------------- .../kse/gui/dialogs/sign/resources.properties | 2 -- 3 files changed, 10 insertions(+), 26 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 7cfa88dea..21a59a9ad 100644 --- a/kse/src/main/java/org/kse/gui/actions/SignFileAction.java +++ b/kse/src/main/java/org/kse/gui/actions/SignFileAction.java @@ -104,7 +104,7 @@ protected void doAction() { KeyPairType keyPairType = KeyPairUtil.getKeyPairType(privateKey); // get the file, signatures, and time stamp - DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType, false); + DSignFile dSignFile = new DSignFile(frame, privateKey, keyPairType); dSignFile.setLocationRelativeTo(frame); dSignFile.setVisible(true); @@ -137,7 +137,6 @@ protected void doAction() { 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()), 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 70df522e6..e0acc600e 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 @@ -101,7 +101,6 @@ public class DSignFile extends JEscDialog { private JButton jbOK; private JButton jbCancel; - // TODO JW Store settings (e.g., signature algorithm, TS setting and URL) in preferences. private PrivateKey signPrivateKey; private KeyPairType signKeyPairType; private File inputFile; @@ -121,26 +120,19 @@ 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 isCounterSign True if the dialog should be counter signing a signature. False for signing a file. */ - public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPairType, boolean isCounterSign) { + public DSignFile(JFrame parent, PrivateKey signPrivateKey, KeyPairType signKeyPairType) { super(parent, Dialog.ModalityType.DOCUMENT_MODAL); this.signPrivateKey = signPrivateKey; this.signKeyPairType = signKeyPairType; - String title = "DSignFile.Sign.Title"; - if (isCounterSign) { - title = "DSignFile.CounterSign.Title"; - } - setTitle(res.getString(title)); - initComponents(isCounterSign); + setTitle(res.getString("DSignFile.Sign.Title")); + initComponents(); } /** * Initializes the dialogue panel and associated elements - * - * @param isCounterSign True for counter signing. False for signing. */ - private void initComponents(boolean isCounterSign) { + private void initComponents() { resetToDefault(); jlInputFile = new JLabel(res.getString("DSignFile.jlInputFile.text")); @@ -231,7 +223,7 @@ private void initComponents(boolean isCounterSign) { jbInputFileBrowse.addActionListener(evt -> { try { CursorUtil.setCursorBusy(DSignFile.this); - inputFileBrowsePressed(isCounterSign); + inputFileBrowsePressed(); } finally { CursorUtil.setCursorFree(DSignFile.this); } @@ -425,16 +417,11 @@ private void okPressed() { /** * Get input file */ - private void inputFileBrowsePressed(boolean isCounterSign) { + private void inputFileBrowsePressed() { 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 = FileChooserFactory.getAllFileChooser(); + chooser.setDialogTitle(res.getString("DSignFile.ChooseInputFile.Sign.Title")); chooser.setCurrentDirectory(CurrentDirectory.get()); chooser.setMultiSelectionEnabled(false); chooser.setApproveButtonText(res.getString("DSignFile.InputFileChooser.button")); @@ -547,7 +534,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, false); + DSignFile dialog = new DSignFile(new JFrame(), kp.getPrivate(), KeyPairType.RSA); DialogViewer.run(dialog); } } \ No newline at end of file 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 d3c21cae7..d3801b7e8 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,10 +234,8 @@ DSignJwt.jdtNotBefore.tooltip = Not before DSignJwt.jlSignatureAlgorithm.text = Signature Algorithm: DSignJwt.jcbSignatureAlgorithm.tooltip = Signature algorithm used to sign the JWT -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 DSignFile.InputFileRequired.message = Path to Input File required. From 801e5df88002185fecf9f22d853a3dc6125fb532 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:43:34 -0800 Subject: [PATCH 65/69] Fixed TODOs in DViewSignature. --- .../org/kse/gui/dialogs/DViewSignature.java | 121 ++++++++---------- 1 file changed, 54 insertions(+), 67 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 04d7b7fcc..31c57f6e1 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -19,13 +19,13 @@ */ package org.kse.gui.dialogs; -import java.awt.Color; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; @@ -34,6 +34,7 @@ import java.text.MessageFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Enumeration; @@ -58,6 +59,7 @@ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; @@ -65,6 +67,7 @@ import org.kse.KSE; import org.kse.crypto.CryptoException; import org.kse.crypto.digest.DigestType; +import org.kse.crypto.signing.CmsSigner; import org.kse.crypto.signing.CmsUtil; import org.kse.crypto.signing.KseSignerInformation; import org.kse.crypto.signing.SignatureType; @@ -78,8 +81,6 @@ import org.kse.gui.components.JEscDialog; import org.kse.gui.crypto.JDistinguishedName; 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; @@ -96,8 +97,6 @@ public class DViewSignature extends JEscDialog { private static ResourceBundle res = ResourceBundle.getBundle("org/kse/gui/dialogs/resources"); - private KsePreferences preferences = PreferencesManager.getPreferences(); - private KseFrame kseFrame; private JLabel jlSigners; @@ -223,7 +222,7 @@ private void initComponents(Collection signers) { pane.add(jlStatus, ""); pane.add(jtfStatus, "sgx, wrap"); pane.add(jlVersion, ""); - pane.add(jtfVersion, "sgx, wrap"); + pane.add(jtfVersion, "wrap"); pane.add(jlSubject, ""); pane.add(jdnSubject, "wrap"); pane.add(jlIssuer, ""); @@ -377,61 +376,46 @@ private void populateDetails() { jdnIssuer.setEnabled(true); jbSignerAsn1.setEnabled(true); - try { - Date signingTime = signerInfo.getSigningTime(); - X509CertificateHolder cert = signerInfo.getCertificate(); - - jtfStatus.setText(signerInfo.getStatus().getText()); - jtfStatus.setCaretPosition(0); - jtfStatus.setToolTipText(signerInfo.getStatus().getToolTip()); - - jtfVersion.setText(Integer.toString(signerInfo.getVersion())); - jtfVersion.setCaretPosition(0); - - if (cert != null) { - jdnSubject.setEnabled(true); - jdnSubject.setDistinguishedName(cert.getSubject()); - } else { - jdnSubject.setEnabled(false); - } - - jdnIssuer.setDistinguishedName(signerInfo.getSID().getIssuer()); - - if (signingTime != null) { - jtfSigningTime.setText(StringUtils.formatDate(signingTime)); - } else { - jtfSigningTime.setText(""); - } - -// if (true) { -// jtfSigningTime.setText( -// MessageFormat.format(res.getString("DViewCertificate.jtfSigningTime.expired.text"), -// jtfSigningTime.getText())); -// jtfSigningTime.setForeground(Color.red); -// } else { -// jtfSigningTime.setForeground(jtfVersion.getForeground()); -// } - jtfSigningTime.setCaretPosition(0); - - SignatureType signatureType = lookupSignatureType(signerInfo); - if (signatureType != null ) { - jtfSignatureAlgorithm.setText(signatureType.friendly()); - } else { - jtfSignatureAlgorithm.setText(""); - } - jtfSignatureAlgorithm.setCaretPosition(0); - - timeStampSigner = getTimeStampSignature(signerInfo); - - if (timeStampSigner != null) { - jbTimeStamp.setEnabled(true); - } else { - jbTimeStamp.setEnabled(false); - } - // TODO JW Remove the try/catch (or fix exception list) - } catch (Exception e) { - DError.displayError(this, e); - dispose(); + Date signingTime = signerInfo.getSigningTime(); + X509CertificateHolder cert = signerInfo.getCertificate(); + + jtfStatus.setText(signerInfo.getStatus().getText()); + jtfStatus.setCaretPosition(0); + jtfStatus.setToolTipText(signerInfo.getStatus().getToolTip()); + + jtfVersion.setText(Integer.toString(signerInfo.getVersion())); + jtfVersion.setCaretPosition(0); + + if (cert != null) { + jdnSubject.setEnabled(true); + jdnSubject.setDistinguishedName(cert.getSubject()); + } else { + jdnSubject.setEnabled(false); + } + + jdnIssuer.setDistinguishedName(signerInfo.getSID().getIssuer()); + + if (signingTime != null) { + jtfSigningTime.setText(StringUtils.formatDate(signingTime)); + } else { + jtfSigningTime.setText(""); + } + jtfSigningTime.setCaretPosition(0); + + SignatureType signatureType = lookupSignatureType(signerInfo); + if (signatureType != null) { + jtfSignatureAlgorithm.setText(signatureType.friendly()); + } else { + jtfSignatureAlgorithm.setText(""); + } + jtfSignatureAlgorithm.setCaretPosition(0); + + timeStampSigner = getTimeStampSignature(signerInfo); + + if (timeStampSigner != null) { + jbTimeStamp.setEnabled(true); + } else { + jbTimeStamp.setEnabled(false); } } } @@ -540,7 +524,6 @@ private void asn1DumpPressed() { } private void okPressed() { - // TODO JW - set any preferences here (e.g., chosen fingerprint algorithm) closeDialog(); } @@ -570,10 +553,14 @@ SignatureType.SHA224WITHRSAANDMGF1, new BigInteger( Hex.decode("0011223344556677889900112233445566778899"))); X509Certificate[] certs = new X509Certificate[] { eeCert, caCert }; - - // TODO JW - fix main method -// DViewSignature dialog = new DViewSignature(new javax.swing.JFrame(), "Title", certs, new KseFrame(), -// IMPORT_EXPORT); -// DialogViewer.run(dialog); + CMSSignedData signedData = CmsSigner.sign(new File("build.gradle"), eeKeyPair.getPrivate(), certs, false, + SignatureType.SHA256_RSA, null, null); + @SuppressWarnings("unchecked") + List signers = CmsUtil.convertSignerInformations(signedData.getSignerInfos().getSigners(), + new JcaCertStore(Arrays.asList(certs)), signedData.getCertificates()); + + DViewSignature dialog = new DViewSignature(new javax.swing.JFrame(), "Title", signedData, signers, + new KseFrame()); + DialogViewer.run(dialog); } } From 508bb70b31562ce2b6f3696406f55e54f1ede0bb Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:44:55 -0800 Subject: [PATCH 66/69] Verify cert trust during signature verification. --- .../crypto/signing/KseSignerInformation.java | 39 +++++++++++-------- .../org/kse/crypto/x509/X509CertUtil.java | 2 +- 2 files changed, 23 insertions(+), 18 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 8438d5228..af5d1baa8 100644 --- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java +++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java @@ -80,7 +80,6 @@ public KseSignerInformation(SignerInformation signerInfo, Store certs = new ArrayList<>(); + List allCerts = new ArrayList<>(); - // TODO JW Don't want to use signature certs for establishing trust, but need to use - // them for building a chain that can potentially lead to trust. - for (X509CertificateHolder certHolder : signatureCerts.getMatches(null)) { - certs.add(X509CertUtil.convertCertificate(certHolder)); - } - for (X509CertificateHolder certHolder : trustedCerts.getMatches(null)) { - certs.add(X509CertUtil.convertCertificate(certHolder)); - } + List certs = new ArrayList<>(); + for (X509CertificateHolder certHolder : trustedCerts.getMatches(null)) { + X509Certificate c = X509CertUtil.convertCertificate(certHolder); + certs.add(c); + allCerts.add(c); + } + for (X509CertificateHolder certHolder : signatureCerts.getMatches(null)) { + allCerts.add(X509CertUtil.convertCertificate(certHolder)); + } + + // Builds a chain from the signer cert to a root cert. The root cert is not + // necessarily trusted. Uses all known certs. + X509Certificate[] signatureChain = X509CertUtil.establishTrust(X509CertUtil.convertCertificate(cert), + allCerts); + if (signatureChain != null) { + int rootIndex = Math.max(signatureChain.length - 1, 0); - X509Certificate[] trustChain = null; //X509CertUtil.establishTrust(X509CertUtil.convertCertificate(cert), certs); + // Establishes trust from the root of the signature chain to all trusted certs. + X509Certificate[] trustChain = X509CertUtil.establishTrust(signatureChain[rootIndex], certs); trustedCert = trustChain != null; - } catch (CryptoException e) { - // TODO JW Auto-generated catch block - e.printStackTrace(); } } @@ -269,11 +273,12 @@ private void verify() { boolean verified = false; try { + establishTrust(); + if (verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(KSE.BC).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(); if (getCounterSignatures().size() > 0) { 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..93457f9cf 100644 --- a/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java +++ b/kse/src/main/java/org/kse/crypto/x509/X509CertUtil.java @@ -662,7 +662,7 @@ public static X509Certificate[] establishTrust(X509Certificate cert, KeyStore[] return establishTrust(cert, ksCerts); } - private static X509Certificate[] establishTrust(X509Certificate cert, List compCerts) + public static X509Certificate[] establishTrust(X509Certificate cert, List compCerts) throws CryptoException { /* * Check whether or not a trust path exists between the supplied X.509 From 38700bdfb12a6a340c72d0aa136cbdd1958ba95f Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:59:31 -0800 Subject: [PATCH 67/69] Use cacerts for verify timestamp cert trust. --- .../crypto/signing/KseSignerInformation.java | 1 - .../actions/AuthorityCertificatesAction.java | 2 +- .../gui/actions/VerifySignatureAction.java | 76 +++++++++++++++++-- .../org/kse/gui/dialogs/DViewSignature.java | 25 +++--- 4 files changed, 86 insertions(+), 18 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 af5d1baa8..340c1232f 100644 --- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java +++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java @@ -278,7 +278,6 @@ private void verify() { if (verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(KSE.BC).build(cert))) { verified = true; - // TODO JW always use the cacerts store for validating the timestamp trust. verifyTimeStamp(); if (getCounterSignatures().size() > 0) { diff --git a/kse/src/main/java/org/kse/gui/actions/AuthorityCertificatesAction.java b/kse/src/main/java/org/kse/gui/actions/AuthorityCertificatesAction.java index 7ba617f2a..77f111af0 100644 --- a/kse/src/main/java/org/kse/gui/actions/AuthorityCertificatesAction.java +++ b/kse/src/main/java/org/kse/gui/actions/AuthorityCertificatesAction.java @@ -95,7 +95,7 @@ protected KeyStore getWindowsTrustedRootCertificates() throws CryptoException { return windowsTrustedRootCertificates; } - private KeyStore loadCaCertificatesKeyStore() { + protected KeyStore loadCaCertificatesKeyStore() { File caCertificatesFile = new File(preferences.getCaCertsSettings().getCaCertificatesFile()); KeyStore caCertificatesKeyStore = 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 5bcd10a67..57074299b 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -25,7 +25,9 @@ import java.io.File; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.Security; import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.ArrayList; @@ -45,7 +47,9 @@ import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.util.Store; +import org.kse.AuthorityCertificates; import org.kse.crypto.CryptoException; +import org.kse.crypto.SecurityProvider; import org.kse.crypto.signing.CmsUtil; import org.kse.crypto.signing.KseSignerInformation; import org.kse.crypto.x509.X509CertUtil; @@ -134,7 +138,7 @@ protected void doAction() { DViewSignature dViewSignature = new DViewSignature(frame, MessageFormat .format(res.getString("VerifySignatureAction.SignatureDetailsFile.Title"), signatureFile.getName()), - signedData, signers, null); + signedData, signers, getTrustedCertsNoPrefs(), null); dViewSignature.setLocationRelativeTo(frame); dViewSignature.setVisible(true); @@ -144,7 +148,7 @@ protected void doAction() { } } - private Collection extractCertificates(KeyStore keystore) { + private Collection extractCertificates(KeyStore keystore) throws CryptoException { List certs = new ArrayList<>(); @@ -168,16 +172,74 @@ private Collection extractCertificates(KeyStore keystore) { } catch (KeyStoreException e) { // TODO JW Auto-generated catch block - e.printStackTrace(); - } - catch (CryptoException e) { - // TODO JW Auto-generated catch block - e.printStackTrace(); + throw new CryptoException(res.getString("NoExtractCertificates.exception.message"), e); } return certs; } + private Store getTrustedCertsNoPrefs() + throws CryptoException, CertificateEncodingException { + KeyStore caCertificates = getCaCertificatesNoPrefCheck(); + KeyStore windowsTrustedRootCertificates = getWindowsTrustedRootCertificatesNoPrefCheck(); + + // Perform cert lookup against current KeyStore + Set compCerts = new HashSet<>(); + + if (caCertificates != null) { + // Perform cert lookup against CA Certificates KeyStore + compCerts.addAll(extractCertificates(caCertificates)); + } + + if (windowsTrustedRootCertificates != null) { + // Perform cert lookup against Windows Trusted Root Certificates KeyStore + compCerts.addAll(extractCertificates(windowsTrustedRootCertificates)); + } + + @SuppressWarnings("unchecked") + Store trustedCerts = new JcaCertStore(compCerts); + return trustedCerts; + } + + /** + * Get CA Certificates KeyStore. + * + * @return KeyStore or null if unavailable + */ + private KeyStore getCaCertificatesNoPrefCheck() { + AuthorityCertificates authorityCertificates = AuthorityCertificates.getInstance(); + + KeyStore caCertificates = authorityCertificates.getCaCertificates(); + + if (caCertificates == null) { + caCertificates = loadCaCertificatesKeyStore(); + + if (caCertificates != null) { + authorityCertificates.setCaCertificates(caCertificates); + } + } + + return caCertificates; + } + + /** + * Get Windows Trusted Root Certificates KeyStore. + * + * @return KeyStore or null if unavailable + * @throws CryptoException If a problem occurred getting the KeyStore + */ + private KeyStore getWindowsTrustedRootCertificatesNoPrefCheck() throws CryptoException { + AuthorityCertificates authorityCertificates = AuthorityCertificates.getInstance(); + + KeyStore windowsTrustedRootCertificates = null; + + if (Security.getProvider(SecurityProvider.MS_CAPI.jce()) != null) { + windowsTrustedRootCertificates = authorityCertificates.getWindowsTrustedRootCertificates(); + } + + return windowsTrustedRootCertificates; + } + 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 31c57f6e1..0f6a26313 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -63,6 +63,7 @@ import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.util.Store; import org.bouncycastle.util.encoders.Hex; import org.kse.KSE; import org.kse.crypto.CryptoException; @@ -122,22 +123,26 @@ public class DViewSignature extends JEscDialog { private JButton jbOK; private CMSSignedData signedData; + private Store tsaTrustedCerts; private CMSSignedData timeStampSigner; /** * Creates a new DViewCertificate dialog. * - * @param parent Parent frame - * @param title The dialog title - * @param signedData Signature to display - * @param signers Signature(s) to display - * @param kseFrame Reference to main class with currently opened keystores and their contents + * @param parent Parent frame + * @param title The dialog title + * @param signedData Signature to display + * @param signers Signature(s) to display + * @param tsaTrustedCerts All trusted certs suitable for verifying TSA signatures + * @param kseFrame Reference to main class with currently opened keystores and their contents */ - public DViewSignature(Window parent, String title, CMSSignedData signedData, Collection signers, + public DViewSignature(Window parent, String title, CMSSignedData signedData, + Collection signers, Store tsaTrustedCerts, KseFrame kseFrame) { super(parent, title, Dialog.ModalityType.MODELESS); this.kseFrame = kseFrame; this.signedData = signedData; + this.tsaTrustedCerts = tsaTrustedCerts; initComponents(signers); } @@ -480,13 +485,15 @@ private void timeStampPressed() { KseSignerInformation signer = getSelectedSignerInfo(); String shortName = signer.getShortName(); + // Use the trusted certs not managed per the user preferences setting for + // the time stamp trusted certs. List timeStampSigners = CmsUtil.convertSignerInformations( - timeStampSigner.getSignerInfos().getSigners(), signer.getTrustedCerts(), + timeStampSigner.getSignerInfos().getSigners(), tsaTrustedCerts, timeStampSigner.getCertificates()); DViewSignature dViewSignature = new DViewSignature(this, MessageFormat.format(res.getString("DViewSignature.TimeStampSigner.Title"), shortName), - timeStampSigner, timeStampSigners, null); + timeStampSigner, timeStampSigners, tsaTrustedCerts, kseFrame); dViewSignature.setLocationRelativeTo(this); dViewSignature.setVisible(true); } @@ -559,7 +566,7 @@ SignatureType.SHA224WITHRSAANDMGF1, new BigInteger( List signers = CmsUtil.convertSignerInformations(signedData.getSignerInfos().getSigners(), new JcaCertStore(Arrays.asList(certs)), signedData.getCertificates()); - DViewSignature dialog = new DViewSignature(new javax.swing.JFrame(), "Title", signedData, signers, + DViewSignature dialog = new DViewSignature(new javax.swing.JFrame(), "Title", signedData, signers, null, new KseFrame()); DialogViewer.run(dialog); } From c72b9dc2fe4d6c03156b5d190ca8a3acdb921028 Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:04:26 -0800 Subject: [PATCH 68/69] Resolved some more TODOs. Displayed "Not Verified" when the signed content is not available. --- .../kse/crypto/signing/KseSignerInformation.java | 1 - .../org/kse/gui/actions/VerifySignatureAction.java | 12 ------------ .../java/org/kse/gui/dialogs/DViewSignature.java | 14 ++++++++++++-- 3 files changed, 12 insertions(+), 15 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 340c1232f..5e29fe9bb 100644 --- a/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java +++ b/kse/src/main/java/org/kse/crypto/signing/KseSignerInformation.java @@ -286,7 +286,6 @@ private void verify() { } } } catch (Exception e) { - // TODO JW Display verification failure reason. verified = false; } 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 57074299b..098d5786f 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -124,18 +124,6 @@ protected void doAction() { List signers = CmsUtil.convertSignerInformations(signerInfos.getSigners(), trustedCerts, signedData.getCertificates()); - // 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 detached -// // 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()), signedData, signers, getTrustedCertsNoPrefs(), 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 0f6a26313..fa79cc7b7 100644 --- a/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java +++ b/kse/src/main/java/org/kse/gui/dialogs/DViewSignature.java @@ -68,6 +68,7 @@ import org.kse.KSE; import org.kse.crypto.CryptoException; import org.kse.crypto.digest.DigestType; +import org.kse.crypto.signing.CmsSignatureStatus; import org.kse.crypto.signing.CmsSigner; import org.kse.crypto.signing.CmsUtil; import org.kse.crypto.signing.KseSignerInformation; @@ -384,9 +385,18 @@ private void populateDetails() { Date signingTime = signerInfo.getSigningTime(); X509CertificateHolder cert = signerInfo.getCertificate(); - jtfStatus.setText(signerInfo.getStatus().getText()); + // Don't verify the signature if there is no signed content. CmsUtil.loadSignature already + // tried to find and load the detached content for verification purposes. + CmsSignatureStatus status; + if (signedData.getSignedContent() != null) { + status = signerInfo.getStatus(); + } else { + status = CmsSignatureStatus.NOT_VERIFIED; + } + + jtfStatus.setText(status.getText()); jtfStatus.setCaretPosition(0); - jtfStatus.setToolTipText(signerInfo.getStatus().getToolTip()); + jtfStatus.setToolTipText(status.getToolTip()); jtfVersion.setText(Integer.toString(signerInfo.getVersion())); jtfVersion.setCaretPosition(0); From 3dc3c8ee32b07ba767f1d4664f8ccbd99be9431e Mon Sep 17 00:00:00 2001 From: jonwltn <86822083+jonwltn@users.noreply.github.com> Date: Thu, 9 Jan 2025 21:18:55 -0800 Subject: [PATCH 69/69] Removed last remaining TODOs in VerifySignatureAction. --- .../main/java/org/kse/gui/actions/VerifySignatureAction.java | 4 +--- .../main/resources/org/kse/gui/actions/resources.properties | 1 + 2 files changed, 2 insertions(+), 3 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 098d5786f..07a56abed 100644 --- a/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java +++ b/kse/src/main/java/org/kse/gui/actions/VerifySignatureAction.java @@ -106,7 +106,6 @@ protected void doAction() { Set compCerts = new HashSet<>(); compCerts.addAll(extractCertificates(keyStore)); - // TODO JW make X509CertUtil.extractCertificates public and use it for cacerts and windowstrustedrootcerts if (caCertificates != null) { // Perform cert lookup against CA Certificates KeyStore compCerts.addAll(extractCertificates(caCertificates)); @@ -159,8 +158,7 @@ private Collection extractCertificates(KeyStore keystore) throw } } catch (KeyStoreException e) { - // TODO JW Auto-generated catch block - throw new CryptoException(res.getString("NoExtractCertificates.exception.message"), e); + throw new CryptoException(res.getString("VerifySignatureAction.NoExtractCertificates.message"), e); } return certs; 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 f49405a65..e19b28b88 100644 --- a/kse/src/main/resources/org/kse/gui/actions/resources.properties +++ b/kse/src/main/resources/org/kse/gui/actions/resources.properties @@ -693,6 +693,7 @@ VerifySignatureAction.ChooseContent.Title = Choose Content File VerifySignatureAction.ChooseContent.button = Select VerifySignatureAction.ChooseSignature.Title = Choose Signature File VerifySignatureAction.ChooseSignature.button = Verify +VerifySignatureAction.NoExtractCertificates.message = Could not extract certificates from KeyStore. VerifySignatureAction.NoSignatures.message = ''{0}'' does not have any signatures to verify VerifySignatureAction.VerifySignature.Title = Verify PKCS#7/CMS Signature VerifySignatureAction.statusbar = Verify a PKCS#7/CMS signature