2012-09-27 19 views
18

Fondo rápido: Lanzamos una aplicación webstart, que incluye nuestros propios archivos jar y numerosos archivos jar de terceros. Webstart requiere que todos los archivos jar a los que hace referencia el archivo jnlp estén firmados por un solo certificado. Por lo tanto, firmamos todos los frascos (nuestros frascos y frascos de terceros) utilizando un certificado autofirmado. Algunos frascos de terceros ya están firmados por la parte que los produjo, pero solo los firmamos nuevamente, y esto funciona bien. Hasta ahora.Lo que impide que Java verifique los archivos jar firmados con múltiples algoritmos de firma

Problema: Hace poco pasamos de Java 6 a Java 7 y de repente, webstart se niega a cargar algunas jarras, quejándose de: "Inventario de firmas SHA1 inválido". Esto solo ocurre para algunos tarros y no para otros, y el hilo común aparece entre aquellos tarros que parecen tener múltiples firmas.

Después de buscar en S.O. e Internet, parece que el algoritmo de firma predeterminado para jarsigner de Java ha cambiado entre Java 6 y Java 7, de SHA1 a SHA256, y varias personas recomiendan utilizar "jarsigner -digestalg SHA1" para solucionar problemas de verificación. Intenté eso y, por supuesto, ahora verificamos nuestros frascos con signo múltiple. Así que esto parece ser una solución para nuestro problema.

Por lo que puedo deducir, parece que la firma de un tercero es una firma SHA1, y estábamos firmando con el valor predeterminado - SHA256 - lo que resulta en una mezcla de firmas. Cuando fuerzo a SHA1 con el interruptor '-digestalg', tenemos dos firmas del mismo tipo, y la verificación ahora funciona. ¿Entonces parece que el problema es causado por tener múltiples firmas con diferentes algoritmos? O hay algún otro factor que me estoy perdiendo.

Preguntas:

  1. ¿Por qué no verifica con SHA1 + SHA256, pero verifica con SHA1 SHA1 +? ¿Hay una razón técnica? ¿Una razón de política de seguridad? ¿Por qué no puede verificar que ambas firmas son correctas?
  2. ¿Hay alguna desventaja para nosotros al usar (seguir usando) SHA1 en lugar del SHA256 ahora predeterminado?
+0

he observado que incluso, aunque SHA1 + SHA256 falla cuando se utilizan diferentes teclas ... si usted firmó el JAR utilizando SHA1 + SHA256 con la misma clave, la verificación nunca falla. –

Respuesta

7

En lugar de volver a firmar los terceros frascos partido mismo, puede crear un archivo JNLP separado para cada firmante de terceros que se refiere a los archivos JAR pertinentes, y luego tener su JNLP principal dependerá de éstos usando el elemento <extension> . La restricción de que todos los archivos JAR deben estar firmados por el mismo firmante solo se aplica dentro de un JNLP, cada extensión puede tener un firmante diferente.

De no ser así, se podría despojar a cabo el tercer partido de firmas antes de añadir su propio (reembalando sin META-INF/*.{SF,DSA,RSA})

+0

Gracias por las sugerencias. Es demasiado engorroso crear jnlps por separado para cada biblioteca de terceros (tenemos más de 150 jarras). Consideré eliminar las otras firmas en nuestro script de compilación, pero: (1) nuestra solución actual (descrita en mi pregunta) fue * mucho * menos esfuerzo; (2) nuestra solución actual da como resultado una compilación más rápida (sin tener que reconstruir esos archivos jar), que es importante para una aplicación grande; (3) no estoy seguro de si eliminar esas firmas realmente podría causar un problema para cualquiera de esas librerías. – JimN

+0

Veo su punto, pero tenga en cuenta que solo las libs de terceros _que están firmadas por código_ necesitarían hacer extensiones (en mi aplicación 50+ JAR solo era javamail y la activación a la que esto se aplica). –

1

Sé que esto es un poco tarde - pero vamos a través de esto ahora. Nuestro problema era el problema de la firma de "MD2withRSA". Resolví el problema en un par de pasos:

1) Trabajé con Verisign para eliminar el 'antiguo' algoritmo de nuestro certificado, por lo que el algoritmo MD2withRSA ya no se usaba para firmar nuestros archivos jar.

2) También tenemos un montón de jarras de terceros y las volvemos a firmar sin nuestro certificado. Encontramos los "no todos los archivos jar firmados con el mismo certificado" cuando los algoritmos SHA1 y SHA-256 se enumeraron en MANIFEST.MF. Esto era solo un pequeño subconjunto de los archivos jar, así que para ellos, eliminamos la mitad inferior del archivo MANIFEST.MF; esa parte con el Nombre: clase y la especificación del algoritmo. Esa información se vuelve a generar en la última parte de nuestro proceso. Descomprimimos, excluimos la vieja información de firmas y re-jar.El último paso es volver a firmar los frascos. Encontramos que, en algunos casos, si el antiguo nombre: entrada con la entrada SHA1 estaba en MANIFEST.MF, que la firma no lo reemplazó con SHA-256, entonces manejamos manualmente esos archivos (por ahora). Trabajando en la actualización de nuestras tareas Ant para manejar esto.

Lo sentimos, no se explica por qué el inicio web no lo maneja/lo permite, ¡solo descubrió cómo hacerlo funcionar!

¡Buena suerte!

1

Parece un error en el JRE. Personalmente, estoy asumiendo que el antiguo algoritmo de firma predeterminado (DSA con resumen SHA1) es menos seguro que el nuevo (RSA con resumen SHA256), por lo que es mejor no usar la opción "-digestalg SHA1".

He resuelto este problema mediante el uso de una tarea Ant personalizada en mi script de compilación para 'anular' mis tarros antes de firmarlos. De esta forma, solo hay una firma para cada jarra.

Aquí es mi tarea Ant:

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.zip.ZipEntry; 
import java.util.zip.ZipInputStream; 
import java.util.zip.ZipOutputStream; 

import org.apache.tools.ant.BuildException; 
import org.apache.tools.ant.Task; 
import org.apache.tools.ant.types.FileSet; 
import org.apache.tools.ant.types.Path; 
import org.apache.tools.ant.types.Resource; 
import org.apache.tools.ant.types.resources.FileProvider; 
import org.apache.tools.ant.types.resources.FileResource; 
import org.apache.tools.ant.util.FileUtils; 
import org.apache.tools.ant.util.ResourceUtils; 

public class UnsignJar extends Task { 

    protected List<FileSet> filesets = new ArrayList<FileSet>(); 

    protected File todir; 

    public void addFileset(final FileSet set) { 
     filesets.add(set); 
    } 

    public void setTodir(File todir) { 
     this.todir = todir; 
    } 

    @Override 
    public void execute() throws BuildException { 
     if (todir == null) { 
      throw new BuildException("todir attribute not specified"); 
     } 
     if (filesets.isEmpty()) { 
      throw new BuildException("no fileset specified"); 
     } 

     Path path = new Path(getProject()); 
     for (FileSet fset : filesets) { 
      path.addFileset(fset); 
     } 

     for (Resource r : path) { 
      FileResource from = ResourceUtils.asFileResource(r 
        .as(FileProvider.class)); 

      File destFile = new File(todir, from.getName()); 
      File fromFile = from.getFile(); 

      if (!isUpToDate(destFile, fromFile)) { 
       unsign(destFile, fromFile); 
      } 
     } 


    } 

    private void unsign(File destFile, File fromFile) { 
     log("Unsigning " + fromFile); 
     try { 
      ZipInputStream zin = new ZipInputStream(
        new FileInputStream(fromFile)); 
      ZipOutputStream zout = new ZipOutputStream(
        new FileOutputStream(destFile)); 

      ZipEntry entry = zin.getNextEntry(); 
      while (entry != null) { 
       if (!entry.getName().startsWith("META-INF")) { 
        copyEntry(zin, zout, entry); 
       } 
       zin.closeEntry(); 

       entry = zin.getNextEntry(); 
      } 

      zin.close(); 
      zout.close(); 

     } catch (IOException e) { 
      throw new BuildException(e); 
     } 
    } 

    private void copyEntry(ZipInputStream zin, ZipOutputStream zout, 
      ZipEntry entry) throws IOException { 
     zout.putNextEntry(entry); 
     byte[] buffer = new byte[1024 * 16]; 
     int byteCount = zin.read(buffer); 
     while (byteCount != -1) { 
      zout.write(buffer, 0, byteCount); 
      byteCount = zin.read(buffer); 
     } 
     zout.closeEntry(); 
    } 

    private boolean isUpToDate(File destFile, File fromFile) { 
     return FileUtils.getFileUtils().isUpToDate(fromFile, destFile); 
    } 

} 
Cuestiones relacionadas