Создание/использование партнёрского сервиса
Партнёрский сервис — это сервис, который может использоваться только определённым приложением. Система состоит из приложений от партнёрских компаний и внутренних приложений и предназначена для защиты информации и функций, обрабатываемых между партнёрскими и внутренними приложениями.
Ниже приведён пример привязки типа службы AIDL.
Основные моменты (создание службы):
Кроме того, обратитесь к разделу «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(); }
/**
/**
/**
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 )