1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/wizardforcel-android-app-sec-guidebook

Клонировать/Скачать
4.2.1.3.md 22 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 27.11.2024 20:24 c13bfb7

Внутренний широковещательный приёмник

Внутренний широковещательный приёмник — это широковещательный приёмник, который никогда не будет получать широковещательные сообщения от приложений, внешних по отношению к внутренним. Он состоит из нескольких внутренних приложений и используется для защиты информации или функций, обрабатываемых внутренними приложениями.

Ключевые моменты (приём широковещательных сообщений):

  1. Определить внутреннее разрешение на подпись для приёма широковещательных сообщений.
  2. Объявить использование внутреннего разрешения на подпись для получения результатов.
  3. Явно установить атрибут экспорта в значение true.
  4. Требуется определение внутреннего разрешения на подпись статического широковещательного приёмника.
  5. Для регистрации динамического широковещательного приёмника требуется внутренняя подпись.
  6. Подтвердить, что внутреннее разрешение на подпись определено внутренним приложением.
  7. Хотя широковещательное сообщение отправляется внутренним приложением, необходимо осторожно и безопасно обрабатывать полученное намерение.
  8. Поскольку запрашивающее приложение является внутренним, можно возвращать конфиденциальную информацию.
  9. При экспорте APK используйте один и тот же ключ разработчика для подписи APK, как и у отправляющего приложения.

Пример кода внутреннего широковещательного приёмника может использоваться для статических и динамических широковещательных приёмников.

InhouseReceiver.java

package org.jssec.android.broadcast.inhousereceiver;

import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class InhouseReceiver extends BroadcastReceiver {
    // Внутреннее разрешение на подпись
    private static final String MY_PERMISSION = "org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION";
    // Хеш-значение сертификата
    private static String sMyCertHash = null;

    private static String myCertHash(Context context) {
        if (sMyCertHash == null) {
            if (Utils.isDebuggable(context)) {
                // Значение хеш-функции сертификата "androiddebugkey" в debug.keystore.
                sMyCertHash = "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255";
            } else {
                // Значение хеш-функции сертификата «ключ моей компании» в keystore.
                sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2 42E142CA";
            }
        }
        return sMyCertHash;
    }

    private static final String MY_BROADCAST_INHOUSE =
    "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";
    public boolean isDynamic = false;

    private String getName() {
        return isDynamic ? "Внутренний динамический широковещательный приёмник" : "Внутренний статический широковещательный приёмник";
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // *** Пункт 6 *** Убедитесь, что внутреннее разрешение на подпись определяется внутренним приложением.
        if (!SigPerm.test(context, MY_PERMISSION, myCertHash(context))) {
            Toast.makeText(context, "Внутреннее разрешение на подпись не объявлено внутренним приложением.",
            Toast.LENGTH_LONG).show();
            return;
        }
        // *** Пункт 7 *** Осторожно и надёжно обработайте полученное намерение,
        // даже если широковещательная передача была отправлена внутренним приложением..
        // Опущено, поскольку это пример. Пожалуйста, обратитесь к разделу «3.2 Обработка входных данных осторожно и надёжно».
        if (MY_BROADCAST_INHOUSE.equals(intent.getAction())) {
            String param = intent.getStringExtra("PARAM");
            Toast.makeText(context,
            String.format("%s:¥nReceived param: ¥"%s¥"", getName(), param),
            Toast.LENGTH_SHORT).show();
        }
        // *** Пункт 8 *** Можно вернуть конфиденциальную информацию, так как запрашивающее приложение является внутренним.
        setResultCode(Activity.RESULT_OK);
        setResultData(String.format("Конфиденциальная информация от %s", getName()));
        abortBroadcast();
    }
}

Статический широковещательный приёмник определяется в AndroidManifest.xml.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.jssec.android.broadcast.inhousereceiver" >

    <!-- *** Пункт 1 *** Определите внутреннее разрешение на подпись для приёма Широковещательных Сообщений -->
    <permission
    android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION"
    android:protectionLevel="signature" />
    <!-- *** Пункт 2 *** Объявите об использовании внутреннего разрешения на подпись для получения результатов. -->
    <uses-permission
    android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="false" >

        <!-- *** Пункт 3 *** Явно установите атрибут exported в значение true. -->
        <!-- *** Пункт 4 *** Требуйте внутреннего разрешения на подпись статического Широковещательного Приёмника -->

А здесь приведённый текст обрывается. Возможно, в запросе есть продолжение, которое нужно перевести? В динамическом широковещательном приёмнике регистрация/отмена регистрации выполняется через вызов программы `registerReceiver()` или `unregisterReceiver()`, чтобы выполнить регистрацию/отмену регистрации, необходимо определить кнопку в `PublicReceiverActivity`. Поскольку область действия экземпляра динамического широковещательного приёмника больше, чем у `PublicReceiverActivity`, его нельзя сохранить как переменную-член `PublicReceiverActivity`. В этом случае экземпляр динамического широковещательного приёмника следует сохранить как переменную-член `DynamicReceiverService`, а затем запустить/остановить `DynamicReceiverService` из `PublicReceiverActivity` для косвенной регистрации/отмены регистрации динамического широковещательного приёмника.

**InhouseReceiverActivity.java**
```java
package org.jssec.android.broadcast.inhousereceiver;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class InhouseReceiverActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public void onRegisterReceiverClick(View view) {
        Intent intent = new Intent(this, DynamicReceiverService.class);
        startService(intent);
    }

    public void onUnregisterReceiverClick(View view) {
        Intent intent = new Intent(this, DynamicReceiverService.class);
        stopService(intent);
    }
}

DynamicReceiverService.java

package org.jssec.android.broadcast.inhousereceiver;

import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.widget.Toast;

public class DynamicReceiverService extends Service {

    private static final String MY_BROADCAST_INHOUSE = "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";
    private InhouseReceiver mReceiver;

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mReceiver = new InhouseReceiver();
        mReceiver.isDynamic = true;
        IntentFilter filter = new IntentFilter();
        filter.addAction(MY_BROADCAST_INHOUSE);
        filter.setPriority(1); // Prioritize Dynamic Broadcast Receiver, rather than Static Broadcast Receiver.
        // *** POINT 5 *** When registering a dynamic broadcast receiver, require the in-house signature permission.
        registerReceiver(mReceiver, filter, "org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION", null);
        Toast.makeText(this,
            "Registered Dynamic Broadcast Receiver.",
            Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        unregisterReceiver(mReceiver);
        mReceiver = null;
        Toast.makeText(this,
            "Unregistered Dynamic Broadcast Receiver.",
        Toast.LENGTH_SHORT).show();
    }
}

Примечание: в тексте запроса есть фрагменты кода на языке Java, но они не содержат ошибок и не требуют доработки. Код PkgCert.java

public class PkgCert {
    public static boolean test(Context ctx, String pkgname, String correctHash) {
        if (correctHash == null) return false;
        correctHash = correctHash.replaceAll(" ", "");
        return correctHash.equals(hash(ctx, pkgname));
    }

    public static String hash(Context ctx, String sigPermName) {
        if (sigPermName == null) return null;
        try {
            // Get the package name of the application which declares a permission named sigPermName.
            PackageManager pm = ctx.getPackageManager();
            PermissionInfo pi;
            pi = pm.getPermissionInfo(sigPermName, PackageManager.GET_META_DATA);
            String pkgname = pi.packageName;
            // Fail if the permission named sigPermName is not a Signature Permission
            if (pi.protectionLevel != PermissionInfo.PROTECTION_SIGNATURE) return null;
            // Return the certificate hash value of the application which declares a permission named sigPermName.
            return PkgCert.hash(ctx, pkgname);
        } catch (NameNotFoundException e) {
            return null;
        }
    }
}

Текст запроса

При экспорте APK необходимо использовать тот же ключ разработчика, который использовался для подписи приложения.

Далее представлен пример кода для отправки широковещательного сообщения.

Основные шаги: 10. Определить внутреннее разрешение на подпись для получения результатов. 11. Объявить использование внутреннего разрешения на подпись для приёма широковещательных сообщений. 12. Убедиться, что внутреннее разрешение на подпись определено внутренним приложением. 13. Поскольку запрашивающее приложение является внутренним, можно вернуть конфиденциальную информацию. 14. Необходим приёмник с внутренним разрешением на подпись. 15. Осторожно и безопасно обрабатывать полученные данные. 16. При экспорте APK использовать тот же ключ разработчика для подписания APK.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.jssec.android.broadcast.inhousesender" >
    
    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
    
    <!-- *** POINT 10 *** Define an in-house signature permission to receive results. -->
    <permission
    android:name="org.jssec.android.broadcast.inhousesender.MY_PERMISSION"
    android:protectionLevel="signature" />
    
    <!-- *** POINT 11 *** Declare to use the in-house signature permission to receive Broadcasts. -->
    <uses-permission
    android:name="org.jssec.android.broadcast.inhousereceiver.MY_PERMISSION" />
    
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="false" >
        <activity
            android:name="org.jssec.android.broadcast.inhousesender.InhouseSenderActivity"
            android:label="@string/app_name"
            android:exported="true" >
            <intent-filter>
                <action
``` ```
android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

InhouseSenderActivity.java

package org.jssec.android.broadcast.inhousesender;

import org.jssec.android.shared.SigPerm;
import org.jssec.android.shared.Utils;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class InhouseSenderActivity extends Activity {

    // In-house Signature Permission
    private static final String MY_PERMISSION = "org.jssec.android.broadcast.inhousesender.MY_PERMISSION";
    // In-house certificate hash value
    private static String sMyCertHash = null;
    
    private static String myCertHash(Context context) {
        if (sMyCertHash == null) {
            if (Utils.isDebuggable(context)) {
                // Certificate hash value of "androiddebugkey" in the debug.keystore.
                sMyCertHash = "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255";
            } else {
                // Certificate hash value of "my company key" in the keystore.
                sMyCertHash = "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2 42E142CA";
            }
        }
        return sMyCertHash;
    }

    private static final String MY_BROADCAST_INHOUSE =
    "org.jssec.android.broadcast.MY_BROADCAST_INHOUSE";
    
    public void onSendNormalClick(View view) {
        // *** POINT 12 *** Verify that the in-house signature permission is defined by an in-house application.
        if (!SigPerm.test(this, MY_PERMISSION, myCertHash(this))) {
            Toast.makeText(this, "The in-house signature permission is not declared by in-house application.",
            Toast.LENGTH_LONG).show();
            return;
        }
        // *** POINT 13 *** Sensitive information can be returned since the requesting application is in-house.
        Intent intent = new Intent(MY_BROADCAST_INHOUSE);
        intent.putExtra("PARAM", "Sensitive Info from Sender");
        // *** POINT 14 *** Require the in-house signature permission to limit receivers.
        sendBroadcast(intent, "org.jssec.android.broadcast.inhousesender.MY_PERMISSION");
    }
    
    public void onSendOrderedClick(View view) {
        // *** POINT 12 *** Verify that the in-house signature permission is defined by an in-house application.
        if (!SigPerm.test(this, MY_PERMISSION, myCertHash(this))) {
            Toast.makeText(this, "The in-house signature permission is not declared by in-house application.",
            Toast.LENGTH_LONG).set
            return;
        }
        // *** POINT 13 *** Sensitive information can be returned since the requesting application is in-house.
        Intent intent = new Intent(MY_BROADCAST_INHOUSE);
        intent.putExtra("PARAM", "Sensitive Info from Sender");
        // *** POINT 14 *** Require the in-house signature permission to limit receivers.
        sendOrderedBroadcast(intent, "org.jssec.android.broadcast.inhousesender.MY_PERMISSION",
        mResultReceiver, null, 0, null, null);
    }
    
    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // *** POINT 15 *** Handle the received result data carefully and securely,
            // even though the data came from an in-house application.
            // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely."
            String data = getResultData();
            InhouseSenderActivity.this.logLine(String.format("Received result: ¥"%s¥"", data));
        }
    };
    
    private TextView mLogView;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
``` **SigPerm.java**

package org.jssec.android.shared;

import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PermissionInfo;

public class SigPerm { public static boolean test(Context ctx, String sigPermName, String correctHash) { if (correctHash == null) return false; correctHash = correctHash.replaceAll(" ", ""); return correctHash.equals(hash(ctx, sigPermName)); }

public static String hash(Context ctx, String sigPermName) {
    if (sigPermName == null) return null;
    try {
        // Получить имя пакета приложения, которое объявляет разрешение с именем sigPermName.
        PackageManager pm = ctx.getPackageManager();
        PermissionInfo pi;
        pi = pm.getPermissionInfo(sigPermName, PackageManager.GET_META_DATA);
        String pkgname = pi.packageName;
        // Завершить работу, если разрешение с именем sigPermName не является разрешением подписи
        if (pi.protectionLevel != PermissionInfo.PROTECTION_SIGNATURE) return null;
        // Возвратить значение хэша сертификата приложения, которое заявляет разрешение с именем sigPermName.
        return PkgCert.hash(ctx, pkgname);
    } catch (NameNotFoundException e) {
        return null;
    }
}

}


**PkgCert.java**

package org.jssec.android.shared;

import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature;

public class PkgCert { public static boolean test(Context ctx, String pkgname, String correctHash) { if (correctHash == null) return false; correctHash = correctHash.replaceAll(" ", ""); return correctHash.equals(hash(ctx, pkgname)); }

public static String hash(Context ctx, String pkgname) {
    if (pkgname == null) return null;
    try {
        PackageManager pm = ctx.getPackageManager();
        PackageInfo pkginfo = pm.getPackageInfo(pkgname, PackageManager.GET_SIGNATURES);
        if (pkginfo.signatures.length != 1) return null; // Не будет обрабатывать несколько подписей.
        Signature sig = pkginfo.signatures[0];
        byte[] cert = sig.toByteArray();
        byte[] sha256 = computeSha256(cert);
        return byte2hex(sha256);
    } catch (NameNotFoundException e) {
        return null;
    }
}

private static byte[] computeSha256(byte[] data) {
    try {
        return MessageDigest.getInstance("SHA-256").digest(data);
    } catch (NoSuchAlgorithmException e) {
        return null;
    }
}

private static String byte2hex(byte[] data) {
    if (data == null) return null;
    final StringBuilder hexadecimal = new StringBuilder();
    for (final byte b : data) {
        hexadecimal.append(String.format("%02X", b));
    }
    return hexadecimal.toString();
}

}

При экспорте APK используйте тот же ключ разработчика, что и для отправки приложения.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/wizardforcel-android-app-sec-guidebook.git
git@api.gitlife.ru:oschina-mirror/wizardforcel-android-app-sec-guidebook.git
oschina-mirror
wizardforcel-android-app-sec-guidebook
wizardforcel-android-app-sec-guidebook
master