Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - SMS Retriever API - Computing app's hash string problem

Tags:

I am really new in Android and I am trying to implement the SMS Retriever API for using OTP in my app.

I am following this guide: https://developers.google.com/identity/sms-retriever/verify#computing_your_apps_hash_string

Unfortunately I'm stuck in the part of "Computing your app's hash string"

I am quoting here the guide sections, and below each one- my question:

  1. Get your app's public key certificate as a lower-case hex string. For example, to get the hex string from your keystore, type the following command

    keytool -alias MyAndroidKey -exportcert -keystore MyProduction.keystore | xxd -p | tr -d "[:space:]"
    

Where can I find my "public key certificate", and where am I supposed to run this command?

  1. Compute the SHA-256 sum of the combined string.

What is SHA-256 and what does it mean to compute it?

  1. Base64-encode the binary value of the SHA-256 sum. You might need to decode the SHA-256 sum from its output format first.

Can't understand, what am I supposed to do here?

like image 214
levi Avatar asked Dec 19 '18 10:12

levi


2 Answers

Google has created a script to wrap the necessary CLI commands to generate the app hash.

Usage is:

./sms_retriever_hash_v9.sh --package "com.your.packagename" --keystore /path/to/your.keystore

Example output:

$ ./sms_retriever_hash_v9.sh --package "com.your.packagename" --keystore debug.keystore

package name: com.your.packagename
keystore file: debug.keystore

File debug.keystore is found.

Enter keystore password:  


certificate in hex: 3082030d308201f5a003020102020475125fad300d06092a864886f70d01010b05003037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f6964204465627567301e170d3135313132333231323734355a170d3435313131353231323734355a3037310b30090603550406130255533110300e060355040a1307416e64726f6964311630140603550403130d416e64726f696420446562756730820122300d06092a864886f70d01010105000382010f003082010a0282010100c7604e3b464d0c3f1b556aecfbfcd60b35bb8274909c3eac8825d909b47d44ad60f3dcbd3bdb270a91ed09a8f4c7d39a7da51519116ab2085fdc5761ab472c53860e71779dbf1ebdb5ce2d0140197ac9bcc6ab0e249440be09e233885b110a0fce4b04c903b7741cbc31207ceeb55f71f02b59c2771986238972610cf33e472c08d3b67147117f356617357300dac2655cfa3c056fcc12aa5837a22f9af82164008aae32564db25c2801a45cb66bc087fa8710d14f6448446bc43fb5938c30306959eb5e03dee3dfaf1c83d684338c213208b94a6ea2aa937ba00dd800cbe5b6e30a5a3752b95e5948b20eb6a7051768395e498d12cf2e507458e14e9433d7d70203010001a321301f301d0603551d0e04160414efd057879cfb3ed6c9122caa5d26a6da5f59aadd300d06092a864886f70d01010b0500038201010074004b26417b91333a0503e505030784172a5ac5ffa68d02d42f5991fa637365a3c4833707d062063210da0c16f32be730081420b4ec9563475a57f02f2bf0364cbdc01154e9921edd5140bb4218d7ec6fd3f062d1acacc7cc005c64b7f7e362601fea2a7571c395ecf071a0f10a1bf3c44aa874eb61375e11308ec318c81f4bbd701de2d2fcbbbf764507074da570636f740b379652afe386eb48f69407074b096f3ce03e1d7ac50d9b79169132b01d75389959255b530549a3179798503c83e153e6feb78a89ef80bfce197e23314740f1d55a0db140eb2a44d3acce82d41503b180b6e8ed28f2411f750f9308c72cd8867486ad64af593bc1f1fff5b30510

SHA-256 output in hex: 20e861ecc8550c1e608efc3006f82278025d5e3d7169b40c72b8c3dd0aa9cfd9

First 8 bytes encoded by base64: IOhh7MhVDB5

SMS Retriever hash code:  IOhh7MhVDB5

Save the raw script file locally then run chmod u+x sms_retriever_hash_v9.sh to make it executable.

In case the link to the script disappears, here's the script contents:

#!/bin/sh

# ------------------------------------------------------------------
# [Author] Title
#          Description
# ------------------------------------------------------------------

VERSION=0.1.0
SUBJECT=sms-retriever-hash-generator
USAGE="Usage: sms_retriever_hash_v9.sh --package package_name --keystore keystore_file"

# --- Options processing -------------------------------------------
if [ $# == 0 ] ; then
    echo $USAGE
    exit 1;
fi

# USE: apkblacklister.sh --source source.apk --target target.apk more files to scan

if [[ "$1" != "--package" ]]; then
  echo "Error: expected --package as first parameter"
  exit 1
fi
pkg="$2"
shift 2

if [[ "$1" != "--keystore" ]]; then
  echo "Error: expected --keystore as third parameter"
  exit 1
fi
keystore="$2"
shift 2



echo
echo "package name: $pkg"
echo "keystore file: $keystore"
echo 

if [ -e "$keystore" ]
then
  echo "File $keystore is found."
  echo
else
  echo "File $keystore is not found."
  echo
  exit 0;
fi

# Retrieve certificate from keystore file. Decoded with Base64 and converted to hex
cert=$(keytool -list -rfc -keystore $keystore | sed  -e '1,/BEGIN/d' | sed -e '/END/,$d' | tr -d ' \n' | base64 --decode | xxd -p | tr -d ' \n')

echo
echo "certificate in hex: $cert"


# concatenate input
input="$pkg $cert"

# 256 bits = 32 bytes = 64 hex chars
output=$(printf "$input" | shasum -a 256 | cut -c1-64)
echo
echo "SHA-256 output in hex: $output"

# take the beginning 72 bits (= 9 bytes = 18 hex chars)
output=$(printf $output | cut -c1-18)

# encode sha256sum output by base64 (11 chars)
base64output=$(printf $output | xxd -r -p | base64 | cut -c1-11)
echo
echo "First 8 bytes encoded by base64: $base64output"
echo
echo "SMS Retriever hash code:  $base64output"
echo
like image 65
DaveAlden Avatar answered Oct 21 '22 01:10

DaveAlden


import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.util.Base64;
import android.util.Log;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;


/*
  This is a helper class to generate your message hash to be included in your SMS message.

  Without the correct hash, your app won't recieve the message callback. This only needs to be
  generated once per app and stored. Then you can remove this helper class from your code.
*/

public class AppSignatureHelper extends ContextWrapper {
    public static final String TAG = AppSignatureHelper.class.getSimpleName();
    private static final String HASH_TYPE = "SHA-256";
    public static final int NUM_HASHED_BYTES = 9;
    public static final int NUM_BASE64_CHAR = 11;

    public AppSignatureHelper(Context context) {
        super(context);
        getAppSignatures();
    }

    /**
     * Get all the app signatures for the current package
     * @return
     */
    public ArrayList<String> getAppSignatures() {
        ArrayList<String> appCodes = new ArrayList<>();

        try {
            // Get all package signatures for the current package
            String packageName = getPackageName();
            PackageManager packageManager = getPackageManager();
            Signature[] signatures = packageManager.getPackageInfo(packageName,
                    PackageManager.GET_SIGNATURES).signatures;

            // For each signature create a compatible hash
            for (Signature signature : signatures) {
                String hash = hash(packageName, signature.toCharsString());
                if (hash != null) {
                    appCodes.add(String.format("%s", hash));
                }

                Log.v(TAG, "Hash " + hash);

            }
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "Unable to find package to obtain hash.", e);
        }
        return appCodes;
    }

    private static String hash(String packageName, String signature) {
        String appInfo = packageName + " " + signature;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
            }
            byte[] hashSignature = messageDigest.digest();

            // truncated into NUM_HASHED_BYTES
            hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
            // encode into Base64
            String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
            base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);

            Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
            return base64Hash;
        } catch (NoSuchAlgorithmException e) {
            Log.e(TAG, "hash:NoSuchAlgorithm", e);
        }
        return null;
    }
}

Add the above class in your project and then call it from your LoginActivity like below:

AppSignatureHelper appSignatureHelper = new AppSignatureHelper(LoginActivity.this);

This way you will get 11 digit hash and you'll be able to get it as:

Log.v(TAG, appSignatureHelper.getAppSignatures().get(0));
like image 31
Teja Avatar answered Oct 20 '22 23:10

Teja