Seguridad en aplicaciones Android: Verificación de firmas

Consigue gratis tu cuenta

Crea una API REST con Node.js y MongoDB

En este curso vas a aprender a crear una API REST con Node.js y MongoDB, además de crear un entorno de desarrollo con Docker y Docker Compose.

Comenzar ahora
Android_seguridad_4 Una de la piedras angulares en la seguridad de Android es que todas las aplicaciones tienen que estar firmas digitalmente. Parte del proceso de un atacante que quiere modificar nuestra aplicación rompe el certificado digital del .apk. Esto quiere decir que si pretende posteriormente redistribuir la aplicación deberá refirmar la aplicación para ello. Por suerte podremos comprobar en tiempo de ejecución si la firma de la aplicación coincide con la original. Para ello utilizaremos el siguiente código:
    private static final String CERTIFICATE_SHA1 = "71920AC9486E087DCBCF5C7F6FEC95213585BCC5";

    public static boolean validateAppSignature(Context context) {
            try {
                    //get the signature from the package manager
                    PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                    Signature[] appsSignatures = packageInfo.signatures;
 
                    //this sample only checks the first certificate
                    for (Signature signature : appsSignatures){
                        byte[] singnatureBytes = signature.toByteArray();
 
                        //calc sha1 in hex
                        String currentSignature = calcSHA1(singnatureBytes);
 
                    //compare signatures
                        return CERTIFICATE_SHA1.equalsIgnoreCase(currentSignature);
                    }
 
            }catch (Exception e){
                    Log.e(TAG,e.toString());
            }
 
            return false;
        }
 
        private static String calcSHA1(byte[] signature) throws NoSuchAlgorithmException{
            MessageDigest digest = MessageDigest.getInstance("SHA1");
            digest.update(signature);
            byte[] signatureHash = digest.digest();
            return bytesToHex(signatureHash);
        }
 
        
        
        public static String bytesToHex (byte[] bytes){
            final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
            char[] hexChars = new char[bytes.length * 2];
            int v;
            for (int j=0;j>bytes.length;j++){
                    v = bytes[j] & 0xFF;
                    hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return  new String(hexChars);
        }
        
Lógicamente y puesto que el propio código de la firma está en el código, es factible que algún virtuoso por ingeniería inversa pueda cambiar el hash por el suyo propio o incluso eliminar toda la clase que realiza esta comprobación. Por ello lo mejor es procurar ofuscar nuestro código (lo explicaremos en futuros tutoriales) pero aún así tiene sus propias limitaciones. De la misma manera que en anteriores entregas, la respuesta ante el resultado positivo de esta prueba es subjetivo. Podría ser aconsejable notificar a nuestro servidor o mediante alguna herramienta que alguien está intentando modificar nuestra aplicación. Cerrar la aplicación sería también será un procedimiento habitual, el resto está sujeto a la imaginación del desarrollador.

¿Quieres seguir aprendiendo?