Подтвердить что ты не робот

Внедрить безопасность на уровне подписки на услуги Android с более чем одной разрешенной подписью

В настоящее время я разрабатываю приложение, которое содержит довольно много личной информации пользователя - такие вещи, как контакты Facebook и т.д. Теперь, одна из вещей, которые я хочу сделать (и сделал, довольно эффективно) открывает части приложения для приложений "третьей стороны", используя встроенный межсетевой протокол связи (AIDL) для Android. Пока все хорошо.

Вот улов: потому что мы занимаемся обработкой довольно много личной информации, мы должны быть очень осторожны, кто может и не может получить к нему доступ; в частности, только "доверенные" приложения должны быть в состоянии сделать это. Таким образом, естественным способом сделать это является использование пользовательского разрешения в файле AndroidManifest.xml, где мы объявляем службы. Моя проблема заключается в следующем: я хочу иметь возможность устанавливать защиту уровня подписи (аналогично нормальному уровню "подписи" ), но с небольшим количеством улов:

Я не только хочу, чтобы приложение, подписанное с нашей внутренней подписью, имело возможность доступа к сервисам. Я хотел бы иметь возможность создавать список "доверенных подписей" и во время выполнения (или если есть лучший способ, а может быть, какое-то другое время?) Иметь возможность проверять входящие запросы на этот список доверенных ключей.

Это будет удовлетворять ограничениям безопасности так же, как и нормальный уровень разрешения "подписи", который я думаю: только программы из списка "доверенные ключи" смогут получить доступ к службам, а ключи трудно подделать (если возможно на всех?), но с добавленным бонусом, что нам не нужно будет подписывать каждое приложение, использующее API, с помощью нашего внутреннего командного ключа.

Возможно ли это на данный момент в Android? И если да, существуют ли особые требования?

Спасибо

4b9b3361

Ответ 1

Теперь я нашел ответ на этот вопрос, но я оставлю его ради любого, кто смотрит в будущее.

Я открыл дискуссию по обсуждению android-security, где был дан ответ. Ссылка: http://groups.google.com/group/android-security-discuss/browse_thread/thread/e01f63c2c024a767

Короткий ответ:

    private boolean checkAuthorised(){
        PackageManager pm = getPackageManager();
        try {
            for (Signature sig :
                pm.getPackageInfo(pm.getNameForUid(getCallingUid()),
                        PackageManager.GET_SIGNATURES).signatures){
                LogUtils.logD("Signature: " + sig.toCharsString());
                if (Security.trustedSignatures.get(sig.toCharsString()) != null) {
                    return true;
                }
            }
        } catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        LogUtils.logD("Couldn't find signature in list of trusted keys! Possibilities:");
        for(String sigString : Security.trustedSignatures.keySet()){
            LogUtils.logD(sigString);
        }

        /* Crash the calling application if it doesn't catch */
        throw new SecurityException();

    }

Где Security.trustedSignatures - это карта формы:

Map<String,String>().put("public key","some description eg. name");

Поместите этот метод в любой код, который вызывается внешним процессом (т.е. внутри вашего интерфейса). Обратите внимание, что это не будет иметь желаемого эффекта внутри метода onBind() вашего RemoteService.

Ответ 2

Отличная информация jelford, но я бы предложил вместо хранения всей строки подписи сохранить/сравнить SHA-1 сертификата, как показано в этом ответе от matreshkin.

Это похоже на то, как Google обрабатывает API Android для Android, и это будет соответствовать выходному показателю с помощью keytool.

private boolean checkAuthorized() throws SecurityException {
    PackageManager pm = getPackageManager();
    try {
        PackageInfo packageInfo = pm.getPackageInfo(pm.getNameForUid(getCallingUid()),
            PackageManager.GET_SIGNATURES);
        Signature[] signatures = packageInfo.signatures;
        byte[] certBytes = signatures[0].toByteArray();
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(
            new ByteArrayInputStream(certBytes));
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] encodedCert = md.digest(cert.getEncoded());
        String hexString = byte2HexFormatted(encodedCert);

        Log.d("public certificate SHA-1: " + hexString);

        String trustedAppName = trustedCerts.get(hexString);
        if (trustedAppName != null) {
            Log.d("Found public certificate SHA-1 for " + trustedAppName);
            return true;
        }
    } catch (Exception e) {
        Log.e(e, "Unable to get certificate from client");
    }

    Log.w("Couldn't find signature in list of trusted certs!");
    /* Crash the calling application if it doesn't catch */
    throw new SecurityException();
}

public static String byte2HexFormatted(byte[] arr) {
    StringBuilder str = new StringBuilder(arr.length * 2);
    for (int i = 0; i < arr.length; i++) {
        String h = Integer.toHexString(arr[i]);
        int l = h.length();
        if (l == 1) h = "0" + h;
        if (l > 2) h = h.substring(l - 2, l);
        str.append(h.toUpperCase());
        if (i < (arr.length - 1)) str.append(':');
    }
    return str.toString();
}