Created
July 7, 2020 17:24
-
-
Save arberg/ac70e2ecc4f3c7249c52b639309d0900 to your computer and use it in GitHub Desktop.
Android Sms AutoRetrieval hash-generator in Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sms | |
import android.annotation.SuppressLint | |
import android.content.Context | |
import android.content.pm.PackageManager | |
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 receive the message callback. This only needs to be | |
* generated once per app and stored. Then you can remove this helper class from your code. | |
* | |
* See https://stackoverflow.com/questions/53849023/android-sms-retriever-api-computing-apps-hash-string-problem | |
* See https://developers.google.com/identity/sms-retriever/verify#generating_a_one-time_code | |
*/ | |
class AppSignatureSmsVerificationHelper(context: Context) { | |
/** | |
* Get all the app signatures for the current package | |
*/ | |
val appSignatures: ArrayList<String> | |
val appSignature: String? | |
get() = appSignatures.firstOrNull() | |
init { | |
val appCodes = ArrayList<String>() | |
try { | |
val packageName = context.packageName | |
val packageManager = context.packageManager | |
val signatures = | |
if (Build.VERSION.SDK_INT >= 28) { | |
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo.apkContentsSigners | |
} else { | |
@Suppress("DEPRECATION") | |
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures | |
} | |
// For each signature create a compatible hash | |
for (signature in signatures) { | |
val hash = hash(packageName, signature.toCharsString()) | |
if (hash != null) { | |
appCodes.add(String.format("%s", hash)) | |
} | |
Log.v(TAG, "Hash $hash") | |
} | |
} catch (e: PackageManager.NameNotFoundException) { | |
Log.w(TAG, "PackageManager error", e) | |
} | |
appSignatures = appCodes | |
} | |
/** | |
* Generates the hash by running sha on the string 'package hash'. | |
*/ | |
@SuppressLint("ObsoleteSdkInt") | |
private fun hash(packageName: String, signature: String): String? { | |
val appInfo = "$packageName $signature" | |
try { | |
val messageDigest = MessageDigest.getInstance(HASH_TYPE) | |
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | |
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8)) | |
} | |
var hashSignature = messageDigest.digest() | |
// truncated into NUM_HASHED_BYTES | |
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES) | |
// encode into Base64 | |
var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP) | |
base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR) | |
// Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash)) | |
return base64Hash | |
} catch (e: NoSuchAlgorithmException) { | |
Log.w(TAG, "hash:NoSuchAlgorithm", e) | |
} | |
return null | |
} | |
companion object { | |
val TAG = AppSignatureSmsVerificationHelper::class.java.simpleName | |
private const val HASH_TYPE = "SHA-256" | |
const val NUM_HASHED_BYTES = 9 | |
const val NUM_BASE64_CHAR = 11 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment