2010-01-12 39 views
17

Tôi đang cố gắng để digitally sign an XML document sử dụng Java. Tôi đã có một thực hiện làm việc với một số tài liệu tham khảo tôi đã tìm thấy rằng sử dụng thực hiện khác nhau trong gói javax.xml.crypto.dsig.Có cách nào dễ dàng hơn để ký một tài liệu XML trong Java không?

Tuy nhiên, thực hiện hiện tại của tôi cũng giống như many của examples Tôi đã nhìn vào - nó khá dài dòng và liên quan đến sử dụng không ít hơn 23 lớp API khác nhau từ java.xml.crypto.dsig, javax.xml.transform, và java.security gói, trong số những người khác. Tôi cảm thấy như mình đã bước vào vùng đất factory factory factory và tôi mất vài giờ để tìm ra những gì đang diễn ra.

Câu hỏi của tôi là, có cách nào dễ dàng hơn để thực hiện việc này không? Nếu tôi đã có public/private key files và tôi muốn thêm một <Signature/> đến một tài liệu XML, là có một thư viện ra khỏi đó mà chỉ cho phép tôi gọi cái gì đó như:

OutputStream signFile(InputStream xmlFile, File privateKey) 

... mà không cần tất cả XMLSignatureFactory/CanonicalizationMethod/DOMSignContext craziness?

Tôi không hiểu nhiều về mật mã và API do Java cung cấp dường như khá khó khăn đối với các nhà phát triển giống như bản thân tôi đang cố làm quen với ký điện tử. Nếu tất cả điều này là cần thiết hoặc hiện tại không có API thân thiện hơn ở đó, điều đó là tốt và tôi sẵn sàng chấp nhận điều đó như một câu trả lời. Tôi chỉ muốn biết nếu tôi không cần thiết đi theo con đường khó khăn ở đây.

+0

Là một giải pháp thay thế, tôi đã có thể thực hiện cuộc gọi hệ thống một lót và sử dụng http://www.aleksey.com/xmlsec/ ký XML. Tôi không đăng câu trả lời này vì câu trả lời không nằm trong giới hạn của câu hỏi (ví dụ: "trong Java"). –

Trả lời

3

Tôi đã xem tất cả các tùy chọn để ký các tệp XML và quyết định đi theo một phương pháp không chuẩn. Các tiêu chuẩn là tất cả các cách quá tiết. Ngoài ra, tôi không cần sự tương thích với các tiêu chuẩn --- tôi chỉ cần chữ ký trên một khối XML.

Có lẽ cách dễ nhất để "ký" một khối XML là sử dụng GPG với chữ ký tách rời.

+0

Ý tưởng hay, có lẽ là ý tưởng đơn giản nhất được cung cấp cho đến thời điểm này. Nó không cung cấp tất cả các cấu hình của javax.xml.crypto hoặc của Apache Santuario, nhưng đó thực sự là những gì làm cho những người khác rất phức tạp. –

+0

Cảm ơn. Một hệ thống thực hiện điều này có cách tiếp cận này lấy khối XML, tính toán một chữ ký bằng khóa công khai RSA và OpenSSL, và sau đó nhúng chữ ký đó vào cuối khối XML trong tệp văn bản. Bạn có thể tìm thấy mã trong chương trình afsign.cpp của tôi là một phần của AFFLIB, có thể tải xuống từ http://afflib.org/ – vy32

+0

Chấp nhận mã này vì nó là giải pháp đơn giản hơn. Giải pháp của Pascal cũng hợp lý, mặc dù nó vẫn đòi hỏi một số tiền hợp lý của mã. –

10

Hãy xem Apache XML Security. Để sử dụng gói để tạo và xác minh chữ ký, hãy kiểm tra các mẫu trong src_samples/org/apache/xml/security/samples/signature/.

+0

Cảm ơn bạn, Pascal. Đã có một số rắc rối thực sự nhận được những ví dụ để làm việc, cụ thể là "java.io.IOException: Định dạng kho khóa không hợp lệ", mặc dù tôi đã có thể tạo keystore.pks của riêng mình và làm cho nó hoạt động. – Kirby

+0

Liên kết không còn hợp lệ –

8

Xây dựng từ ví dụ Apache Santuario CreateSignature, điều ngắn nhất tôi có thể đưa ra là đây. Nếu không có sự main() và kèm theo nó output(), đó là 20 dòng

import java.io.*; 
import java.security.Key; 
import java.security.KeyStore; 
import java.security.cert.X509Certificate; 
import javax.xml.parsers.DocumentBuilderFactory; 

import org.apache.commons.io.IOUtils; 
import org.apache.xml.security.Init; 
import org.apache.xml.security.c14n.Canonicalizer; 
import org.apache.xml.security.signature.XMLSignature; 
import org.apache.xml.security.transforms.Transforms; 
import org.apache.xml.security.utils.Constants; 
import org.apache.xml.security.utils.ElementProxy; 
import org.w3c.dom.Document; 

public class CreateSignature { 

    private static final String PRIVATE_KEY_ALIAS = "test-alias"; 
    private static final String PRIVATE_KEY_PASS = "test"; 
    private static final String KEY_STORE_PASS = "test"; 
    private static final String KEY_STORE_TYPE = "JKS"; 

    public static void main(String... unused) throws Exception { 
     final InputStream fileInputStream = new FileInputStream("test.xml"); 
     try { 
      output(signFile(fileInputStream, new File("keystore.jks")), "signed-test.xml"); 
     } 
     finally { 
      IOUtils.closeQuietly(fileInputStream); 
     } 
    } 

    public static ByteArrayOutputStream signFile(InputStream xmlFile, File privateKeyFile) throws Exception { 
     final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile); 
     Init.init(); 
     ElementProxy.setDefaultPrefix(Constants.SignatureSpecNS, ""); 
     final KeyStore keyStore = loadKeyStore(privateKeyFile); 
     final XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA); 
     final Transforms transforms = new Transforms(doc); 
     transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE); 
     sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1); 
     final Key privateKey = keyStore.getKey(PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASS.toCharArray()); 
     final X509Certificate cert = (X509Certificate)keyStore.getCertificate(PRIVATE_KEY_ALIAS); 
     sig.addKeyInfo(cert); 
     sig.addKeyInfo(cert.getPublicKey()); 
     sig.sign(privateKey); 
     doc.getDocumentElement().appendChild(sig.getElement()); 
     final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
     outputStream.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc)); 
     return outputStream; 
    } 

    private static KeyStore loadKeyStore(File privateKeyFile) throws Exception { 
     final InputStream fileInputStream = new FileInputStream(privateKeyFile); 
     try { 
      final KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE); 
      keyStore.load(fileInputStream, KEY_STORE_PASS.toCharArray()); 
      return keyStore; 
     } 
     finally { 
      IOUtils.closeQuietly(fileInputStream); 
     } 
    } 

    private static void output(ByteArrayOutputStream signedOutputStream, String fileName) throws IOException { 
     final OutputStream fileOutputStream = new FileOutputStream(fileName); 
     try { 
      fileOutputStream.write(signedOutputStream.toByteArray()); 
      fileOutputStream.flush(); 
     } 
     finally { 
      IOUtils.closeQuietly(fileOutputStream); 
     } 
    } 
} 
+0

Đã thử đề xuất của bạn, tuy nhiên tôi dường như không thể thay đổi bản ngã C14N từ Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS thành Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS. Ngay cả khi tôi thay đổi điều này trong mã, chữ ký đầu ra XML vẫn hiển thị CanonicalizationMethod là . Bất kỳ ý tưởng nào về những gì có thể xảy ra ở đây? – mithrandir

Các vấn đề liên quan