2012-09-27 40 views
18

Nền nhanh: Chúng tôi phát hành ứng dụng webstart, bao gồm các lọ ứng dụng riêng và nhiều lọ bên thứ ba. Webstart yêu cầu tất cả các jars phân tán được giới thiệu bởi tệp jnlp được ký bởi một chứng chỉ đơn. Do đó chúng tôi ký tất cả các lọ (lọ của chúng tôi và các lọ bên thứ ba) bằng cách sử dụng một chứng chỉ tự ký. Một số lọ bên thứ ba đã được ký bởi bên sản xuất chúng, nhưng chúng tôi chỉ ký lại chúng, và điều này hoạt động tốt. Cho đến bây giờ.Điều gì ngăn Java xác minh các ký hiệu có nhiều thuật toán chữ ký

Vấn đề: Gần đây, chúng tôi đã chuyển từ Java 6 sang Java 7 và đột nhiên webstart từ chối tải một số lọ, phàn nàn: "Thông báo tệp chữ ký SHA1 không hợp lệ". Điều này chỉ xảy ra đối với một số lọ và không phải những người khác, và các chủ đề phổ biến xuất hiện trong số những lọ mà thất bại dường như có nhiều chữ ký.

Sau khi tìm kiếm xung quanh trên S.O. và Internet, có vẻ như thuật toán chữ ký mặc định cho jarsigner của Java đã thay đổi giữa Java 6 và Java 7, từ SHA1 thành SHA256 và nhiều người khác đang đề xuất sử dụng "jarsigner -digestalg SHA1" để giải quyết các vấn đề xác minh. Tôi đã thử điều đó, và chắc chắn rằng đủ các lọ đã ký của chúng tôi hiện đã được xác minh. Vì vậy, điều này dường như là một giải pháp cho vấn đề của chúng tôi.

Từ những gì tôi có thể thu thập, có vẻ chữ ký của bên thứ ba là chữ ký SHA1 và chúng tôi đã ký với mặc định - SHA256 - dẫn đến việc trộn chữ ký. Khi tôi ép SHA1 bằng cách sử dụng công tắc '-digestalg', chúng tôi có hai chữ ký cùng loại và xác minh hiện hoạt động. Vì vậy, có vẻ như vấn đề là do có nhiều chữ ký với các thuật toán khác nhau? Hoặc là có một số yếu tố khác tôi đang mất tích.

Câu hỏi:

  1. Tại sao nó lại thất bại trong việc xác minh với SHA1 + SHA256, nhưng xác minh với SHA1 + SHA1? Có lý do kỹ thuật nào không? Lý do chính sách bảo mật? Tại sao nó không thể xác minh rằng cả hai chữ ký là chính xác?
  2. Có bất kỳ hạn chế nào đối với chúng tôi khi sử dụng (tiếp tục sử dụng) SHA1 thay vì SHA256 mặc định hiện tại không?
+0

tôi quan sát thấy rằng ngay cả-dù SHA1 + SHA256 không thành công khi sử dụng các phím khác nhau ... nếu bạn đã đăng ký JAR sử dụng SHA1 + SHA256 với cùng khóa, xác minh không bao giờ thất bại. –

Trả lời

7

Thay vì tái ký lọ của bên thứ ba cho mình, bạn có thể tạo một file JNLP riêng cho mỗi người ký của bên thứ ba đề cập đến các tập tin jar có liên quan, sau đó có JNLP chính của bạn phụ thuộc vào những cách sử dụng các yếu tố <extension> . Hạn chế rằng tất cả các tệp JAR phải được ký bởi cùng một người ký chỉ áp dụng trong một JNLP, mỗi phần mở rộng có thể có một người ký khác.

Nếu không được, bạn có thể loại bỏ các chữ ký của bên thứ ba trước khi thêm của riêng bạn (bằng cách đóng gói lại chúng mà không cần META-INF/*.{SF,DSA,RSA})

+0

Cảm ơn bạn đã đề xuất. Nó chỉ là quá nặng nề để tạo jnlps riêng biệt cho mỗi thư viện của bên thứ ba (chúng tôi có hơn 150 lọ). Tôi đã xem xét tước bỏ các chữ ký khác trong kịch bản xây dựng của chúng tôi, nhưng: (1) cách giải quyết hiện tại của chúng tôi (được mô tả trong câu hỏi của tôi) là * nhiều * ít nỗ lực hơn; (2) giải pháp hiện tại của chúng tôi dẫn đến việc xây dựng nhanh hơn (không phải xây dựng lại các lọ đó), điều quan trọng đối với một ứng dụng lớn; (3) không chắc chắn nếu loại bỏ những chữ ký đó thực sự có thể gây ra một vấn đề cho bất kỳ libs nào. – JimN

+0

Tôi thấy quan điểm của bạn nhưng nhớ rằng nó chỉ là libs bên thứ ba _ đó là mã-signed_ mà bạn sẽ cần phải thực hiện vào các phần mở rộng (trong 50 ứng dụng JAR của tôi nó chỉ là javamail và kích hoạt mà áp dụng cho). –

1

Tôi biết điều này là hơi muộn - nhưng chúng tôi đang đi qua này ngay bây giờ. Vấn đề của chúng tôi là vấn đề ký kết "MD2withRSA". Tôi đã giải quyết vấn đề theo một vài bước:

1) Làm việc với Verisign để xóa thuật toán 'cũ' khỏi chứng chỉ của chúng tôi - vì vậy thuật toán MD2withRSA không còn được sử dụng để ký các bình của chúng tôi nữa.

2) Chúng tôi cũng có một đống lọ bên thứ 3 và chúng tôi chỉ ký lại chúng với chứng chỉ của chúng tôi. Chúng tôi đã gặp phải 'không phải tất cả các lọ được ký với cùng một chứng chỉ' khi cả hai thuật toán SHA1 và SHA-256 được liệt kê trong MANIFEST.MF. Đây chỉ là một tập nhỏ các lọ - vì vậy đối với những cái đó, chúng tôi đã xóa nửa dưới của tệp MANIFEST.MF; phần đó với tên: lớp và thông số thuật toán. Dữ liệu đó được tạo lại trong phần cuối của quy trình của chúng tôi. Chúng tôi giải nén, loại trừ các thông tin ký cũ và tái jar.Bước cuối cùng là ký lại các lọ. Chúng tôi thấy rằng trong một số trường hợp, nếu tên cũ: mục nhập có mục SHA1 nằm trong MANIFEST.MF, việc ký đã không thay thế bằng SHA-256 - vì vậy chúng tôi xử lý thủ công các lọ đó (hiện tại). Làm việc để cập nhật nhiệm vụ Ant của chúng tôi để xử lý việc này.

Xin lỗi - không thể nói lý do tại sao bắt đầu web không xử lý/cho phép - chỉ tìm ra cách làm cho nó hoạt động!

Chúc may mắn!

1

Có vẻ như một lỗi trong JRE. Cá nhân tôi giả định thuật toán ký mặc định cũ (DSA với tiêu hóa SHA1) kém an toàn hơn so với thuật toán mới (RSA với tiêu chuẩn SHA256), vì vậy tốt nhất là không sử dụng tùy chọn "-digestalg SHA1".

Tôi đã giải quyết vấn đề này bằng cách sử dụng tác vụ Ant tùy chỉnh trong tập lệnh xây dựng của tôi để 'hủy' các lọ của tôi trước khi ký chúng. Bằng cách đó chỉ có một chữ ký cho mỗi cái bình.

Dưới đây là nhiệm vụ Ant của tôi:

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); 
    } 

}