2010-09-21 13 views
21

Estoy desarrollando una aplicación en este momento que contiene bastante información personal del usuario - cosas como contactos de Facebook, etc ... Ahora, uno de las cosas que quiero ser capaz de hacer (y lo he hecho de manera bastante efectiva) son abrir partes de la aplicación a aplicaciones de "terceros", utilizando el protocolo de comunicación interproceso (AIDL) incorporado de Android. Hasta aquí todo bien.Implementar seguridad de nivel de firma en servicios de Android con más de una firma permitida

Aquí está el truco: porque estamos involucrados en el manejo de una gran cantidad de información personal, tenemos que tener mucho cuidado sobre quién puede y no puede acceder a ella; específicamente, solo las aplicaciones "de confianza" deberían poder hacerlo. Entonces, la forma más natural de hacerlo es usar un permiso personalizado dentro del archivo AndroidManifest.xml donde declaramos los servicios. Mi problema es el siguiente: Quiero ser capaz de promulgar protección a nivel de firma (similar al nivel de permiso normal "firma"), pero con un poco de la captura:

yo no solamente desee solicitud firmada con nuestra firma interna para poder acceder a los servicios. Me gustaría poder crear una lista de "firmas de confianza" & en tiempo de ejecución (o si hay una forma mejor, ¿luego tal vez en algún otro momento?) Poder verificar las solicitudes entrantes con esta lista de claves de confianza.

Esto satisfaría las restricciones de seguridad del mismo modo que el nivel de permiso de "firma" normal, creo que solo los programas en la "lista de claves de confianza" podrían acceder a los servicios, y las claves son difíciles de falsificar (si posible en absoluto?) - pero con la ventaja adicional de que no tendríamos que firmar cada aplicación haciendo uso de las API con la clave de nuestro equipo interno.

¿Es esto posible por el momento en Android? Y si es así, ¿hay algún requisito especial?

Gracias

Respuesta

14

Ahora he encontrado la respuesta a esta pregunta, pero lo dejo por el bien de todos aquellos que buscan en el futuro.

Abrí una discusión sobre android-security-discuss donde fue respondida. Enlace: http://groups.google.com/group/android-security-discuss/browse_thread/thread/e01f63c2c024a767

Respuesta corta:

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(); 

    } 

Dónde Security.trustedSignatures es un mapa de la forma:

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

Ponga este método dentro de cualquier código que está siendo llamado por el proceso externo (es decir, dentro de su interfaz). Tenga en cuenta que esto hará que no tenga el efecto deseado dentro del método onBind() de su RemoteService.

+0

Excelente información, gracias. –

+0

no podemos simplemente verificar el nombre del paquete en el enlace y si la aplicación no está autorizada. paquete intente acceder y luego devolver el valor nulo devolviendo el objeto ibinder. –

+1

@kamal_tech_view Desafortunadamente, no creo que ese enfoque funcione como espera, ya que 'onBind()' solo se llama una vez, por el sistema operativo (consulte aquí: https://groups.google.com/d/msg/ android-security-discuss/4B9jwsAkp2c/QJc4A5FSWCMJ). Tenga en cuenta el consejo de utilizar una fábrica para proteger la parte sensible del sistema en el primer punto de llamada, no lo he investigado, pero creo que desearía verificar la configuración en el 'SecurityManager' actual (usted puede encontrar que el llamado puede obtener sus campos ocultos a través de la introspección y evitar su cheque). – jelford

1

Gran información jelford, pero yo sugeriría en lugar de almacenar toda la cadena de la firma, para almacenar/comparar el SHA-1 del certificado como se muestra en este answer from matreshkin.

Esto es similar a cómo Google handles the Maps Android API, y esto coincidirá con la salida que se muestra a través de la herramienta de 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(); 
} 
+0

Sugeriría que no hay razón para no almacenar toda la clave pública. SHA-ing (o usando cualquier hash antes de almacenar) introduce la posibilidad de una colisión clave (es poco probable que ocurra por accidente, es fácil de causar por un tercero malicioso). Dado que las claves públicas no son sensibles, realmente no gana mucho haciendo esto. – jelford

+0

Según tengo entendido, el objetivo de utilizar una función hash criptográfica como SHA-1 es hacer que sea difícil/inviable encontrar otra entrada que coincida con la salida. Aunque tendría sentido pasar a SHA-2/3 en este momento en lugar de SHA-1 y la herramienta de claves actual de Java también muestra SHA-256 de certificado. 'MessageDigest md = MessageDigest.getInstance ("SHA-256");' El principal beneficio que veo es en la legibilidad del código, la gestión de los certs de confianza y un cierto nivel de ofuscación, evitando que pasa alrededor de una larga cadena si el la aplicación está descompilada – PRIZ3

+0

Sugeriría que hay mejores formas de mejorar la legibilidad del código y la administración de claves que las claves hash: colóquelas en las estructuras de datos apropiadas y use las herramientas disponibles. La razón por la que las claves se muestran a menudo como hashes es que las hace más fáciles de leer y reconocer, como dices. Pero para fines criptográficos, no es una buena idea tirar los N bits adicionales de tu llave que pierdes al mezclarlos. Las claves públicas son, como su nombre indica, completamente públicas; no se gana nada al ofuscarlos u oscurecerlos. – jelford

Cuestiones relacionadas