diff --git a/.gitea/workflows/testflight-release.yml b/.gitea/workflows/testflight-release.yml index 19a42a3..04140b7 100644 --- a/.gitea/workflows/testflight-release.yml +++ b/.gitea/workflows/testflight-release.yml @@ -89,14 +89,12 @@ jobs: printf '%s' "${APPSTORE_CERTIFICATES_FILE_BASE64}" | base64 --decode > ios/build/secrets/appstore-signing.p12 printf '%s' "${APPSTORE_PROVISIONING_PROFILE_BASE64}" | base64 --decode > "${HOME}/Library/MobileDevice/Provisioning Profiles/Sybil_AppStore_CI.mobileprovision" - curl -fsSL https://www.apple.com/appleca/AppleIncRootCertificate.cer -o ios/build/secrets/AppleIncRootCertificate.cer curl -fsSL https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer -o ios/build/secrets/AppleWWDRCAG3.cer 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 list-keychains -d user -s "${keychain_path}" $(security list-keychains -d user | sed 's/[ "]//g') - security add-trusted-cert -r trustRoot -k "${keychain_path}" ios/build/secrets/AppleIncRootCertificate.cer security import ios/build/secrets/AppleWWDRCAG3.cer \ -k "${keychain_path}" \ -T /usr/bin/codesign \ diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 91b27ee..27e5187 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -188,6 +188,67 @@ def recreate_app_store_profile(certificate_id) profile_path end +def signing_identity_p12(p12_path, p12_password) + work_dir = File.join(SIGNING_OUTPUT_DIR, "single-identity") + FileUtils.rm_rf(work_dir) + FileUtils.mkdir_p(work_dir) + + all_pem = File.join(work_dir, "all.pem") + stdout, stderr, status = Open3.capture3( + "openssl", "pkcs12", + "-in", p12_path, + "-nodes", + "-passin", "pass:#{p12_password}", + "-out", all_pem + ) + UI.user_error!("Could not inspect exported p12\n#{stderr}\n#{stdout}") unless status.success? + + records = File.read(all_pem).split(/(?=Bag Attributes\n)/) + cert_record = records.find do |record| + record.include?("friendlyName: #{SIGNING_CERTIFICATE_NAME}") && record.include?("-----BEGIN CERTIFICATE-----") + end + UI.user_error!("Could not find #{SIGNING_CERTIFICATE_NAME} certificate in exported p12") unless cert_record + + local_key_id = cert_record[/localKeyID: (.+)/, 1].to_s.strip + UI.user_error!("Could not resolve localKeyID for #{SIGNING_CERTIFICATE_NAME}") unless present?(local_key_id) + + key_record = records.find do |record| + record.include?("localKeyID: #{local_key_id}") && record.include?("-----BEGIN PRIVATE KEY-----") + end + UI.user_error!("Could not find private key for #{SIGNING_CERTIFICATE_NAME}") unless key_record + + cert_path = File.join(work_dir, "identity.cer.pem") + key_path = File.join(work_dir, "identity.key.pem") + File.write(cert_path, cert_record[/-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m]) + File.write(key_path, key_record[/-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----/m]) + + root_cer = File.join(work_dir, "AppleIncRootCertificate.cer") + wwdr_cer = File.join(work_dir, "AppleWWDRCAG3.cer") + root_pem = File.join(work_dir, "AppleIncRootCertificate.pem") + wwdr_pem = File.join(work_dir, "AppleWWDRCAG3.pem") + chain_pem = File.join(work_dir, "chain.pem") + run_silent("curl", "-fsSL", "https://www.apple.com/appleca/AppleIncRootCertificate.cer", "-o", root_cer, error_message: "Could not download Apple root certificate") + run_silent("curl", "-fsSL", "https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer", "-o", wwdr_cer, error_message: "Could not download Apple WWDR G3 certificate") + run_silent("openssl", "x509", "-inform", "DER", "-in", root_cer, "-out", root_pem, error_message: "Could not convert Apple root certificate") + run_silent("openssl", "x509", "-inform", "DER", "-in", wwdr_cer, "-out", wwdr_pem, error_message: "Could not convert Apple WWDR G3 certificate") + File.write(chain_pem, File.read(wwdr_pem) + File.read(root_pem)) + + single_p12_path = File.join(work_dir, "appstore-signing.p12") + run_silent( + "openssl", "pkcs12", "-export", + "-inkey", key_path, + "-in", cert_path, + "-certfile", chain_pem, + "-name", SIGNING_CERTIFICATE_NAME, + "-out", single_p12_path, + "-passout", "pass:#{p12_password}", + error_message: "Could not create single-identity p12" + ) + FileUtils.cp(single_p12_path, p12_path) +ensure + FileUtils.rm_rf(work_dir) if present?(work_dir) +end + def app_store_connect_key_options key_id = ENV["APP_STORE_CONNECT_API_KEY_ID"] issuer_id = ENV["APP_STORE_CONNECT_API_ISSUER_ID"] @@ -293,6 +354,7 @@ platform :ios do end UI.user_error!("Could not find exported p12 at #{p12_path}") unless File.exist?(p12_path) + signing_identity_p12(p12_path, p12_password) profile_path = recreate_app_store_profile(cert_id) UI.user_error!("Could not resolve generated provisioning profile path") unless present?(profile_path) && File.exist?(profile_path)