질문 정리

안드로이드에서 CRC32로 무결성 체크하기

five2week 2024. 7. 2. 21:36

문제 상황

펌웨어 업데이트나 블루투스 통신을 통해 데이터를 안전하게 전송하기 위해 CRC32를 사용하여 데이터의 무결성을 검사하려고 합니다. CRC32은 어떤 것이며, 안드로이드 앱에서 이를 어떻게 사용할 수 있는지 정리해 보겠습니다.

 

왜 CRC32를 사용해야하는가?

CRC32는 데이터의 무결성을 검증하기 위한 체크섬 기법 중 하나입니다. 데이터가 전송 도중 변경되었는지 여부를 간단하게 확인할 수 있어, 데이터 손상을 감지하고 재전송 요구 없이 효율적으로 오류를 처리할 수 있습니다.

  • 체크섬(Checksum)은 주어진 데이터의 일부를 특정 알고리즘을 통해 계산하여 생성됩니다. 이 값은 데이터 블록이나 파일의 모든 비트를 고려하여 생성되며, 일반적으로 고유한 데이터 시그니처 역할을 합니다. 데이터가 손상되었는지 여부를 빠르게 감지하거나, 데이터의 무결성을 확인하는 데 사용됩니다.

 

CRC32는 무엇인가(작동 원리)

  1. 다항식 선택: CRC32는 다항식(Polynomial)을 사용하여 체크섬을 계산합니다. 이 다항식은 CRC 알고리즘의 핵심 요소로, 여러 다항식 중 하나가 선택됩니다. 예를 들어, CRC32의 다항식은 다음과 같습니다:이 다항식은 CRC32의 체크섬 계산 과정에서 각 비트에 대한 계수로 사용됩니다.
  2. 초기값 설정: CRC32는 초기값으로 일반적으로 모든 비트가 1인 값이 사용됩니다. 이 초기값은 CRC 알고리즘의 시작점을 결정짓습니다.
  3. 데이터 처리: CRC32는 데이터 블록의 각 비트를 순차적으로 처리하여 체크섬 값을 생성합니다. 데이터 블록은 보통 바이트(Byte) 단위로 처리되며, 각 바이트의 모든 비트를 고려하여 체크섬 값을 업데이트합니다.
  4. 비트 연산: CRC32는 XOR(Xclusive OR) 연산과 시프트(Shift) 연산을 통해 체크섬 값을 계산합니다. 각 비트 처리 단계에서 다항식을 사용하여 데이터의 각 비트에 대한 계수를 반영하고, 이전 체크섬 값에 XOR 연산을 수행하여 새로운 체크섬 값을 생성합니다.
  5. 종료 처리: 데이터 블록의 모든 비트가 처리된 후에는 최종적으로 계산된 CRC32 값이 생성됩니다. 이 값은 데이터 블록의 내용을 나타내는 체크섬으로서, 데이터의 무결성을 나타냅니다.

 

CRC32를 안드로이드 앱에서 사용하는 방법

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import java.io.File
import java.io.FileInputStream
import java.util.zip.CRC32
import kotlin.experimental.and

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface {
                    CRC32FileVerifier()
                }
            }
        }
    }
}

@Composable
fun CRC32FileVerifier() {
    var filePath by remember { mutableStateOf("") }
    var crc32Result by remember { mutableStateOf("") }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        OutlinedTextField(
            value = filePath,
            onValueChange = { filePath = it },
            label = { Text("File Path") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(16.dp))
        Button(
            onClick = {
                if (filePath.isNotBlank()) {
                    crc32Result = calculateFileCRC32(filePath)
                } else {
                    crc32Result = ""
                }
            },
            enabled = filePath.isNotBlank(),
        ) {
            Text("Verify File Integrity")
        }
        Spacer(modifier = Modifier.height(16.dp))
        Text(
            text = "CRC32: $crc32Result",
            style = MaterialTheme.typography.body1
        )
    }
}

fun calculateFileCRC32(filePath: String): String {
    val crc32 = CRC32()
    val file = File(filePath)
    val buffer = ByteArray(8192)
    FileInputStream(file).use { fis ->
        var bytesRead = fis.read(buffer)
        while (bytesRead != -1) {
            crc32.update(buffer, 0, bytesRead)
            bytesRead = fis.read(buffer)
        }
    }
    return crc32.value.toString(16).toUpperCase()
}

블루투스 통신 과정에서 받은 값과 제가 CRC32를 사용해서 얻은 값이 같다면 데이터가 무결하다는 증명이 됩니다.

예를 들어, "Hello"라는 문자열에 대한 CRC32 값은 다음과 같이 계산됩니다:

  • 문자열 "Hello"의 각 바이트를 이진 데이터로 변환합니다.
  • 각 바이트의 모든 비트를 CRC32 알고리즘에 따라 처리하여 최종적인 CRC32 값이 생성됩니다

 

참고 링크

https://developer.android.com/reference/kotlin/java/util/zip/CRC32?hl=en

 

CRC32  |  Android Developers

 

developer.android.com

https://stackoverflow.com/questions/20993557/wrong-crc32-hash-in-android

 

Wrong CRC32 hash in android

I'm calculating CRC32 hash using the below code. CRC32 crc = new CRC32(); crc.update(str.getBytes()); String enc = Long.toHexString(crc.getValue()); My problem is that in the output (enc) if the...

stackoverflow.com