首页

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

标签:android,In-house Broadcast Receiver,安全     发布时间:2017-10-31   

一、注意事项

1、定义一个内部签名权限来接收广播。

2、定义使用内部签名的许可来接收结果。

3、定义exported属性为true。

4、使用静态Broadcat Receiver请求一个内部签名许可。

5、请求内部签名许可来注册Dynamic Broadcast Receiver。

6、验证内部签名许可的正确性。

7、处理收到的intent,验证其正确性。

8、敏感信息可以在内部应用中返回。

9、当导出apk的时候,用相同的developer key进行签名。

二、原代码示例

1.InhouseReceiver.java

package org.jssec.android.broadcast.inhousereceiver;@b@ @b@import org.jssec.android.shared.SigPerm;@b@import org.jssec.android.shared.Utils;@b@ @b@import android.app.Activity;@b@import android.content.BroadcastReceiver;@b@import android.content.Context;@b@import android.content.Intent;@b@import android.widget.Toast;@b@ @b@public class InhouseReceiver extends BroadcastReceiver {@b@ @b@    // In-house Signature Permission@b@    private static final String MY_PERMISSION = "org.jssec.android.broadcast.inhousereceiver.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 String MY_BROADCAST_INHOUSE = "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";@b@ @b@    public boolean isDynamic = false;@b@    private String getName() {@b@        return isDynamic ? "In-house Dynamic Broadcast Receiver" : "In-house Static Broadcast Receiver"; }@b@ @b@    @Override@b@    public void onReceive(Context context, Intent intent) {@b@ @b@        // *** POINT 6 *** Verify that the in-house signature permission is defined by an in-house application.@b@        if (!SigPerm.test(context, MY_PERMISSION, myCertHash(context))) {@b@            Toast.makeText(context, "The in-house signature permission is not declared by in-house application.", Toast.LENGTH_LONG).show();@b@            return;@b@        }@b@ @b@        // *** POINT 7 *** Handle the received intent carefully and securely,@b@        // even though the Broadcast 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@        if (MY_BROADCAST_INHOUSE.equals(intent.getAction())) {@b@            String param = intent.getStringExtra("PARAM");@b@            Toast.makeText(context,String.format("%s:¥nReceived param: ¥"%s¥"", getName(), param), Toast.LENGTH_SHORT).show();@b@        }@b@ @b@        // *** POINT 8 *** Sensitive information can be returned since the requesting application is in-house.@b@        setResultCode(Activity.RESULT_OK);@b@        setResultData(String.format("Sensitive Info from %s", getName()));@b@        abortBroadcast();@b@    }@b@}

2.AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>@b@<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jssec.android.broadcast.inhousereceiver" >@b@    <!-- *** POINT 1 *** Define an in-house signature permission to receive Broadcasts -->@b@    <permission android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION" android:protectionLevel="signature" />@b@ @b@    <!-- *** POINT 2 *** Declare to use the in-house signature permission to receive results. -->@b@    <uses-permission android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION" />@b@ @b@    <application@b@        android:icon="@drawable/ic_launcher"@b@        android:label="@string/app_name"@b@        android:allowBackup="false" >@b@ @b@        <!-- *** POINT 3 *** Explicitly set the exported attribute to true. -->@b@        <!-- *** POINT 4 *** Require the in-house signature permission by the Static Broadcast Receiver definition. -->@b@ @b@        <receiver@b@            android:name=".InhouseReceiver"@b@            android:permission="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION"@b@            android:exported="true">@b@            <intent-filter>@b@                <action android:name="org.jssec.android.broadcast.MY_BROADCAST_INHOUSE" />@b@            </intent-filter>@b@        </receiver>@b@ @b@        <service android:name=".DynamicReceiverService" android:exported="false" />@b@ @b@        <activity@b@            android:name=".InhouseReceiverActivity"@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@ @b@</manifest>

3.InhouseReceiverActivity.java

package org.jssec.android.broadcast.inhousereceiver;@b@import android.app.Activity;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@ @b@public class InhouseReceiverActivity extends Activity {@b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@    }@b@ @b@    public void onRegisterReceiverClick(View view) {@b@        Intent intent = new Intent(this, DynamicReceiverService.class);@b@        startService(intent);@b@    }@b@ @b@    public void onUnregisterReceiverClick(View view) {@b@        Intent intent = new Intent(this, DynamicReceiverService.class);@b@        stopService(intent);@b@    }@b@}

4.DynamicReceiverService.java

package org.jssec.android.broadcast.inhousereceiver;@b@ @b@import android.app.Service;@b@import android.content.Intent;@b@import android.content.IntentFilter;@b@import android.os.IBinder;@b@import android.widget.Toast;@b@ @b@public class DynamicReceiverService extends Service {@b@ @b@    private static final String MY_BROADCAST_INHOUSE = "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";@b@ @b@    private InhouseReceiver mReceiver;@b@ @b@    @Override@b@        public IBinder onBind(Intent intent) {@b@        return null;@b@        }@b@ @b@        @Override@b@        public void onCreate() {@b@        super.onCreate();@b@ @b@        mReceiver = new InhouseReceiver();@b@        mReceiver.isDynamic = true;@b@        IntentFilter filter = new IntentFilter();@b@        filter.addAction(MY_BROADCAST_INHOUSE);@b@        filter.setPriority(1); // Prioritize Dynamic Broadcast Receiver, rather than Static Broadcast Receiver.@b@ @b@        // *** POINT 5 *** When registering a dynamic broadcast receiver, require the in-house signature permission.@b@        registerReceiver(mReceiver, filter, "org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION", null);@b@   @b@        Toast.makeText(this,"Registered Dynamic Broadcast Receiver.", Toast.LENGTH_SHORT).show();@b@    }@b@ @b@    @Override@b@    public void onDestroy() {@b@        super.onDestroy();@b@        unregisterReceiver(mReceiver);@b@        mReceiver = null;@b@        Toast.makeText(this,"Unregistered Dynamic Broadcast Receiver.", Toast.LENGTH_SHORT).show();@b@    }@b@}

5.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. 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@}

6.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@ @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@    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 Broadcast Receiver 安全用法及代码示例

三、安全代码示例

1、定义一个内部签名的许可来接收结果。@b@2、声明使用内部签名来接收广播。@b@3、验证内部签名许可是由内部应用定义的。@b@4、当调用方为内部应用时,可以返回敏感数据。@b@5、请求签名许可。@b@6、处理收到的数据。@b@7、当导出APk,用与目标应用相同的developer key进行签名。
AndroidManifest.xml@b@ @b@<?xml version="1.0" encoding="utf-8"?>@b@<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.jssec.android.broadcast.inhousesender" >@b@ @b@    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>@b@ @b@    <!-- *** POINT 10 *** Define an in-house signature permission to receive results. -->@b@    <permission@b@        android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION"@b@        android:protectionLevel="signature" />@b@ @b@    <!-- *** POINT 11 *** Declare to use the in-house signature permission to receive Broadcasts. -->@b@    <uses-permission android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION" />@b@ @b@    <application@b@        android:icon="@drawable/ic_launcher"@b@        android:label="@string/app_name"@b@        android:allowBackup="false" >@b@ @b@        <activity@b@            android:name="org.jssec.android.broadcast.inhousesender.InhouseSenderActivity"@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>
InhouseSenderActivity.java@b@ @b@package org.jssec.android.broadcast.inhousesender;@b@ @b@import org.jssec.android.shared.SigPerm;@b@import org.jssec.android.shared.Utils;@b@ @b@import android.app.Activity;@b@import android.content.BroadcastReceiver;@b@import android.content.Context;@b@import android.content.Intent;@b@import android.os.Bundle;@b@import android.view.View;@b@import android.widget.TextView;@b@import android.widget.Toast;@b@ @b@public class InhouseSenderActivity extends Activity {@b@ @b@    // In-house Signature Permission@b@    private static final String MY_PERMISSION = "org.jssec.android.broadcast.inhousesender.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@        }@b@        return sMyCertHash;@b@    }@b@ @b@    private static final String MY_BROADCAST_INHOUSE = "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";@b@ @b@    public void onSendNormalClick(View view) {@b@ @b@        // *** POINT 12 *** 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 13 *** Sensitive information can be returned since the requesting application is in-house.@b@        Intent intent = new Intent(MY_BROADCAST_INHOUSE);@b@        intent.putExtra("PARAM", "Sensitive Info from Sender");@b@ @b@        // *** POINT 14 *** Require the in-house signature permission to limit receivers.@b@        sendBroadcast(intent, "org.jssec.android.broadcast.inhousesender.MY_PERMISSION");@b@    }@b@ @b@    public void onSendOrderedClick(View view) {@b@        // *** POINT 12 *** 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@        // *** POINT 13 *** Sensitive information can be returned since the requesting application is in-house.@b@        Intent intent = new Intent(MY_BROADCAST_INHOUSE);@b@        intent.putExtra("PARAM", "Sensitive Info from Sender");@b@ @b@        // *** POINT 14 *** Require the in-house signature permission to limit receivers.@b@        sendOrderedBroadcast(intent, "org.jssec.android.broadcast.inhousesender.MY_PERMISSION",@b@            mResultReceiver, null, 0, null, null);@b@    }@b@ @b@    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {@b@        @Override@b@        public void onReceive(Context context, Intent intent) {@b@ @b@            // *** POINT 15 *** Handle the received result 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@            String data = getResultData();@b@            InhouseSenderActivity.this.logLine(String.format("Received result: ¥"%s¥"", data));@b@        }@b@    };@b@ @b@    private TextView mLogView;@b@ @b@    @Override@b@    public void onCreate(Bundle savedInstanceState) {@b@        super.onCreate(savedInstanceState);@b@        setContentView(R.layout.main);@b@        mLogView = (TextView)findViewById(R.id.logview);@b@    }@b@    private void logLine(String line) {@b@        mLogView.append(line);@b@        mLogView.append("¥n");@b@    }@b@}
SigPerm.java@b@ @b@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@            // 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@}
PkgCert.java@b@ @b@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@ @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@        // Will not handle multiple signatures.@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 Broadcast Receiver 安全用法及代码示例