From 03a09debfad5fe6074878bd5d65bf4c217718261 Mon Sep 17 00:00:00 2001 From: asviridenko Date: Thu, 5 Jul 2018 12:40:27 +0300 Subject: [PATCH] gui: show app certificate (#305) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add node * add node * add certificate panel * add certifcate manager * работа над проектом * ресурсы * включение возможности показа * небольшие исправления * start tests * more tests * signature test * fingerprint test * public key test * удалено лишний код * add scroll --- .../java/jadx/gui/treemodel/JCertificate.java | 69 ++++++ .../main/java/jadx/gui/treemodel/JRoot.java | 24 ++ .../java/jadx/gui/ui/CertificatePanel.java | 23 ++ .../src/main/java/jadx/gui/ui/MainWindow.java | 12 +- .../src/main/java/jadx/gui/ui/TabbedPane.java | 20 ++ .../jadx/gui/utils/CertificateManager.java | 207 ++++++++++++++++++ .../resources/i18n/Messages_en_US.properties | 19 ++ .../resources/icons-16/certificate_obj.png | Bin 0 -> 569 bytes .../gui/utils/CertificateManagerTest.java | 112 ++++++++++ jadx-gui/src/test/resources/CERT.DSA | Bin 0 -> 855 bytes jadx-gui/src/test/resources/CERT.RSA | Bin 0 -> 1395 bytes jadx-gui/src/test/resources/EMPTY.txt | 1 + 12 files changed, 481 insertions(+), 6 deletions(-) create mode 100644 jadx-gui/src/main/java/jadx/gui/treemodel/JCertificate.java create mode 100644 jadx-gui/src/main/java/jadx/gui/ui/CertificatePanel.java create mode 100755 jadx-gui/src/main/java/jadx/gui/utils/CertificateManager.java create mode 100644 jadx-gui/src/main/resources/icons-16/certificate_obj.png create mode 100644 jadx-gui/src/test/java/jadx/gui/utils/CertificateManagerTest.java create mode 100644 jadx-gui/src/test/resources/CERT.DSA create mode 100644 jadx-gui/src/test/resources/CERT.RSA create mode 100644 jadx-gui/src/test/resources/EMPTY.txt diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JCertificate.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JCertificate.java new file mode 100644 index 000000000..3ec10f6ab --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JCertificate.java @@ -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; + } + + +} diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java index 05fb9c114..b6118f96d 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java @@ -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 getHierarchyResources(List resources) { @@ -71,6 +76,25 @@ public class JRoot extends JNode { return Collections.singletonList(root); } + private JCertificate getCertificate(List 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)) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/CertificatePanel.java b/jadx-gui/src/main/java/jadx/gui/ui/CertificatePanel.java new file mode 100644 index 000000000..c6bda9697 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/ui/CertificatePanel.java @@ -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() { + + + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index 5bccfc82f..2a761daa1 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -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) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java index d9ccaa3ac..fd9923ec0 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/TabbedPane.java @@ -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); } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/CertificateManager.java b/jadx-gui/src/main/java/jadx/gui/utils/CertificateManager.java new file mode 100755 index 000000000..394cb8b02 --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/CertificateManager.java @@ -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 certificates = readCertificates(in); + if(certificates!=null) { + for (Certificate cert : certificates) { + CertificateManager certificateManager= new CertificateManager(cert); + strBuild.append(certificateManager.generateText()); + } + } + return strBuild.toString(); + } + + + static Collection readCertificates(InputStream in) { + CertificateFactory cf; + try { + cf = CertificateFactory.getInstance("X.509"); + Collection 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(); + } + +} diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index 911501986..93aeb75d2 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -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 + diff --git a/jadx-gui/src/main/resources/icons-16/certificate_obj.png b/jadx-gui/src/main/resources/icons-16/certificate_obj.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe6a175ade00cc8600b51549da4b508340e6ea6 GIT binary patch literal 569 zcmV-90>=G`P)yJgg+c5gkSGyE3!>=MC67V4TE{LC1QiHL zgtP=*wN7@(3Imsxw#>*_Ndj^IzL}=C^$za75AQL|!1we04UAAq@t?V5Bb#UgZUG4( z4EzG7fl(j_5Wt^4A%Y+6o=Zbr1JA>~*FV!W_#&+LBY)3<6E%YPXuTrONvWZF=dMA!CpH1A6i=S1hD)jNZLIk;l_qHRMws%i&<6{(!86z&+_Xeq2LUy z@gWPiSx*oG`Xc-GAPbA2l>Q{EpT5$4d5YD{5>7tDj%WxVVNgp6u!Dg-QaY{x6p_7c zyuEjh=KZ@+P6gql0W5?1%bkOHk@$74dHeGWGRm< z7qqC1l)8CsgXQ_mx{~TFko5#JFufiqs%hzzqhJ?6U*?I$``5Ft4PT`Li_7EQ)#1tNWBm46Q)NSZOm`V-Q00000NkvXX Hu0mjfRObLc literal 0 HcmV?d00001 diff --git a/jadx-gui/src/test/java/jadx/gui/utils/CertificateManagerTest.java b/jadx-gui/src/test/java/jadx/gui/utils/CertificateManagerTest.java new file mode 100644 index 000000000..0fd763e62 --- /dev/null +++ b/jadx-gui/src/test/java/jadx/gui/utils/CertificateManagerTest.java @@ -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 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")); + } + +} \ No newline at end of file diff --git a/jadx-gui/src/test/resources/CERT.DSA b/jadx-gui/src/test/resources/CERT.DSA new file mode 100644 index 0000000000000000000000000000000000000000..dd1e6f74d70caa4326164ae956f9506439ccef39 GIT binary patch literal 855 zcmXqLVh(2G)N1o+`_9YA&a|M3*~OrV*@20X(U9AKlZ{oIkC{n|mBD};p@?xo6Vnod zCZ+{I9Jhd(iIItkMa+qN5l{^~(4cb`EX+U+N`?vsa%{|@EL_6kpBXkyfXSz*(hR!qHAnBwljq+Aw+Gq0cVRdzXWy_bP%YCn)lc6xSb&h+t8tRxA_c zXZhXy?ebB%!%U_j!ck%qjlPwBUZ1VhPw=H{9dSGOD->xf> z{?7{81sWT+c#0J&zwprz6W)HF)2QdfvWGTeRox*qy&4Y|uanc>ysUI$-r{=hUy7zl zAAcuRF{)UfS@Ln;iogk-E1K^=jCwksZBlnyTtjuJY6_!|^}q9;TFi|t3`~s;(k;a^ zo=d+wdT`fdwd0mIBbGOFEj>KTG133N{-pW0?g>ccuqLbI^z$oJ`)y@m?V7lG!*a)- zcegm^^v~sx+m-&LZGI}xGv(=P_BXjq7HSur$6;`Jd6!^Y`XmHlDGWw#iEY;%kLZsZ8v|5t9YqJg}DEHH`4^0A1qh$t?&xJo)EdUkpD z;@I?x^*7y4%CMs(DQ0~J105z2(~R$2MxEXz7xwa8{=RdO>-M(UznMfdzed??=$TO? zH~Y5owFZX)dd$aiO8Q^bi#Z3OG&Lpd6Q1t8T$|F*PH?X3%WM{ literal 0 HcmV?d00001 diff --git a/jadx-gui/src/test/resources/CERT.RSA b/jadx-gui/src/test/resources/CERT.RSA new file mode 100644 index 0000000000000000000000000000000000000000..3ac6fc8aa1e08803aaffc656d0c55589cc41eb82 GIT binary patch literal 1395 zcmXqLV$Emc)N1o+`_9YA&a|M3HNl{XHHL|i(U9AKlZ{oIkC{n|mBD};p@?xo6Z1ra zCgxs)CZ^H_%uI|-Of25l8iEXX;c6JUfm+H9`GG>rp)72|Y=%b0CZ=XE0S;ldpwi;v z%tS*`17VN~c440261{-blGLK4(xP-j5d$HRFqbg5zrVjiNNRD3KTuT2Kma7lEzDU0 z6j3P6%PfJ3F+;_YQ;SLr*0AF)}i=+B8nzbz=ANmV*7emO5okIPWj>>ek*>7bXRHPvTe;FimFS z+N}~km#w-Cg@m_6obfrYWTp4Mf9ndppDb?H48;*2?Wea^-E$AFzTLKd)=TCidM7W1 z`fCUm3$LsAedO|GpX2X-|6jov+-K6gV`A&B{e{PNN^i6IJMX+I?_EC+&3jfFvB8PE z9yS$Cd-ch7k;=g>`Mv&st*Qcf+1?gU-%}IM^gt^4uKKo3mY-kvSzS(l@;vnx+g{V$ zIekpYX-WH|J?>3h+Hu$9{Uwd4Us2LGma0GXX7x+p;3*X34oJXMXhG=VOaZ zOm>?m^pb7UOm0`+xnkvJTXs+CbJE$iTQ*(Lmln78ud8d@N!tB5!`5{P(;( ze(nKxw>L|-#2Oy@D=vu~h`^Kv3`9l-rQaOqBA2#`98>i<_$$2fZl`~g(X4YH{@kz9 zKK*d-xrNnp+Y0Z8P72=6x$_k(o9F&wuAmLGYpj+wh$iH$SjER}`%&hUK_kzKTkcOq zl0yF|Xdl(m)wOmr^SLGEwEophcWJ@mwg;0IwAHxGn~}Hg^QO9W+vooa-=%T!#)(D8 zj=I0tCTAi3Lp|sI$w^z~JK6r_I4(#$CM^8je)cp8Wy$H@RbS5O-9B}C&ZIEz(l<&e zvzd*`{n^{kOKY#HE-#x{dTf*3rGH*$q$<_r>$2EZo()aDecjp2L39864@VqU@Q%gn0&2hQv5RAu##d=mmtk~2K8o)MPV01@9n(4Fj~#y zW&O*8MIL5uw;vcUT0NU{QCCjL>vw-O`epZDahY`d`=lGU`Q*2zh{ncj=9>MM-R@7L z@FqQG?XL4GufEF)H5#%y9KGAlGc93q!kkk-4qeO1@&7fR9qT!jUy^AAQqyGTNc6Da2WA^SJz#tNEs<8yXJy zxSWX$QgRowRgVr#S##}d%P6^*8Hd6 Zd8k_6G1;(c3scpmT7fMQb?lw5c>v9_0S^EG literal 0 HcmV?d00001 diff --git a/jadx-gui/src/test/resources/EMPTY.txt b/jadx-gui/src/test/resources/EMPTY.txt new file mode 100644 index 000000000..30d74d258 --- /dev/null +++ b/jadx-gui/src/test/resources/EMPTY.txt @@ -0,0 +1 @@ +test \ No newline at end of file