Author: tilman
Date: Sun Aug 23 08:52:53 2026
New Revision: 1937339

Log:
PDFBOX-6239: show certificate details

Added:
   
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/
   
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
   (contents, props changed)
Modified:
   
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java

Modified: 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
==============================================================================
--- 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
      Sun Aug 23 08:52:48 2026        (r1937338)
+++ 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/PDFDebugger.java
      Sun Aug 23 08:52:53 2026        (r1937339)
@@ -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;
@@ -835,6 +836,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);
@@ -895,7 +908,61 @@ 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)
+    {
+        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, eg. 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/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ 
pdfbox/branches/3.0/debugger/src/main/java/org/apache/pdfbox/debugger/certificatepane/CertificatePane.java
  Sun Aug 23 08:52:53 2026        (r1937339)
@@ -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;
+    }
+}

Reply via email to