1

I'm using Java (J2SE) to create a desktop application. It has a function that check if two unknown .apk files (for ex app1.apk and app2.apk) are same application or not. My idea is comparing the fingerprint (in file META-INF/CERT.RSA) and names of the two apps.

  • By using keytool (cmd: keytool -printcert -file .\META-INF\CERT.RSA), i can get meta information of signature inside the CERT.RSA file. However I cannot get those information by using java source code.
  • I also try using openssl to get the information, again I cannot have those information inside my source code to compare fingerprint of the two android apps.
  • By using normal Java, I have no idea how to extract the fingerprint in file CERT.RSA.

In addition, I also want to get the android app name which is defined in the file strings.xml. In particular, I read the AndroidManifest.xml first, in tag application I get the value of attribute android:label="@string/app_name". Then i get the value of attribute app_name in file strings.xml. However the file is compiled in file resources.arsc which is an unreadable binary file.

Any solution for this idea or any other ideas for checking if two unknown .apk files are same app? I really appreciate your help.
Thanks

Dang Phan
  • 13
  • 4
  • if you want to have access to CERT.RSA file. Just unzip the apk file and it is in META-INF folder just like you said. If you want to do this in Java, java.util.zip.ZipEntry will help you http://www.mkyong.com/java/how-to-decompress-files-from-a-zip-file/ – Bertrand Martel Aug 16 '15 at 10:05
  • Thank Bertrand Martel. I know how to use ZipEntry and already used it for access to the CERT.RSA. However the thing here is to extract to signature in the CERT.RSA file and get the name of app inside the resources.arsc. Do you have any idea? – Dang Phan Aug 16 '15 at 15:55

1 Answers1

0

The package name serves as a unique identifier for the application.

So basically 2 apk which refer to he same app will have same package name.

Then maybe one of these apk is corrupted somehow eg :

  • signature file is missing-inconsistent
  • public key file is missing-inconsistent
  • computing of digest of files's entries in CERT.SF doesn't match (for one or several files listed)
  • CERT.SF file's digest (at the beginning of the file) doesnt't match with computing of its own digest

If you want to be sure that one of this app has not been modified by 3rd part, you will have to do all these check above.

Then to sum up the whole thing of determining if 2 apks are the same official app :

  • package name must be the same
  • public keys must be the same
  • computing of digests of file listed in CERT.SF must match with file entries' digest in the same CERT.SF file
  • computing of digest of file CERT.SF must match with CERT.SF's own digest

CERT.SF are not necesserally the same if apk refer to the same app with different version (files added or deleted) but public key/private key pair must remained untouched to enable update of this app (unless it will result in "signature conflict" or "existing package already exist" errors)


Using Java

Check signature verification

You can use JarSigner source code for this : http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/security/tools/JarSigner.java#JarSigner.signatureRelated%28java.lang.String%29

with external jar lib sun-security-tool : http://www.java2s.com/Code/Jar/a/Downloadandroidsunjarsignsupport11jar.htm

Check public keys are the same

Compare the extracted public key. Here is the method I made for extracting PKCS7 certificate and public key from it :

/**
 * Retrieve public key from PKCS7 certificate
 * 
 * @param certPath
 * @return
 * @throws IOException
 * @throws InvalidKeySpecException
 * @throws NoSuchAlgorithmException
 */
public static String getPublicKey(String certPath) throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {

    File f = new File(certPath);
    FileInputStream is = new FileInputStream(f);

    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    int nRead;
    byte[] data = new byte[16384];

    while ((nRead = is.read(data, 0, data.length)) != -1) {
        buffer.write(data, 0, nRead);
    }

    buffer.flush();
    PKCS7 test = new PKCS7(buffer.toByteArray());
    X509Certificate[] certs = test.getCertificates();

    for (int i = 0; i < certs.length; i++) {
        if (certs[i] != null && certs[i].getPublicKey() != null) {
            return new BASE64Encoder().encode(certs[i].getPublicKey().getEncoded());
        }
    }
    return "";
}

Read package-name from Android Manifest

Here you have to parse Android binary XML for extracting these information. with this link, you have a lot of tools you can use (and snippet code) :

How to parse the AndroidManifest.xml file inside an .apk package

To get your app-name this is a bit different, this is an Android compiled resource. apktool can decode that kind of file but if you want to do this in java you'll have to decode it yourself. Here is apktool decoder https://github.com/iBotPeaches/Apktool/blob/master/brut.apktool/apktool-lib/src/main/java/brut/androlib/ApkDecoder.java. But if you want the app name it is not necessary in string.xml so this is rather something specific you want to do.


I have made a Java tool that makes jar verification / public key comparison : https://github.com/bertrandmartel/apk-checker


Using Bash

With command line, you can test your output much faster and easier :

Check signature verification

jarsigner -verbose -verify <your_apk_file_name>.apk | grep "jar verified"

Check public keys are the same

unzip -p <your_apk_file_name1>.apk META-INF/CERT.RSA | keytool -printcert

check package-name are the same

aapt d xmltree <your_apk_file_name1>.apk AndroidManifest.xml | grep "package=" | sed -e "s/.*=//g" | sed -e "s/ .*//g"

A gist for Bash scripts is available at https://gist.github.com/bertrandmartel/374564b950e8a577550b

Bertrand Martel
  • 42,756
  • 16
  • 135
  • 159