关于 axiom-code-signing-ref
面向 iOS/macOS 开发者的代码签名命令与 API 参考资料,汇集证书、描述文件、权限、Keychain、fastlane match 和 Xcode 构建设置的操作说明。
主要功能
- 01查询证书类型与操作命令
- 02检查描述文件内容
- 03提取并比较权限
- 04参考 Xcode 签名设置
- 05查看 fastlane match 与 CI 示例
适用场景
- 01查找签名诊断命令
- 02设置 CI 临时 Keychain
- 03配置 fastlane match
- 04核对 Xcode 导出设置
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-code-signing-ref安装后在 Claude Code 中输入/axiom-code-signing-ref确认已加载。
详细介绍
代码签名 API 参考
iOS/macOS 代码签名 CLI 和 API 的完整参考:证书管理、预置描述文件检查、权利提取、钥匙串操作、codesign 验证、fastlane match 和 Xcode 构建设置。
快速参考
# Diagnostic flow — run these 3 commands first for any signing issue
security find-identity -v -p codesigning # List valid signing identities
security cms -D -i path/to/embedded.mobileprovision # Decode provisioning profile
codesign -d --entitlements - MyApp.app # Extract entitlements from binary
证书参考
证书类型
| 类型 | 用途 | 有效期 | 每个账户的上限 |
|---|---|---|---|
| Apple Development | 在已注册设备上调试构建 | 1 年 | 不限(每位开发者) |
| Apple Distribution | 提交至 App Store 和 TestFlight | 1 年 | 每个账户 3 个 |
| iOS Distribution(旧版) | 提交至 App Store(Xcode 11 之前) | 1 年 | 每个账户 3 个 |
| iOS Development(旧版) | 调试构建(Xcode 11 之前) | 1 年 | 不限 |
| Developer ID Application | 在 App Store 之外分发 macOS 应用 | 5 年 | 每个账户 5 个 |
| Developer ID Installer | 为 macOS 软件包签名 | 5 年 | 每个账户 5 个 |
| Apple Push Services | APNs .p12 证书身份验证(旧版) | 1 年 | 每个 App ID 1 个 |
CSR 生成
# Generate Certificate Signing Request
openssl req -new -newkey rsa:2048 -nodes \
-keyout CertificateSigningRequest.key \
-out CertificateSigningRequest.certSigningRequest \
-subj "/[email protected]/CN=Developer Name/C=US"
或者使用钥匙串访问:证书助理 → 从证书颁发机构请求证书。
证书检查
# View certificate details (from .cer file)
openssl x509 -in certificate.cer -inform DER -text -noout
# View certificate from .p12
openssl pkcs12 -in certificate.p12 -nokeys -clcerts | openssl x509 -text -noout
# List certificates in Keychain with SHA-1 hashes
security find-identity -v -p codesigning
# Example output:
# 1) ABC123... "Apple Development: [email protected] (TEAMID)"
# 2) DEF456... "Apple Distribution: Company Name (TEAMID)"
# 2 valid identities found
# Find specific certificate by name
security find-certificate -c "Apple Distribution" login.keychain-db -p
# Check certificate expiration (pipe PEM output to openssl)
security find-certificate -c "Apple Distribution" login.keychain-db -p | openssl x509 -noout -enddate
证书安装
# Import .p12 into Keychain (interactive — prompts for password)
security import certificate.p12 -k ~/Library/Keychains/login.keychain-db -P "$P12_PASSWORD" -T /usr/bin/codesign
# Import .cer into Keychain
security import certificate.cer -k ~/Library/Keychains/login.keychain-db
# For CI: import into temporary keychain (see CI section below)
预置描述文件参考
描述文件类型
| 类型 | 包含内容 | 使用场景 |
|---|---|---|
| Development | 开发证书 + 设备 UDID + App ID + 权利 | 在已注册设备上调试构建 |
| Ad Hoc | 分发证书 + 设备 UDID + App ID + 权利 | 无需 TestFlight,在指定设备上测试 |
| App Store | 分发证书 + App ID + 权利(无设备列表) | 提交至 App Store 和 TestFlight |
| Enterprise | 企业证书 + App ID + 权利(无设备列表) | 内部分发(仅限企业计划) |
描述文件内容
预置描述文件(.mobileprovision)是一个经过签名的 plist,包含:
├── AppIDName — App ID name
├── ApplicationIdentifierPrefix — Team ID
├── CreationDate — When profile was created
├── DeveloperCertificates — Embedded signing certificates (DER-encoded)
├── Entitlements — Granted entitlements
│ ├── application-identifier
│ ├── aps-environment (development|production)
│ ├── com.apple.developer.associated-domains
│ ├── keychain-access-groups
│ └── ...
├── ExpirationDate — When profile expires (1 year)
├── Name — Profile name in Apple Developer Portal
├── ProvisionedDevices — UDIDs (Development/Ad Hoc only)
├── TeamIdentifier — Team ID array
├── TeamName — Team display name
├── TimeToLive — Days until expiration
├── UUID — Unique profile identifier
└── Version — Profile version (1)
解码预置描述文件
# Decode and display full contents
security cms -D -i path/to/embedded.mobileprovision
# Extract specific fields
security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -
# Check aps-environment (push notifications)
security cms -D -i embedded.mobileprovision | grep -A1 "aps-environment"
# Check expiration
security cms -D -i embedded.mobileprovision | grep -A1 "ExpirationDate"
# List provisioned devices (Development/Ad Hoc only)
security cms -D -i embedded.mobileprovision | grep -A100 "ProvisionedDevices"
# Check team ID
security cms -D -i embedded.mobileprovision | grep -A1 "TeamIdentifier"
描述文件安装路径
# Installed profiles (Xcode manages these)
~/Library/MobileDevice/Provisioning Profiles/
# List installed profiles
ls ~/Library/MobileDevice/Provisioning\ Profiles/
# Decode a specific installed profile
security cms -D -i ~/Library/MobileDevice/Provisioning\ Profiles/<UUID>.mobileprovision
# Find profile embedded in app bundle
find ~/Library/Developer/Xcode/DerivedData -name "embedded.mobileprovision" -newer . 2>/dev/null | head -5
# Install a profile manually (copy to managed directory)
cp MyProfile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
权利参考
常见权利
| 权利 | 键 | 值 |
|---|---|---|
| App ID | application-identifier | TEAMID.com.example.app |
| 推送通知 | aps-environment | development / production |
| App 组 | com.apple.security.application-groups | ["group.com.example.shared"] |
| 钥匙串共享 | keychain-access-groups | ["TEAMID.com.example.keychain"] |
| 关联域 | com.apple.developer.associated-domains | ["applinks:example.com"] |
| iCloud | com.apple.developer.icloud-container-identifiers | 容器 ID |
| HealthKit | com.apple.developer.healthkit | true |
| Apple Pay | com.apple.developer.in-app-payments | 商户 ID |
| 网络扩展 | com.apple.developer.networking.networkextension | 类型数组 |
| Siri | com.apple.developer.siri | true |
| 使用 Apple 登录 | com.apple.developer.applesignin | ["Default"] |
权利 .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>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.example.shared</string>
</array>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>
</dict>
</plist>
提取与比较
# Extract entitlements from signed app binary
codesign -d --entitlements - /path/to/MyApp.app
# Extract to file for comparison
codesign -d --entitlements entitlements.plist /path/to/MyApp.app
# Compare entitlements between app and provisioning profile
diff <(codesign -d --entitlements - MyApp.app 2>/dev/null) \
<(security cms -D -i embedded.mobileprovision | plutil -extract Entitlements xml1 -o - -)
# Extract entitlements from .ipa
unzip -o MyApp.ipa -d /tmp/ipa_contents
codesign -d --entitlements - /tmp/ipa_contents/Payload/*.app
# Verify entitlements match between build and profile
codesign -d --entitlements - --xml MyApp.app # XML format
CLI 命令参考
security 命令
# --- Identity & Certificate ---
# List valid code signing identities
security find-identity -v -p codesigning
# -v: valid only, -p codesigning: code signing policy
# Find certificate by common name
security find-certificate -c "Apple Distribution" login.keychain-db -p
# -c: common name substring, -p: output PEM, keychain is positional arg
# Find certificate by SHA-1 hash
security find-certificate -Z -a login.keychain-db | grep -B5 "ABC123"
# --- Import/Export ---
# Import .p12 (with password, allow codesign access)
security import certificate.p12 -k login.keychain-db -P "$PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
# Import .cer
security import certificate.cer -k login.keychain-db
# Export certificate to .p12
security export -t identities -f pkcs12 -k login.keychain-db -P "$PASSWORD" -o exported.p12
# --- Provisioning Profile Decode ---
# Decode provisioning profile (CMS/PKCS7 signed plist)
security cms -D -i embedded.mobileprovision
# --- Keychain Management ---
# Create temporary keychain (for CI)
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Set as default keychain
security default-keychain -s build.keychain
# Add to search list (required for codesign to find certs)
security list-keychains -d user -s build.keychain login.keychain-db
# Unlock keychain (required in CI before signing)
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Set keychain lock timeout (0 = never lock during session)
security set-keychain-settings -t 3600 -l build.keychain
# -t: timeout in seconds, -l: lock on sleep
# Allow codesign to access keys without UI prompt (critical for CI)
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
# Delete keychain (CI cleanup)
security delete-keychain build.keychain
codesign 命令
# --- Signing ---
# Sign with specific identity
codesign -s "Apple Distribution: Company Name (TEAMID)" MyApp.app
# -s: signing identity (name or SHA-1 hash)
# Sign with entitlements file
codesign -s "Apple Distribution" --entitlements entitlements.plist MyApp.app
# Force re-sign (overwrite existing signature)
codesign -f -s "Apple Distribution" MyApp.app
# Sign with timestamp (required for notarization)
codesign -s "Developer ID Application" --timestamp MyApp.app
# Deep sign (sign all nested code — frameworks, extensions)
codesign --deep -s "Apple Distribution" MyApp.app
# Warning: --deep is unreliable for complex apps. Sign each component individually.
# --- Verification ---
# Verify signature is valid
codesign --verify --verbose=4 MyApp.app
# Verify deep (check nested code)
codesign --verify --deep --strict MyApp.app
# Display signing information
codesign -dv MyApp.app
# Shows: Identifier, Format, TeamIdentifier, Signing Authority chain
# Display verbose signing info
codesign -dvvv MyApp.app
# Extract entitlements from signed binary
codesign -d --entitlements - MyApp.app
codesign -d --entitlements - --xml MyApp.app # XML format
openssl 命令
注意:macOS 自带的是 LibreSSL,而不是 OpenSSL。在原版 macOS 上,某些
openssl pkcs12命令可能会因“MAC verification failed”而失败。如有需要,请通过brew install openssl安装 OpenSSL,然后使用完整路径(/opt/homebrew/opt/openssl/bin/openssl)。
# --- Certificate Inspection ---
# View .cer details
openssl x509 -in certificate.cer -inform DER -text -noout
# View .pem details
openssl x509 -in certificate.pem -text -noout
# Check certificate expiration
openssl x509 -in certificate.cer -inform DER -noout -enddate
# Extract public key
openssl x509 -in certificate.cer -inform DER -pubkey -noout
# --- PKCS12 (.p12) ---
# Extract certificate from .p12
openssl pkcs12 -in certificate.p12 -nokeys -clcerts -out cert.pem
# Extract private key from .p12
openssl pkcs12 -in certificate.p12 -nocerts -nodes -out key.pem
# Create .p12 from cert + key
openssl pkcs12 -export -in cert.pem -inkey key.pem -out certificate.p12
# Verify .p12 contents
openssl pkcs12 -info -in certificate.p12 -nokeys
Xcode 构建设置参考
| 设置 | 键 | 值 |
|---|---|---|
| 代码签名样式 | CODE_SIGN_STYLE | Automatic / Manual |
| 签名身份 | CODE_SIGN_IDENTITY | Apple Development / Apple Distribution / iPhone Distribution |
| 开发团队 | DEVELOPMENT_TEAM | 团队 ID(10 位字母数字) |
| 配置描述文件 | PROVISIONING_PROFILE_SPECIFIER | 描述文件名称或 UUID |
| 配置描述文件(旧版) | PROVISIONING_PROFILE | 描述文件 UUID(已弃用,请使用 SPECIFIER) |
| 其他代码签名标志 | OTHER_CODE_SIGN_FLAGS | --timestamp / --options runtime |
| 代码签名权利 | CODE_SIGN_ENTITLEMENTS | .entitlements 文件的路径 |
| 启用强化运行时 | ENABLE_HARDENED_RUNTIME | YES / NO(macOS) |
xcodebuild 签名覆盖项
# Automatic signing
xcodebuild -scheme MyApp -configuration Release \
CODE_SIGN_STYLE=Automatic \
DEVELOPMENT_TEAM=YOURTEAMID
# Manual signing
xcodebuild -scheme MyApp -configuration Release \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Apple Distribution: Company Name (TEAMID)" \
PROVISIONING_PROFILE_SPECIFIER="MyApp App Store Profile"
# Archive for distribution
xcodebuild archive -scheme MyApp \
-archivePath build/MyApp.xcarchive \
CODE_SIGN_STYLE=Manual \
CODE_SIGN_IDENTITY="Apple Distribution" \
PROVISIONING_PROFILE_SPECIFIER="MyApp App Store"
# Export .ipa from archive
xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath build/ipa
ExportOptions.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>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>YOURTEAMID</string>
<key>signingStyle</key>
<string>manual</string>
<key>signingCertificate</key>
<string>Apple Distribution</string>
<key>provisioningProfiles</key>
<dict>
<key>com.example.myapp</key>
<string>MyApp App Store Profile</string>
</dict>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
导出方法的值:app-store、ad-hoc、enterprise、development、developer-id。
fastlane match 参考
设置
# Initialize match (interactive — choose storage type)
fastlane match init
# Options: git, google_cloud, s3, azure_blob
# Generate certificates + profiles for all types
fastlane match development
fastlane match appstore
fastlane match adhoc
Matchfile
# fastlane/Matchfile
git_url("https://github.com/your-org/certificates.git")
storage_mode("git")
type("appstore") # Default type
app_identifier(["com.example.app", "com.example.app.widget"])
username("[email protected]")
team_id("YOURTEAMID")
# For multiple targets with different profiles
# for_lane(:beta) do
# type("adhoc")
# end
用法
# Generate or fetch development certs + profiles
fastlane match development
# Generate or fetch App Store certs + profiles
fastlane match appstore
# CI: read-only mode (never create, only fetch)
fastlane match appstore --readonly
# Force regenerate (revokes existing)
fastlane match nuke distribution # Revoke all distribution certs
fastlane match appstore # Generate fresh
# Also: nuke development, nuke enterprise
CI 环境变量
MATCH_GIT_URL="https://github.com/your-org/certificates.git"
MATCH_PASSWORD="encryption_password" # Encrypts the repo
MATCH_KEYCHAIN_NAME="fastlane_tmp"
MATCH_KEYCHAIN_PASSWORD="keychain_password"
MATCH_READONLY="true" # CI should never create certs
FASTLANE_USER="[email protected]"
FASTLANE_TEAM_ID="YOURTEAMID"
CI Fastfile 示例
# fastlane/Fastfile
lane :release do
setup_ci # Creates temporary keychain
match(
type: "appstore",
readonly: true, # Critical: never create certs in CI
keychain_name: "fastlane_tmp",
keychain_password: ""
)
build_app(
scheme: "MyApp",
export_method: "app-store"
)
upload_to_app_store(skip_metadata: true, skip_screenshots: true)
end
CI 的钥匙串管理
完整的 CI 钥匙串设置脚本
#!/bin/bash
set -euo pipefail
KEYCHAIN_NAME="ci-build.keychain-db"
KEYCHAIN_PASSWORD="ci-temporary-password"
# 1. Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
# 2. Add to search list (MUST include login.keychain-db or it disappears)
security list-keychains -d user -s "$KEYCHAIN_NAME" login.keychain-db
# 3. Unlock keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
# 4. Prevent keychain from locking during build
security set-keychain-settings -t 3600 -l "$KEYCHAIN_NAME"
# 5. Import signing certificate
security import "$P12_PATH" -k "$KEYCHAIN_NAME" -P "$P12_PASSWORD" \
-T /usr/bin/codesign -T /usr/bin/security
# 6. Allow codesign access without UI prompt (CRITICAL)
# Without this, CI gets errSecInternalComponent
security set-key-partition-list -S apple-tool:,apple: -s \
-k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_NAME"
echo "Keychain ready for code signing"
CI 钥匙串清理脚本
#!/bin/bash
# Run in CI post-build (always, even on failure)
KEYCHAIN_NAME="ci-build.keychain-db"
# Delete temporary keychain
security delete-keychain "$KEYCHAIN_NAME" 2>/dev/null || true
# Restore default keychain search list
security list-keychains -d user -s login.keychain-db
GitHub Actions 示例
- name: Install signing certificate
env:
P12_BASE64: ${{ secrets.P12_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
PROVISION_PROFILE_BASE64: ${{ secrets.PROVISION_PROFILE_BASE64 }}
run: |
# Decode certificate
echo "$P12_BASE64" | base64 --decode > certificate.p12
# Decode provisioning profile
echo "$PROVISION_PROFILE_BASE64" | base64 --decode > profile.mobileprovision
# Create and configure keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security list-keychains -d user -s build.keychain login.keychain-db
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -l build.keychain
security import certificate.p12 -k build.keychain -P "$P12_PASSWORD" \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple: -s \
-k "$KEYCHAIN_PASSWORD" build.keychain
# Install provisioning profile
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Cleanup keychain
if: always()
run: security delete-keychain build.keychain 2>/dev/null || true
Xcode Cloud
Xcode Cloud 会自动管理签名:
- 证书由 Apple 管理,无需手动管理证书
- 配置描述文件会从 Developer Portal 获取
- 在 Xcode → 云端工作流设置中配置签名
- 如有需要,可使用
ci_post_clone.sh执行自定义钥匙串操作
APNs 身份验证:.p8 与 .p12
| 方面 | .p8(基于令牌) | .p12(基于证书) |
|---|---|---|
| 有效期 | 永不过期(撤销后失效) | 1 年(必须每年续期) |
| 适用范围 | 团队中的所有 App | 单个 App ID |
| 每个团队的上限 | 2 个密钥 | 每个 App ID 1 个证书 |
| 设置复杂度 | 较低(一个密钥适用于所有 App) | 较高(每个 App 使用一个证书) |
| 服务端实现 | 需要生成 JWT 令牌 | TLS 客户端证书 |
| 推荐 | 是(Apple 当前的建议) | 旧版(仍受支持) |
.p8 密钥用法
# Generate JWT for APNs (simplified — use a library in production)
# Header: {"alg": "ES256", "kid": "KEY_ID"}
# Payload: {"iss": "TEAM_ID", "iat": TIMESTAMP}
# Sign with .p8 private key
# JWT is valid for 1 hour — cache and refresh before expiry
.p12 证书用法
# Send push with certificate authentication
curl -v \
--cert-type P12 --cert apns-cert.p12:password \
--header "apns-topic: com.example.app" \
--header "apns-push-type: alert" \
--data '{"aps":{"alert":"Hello"}}' \
--http2 https://api.sandbox.push.apple.com/3/device/$TOKEN
# Production: https://api.push.apple.com/3/device/$TOKEN
错误代码参考
security 命令错误
| 错误 | 代码 | 原因 |
|---|---|---|
| errSecInternalComponent | -2070 | 钥匙串已锁定,或未调用 set-key-partition-list |
| errSecItemNotFound | -25300 | 在搜索的钥匙串中找不到证书/密钥 |
| errSecDuplicateItem | -25299 | 钥匙串中已存在证书 |
| errSecAuthFailed | -25293 | 钥匙串密码错误 |
| errSecInteractionNotAllowed | -25308 | 钥匙串已锁定,且无法使用 UI(CI 中未解锁) |
| errSecMissingEntitlement | -34018 | App 缺少访问钥匙串所需的权利 |
codesign 错误
| 错误 | 原因 | 修复方法 |
|---|---|---|
No signing certificate found | 钥匙串中没有有效身份 | 导入证书或检查是否过期 |
ambiguous (matches ...) | 存在多个匹配的身份 | 指定完整身份名称或 SHA-1 哈希 |
not valid for use in ... | 证书类型不匹配(开发版与分发版) | 使用正确的证书类型 |
a sealed resource is missing or invalid | 签名后资源被修改 | 所有修改完成后重新签名 |
invalid signature (code or signature have been modified) | 签名后二进制文件遭到篡改 | 重新签名或重新构建 |
ITMS(App Store 上传)错误
| 代码 | 错误 | 原因 | 解决方法 |
|---|---|---|---|
| ITMS-90035 | 签名无效 | 证书类型错误或证书已过期 | 使用有效的 Apple Distribution 证书签名 |
| ITMS-90161 | 配置描述文件无效 | 配置描述文件与应用不匹配 | 在 Developer Portal 中重新生成配置描述文件 |
| ITMS-90046 | 代码签名 Entitlements 无效 | Entitlements 不在配置描述文件中 | 在门户中添加功能,然后重新生成配置描述文件 |
| ITMS-90056 | 缺少推送通知 Entitlement | 配置描述文件中没有 aps-environment | 启用推送通知功能 |
| ITMS-90174 | 缺少配置描述文件 | 未嵌入配置描述文件 | 使用正确的签名设置进行归档 |
| ITMS-90283 | 配置描述文件无效 | 配置描述文件已过期 | 下载新的配置描述文件 |
| ITMS-90426 | Swift 支持无效 | Swift 库未正确签名 | 使用 Xcode 的管理器导出(不要手动导出) |
| ITMS-90474 | 缺少 Bundle Identifier | Bundle ID 与配置描述文件不匹配 | 确保 Xcode、门户和配置描述文件中的 Bundle ID 一致 |
| ITMS-90478 | Team ID 无效 | Team ID 不匹配 | 检查 DEVELOPMENT_TEAM 构建设置 |
| ITMS-90717 | App Store 分发证书无效 | 使用开发证书提交到 App Store | 切换为 Apple Distribution 证书 |
资源
WWDC:2021-10204、2022-110353
文档:/security、/bundleresources/entitlements、/xcode/distributing-your-app
技能:axiom-code-signing、axiom-code-signing-diag
评论
支持 Markdown