Android Open Source - devoxx-2013-android-app-security Tamper Detection Utils






From Project

Back to project page devoxx-2013-android-app-security.

License

The source code is released under:

Apache License

If you think the Android project devoxx-2013-android-app-security listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package be.idamf.android.tamperdetection.util;
// w w w .  j  a  v  a2  s  . com
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;

import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.security.auth.x500.X500Principal;

import be.idamf.android.tamperdetection.data.PublicKeyInfo;


/**
 * Tamper detection utilities.
 */
public class TamperDetectionUtils {

    /**
     * Check whether the app is currently running in the emulator.
     * There is no supported mechanism in the Android SDK to determine this.
     * We try to determine this by looking through the system properties,
     * via the {@link android.os.Build} class.
     *
     * @return true when the app seems to run on an emulator, otherwise false
     */
    public static boolean isRunningInEmulator() {
        boolean runningInEmulator = false;
        if (Build.BRAND.equalsIgnoreCase("generic")) {
            runningInEmulator = true;
        } else if (Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK")) {
            runningInEmulator = true;
        } else if (Build.PRODUCT.contains("sdk") || Build.PRODUCT.equalsIgnoreCase("full_x86")) {
            runningInEmulator = true;
        } else if (Build.HARDWARE.contains("goldfish")) {
            runningInEmulator = true;
        }
        return runningInEmulator;
    }

    /**
     * Check whether the application is installed through the Play Store.
     *
     * @param context Context
     * @return true when the app is installed through the Play store, otherwise false
     */
    public static boolean isInstalledThroughPlayStore(final Context context) {
        final PackageManager packageManager = context.getPackageManager();
        final String packageInstallerName = packageManager.getInstallerPackageName(getPackageName(context));
        if ("com.android.vending".equals(packageInstallerName)) {
            // App is installed through the Play Store
            return true;
        }
        return false;
    }

    /**
     * Check whether the application is debuggable.
     * The first check is to see what the PackageManager reports.
     * If wanted, an additional check can be performed by looking at the certificate.
     * The default auto-generated certificate has the DN 'CN=Android Debug,O=Android,C=US',
     * as described at http://developer.android.com/tools/publishing/app-signing.html#debugmode
     * If the app's DN matches this default, it is probably using the debug certificate.
     *
     * @param context Context
     * @return true when the app is debuggable, otherwise false
     */
    public static boolean isDebuggable(final Context context, final boolean includeDefaultDebugCertificateCheck) {
        boolean debuggable = ( 0 != ( context.getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) );
        if (!debuggable && includeDefaultDebugCertificateCheck) {
            debuggable = isDebugCertificateCheck(context);
        }
        return debuggable;
    }

    private static boolean isDebugCertificateCheck(final Context context) {
        final X500Principal DEBUG_CERTIFICATE_DN = new X500Principal("CN=Android Debug,O=Android,C=US");
        boolean debuggable = false;

        try {
            Signature[] signatures = getSignatures(context);

            for (int i = 0; i < signatures.length; i++) {
                X509Certificate certificate = generateX509CertificateFromSignature(signatures[i]);
                debuggable = certificate.getSubjectX500Principal().equals(DEBUG_CERTIFICATE_DN);
                if (debuggable) {
                    return true;
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            // package not found - debuggable = false
        } catch (CertificateException e) {
            // certificate factory non-instantiable - debuggable = false
        }
        return false;
    }

    /**
     * Check whether the signing key can be validated.
     *
     * @param context Context
     * @param certificateToCheckAgainst the SHA1 of the signing certificate
     * @return true when the signing key appears valid, otherwise false
     */
    public static boolean isValidSigningKey(final Context context, final String certificateToCheckAgainst) {
        try {
            Signature[] signatures = getSignatures(context);
            for (int i = 0; i < signatures.length; i++) {
                X509Certificate certificate = generateX509CertificateFromSignature(signatures[i]);
                String sha1 = getCertificateSHA1(certificate);
                if (!certificateToCheckAgainst.equalsIgnoreCase(sha1)) {
                    return false;
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            // package not found - leave valid true
        } catch (CertificateException e) {
            // certificate factory non-instantiable - leave valid true
        } catch (NoSuchAlgorithmException e) {
            // algorithm not found - leave valid true
        }
        return true;
    }

    /**
     * Get basic information about the app's public key.
     * The Signature array from the PackageInfo object can contain multiple signatures, but
     * most applications are only signed once. Thus we only check the first signature here.
     * If your app is signed with multiple keys, you should check all of those keys!
     *
     * @param context Context
     * @return {@link be.idamf.android.tamperdetection.data.PublicKeyInfo} object or null
     */
    public static PublicKeyInfo getAppPublicKeyInfo(final Context context) {
        try {
            Signature[] signatures = getSignatures(context);
            // most apps are only signed once, as such: only get the first one here
            X509Certificate certificate = generateX509CertificateFromSignature(signatures[0]);
            PublicKeyInfo x509CertificateInfo = new PublicKeyInfo(
                    certificate.getSubjectDN().toString(),
                    certificate.getIssuerDN().toString(),
                    certificate.getNotBefore(),
                    certificate.getNotAfter());
            return x509CertificateInfo;
        } catch (PackageManager.NameNotFoundException e) {
            // problem getting the signatures
        } catch (CertificateException e) {
            // exception getting at the CertificateFactory
        }
        return null;
    }

    /**
     * Get the application package name.
     *
     * @param context Context
     * @return package name
     */
    public static String getPackageName(final Context context) {
        return context.getPackageName();
    }

    private static String getCertificateSHA1(X509Certificate certificate) throws NoSuchAlgorithmException, CertificateEncodingException {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
        byte[] der = certificate.getEncoded();
        messageDigest.update(der);
        byte[] digest = messageDigest.digest();
        return hexify(digest);
    }

    private static Signature[] getSignatures(final Context context) throws PackageManager.NameNotFoundException {
        PackageInfo packageInfo = context.getPackageManager().getPackageInfo( getPackageName(context), PackageManager.GET_SIGNATURES );
        Signature[] signatures = packageInfo.signatures;
        return signatures;
    }

    private static X509Certificate generateX509CertificateFromSignature(final Signature signature) throws CertificateException {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        ByteArrayInputStream inputStream = new ByteArrayInputStream(signature.toByteArray());
        X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
        return certificate;
    }

    private 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 * 2);
        for (int i = 0; i < bytes.length; ++i) {
            buf.append(hexDigits[(bytes[i] & 0xf0) >> 4]);
            buf.append(hexDigits[bytes[i] & 0x0f]);
        }
        return buf.toString();
    }

}




Java Source Code List

be.idamf.android.tamperdetection.MainActivity.java
be.idamf.android.tamperdetection.data.PublicKeyInfo.java
be.idamf.android.tamperdetection.pinning.TrustAllTrustManager.java
be.idamf.android.tamperdetection.task.EnvironmentChecker.java
be.idamf.android.tamperdetection.task.RootDetector.java
be.idamf.android.tamperdetection.ui.EnvironmentCheckFragment.java
be.idamf.android.tamperdetection.ui.NetworkCheckFragment.java
be.idamf.android.tamperdetection.ui.RootCheckFragment.java
be.idamf.android.tamperdetection.util.ExecShell.java
be.idamf.android.tamperdetection.util.RootDetectionUtils.java
be.idamf.android.tamperdetection.util.TamperDetectionUtils.java