Author: tilman
Date: Sun Aug 23 08:52:59 2026
New Revision: 1937340
Log:
PDFBOX-6239: show certificate details
Added:
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
(contents, props changed)
Modified:
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
Modified:
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
==============================================================================
---
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
Sun Aug 23 08:52:53 2026 (r1937339)
+++
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
Sun Aug 23 08:52:59 2026 (r1937340)
@@ -99,6 +99,7 @@ import org.apache.pdfbox.cos.COSNull;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.cos.COSStream;
import org.apache.pdfbox.cos.COSString;
+import org.apache.pdfbox.debugger.certificatepane.CertificatePane;
import org.apache.pdfbox.debugger.colorpane.CSArrayBased;
import org.apache.pdfbox.debugger.colorpane.CSDeviceN;
import org.apache.pdfbox.debugger.colorpane.CSIndexed;
@@ -836,6 +837,18 @@ public class PDFDebugger extends JFrame
showFlagPane(parentNode, selectedNode);
return;
}
+ if (path.getParentPath() != null &&
+ isCertificateStream(selectedNode,
path.getParentPath().getLastPathComponent()))
+ {
+ showCertificatePane(selectedNode);
+ return;
+ }
+ if (path.getParentPath() != null &&
+ isCertificateString(selectedNode,
path.getParentPath().getLastPathComponent()))
+ {
+ showCertificatePane(selectedNode);
+ return;
+ }
if (isStream(selectedNode))
{
showStream((COSStream) getUnderneathObject(selectedNode),
path);
@@ -896,7 +909,62 @@ public class PDFDebugger extends JFrame
COSName type = sigDict.getCOSName(COSName.TYPE);
if (type != null && type.equals(COSName.SIG))
{
- LOG.info("Found signature contents entry");
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private void showCertificatePane(Object selectedNode)
+ {
+ COSBase base = getUnderneathObject(selectedNode);
+ if (base instanceof COSStream)
+ {
+ replaceRightComponent(new CertificatePane((COSStream)
base).getPane());
+ }
+ if (base instanceof COSString)
+ {
+ replaceRightComponent(new CertificatePane((COSString)
base).getPane());
+ }
+ }
+
+ private boolean isCertificateStream(Object selectedNode, Object parentNode)
+ {
+ // e.g. in DSS structures
+ if (selectedNode instanceof ArrayEntry)
+ {
+ ArrayEntry entry = (ArrayEntry) selectedNode;
+ if (entry.getValue() instanceof COSStream && parentNode instanceof
MapEntry)
+ {
+ MapEntry mapEntry = (MapEntry) parentNode;
+ if ((mapEntry.getKey().equals(COSName.CERT) ||
+ mapEntry.getKey().equals(COSName.CERTS)))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isCertificateString(Object selectedNode, Object parentNode)
+ {
+ // adbe.x509.rsa_sha1 signature, e.g. in PDFBOX-2693
+ if (selectedNode instanceof MapEntry)
+ {
+ MapEntry entry = (MapEntry) selectedNode;
+ if (entry.getKey().equals(COSName.CERT) && parentNode instanceof
MapEntry)
+ {
+ MapEntry mapEntry = (MapEntry) parentNode;
+ COSDictionary sigDict;
+ if (mapEntry.getValue() instanceof COSDictionary)
+ {
+ sigDict = (COSDictionary) mapEntry.getValue();
+ COSName type = sigDict.getCOSName(COSName.TYPE);
+ if (type != null && type.equals(COSName.SIG))
+ {
return true;
}
}
Added:
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++
pdfbox/trunk/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
Sun Aug 23 08:52:59 2026 (r1937340)
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.debugger.certificatepane;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import javax.swing.JScrollPane;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextPane;
+import javax.swing.UIManager;
+import org.apache.pdfbox.cos.COSBase;
+import org.apache.pdfbox.cos.COSStream;
+import org.apache.pdfbox.cos.COSString;
+
+/**
+ * For displaying the Contents of a certificate.
+ *
+ * @author Tilman Hausherr
+ */
+public class CertificatePane
+{
+ private static final String TEXT_TAB = "Certificate View";
+ private static final int FONT_SIZE =
((Font)UIManager.get("Label.font")).getSize();
+ private static final Font FONT_MONOSPACED = new Font("monospaced",
Font.PLAIN, FONT_SIZE);
+
+ private final JTabbedPane tabbedPane;
+
+ public CertificatePane(COSBase base)
+ {
+ tabbedPane = new JTabbedPane();
+ tabbedPane.setPreferredSize(new Dimension(300, 500));
+ tabbedPane.addTab(TEXT_TAB, new JScrollPane(createTextView(base)));
+ }
+
+ private JTextPane createTextView(COSBase base)
+ {
+ JTextPane textPane = new JTextPane();
+ textPane.setText(getTextString(base));
+ textPane.setEditable(false);
+ textPane.setFont(FONT_MONOSPACED);
+ textPane.setCaretPosition(0);
+ return textPane;
+ }
+
+ private String getTextString(COSBase base)
+ {
+ try (InputStream is = createInputStream(base))
+ {
+ return
CertificateFactory.getInstance("X.509").generateCertificate(is).toString();
+ }
+ catch (CertificateException | IOException ex)
+ {
+ return ex.getMessage();
+ }
+ }
+
+ private InputStream createInputStream(COSBase base) throws IOException
+ {
+ if (base instanceof COSStream)
+ {
+ return ((COSStream) base).createInputStream();
+ }
+ if (base instanceof COSString)
+ {
+ return new ByteArrayInputStream(((COSString) base).getBytes());
+ }
+ throw new IllegalArgumentException("COSString or COSStream expected
here");
+ }
+
+ public JTabbedPane getPane()
+ {
+ return tabbedPane;
+ }
+}