Skip to content

Instantly share code, notes, and snippets.

@ghasemdev
Created September 12, 2022 07:32
Show Gist options
  • Save ghasemdev/293d90d33c20286c537ca423c1fb29ee to your computer and use it in GitHub Desktop.
Save ghasemdev/293d90d33c20286c537ca423c1fb29ee to your computer and use it in GitHub Desktop.
App Signature for OTP
import android.annotation.SuppressLint
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 timber.log.Timber
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
/**
* ## App signature
* generate hash secret keys for different build variants.
* (release, debug, and play store release version).
*/
class AppSignatureHelper(context: Context) : ContextWrapper(context) {
val signatures: List<String>
get() {
val appCodes = mutableListOf<String>()
try {
// For each signature create a compatible hash
for (signature in getSignatures()) {
val hash = hash(packageName, signature.toCharsString())
if (hash != null) {
appCodes.add(String.format("%s", hash))
}
}
} catch (e: PackageManager.NameNotFoundException) {
Timber.tag(TAG).e(e, "Unable to find package to obtain hash.")
}
return appCodes
}
@SuppressLint("PackageManagerGetSignatures")
@Suppress("DEPRECATION")
@JvmName("getSignaturesList")
/** Get all package signatures for the current package */
private fun getSignatures(): Array<Signature> {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val packageInfo = packageManager
.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
if (packageInfo?.signingInfo != null) {
return if (packageInfo.signingInfo.hasMultipleSigners()) {
packageInfo.signingInfo.apkContentsSigners
} else {
packageInfo.signingInfo.signingCertificateHistory
}
}
} else {
val packageInfo = packageManager
.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
if (packageInfo?.signatures != null &&
packageInfo.signatures.isNotEmpty() && packageInfo.signatures[0] != null
) {
return packageInfo.signatures
}
}
} catch (_: PackageManager.NameNotFoundException) {
}
return emptyArray()
}
private fun hash(packageName: String, signature: String): String? {
val appInfo = "$packageName $signature"
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
messageDigest.update(appInfo.encodeToByteArray())
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)
Timber.tag(TAG).d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash))
return base64Hash
} catch (e: NoSuchAlgorithmException) {
Timber.tag(TAG).e(e, "Unable to find package to obtain hash.")
}
return null
}
companion object {
private val TAG = AppSignatureHelper::class.java.simpleName
private const val HASH_TYPE = "SHA-256"
private const val NUM_HASHED_BYTES = 9
private const val NUM_BASE64_CHAR = 11
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment