# Guía de Firma de Extensiones Esta guía explica cómo firmar las extensiones de Classic Add-ons Archive para Firefox y Chrome. ## 🔐 ¿Por qué firmar? - **Firefox**: Las extensiones firmadas pueden distribuirse fuera de AMO - **Chrome**: La firma genera un ID consistente y permite actualizaciones - **Seguridad**: Los usuarios pueden verificar la autenticidad - **Profesional**: Distribución en stores oficiales ## 📋 Requisitos Previos ### Software necesario ```bash # Instalar Node.js y npm (si no lo tienes) sudo apt install nodejs npm # Debian/Ubuntu # o brew install node # macOS # Instalar dependencias del proyecto (incluye web-ext) npm install # Para Chrome: openssl (generalmente ya instalado) openssl version ``` **Nota**: web-ext se instala automáticamente como dependencia local, no necesitas instalarlo globalmente. ### Permisos de ejecución ```bash chmod +x scripts/generate-keys.sh chmod +x build.sh ``` ## 🔑 Paso 1: Generar Claves ### Generación automática ```bash cd /home/ale/projects/firefox/ca-archive ./scripts/generate-keys.sh ``` Este script genera: - **Chrome**: Clave privada RSA 2048-bit (`private-keys/chrome-extension.pem`) - **Chrome**: Extension ID calculado - **Firefox**: Plantilla de credenciales AMO ### Generación manual (alternativa) **Para Chrome:** ```bash mkdir -p private-keys openssl genrsa -out private-keys/chrome-extension.pem 2048 ``` **Para Firefox:** Crea `private-keys/firefox-amo-credentials.json`: ```json { "web-ext": { "sign": { "apiKey": "user:12345678:123", "apiSecret": "tu-secret-aqui", "id": "ca-archive@Off.JustOff", "channel": "unlisted" } } } ``` ## 🦊 Paso 2: Configurar Credenciales de Firefox (AMO) ### Obtener credenciales AMO 1. **Inicia sesión** en https://addons.mozilla.org 2. **Ve a API Keys**: https://addons.mozilla.org/developers/addon/api/key/ 3. **Genera credenciales**: - Click en "Generate new credentials" - Copia el **JWT issuer** (API Key) - Copia el **JWT secret** (API Secret) 4. **Edita el archivo de credenciales**: ```bash nano private-keys/firefox-amo-credentials.json ``` 5. **Agrega tus credenciales**: ```json { "web-ext": { "sign": { "apiKey": "user:12345678:456", "apiSecret": "1a2b3c4d5e6f7g8h9i0j...", "channel": "unlisted" } } } ``` **Nota importante**: El ID de la extensión (`ca-archive@Off.JustOff`) se lee automáticamente de `manifest.json`, no es necesario incluirlo en las credenciales. ### Tipos de canal (channel) - **`unlisted`**: No aparece en búsquedas de AMO, distribución externa - **`listed`**: Aparece en AMO, revisión más estricta **Recomendado**: Usar `unlisted` para desarrollo y `listed` para producción. ## 🏗️ Paso 3: Construir y Firmar ### Firmar ambas versiones ```bash ./build.sh --sign ``` Esto: 1. Construye paquetes ZIP sin firmar 2. Firma el paquete de Firefox con AMO 3. Firma el paquete de Chrome con tu clave privada 4. Genera archivos `.xpi` (Firefox) y `.crx` (Chrome) ### Firmar solo Firefox ```bash ./build.sh --sign-firefox ``` ### Firmar solo Chrome ```bash ./build.sh --sign-chrome ``` ### Usando npm scripts ```bash # Generar claves npm run keys:generate # Construir sin firmar npm run build # Construir y firmar todo npm run build:sign # Firmar solo Firefox npm run build:sign-firefox # Firmar solo Chrome npm run build:sign-chrome ``` ## 📦 Archivos Generados Después de la firma encontrarás en `dist/`: ### Firefox - `ca-archive-3.0.0.xpi` - Paquete sin firmar - `ca-archive-3.0.0-an+fx.xpi` - Paquete firmado por AMO - `firefox-sign.log` - Log del proceso de firma ### Chrome - `ca-archive-3.0.0-chrome.zip` - Paquete ZIP sin firmar - `ca-archive-3.0.0-chrome.crx` - Paquete firmado - `chrome-extension-id.txt` - ID de la extensión - `ca-archive-3.0.0-chrome.crx.sha256` - Hash de verificación ## 🔍 Verificar Firma ### Firefox ```bash # Verificar que es un ZIP válido unzip -t dist/ca-archive-*.xpi # Ver metadatos (requiere web-ext) web-ext lint --source-dir=. --self-hosted ``` ### Chrome ```bash # Verificar hash SHA256 sha256sum -c dist/ca-archive-*-chrome.crx.sha256 # Ver Extension ID cat dist/chrome-extension-id.txt ``` ## 🚀 Distribución ### Firefox **Opción 1: AMO (Recomendado para usuarios finales)** 1. Ve a: https://addons.mozilla.org/developers/ 2. Click en "Submit a New Add-on" 3. Sube el archivo `.xpi` firmado 4. Completa información y submit para revisión **Opción 2: Self-hosted (para desarrollo)** 1. Sube el `.xpi` firmado a tu servidor 2. Usuarios pueden instalarlo desde: `https://tu-servidor.com/ca-archive.xpi` 3. Firefox verificará la firma automáticamente ### Chrome **Opción 1: Chrome Web Store (Recomendado)** 1. Ve a: https://chrome.google.com/webstore/devconsole 2. Click en "New Item" 3. Sube el archivo **`.zip`** (NO el .crx) 4. Completa información y publica **Nota**: Chrome Web Store firma automáticamente, no subas el .crx. **Opción 2: Enterprise distribution (.crx)** 1. Distribuye el archivo `.crx` firmado 2. Los usuarios deben agregarlo manualmente 3. Requiere políticas empresariales habilitadas ## 🔐 Seguridad de las Claves ### ⚠️ IMPORTANTE: Protege tus claves ```bash # Permisos seguros chmod 600 private-keys/chrome-extension.pem chmod 600 private-keys/firefox-amo-credentials.json chmod 700 private-keys/ # Verificar que están en .gitignore git status private-keys/ # Debe decir "ignored" ``` ### Backup de claves ```bash # Hacer backup cifrado tar czf ca-archive-keys-backup.tar.gz private-keys/ gpg -c ca-archive-keys-backup.tar.gz rm ca-archive-keys-backup.tar.gz # Guarda ca-archive-keys-backup.tar.gz.gpg en lugar seguro ``` ### Restaurar backup ```bash gpg -d ca-archive-keys-backup.tar.gz.gpg > ca-archive-keys-backup.tar.gz tar xzf ca-archive-keys-backup.tar.gz ``` ### ¿Qué hacer si pierdes las claves? **Chrome:** - El Extension ID cambiará - Los usuarios verán una extensión "nueva" - Perderás valoraciones/estadísticas **Firefox:** - Puedes generar nuevas credenciales AMO - El ID de la extensión se mantiene (está en manifest.json) ## 🐛 Troubleshooting ### Error: "Cannot set custom ID ... because manifest.json declares ID" **Causa**: El archivo de credenciales incluye un campo `"id"` que ya está en manifest.json **Solución**: Edita `private-keys/firefox-amo-credentials.json` y elimina la línea con `"id"`: ```json { "web-ext": { "sign": { "apiKey": "user:12345678:123", "apiSecret": "tu-secret-aqui", "channel": "unlisted" } } } ``` El ID se lee automáticamente de `manifest.json`. ### Error: "Unknown JWT iss (issuer)" - Status 401 **Error completo:** ``` WebExtError: Received bad response from the server while requesting https://addons.mozilla.org/api/v4/addons/... status: 401 response: {"detail":"Unknown JWT iss (issuer)."} ``` **Causa**: Las credenciales AMO son inválidas o son credenciales de ejemplo/prueba. **Soluciones**: 1. **Verificar que tienes credenciales reales de AMO:** ```bash cat private-keys/firefox-amo-credentials.json ``` Si ves valores como: - `"apiKey": "user:12345678:123"` ← ❌ Ejemplo, no funciona - `"apiSecret": "1234567890abcdef..."` ← ❌ Ejemplo, no funciona **Debes obtener credenciales reales:** 2. **Obtener credenciales reales de AMO:** - Ve a: https://addons.mozilla.org/developers/addon/api/key/ - Inicia sesión con tu cuenta de Firefox - Click en "Generate new credentials" - Dale un nombre descriptivo (ej: "ca-archive signing") - **Copia el JWT issuer** → Este es tu `apiKey` - **Copia el JWT secret** → Este es tu `apiSecret` 3. **Actualizar archivo de credenciales:** ```json { "web-ext": { "sign": { "apiKey": "user:TU_USER_ID_REAL:TU_KEY_ID_REAL", "apiSecret": "tu_secret_real_de_64_caracteres_hexadecimales_aqui", "channel": "unlisted" } } } ``` 4. **Volver a intentar:** ```bash ./build.sh --sign ``` **⚠️ Nota importante:** - El archivo de ejemplo tiene credenciales de prueba que NO funcionan - DEBES generar tus propias credenciales en AMO - Las credenciales son secretas, no las compartas ni las subas a Git - El archivo `private-keys/` está excluido de Git por seguridad ### Error: "API Key invalid" **Causa**: Credenciales de AMO incorrectas o mal formateadas **Solución**: 1. Verifica que copiaste correctamente API Key y Secret (sin espacios extra) 2. Verifica formato JSON válido: `python3 -m json.tool private-keys/firefox-amo-credentials.json` 3. Regenera credenciales en AMO si lleva mucho tiempo creadas 4. Verifica que tu cuenta AMO tenga permisos de API habilitados ### Error: "Private key not found" **Causa**: No se generaron las claves **Solución**: ```bash ./scripts/generate-keys.sh ``` ### Error: "web-ext: command not found" **Solución**: ```bash # Instalar dependencias locales npm install # El script usará automáticamente web-ext desde node_modules ./build.sh --sign ``` **Alternativa (instalación global)**: ```bash npm install -g web-ext ``` ### Chrome: "Package is invalid: CRX_HEADER_INVALID" **Causa**: Archivo .crx mal formado **Solución**: - Usa la versión .zip para Chrome Web Store - Regenera el .crx con: `./build.sh --sign-chrome` ### Firefox: Validación falla **Solución**: ```bash # Validar primero sin firmar npm run lint # Corregir errores y luego firmar ./build.sh --sign-firefox ``` ## 📊 Automatización (CI/CD) ### GitHub Actions (ejemplo) ```yaml name: Build and Sign on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node uses: actions/setup-node@v2 with: node-version: '16' - name: Install dependencies run: npm install -g web-ext - name: Setup keys run: | mkdir -p private-keys echo "${{ secrets.CHROME_KEY }}" > private-keys/chrome-extension.pem echo "${{ secrets.FIREFOX_CREDS }}" > private-keys/firefox-amo-credentials.json - name: Build and sign run: ./build.sh --sign - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: signed-packages path: dist/ ``` **Configura secrets en**: Repository Settings > Secrets and variables > Actions ## 📚 Recursos Adicionales - [Firefox Extension Workshop](https://extensionworkshop.com/) - [Chrome Extension Publishing](https://developer.chrome.com/docs/webstore/publish/) - [web-ext Documentation](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/) - [AMO Signing API](https://addons-server.readthedocs.io/en/latest/topics/api/signing.html) ## 🆘 Soporte ¿Problemas con la firma? 1. Revisa los logs en `dist/*-sign.log` 2. Verifica permisos: `ls -la private-keys/` 3. Prueba con: `npm run lint` 4. Abre un issue en GitHub --- **Recuerda**: NUNCA compartas tus claves privadas ni las subas a repositorios públicos.