En el artículo anterior de Firmado iOS hablamos sobre los certificados y como funcionaban. Los certificados son necesarios para firmar todo el software que los desarrolladores van a instalar en un dispositivo iOS. Cuando construimos una aplicación con Xcode, éste usa “codesign”. Esto genera una firma de los contenidos del código ejecutable en el binario usando la clave privada asociada con el certificado de identidad creado por Apple. La firma será incluida en el binario ejecutable. Esto le permite validar y también asegurarse de que el código de que la aplicación tiene no ha podido ser modificado sin causar un error de validación.

Pero el binario firmado no es suficiente para estar seguro de la integridad de la aplicación porque las aplicaciones iOS están comprimidas con más cosas que el ejecutable binario.

Así que, para continuar nuestro aprendizaje sobre el proceso de firmado, en este post vamos a analizar y a tratar de entender las diferentes partes de los ficheros .app y los Provisioning Profiles.

Los ficheros .app

Existen dos tipos diferentes de ficheros .app: aplicaciones OS X y aplicaciones iOS. Las aplicaciones OS son las diseñadas para los ordenadores Apple y las aplicaciones iOS son las diseñadas para dispositivos Apple y tienen estructuras diferentes.

Estructura de las aplicaciones OS X

En OS X las aplicaciones son archivos con extensión .app. Esto se llama “bundle”, una carpeta con contenido estructurado. Son carpetas con extensiones de fichero. Esto permite a otros programas cargarlas o lanzarlas. Por ejemplo tu puedes lanzar la aplicación Xcode haciendo click en el icono del Dock, haciendo doble click en el fichero .app en Finder o buscando Xcode en el Spotlight y pulsando enter. Esto es posible porque los .app bundles se cargan mediante LaunchServices, un sistema que hace posible que un software sea capaz de ejecutar otro software.

Haciendo un click derecho en el fichero y pulsando Mostrar contenido del paquete se puede ver la carpeta Contents:

Esto es lo que se puede ver dentro de un bundle de OS X:

_CodeSignature: una carpeta que contiene el fichero CodeResources.

Frameworks: esta carpeta incluye todas las librerías dinámicas y frameworks usados por la aplicación.

Info.plist: contiene las claves más importantes que requiere el sistema y éstas siempre son accesibles por él. Esto asegura que el sistema siempre tenga la información que necesita para trabajar con la aplicación.

MacOS: incluye el binario ejecutable.

PkgInfo: es un conjunto de datos agrupados en bloques de 4 bytes seguidos de una firma de 4 bytes de la aplicación. Este fichero se crea a la vez que el bundle.

Resources: esta carpeta incluye recursos disponibles para la aplicación. Se pueden encontrar una gran cantidad de archivos diferentes como audios, iconos, layouts, etc. (Incluido el fichero archived-expanded-entitlements.xcent)

Estructura de las aplicaciones iOS

Las aplicaciones iOS son archivos con extensión .ipa. Éstos son también bundles de la misma forma que lo son las aplicaciones OS X pero con una estructura un poco diferente. Si descomprimimos el .ipa podremos ver la carpeta Payload. En esta carpeta se encuentra el .app bundle.

_CodeSignature: una carpeta que contiene el fichero CodeResources.

ficheros .png: ficheros de iconos de la aplicación.

archived-expanded-entitlements.xcent: desde Xcode 6, este fichero ha sido incluido en el bundle de la aplicación. Se trata de una lista de “autorizaciones” (llamados entitlements) que la aplicación puede solicitar y cuando se envía la aplicación a la AppStore permite además mensajes más útiles para los errores que se producen.

Assets.car: este contiene imágenes en múltiples resoluciones por cada dispositivo que se indicase cuando se creó la aplicación. Son las que aparecen en el IDE Xcode en Assets.xcassets.

Base.lproj: contiene los ficheros .storyboard y .xib en el idioma elegido por defecto para la creación de la aplicación. Otros ficheros con extensión .lproj se incluyen cuando en desarrollo se tienen en cuenta más lenguages.

embedded.mobileprovision: el Provisioning Profile incorporado.

Frameworks: esta carpeta incluye todas las librerías dinámicas y frameworks usados por la aplicación.

Info.plist: contiene las claves más importantes que requiere el sistema y éstas siempre son accesibles por él. Esto asegura que el sistema siempre tenga la información que necesita para trabajar con la aplicación.

PkgInfo: es un conjunto de datos agrupados en bloques de 4 bytes seguidos de una firma de 4 bytes de la aplicación. Este fichero se crea a la vez que el bundle y no es necesario para el funcionamiento de una aplicación iOS.

SolidGeariOSExample: este es el binario ejecutable de la aplicación.

Pero la cosa más importante para nosotros es la carpeta _CodeSignature. Como dijimos antes contiene el fichero CodeResources. Esto asegura que no sólo el binario se mantiene sin modificar, sino que también el resto de datos que se usan mientras la aplicación está ejecutándose. El fichero CodeResources es un plist que todos los campos que se incluyen el bundle con un hash para cada uno. Esto puede ser usado para validar que los contenidos del bundle han permanecido sin tocar. Para evitar que la aplicación invalide su propia firma hay recursos que pueden ser omitidos o que son actualizables. Todo ello permite a los desarrolladores firmar un proyecto construido y saber si el binario que van a distribuir es el mismo que se construyó.

Provisioning Profiles

Los certificados que explicamos en el post previo son usados para validar la autenticidad de el firmador de la aplicación. Los Provisioning Profiles son usados para permitir que una aplicación sea instalada en un dispositivo. Es necesario seleccionar el App ID, los Entitlements apropiados y los UDIDs de los dispositivos en los que se quiere instalar la aplicación. Apple firma digitalmente el fichero (un plist con cifrado PKCS#7) de forma que no puede ser modificado después de ser creado si quieres usarlo.

Apple verifica que la aplicación que está siendo instalada pertenece al desarrollador correcto y que el contenido del plist no ha sido modificado.

Para verificar que la firma es correcta se puede usar este comando:

openssl smime -in ProvisioningProfilePath/SolidGearExampleiOS.mobileprovision -inform der -verify

Si todo funciona correctamente aparecerá el mensaje:

</dict>

Verification successful

</plist>%

Pero qué pasa, por ejemplo, si se edita usando vi el Provisioning Profile. Si se cambia la cadena de caracteres deApplicationIdentifierPrefix por otra con un formato válido (por ejemplo una cadena de diez dígitos compuestos por números y letras) se verá lo siguiente:

Verification failure

29456:error:21071065:PKCS7 routines:PKCS7_signatureVerify:digest failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/pkcs7/pk7_doit.c:1039:

29456:error:21075069:PKCS7 routines:PKCS7_verify:signature failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/pkcs7/pk7_smime.c:312:

Pero si se cambia por otra cadena con un formato incorrecto el mensaje de error que aparecerá será:

Error reading S/MIME message

29616:error:0D07207B:asn1 encoding routines:ASN1_get_object:header too long:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/asn1/asn1_lib.c:153:

29616:error:0D068066:asn1 encoding routines:ASN1_CHECK_TLEN:bad object header:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/asn1/tasn_dec.c:1331:

29616:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/asn1/tasn_dec.c:678:Field=cert, Type=PKCS7_SIGNED

29616:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/asn1/tasn_dec.c:768:

29616:error:0D08403A:asn1 encoding routines:ASN1_TEMPLATE_EX_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.50.7/src/crypto/asn1/tasn_dec.c:598:Field=d.sign, Type=PKCS7

Inside the Provisioning Profile

La primera parte hasta que puede verse el principio de la etiqueta plist es la cabecera de la firma PKCS#7 y la parte del final del fichero es también parte de esta firma.

Aquí listamos los valores incluidos en el plist en el orden en el que aparecen en el fichero:

AppIDName: el AppID en el iOS Provisioning Portal.

ApplicationIdentifierPrefix: los diez caracteres generados cuando el App ID se crea. También conocido como App ID Prefix (prefijo del App ID) o Bundle Seed ID.

CreationDate: la fecha en la que el Provisioning Profile fué creado.

DeveloperCertificates: es un array que contiene el certificado. Para inspeccionar su contenido se puede copiar el texto que aparece entre las etiquetas <data> y pegarlo entre estas dos líneas:

—–BEGIN CERTIFICATE—–

TEXT YOU COPY INSIDE THE <DATA> LABELS

—–END CERTIFICATE—–

El texto debe separarse en filas de 64 caracteres y guardarse en un fichero con extensión .pem, por ejemplo SolidGearExampleCertificate.pem y usar este comando:

openssl x509 -text -in SolidGearExampleCertificate.pem

Entitlements: un diccionario que contiene los valores relacionados con Push Notifications, iCloud, GameCenter, PassKit, etc.

ExpirantionDate: la fecha en la que expira el Provisioning Profile.

Name: el nombre del Provisioning Profile cuando el fichero fue creado en el Apple Developer Portal

ProvisionedDevices/ ProvisionsAllDevices: el primero incluye la lista de UDIDs en los cuales la aplicación podrá ser instalada. El segundo está disponible en cuentas Enterprise y “In House Distribution” (forma interna de distribución) y permite instalar la aplicación en cualquier dispositivo. Un Provisioning Profile sólo pude tener una de estos dos valores.

TeamIdentifier: un array con un único valor, el identificador de equipo al que pertenece este Provisioning Profile.

TeamName: el nombre del equipo al que pertenece este Provisioning Profile.

TimeToLive: un valor que muestra el número de días durante los cuales el Provisioning Profile es válido.

UUID: un identificador único que cambia cada vez que el fichero se actualiza.

Version: la actual versión de este tipo de ficheros.

El propósito del Provisioning Profile es permitir la instalación de tu aplicación en dispositivos.El fichero incluye información sobre los dispositivos y el software necesario.

Debido a la firma puede asegurarse que se está instalando la misma aplicación que el desarrollador creó. Además, con una cuenta básica de desarrollador de Apple, el administrador puede controlar los dispositivos en los que la aplicación puede instalarse (máximo 100 e ilimitados para una cuenta Enterprise).

Ahora cuando creas un .app file ya sabes que se genera y también como instalar tu aplicación en un dispositivo. La próxima vez veremos más cosas sobre el firmado iOS.

Otros artículos interesantes

iOS: Gráfico de barras sencillo en Swift con UIStackViews

Mejora la calidad de tu Apps aplicando Testing – iOS

Desarrolla tu app iOS securizada con Face ID y Touch ID