La firma de código es una de esas partes del desarrollo para plataformas Apple que normalmente pasa desapercibida hasta que deja de funcionar. Xcode muestra un mensaje sobre un certificado, otro sobre un perfil de aprovisionamiento y, unos minutos después, uno más indicando que una capability no está permitida. Como todos esos errores aparecen durante la misma fase del build, es fácil pensar que certificados, App IDs, perfiles y entitlements son nombres diferentes para una misma cosa.
En realidad, cada pieza responde a una pregunta distinta. La firma demuestra quién ha generado una aplicación y permite detectar si su contenido ha cambiado. El App ID identifica la aplicación dentro del equipo de desarrollo. Los entitlements describen los privilegios que necesita el ejecutable. Finalmente, el perfil de aprovisionamiento conecta todos esos elementos y establece dónde puede ejecutarse el resultado.
Entender esta separación no solo ayuda a resolver errores de Xcode. También permite diseñar mejor la firma de proyectos con varias extensiones, preparar entornos de integración continua y evitar que una renovación de certificados bloquee una publicación.
La firma de código garantiza identidad e integridad
Al firmar una aplicación, el sistema calcula representaciones criptográficas de su ejecutable y de los recursos que forman parte del sello de código. La firma se crea utilizando una clave privada y queda asociada a un certificado emitido por Apple. Cuando iOS verifica la aplicación puede comprobar dos propiedades fundamentales:
- El código procede de una identidad de desarrollo reconocida.
- El contenido protegido por la firma no ha sido modificado después de firmarse.
La firma no indica que una aplicación sea segura, esté libre de errores o haya superado la revisión del App Store. Únicamente establece una cadena de confianza y protege la integridad del paquete firmado. Si se altera el ejecutable o uno de los recursos sellados después del proceso, la verificación deja de ser válida.
Aquí aparece una distinción especialmente importante: un certificado no contiene la clave privada. El certificado incluye la identidad y la clave pública, pero para firmar hace falta la clave privada correspondiente, que suele almacenarse en el llavero del Mac.
Por eso copiar un archivo .cer a otro ordenador no suele solucionar los errores de firma. El nuevo equipo podrá instalar el certificado, pero Xcode no encontrará la identidad completa si no dispone también de su clave privada. Cuando es necesario trasladar una identidad local, se exportan certificado y clave privada juntos en un fichero .p12, protegido con una contraseña y tratado como un secreto.
Apple diferencia principalmente dos identidades para aplicaciones modernas:
Apple Development, destinada a ejecutar y depurar aplicaciones durante el desarrollo.Apple Distribution, utilizada para distribución Ad Hoc y para enviar compilaciones a App Store Connect, entre otros flujos de distribución.
Los certificados de desarrollo pertenecen a miembros concretos del equipo, mientras que los de distribución representan al equipo. Además, desde Xcode 13 el flujo de distribución del Organizer puede recurrir a certificados gestionados en la nube cuando no existe una identidad de distribución local. Esto reduce la necesidad de compartir claves privadas entre todos los miembros, aunque no elimina la importancia de entender qué identidad está utilizando cada proceso de firma.
El App ID identifica a la aplicación
El bundle identifier definido en Xcode, por ejemplo com.example.TravelJournal, identifica el producto en el proyecto. En la cuenta de Apple Developer existe un App ID asociado a ese identificador. Para la mayoría de aplicaciones modernas se utiliza un App ID explícito, que representa exactamente un bundle ID.
El identificador efectivo que aparece en algunos entitlements incorpora también el Team ID:
AB12C3D4E5.com.example.TravelJournal
└── Team ID ──┘└──────── Bundle ID ────────┘
Esta composición explica muchos errores relacionados con application-identifier o con grupos de acceso del llavero. No basta con que la parte final parezca correcta: la aplicación, el perfil y los servicios asociados deben pertenecer al mismo equipo.
Cada ejecutable necesita su propia identidad. Si la aplicación incluye un widget, una extensión de notificaciones o un App Clip, cada uno posee un bundle identifier diferente y, normalmente, su propio App ID y su propio perfil. Firmar correctamente el target principal no implica que los ejecutables anidados estén bien configurados.
Capabilities y entitlements no son exactamente lo mismo
Una capability es la funcionalidad de alto nivel que activamos en la pestaña Signing & Capabilities de Xcode: App Groups, iCloud, Push Notifications, Sign in with Apple o Keychain Sharing, por ejemplo. Al añadirla, Xcode modifica la configuración necesaria y, cuando corresponde, incorpora claves a un archivo .entitlements.
Los entitlements son los privilegios concretos que terminan dentro de la firma del ejecutable. Se representan como un diccionario de propiedades. Una aplicación de notas compartidas entre la app principal y su widget podría declarar algo parecido a lo siguiente:
<?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>com.apple.security.application-groups</key>
<array>
<string>group.com.example.SharedNotes</string>
</array>
</dict>
</plist>
Ese fichero expresa lo que el target solicita, pero no concede el permiso por sí mismo. Para que la firma sea válida deben coincidir tres niveles:
- La “capacidad” debe estar habilitada para el App ID en la cuenta de Apple Developer.
- El perfil de aprovisionamiento debe autorizar el entitlement y sus valores.
- La firma del ejecutable debe contener un valor compatible con el permitido por el perfil.
Esta es la razón por la que añadir manualmente una clave a .entitlements no permite utilizar una capacidad restringida. El archivo forma parte de la solicitud; el perfil, firmado por Apple, actúa como autorización. Algunas capacidades, además, necesitan aprobación expresa de Apple o solo están disponibles para determinados métodos de distribución.
Modificar las capacidades de un App ID puede invalidar los perfiles existentes que dependen de él. Con firma automática, Xcode suele solicitar un perfil actualizado. Con firma manual, el equipo debe regenerarlo, descargarlo e instalarlo antes de volver a compilar.
El perfil de aprovisionamiento conecta todas las piezas
Un perfil de aprovisionamiento es un contenedor firmado por Apple, con extensión .mobileprovision, que establece las condiciones bajo las que puede utilizarse una aplicación. Entre otros datos, puede incluir:
- El App ID autorizado.
- Los certificados que pueden firmar la aplicación.
- Los entitlements permitidos.
- La modalidad de distribución.
- Los dispositivos registrados, cuando el tipo de perfil los necesita.
- Una fecha de caducidad.
El perfil no sustituye a la firma. Su función es demostrar que Apple autoriza a una identidad concreta a firmar una aplicación determinada, con ciertos permisos y para un destino de ejecución concreto. Se puede imaginar como la intersección de varios conjuntos:
Aplicación + identidad + permisos + destino = perfil válido
Si uno solo de esos términos deja de coincidir, la firma o la instalación falla. Un certificado nuevo no estará incluido en un perfil antiguo; un dispositivo recién registrado no aparecerá en un perfil ya generado; y una nueva capacidad no podrá utilizarse hasta que el perfil la autorice.
Los principales tipos de perfil
La información que contiene el perfil cambia en función de su propósito.
Desarrollo
Un perfil de desarrollo relaciona el App ID con uno o varios certificados Apple Development y con una lista de dispositivos registrados. Permite instalar la aplicación desde Xcode y depurarla. Normalmente incluye el entitlement get-task-allow, que hace posible que el depurador se conecte al proceso.
Ad Hoc
Un perfil Ad Hoc utiliza un certificado de distribución, pero también contiene una lista de dispositivos registrados. Permite distribuir una compilación fuera de Xcode exclusivamente a esos dispositivos. Es útil para ciertos procesos internos de prueba, aunque TestFlight suele ofrecer un flujo más cómodo para grupos amplios.
App Store Connect
Este perfil utiliza un App ID explícito y un certificado de distribución. No contiene una lista de dispositivos porque la compilación se envía a App Store Connect y no se instala directamente mediante el perfil en un conjunto cerrado de terminales. El mismo flujo sirve como entrada para la distribución mediante TestFlight y el App Store.
La diferencia práctica puede resumirse así:
| Tipo | Certificado | Limita por dispositivo | Uso principal |
|---|---|---|---|
| Desarrollo | Apple Development | Sí | Ejecutar y depurar desde Xcode |
| Ad Hoc | Apple Distribution | Sí | Distribuir a dispositivos registrados |
| App Store Connect | Apple Distribution | No | TestFlight y App Store |
Elegir un perfil con el App ID correcto no es suficiente si pertenece a otra modalidad. Una compilación de App Store no puede firmarse como desarrollo, y una aplicación Ad Hoc no se instalará en un dispositivo cuyo UDID no esté incluido.
Firma automática o manual
Con Automatically manage signing, Xcode crea o selecciona certificados, registra dispositivos cuando tiene permisos para hacerlo y mantiene actualizados los perfiles necesarios. Para desarrollo local es la opción más sencilla y, en la mayoría de proyectos, también la más robusta.
La firma automática no significa que desaparezcan las piezas anteriores. Xcode sigue utilizando App IDs, certificados, perfiles y entitlements; simplemente se encarga de coordinarlos. Si la cuenta del desarrollador no tiene permisos para crear identificadores, modificar capacidades o registrar dispositivos, la automatización también puede fallar. Apple ofrece controles específicos para limitar esas operaciones en equipos grandes.
La firma manual permite seleccionar de forma explícita la identidad y el perfil de cada target y configuración. Puede ser adecuada cuando un pipeline necesita artefactos completamente controlados, existen varios equipos de firma o las políticas internas impiden que el entorno de CI modifique recursos de la cuenta de desarrollador.
Sin embargo, esa previsibilidad tiene un coste. El equipo debe renovar certificados y perfiles, incorporar nuevos dispositivos, regenerar perfiles cuando cambian las capacidades y garantizar que cada extensión utiliza los activos correctos. Una configuración manual abandonada durante meses suele convertirse en una fuente de fallos justo cuando hay que publicar una versión urgente.
Qué ocurre durante el build
De forma simplificada, Xcode realiza la siguiente secuencia:
- Resuelve los ajustes de firma para el target y la configuración actuales.
- Selecciona una identidad cuya clave privada esté disponible, o utiliza un flujo compatible de firma en la nube.
- Busca un perfil cuyo App ID, certificado, modalidad y capacidades sean compatibles.
- Construye los entitlements definitivos, expandiendo valores como el Team ID cuando sea necesario.
- Firma el ejecutable y los recursos del bundle.
- Inserta el perfil correspondiente en la aplicación cuando el flujo lo requiere.
Los targets anidados se firman antes que el contenedor que los incluye. Por eso un error en una extensión puede aparecer al final del proceso como un fallo de firma de toda la aplicación. Todos los ejecutables deben formar una cadena coherente: no es válido corregir únicamente el target principal.
Cómo diagnosticar los errores sin probar al azar
Cuando la firma falla, cambiar certificados y borrar perfiles indiscriminadamente suele ocultar la causa en lugar de resolverla. Es más efectivo comparar lo que solicita el proyecto con lo que realmente contienen el producto firmado y su perfil.
Para enumerar las identidades de firma disponibles en el llavero:
security find-identity -v -p codesigning
Si el certificado aparece en Acceso a Llaveros pero no en esta lista, es frecuente que falte la clave privada, que el certificado haya caducado o que la cadena de confianza no sea válida.
Los entitlements que han terminado dentro de una aplicación ya compilada pueden inspeccionarse con:
codesign -d --entitlements :- "/ruta/TravelJournal.app"
El perfil incrustado se puede decodificar como un plist y revisar por separado:
security cms -D \
-i "/ruta/TravelJournal.app/embedded.mobileprovision" \
> "/tmp/travel-profile.plist"
/usr/libexec/PlistBuddy \
-c "Print:Entitlements" \
"/tmp/travel-profile.plist"
La comparación entre ambos resultados es muy reveladora. El ejecutable no debe reclamar privilegios que el perfil no autoriza. También conviene revisar application-identifier, com.apple.developer.team-identifier, los App Groups y los grupos de acceso del llavero, ya que incorporan identificadores de equipo o valores exactos que no admiten aproximaciones.
Para comprobar la integridad de la firma del bundle y de su código anidado:
codesign --verify --deep --strict --verbose=4 \
"/ruta/TravelJournal.app"
Y cuando el problema está en la selección que hace Xcode, los ajustes efectivos del proyecto muestran más información que la interfaz gráfica:
xcodebuild -project TravelJournal.xcodeproj \
-scheme TravelJournal \
-configuration Release \
-showBuildSettings \
| grep -E "CODE_SIGN|DEVELOPMENT_TEAM|PROVISIONING_PROFILE"
Estos comandos permiten clasificar el fallo antes de modificar nada: identidad ausente, App ID incorrecto, perfil incompatible, entitlements desalineados o configuración equivocada para el método de distribución.
Firma en integración continua
Que un proyecto compile en el Mac de un desarrollador y falle en CI suele indicar una diferencia de entorno, no de código. En firma manual, el agente necesita tanto el perfil como la identidad completa. Instalar únicamente el certificado público no sirve: la clave privada debe importarse en un llavero accesible durante el job.
Los ficheros .p12, sus contraseñas y cualquier credencial de App Store Connect deben almacenarse como secretos del sistema de CI, nunca en el repositorio. También conviene crear llaveros temporales para el proceso y limitar el acceso de cada pipeline a los recursos que realmente necesita.
Con firma automática, el agente debe estar autenticado y autorizado para resolver o actualizar los activos. xcodebuild puede recibir -allowProvisioningUpdates en un entorno preparado para ello, pero activar esa opción sin proporcionar las credenciales y permisos adecuados no convierte el proceso en automático por sí solo.
Independientemente de la estrategia, un pipeline estable debe hacer explícitos el Team ID, el bundle identifier, la configuración que se archiva y el método de exportación. La mezcla más problemática consiste en gestionar automáticamente el proyecto en local, fijar perfiles manuales en CI y no documentar cuál de los dos entornos es la fuente de verdad.
Errores frecuentes y su causa real
No signing certificate ... found indica que Xcode no encuentra una identidad adecuada. Puede faltar el certificado, pero también su clave privada.
Provisioning profile doesn't include signing certificate significa que la identidad seleccionada no está autorizada por ese perfil. Suele ocurrir después de renovar o sustituir un certificado sin regenerar los perfiles manuales.
Provisioning profile doesn't support the ... capability revela una diferencia entre los entitlements solicitados y los permitidos. Hay que comprobar la capacidad en el App ID y generar un perfil nuevo, no limitarse a editar el archivo .entitlements.
A valid provisioning profile for this executable was not found puede deberse a un App ID, un Team ID o un tipo de distribución incorrectos, a un perfil caducado o a que el dispositivo no figura en un perfil de desarrollo o Ad Hoc.
Si la app principal se firma pero una extensión no, el problema suele estar en el App ID o el perfil de esa extensión. Cada .appex es un ejecutable independiente y debe disponer de una configuración compatible con sus propios entitlements.
Un modelo mental que evita la mayoría de errores
La firma de código resulta mucho más sencilla cuando no se trata el perfil como un archivo mágico que hace funcionar Xcode. Cada elemento conserva una responsabilidad precisa:
- Certificado y clave privada: quién firma.
- App ID: qué aplicación se firma.
- Entitlements: qué privilegios solicita el ejecutable.
- Perfil de aprovisionamiento: qué combinación ha autorizado Apple y dónde puede ejecutarse.
- Firma: prueba criptográfica de identidad e integridad sobre el producto final.
La comprobación útil no consiste en preguntar si existe un certificado o un perfil, sino si todas las piezas describen exactamente la misma aplicación, el mismo equipo, las mismas capacidades y el mismo método de distribución. Cuando esa intersección es coherente, Xcode deja de parecer caprichoso y los mensajes de error pasan a señalar desajustes concretos que se pueden verificar.