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

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

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

Создание/использование партнёрского сервиса

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

Ниже приведён пример привязки типа службы AIDL.

Основные моменты (создание службы):

  1. Не определять фильтр намерений и явно устанавливать экспортируемый атрибут в значение true.
  2. Проверять, зарегистрирован ли сертификат запрашивающего приложения в собственном белом списке.
  3. Нельзя (невозможно) определить, является ли запрос приложением-партнёром через onBind(onStartCommand, onHandleIntent).
  4. Осторожно и безопасно обрабатывать полученные намерения, даже если намерение отправлено партнёрским приложением.
  5. Возвращать информацию, открытую только для партнёрских приложений.

Кроме того, обратитесь к разделу «5.2.1.3 Как проверить хэш-значение сертификата приложения», чтобы узнать, как проверить хэш-значения целевого приложения, указанные в белом списке.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.jssec.android.service.partnerservice.aidl">
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:allowBackup="false">
        <!-- Service using AIDL -->
        <!-- *** POINT 1 *** Do not define the intent filter and explicitly set the exported attribute to
        true. -->
        <service
            android:name="org.jssec.android.service.partnerservice.aidl.PartnerAIDLService"
            android:exported="true" />
    </application>
</manifest>

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

IExclusiveAIDLServiceCallback.aidl

package org.jssec.android.service.exclusiveservice.aidl;

interface IExclusiveAIDLServiceCallback {
    /**
    * It's called when the value is changed.
    */
    void valueChanged(String info);
}

IExclusiveAIDLService.aidl

package org.jssec.android.service.exclusiveservice.aidl;

import org.jssec.android.service.exclusiveservice.aidl.IExclusiveAIDLServiceCallback;

interface IExclusiveAIDLService {
    /**
    * Register Callback.
    */
    void registerCallback(IExclusiveAIDLServiceCallback cb);
    
    /**
    * Get Information
    */
    String getInfo(String param);
    
    /**
    * Unregister Callback
    */
    void unregisterCallback(IExclusiveAIDLServiceCallback cb);
}

PartnerAIDLService.java

package org.jssec.android.service.partnerservice.aidl;

import org.jssec.android.shared.PkgCertWhitelists;
import org.jssec.android.shared.Utils;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.widget.Toast;

public class PartnerAIDLService extends Service {

    private static final int REPORT_MSG = 1;
    private static final int GETINFO_MSG = 2;
    // The value which this service informs to client
    private int mValue = 0;
    // *** POINT 2 *** Verify that the certificate of the requesting application has been registered in the own white list.
    private static PkgCertWhitelists sWhitelists = null;
    
    private static void buildWhitelists(Context context) {
        boolean isdebug = Utils.isDebuggable(context);
        sWhitelists = new PkgCertWhitelists();
        // Register certificate hash value of partner application "org.jssec.android.service.partnerservice.aidluser"
        sWhitelists.add("org.jssec.android.service.partnerservice.aidluser", isdebug ?
        // Certificate hash value of debug.keystore "androiddebugkey"
            "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255" :
            // Certificate hash value of keystore "partner key"
            "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A");
            // Register other partner applications in the same way
    }
    
    private static boolean checkPartner(Context context, String pkgname) {
        if (sWhitelists == null) buildWhitelists(context);
        return sWhitelists.test(context, pkgname);
    }
    
    // Object to register callback
    // Methods which RemoteCallbackList provides are thread-safe.
    private final RemoteCallbackList<IPartnerAIDLServiceCallback> mCallbacks =
        new RemoteCallbackList<IPartnerAIDLServiceCallback>();
    
    // Handler to send data when callback is called.
    private static class ServiceHandler extends Handler{

...

*Примечание: предоставленный перевод не является точным и может содержать ошибки.* Вот перевод текста на русский язык:

    private Context mContext;
    private RemoteCallbackList<IPartnerAIDLServiceCallback> mCallbacks;
    private int mValue = 0;

    public ServiceHandler(Context context, RemoteCallbackList<IPartnerAIDLServiceCallback> callback, int value){
        this.mContext = context;
        this.mCallbacks = callback;
        this.mValue = value;
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case REPORT_MSG: {
                if(mCallbacks == null){
                    return;
                }
                // Start broadcast
                // To call back on to the registered clients, use beginBroadcast().
                // beginBroadcast() makes a copy of the currently registered callback list.
                final int N = mCallbacks.beginBroadcast();
                for (int i = 0; i < N; i++) {
                    IPartnerAIDLServiceCallback target = mCallbacks.getBroadcastItem(i);
                    try {
                        // *** POINT 5 *** Information that is granted to disclose to partner applications can be returned.
                        target.valueChanged("Информация, раскрытая партнерскому приложению (обратный вызов от сервиса) №" + (++mValue));
                    } catch (RemoteException e) {
                        // Callbacks are managed by RemoteCallbackList, do not unregister callbacks here.
                        // RemoteCallbackList.kill() unregister all callbacks
                    }
                }
                // finishBroadcast() cleans up the state of a broadcast previously initiated by calling beginBroadcast().
                mCallbacks.finishBroadcast();
                // Repeat after 10 seconds
                sendEmptyMessageDelayed(REPORT_MSG, 10000);
                break;
            }
            case GETINFO_MSG: {
                if(mContext != null) {
                    Toast.makeText(mContext,
                        (String) msg.obj, Toast.LENGTH_LONG).show();
                }
                break;
            }
            default:
                super.handleMessage(msg);
                break;
        } // switch
    }

    protected final ServiceHandler mHandler = new ServiceHandler(this, mCallbacks, mValue);

    // Interfaces defined in AIDL
    private final IPartnerAIDLService.Stub mBinder = new IPartnerAIDLService.Stub() {
        private boolean checkPartner() {
            Context ctx = PartnerAIDLService.this;
            if (!PartnerAIDLService.checkPartner(ctx, Utils.getPackageNameFromUid(ctx, getCallingUid()))) {
                mHandler.post(new Runnable(){
                    @Override
                    public void run(){
                        Toast.makeText(PartnerAIDLService.this, "Запрашивающее приложение не является партнерским приложением.", Toast.LENGTH_LONG).show();
                    }
                });
                return false;
            }
            return true;
        }

        public String getInfo(String param) {
            // *** POINT 2 *** Verify that the certificate of the requesting application has been registered in the own white list.
            if (!checkPartner()) {
                return null;
            }
            // *** POINT 4 *** Handle the received intent carefully and securely,
            // even though the intent was sent from a partner application
            // Опущено, поскольку это образец. Пожалуйста, обратитесь к разделу «3.2 Обработка входных данных тщательно и безопасно».
            Message msg = new Message();
            msg.what = GETINFO_MSG;
            msg.obj = String.format("Метод, вызываемый из партнерского приложения. Получено ¥"%s¥"", param);
            PartnerAIDLService.this.mHandler.sendMessage(msg);
            // *** POINT 5 *** Возвращать только информацию, которая разрешена для раскрытия партнерскому приложению.
            return "Информация, раскрываемая партнерскому приложению (метод из сервиса)";
        }

        public void unregisterCallback(IPartnerAIDLServiceCallback cb) {
            // *** POINT 2 *** Проверить... **PkgCertWhitelists.java**

package org.jssec.android.shared;

import java.util.HashMap; import java.util.Map; import android.content.Context;

public class PkgCertWhitelists { private Map<String, String> mWhitelists = new HashMap<String, String>();

public boolean add(String pkgname, String sha256) {
    if (pkgname == null) return false;
    if (sha256 == null) return false;
    sha256 = sha256.replaceAll(" ", "");
    if (sha256.length() != 64) return false; // SHA-256 -> 32 bytes -> 64 chars
    sha256 = sha256.toUpperCase();
    if (sha256.replaceAll("[0-9A-F]+", "").length() != 0) return false; // found non hex char
    mWhitelists.put(pkgname, sha256);
    return true;
}

public boolean test(Context ctx, String pkgname) {
    // Get the correct hash value which corresponds to pkgname.
    String correctHash = mWhitelists.get(pkgname);
    // Compare the actual hash value of pkgname with the correct hash value.
    return PkgCert.test(ctx, pkgname, correctHash);
}

}


**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.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; // Will not handle multiple signatures.
        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();
}

}


*Использование партнёрского сервиса:*

Ключевые моменты (использование сервиса):

1. Проверить, зарегистрирован ли сертификат запрашивающего приложения в собственном белом списке.
2. Запрашивающее приложение должно быть проверено методами, определёнными в AIDL. **ЭксклюзивАйдлЮзерАктивти. Ява**

```java
пакет орг. джс сек. андроид. сервис. партнер сервис. айдл юзер;

импорт орг. джс сек. андройд. сервис. партнёр сервис. айдл. ИПартнерАйдлСервис;
импорт орг. джс сек. андройд. сервис. партнёр сервис. айдл. ИПартнерАйдлСервискейблбэк;
импорт орг. джс сек. андройд. шаред. ПкгКертВитлистс;
импорт орг. джс сек. андройд. шаред. Утилс;
импорт андройд. апп. Активти;
импорт андройд. контент. Контекстнт;
импорт андройд. контент. Интент;
импорт андройд. контент. Сервисконекшн;
импорт андройд. ос. Бандл;
импорт андройд. ос. Хэндлер;
импорт андройд. ос. Мессадж;
импорт андройд. ос. Ремотеэксепшн;
импорт андройд. виев. Вью;
импорт андройд. виджет. Тост;

публичный класс ПартнёрАйдлЮзерАктивти расширяет Активти {
    
    приват бул мИсБонд;
    приват Контекстнт мКонтекстнт;
    приват фиксэд инт МГЗ_ВЭЛЮЕ_ЧАНГЭД = 1;
    // *** ПОЙНТ 6 *** Верифай, иф зе сертификат оф зэ таргэт аппликэйшн хэз биэн рэджистэрд ин зэ уан уайт лист.
    приват статик ПкгКертВитлистс сВитлистс = нулл;
    
    приват статик воид билдВитлистс(Контекстнт контекстнт) {
        бул из дебаг = Утилс. из Дебагэбэл(контекстнт);
        сВитлистс = нью ПкгКертВитлистс();
        // Рэджистэр сертификат хэш вэлй оф партнэр сервис аппликэйшн "орг. джс сек. андройд. сервис. партнёр сервис. айдл"
        сВитлистс. адд("орг. джс сек. андройд. сервис. партнёр сервис. айдл", из дебаг ?
            // Сертификат хэш вэлй оф дебаг точка кейстор "андроид дебаг кей"
            "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255" :
            // Сертификат хэш вэлй оф кейстор "май компани кей"
            "D397D343 A5CBC10F 4EDDEB7C A10062DE 5690984F 1FB9E88B D7B3A7C2 42E142CA");
            // Рэджистэр оутер партнэр сервис аппликатионс ин зэ сам вэй
    }
    
    приват статик бул чекПартнэр(Контекстнт контекстнт, стрнг пкгнэйм) {
        иф (сВитлистс == нулл) билдВитлистс(контекстнт);
        ретёрн сВитлистс. тест(контекстнт, пкгнэйм);
    }
    
    // Информэйшн эбаут дестинатион (рекуэстэд) партнэр активти.
    приват фиксид стрнг ТАРГЭТ_ПАКЭДЖЭ = "орг. джс сек. андройд. сервис. партнёр сервис. айдл";
    приват фиксид стрнг ТАРГЭТ_КЛАСС = "орг. джс сек. андройд. сервис. партнёр сервис. айдл партнэр айдл сервис";
    
    приват статик класс РисивХэндлер расширяет Хэндлер{
        приват Контекстнт мКонтекстнт;
        публик РисивХэндлер(Контекстнт контестнт){
        диз зис. мКонтекстнт = контестнт;
    }
    
    овиррид публ воид хендлэМэссэйдж(Мессадж мэссэйдж) {
        свап чэ ин мэссэйдж {
            кей МГЗ_ВЭЛЮЭ_ЧАНГЭД {
                стрнг инфо = (стрнг)мэссэйдж. обдж;
                Тост. мэйтэк(мКонтекстнт, формат("Рекуивэд ¥"%с¥" уитх кэбкулбэк.", инфо), Тост. ЛЕНГТХ_ШОРТ).шоу();
                брейк;
            }
            дефолт:
                супер. хэндлэМэссэйдж(мэссэйдж);
                брейк;
        } // свап
    }
    
    приват фэкс РисивХэндлэр мХэндлэр = нью РисивХэндлэр(зис);
    
    // Интерфэйс дэфинд ин Айдл. Рисив нотис фром сервис
    приват финаль ИПартнэрАйдлСэрвисКэбкулбэкстуб мКэбкулбэк =
    нью ИПартнэрАйдлСэрвисКэбкулбэкстуб() {
        оверрид
        публ воид вэлЮэЧангэд(стрнг инфо) трэис РемотеЭксепшн {
            Мессадж мэссэйдж = мХэндлэр. овэнэрМэссэйдж(МГЗ_ВЭЛЮЭ_ЧАНГЭД, инфо);
            мХэндлэр. сэнтМэссэйдж(мэссэйдж);
        }
    };

    // Интерфэйс дэфинд ин Айдл. Информ сервйс.
    приват ИПартнэрАйдлСэрвис мСэрвис = нулл;
    // Коннекшн юзд уэн сервйс из имплэмэнтэд уиз биндСэрвис().
    приват Сервисконекшн мКонекшн = нью Сервисконекшн() {
        // Зис из колд уэн зэ коннекшн уиз зэ сервйс хас бин энэстэблэд.
        оверрид
        публ воид онСэрвичКоннектэд(Компонентнэйм клэйсНэйм, ИБиндер сэруис) {
            мСэрвис = ИПартнэрАйдлСэрвис. Стуб. асИнтэрфэйс(сэруис);
            трай{
                // коннэк ту сервйс

Перевод приведён без технической редактуры.

В запросе представлен исходный код на языке Java, который является частью класса PartnerAIDLUserActivity. Данный класс расширяет Activity и содержит методы для обработки данных, полученных от партнёра через интерфейс AIDL. Также в классе реализованы методы для проверки сертификата партнёра и подключения к сервису.

Класс содержит приватные поля, методы и статические переменные, которые используются для работы с данными и взаимодействия с партнёром.

Для получения дополнительной информации о классе и его методах рекомендуется обратиться к документации по языку Java или к специалистам по разработке программного обеспечения. Текст запроса написан на языке Java.

mService.registerCallback(mCallback); }catch(RemoteException e){ // service stopped abnormally } Toast.makeText(mContext, "Connected to service", Toast.LENGTH_SHORT).show(); }

// This is called when the service stopped abnormally and connection is disconnected. @Override public void onServiceDisconnected(ComponentName className) { Toast.makeText(mContext, "Disconnected from service", Toast.LENGTH_SHORT).show(); } };

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.partnerservice_activity); mContext = this; }

// --- StartService control --- public void onStartServiceClick(View v) { // Start bindService doBindService(); }

public void onGetInfoClick(View v) { getServiceinfo(); }

public void onStopServiceClick(View v) { doUnbindService(); }

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

/**

  • Connect to service */ private void doBindService() { if (!mIsBound){ // *** POINT 6 *** Verify if the certificate of the target application has been registered in the own white list. if (!checkPartner(this, TARGET_PACKAGE)) { Toast.makeText(this, "Destination(Requested) sevice application is not registered in white list.", Toast.LENGTH_LONG).show(); return; } Intent intent = new Intent(); // *** POINT 7 *** Return only information that is granted to be disclosed to a partner application. intent.putExtra("PARAM", "Information disclosed to partner application"); // *** POINT 8 *** Use the explicit intent to call a partner service. intent.setClassName(TARGET_PACKAGE, TARGET_CLASS); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; } }

/**

  • Disconnect service */ private void doUnbindService() { if (mIsBound) { // Unregister callbacks which have been registered. if(mService != null){ try{ mService.unregisterCallback(mCallback); }catch(RemoteException e){ // Service stopped abnormally // Omitted, since it' s sample. } } unbindService(mConnection); Intent intent = new Intent(); // *** POINT 8 *** Use the explicit intent to call a partner service. intent.setClassName(TARGET_PACKAGE, TARGET_CLASS); stopService(intent); mIsBound = false; } }

/**

  • Get information from service */ void getServiceinfo() { if (mIsBound && mService != null) { String info = null; try { // *** POINT 7 *** Return only information that is granted to be disclosed to a partner application. info = mService.getInfo("Information disclosed to partner application (method from activity)"); } catch (RemoteException e) { e.printStackTrace(); } // *** POINT 9 *** Handle the received result data carefully and securely, // even though the data came from a partner application. // Omitted, since this is a sample. Please refer to "3.2 Handling Input Data Carefully and Securely." Toast.makeText(mContext, String.format("Received ¥"%s¥" from service.", info), Toast.LENGTH_SHORT).show(); } }

PkgCertWhitelists.java

```java
package org.jssec.android.shared;

import java.util.HashMap;
import java.util.Map;
import android.content.Context;

public class PkgCertWhitelists {

    private Map<String, String> mWhitelists = new HashMap<String, String>();

    public boolean add(String pkgname, String sha256) {
        if

**Это исходный текст с незначительными изменениями форматирования.** ```
pkgname == null return false;
if (sha256 == null) return false;
sha256 = sha256.replaceAll(" ", "");
if (sha256.length() != 64) return false; // SHA-256 -> 32 bytes -> 64 chars
sha256 = sha256.toUpperCase();
if (sha256.replaceAll("[0-9A-F]+", "").length() != 0) return false; // found non hex char
mWhitelists.put(pkgname, sha256);
return true;
}

public boolean test(Context ctx, String pkgname) {
// Get the correct hash value which corresponds to pkgname.
String correctHash = mWhitelists.get(pkgname);
// Compare the actual hash value of pkgname with the correct hash value.
return PkgCert.test(ctx, pkgname, correctHash);
}

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; // Will not handle multiple signatures.
            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();
    }
}

Опубликовать ( 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