首页

关于android开发中如何正确使用in-house activity安全用法及代码示例

标签:android,in-house activity,安全     发布时间:2017-10-29   

一、前言

只允许企业内部程序可使用的Activity。其风险有:第三方应用程序可以读取intent,应保护敏感信息不被第三方读取到敏感信息。注意事项如下:

1、定义内部签名授权。@b@2、不要指定taskAffinity。@b@3、不要指定launchMode。@b@4、请求内部的签名许可。@b@5、不定义intent过滤器,导出属性显式设置为true。@b@6、确认内部签名是由内部程序定义的。@b@7、验证收到的intent。@b@8、敏感信息可以在内部应用程序间返回。@b@9、在导出apk时,使用相同的developer key对apk进行签名。

二、原代码示例

1.AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>@b@<manifest xmlns:android="http://schemas.android.com/apk/res/android"  _package="org.jssec.android.activity.inhouseactivity" >@b@ @b@<permission@b@    android:name="org.jssec.android.activity.inhouseactivity.MY_PERMISSION"@b@    android:protectionLevel="signature" />@b@ @b@<application@b@    android:allowBackup="false"@b@    android:icon="@drawable/ic_launcher"@b@    android:label="@string/app_name" >@b@ @b@    <!-- In-house Activity -->@b@ @b@    <activity@b@        android:name="org.jssec.android.activity.inhouseactivity.InhouseActivity"@b@        android:exported="true"@b@        android:permission="org.jssec.android.activity.inhouseactivity.MY_PERMISSION" />@b@    </application>@b@</manifest>

2.InhouseActivity.java

package org.jssec.android.activity.inhouseactivity;@b@ @b@import org.jssec.android.shared.SigPerm;@b@import org.jssec.android.shared.Utils;@b@import android.app.Activity;@b@import android.content.Context;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.Toast;@b@ @b@public class InhouseActivity extends Activity {@b@ @b@    // In-house Signature Permission@b@    private static final String MY_PERMISSION = "org.jssec.android.activity.inhouseactivity.MY_PERMISSION";@b@ @b@    // In-house certificate hash value@b@    private static String sMyCertHash = null;@b@    private static String myCertHash(Context context) {@b@        if (sMyCertHash == null) {@b@            if (Utils.isDebuggable(context)) {@b@                // Certificate hash value of "androiddebugkey" in the debug.keystore.@b@                sMyCertHash = "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255";@b@            } else {@b@                // Certificate hash value of "my company key" in the keystore.@b@                sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2 42E142CA"; }@b@            }@b@            return sMyCertHash;@b@    }@b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@ @b@        if (!SigPerm.test(this, MY_PERMISSION, myCertHash(this))) {@b@            Toast.makeText(this, "The in-house signature permission is not declared by in-house application.",@b@                Toast.LENGTH_LONG).show();@b@            finish();@b@            return;@b@        }@b@ @b@        // *** POINT 7 *** Handle the received intent carefully and securely, even though the intent was sent from an in-house application.@b@        // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely." @b@        String param = getIntent().getStringExtra("PARAM");@b@        Toast.makeText(this, String.format("Received param: "3.2 Handling Input Data Carefully and Secur    }@b@ @b@    public void onReturnResultClick(View view) {@b@        // *** POINT 8 *** Sensitive information can be returned since the requesting application is in-house.@b@        Intent intent = new Intent();@b@        intent.putExtra("RESULT", "Sensitive Info");@b@        setResult(RESULT_OK, intent);@b@        finish();@b@    }@b@}

3.SigPerm.java

package org.jssec.android.shared;@b@ @b@import android.content.Context;@b@import android.content.pm.PackageManager;@b@import android.content.pm.PackageManager.NameNotFoundException;@b@import android.content.pm.PermissionInfo;@b@ @b@public class SigPerm {@b@ @b@    public static boolean test(Context ctx, String sigPermName, String correctHash) {@b@        if (correctHash == null) return false;@b@        correctHash = correctHash.replaceAll(" ", "");@b@        return correctHash.equals(hash(ctx, sigPermName));@b@    }@b@ @b@    public static String hash(Context ctx, String sigPermName) {@b@        if (sigPermName == null) return null;@b@        try {@b@            // Get the package name of the application which declares a permission named sigPermName.  @b@            PackageManager pm = ctx.getPackageManager();@b@            PermissionInfo pi;@b@            pi = pm.getPermissionInfo(sigPermName, PackageManager.GET_META_DATA);@b@            String pkgname = pi.packageName;@b@ @b@            // Fail if the permission named sigPermName is not a Signature Permission@b@            if (pi.protectionLevel != PermissionInfo.PROTECTION_SIGNATURE) return null;@b@ @b@            // Return the certificate hash value of the application which declares a permission named sigPermName.@b@            return PkgCert.hash(ctx, pkgname);@b@ @b@        } catch (NameNotFoundException e) {@b@         return null;@b@        }  @b@    }@b@}

4.PkgCert.java

package org.jssec.android.shared;@b@ @b@import java.security.MessageDigest;@b@import java.security.NoSuchAlgorithmException;@b@ @b@import android.content.Context;@b@import android.content.pm.PackageInfo;@b@import android.content.pm.PackageManager;@b@import android.content.pm.PackageManager.NameNotFoundException;@b@import android.content.pm.Signature;@b@ @b@public class PkgCert {@b@    public static boolean test(Context ctx, String pkgname, String correctHash) {@b@        if (correctHash == null) return false;@b@        correctHash = correctHash.replaceAll(" ", "");@b@        return correctHash.equals(hash(ctx, pkgname));@b@    }@b@ @b@    public static String hash(Context ctx, String pkgname) {@b@        if (pkgname == null) return null;@b@        try {@b@            PackageManager pm = ctx.getPackageManager();@b@            PackageInfo pkginfo = pm.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);@b@            if (pkginfo.signatures.length != 1) return null; // Will not handle multiple signatures.@b@            Signature sig = pkginfo.signatures[0];@b@            byte[] cert = sig.toByteArray();@b@            byte[] sha256 = computeSha256(cert);@b@            return byte2hex(sha256);@b@        } catch (NameNotFoundException e) {@b@            return null;@b@        }@b@    }@b@ @b@    private static byte[] computeSha256(byte[] data) {@b@        try {@b@            return MessageDigest.getInstance("SHA-256").digest(data);@b@        } catch (NoSuchAlgorithmException e) {@b@            return null;@b@        }@b@    }@b@ @b@    private static String byte2hex(byte[] data) {@b@        if (data == null) return null;@b@        final StringBuilder hexadecimal = new StringBuilder();@b@        for (final byte b : data) {@b@            hexadecimal.append(String.format("%02X", b));@b@        }@b@        return hexadecimal.toString();@b@    }@b@}

关于android开发中如何正确使用in-house activity安全用法及代码示例

三、安全代码示例

注意事项如下:

1、申报需要使用的内部签名许可。@b@2、验证内部签名是由内部应用程序定义的。@b@3、验证目标应用程度已经由内部证书签名。@b@4、在目标应用为内部时,敏感信息可以用putExtra()发送。@b@5、使用显式intent来调用内部Activity。@b@6、验证收到的数据。@b@7、当导出apk时,用与目标程序相同的developer key进行签名。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>@b@<manifest xmlns:android="http://schemas.android.com/apk/res/android"@b@    package="org.jssec.android.activity.inhouseuser" >@b@        android:name="org.jssec.android.activity.inhouseactivity.MY_PERMISSION" />@b@ @b@    <application@b@        android:allowBackup="false"@b@        android:icon="@drawable/ic_launcher"@b@        android:label="@string/app_name" >@b@ @b@        <activity@b@            android:name="org.jssec.android.activity.inhouseuser.InhouseUserActivity"@b@            android:label="@string/app_name"@b@            android:exported="true" >@b@            <intent-filter>@b@                <action android:name="android.intent.action.MAIN" />@b@                <category android:name="android.intent.category.LAUNCHER" />@b@            </intent-filter>@b@        </activity>@b@    </application>@b@</manifest>

InhouseUserActivity.java

package org.jssec.android.activity.inhouseuser;@b@ @b@import org.jssec.android.shared.PkgCert;@b@import org.jssec.android.shared.SigPerm;@b@import org.jssec.android.shared.Utils;@b@ @b@import android.app.Activity;@b@import android.content.ActivityNotFoundException;@b@import android.content.Context;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.Toast;@b@ @b@public class InhouseUserActivity extends Activity {@b@ @b@    // Target Activity information@b@    private static final String TARGET_PACKAGE = "org.jssec.android.activity.inhouseactivity";@b@    private static final String TARGET_ACTIVITY = "org.jssec.android.activity.inhouseactivity.InhouseActivity";@b@ @b@    // In-house Signature Permission@b@    private static final String MY_PERMISSION = "org.jssec.android.activity.inhouseactivity.MY_PERMISSION";@b@ @b@    // In-house certificate hash value@b@    private static String sMyCertHash = null;@b@    private static String myCertHash(Context context) {@b@        if (sMyCertHash == null) {@b@            if (Utils.isDebuggable(context)) {@b@            // Certificate hash value of "androiddebugkey" in the debug.keystore.@b@            sMyCertHash = "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255"; }@b@            else {@b@                // Certificate hash value of "my company key" in the keystore.@b@                sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2 42E142CA"; }@b@            }@b@            return sMyCertHash;@b@    }@b@ @b@    private static final int REQUEST_CODE = 1;@b@ @b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@    }@b@ @b@    public void onUseActivityClick(View view) {@b@ @b@        // *** POINT 11 *** Verify that the in-house signature permission is defined by an in-house application.@b@        if (!SigPerm.test(this, MY_PERMISSION, myCertHash(this))) {@b@            Toast.makeText(this, "The in-house signature permission is not declared by in-house application.", Toast.LENGTH_LONG).show();@b@            return;@b@        }@b@ @b@        // ** POINT 12 *** Verify that the destination application is signed with the in-house certificate.@b@        if (!PkgCert.test(this, TARGET_PACKAGE, myCertHash(this))) {@b@            Toast.makeText(this, "Target application is not an in-house application.", Toast.LENGTH_LONG).show();@b@            return;@b@        }@b@ @b@        try {@b@            Intent intent = new Intent();@b@            // *** POINT 13 *** Sensitive information can be sent only by putExtra() since the destination application is in-house.@b@            intent.putExtra("PARAM", "Sensitive Info");@b@ @b@            // *** POINT 14 *** Use explicit intents to call an In-house Activity. i@b@            ntent.setClassName(TARGET_PACKAGE, TARGET_ACTIVITY);@b@            startActivityForResult(intent, REQUEST_CODE);@b@        }@b@        catch (ActivityNotFoundException e) {@b@            Toast.makeText(this, "Target activity not found.", Toast.LENGTH_LONG).show();@b@        }@b@    }@b@ @b@    @Override@b@    public void onActivityResult(int requestCode, int resultCode, Intent data) {@b@        super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK) return;@b@ @b@        switch (requestCode) {@b@        case REQUEST_CODE:@b@            String result = data.getStringExtra("RESULT");@b@            // *** POINT 15 *** Handle the received data carefully and securely,@b@            // even though the data came from an in-house application.@b@            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."@b@            Toast.makeText(this, String.format("Received result: ¥"%s¥"", result), Toast.LENGTH_LONG).show();@b@         break;@b@        }@b@    }@b@}

SigPerm.java

package org.jssec.android.shared;@b@ @b@import android.content.Context;@b@import android.content.pm.PackageManager;@b@import android.content.pm.PackageManager.NameNotFoundException;@b@import android.content.pm.PermissionInfo;@b@ @b@public class SigPerm {@b@    public static boolean test(Context ctx, String sigPermName, String correctHash) {@b@        if (correctHash == null) return false;@b@        correctHash = correctHash.replaceAll(" ", "");@b@        return correctHash.equals(hash(ctx, sigPermName));@b@    }@b@ @b@    public static String hash(Context ctx, String sigPermName) {@b@        if (sigPermName == null) return null;@b@        try {@b@            // Get the package name of the application which declares a permission named sigPermName.@b@            PackageManager pm = ctx.getPackageManager();@b@            PermissionInfo pi;@b@            pi = pm.getPermissionInfo(sigPermName, PackageManager.GET_META_DATA);@b@            String pkgname = pi.packageName;@b@ @b@            // Fail if the permission named sigPermName is not a Signature Permission@b@            if (pi.protectionLevel != PermissionInfo.PROTECTION_SIGNATURE) return null;@b@ @b@            // Return the certificate hash value of the application which declares a permission named sigPermName.@b@            return PkgCert.hash(ctx, pkgname);@b@        } catch (NameNotFoundException e) {@b@            return null;@b@        }@b@    }@b@}

PkgCert.java

package org.jssec.android.shared;@b@ @b@import java.security.MessageDigest;@b@import java.security.NoSuchAlgorithmException;@b@ @b@import android.content.Context;@b@import android.content.pm.PackageInfo;@b@import android.content.pm.PackageManager;@b@import android.content.pm.PackageManager.NameNotFoundException;@b@import android.content.pm.Signature;@b@ @b@public class PkgCert {@b@    public static boolean test(Context ctx, String pkgname, String correctHash) {@b@        if (correctHash == null) return false;@b@        correctHash = correctHash.replaceAll(" ", "");@b@        return correctHash.equals(hash(ctx, pkgname));@b@    }@b@ @b@    public static String hash(Context ctx, String pkgname) {@b@        if (pkgname == null) return null;@b@        try {@b@            PackageManager pm = ctx.getPackageManager();@b@            PackageInfo pkginfo = pm.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);@b@            if (pkginfo.signatures.length != 1) return null;@b@            Signature sig = pkginfo.signatures[0];@b@            byte[] cert = sig.toByteArray();@b@            byte[] sha256 = computeSha256(cert);@b@            return byte2hex(sha256);@b@            } catch (NameNotFoundException e) {@b@                return null;@b@            }@b@    }@b@ @b@    private static byte[] computeSha256(byte[] data) {@b@        try {@b@            return MessageDigest.getInstance("SHA-256").digest(data);@b@        }catch (NoSuchAlgorithmException e) {@b@            return null;@b@        }@b@    }@b@ @b@    private static String byte2hex(byte[] data) {@b@        if (data == null) return null;@b@        final StringBuilder hexadecimal = new StringBuilder();@b@        for (final byte b : data) {@b@            hexadecimal.append(String.format("%02X", b));@b@        }@b@        return hexadecimal.toString();@b@    }@b@}

关于android开发中如何正确使用in-house activity安全用法及代码示例