Android(Kotlin)
[Kotlin] 휴대폰 인증번호 자동추출
삽질중
2025. 7. 1. 11:09
// SMS
implementation 'com.google.android.gms:play-services-auth:21.3.0'
1. SmsReciver Class
@Suppress("DEPRECATION")
open class SmsReceiver() : BroadcastReceiver() {
var pattern: Pattern = Pattern.compile("\\d{6}")
var mAuthNumber: String? = null
private val HASH_TYPE = "SHA-256"
private val NUM_HASHED_BYTES = 9
private val NUM_BASE64_CHAR = 11
override fun onReceive(context: Context, intent: Intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
val extras = intent.extras
if (extras == null) {
DLog.e(Exception(), "SmsReceiver Extras Null !!!!!!!!!!!!!")
return
}
var status: Status? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
status = BundleCompat.getParcelable(extras, SmsRetriever.EXTRA_STATUS, Status::class.java)
} else {
status = extras.get(SmsRetriever.EXTRA_STATUS) as Status
}
when (status?.statusCode) {
CommonStatusCodes.SUCCESS -> {
val message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
DLog.d(Exception(), "SMS : $message")
mAuthNumber = null
val matcher = pattern.matcher(message)
if (matcher.find()) {
mAuthNumber = matcher.group(0)
DLog.d(Exception(), "SMS 인증번호 : $mAuthNumber")
}
}
CommonStatusCodes.TIMEOUT -> {}
}
}
}
// Todo: SMS Hash 추출
// Debug 모드와 Release 모드 Hash 값은 다름
// (Package Hash Ex : Debug - sSJX3jjngYe / Release - 20ak8Oh6RlW)
@SuppressLint("PackageManagerGetSignatures")
fun getAppSignatures(context: Context): String? {
var hash: String? = ""
val packageName = context.packageName
val packageManager = context.packageManager
// val signatures = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
val signatures = with(packageManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
getPackageInfo(packageName, GET_SIGNING_CERTIFICATES)
.signingInfo
?.apkContentsSigners
} else {
getPackageInfo(packageName, GET_SIGNATURES).signatures
}
}
if (signatures != null) {
for (signature in signatures) {
hash = getHash(packageName, signature.toCharsString())
if (hash != null) {
DLog.d(Exception(), String.format("문자 마지막에 붙는 해쉬값 : %s", hash))
return hash
}
}
}
DLog.e(Exception(), "getAppSignatures Hash Value Fail~~~~~~~~ : " + hash)
return hash
}
private fun getHash(packageName: String, signature: String): String? {
val appInfo = "$packageName $signature"
try {
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
val hashSignature = Arrays.copyOfRange(messageDigest.digest(), 0, NUM_HASHED_BYTES)
val base64Hash = Base64
.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
.substring(0, NUM_BASE64_CHAR)
return base64Hash
} catch (e: CloneNotSupportedException) {
DLog.e(Exception(), e.stackTraceToString())
}
return null
}
}
2. 적용방법
// 문자 인증번호 추출 Receiver
private lateinit var mSmsReceiver: SmsReceiver
override fun onDestroy() {
super.onDestroy()
try {
unregisterReceiver(mSmsReceiver)
} catch (ignored: IllegalArgumentException) {
DLog.e(Exception(), ignored.stackTraceToString())
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//Todo : SMS 문자 인증번호 추출 (240613 추가)
mSmsReceiver = object : SmsReceiver() {
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
mAuthNumber?.let { authNumber ->
if (!authNumber.isEmpty()) {
//JavaScript Interface 전송
Toast.makeText(context, "SMS : " + authNumber, Toast.LENGTH_SHORT).show();
DLog.i(Exception(), "SMS : $authNumber")
val result = JSONObject()
mIWebview.post {
try {
result.put("auth_number", authNumber)
val callback = "authNumber('$result')"
DLog.i(Exception(), "SMS JSON : $callback")
mIWebview.evaluateJavascript(callback, null)
} catch (e: JSONException) {
DLog.e(Exception(), e.stackTraceToString())
}
}
//원인은 알 수 없으나 메세지 한번 들어오고 다시 보내면 문자가 안들어옴.
//추측으로는 register 해제되는게 아닐까?
//문자 받아서 처리후 다시 초기화 해주면 계속 받을 수 있어 이렇게 처리함.
initSMSReceive()
}
}
}
}
// SMS 인증번호 Receive 초기화
initSMSReceive()
}
// Todo : SMS Receiver Register
private fun initSMSReceive() {
val client = SmsRetriever.getClient(this)
val task = client.startSmsRetriever()
task.addOnSuccessListener {
val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(mSmsReceiver, intentFilter, RECEIVER_EXPORTED)
} else {
registerReceiver(mSmsReceiver, intentFilter)
}
// SMS 문자 뒤에 붙일 Hash 값. (고정값임)
DLog.i(Exception(), "Signatures Hash : " + mSmsReceiver.getAppSignatures(this@MainActivity))
// Toast.makeText(MainActivity.this, "Signatures Hash : " + smsReceiver.getAppSignatures(MainActivity.this), Toast.LENGTH_SHORT).show();
}
task.addOnFailureListener { e -> DLog.e(Exception(), e.stackTraceToString()) }
}