본문 바로가기
iOS, Swift 개발

앱 스토어 영수증 서명 인증서 SHA-256 이슈

by Nin J 2025. 1. 6.

1. App Store 영수증 서명 인증서

오늘 앱 스토어를 방문해 보니 ' 예정된 요구 사항: App Store 영수증 서명 인증서 '가 나와 그대 해당하는 이슈를 확인해 보려고 한다.

App Store 영수증 서명 인증서

확인해 보니 기존에는  SHA-1 중간 인증서 형태로 진행되고 있었다.

그러나 이 부분이 SHA-256 형태로 변경되어 앱에서 기기 내 영수증 검증을 수행하는 경우 꼭  SHA-256 암호화 형태로 변경해야 한다.

 

2. 암호적 해시함수 (SHA)

그럼 SHA-1, SHA-256 이 SHA 가 무엇인가 알아보자.

Secure Hash Algorithm(SHA)

미국 국가안보국(NSA)이 1993년에 처음으로 설계했으며 미국 국가 표준으로 지정되었다.

SHA 함수군에 속하는 최초의 함수는 공식적으로 SHA이지만 SHA-0라고도 불린다.

SHA-0는 얼마 지나지 않아 표준이 폐기되었고, 1995년에 SHA-1이 새로 출판된다.

SHA-1은 SHA-0의 압축 함수에 비트 회전 연산을 추가한 것이다.

NSA에 따르면, 암호학적 보안을 감소시키는 문제점을 고쳤다고 한다.(실제 문제점을 공개하진 않음)

SHA-0와 SHA-1은 최대 264비트의 메시지로부터 160비트의 해시값을 만들어 낸다.

로널드 라이베스트가 만든 MD4와 MD5 해시함수에서 사용했던 것과 비슷한 방법에 기초한다.

이후, 4종류의 변형,

즉 SHA-224, SHA-256, SHA-384, SHA-512가 더 발표되었다. 이들을 통칭해서 SHA-2라고 하기도 한다.

2002년에 SHA-1과 함께 정식표준으로 지정되었다.

SHA2부터 뒤에 비트수가 붙는데, 주로 이렇게 다양한 비트들은 암호연산의 길이와 비례한다.

- 출처 위키백과

 

3. 업데이트 이유와 방안

장치 영수증 유효성 검사를 수행하는 개발자는 앱이 SHA-256 알고리즘을 지원할 있도록 해야 한다.

업데이트를 수행하지 않은 앱은 영수증 유효성 검사 문제가 발생할 있어, 이로 인해 사용자가 구매한 콘텐츠에 대한 접근성 못할 수 있는 아주 안 좋은 상황이 발생한다.

개발자는 SHA-256 지원하도록 코드를 업데이트하거나 트랜잭션 트랜잭션 API 사용하여 영수증 유효성 검사를 수행해야 한다.

 트랜잭션 관련링크 : https://developer.apple.com/documentation/storekit/apptransaction

트랜잭션 API 관련링크 : https://developer.apple.com/documentation/storekit/transaction

개발자는 샌드박스 환경에서 앱을 테스트해야 한다.

추가적으로

샌드박스 환경은 2023 6 20일부터 SHA-256 인증서를 채용하였고, TestFlight 2023 8 16일부터 채용하였다.

스토어는 2025 1 24일까지 SHA-256 전환을 해야 한다.

이는 영수증이 SHA-256 인증서로 서명되었고, 장치 영수증 유효성 검사가 성공적으로 이루어지는지 확인하는 것을 포함한다.

개발자는 SHA-256 알고리즘을 수용하기 위해 암호 라이브러리나 사용자 정의 코드를 업데이트해야 한다.

 

4. SHA-256 전환 방법

여러 가지의 방법이 있고 각자의 입맛에 맞게 암호화 해시화를 하면 된다.

심플한 방법 예시는 아래와 같이 암호화 해시화 형태를 단일하게 구현하는 게 아니라 복합적으로 구현을 해놓으면 나중에 

좀 더 편안하게 개발을 할 수 있다.

이 부분은 1. 필요한 case의 알고리즘 형태를 정하고 그것을 2. String 타입으로 만들어서  3. Base64로 암호화하여 전달하는 로직이다.

enum HMACAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
    func toCCHmacAlgorithm() -> CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5:
            result = kCCHmacAlgMD5
        case .SHA1:
            result = kCCHmacAlgSHA1
        case .SHA224:
            result = kCCHmacAlgSHA224
        case .SHA256:
            result = kCCHmacAlgSHA256
        case .SHA384:
            result = kCCHmacAlgSHA384
        case .SHA512:
            result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }
    
    func digestLength() -> Int {
        var result: CInt = 0
        switch self {
        case .MD5:
            result = CC_MD5_DIGEST_LENGTH
        case .SHA1:
            result = CC_SHA1_DIGEST_LENGTH
        case .SHA224:
            result = CC_SHA224_DIGEST_LENGTH
        case .SHA256:
            result = CC_SHA256_DIGEST_LENGTH
        case .SHA384:
            result = CC_SHA384_DIGEST_LENGTH
        case .SHA512:
            result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}
extension String {
    
    func hmac(algorithm: HMACAlgorithm, key: String) -> String {
        let cKey = key.cString(using: String.Encoding.utf8)
        let cData = self.cString(using: String.Encoding.utf8)
        var result = [CUnsignedChar](repeating: 0, count: Int(algorithm.digestLength()))
        CCHmac(algorithm.toCCHmacAlgorithm(), cKey!, strlen(cKey!), cData!, strlen(cData!), &result)
        let hmacData:NSData = NSData(bytes: result, length: (Int(algorithm.digestLength())))
        let hmacBase64 = hmacData.base64EncodedString(options: .lineLength64Characters)
        let hmacString = JWTController.sharedInstance.base64ToBase64url(base64: String(hmacBase64))
        return hmacString
    }
}

 

이렇게 입맛에 맞게 암호적 해시함수 SHA-256 전환하기를 알아보았다.

끝!