gui: show app certificate (#305)
* add node * add node * add certificate panel * add certifcate manager * работа над проектом * ресурсы * включение возможности показа * небольшие исправления * start tests * more tests * signature test * fingerprint test * public key test * удалено лишний код * add scroll
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package jadx.gui.treemodel;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.gui.utils.CertificateManager;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.Utils;
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class JCertificate extends JNode {
|
||||
|
||||
private static final ImageIcon CERTIFICATE_ICON = Utils.openIcon("certificate_obj");
|
||||
private final transient ResourceFile rf;
|
||||
|
||||
public JCertificate(ResourceFile resFile) {
|
||||
this.rf = resFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClass getJParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return CERTIFICATE_ICON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeString() {
|
||||
return NLS.str("certificate.title");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContent() {
|
||||
|
||||
try {
|
||||
ResourceFile.ZipRef zipRef = rf.getZipRef();
|
||||
if (zipRef == null) {
|
||||
File file = new File(rf.getName());
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return CertificateManager.decode(inputStream);
|
||||
}
|
||||
} else {
|
||||
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
|
||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||
if (entry == null) {
|
||||
throw new IOException("Zip entry not found: " + zipRef);
|
||||
}
|
||||
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||
return null;
|
||||
}
|
||||
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
|
||||
return CertificateManager.decode(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
// throw new JadxException("Error decode: " + rf.getName(), e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -36,6 +36,11 @@ public class JRoot extends JNode {
|
||||
jRes.update();
|
||||
add(jRes);
|
||||
}
|
||||
|
||||
JCertificate certificate = getCertificate(wrapper.getResources());
|
||||
if(certificate != null) {
|
||||
add(certificate);
|
||||
}
|
||||
}
|
||||
|
||||
private List<JResource> getHierarchyResources(List<ResourceFile> resources) {
|
||||
@@ -71,6 +76,25 @@ public class JRoot extends JNode {
|
||||
return Collections.singletonList(root);
|
||||
}
|
||||
|
||||
private JCertificate getCertificate(List<ResourceFile> resources) {
|
||||
if (resources.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
for (ResourceFile rf : resources) {
|
||||
|
||||
if (rf.getZipRef() != null) {
|
||||
String rfName = rf.getName();
|
||||
if (rfName.contains("/CERT.DSA")||rfName.contains("/CERT.RSA")) {
|
||||
return new JCertificate(rf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private JResource getResourceByName(JResource rf, String name) {
|
||||
for (JResource sub : rf.getFiles()) {
|
||||
if (sub.getName().equals(name)) {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package jadx.gui.ui;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class CertificatePanel extends ContentPanel {
|
||||
CertificatePanel(TabbedPane panel, JNode jnode) {
|
||||
super(panel, jnode);
|
||||
setLayout(new BorderLayout());
|
||||
JTextArea textArea = new JTextArea(jnode.getContent());
|
||||
textArea.setFont(textArea.getFont().deriveFont(12f)); // will only change size to 12pt
|
||||
JScrollPane sp = new JScrollPane(textArea);
|
||||
add(sp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSettings() {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import java.util.Arrays;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import jadx.gui.treemodel.*;
|
||||
import org.fife.ui.rsyntaxtextarea.Theme;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -39,11 +40,6 @@ import jadx.gui.jobs.DecompileJob;
|
||||
import jadx.gui.jobs.IndexJob;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.JadxSettingsWindow;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JLoadableNode;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.treemodel.JRoot;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
import jadx.gui.update.JadxUpdate.IUpdateCallback;
|
||||
import jadx.gui.update.data.Release;
|
||||
@@ -292,7 +288,11 @@ public class MainWindow extends JFrame {
|
||||
if (resFile != null && JResource.isSupportedForView(resFile.getType())) {
|
||||
tabbedPane.showResource(res);
|
||||
}
|
||||
} else if (obj instanceof JNode) {
|
||||
}else if (obj instanceof JCertificate) {
|
||||
JCertificate cert = (JCertificate) obj;
|
||||
tabbedPane.showCertificate(cert);
|
||||
}
|
||||
else if (obj instanceof JNode) {
|
||||
JNode node = (JNode) obj;
|
||||
JClass cls = node.getRootClass();
|
||||
if (cls != null) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jadx.gui.treemodel.JCertificate;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -103,6 +104,20 @@ class TabbedPane extends JTabbedPane {
|
||||
});
|
||||
}
|
||||
|
||||
public void showCertificate(JCertificate cert) {
|
||||
final ContentPanel contentPanel = getContentPanel(cert);
|
||||
if (contentPanel == null) {
|
||||
return;
|
||||
}
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setSelectedComponent(contentPanel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void codeJump(Position pos) {
|
||||
Position curPos = getCurrentPosition();
|
||||
if (curPos != null) {
|
||||
@@ -172,6 +187,11 @@ class TabbedPane extends JTabbedPane {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if(node instanceof JCertificate)
|
||||
{
|
||||
return new CertificatePanel(this,node);
|
||||
}
|
||||
|
||||
return new CodePanel(this, node);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package jadx.gui.utils;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Principal;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.DSAPublicKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.Collection;
|
||||
|
||||
public class CertificateManager {
|
||||
|
||||
static public String decode(InputStream in){
|
||||
StringBuilder strBuild = new StringBuilder();
|
||||
Collection<? extends Certificate> certificates = readCertificates(in);
|
||||
if(certificates!=null) {
|
||||
for (Certificate cert : certificates) {
|
||||
CertificateManager certificateManager= new CertificateManager(cert);
|
||||
strBuild.append(certificateManager.generateText());
|
||||
}
|
||||
}
|
||||
return strBuild.toString();
|
||||
}
|
||||
|
||||
|
||||
static Collection<? extends Certificate> readCertificates(InputStream in) {
|
||||
CertificateFactory cf;
|
||||
try {
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
Collection<? extends Certificate> certs = cf.generateCertificates(in);
|
||||
in.close();
|
||||
return certs;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private X509Certificate x509cert;
|
||||
private Certificate cert;
|
||||
|
||||
public CertificateManager(Certificate cert)
|
||||
{
|
||||
this.cert = cert;
|
||||
String type = cert.getType();
|
||||
if (type.equals("X.509")) {
|
||||
if (cert instanceof X509Certificate) {
|
||||
x509cert = (X509Certificate) cert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String generateHeader()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(builder, NLS.str("certificate.cert_type"), x509cert.getType());
|
||||
append(builder, NLS.str("certificate.serialSigVer"),((Integer) x509cert.getVersion()).toString());
|
||||
// seral number
|
||||
append(builder, NLS.str("certificate.serialNumber"), "0x" + x509cert.getSerialNumber().toString(16));
|
||||
|
||||
// Get subject
|
||||
Principal subjectDN = x509cert.getSubjectDN();
|
||||
append(builder, NLS.str("certificate.cert_subject"), subjectDN.getName());
|
||||
|
||||
// Get issuer
|
||||
// Principal issuerDN = x509cert.getIssuerDN();
|
||||
// append(str, NLS.str("certificate.cert_issuer"), issuerDN.getName());
|
||||
|
||||
append(builder, NLS.str("certificate.serialValidFrom"), x509cert.getNotBefore().toString());
|
||||
append(builder, NLS.str("certificate.serialValidUntil"), x509cert.getNotAfter().toString());
|
||||
return builder.toString();
|
||||
|
||||
}
|
||||
|
||||
String generateSignature()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(builder, NLS.str("certificate.serialSigType"), x509cert.getSigAlgName());
|
||||
append(builder, NLS.str("certificate.serialSigOID"), x509cert.getSigAlgOID());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
String generateFingerprint()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
append(builder, NLS.str("certificate.serialMD5"), getThumbPrint(x509cert, "MD5"));
|
||||
append(builder, NLS.str("certificate.serialSHA1"), getThumbPrint(x509cert, "SHA-1"));
|
||||
append(builder, NLS.str("certificate.serialSHA256"), getThumbPrint(x509cert, "SHA-256"));
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
} catch (CertificateEncodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
String generatePublicKey()
|
||||
{
|
||||
PublicKey publicKey = x509cert.getPublicKey();
|
||||
if(publicKey instanceof RSAPublicKey)
|
||||
{
|
||||
return generateRSAPublicKey();
|
||||
}
|
||||
if(publicKey instanceof DSAPublicKey)
|
||||
{
|
||||
return generateDSAPublicKey();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
String generateRSAPublicKey()
|
||||
{
|
||||
RSAPublicKey pub = (RSAPublicKey) cert.getPublicKey();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
append(builder, NLS.str("certificate.serialPubKeyType"), pub.getAlgorithm());
|
||||
append(builder, NLS.str("certificate.serialPubKeyExponent"), pub.getPublicExponent().toString(10));
|
||||
append(builder, NLS.str("certificate.serialPubKeyModulus"), pub.getModulus().toString(10));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
String generateDSAPublicKey()
|
||||
{
|
||||
DSAPublicKey pub = (DSAPublicKey) cert.getPublicKey();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
append(builder, NLS.str("certificate.serialPubKeyType"), pub.getAlgorithm());
|
||||
append(builder, NLS.str("certificate.serialPubKeyY"), pub.getY().toString(10));
|
||||
|
||||
return builder.toString();
|
||||
|
||||
}
|
||||
|
||||
String generateTextForX509()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if(x509cert!=null){
|
||||
builder.append(generateHeader());
|
||||
builder.append("\n");
|
||||
|
||||
builder.append(generatePublicKey());
|
||||
builder.append("\n");
|
||||
|
||||
builder.append(generateSignature());
|
||||
builder.append("\n");
|
||||
builder.append(generateFingerprint());
|
||||
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String generateText() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
String type = cert.getType();
|
||||
if (!type.equals("X.509")) {
|
||||
str.append(cert.toString());
|
||||
} else {
|
||||
str.append(generateTextForX509());
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void append(StringBuilder str, String name, String value) {
|
||||
str.append(name + ": " + value + "\n");
|
||||
}
|
||||
|
||||
public static String getThumbPrint(X509Certificate cert, String type)
|
||||
throws NoSuchAlgorithmException, CertificateEncodingException {
|
||||
MessageDigest md = MessageDigest.getInstance(type);
|
||||
byte[] der = cert.getEncoded();
|
||||
md.update(der);
|
||||
byte[] digest = md.digest();
|
||||
return hexify(digest);
|
||||
|
||||
}
|
||||
|
||||
public static String hexify(byte bytes[]) {
|
||||
|
||||
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
StringBuffer buf = new StringBuffer(bytes.length * 3);
|
||||
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
|
||||
buf.append(hexDigits[bytes[i] & 0x0f]);
|
||||
buf.append(' ');
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,3 +101,22 @@ popup.copy=Copy
|
||||
popup.paste=Paste
|
||||
popup.delete=Delete
|
||||
popup.select_all=Select All
|
||||
|
||||
certificate.title=Certificate
|
||||
certificate.cert_type=Type
|
||||
certificate.cert_subject=Subject
|
||||
certificate.cert_issuer=Issuer
|
||||
certificate.serialNumber=Serial number
|
||||
certificate.serialValidFrom=Valid from
|
||||
certificate.serialValidUntil=Valid until
|
||||
certificate.serialSigVer=Version
|
||||
certificate.serialSigType=Signature type
|
||||
certificate.serialSigOID=Signature OID
|
||||
certificate.serialMD5=MD5 Fingerprint
|
||||
certificate.serialSHA1=SHA-1 Fingerprint
|
||||
certificate.serialSHA256=SHA-256 Fingerprint
|
||||
certificate.serialPubKeyType=Public key type
|
||||
certificate.serialPubKeyExponent=Exponent
|
||||
certificate.serialPubKeyModulus=Modulus
|
||||
certificate.serialPubKeyY=Y
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 569 B |
@@ -0,0 +1,112 @@
|
||||
package jadx.gui.utils;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
public class CertificateManagerTest {
|
||||
private static final String DSA = "CERT.DSA";
|
||||
private static final String RSA = "CERT.RSA";
|
||||
private static final String EMPTY = "EMPTY.txt";
|
||||
private String emptyPath;
|
||||
CertificateManager certificateManagerRSA;
|
||||
CertificateManager certificateManagerDSA;
|
||||
|
||||
private CertificateManager getCertificateManger(String resName)
|
||||
{
|
||||
String sertPath = getClass().getClassLoader().getResource(resName).getPath();
|
||||
try (InputStream in = new FileInputStream(sertPath)) {
|
||||
Collection<? extends Certificate> certificates = CertificateManager.readCertificates(in);
|
||||
Certificate cert = (Certificate)certificates.toArray()[0];
|
||||
return new CertificateManager(cert);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
emptyPath = getClass().getClassLoader().getResource(EMPTY).getPath();
|
||||
certificateManagerRSA = getCertificateManger(RSA);
|
||||
certificateManagerDSA = getCertificateManger(DSA);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void decodeNotCertificateFile() {
|
||||
try (InputStream in = new FileInputStream(emptyPath)) {
|
||||
String result = CertificateManager.decode(in);
|
||||
Assert.assertEquals(result, "");
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeRSAKeyHeader() {
|
||||
String string = certificateManagerRSA.generateHeader();
|
||||
Assert.assertTrue(string.contains("X.509"));
|
||||
Assert.assertTrue(string.contains("0x4bd68052"));
|
||||
Assert.assertTrue(string.contains("CN=test cert, OU=test unit, O=OOO TestOrg, L=St.Peterburg, ST=Russia, C=123456"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeDSAKeyHeader() {
|
||||
String string = certificateManagerDSA.generateHeader();
|
||||
Assert.assertTrue(string.contains("X.509"));
|
||||
Assert.assertTrue(string.contains("0x16420ba2"));
|
||||
Assert.assertTrue(string.contains("O=\"UJMRFVV CN=EDCVBGT C=TG\""));
|
||||
|
||||
}
|
||||
@Test
|
||||
public void decodeRSAKeySignature() {
|
||||
String string = certificateManagerRSA.generateSignature();
|
||||
Assert.assertTrue(string.contains("SHA256withRSA"));
|
||||
Assert.assertTrue(string.contains("1.2.840.113549.1.1.11"));
|
||||
}
|
||||
@Test
|
||||
public void decodeDSAKeySignature() {
|
||||
String string = certificateManagerDSA.generateSignature();
|
||||
Assert.assertTrue(string.contains("SHA1withDSA"));
|
||||
Assert.assertTrue(string.contains("1.2.840.10040.4.3"));
|
||||
}
|
||||
@Test
|
||||
public void decodeRSAFingerprint() {
|
||||
String string = certificateManagerRSA.generateFingerprint();
|
||||
Assert.assertTrue(string.contains("61 18 0A 71 3F C9 55 16 4E 04 E3 C5 45 08 D9 11"));
|
||||
Assert.assertTrue(string.contains("A0 6E A6 06 DB 2C 6F 3A 16 56 7F 75 97 7B AE 85 C2 13 09 37"));
|
||||
Assert.assertTrue(string.contains("12 53 E8 BB C8 AA 27 A8 49 9B F8 0D 6E 68 CE 32 35 50 DE 55 A7 E7 8C 29 51 00 96 D7 56 F4 54 44"));
|
||||
}
|
||||
@Test
|
||||
public void decodeDSAFingerprint() {
|
||||
String string = certificateManagerDSA.generateFingerprint();
|
||||
Assert.assertTrue(string.contains("D9 06 A6 2D 1F 79 8C 9D A6 EF 40 C7 2E C2 EA 0B"));
|
||||
Assert.assertTrue(string.contains("18 E9 9C D4 A1 40 8F 63 FA EC 2E 62 A0 F2 AE B7 3F C3 C2 04"));
|
||||
Assert.assertTrue(string.contains("74 F9 48 64 EE AC 92 26 53 2C 7A 0E 55 BE 5E D8 2F A7 D9 A9 99 F5 D5 21 2C 51 21 C4 31 AD 73 40"));
|
||||
}
|
||||
@Test
|
||||
public void decodeRSAPubKey() {
|
||||
String string = certificateManagerRSA.generatePublicKey();
|
||||
Assert.assertTrue(string.contains("RSA"));
|
||||
Assert.assertTrue(string.contains("65537"));
|
||||
Assert.assertTrue(string.contains("16819531290318044625546437357099080306019392752925688951114880688329201213180109168890384305768067101521914473763638669503560977521269328582980060332888147680193318231260043189411794465899645633586173494259691101582064441956032924396850221679489313043628562082670183392670094163371858684118480409374749790551473773845213427476236147328434427272177623018935282929152308753854314219987617604037468769472089902090243358285991739642170211970862773121939911777280101937073243006335384636193260583579409760790138329893534549366882523130765297472656435892831796545149793228897111760122091442123535919361963075454640516520743"));
|
||||
}
|
||||
@Test
|
||||
public void decodeDSAPubKey() {
|
||||
String string = certificateManagerDSA.generatePublicKey();
|
||||
Assert.assertTrue(string.contains("DSA"));
|
||||
Assert.assertTrue(string.contains("19323367605058154682563301282345453222279312104889899001698209626254725581511375469963812461090495963838615773832867364330457010553974237985991904800958394169421485070378434746792379708805563793253282995274293621162504943287538455944652344378242226897507369146942411692220922477368782490423187845815262510366"));
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
test
|
||||
Reference in New Issue
Block a user