Eth Dev Post

Code Signing iOS(macOS)

Code Signing iOS(macOS) feature image

Code Signing이라는 것은 종이 계약서에 서명을 남기는 것처럼 코드에 서명을 남기는 작업입니다. 여기서 코드의 범주는 단순히 우리가 작성하는 코드뿐만 아니라, 데이터, 라이브러리, 툴 등 모든 코드로 이뤄진 것들을 포함합니다. 우리가 계약서에 서명을 한다고 하였을 때 중요한 것은 계약서의 내용과 계약 내용의 이행을 보장하는 서명 자체입니다. 즉, 계약서의 내용은 계약서에 서명한 이후에 바뀌어선 안 됩니다. Code Signing도 계약서의 서명과 동일한 동작을 보장해야 합니다. 그래서

  1. Code Signing은 실제로 서명자가 서명했다는 것을 알려줄 수 있어야 하고,
  2. 코드의 내용이 Code Signing 시점에서 변경되었는지, 아닌지를 알려줄 수 있어야 합니다.

이와 같은 기능을 포함하기 위해 Code Signing은 몇 가지 구성요소의 조합을 통해 구성됩니다. 여기서는 이 Code Signing이 어떻게 이루어져 있는지에 대해 살펴보겠습니다.

Code Signing의 구성

1. Seal - 도장

Code Signing을 수행하는 소프트웨어는 최종 번들(앱, 라이브러리, 프레임워크)에서 여러 리소스를 사용하여 단방향 해싱 알고리즘으로 seal(도장)을 만듭니다. 이 seal은 짧은 문자열 묶음(해시)으로, 특정 input block에 대해 unique하고, original input을 재조립하는 데 사용할 수 없도록 만들어집니다.

코드를 평가하는 주체(시스템에서 코드 변경을 파악하는 주체)는 코드를 서명하는 주체와 동일한 해싱 알고리즘을 사용하여 결과를 생성합니다. 그리고 두 결과를 비교하여 코드의 변경 여부에 대해 파악합니다. 이와 같은 비교 과정은 해시가 변경되지 않았음이 보장될 때 유효합니다. 그리고 이를 보장하는 것이 Digital Signature입니다.

2. Digital Signature - 전자 서명

  • 전자 서명의 기본적인 용도는 서명자의 신분을 인증하는 것입니다. 하지만, 전자 서명은 이것뿐만 아니라 서명 데이터가 변경되지 않는 것을 보장합니다.(문서에 워터마크를 심는 것과 비슷합니다)

전자 서명의 생성

  • 전자 서명은 공개키 암호화 방식을 사용하여 만들어집니다.
  • 전자 서명은 Seal의 해시 값과 서명자의 개인키를 통해 생성됩니다.
  • 서명자가 만들어낸 암호화된 해시와 Certificate가 종합적으로 전자 서명을 나타냅니다.

전자 서명의 검증

  • 서명 검증 소프트웨어는 앱의 코드와 데이터로부터 해시(1)를 생성합니다.
  • Certifcate에 있는 공개키를 사용하여 서명자가 제출한 암호화된 해시를 해독하여 원래 해시(2)를 생성합니다.
  • 두 해시(1, 2)를 비교하여 데이터 변화를 검증합니다. 여기서 둘의 결과가 같으면 동일한 서명을 가지고 있는 것이고, 그렇지 않으면 서로 다른 서명으로 판단합니다.

서명된 코드의 포함 요소

  • 앱의 bundle에 있는 데이터 컴포넌트(ex: Info.plist 등)에도 서명을 할 수 있습니다. 이 서명은 _CodeSignature/CodeResources에 들어가 있습니다.
  • 라이브러리, 툴 등 앱 안에 있는 코드들도 서명될 수 있고 이 내용도 _CodeSignature/CodeResources에 들어갑니다.

위의 그림은 _CodeSignature/CodeResources의 일부분을 캡쳐한 것입니다. 자세히 들여다보면 각 파일 이름으로 key가 설정되어 있고, 일정한 해시가 value로 들어가 있는 것을 확인할 수 있습니다.

3. Code Requirement

Code RequirementCode Signing을 평가할 때 사용하는 규칙을 의미합니다. 시스템은 코드 Code Signing을 평가할 때 어떤 Code Requirement을 적용할지 판단합니다. 예를 들어서, 앱은 Code Requirement을 통해 앱 내에서 사용하는 모든 플러그인이 애플의 서명을 얻었는지 확인하는 절차를 거치도록 할 수 있습니다.

Internal Requirement

Internal requirement이란 Code Requirement 중 서명자에 의해 구체화되고, 코드 서명 안에 포함된 것을 의미합니다. Internal requirement는 시스템이 코드 서명을 확인할 때 사용할 수 있습니다. 하지만 시스템이 이를 반드시 사용하는 것은 아닙니다. 예를 들어서 앱에 포함된 플러그인도 Internal requirement을 가지고 있지만 시스템은 이를 쓰지 않을 수도 있습니다.

Designated Requirement

Internal requirement이면서 시스템에게 특정 코드를 어떻게 identifying하는지에 대한 규칙을 담고 있는 것을 Designated Requirement이라고 합니다.

  • 특정 코드가 동일한 Designated Requirement을 가지고 있으면(그리고 정해진 확인 작업 거쳤다면), 두 코드는 동일하다고 할 수 있습니다. 이는 코드 서명자가 동일한 앱의 다른 버전을 배포할 수 있도록 해줍니다. 예를 들어 Apple Mail이라는 앱이 있을 때 동일한 Designated Requirement을 가지고 있고 동일한 identifier(com.apple.mail)를 가지고 있는 코드는 동일한 앱이라고 인식될 수 있습니다. Designated Requirement을 통한 비교는 완전히 다른 바이너리를 갖고 있어도 적용되기 때문에 다른 버전의 애플 메일 앱을 배포할 수 있도록 해줍니다.
  • Designated RequirementInfo.plist에 있는 CFBundleIdentifier와 코드 서명을 보호하는 서명 체인을 통해 생성됩니다.

참고자료

< 홈으로

잘못된 정보나 궁금하신 점은 hcn1519@gmail.com으로 알려주시면 감사하겠습니다.