개요
macOS 앱을 앱스토어가 아닌 직접 배포를 수행하는데, 게이트키퍼에 올바르게 인식되도록 하려면
개발자 인증서를 통한 코드서명과 앱에 대한 애플 서버의 공증이 필요하다
우선은 관련 키워드들에 대해 정리하고, 이후 배포 과정 및 배포 방식에서 이들이 언제 어떻게 쓰이는지 정리한다
- 빌드 결과물과 코드서명
- 배포과정(검증, 배포, 공증)
- 커맨드 명령어(코드서명, 공증, 배포 패키지)
- 배포 쉘스크립트
빌드 결과물과 코드 서명
앱을 배포하기까지 코드 → 아카이브 → 앱 과정을 거치는데 이와 관련된 키워드를 우선 정리하고 넘어간다
.xcarchive
XCode에서 생성된 앱의 빌드로, 앱의 모든 파일을 포함하는 번들이다
다음과 같이 XCode를 실행하지 않고도 xcodebuild -scheme를 이용해서 아카이브 빌드를 수행할 수 있다
# 아카이브 생성
xcodebuild -scheme "SCHEME_NAME" \ # 프로젝트 Scheme
-archivePath "ARCHIVE_PATH" \ # 생성될 위치
archive
.app
사용자가 실행할 수 있는 최종 소프트웨어로 아카이브에서 생성된 실행파일과 리소스를 포함한다
아카이브 된 빌드를 배포형식에 맞게 패키징하여 생성한다
마찬가지로 xcodebuild -exportArchive를 이용해서 .app 파일을 추출할 수 있다
# 아카이브에서 .app 파일 추출
xcodebuild -exportArchive \
-archivePath "ARCHIVE_PATH" \ # 빌드 아카이브 위치
-exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \ # 추출옵션 .plist 파일
-exportPath "$EXPORT_PATH" # 생성될 위치
Codesign
코드 서명은 애플리케이션 코드와 바이너리를 디지털 방식으로 서명하는 과정이다
서명을 통해 코드의 무결성과 출처를 보증하는데, 코드 서명은 애플이 발급한 개발자 인증서를 사용하여 수행된다
참고로 애플 개발자 인증서는 공개 키 암호화 방식을 사용하여 개발자의 신원을 검증한다
코드서명 확인
다음 명령어를 통해 유효한 인증서와 그 종류를 알 수 있다
security find-identity -v
다음을 수행하면 아래같은 결과를 얻을 수 있다
1) 5F13DF "Apple Development: myEmail@icloud.com (3C4DFFFFFF)"
2) 0B33FD "Apple Distribution: myEmail@icloud.com (8PRD111111)"
참고로 서명을 사용할 땐 “Apple …: EMAIL (IDENTIFIER)“ 부분이 서명으로 전부 사용된다
- Apple Development
- 주로 Xcode에서 앱을 빌드하고 테스트할 때처럼, 개발 중인 앱을 테스트하고 디버깅할 때 사용한다
- Apple Distribution
- 앱을 배포할 때 사용한다(App Store, Direct Distribution)
- Developer ID Installer
- .pkg 파일에 서명할 때 사용된다
배포 과정
검증 → 배포 → 공증의 순서로 진행되며, 각 과정은 앱의 안전성과 신뢰성을 보장하기 위해 필수적인 역할을 수행한다
공증은 단지 해당 코드가 안전하단 것을 증명하는 증명서를 발급하는 과정이지, 검증에 통과하지 못한다고 공증서가 안되지 않는다(단 배포는 가능할 정도로 검증은 되어야 한다..)
검증과 배포
1. Validate App
배포 과정에서 발생할 수 있는 문제를 미리 발견하는 데 사용, 주로 검증하는 요소들은 다음과 같다
- 앱 서명 - 앱이 올바르게 서명되었는지 확인
- 프로비저닝 프로파일 - 앱에 포함된 프로비저닝 프로파일(애플 개발자 프로그램에서 발급)이 올바른지 확인
- 앱 구성 - 앱의 번들 ID, 버전 번호, 빌드 번호 등이 올바른지 확인
2. Distribute App
앱 배포 방식 옵션으로 아래와 같이 존재한다(Copy App이 아닌이상 앱에 애플 개발자 인증서를 통해 서명을 수행한다)
- App Store Connect - 앱을 App Store에 배포하기 위한 옵션, 앱을 App Store Connect에 업로드하고, 리뷰를 거쳐 App Store에 게시
- TestFlight - 테스트 목적으로 앱을 배포할 때 사용(특정 기기 지정)
- Debugging - 개발 중인 앱을 테스트 목적으로 배포할 때 사용(개발자 계정에 등록된 기기에서만 실행 가능)
- Direct Distribution - Developer ID를 통해 macOS 앱을 서명하고, Notarization을 거쳐 배포
- Copy App(Custom) - 앱을 특정 위치에 복사하여 배포, 이 과정에서는 코드 서명이나 공증이 필요하지 않다
공증
3. Notarization
- 애플 개발자 인증서를 통한 코드서명 → Notarization 서버에 제출 → Notarization Ticket 획득
Notrization은 Apple이 소프트웨어를 악성 콘텐츠로부터 검사하고 코드 서명 문제를 확인하는 자동화된 시스템이다
Apple 서버로 앱을 보내고 통과하면, Notarization Ticket을 발급받게 되는데 이를 애플리케이션에 첨부하여 배포한다(이때 .app 번들을 직접 업로드할 수 없기 때문에, 압축된 아카이브 파일로 만들어서 보내야 한다)
그러면 사용자가 애플리케이션을 다운로드하고 실행할 때, 게이트키퍼가 이 공증티켓을 확인하여 애플리케이션의 무결성을 검증한다
또 관련 내용을 보다보면 Hardened Runtime이라는 키워드가 등장하는데
이는 앱의 실행 중 보안성을 강화하기 위해 도입된 기능으로 코드 주입, 동적 라이브러리 하이재킹, 프로세스 메모리 공간 변조와 같은 특정 유형의 공격을 방지한다고 한다
macOS 앱을 공증(Notarization)하려면 반드시 Hardened Runtime을 활성화해야 하는데
앱이 특정 기능을 필요로 하는 경우, 해당 기능을 허용하는 권한을 추가하여 예외 처리할 수 있다고 한다
커맨드 명령어
Codesign과 Notarization 확인 관련
실행 파일 서명(codesign --force)
codesign --force --deep --options runtime --sign "SIGNING_IDENTITY" /Path/To/App
- --deep : 하위 요소들에도 서명 적용
- --options runtime: Hardened Runtime 설정
- SIGNING_IDENTITY: 서명의 종류(ex. Developer ID Application: NAME (TEAM_ID) )
실행 파일 서명 확인(codesign -dv)
codesign -dv --verbose=4 /Path/To/App
패키지의 서명 상태를 확인(pkgutil)
pkgutil --check-signature /Path/To/Package
앱 코드 서명 상태와 실행가능 여부 확인(spctl)
spctl -a -v /Path/To/App
Distribution 패키징 생성
App Store 배포가 아닌 Direct 배포를 위해서는 3가지 방식이 존재한다
그중, Disk image file를 이용한 방식을 빼고 정리한다
zip archive(ditto)
ditto -c -k --keepParent <PathToDirectory> <PathToZip>
zip archive에 서명은 못하지만, 해제된 파일에는 서명되어 있어야 한다
installer package(productbuild, pkgbuild)
우선 Installer에 서명하기 위해 Developer ID Installer 서명 확인부터 해야 하는데
이전에 식별자 확인에서 Apple Distribution 식별자를 서명에 사용한다
패키지 설치 프로그램은 아래와 같이 productbuild와 pkgbuild로 만들 수 있으며, 일반적으로 productbuild는 복잡한 경우에 사용된다
다음과 같이 /Applications 위치에 설치되는 설치 프로그램을 만들 수 있다
productbuild
productbuild --sign "Developer ID Installer: Name (ABCD123456)" \
--component "Path/To/App" /Applications \
"PathToPackage.pkg"
pkgbuild
pkgbuild --root "Path/To/FolderToApp" \
--identifier "com.example.hello" \
--version "1.0" \
--install-location "/Applications" \
--sign "Developer ID Installer: Name (ABCD123456)" \
"PathToPackage.pkg"
Notarization 관련
기본으로 Xcode 커맨드라인에서 실행하기 위한 유틸리티인 xcrun을 통해 사용한다
자격증명(notarytool store-credentials)
KeyChain에 해당 정보를 등록해 두면 notarytool에서 --keychain-profile로 간단하게 쓸 수 있다
이때 ASP(App Specific Password)는 Apple ID 계정의 보안을 강화하기 위해 사용되는 일회용 비밀번호로, 특정 앱이나 서비스에 대해 Apple ID를 사용하여 인증할 때 사용된다
xcrun notarytool store-credentials "AC_CREDENTIALS" \
--apple-id "your-apple-id@example.com" \
--team-id "YOUR_TEAM_ID" \ # apple developer에서 확인
--password "your-app-specific-password" # ASP는 appleid.apple.com에서 설정
공증 요청(notarytool submit)
위처럼 store-credentials를 수행하면 다음과 같이 --keychain-profile을 통해 쉽게 인증을 수행시킬 수 있다
xcrun notarytool submit "YourApp.zip" --keychain-profile "AC_CREDENTIALS" --wait
현재 진행 중인 공증 확인(notarytool history)
xcrun notarytool history --keychain-profile "AC_CREDENTIALS"
특정 요청에 대한 정보 요청(notarytool info)
xcrun notarytool info "RequestUUID" --keychain-profile "AC_CREDENTIALS"
배포 앱에 공증 첨부(stapler staple)
xcrun stapler staple "/path/to/YourApp.app"
공증 여부 확인(stapler validate)
xcrun stapler validate "/path/to/YourApp.app"
배포 쉘스크립트
우선, 지금까지 배포과정을 정리해 보면 아래와 같다
.xcarchive → .app distribute → submit Notarization → apply Notarization Ticket
이때 해당 내용들을 하나의 쉘스크립트로 작성해 보면 아래와 같다
PROJECT_NAME="TodoMate"
SCHEME_NAME="TodoMate"
EXPORT_PATH="$HOME/Export"
ARCHIVE_PATH="$EXPORT_PATH/$PROJECT_NAME.xcarchive"
EXPORT_OPTIONS_PLIST="ExportOptions.plist"
APP_NAME="TodoMate"
# 아카이브 생성
xcodebuild -scheme "$SCHEME_NAME" \
-archivePath "$ARCHIVE_PATH" \
archive
# 아카이브에서 .app 파일 추출
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \
-exportPath "$EXPORT_PATH"
# .app 파일 경로
APP_PATH="$EXPORT_PATH/$APP_NAME.app"
# .app 파일에서 버전 정보 추출
INFO_PLIST="$APP_PATH/Contents/Info.plist"
VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFO_PLIST")
# 압축할 ZIP 파일의 경로와 이름 설정
ZIP_PATH="$EXPORT_PATH/$APP_NAME-$VERSION.zip"
# 앱 압축 (Notarization을 위해 ZIP 파일로 준비)
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ZIP_PATH"
# Notarize 앱 (애플 개발자 계정 정보를 KeyChain으로 전달)
xcrun notarytool submit "$ZIP_PATH" --keychain-profile "AC_CREDENTIALS" --wait
# ZIP 파일을 스테이플링 (Notarization이 Accepted된 ZIP 파일에 대해 Notarization 인증서를 첨부해줌)
xcrun stapler staple "$ZIP_PATH"
마무리
배포 과정을 정리해 보면 다음과 같다
우선 프로젝트 코드를 작성한다
어느정도 작성하고 아카이브를 진행하면, 해당 프로젝트를 .xcarchive 파일로 만들 수 있다
해당 파일을 우선 검증을 시도해보고 문제없다면 배포를 수행하여 .app 파일로 만들 수 있다
배포에는 개발자 서명이 들어가는 경우와 서명 없이 그냥 파일을 추출하는 경우가 있다
전자의 경우 중, Direct Distribute는 AppStore를 거치지 않고 배포하는 데 사용된다
이렇게 코드에 서명이 들어갔다고 해서 다른 사용자가 바로 앱을 사용할 수는 없고 공증 과정을 거쳐야 한다
후자는 Copy Export로 서명없이. app 파일을 만들 수 있다
공증 과정에선 Apple 서버로 지정파일을 업로드하고, 검사가 완료되면 공증 마크를 얻는다
그리고 이렇게 얻은 공증마크를 앱에 추가해야 한다
앱에 공증 마크가 있다면, 다른 사용자가 앱을 실행하면 게이트키퍼가 이를 안전한 앱이라고 판단해서 실행할 수 있다
'iOS+' 카테고리의 다른 글
| Objective-C (2) | 2024.09.22 |
|---|---|
| WidgetKit (0) | 2024.09.15 |
| Cannot preview in this file (0) | 2024.08.18 |
| UserNotifications (0) | 2024.08.11 |
| EventKit (0) | 2024.08.03 |