Skip to main content

Tauri v2 코드 서명 가이드

작성일: 2026-04-06 Last Updated: 2026-04-06 대상: Tauri v2 데스크탑 앱 코드 서명 (Windows / macOS / Linux) 공통 참조: ../common/security.md (보안 전반, 코드 서명 개요) 공통 참조: ../common/ci-cd.md (CI 코드 서명 자동화)


목차

  1. 코드 서명 개요
  2. macOS 코드 서명 + Notarization
  3. Windows 코드 서명
  4. Linux 서명
  5. CI 자동 서명
  6. 인증서 관리
  7. Gatekeeper / SmartScreen 대응
  8. 비용 및 인증서 비교

1. 코드 서명 개요

왜 코드 서명이 필요한가

OS서명 없을 때서명 있을 때
macOSGatekeeper가 실행 차단 ("손상됨" 경고)정상 실행
WindowsSmartScreen "알 수 없는 게시자" 경고경고 없이 실행
Linux영향 없음 (패키지 관리자 제외)GPG 서명으로 신뢰성 제공

서명 워크플로우 전체 흐름

[코드 작성] → [빌드] → [코드 서명] → [Notarization*] → [배포]
| |
인증서/키 필요 macOS만 해당
| |
CI에서 자동화 Apple 서버 검증

Tauri가 자동으로 처리하는 것

항목자동수동비고
macOS 코드 서명O-환경 변수 설정 시 빌드 중 자동 적용
macOS NotarizationO-Apple ID + Team ID 설정 시 자동
macOS 중첩 바이너리 서명O-dylib, framework 자동 탐색 서명
Windows 코드 서명O-tauri.conf.json 설정 시 자동
Ed25519 업데이트 서명O-빌드 시 .sig 파일 자동 생성
Linux GPG 서명-O수동 스크립트 필요

2. macOS 코드 서명 + Notarization

2.1 사전 준비

항목설명취득 방법
Apple Developer 계정$99/년 (개인 또는 기관)developer.apple.com
Developer ID Application 인증서코드 서명용Keychain Access > Certificate Assistant
App-Specific PasswordNotarization API 인증appleid.apple.com > App-Specific Passwords
Team IDApple Developer 팀 식별자Developer Portal > Membership

2.2 인증서 생성 절차

1. Apple Developer Portal > Certificates, IDs & Profiles
2. Certificates > + (새 인증서 생성)
3. "Developer ID Application" 선택
4. CSR 파일 업로드 (Keychain Access에서 생성)
5. 인증서 다운로드 + 더블 클릭으로 Keychain에 설치
6. Keychain Access에서 인증서 + 비공개 키 함께 P12로 내보내기

2.3 환경 변수 설정

# 로컬 개발 (Keychain에 인증서 설치 상태)
export APPLE_SIGNING_IDENTITY="Developer ID Application: Company Name (TEAMID)"

# CI 환경 (P12 인증서 Base64 인코딩)
export APPLE_CERTIFICATE=$(base64 -i certificate.p12)
export APPLE_CERTIFICATE_PASSWORD="p12-password"

# Notarization
export APPLE_ID="your@apple.id"
export APPLE_PASSWORD="app-specific-password" # 앱 전용 패스워드
export APPLE_TEAM_ID="TEAMID1234"

2.4 Entitlements 설정

<!-- src-tauri/entitlements.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Hardened Runtime (Notarization 필수) -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<false/>

<!-- WebView에 필요한 JIT -->
<key>com.apple.security.cs.allow-jit</key>
<true/>

<!-- 네트워크 접근 (API 통신) -->
<key>com.apple.security.network.client</key>
<true/>

<!-- 파일 접근 (사용자 선택 파일) -->
<key>com.apple.security.files.user-selected.read-write</key>
<true/>

<!-- 카메라/마이크 (필요 시) -->
<!-- <key>com.apple.security.device.camera</key> -->
<!-- <true/> -->
</dict>
</plist>

2.5 tauri.conf.json macOS 설정

{
"bundle": {
"macOS": {
"signingIdentity": null,
"entitlements": "./entitlements.plist",
"hardenedRuntime": true,
"providerShortName": "TEAMID1234",
"minimumSystemVersion": "10.15"
}
}
}

signingIdentitynull로 설정하면 환경 변수(APPLE_SIGNING_IDENTITY) 또는 Keychain에서 자동 감지한다.

2.6 Notarization 프로세스

빌드 완료 → codesign (자동) → xcrun notarytool submit (자동)
|
Apple 서버 검증 (1~15분)
|
staple (오프라인 검증용)
|
배포 준비 완료

Tauri는 APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID 환경 변수가 설정되어 있으면 빌드 후 자동으로 Notarization을 수행한다.

2.7 수동 Notarization (디버깅용)

# 1. 공증 제출
xcrun notarytool submit target/release/bundle/macos/MyApp.dmg \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$APPLE_TEAM_ID" \
--wait

# 2. 상태 확인
xcrun notarytool info <submission-id> \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$APPLE_TEAM_ID"

# 3. 로그 확인 (실패 시)
xcrun notarytool log <submission-id> \
--apple-id "$APPLE_ID" \
--password "$APPLE_PASSWORD" \
--team-id "$APPLE_TEAM_ID"

# 4. Staple
xcrun stapler staple target/release/bundle/macos/MyApp.dmg

# 5. 검증
spctl --assess --verbose target/release/bundle/macos/MyApp.app
codesign --verify --deep --strict target/release/bundle/macos/MyApp.app

macOS 코드 서명 체크리스트

  • Apple Developer 계정 활성화 ($99/년)
  • Developer ID Application 인증서 발급
  • P12 내보내기 + Base64 인코딩 (CI용)
  • App-Specific Password 생성
  • Team ID 확인
  • entitlements.plist 작성
  • tauri.conf.json macOS 설정
  • CI에 환경 변수 등록 (5개)
  • Notarization 정상 동작 확인
  • spctl --assess 검증 통과 확인

3. Windows 코드 서명

3.1 인증서 유형 비교

유형발급 대상SmartScreen 즉시 신뢰비용발급 소요
OV (Organization Validation)법인X (평판 축적 필요)$100~200/년3~7일
EV (Extended Validation)법인 (엄격 심사)O (즉시)$200~400/년1~4주
Azure Trusted SigningAzure 구독자O (즉시)$9.99/월수 일 (ID 검증)

추천: 소규모 팀은 Azure Trusted Signing, 대규모는 EV 인증서. OV 인증서는 SmartScreen 평판 축적까지 수주~수개월 경고가 표시될 수 있다.

3.2 tauri.conf.json Windows 설정

{
"bundle": {
"windows": {
"certificateThumbprint": "A1B1A2B2A3B3A4B4A5B5A6B6A7B7A8B8A9B9A0B0",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"tsp": false
}
}
}
필드설명
certificateThumbprint인증서 SHA1 지문 (인증서 상세에서 확인)
digestAlgorithm서명 해시 (sha256 권장)
timestampUrl타임스탬프 서버 (서명 시간 증명)
tspRFC 3161 TSP 사용 여부

3.3 인증서 설치 (로컬)

# PFX 인증서 가져오기
Import-PfxCertificate `
-FilePath "certificate.pfx" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Password (ConvertTo-SecureString -String "password" -AsPlainText -Force)

# 인증서 지문 확인
Get-ChildItem Cert:\CurrentUser\My | Format-Table Thumbprint, Subject

3.4 커스텀 서명 명령 (signCommand)

EV 인증서(USB 토큰) 또는 Azure Trusted Signing처럼 signtool 직접 호출이 필요한 경우:

{
"bundle": {
"windows": {
"signCommand": {
"cmd": "signtool",
"args": [
"sign",
"/tr", "http://timestamp.digicert.com",
"/td", "sha256",
"/fd", "sha256",
"/a",
"%1"
]
}
}
}
}

%1은 서명할 바이너리 경로로 자동 치환된다.

3.5 Azure Trusted Signing

{
"bundle": {
"windows": {
"signCommand": {
"cmd": "azuresigntool",
"args": [
"sign",
"-kvu", "https://your-vault.vault.azure.net",
"-kvc", "your-cert-name",
"-kvt", "your-tenant-id",
"-kvi", "your-client-id",
"-kvs", "your-client-secret",
"-tr", "http://timestamp.digicert.com",
"-td", "sha256",
"%1"
]
}
}
}
}

3.6 수동 서명 (디버깅용)

# signtool로 수동 서명
signtool sign /tr http://timestamp.digicert.com /td sha256 \
/fd sha256 /a \
target/release/bundle/nsis/MyApp_2.4.0_x64-setup.exe

# 서명 검증
signtool verify /pa target/release/bundle/nsis/MyApp_2.4.0_x64-setup.exe

Windows 코드 서명 체크리스트

  • 인증서 유형 결정 (OV / EV / Azure Trusted Signing)
  • 인증서 구매/발급 신청
  • tauri.conf.json에 certificateThumbprint 설정
  • 타임스탬프 서버 URL 설정
  • CI에 인증서 등록 (PFX Base64 또는 Azure 자격 증명)
  • signtool verify 검증 통과 확인
  • SmartScreen 경고 확인 (OV는 평판 축적 필요)

4. Linux 서명

Linux에서 코드 서명은 OS 수준에서 강제되지 않지만, 패키지 배포 시 무결성 검증을 위해 GPG 서명을 권장한다.

4.1 GPG 키 생성

# GPG 키 쌍 생성
gpg --full-generate-key
# RSA and RSA, 4096 bits, 2년 유효 기간 권장

# 키 목록 확인
gpg --list-keys --keyid-format LONG

# 공개 키 내보내기 (사용자 배포용)
gpg --armor --export your@email.com > public-key.asc

4.2 빌드 아티팩트 서명

#!/bin/bash
# scripts/sign-linux.sh

ARTIFACT="target/release/bundle/appimage/myapp_2.4.0_amd64.AppImage"
GPG_KEY_ID="ABCDEF1234567890"

# GPG 서명 (분리 서명)
gpg --detach-sign --armor --local-user "$GPG_KEY_ID" "$ARTIFACT"
# 출력: myapp_2.4.0_amd64.AppImage.asc

# SHA256 체크섬
sha256sum "$ARTIFACT" > "${ARTIFACT}.sha256"

# 검증
gpg --verify "${ARTIFACT}.asc" "$ARTIFACT"
sha256sum --check "${ARTIFACT}.sha256"

echo "Signed: ${ARTIFACT}"
echo "Signature: ${ARTIFACT}.asc"
echo "Checksum: ${ARTIFACT}.sha256"

4.3 deb 패키지 서명

# dpkg-sig로 deb 패키지 서명
dpkg-sig --sign builder -k "$GPG_KEY_ID" \
target/release/bundle/deb/myapp_2.4.0_amd64.deb

# 검증
dpkg-sig --verify target/release/bundle/deb/myapp_2.4.0_amd64.deb

4.4 Linux 배포 시 서명 파일 포함

releases/v2.4.0/
+-- myapp_2.4.0_amd64.AppImage
+-- myapp_2.4.0_amd64.AppImage.asc # GPG 서명
+-- myapp_2.4.0_amd64.AppImage.sha256 # SHA256 체크섬
+-- myapp_2.4.0_amd64.deb
+-- myapp_2.4.0_amd64.deb.asc
+-- myapp_2.4.0_amd64.deb.sha256
+-- public-key.asc # GPG 공개 키

5. CI 자동 서명

CI/CD 파이프라인 전체 구성: ../common/ci-cd.md 참조.

5.1 GitHub Actions - macOS 서명

jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

# 인증서 설치 (Keychain)
- name: Install Apple Certificate
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
run: |
# P12 인증서 디코딩
CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
echo -n "$APPLE_CERTIFICATE" | base64 --decode > $CERTIFICATE_PATH

# 임시 Keychain 생성
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

# 인증서 가져오기
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" \
-A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: \
-k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH

- name: Build Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

# Keychain 정리
- name: Cleanup Keychain
if: always()
run: security delete-keychain $RUNNER_TEMP/app-signing.keychain-db

5.2 GitHub Actions - Windows 서명

jobs:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

# PFX 인증서 설치
- name: Install Certificate
env:
WINDOWS_PFX_BASE64: ${{ secrets.WINDOWS_PFX_BASE64 }}
WINDOWS_PFX_PASSWORD: ${{ secrets.WINDOWS_PFX_PASSWORD }}
run: |
$pfxBytes = [Convert]::FromBase64String($env:WINDOWS_PFX_BASE64)
$pfxPath = "$env:RUNNER_TEMP\certificate.pfx"
[IO.File]::WriteAllBytes($pfxPath, $pfxBytes)
Import-PfxCertificate `
-FilePath $pfxPath `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Password (ConvertTo-SecureString -String $env:WINDOWS_PFX_PASSWORD -AsPlainText -Force)

- name: Build Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

- name: Cleanup Certificate
if: always()
run: Remove-Item "$env:RUNNER_TEMP\certificate.pfx" -ErrorAction SilentlyContinue

5.3 GitHub Actions - Linux GPG 서명

jobs:
build-linux:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: Build Tauri
uses: tauri-apps/tauri-action@v0
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

# GPG 서명 (선택)
- name: GPG Sign artifacts
env:
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
for file in target/release/bundle/appimage/*.AppImage target/release/bundle/deb/*.deb; do
gpg --batch --yes --pinentry-mode loopback \
--passphrase "$GPG_PASSPHRASE" \
--detach-sign --armor "$file"
sha256sum "$file" > "${file}.sha256"
done

6. 인증서 관리

6.1 인증서 수명 관리

인증서유효 기간갱신 시점갱신 방법
Apple Developer ID5년만료 6개월 전Developer Portal에서 갱신
Windows OV/EV1~3년만료 3개월 전CA에서 재발급
Azure Trusted Signing구독 기간월간 갱신 (자동)Azure 구독 유지
GPG 키설정 기간만료 1개월 전gpg --edit-key

6.2 인증서 만료 알림 설정

# .github/workflows/cert-expiry-check.yml
name: Certificate Expiry Check
on:
schedule:
- cron: '0 9 1 * *' # 매월 1일 09:00 UTC

jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Check Apple Certificate Expiry
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
run: |
echo "$APPLE_CERTIFICATE" | base64 -d > cert.p12
openssl pkcs12 -in cert.p12 -nokeys -passin pass:"" 2>/dev/null | \
openssl x509 -noout -enddate
# 만료일 파싱 후 30일 이내면 경고

- name: Notify on Slack/Email
if: failure()
run: echo "Certificate expiring soon!"

6.3 인증서 백업 전략

항목백업 방법저장 위치
Apple P12인증서 + 비공개 키 P12 내보내기1Password / Vault
Windows PFXPFX 내보내기 (비공개 키 포함)1Password / Vault
EV USB 토큰물리 토큰 자체금고
GPG 비밀 키gpg --export-secret-keys > backup.gpg1Password / Vault
Ed25519 서명 키.key 파일 백업1Password / Vault

EV 인증서 주의: EV 인증서는 USB 토큰(HSM)에 저장되며, 복사가 불가하다. 토큰 분실 = 인증서 분실이므로 물리적 보관에 주의.


7. Gatekeeper / SmartScreen 대응

7.1 macOS Gatekeeper

시나리오결과해결
서명 + Notarization 완료정상 실행-
서명만, Notarization 없음"확인되지 않은 개발자" 경고xattr -d com.apple.quarantine 안내
서명 없음"손상되었기 때문에 열 수 없습니다"코드 서명 필수
Ad Hoc 서명경고 표시 (최초 실행 시)Developer ID 서명 필수

Gatekeeper 우회 안내 (서명 전 배포 시)

# 사용자에게 안내할 명령 (임시 방편)
xattr -d com.apple.quarantine /Applications/MyApp.app

# 또는 시스템 환경설정 > 보안 > "그래도 열기" 클릭

프로덕션 배포에서는 반드시 코드 서명 + Notarization을 완료해야 한다.

7.2 Windows SmartScreen

인증서 유형SmartScreen 반응대응
EV즉시 신뢰경고 없음
OV (신규)"알 수 없는 게시자" 경고다운로드 수 누적으로 평판 축적 (수주~수개월)
OV (평판 축적)경고 없음충분한 다운로드 후 자동 해제
Azure Trusted Signing즉시 신뢰Microsoft 신뢰 체인
서명 없음강력한 경고 + 실행 차단코드 서명 필수

SmartScreen 평판 축적 팁

방법설명
동일 인증서 유지인증서 변경 시 평판 리셋
다운로드 수 확보실제 사용자 다운로드가 평판에 기여
Microsoft 제출Windows Defender Security Intelligence에 제출
점진적 배포초기에는 기존 사용자 위주로 배포

8. 비용 및 인증서 비교

8.1 전체 비용 요약

항목비용주기필수 여부
Apple Developer Program$99/년macOS 필수
Windows OV Code Signing (DigiCert 등)$200~400/년Windows 권장
Windows EV Code Signing$300~600/년SmartScreen 즉시 해제
Azure Trusted Signing$9.99/월Azure 사용 시 추천
GPG 키무료-Linux 선택
SSL.com OV$70~140/년저렴한 OV 옵션

8.2 인증서 발급 기관 비교

CAOV 비용EV 비용발급 속도특징
DigiCert$400/년$600/년1~3일가장 널리 사용, 빠른 발급
Sectigo (Comodo)$200/년$350/년3~7일가성비
SSL.com$140/년$240/년1~5일저렴, EV도 합리적
GlobalSign$250/년$500/년3~7일기업용
Azure Trusted Signing$10/월-수 일 (심사)Microsoft 직접, 월 구독

8.3 시나리오별 추천

시나리오macOSWindowsLinux연간 비용
오픈소스 / 사이드 프로젝트Apple Developer ($99)서명 없음 (경고 감수)GPG (무료)~$99
소규모 스타트업Apple Developer ($99)Azure Trusted Signing ($120/년)GPG (무료)~$219
상용 제품 (권장)Apple Developer ($99)EV 인증서 ($300~600)GPG (무료)$400700
대기업Apple Developer ($99)EV + HSM ($500+)GPG + repo 서명~$600+

코드 서명 전체 체크리스트

macOS

  • Apple Developer 계정 가입 ($99/년)
  • Developer ID Application 인증서 생성
  • P12 내보내기 + 안전한 보관
  • App-Specific Password 생성
  • entitlements.plist 작성
  • CI 환경 변수 등록 (5개)
  • Notarization 정상 동작 확인
  • spctl --assess 통과

Windows

  • 인증서 유형 결정 (OV / EV / Azure)
  • 인증서 구매/발급
  • tauri.conf.json에 certificateThumbprint 설정
  • CI 환경 변수 등록
  • signtool verify 통과
  • SmartScreen 경고 확인

Linux

  • GPG 키 생성 (4096 bit RSA)
  • 공개 키 배포 방안 준비
  • CI에서 GPG 서명 자동화
  • SHA256 체크섬 생성

공통

  • Ed25519 업데이트 서명 키 생성 (Tauri Updater용)
  • 모든 인증서/키 안전한 백업 (1Password/Vault)
  • 인증서 만료 알림 설정 (월 1회 체크)
  • CI 파이프라인에서 서명 자동화 검증
  • 서명된 바이너리로 업데이트 테스트