diff options
author | Ed Merks | 2022-01-14 12:48:14 +0000 |
---|---|---|
committer | Ed Merks | 2022-01-15 04:56:31 +0000 |
commit | b40860241cf12625c3611f00649c5d1d88aeafa1 (patch) | |
tree | 7c10183b88aa56b3243ce67834caf65c665d1139 | |
parent | c193e5197535846f3f546f0e9ce210660c190484 (diff) | |
download | rt.equinox.p2-b40860241cf12625c3611f00649c5d1d88aeafa1.tar.gz rt.equinox.p2-b40860241cf12625c3611f00649c5d1d88aeafa1.tar.xz rt.equinox.p2-b40860241cf12625c3611f00649c5d1d88aeafa1.zip |
Bug 578024 - Improve the TrustCertificateDialog
Provide support for computing the verified certifications of all known
keys and use this to display the web-of-trust information in the trust
certificate dialog.
Also support "Copy Fingerprint" for PGP keys in the certificate chain
viewer.
Change-Id: I2039b585911def54cf888d91ab5bc6ee8606ddb2
Signed-off-by: Ed Merks <ed.merks@gmail.com>
Reviewed-on: https://git.eclipse.org/r/c/equinox/rt.equinox.p2/+/189634
Tested-by: Equinox Bot <equinox-bot@eclipse.org>
6 files changed, 124 insertions, 10 deletions
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF index 49e639b77..05dc70a3c 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF @@ -10,7 +10,7 @@ Export-Package: org.eclipse.equinox.internal.p2.artifact.processing;x-friends:=" org.eclipse.equinox.internal.p2.artifact.processors.checksum;x-friends:="org.eclipse.equinox.p2.publisher", org.eclipse.equinox.internal.p2.artifact.processors.md5;x-internal:=true, org.eclipse.equinox.internal.p2.artifact.processors.pack200;x-friends:="org.eclipse.equinox.p2.artifact.processors,org.eclipse.equinox.p2.artifact.optimizers", - org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk", + org.eclipse.equinox.internal.p2.artifact.processors.pgp;x-friends:="org.eclipse.equinox.p2.engine,org.eclipse.equinox.p2.ui.sdk,org.eclipse.equinox.p2.ui", org.eclipse.equinox.internal.p2.artifact.repository; x-friends:="org.eclipse.equinox.p2.publisher, org.eclipse.equinox.p2.reconciler.dropins, diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java index 5a06ec3b7..410214f8b 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/pgp/PGPSignatureVerifier.java @@ -47,6 +47,54 @@ public final class PGPSignatureVerifier extends ProcessingStep { link(nullOutputStream(), new NullProgressMonitor()); // this is convenience for tests } + public static Map<PGPPublicKey, Set<PGPPublicKey>> getVerifiedKnownKeyCertifications() { + Map<PGPPublicKey, Set<PGPPublicKey>> result = new LinkedHashMap<>(); + for (PGPPublicKey key : KNOWN_KEYS.all()) { + Set<PGPPublicKey> certifications = new LinkedHashSet<>(); + for (Iterator<PGPSignature> signatures = key.getSignatures(); signatures.hasNext();) { + PGPSignature signature = signatures.next(); + long signingKeyID = signature.getKeyID(); + PGPPublicKey signingKey = KNOWN_KEYS.getKey(signingKeyID); + if (signingKey != null) { + switch (signature.getSignatureType()) { + case PGPSignature.SUBKEY_BINDING: + case PGPSignature.PRIMARYKEY_BINDING: { + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(signingKey, key)) { + certifications.add(signingKey); + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + break; + } + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: { + for (Iterator<String> userIDs = key.getUserIDs(); userIDs.hasNext();) { + String userID = userIDs.next(); + try { + signature.init(new BcPGPContentVerifierBuilderProvider(), signingKey); + if (signature.verifyCertification(userID, key)) { + certifications.add(signingKey); + break; + } + } catch (PGPException e) { + //$FALL-THROUGH$ + } + } + break; + } + } + } + } + result.put(key, certifications); + } + return result; + } + public static Collection<PGPSignature> getSignatures(IArtifactDescriptor artifact) throws IOException, PGPException { String signatureText = unnormalizedPGPProperty(artifact.getProperty(PGP_SIGNATURES_PROPERTY_NAME)); diff --git a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF index 036afa9cb..9a353459c 100644 --- a/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui/META-INF/MANIFEST.MF @@ -39,6 +39,7 @@ Import-Package: javax.xml.parsers, org.bouncycastle.bcpg;version="1.65.0", org.bouncycastle.openpgp;version="1.65.0", org.bouncycastle.util;version="1.65.1", + org.eclipse.equinox.internal.p2.artifact.processors.pgp, org.eclipse.equinox.internal.p2.artifact.repository, org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.internal.p2.director, diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java index 482fe4844..e0dd88651 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ValidationDialogServiceUI.java @@ -21,6 +21,7 @@ import java.util.List; import org.bouncycastle.openpgp.PGPPublicKey; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.equinox.internal.p2.artifact.processors.pgp.PGPSignatureVerifier; import org.eclipse.equinox.internal.p2.ui.dialogs.TrustCertificateDialog; import org.eclipse.equinox.internal.p2.ui.dialogs.UserValidationDialog; import org.eclipse.equinox.p2.core.UIServices; @@ -208,13 +209,41 @@ public class ValidationDialogServiceUI extends UIServices { parent = node; } } - for (PGPPublicKey key : untrustedPublicKeys) { - children[i] = new TreeNode(key); - i++; + + if (!untrustedPublicKeys.isEmpty()) { + Map<PGPPublicKey, Set<PGPPublicKey>> verifiedKnownKeyCertifications = PGPSignatureVerifier + .getVerifiedKnownKeyCertifications(); + for (PGPPublicKey key : untrustedPublicKeys) { + children[i] = createTreeNode(key, verifiedKnownKeyCertifications, new HashSet<>()); + i++; + } } return children; } + private TreeNode createTreeNode(PGPPublicKey key, + Map<PGPPublicKey, Set<PGPPublicKey>> verifiedKnownKeyCertification, Set<PGPPublicKey> visited) { + if (visited.add(key)) { + TreeNode result = new TreeNode(key); + List<TreeNode> children = new ArrayList<>(); + Set<PGPPublicKey> visitedChildren = new LinkedHashSet<>(); + Set<PGPPublicKey> certifications = verifiedKnownKeyCertification.get(key); + if (certifications != null) { + for (PGPPublicKey certifyingKey : certifications) { + if (!visited.contains(certifyingKey) && visitedChildren.add(certifyingKey)) { + children.add(createTreeNode(certifyingKey, verifiedKnownKeyCertification, visited)); + } + } + } + if (!children.isEmpty()) { + result.setChildren(children.toArray(TreeNode[]::new)); + children.forEach(child -> child.setParent(result)); + } + return result; + } + return null; + } + @Override public AuthenticationInfo getUsernamePassword(final String location, final AuthenticationInfo previousInfo) { final AuthenticationInfo[] result = new AuthenticationInfo[1]; diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java index 23fcf9b35..fa4395143 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/TrustCertificateDialog.java @@ -133,8 +133,46 @@ public class TrustCertificateDialog extends SelectionDialog { certificateChainViewer.getTree().setLayoutData(data); certificateChainViewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); certificateChainViewer.setContentProvider(new TreeNodeContentProvider()); - certificateChainViewer.setLabelProvider(new CertificateLabelProvider()); + certificateChainViewer.setLabelProvider(new CertificateLabelProvider() { + @Override + public String getText(Object element) { + if (element instanceof TreeNode) { + Object o = ((TreeNode) element).getValue(); + if (o instanceof PGPPublicKey) { + PGPPublicKey key = (PGPPublicKey) o; + String userFriendlyFingerPrint = userFriendlyFingerPrint(key); + java.util.List<String> users = new ArrayList<>(); + key.getUserIDs().forEachRemaining(users::add); + String userIDs = String.join(",", users); //$NON-NLS-1$ + if (!userIDs.isEmpty()) { + return userFriendlyFingerPrint + " [" + userIDs + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + return userFriendlyFingerPrint; + } + } + return super.getText(element); + } + }); certificateChainViewer.addSelectionChangedListener(getChainSelectionListener()); + Menu menu = new Menu(certificateChainViewer.getTree()); + certificateChainViewer.getTree().setMenu(menu); + MenuItem item = new MenuItem(menu, SWT.PUSH); + item.setText(ProvUIMessages.TrustCertificateDialog_CopyFingerprint); + item.addSelectionListener(widgetSelectedAdapter(e -> { + Object o = ((IStructuredSelection) certificateChainViewer.getSelection()).getFirstElement(); + if (o instanceof TreeNode) { + o = ((TreeNode) o).getValue(); + } + if (o instanceof PGPPublicKey) { + PGPPublicKey key = (PGPPublicKey) o; + Clipboard clipboard = new Clipboard(getShell().getDisplay()); + clipboard.setContents(new Object[] { userFriendlyFingerPrint(key) }, + new Transfer[] { TextTransfer.getInstance() }); + clipboard.dispose(); + } + })); + certificateChainViewer.addSelectionChangedListener(e -> item.setEnabled(containsPGPKeys(e.getSelection()))); + listViewer.addDoubleClickListener(getDoubleClickListener()); listViewer.addSelectionChangedListener(getParentSelectionListener()); createButtons(composite); @@ -406,9 +444,7 @@ public class TrustCertificateDialog extends SelectionDialog { if (selection instanceof StructuredSelection) { TreeNode firstElement = (TreeNode) ((StructuredSelection) selection).getFirstElement(); getCertificateChainViewer().setInput(new TreeNode[] { firstElement }); - if (firstElement.getValue() instanceof X509Certificate) { - getCertificateChainViewer().setSelection(new StructuredSelection(firstElement)); - } + getCertificateChainViewer().setSelection(new StructuredSelection(firstElement)); Button okButton = getOkButton(); if (okButton != null) { okButton.setEnabled(listViewer.getCheckedElements().length > 0); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties index a26826e94..5a5fc0363 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties @@ -281,8 +281,8 @@ TrustCertificateDialog_Export=\uD83D\uDCE5 E&xport... TrustCertificateDialog_Title=Trust TrustCertificateDialog_Message=Do you trust these signers? TrustCertificateDialog_MessageWithPGP=Do you trust these signers?\n\ -For PGP keys, verifying user can usually be achieved by querying key fingerprint against some trusted keyserver. -TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust selected +For PGP keys, verification is typically achieved by querying the key\'s fingerprint against a trusted key server. +TrustCertificateDialog_AcceptSelectedButtonLabel=&Trust Selected TrustCertificateDialog_SelectAll=&Select All TrustCertificateDialog_DeselectAll=&Deselect All TrustCertificateDialog_ObjectType=Type |