/*
* Copyright 2006-2017 ICEsoft Technologies Canada Corp.
*
* Licensed 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.icepdf.ri.common.views.annotations.signatures;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.icepdf.core.util.HexDumper;
import org.icepdf.ri.common.EscapeJDialog;
import org.icepdf.ri.common.utility.signatures.SignatureUtilities;
import org.icepdf.ri.images.Images;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.ResourceBundle;
/**
* CertificatePropertiesDialog takes a certificate chain and displays each certificate in a summery view. Certificates
* can be easily viewed and selected via a jTree component hierarchy.
*/
public class CertificatePropertiesDialog extends EscapeJDialog {
protected static ResourceBundle messageBundle;
private Collection<Certificate> certs;
public CertificatePropertiesDialog(Frame parent, ResourceBundle messageBundle, Collection<Certificate> certs) {
super(parent, true);
CertificatePropertiesDialog.messageBundle = messageBundle;
this.certs = certs;
buildUI();
}
public CertificatePropertiesDialog(JDialog parent, ResourceBundle messageBundle, Collection<Certificate> certs) {
super(parent, true);
CertificatePropertiesDialog.messageBundle = messageBundle;
this.certs = certs;
buildUI();
}
private void buildUI() {
setTitle(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.title"));
getContentPane().setLayout(new BorderLayout());
Certificate[] certArray = new Certificate[certs.size()];
int i = 0;
for (Certificate certificate : certs) {
certArray[i] = certificate;
i++;
}
getContentPane().add(getComponents(certArray),
BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.TRAILING));
JButton closeButton = new JButton(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.closeButton.label"));
closeButton.setMnemonic("viewer.utilityPane.signatures.cert.dialog.closeButton.mnemonic".charAt(0));
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
buttonPanel.add(closeButton);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
setSize(new Dimension(760, 450));
setLocationRelativeTo(getParent());
setResizable(true);
}
/**
* builds out the dialog components, mainly the tree and info panel.
*/
private JComponent getComponents(Certificate[] certificateChain) {
if (certificateChain.length > 0) {
final JTable certificateInfoTable = new JTable();
final JTextArea propteryValueTextAea = new JTextArea();
// Build certificate chain into a tree hierarchy.
final JTree certChainTree = buildCertChainTree(certificateChain);
certChainTree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) certChainTree.getLastSelectedPathComponent();
if (node != null) {
CertificateInfo certInfo = (CertificateInfo) node.getUserObject();
// Show certificate in the cert info panel
showCertificateInfo(certInfo.getCertificate(), certificateInfoTable, propteryValueTextAea);
}
}
});
// Build certificate info table
showCertificateInfo((X509Certificate) certificateChain[0], certificateInfoTable, propteryValueTextAea);
certificateInfoTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
ListSelectionModel selectionModel = certificateInfoTable.getSelectionModel();
selectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int row = certificateInfoTable.getSelectedRow();
if (row >= 0) {
String value = (String) certificateInfoTable.getValueAt(row, 1);
// Update text area when selection changes
propteryValueTextAea.setText(value);
propteryValueTextAea.repaint();
}
}
});
// main properties view.
propteryValueTextAea.setLineWrap(false);
propteryValueTextAea.setEditable(false);
propteryValueTextAea.setRows(10);
propteryValueTextAea.setColumns(40);
// Get font from ResourceManager, and create new font
Font fixedWidthFont = new java.awt.Font("Monospaced", java.awt.Font.PLAIN, 12);
propteryValueTextAea.setFont(fixedWidthFont);
// Select last row by default
certificateInfoTable.setRowSelectionInterval(8, 8);
// Create cert info panel
JScrollPane scrollPane = new JScrollPane(certificateInfoTable);
JSplitPane panelInfo = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
panelInfo.setDividerLocation(175);
panelInfo.setTopComponent(scrollPane);
panelInfo.setBottomComponent(new JScrollPane(propteryValueTextAea));
JSplitPane panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
panel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(5, 5, 5, 5),
scrollPane.getBorder()));
panel.setDividerLocation(200);
scrollPane = new JScrollPane(certChainTree);
panel.setLeftComponent(scrollPane);
panel.setRightComponent(panelInfo);
return panel;
}
return new JPanel();
}
/**
* Break down DN string into an array used for message format.
* Organization: {0}\n Organization Unit :{1}\n Common Name: {2}\n Local: {3}\n State: {4}\n Country:{5}\n Email: {6}
*/
private Object[] formatDNString(X500Name rdName) {
Object[] output = new Object[7];
output[0] = parseRelativeDistinguishedName(rdName, BCStyle.O);
output[1] = parseRelativeDistinguishedName(rdName, BCStyle.OU);
output[2] = parseRelativeDistinguishedName(rdName, BCStyle.CN);
output[3] = parseRelativeDistinguishedName(rdName, BCStyle.L);
output[4] = parseRelativeDistinguishedName(rdName, BCStyle.ST);
output[5] = parseRelativeDistinguishedName(rdName, BCStyle.C);
output[6] = parseRelativeDistinguishedName(rdName, BCStyle.EmailAddress);
return output;
}
protected static String parseRelativeDistinguishedName(X500Name rdName, ASN1ObjectIdentifier commonCode) {
String rdn = SignatureUtilities.parseRelativeDistinguishedName(rdName, commonCode);
if (rdn != null) {
return rdn;
}
return messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.notAvailable.label");
}
/**
* Method to reflect certificate chain in the tree view
*/
private JTree buildCertChainTree(Certificate cert[]) {
DefaultMutableTreeNode root = null;
DefaultMutableTreeNode currentNode = null;
for (Certificate aCert : cert) {
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
new CertificateInfo((X509Certificate) aCert, messageBundle));
if (root == null) {
root = childNode;
currentNode = childNode;
} else {
currentNode.add(childNode);
currentNode = childNode;
}
}
JTree tree = new JTree(root);
// Disable HTML to disable anchor click out.
DefaultTreeCellRenderer customCellRenderer = new DefaultTreeCellRenderer();
customCellRenderer.putClientProperty("html.disable", Boolean.TRUE);
customCellRenderer.setOpenIcon(new ImageIcon(Images.get("page.gif")));
customCellRenderer.setClosedIcon(new ImageIcon(Images.get("page.gif")));
customCellRenderer.setLeafIcon(new ImageIcon(Images.get("page.gif")));
tree.setCellRenderer(customCellRenderer);
// Allow single node selection only
tree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setRootVisible(true);
tree.setShowsRootHandles(true);
tree.setScrollsOnExpand(true);
return tree;
}
/**
* Converts a byte to hex digit and writes to the supplied buffer
*/
private void byte2hex(byte b, StringBuffer buf) {
char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F'};
int high = ((b & 0xf0) >> 4);
int low = (b & 0x0f);
buf.append(hexChars[high]);
buf.append(hexChars[low]);
}
/**
* Converts a byte array to hex string
*/
private String toHexString(byte[] block) {
StringBuffer buf = new StringBuffer();
int len = block.length;
for (int i = 0; i < len; i++) {
byte2hex(block[i], buf);
if (i < len - 1) {
buf.append(":");
}
}
return buf.toString();
}
/**
* Gets the requested finger print of the certificate.
*/
private String getCertFingerPrint(String mdAlg, X509Certificate cert)
throws Exception {
byte[] encCertInfo = cert.getEncoded();
MessageDigest md = MessageDigest.getInstance(mdAlg);
byte[] digest = md.digest(encCertInfo);
return toHexString(digest);
}
/**
* Method to reflect table data based on the certificate
*/
private void showCertificateInfo(X509Certificate cert,
JTable certInfoTable, JTextArea textArea) {
MessageFormat formatter = new MessageFormat(messageBundle.getString(
"viewer.utilityPane.signatures.cert.dialog.info.version.value"));
String certVersion = formatter.format(new Object[]{String.valueOf(cert.getVersion())});
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.serialNumber.value"));
String serialNumber = formatter.format(new Object[]{String.valueOf(cert.getSerialNumber())});
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.signatureAlgorithm.value"));
String signatureAlgorithm = formatter.format(new Object[]{cert.getSigAlgName()});
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.issuer.value"));
String issuer = formatter.format(formatDNString(new X500Name(cert.getIssuerDN().toString())));
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.validity.value"));
String validity = formatter.format(new Object[]{cert.getNotBefore(), cert.getNotAfter()});
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.subject.value"));
String subject = formatter.format(formatDNString(new X500Name(cert.getSubjectDN().toString())));
String signature = new HexDumper().dump(cert.getSignature());
String md5 = null;
String sha1 = null;
try {
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.md5.value"));
md5 = formatter.format(new Object[]{getCertFingerPrint("MD5", cert)});
formatter.applyPattern(messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.sha1.value"));
sha1 = formatter.format(new Object[]{getCertFingerPrint("SHA1", cert)});
} catch (Throwable e) {
// eat any errors.
}
Object[][] data = {
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.version.label"), certVersion},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.serialNumber.label"), serialNumber},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.signatureAlgorithm.label"), signatureAlgorithm},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.issuer.label"), issuer},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.validity.label"), validity},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.subject.label"), subject},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.signature.label"), signature},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.md5.label"), md5},
{messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.sha1.label"), sha1}};
String[] columnNames = {
messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.column1.label"),
messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.column2.label")};
certInfoTable.setModel(new DefaultTableModel(data, columnNames) {
public boolean isCellEditable(int row, int col) {
return false;
}
});
// Select last row by default
certInfoTable.setRowSelectionInterval(8, 8);
certInfoTable.repaint();
textArea.repaint();
}
}
class CertificateInfo {
private X509Certificate cert;
private ResourceBundle messageBundle;
CertificateInfo(X509Certificate cert, ResourceBundle messageBundle) {
this.cert = cert;
this.messageBundle = messageBundle;
}
public X509Certificate getCertificate() {
return cert;
}
/**
* Extrace CN from DN in the certificate.
*
* @param cert X509 certificate
* @return CN
*/
private String extractAliasName(X509Certificate cert) {
String subjectName = messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.unknownSubject.label");
String issuerName = messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.unknownIssuer.label");
// Extract CN from the DN for each certificate
try {
X500Name principal = new X500Name(cert.getSubjectDN().toString());
X500Name principalIssuer = new X500Name(cert.getIssuerDN().toString());
// Extract subject name
subjectName = CertificatePropertiesDialog.parseRelativeDistinguishedName(principal, BCStyle.CN);
if (subjectName == null) {
subjectName = CertificatePropertiesDialog.parseRelativeDistinguishedName(principal, BCStyle.O);
}
if (subjectName == null) {
subjectName = messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.unknownSubject.label");
}
// Extract issuer name
issuerName = CertificatePropertiesDialog.parseRelativeDistinguishedName(principalIssuer, BCStyle.CN);
if (issuerName == null) {
issuerName = CertificatePropertiesDialog.parseRelativeDistinguishedName(principalIssuer, BCStyle.O);
}
if (issuerName == null) {
issuerName = messageBundle.getString("viewer.utilityPane.signatures.cert.dialog.info.unknownIssuer.label");
}
} catch (Exception e) {
e.printStackTrace();
}
// Add Subject name and Issuer name in the return string
MessageFormat messageFormat = new MessageFormat(messageBundle.getString(
"viewer.utilityPane.signatures.cert.dialog.info.certificateInfo.label"));
Object[] args = {subjectName, issuerName};
return messageFormat.format(args);
}
public String toString() {
return extractAliasName(cert);
}
}